summaryrefslogtreecommitdiffstats
path: root/o3d/samples
diff options
context:
space:
mode:
authorgspencer@google.com <gspencer@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-27 23:15:42 +0000
committergspencer@google.com <gspencer@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-27 23:15:42 +0000
commit05b47f7a8c5451f858dc220df0e3a97542edace6 (patch)
treea2273d619f0625c9d44d40842845ccce2eac1045 /o3d/samples
parent5cdc8bdb4c847cefe7f4542bd10c9880c2c557a0 (diff)
downloadchromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.zip
chromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.tar.gz
chromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.tar.bz2
This is the O3D source tree's initial commit to the Chromium tree. It
is not built or referenced at all by the chrome build yet, and doesn't yet build in it's new home. We'll change that shortly. git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17035 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'o3d/samples')
-rw-r--r--o3d/samples/2d.html577
-rw-r--r--o3d/samples/MANIFEST270
-rw-r--r--o3d/samples/animated-scene.html227
-rw-r--r--o3d/samples/animation.html411
-rw-r--r--o3d/samples/archive-textures.html297
-rw-r--r--o3d/samples/assets/android.pngbin0 -> 11280 bytes
-rw-r--r--o3d/samples/assets/archive_textures.o3dtgzbin0 -> 1019281 bytes
-rw-r--r--o3d/samples/assets/block.pngbin0 -> 4461 bytes
-rw-r--r--o3d/samples/assets/brush.pngbin0 -> 435 bytes
-rw-r--r--o3d/samples/assets/egg.pngbin0 -> 29942 bytes
-rw-r--r--o3d/samples/assets/empty.txt0
-rw-r--r--o3d/samples/assets/fullscreen.pngbin0 -> 865 bytes
-rw-r--r--o3d/samples/assets/gauge.pngbin0 -> 174 bytes
-rw-r--r--o3d/samples/assets/gaugeback.pngbin0 -> 2355 bytes
-rw-r--r--o3d/samples/assets/gears_init.js86
-rw-r--r--o3d/samples/assets/google-square.pngbin0 -> 12051 bytes
-rw-r--r--o3d/samples/assets/iconback.pngbin0 -> 2316 bytes
-rw-r--r--o3d/samples/assets/normalmap.ddsbin0 -> 43832 bytes
-rw-r--r--o3d/samples/assets/one-pixel-white.tgabin0 -> 48 bytes
-rw-r--r--o3d/samples/assets/orange-flower.pngbin0 -> 11702 bytes
-rw-r--r--o3d/samples/assets/particle-anim.pngbin0 -> 10050 bytes
-rw-r--r--o3d/samples/assets/pillar.pngbin0 -> 7867 bytes
-rw-r--r--o3d/samples/assets/purple-flower.pngbin0 -> 26558 bytes
-rw-r--r--o3d/samples/assets/radar.pngbin0 -> 11122 bytes
-rw-r--r--o3d/samples/assets/ripple.pngbin0 -> 3221 bytes
-rw-r--r--o3d/samples/assets/rock_bumps.jpgbin0 -> 1951243 bytes
-rw-r--r--o3d/samples/assets/rock_texture.jpgbin0 -> 714283 bytes
-rw-r--r--o3d/samples/assets/shaving_cream.jpgbin0 -> 322717 bytes
-rw-r--r--o3d/samples/assets/shaving_cream.pngbin0 -> 496645 bytes
-rw-r--r--o3d/samples/assets/square.pngbin0 -> 14491 bytes
-rw-r--r--o3d/samples/assets/teapot_vertices.js32
-rw-r--r--o3d/samples/assets/texture_b3.jpgbin0 -> 38230 bytes
-rw-r--r--o3d/samples/beachdemo/assets/pe_fire.jpgbin0 -> 10629 bytes
-rw-r--r--o3d/samples/beachdemo/assets/pe_mist.pngbin0 -> 54013 bytes
-rw-r--r--o3d/samples/beachdemo/assets/sky-cubemap.ddsbin0 -> 786560 bytes
-rw-r--r--o3d/samples/beachdemo/beachdemo.html1228
-rw-r--r--o3d/samples/beachdemo/beachdemo.js2663
-rw-r--r--o3d/samples/box2d-3d/box2d-3d.html201
-rw-r--r--o3d/samples/box2d-3d/demos/LICENSE.txt14
-rw-r--r--o3d/samples/box2d-3d/demos/README.o3d5
-rw-r--r--o3d/samples/box2d-3d/demos/compound.js71
-rw-r--r--o3d/samples/box2d-3d/demos/crank.js79
-rw-r--r--o3d/samples/box2d-3d/demos/demo_base.js59
-rw-r--r--o3d/samples/box2d-3d/demos/demos.js271
-rw-r--r--o3d/samples/box2d-3d/demos/draw_world.js90
-rw-r--r--o3d/samples/box2d-3d/demos/manager.js210
-rw-r--r--o3d/samples/box2d-3d/demos/pendulum.js25
-rw-r--r--o3d/samples/box2d-3d/demos/stack.js37
-rw-r--r--o3d/samples/box2d-3d/demos/top.js53
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/LICENSE.txt14
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/README.o3d5
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/collision/ClipVertex.js35
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/collision/Features.js61
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/collision/b2AABB.js45
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/collision/b2Bound.js43
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/collision/b2BoundValues.js31
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/collision/b2BroadPhase.js898
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/collision/b2BufferedPair.js26
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/collision/b2Collision.js738
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/collision/b2ContactID.js52
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/collision/b2ContactPoint.js35
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/collision/b2Distance.js333
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/collision/b2Manifold.js34
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/collision/b2OBB.js34
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/collision/b2Pair.js60
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/collision/b2PairCallback.js34
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/collision/b2PairManager.js386
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/collision/b2Proxy.js40
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2BoxDef.js49
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2CircleDef.js49
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2CircleShape.js198
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2MassData.js36
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2PolyDef.js58
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2PolyShape.js492
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2Shape.js339
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2ShapeDef.js109
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/common/b2Settings.js72
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/common/math/b2Mat22.js130
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/common/math/b2Math.js218
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/common/math/b2Vec2.js131
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/b2Body.js469
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/b2BodyDef.js69
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/b2CollisionFilter.js42
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/b2ContactManager.js337
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/b2Island.js331
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/b2TimeStep.js27
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/b2World.js522
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/b2WorldListener.js52
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2CircleContact.js102
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2Conservative.js228
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2Contact.js201
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactConstraint.js45
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactConstraintPoint.js40
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactNode.js33
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactRegister.js30
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactSolver.js537
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2NullContact.js65
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2PolyAndCircleContact.js103
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2PolyContact.js163
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2DistanceJoint.js264
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2DistanceJointDef.js49
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2GearJoint.js307
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2GearJointDef.js50
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2Jacobian.js49
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2Joint.js200
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2JointDef.js40
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2JointNode.js33
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2MouseJoint.js234
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2MouseJointDef.js53
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PrismaticJoint.js676
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PrismaticJointDef.js56
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PulleyJoint.js618
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PulleyJointDef.js70
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2RevoluteJoint.js491
-rw-r--r--o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2RevoluteJointDef.js55
-rw-r--r--o3d/samples/box2d-3d/third_party/prototype-1.6.0.2.js4221
-rw-r--r--o3d/samples/build.scons348
-rw-r--r--o3d/samples/canvas-fonts.html194
-rw-r--r--o3d/samples/canvas-texturedraw.html268
-rw-r--r--o3d/samples/canvas.html341
-rw-r--r--o3d/samples/checkers.html895
-rw-r--r--o3d/samples/convolution.html397
-rw-r--r--o3d/samples/culling.html340
-rw-r--r--o3d/samples/customcamera.html404
-rw-r--r--o3d/samples/debugging.html246
-rw-r--r--o3d/samples/displayfps.html219
-rw-r--r--o3d/samples/error-texture.html263
-rw-r--r--o3d/samples/fullscreen.html538
-rw-r--r--o3d/samples/gadgets/readme.txt11
-rw-r--r--o3d/samples/gadgets/scatter-chart.xml434
-rw-r--r--o3d/samples/generate-texture.html286
-rw-r--r--o3d/samples/hellocube-colors.html352
-rw-r--r--o3d/samples/hellocube-textures.html445
-rw-r--r--o3d/samples/hellocube.html312
-rw-r--r--o3d/samples/helloworld.html160
-rw-r--r--o3d/samples/home-configurators/assets/empty.txt0
-rw-r--r--o3d/samples/home-configurators/cb_images/cb_item_thumbnails.jpgbin0 -> 42895 bytes
-rw-r--r--o3d/samples/home-configurators/cb_images/toolselector.gifbin0 -> 344 bytes
-rw-r--r--o3d/samples/home-configurators/cb_images/unbranded_bg.pngbin0 -> 26868 bytes
-rw-r--r--o3d/samples/home-configurators/cbassets/empty.txt0
-rw-r--r--o3d/samples/home-configurators/craftsmanassets/craftsman_item_thumbnails.jpgbin0 -> 34143 bytes
-rw-r--r--o3d/samples/home-configurators/deletetool.js88
-rw-r--r--o3d/samples/home-configurators/homedesigner.html171
-rw-r--r--o3d/samples/home-configurators/movetool.js161
-rw-r--r--o3d/samples/home-configurators/orbittool.js94
-rw-r--r--o3d/samples/home-configurators/pantool.js77
-rw-r--r--o3d/samples/home-configurators/rotatetool.js113
-rw-r--r--o3d/samples/home-configurators/searsassets/sears_bg.pngbin0 -> 65087 bytes
-rw-r--r--o3d/samples/home-configurators/searsassets/sears_item_thumbnails.jpgbin0 -> 45942 bytes
-rw-r--r--o3d/samples/home-configurators/viewer.js578
-rw-r--r--o3d/samples/home-configurators/zoomtool.js76
-rw-r--r--o3d/samples/hud-2d-overlay.html503
-rw-r--r--o3d/samples/iframeit.html133
-rw-r--r--o3d/samples/instance-override.html199
-rw-r--r--o3d/samples/instancing.html208
-rw-r--r--o3d/samples/interactive_logic.js576
-rw-r--r--o3d/samples/interactive_sampler_assets/images/bl.gifbin0 -> 157 bytes
-rw-r--r--o3d/samples/interactive_sampler_assets/images/br.gifbin0 -> 158 bytes
-rw-r--r--o3d/samples/interactive_sampler_assets/images/cleardot.gifbin0 -> 43 bytes
-rw-r--r--o3d/samples/interactive_sampler_assets/images/corner.pngbin0 -> 1140 bytes
-rw-r--r--o3d/samples/interactive_sampler_assets/images/db_tl.pngbin0 -> 126 bytes
-rw-r--r--o3d/samples/interactive_sampler_assets/images/db_tr.pngbin0 -> 124 bytes
-rw-r--r--o3d/samples/interactive_sampler_assets/images/google-small.pngbin0 -> 2445 bytes
-rw-r--r--o3d/samples/interactive_sampler_assets/images/lb_tl.pngbin0 -> 123 bytes
-rw-r--r--o3d/samples/interactive_sampler_assets/images/lb_tr.pngbin0 -> 124 bytes
-rw-r--r--o3d/samples/interactive_sampler_assets/images/sprites.gifbin0 -> 6229 bytes
-rw-r--r--o3d/samples/interactive_sampler_assets/images/sprites08132008.pngbin0 -> 4179 bytes
-rw-r--r--o3d/samples/interactive_sampler_assets/images/sprites2.jpgbin0 -> 1087 bytes
-rw-r--r--o3d/samples/interactive_sampler_assets/images/tl.gifbin0 -> 1185 bytes
-rw-r--r--o3d/samples/interactive_sampler_assets/images/tr.gifbin0 -> 158 bytes
-rw-r--r--o3d/samples/interactive_sampler_assets/styles.css282
-rw-r--r--o3d/samples/interactive_sampler_assets/styles_ie.css301
-rw-r--r--o3d/samples/interactive_sampler_assets/utils.js200
-rw-r--r--o3d/samples/interactive_samples.js183
-rw-r--r--o3d/samples/io/README.txt29
-rw-r--r--o3d/samples/io/actors/actor.js144
-rw-r--r--o3d/samples/io/actors/arrow.js94
-rw-r--r--o3d/samples/io/actors/avatar.js67
-rw-r--r--o3d/samples/io/actors/coin.js68
-rw-r--r--o3d/samples/io/actors/horizontalpad.js85
-rw-r--r--o3d/samples/io/actors/mover.js48
-rw-r--r--o3d/samples/io/actors/spikem.js126
-rw-r--r--o3d/samples/io/actors/verticalpad.js84
-rw-r--r--o3d/samples/io/autoincludes.js49
-rw-r--r--o3d/samples/io/cutscenes.js148
-rw-r--r--o3d/samples/io/dynamic_lights.js123
-rw-r--r--o3d/samples/io/editor.html126
-rw-r--r--o3d/samples/io/gamelogic.js578
-rw-r--r--o3d/samples/io/init.js395
-rw-r--r--o3d/samples/io/io.html173
-rw-r--r--o3d/samples/io/levels/all_actors.js383
-rw-r--r--o3d/samples/io/levels/all_actors.skpbin0 -> 1514934 bytes
-rw-r--r--o3d/samples/io/levels/map1.js156
-rw-r--r--o3d/samples/io/levels/map1.skpbin0 -> 663342 bytes
-rw-r--r--o3d/samples/io/levels/starter_level.skpbin0 -> 557136 bytes
-rw-r--r--o3d/samples/io/sound/_MISS.mp3bin0 -> 627 bytes
-rw-r--r--o3d/samples/io/sound/_PUNCH.mp3bin0 -> 940 bytes
-rw-r--r--o3d/samples/io/sound/_SMASH.mp3bin0 -> 40333 bytes
-rw-r--r--o3d/samples/io/sound/_woosh.mp3bin0 -> 6653 bytes
-rw-r--r--o3d/samples/io/sound/ah.mp3bin0 -> 7549 bytes
-rw-r--r--o3d/samples/io/sound/arrow.mp3bin0 -> 5459 bytes
-rw-r--r--o3d/samples/io/sound/coin_3.mp3bin0 -> 12564 bytes
-rw-r--r--o3d/samples/io/sound/music.mp3bin0 -> 3276800 bytes
-rw-r--r--o3d/samples/io/sound/page.mp3bin0 -> 12146 bytes
-rw-r--r--o3d/samples/io/sound/soundplayer.js147
-rw-r--r--o3d/samples/io/sound/soundplayer.swfbin0 -> 3869 bytes
-rw-r--r--o3d/samples/io/sound/step1.mp3bin0 -> 4623 bytes
-rw-r--r--o3d/samples/io/sound/step2.mp3bin0 -> 5041 bytes
-rw-r--r--o3d/samples/io/sound/step3.mp3bin0 -> 4205 bytes
-rw-r--r--o3d/samples/io/sound/ug.mp3bin0 -> 2951 bytes
-rw-r--r--o3d/samples/io/ui/Thumbs.dbbin0 -> 27648 bytes
-rw-r--r--o3d/samples/io/ui/bgtile.jpgbin0 -> 2495 bytes
-rw-r--r--o3d/samples/io/ui/book_capbottom.jpgbin0 -> 18693 bytes
-rw-r--r--o3d/samples/io/ui/book_capleft.jpgbin0 -> 11719 bytes
-rw-r--r--o3d/samples/io/ui/book_capright.jpgbin0 -> 14239 bytes
-rw-r--r--o3d/samples/io/ui/book_captop.jpgbin0 -> 16493 bytes
-rw-r--r--o3d/samples/io/ui/book_cover.jpgbin0 -> 86247 bytes
-rw-r--r--o3d/samples/io/ui/book_innercover.jpgbin0 -> 25296 bytes
-rw-r--r--o3d/samples/io/ui/book_page1.jpgbin0 -> 97294 bytes
-rw-r--r--o3d/samples/io/ui/book_page2.jpgbin0 -> 95923 bytes
-rw-r--r--o3d/samples/io/ui/book_page3.jpgbin0 -> 75059 bytes
-rw-r--r--o3d/samples/io/ui/book_pageblank.jpgbin0 -> 44635 bytes
-rw-r--r--o3d/samples/io/ui/covershadow.pngbin0 -> 6490 bytes
-rw-r--r--o3d/samples/io/ui/io.css181
-rw-r--r--o3d/samples/io/ui/logo.gifbin0 -> 1577 bytes
-rw-r--r--o3d/samples/io/ui/scrollwork.gifbin0 -> 26553 bytes
-rw-r--r--o3d/samples/juggler.html431
-rw-r--r--o3d/samples/julia.html295
-rw-r--r--o3d/samples/multiple-clients.html243
-rw-r--r--o3d/samples/multiple-views.html220
-rw-r--r--o3d/samples/o3djs/arcball.js139
-rw-r--r--o3d/samples/o3djs/base.js779
-rw-r--r--o3d/samples/o3djs/camera.js364
-rw-r--r--o3d/samples/o3djs/canvas.js387
-rw-r--r--o3d/samples/o3djs/debug.js1257
-rw-r--r--o3d/samples/o3djs/dump.js597
-rw-r--r--o3d/samples/o3djs/effect.js737
-rw-r--r--o3d/samples/o3djs/element.js144
-rw-r--r--o3d/samples/o3djs/error.js133
-rw-r--r--o3d/samples/o3djs/event.js360
-rw-r--r--o3d/samples/o3djs/fps.js469
-rw-r--r--o3d/samples/o3djs/io.js577
-rw-r--r--o3d/samples/o3djs/js_list.scons61
-rw-r--r--o3d/samples/o3djs/loader.js194
-rw-r--r--o3d/samples/o3djs/material.js461
-rw-r--r--o3d/samples/o3djs/math.js2493
-rw-r--r--o3d/samples/o3djs/pack.js70
-rw-r--r--o3d/samples/o3djs/particles.js1051
-rw-r--r--o3d/samples/o3djs/picking.js560
-rw-r--r--o3d/samples/o3djs/primitives.js1698
-rw-r--r--o3d/samples/o3djs/quaternions.js479
-rw-r--r--o3d/samples/o3djs/rendergraph.js420
-rw-r--r--o3d/samples/o3djs/scene.js91
-rw-r--r--o3d/samples/o3djs/serialization.js744
-rw-r--r--o3d/samples/o3djs/shape.js140
-rw-r--r--o3d/samples/o3djs/simple.js556
-rw-r--r--o3d/samples/o3djs/test.js342
-rw-r--r--o3d/samples/o3djs/util.js928
-rw-r--r--o3d/samples/particles.html497
-rw-r--r--o3d/samples/phongshading.html367
-rw-r--r--o3d/samples/picking.html288
-rw-r--r--o3d/samples/pingpong/instructions.gifbin0 -> 3439 bytes
-rw-r--r--o3d/samples/pingpong/logo.gifbin0 -> 6958 bytes
-rw-r--r--o3d/samples/pingpong/o3dPingPong.html862
-rw-r--r--o3d/samples/primitives.html245
-rw-r--r--o3d/samples/procedural-texture.html258
-rw-r--r--o3d/samples/render-mode.html283
-rw-r--r--o3d/samples/render-targets.html340
-rw-r--r--o3d/samples/rotatemodel.html247
-rw-r--r--o3d/samples/sampler_index.html187
-rw-r--r--o3d/samples/scatter-chart.html426
-rw-r--r--o3d/samples/shader-test.html403
-rw-r--r--o3d/samples/shaders/README16
-rw-r--r--o3d/samples/shaders/binormal.shader64
-rw-r--r--o3d/samples/shaders/bump.shader137
-rw-r--r--o3d/samples/shaders/checker.shader117
-rw-r--r--o3d/samples/shaders/diffuse.shader96
-rw-r--r--o3d/samples/shaders/normal.shader64
-rw-r--r--o3d/samples/shaders/one-channel-texture.shader94
-rw-r--r--o3d/samples/shaders/phong-vertex-anim.shader95
-rw-r--r--o3d/samples/shaders/phong-with-colormult.shader83
-rw-r--r--o3d/samples/shaders/solid-color.shader74
-rw-r--r--o3d/samples/shaders/tangent.shader64
-rw-r--r--o3d/samples/shaders/texture-colormult.shader74
-rw-r--r--o3d/samples/shaders/texture-only.shader71
-rw-r--r--o3d/samples/shaders/vertex-color.shader75
-rw-r--r--o3d/samples/shaders/yuv2rgb.shader235
-rw-r--r--o3d/samples/simple.html108
-rw-r--r--o3d/samples/simpletexture.html179
-rw-r--r--o3d/samples/simpleviewer/assets/empty.txt0
-rw-r--r--o3d/samples/simpleviewer/simpleviewer.html365
-rw-r--r--o3d/samples/skinning.html276
-rw-r--r--o3d/samples/sobel.html342
-rw-r--r--o3d/samples/stencil_example.html439
-rw-r--r--o3d/samples/texturesamplers.html249
-rw-r--r--o3d/samples/third_party/codemirror/LICENSE23
-rw-r--r--o3d/samples/third_party/codemirror/README.o3d5
-rw-r--r--o3d/samples/third_party/codemirror/css/csscolors.css47
-rw-r--r--o3d/samples/third_party/codemirror/css/docs.css38
-rw-r--r--o3d/samples/third_party/codemirror/css/jscolors.css47
-rw-r--r--o3d/samples/third_party/codemirror/css/xmlcolors.css51
-rw-r--r--o3d/samples/third_party/codemirror/js/codemirror.js189
-rw-r--r--o3d/samples/third_party/codemirror/js/editor.js1052
-rw-r--r--o3d/samples/third_party/codemirror/js/mirrorframe.js83
-rw-r--r--o3d/samples/third_party/codemirror/js/parsecss.js155
-rw-r--r--o3d/samples/third_party/codemirror/js/parsehtmlmixed.js66
-rw-r--r--o3d/samples/third_party/codemirror/js/parsejavascript.js322
-rw-r--r--o3d/samples/third_party/codemirror/js/parsesparql.js162
-rw-r--r--o3d/samples/third_party/codemirror/js/parsexml.js292
-rw-r--r--o3d/samples/third_party/codemirror/js/select.js500
-rw-r--r--o3d/samples/third_party/codemirror/js/stringstream.js111
-rw-r--r--o3d/samples/third_party/codemirror/js/tokenize.js57
-rw-r--r--o3d/samples/third_party/codemirror/js/tokenizejavascript.js168
-rw-r--r--o3d/samples/third_party/codemirror/js/undo.js386
-rw-r--r--o3d/samples/third_party/codemirror/js/util.js134
-rw-r--r--o3d/samples/third_party/lightbox/LICENSE-lightbox-iframe.txt66
-rw-r--r--o3d/samples/third_party/lightbox/LICENSE-prototype.txt16
-rw-r--r--o3d/samples/third_party/lightbox/lightbox-iframe.js199
-rw-r--r--o3d/samples/third_party/lightbox/lightbox.css74
-rw-r--r--o3d/samples/third_party/lightbox/prototype.js1785
-rw-r--r--o3d/samples/trends/assets/clouds.jpgbin0 -> 107460 bytes
-rw-r--r--o3d/samples/trends/assets/earth-large-with-ocean-mask.pngbin0 -> 2367738 bytes
-rw-r--r--o3d/samples/trends/assets/earth-large.jpgbin0 -> 586564 bytes
-rw-r--r--o3d/samples/trends/assets/earth.jpgbin0 -> 46696 bytes
-rw-r--r--o3d/samples/trends/assets/energy.pngbin0 -> 136 bytes
-rw-r--r--o3d/samples/trends/assets/moon.jpgbin0 -> 67152 bytes
-rw-r--r--o3d/samples/trends/assets/night-large.jpgbin0 -> 313629 bytes
-rw-r--r--o3d/samples/trends/assets/night.jpgbin0 -> 86315 bytes
-rw-r--r--o3d/samples/trends/trends.html866
-rw-r--r--o3d/samples/tutorial-primitive.html120
-rw-r--r--o3d/samples/vertex-shader-animation.html210
-rw-r--r--o3d/samples/vertex-shader.html337
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.0.pngbin0 -> 211905 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.1.pngbin0 -> 212138 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.10.pngbin0 -> 211866 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.11.pngbin0 -> 212161 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.12.pngbin0 -> 212358 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.13.pngbin0 -> 212488 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.14.pngbin0 -> 212369 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.15.pngbin0 -> 212315 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.16.pngbin0 -> 211997 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.17.pngbin0 -> 212105 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.18.pngbin0 -> 211728 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.19.pngbin0 -> 211885 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.2.pngbin0 -> 211780 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.20.pngbin0 -> 211708 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.21.pngbin0 -> 211877 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.22.pngbin0 -> 211941 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.23.pngbin0 -> 211997 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.24.pngbin0 -> 211822 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.25.pngbin0 -> 211683 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.26.pngbin0 -> 211858 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.27.pngbin0 -> 211979 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.28.pngbin0 -> 212254 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.29.pngbin0 -> 212175 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.3.pngbin0 -> 212017 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.4.pngbin0 -> 212107 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.5.pngbin0 -> 212184 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.6.pngbin0 -> 212345 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.7.pngbin0 -> 212017 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.8.pngbin0 -> 211892 bytes
-rw-r--r--o3d/samples/waterdemo/assets/deepwater/deepwater.9.pngbin0 -> 211785 bytes
-rw-r--r--o3d/samples/waterdemo/assets/empty.txt0
-rw-r--r--o3d/samples/waterdemo/assets/horizon_ramp.pngbin0 -> 2961 bytes
-rw-r--r--o3d/samples/waterdemo/assets/horizon_ramp_1.pngbin0 -> 2961 bytes
-rw-r--r--o3d/samples/waterdemo/assets/main_rock_normal.ddsbin0 -> 524416 bytes
-rw-r--r--o3d/samples/waterdemo/assets/noise.pngbin0 -> 467149 bytes
-rw-r--r--o3d/samples/waterdemo/assets/reflectivity_map.pngbin0 -> 170 bytes
-rw-r--r--o3d/samples/waterdemo/assets/rock_reflection_new.pngbin0 -> 24018 bytes
-rw-r--r--o3d/samples/waterdemo/assets/rock_tile_normal_x.jpgbin0 -> 5102958 bytes
-rw-r--r--o3d/samples/waterdemo/assets/rock_tile_rgb.jpgbin0 -> 3558437 bytes
-rw-r--r--o3d/samples/waterdemo/assets/sky_compat.pngbin0 -> 147484 bytes
-rw-r--r--o3d/samples/waterdemo/assets/sun_compat.pngbin0 -> 55036 bytes
-rw-r--r--o3d/samples/waterdemo/assets/sun_ramp.pngbin0 -> 2950 bytes
-rw-r--r--o3d/samples/waterdemo/assets/sun_ramp_1.pngbin0 -> 2950 bytes
-rw-r--r--o3d/samples/waterdemo/cameracontrol.js123
-rw-r--r--o3d/samples/waterdemo/uicomponents.js112
-rw-r--r--o3d/samples/waterdemo/waterdemo.html439
-rw-r--r--o3d/samples/waterdemo/waterdemo.js646
-rw-r--r--o3d/samples/yuv2rgb.html206
-rw-r--r--o3d/samples/zsorting.html221
381 files changed, 70601 insertions, 0 deletions
diff --git a/o3d/samples/2d.html b/o3d/samples/2d.html
new file mode 100644
index 0000000..1e9224e
--- /dev/null
+++ b/o3d/samples/2d.html
@@ -0,0 +1,577 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+2D in O3D.
+
+2d is easy in o3d.
+
+Things to notice:
+
+*) Pixel perfect alignment with the screen.
+
+ The client area is 800x600 pixels. google-square.png is 200x200 pixels.
+ There is a 1 pixel border in the google-square.png texture. Notice they line
+ up exactly with each other and to the edge of the client area.
+
+*) Screen Top Left = 0,0 vs Center = 0,0
+
+ This demo is set so the client area's top left corner is 0,0.
+ If you want the center to be 0,0 just call setScreenOriginToCenter.
+ You will have to adjust the math below to match.
+
+*) Image Top left = 0,0 vs Center = 0,0
+
+ The g_googleSquares are all loaded with their origin set to the top left
+ so you can see they rotate around the top left and are positioned from
+ the top left
+
+ The g_squares and the g_things are loaded with their origin set to the center
+ so you can see they rotate and scale around their centers.
+
+*) 2d rotation, scale, fading, color changing
+
+ transform.rotateZ rotates in 2d.
+ transform.scale(x, y, 1) scales in 2d.
+ image.setColor(1, 1, 1, ?) fades out.
+ image.setColor(?, ?, ?, 1) sets the color multiplier
+
+*) Setting draw order by setting Z. negative z is more behind.
+
+ the g_googleSquares have a Z of -2 so they are the furthest back.
+ the g_squares have Z of -1 so they are in the middle
+ the g_things have a Z of 0 so they are in the front.
+
+ The Z range is -0.998 to 999
+
+ That is because the camera is sitting at -1 Z and has the range set
+ in the projection matrix from 0.001 to 1000
+
+*) Things to be aware of.
+
+ Because we are doing 2d, draw order matters but you can only choose
+ the order by setting the Z value. If you don't set the Z values correctly
+ you will see issues. For example if 2 images are at the same Z and draw
+ so that they overlap there is no guarntee which one will get drawn first.
+
+ Note: Right now I think it is which ever one happens to be in the transform
+ graph first. Maybe we should enforce this and then you can use insertBefore,
+ insertAfter to change the order instead of setting Z.
+
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+2D in O3D.
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.effect');
+o3djs.require('o3djs.loader');
+
+
+// global variables
+var g_coordWidth = 800; // The number of units across our area
+var g_coordHeight = 600; // The number of units down our area
+var g_tileWidth = 64;
+var g_tileHeight = 64;
+var g_pillarWidth = 128;
+var g_pillarHeight = 256;
+var g_playerWidth = 183;
+var g_playerHeight = 198;
+var g_tilesAcross = Math.floor((g_coordWidth + g_tileWidth - 1) /
+ g_tileWidth) + 1;
+var g_tilesDown = Math.floor((g_coordHeight + g_tileHeight - 1) /
+ g_tileHeight) + 1;
+var g_pixelsAcrossMap = g_tilesAcross * 64;
+var g_pixelsAcrossPillarMap = (Math.floor((g_coordWidth + g_pillarWidth - 1) /
+ g_pillarWidth) + 1) * g_pillarWidth;
+var g_o3d;
+var g_math;
+var g_client;
+var g_viewInfo;
+var g_pack;
+var g_planeShape;
+var g_textures = [];
+var g_textureUrls = [
+ 'assets/purple-flower.png', // 0
+ 'assets/orange-flower.png', // 1
+ 'assets/egg.png', // 2
+ 'assets/google-square.png', // 3
+ 'assets/square.png', // 4
+ 'assets/texture_b3.jpg', // 5
+ 'assets/pillar.png', // 6
+ 'assets/block.png', // 7
+ 'assets/android.png' // 8
+];
+var g_googleSquares = [];
+var g_squares = [];
+var g_things = [];
+var g_tiles = [];
+var g_pillars = [];
+var g_pillarScale = [];
+var g_player;
+var g_map = [
+ [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1],
+ [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ [ 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0],
+ [ 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
+ [ 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0],
+ [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
+ [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
+ [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
+];
+var g_randSeed = 0;
+
+/**
+ * Returns a deterministic pseudorandom number bewteen 0 and 1
+ * @return {number} a random number between 0 and 1
+ */
+function pseudoRandom() {
+ var range = Math.pow(2, 32);
+
+ return (g_randSeed = (134775813 * g_randSeed + 1) % range) / range;
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ // These are here so they are visible to both the browser (so selenium sees
+ // them) and V8.
+ window.g_clock = 0;
+ window.g_timeMult = 1;
+ window.g_finished = false; // for selenium testing
+
+ // Comment out the line below to run the sample in the browser JavaScript
+ // engine. This may be helpful for debugging.
+ o3djs.util.setMainEngine(o3djs.util.Engine.V8);
+
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Sets the origin of the screen to the top left. Assumes the area is 800 pixels
+ * by 600 pixels.
+ *
+ * We have to pick some representation. If we have an image that's 800x600 and
+ * we want to display it pixel perfect in an 800x600 area then we set the values
+ * below to 800x600. If instead we were to set them to the width and height of
+ * the area and say our area was 400x300 it will still be pixel perfect (1 to 1
+ * pixels) which would meaning only the top left 400x300 pixels of our 800x600
+ * image would show up.
+ *
+ * Doing it this way, we get pixel perfect at 800x600 and if we change the size
+ * of the area, say to 400x300 to 640x480 or any other 4:3 ratio, our program
+ * will still work as expected. It won't be 1x1 pixels but it will put things
+ * where we want them within our scaled area. If our area is not 4:3 our image
+ * will get a little stretched into the new aspect ratio but generally that's
+ * still better than having some of our app not appear.
+ *
+ * The other option is to always set the values below to the width and height of
+ * our area and then make all the code that positions things calculate where to
+ * put things and how to scale them to fit which is a lot more work though may
+ * be appropriate depending on the application.
+ */
+function setScreenOriginToTopLeft() {
+ g_viewInfo.drawContext.projection = g_math.matrix4.orthographic(
+ 0 + 0.5,
+ g_coordWidth + 0.5,
+ g_coordHeight + 0.5,
+ 0 + 0.5,
+ 0.001,
+ 1000);
+
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [0, 0, 1], // eye
+ [0, 0, 0], // target
+ [0, 1, 0]); // up
+}
+
+/**
+ * Sets the origin of the screen to the center. Assumes the area is 800 pixels
+ * by 600 pixels. See setScreenOriginToTopLeft.
+ */
+function setScreenOriginToCenter() {
+ g_viewInfo.drawContext.projection = g_math.matrix4.orthographic(
+ -g_coordWidth * 0.5 + 0.5,
+ g_coordWidth * 0.5 + 0.5,
+ g_coordHeight * 0.5 + 0.5,
+ -g_coordHeight * 0.5 + 0.5,
+ 0.001,
+ 1000);
+
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [0, 0, 1], // eye
+ [0, 0, 0], // target
+ [0, 1, 0]); // up
+}
+
+/**
+ * Initializes O3D and creates one shape.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+
+ // Set window.g_client as well. Otherwise when the sample runs in V8, selenium
+ // won't be able to find this variable (it can only see the browser
+ // environment).
+ window.g_client = g_client = o3dElement.client;
+
+ // Creates a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Set the background color to blue.
+ g_viewInfo.clearBuffer.clearColor = [0.1, 0.1, 1, 1];
+
+ // Set culling to none so we can flip images using rotation or negative scale.
+ g_viewInfo.zOrderedState.getStateParam('CullMode').value =
+ g_o3d.State.CULL_NONE;
+ g_viewInfo.zOrderedState.getStateParam('ZWriteEnable').value = false;
+
+ // Create an orthographic matrix for 2d stuff.
+ setScreenOriginToTopLeft();
+ //setScreenOriginToCenter();
+
+ var effect = g_pack.createObject('Effect');
+ o3djs.effect.loadEffect(effect, 'shaders/texture-colormult.shader');
+
+ // Create a Material for the effect.
+ var material = g_pack.createObject('Material');
+
+ // Apply the effect to this material.
+ material.effect = effect;
+
+ // Set the material's drawList for translucent things.
+ material.drawList = g_viewInfo.zOrderedDrawList;
+
+ // Create the parameters the effect needs on the material.
+ effect.createUniformParameters(material);
+
+ // Set the material params which act as the default. We'll override these with
+ // params on transforms.
+ material.getParam('colorMult').value = [1, 1, 1, 1];
+
+ // Create a 2d plane. createPlane makes an XZ plane by default
+ // so we pass in matrix to rotate it to an XY plane. We could do
+ // all our manipluations in XZ but most people seem to like XY for 2D.
+ g_planeShape = o3djs.primitives.createPlane(
+ g_pack,
+ material,
+ 1,
+ 1,
+ 1,
+ 1,
+ [[1, 0, 0, 0],
+ [0, 0, 1, 0],
+ [0,-1, 0, 0],
+ [0, 0, 0, 1]]);
+
+ // Load all the textures.
+ var loader = o3djs.loader.createLoader(initStep3);
+ for (var ii = 0; ii < g_textureUrls.length; ++ii) {
+ loadTexture(loader, g_textureUrls[ii], ii);
+ }
+ loader.finish();
+}
+
+/**
+ * Loads a texture and saves it in the g_textures array.
+ * @param {!o3djs.loader.Loader} loader The loader to load with.
+ * @param {string} url of texture to load
+ * @param {number} index Index to put texture in g_textures
+ */
+function loadTexture(loader, url, index) {
+ loader.loadTexture(g_pack, url, function(texture, exception) {
+ if (exception) {
+ alert(exception);
+ } else {
+ g_textures[index] = texture;
+ }
+ });
+}
+
+/**
+ * Now that the textures are loaded continue.
+ */
+function initStep3() {
+ for (var ii = 0; ii < 12; ++ii) {
+ g_googleSquares[ii] = new Image(g_textures[3], true);
+ g_squares[ii] = new Image(g_textures[4], false);
+ g_things[ii] = new Image(g_textures[ii % 3], false);
+ g_pillars[ii] = new Image(g_textures[6], true);
+ g_pillarScale[ii] = pseudoRandom() * 1.5 + 0.5;
+ }
+
+ for (var yy = 0; yy < g_tilesDown; ++yy) {
+ g_tiles[yy] = [];
+ for (var xx = 0; xx < g_tilesAcross; ++xx) {
+ if (g_map[yy][xx]) {
+ g_tiles[yy][xx] = new Image(g_textures[7], true);
+ }
+ }
+ }
+
+ g_player = new Image(g_textures[8], true);
+
+ // Setup an onrender callback for animation.
+ g_client.setRenderCallback(onrender);
+
+ window.g_finished = true; // for selenium testing.
+}
+
+/**
+ * Called every frame.
+ * @param {!o3d.RenderEvent} renderEvent Rendering Information.
+ */
+function onrender(renderEvent) {
+ var elapsedTime = renderEvent.elapsedTime;
+
+ // Update g_clock in the browser and cache a V8 copy that can be accesse
+ // efficiently. g_clock must be in the browser for selenium.
+ var clock = window.g_clock + elapsedTime * window.g_timeMult;
+ window.g_clock = clock;
+
+ var duration = 4;
+ var parts = 4;
+ var fadeTime = 1;
+ var partOffset = duration - fadeTime;
+ var cycle = duration * parts - parts;
+
+ var computeFade = function(fadeClock) {
+ var fade = 1;
+ if (fadeClock < fadeTime) {
+ fade = fadeClock / fadeTime;
+ } else if (fadeClock > duration - fadeTime) {
+ fade = Math.max(duration - fadeClock, 0) / fadeTime;
+ }
+ return fade;
+ };
+
+ // Position, fade and rotate the google squares and flowers, eggs.
+ for (var yy = 0; yy < 3; ++yy) {
+ for (var xx = 0; xx < 4; ++xx) {
+ var index = yy * 4 + xx;
+ var newIndex = (index + Math.floor(clock * 0.5)) % 12
+ var offset = (newIndex == 0) ? (clock * 0.5 % 1) : 0;
+
+ // compute the amount to fade the google squares.
+ var fade = computeFade((clock + partOffset * 0) % cycle);
+
+ // position and rotate the google squares.
+ var image = g_googleSquares[newIndex];
+ image.transform.identity();
+ image.transform.translate(xx * 200, yy * 200, -2);
+ image.transform.rotateZ(Math.PI * 2 * offset);
+ image.setColor(Math.sin(index + clock * 2.2) * 0.2 + 0.6,
+ Math.sin(index + clock * 3.3) * 0.2 + 0.6,
+ Math.sin(index + clock * 4.4) * 0.2 + 0.6,
+ fade);
+
+ // compute the amount to fade the squares.
+ fade = computeFade((clock + partOffset * 1) % cycle);
+
+ // position and rotate the squares.
+ var image = g_squares[index];
+ image.setColor(1, 1, 1,
+ fade * (Math.sin(index + clock) * 0.5 + 0.5));
+ image.transform.identity();
+ image.transform.translate(xx * 200 + 100, yy * 200 + 100, -1);
+ image.transform.rotateZ(Math.PI * 2 * offset);
+ image.transform.scale(1, Math.sin(clock * 2+ index), 1);
+
+ // compute the amount to fade the things.
+ fade = computeFade((clock + partOffset * 2) % cycle);
+
+ // position, rotate and scale the things.
+ var image = g_things[index];
+ var nx = newIndex % 4;
+ var ny = Math.floor(newIndex / 4);
+ offset = clock * 0.5 % 1;
+ image.transform.identity();
+ image.transform.translate(nx * 300 - 200 + offset * 300,
+ ny * 200 + 100 +
+ Math.sin(clock * 4 + index * 2) * 50,
+ 0);
+ image.transform.rotateZ((0.1 + index * 0.1) * clock);
+ var scale = Math.sin(index + clock) * 1.0 + 1.7;
+ image.transform.scale(scale, scale, 1);
+ image.setColor(1, 1, 1, fade);
+ }
+ }
+
+ // Position and fade the 2d tile based game.
+ {
+ // This is NOT the best way to a 2d platformer but it does at least show
+ // displaying the graphics of one.
+
+ // compute the amount to fade the tiles and pillars.
+ var fade = computeFade((clock + partOffset * 3) % cycle);
+
+ // position the pillars
+ for (var xx = 0; xx < 12; ++xx) {
+ var image = g_pillars[xx];
+ image.setColor(1, 1 , 1, fade);
+ image.transform.identity();
+ var xOffset =
+ (g_pixelsAcrossPillarMap - g_pillarWidth * 2) -
+ ((((xx + clock * 0.3) * 238) + g_pillarWidth) %
+ g_pixelsAcrossPillarMap - g_pillarWidth);
+ image.transform.translate(xOffset, 600 - 256 * g_pillarScale[xx],
+ -6);
+ image.transform.scale(1, g_pillarScale[xx], 1);
+ }
+
+ // position the tiles.
+ for (var yy = 0; yy < g_tilesDown; ++yy) {
+ for (var xx = 0; xx < g_tilesAcross; ++xx) {
+ if (g_map[yy][xx]) {
+ var image = g_tiles[yy][xx];
+ image.setColor(1, 1, 1, fade);
+ image.transform.identity();
+ var xOffset =
+ (g_pixelsAcrossMap - g_tileWidth * 2) -
+ ((((xx + clock * 2) * g_tileWidth) + g_tileWidth) %
+ g_pixelsAcrossMap - g_tileWidth);
+ image.transform.translate(xOffset, yy * g_tileHeight, -5);
+ }
+ }
+ }
+
+ {
+ // position the player.
+ g_player.setColor(1, 1, 1, fade);
+ var xOffset =
+ (g_pixelsAcrossMap - g_tileWidth * 2) -
+ (((((4.0 + clock * 2) * g_tileWidth) + g_tileWidth) %
+ g_pixelsAcrossMap + g_tileWidth) - g_tileWidth);
+ g_player.transform.identity();
+ g_player.transform.translate(xOffset,
+ 5 * g_tileHeight - g_playerHeight -
+ Math.abs(Math.sin(clock * 5)) * 100,
+ -4);
+ }
+ }
+}
+
+/**
+ * Creates an Image object which is a transform and a child scaleTransform
+ * scaled to match the texture
+ *
+ * @constructor
+ * @param {!o3d.Texture} texture The texture
+ * @param {boolean} opt_topLeft If true the origin of the image will be it's
+ * topleft corner, the default is the center of the image.
+ */
+function Image(texture, opt_topLeft) {
+ // create a transform for positioning
+ this.transform = g_pack.createObject('Transform');
+ this.transform.parent = g_client.root;
+
+ // create a transform for scaling to the size of the image just so
+ // we don't have to manage that manually in the transform above.
+ this.scaleTransform = g_pack.createObject('Transform');
+ this.scaleTransform.parent = this.transform;
+
+ // setup the sampler for the texture
+ this.sampler = g_pack.createObject('Sampler');
+ this.sampler.addressModeU = g_o3d.Sampler.CLAMP;
+ this.sampler.addressModeV = g_o3d.Sampler.CLAMP;
+ this.paramSampler = this.scaleTransform.createParam('texSampler0',
+ 'ParamSampler');
+ this.paramSampler.value = this.sampler;
+
+ // Setup our UV offsets and color multiplier
+ this.paramColorMult = this.scaleTransform.createParam('colorMult',
+ 'ParamFloat4');
+
+ this.setColor(1, 1, 1, 1);
+
+ this.sampler.texture = texture;
+ this.scaleTransform.addShape(g_planeShape);
+ if (opt_topLeft) {
+ this.scaleTransform.translate(texture.width / 2, texture.height / 2, 0);
+ }
+ this.scaleTransform.scale(texture.width, -texture.height, 1);
+}
+
+/**
+ * Sets the color multiplier for the image.
+ * @param {number} r Red component.
+ * @param {number} g Green component.
+ * @param {number} b Blue component.
+ * @param {number} a Alpha component.
+ */
+Image.prototype.setColor = function(r, g, b, a) {
+ this.paramColorMult.set(r, g, b, a);
+};
+
+/**
+ * Remove any callbacks so they don't get called after the page has unloaded.
+ */
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+</script>
+</head>
+<body onload="init()" onunload="unload()">
+<h1>2D In O3D</h1>
+Doing 2D in O3D.
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 800px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/samples/MANIFEST b/o3d/samples/MANIFEST
new file mode 100644
index 0000000..8004acd
--- /dev/null
+++ b/o3d/samples/MANIFEST
@@ -0,0 +1,270 @@
+archive-textures.html
+assets/android.png
+assets/archive_textures.o3dtgz
+assets/block.png
+assets/brush.png
+assets/dome1.o3dtgz
+assets/dome2.o3dtgz
+assets/dome3.o3dtgz
+assets/dome4.o3dtgz
+assets/egg.png
+assets/gauge.png
+assets/gaugeback.png
+assets/gears_init.js
+assets/google-square.png
+assets/iconback.png
+assets/kitty_151_idle_stand05_cff1.o3dtgz
+assets/normalmap.dds
+assets/one-pixel-white.tga
+assets/orange-flower.png
+assets/part1.o3dtgz
+assets/part2.o3dtgz
+assets/part3.o3dtgz
+assets/particle-anim.png
+assets/pillar.png
+assets/purple-flower.png
+assets/radar.png
+assets/ripple.png
+assets/rock_bumps.jpg
+assets/rock_texture.jpg
+assets/seven_shapes.o3dtgz
+assets/shaving_cream.jpg
+assets/shaving_cream.png
+assets/square.png
+assets/stencil_frame.o3dtgz
+assets/teapot.o3dtgz
+assets/teapot_vertices.js
+assets/texture_b3.jpg
+assets/yard.o3dtgz
+beachdemo/assets/beachdemo.o3dtgz
+beachdemo/assets/beach-low-poly.o3dtgz
+beachdemo/assets/pe_fire.jpg
+beachdemo/assets/pe_mist.png
+beachdemo/assets/sky-cubemap.dds
+beachdemo/beachdemo.html
+beachdemo/beachdemo.js
+box2d-3d/box2d-3d.html
+box2d-3d/demos/LICENSE.txt
+box2d-3d/demos/README.o3d
+box2d-3d/demos/compound.js
+box2d-3d/demos/crank.js
+box2d-3d/demos/demo_base.js
+box2d-3d/demos/demos.js
+box2d-3d/demos/draw_world.js
+box2d-3d/demos/manager.js
+box2d-3d/demos/pendulum.js
+box2d-3d/demos/stack.js
+box2d-3d/demos/top.js
+box2d-3d/third_party/box2d/LICENSE.txt
+box2d-3d/third_party/box2d/README.o3d
+box2d-3d/third_party/box2d/collision/ClipVertex.js
+box2d-3d/third_party/box2d/collision/Features.js
+box2d-3d/third_party/box2d/collision/b2AABB.js
+box2d-3d/third_party/box2d/collision/b2Bound.js
+box2d-3d/third_party/box2d/collision/b2BoundValues.js
+box2d-3d/third_party/box2d/collision/b2BroadPhase.js
+box2d-3d/third_party/box2d/collision/b2BufferedPair.js
+box2d-3d/third_party/box2d/collision/b2Collision.js
+box2d-3d/third_party/box2d/collision/b2ContactID.js
+box2d-3d/third_party/box2d/collision/b2ContactPoint.js
+box2d-3d/third_party/box2d/collision/b2Distance.js
+box2d-3d/third_party/box2d/collision/b2Manifold.js
+box2d-3d/third_party/box2d/collision/b2OBB.js
+box2d-3d/third_party/box2d/collision/b2Pair.js
+box2d-3d/third_party/box2d/collision/b2PairCallback.js
+box2d-3d/third_party/box2d/collision/b2PairManager.js
+box2d-3d/third_party/box2d/collision/b2Proxy.js
+box2d-3d/third_party/box2d/collision/shapes/b2BoxDef.js
+box2d-3d/third_party/box2d/collision/shapes/b2CircleDef.js
+box2d-3d/third_party/box2d/collision/shapes/b2CircleShape.js
+box2d-3d/third_party/box2d/collision/shapes/b2MassData.js
+box2d-3d/third_party/box2d/collision/shapes/b2PolyDef.js
+box2d-3d/third_party/box2d/collision/shapes/b2PolyShape.js
+box2d-3d/third_party/box2d/collision/shapes/b2Shape.js
+box2d-3d/third_party/box2d/collision/shapes/b2ShapeDef.js
+box2d-3d/third_party/box2d/common/b2Settings.js
+box2d-3d/third_party/box2d/common/math/b2Mat22.js
+box2d-3d/third_party/box2d/common/math/b2Math.js
+box2d-3d/third_party/box2d/common/math/b2Vec2.js
+box2d-3d/third_party/box2d/dynamics/b2Body.js
+box2d-3d/third_party/box2d/dynamics/b2BodyDef.js
+box2d-3d/third_party/box2d/dynamics/b2CollisionFilter.js
+box2d-3d/third_party/box2d/dynamics/b2ContactManager.js
+box2d-3d/third_party/box2d/dynamics/b2Island.js
+box2d-3d/third_party/box2d/dynamics/b2TimeStep.js
+box2d-3d/third_party/box2d/dynamics/b2World.js
+box2d-3d/third_party/box2d/dynamics/b2WorldListener.js
+box2d-3d/third_party/box2d/dynamics/contacts/b2CircleContact.js
+box2d-3d/third_party/box2d/dynamics/contacts/b2Conservative.js
+box2d-3d/third_party/box2d/dynamics/contacts/b2Contact.js
+box2d-3d/third_party/box2d/dynamics/contacts/b2ContactConstraint.js
+box2d-3d/third_party/box2d/dynamics/contacts/b2ContactConstraintPoint.js
+box2d-3d/third_party/box2d/dynamics/contacts/b2ContactNode.js
+box2d-3d/third_party/box2d/dynamics/contacts/b2ContactRegister.js
+box2d-3d/third_party/box2d/dynamics/contacts/b2ContactSolver.js
+box2d-3d/third_party/box2d/dynamics/contacts/b2NullContact.js
+box2d-3d/third_party/box2d/dynamics/contacts/b2PolyAndCircleContact.js
+box2d-3d/third_party/box2d/dynamics/contacts/b2PolyContact.js
+box2d-3d/third_party/box2d/dynamics/joints/b2DistanceJoint.js
+box2d-3d/third_party/box2d/dynamics/joints/b2DistanceJointDef.js
+box2d-3d/third_party/box2d/dynamics/joints/b2GearJoint.js
+box2d-3d/third_party/box2d/dynamics/joints/b2GearJointDef.js
+box2d-3d/third_party/box2d/dynamics/joints/b2Jacobian.js
+box2d-3d/third_party/box2d/dynamics/joints/b2Joint.js
+box2d-3d/third_party/box2d/dynamics/joints/b2JointDef.js
+box2d-3d/third_party/box2d/dynamics/joints/b2JointNode.js
+box2d-3d/third_party/box2d/dynamics/joints/b2MouseJoint.js
+box2d-3d/third_party/box2d/dynamics/joints/b2MouseJointDef.js
+box2d-3d/third_party/box2d/dynamics/joints/b2PrismaticJoint.js
+box2d-3d/third_party/box2d/dynamics/joints/b2PrismaticJointDef.js
+box2d-3d/third_party/box2d/dynamics/joints/b2PulleyJoint.js
+box2d-3d/third_party/box2d/dynamics/joints/b2PulleyJointDef.js
+box2d-3d/third_party/box2d/dynamics/joints/b2RevoluteJoint.js
+box2d-3d/third_party/box2d/dynamics/joints/b2RevoluteJointDef.js
+box2d-3d/third_party/prototype-1.6.0.2.js
+canvas-fonts.html
+checkers.html
+debugging.html
+home-configurators/cb_images/cb_item_thumbnails.jpg
+home-configurators/cb_images/toolselector.gif
+home-configurators/cb_images/unbranded_bg.png
+home-configurators/cbassets/Agra_Rug.o3dtgz
+home-configurators/cbassets/Asimi_Rug.o3dtgz
+home-configurators/cbassets/Camden_Chair.o3dtgz
+home-configurators/cbassets/Elements_Bookshelf.o3dtgz
+home-configurators/cbassets/Ferrara_Rug.o3dtgz
+home-configurators/cbassets/House_Roofless.o3dtgz
+home-configurators/cbassets/Lounge_Chair.o3dtgz
+home-configurators/cbassets/Lounge_Chaise.o3dtgz
+home-configurators/cbassets/Lounge_Sofa.o3dtgz
+home-configurators/cbassets/Lounge_Storage_Ottoman.o3dtgz
+home-configurators/cbassets/Madison_Dining_Table.o3dtgz
+home-configurators/cbassets/Miles_Side_Chair.o3dtgz
+home-configurators/cbassets/Pullman_Bar_Stool.o3dtgz
+home-configurators/cbassets/Puzzle_TV_Stand.o3dtgz
+home-configurators/cbassets/Stow_Leather_Ottoman.o3dtgz
+home-configurators/cbassets/Tivoli_Dining_Table.o3dtgz
+home-configurators/cbassets/Tivoli_Miles_Dining_Set.o3dtgz
+home-configurators/cbassets/Troy_Chair.o3dtgz
+home-configurators/cbassets/Troy_Ottoman.o3dtgz
+home-configurators/cbassets/Troy_Sofa.o3dtgz
+home-configurators/cbassets/Troy_Storage_Ottoman.o3dtgz
+home-configurators/cbassets/Troy_Twin_Sleeper.o3dtgz
+home-configurators/deletetool.js
+home-configurators/homedesigner.html
+home-configurators/movetool.js
+home-configurators/orbittool.js
+home-configurators/pantool.js
+home-configurators/rotatetool.js
+home-configurators/viewer.js
+home-configurators/zoomtool.js
+io/README.txt
+io/actors/actor.js
+io/actors/arrow.js
+io/actors/avatar.js
+io/actors/coin.js
+io/actors/horizontalpad.js
+io/actors/mover.js
+io/actors/spikem.js
+io/actors/verticalpad.js
+io/autoincludes.js
+io/cutscenes.js
+io/dynamic_lights.js
+io/editor.html
+io/gamelogic.js
+io/init.js
+io/io.html
+io/levels/all_actors.js
+io/levels/all_actors.skp
+io/levels/all_actors.o3dtgz
+io/levels/map1.js
+io/levels/map1.skp
+io/levels/map1.o3dtgz
+io/levels/starter_level.skp
+io/sound/_MISS.mp3
+io/sound/_PUNCH.mp3
+io/sound/_SMASH.mp3
+io/sound/_woosh.mp3
+io/sound/ah.mp3
+io/sound/arrow.mp3
+io/sound/coin_3.mp3
+io/sound/music.mp3
+io/sound/page.mp3
+io/sound/soundplayer.js
+io/sound/soundplayer.swf
+io/sound/step1.mp3
+io/sound/step2.mp3
+io/sound/step3.mp3
+io/sound/ug.mp3
+io/ui/bgtile.jpg
+io/ui/book_capbottom.jpg
+io/ui/book_capleft.jpg
+io/ui/book_capright.jpg
+io/ui/book_captop.jpg
+io/ui/book_cover.jpg
+io/ui/book_innercover.jpg
+io/ui/book_page1.jpg
+io/ui/book_page2.jpg
+io/ui/book_page3.jpg
+io/ui/book_pageblank.jpg
+io/ui/covershadow.png
+io/ui/io.css
+io/ui/logo.gif
+io/ui/scrollwork.gif
+o3djs/arcball.js
+o3djs/base.js
+o3djs/camera.js
+o3djs/canvas.js
+o3djs/debug.js
+o3djs/dump.js
+o3djs/effect.js
+o3djs/element.js
+o3djs/error.js
+o3djs/event.js
+o3djs/fps.js
+o3djs/io.js
+o3djs/loader.js
+o3djs/material.js
+o3djs/math.js
+o3djs/pack.js
+o3djs/particles.js
+o3djs/picking.js
+o3djs/primitives.js
+o3djs/quaternions.js
+o3djs/rendergraph.js
+o3djs/scene.js
+o3djs/serialization.js
+o3djs/shape.js
+o3djs/simple.js
+o3djs/test.js
+o3djs/util.js
+pingpong/instructions.gif
+pingpong/logo.gif
+pingpong/o3dPingPong.html
+shaders/README
+shaders/binormal.shader
+shaders/bump.shader
+shaders/checker.shader
+shaders/diffuse.shader
+shaders/one-channel-texture.shader
+shaders/normal.shader
+shaders/phong-vertex-anim.shader
+shaders/phong-with-colormult.shader
+shaders/solid-color.shader
+shaders/tangent.shader
+shaders/texture-colormult.shader
+shaders/texture-only.shader
+shaders/vertex-color.shader
+shaders/yuv2rgb.shader
+simpleviewer/assets/cube.o3dtgz
+simpleviewer/simpleviewer.html
+trends/assets/clouds.jpg
+trends/assets/earth-large-with-ocean-mask.png
+trends/assets/earth-large.jpg
+trends/assets/earth.jpg
+trends/assets/energy.png
+trends/assets/moon.jpg
+trends/assets/night-large.jpg
+trends/assets/night.jpg
+trends/trends.html
+yuv2rgb.html
diff --git a/o3d/samples/animated-scene.html b/o3d/samples/animated-scene.html
new file mode 100644
index 0000000..7d6fc96
--- /dev/null
+++ b/o3d/samples/animated-scene.html
@@ -0,0 +1,227 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+Animated Scene.
+
+Load a scene with animation and play it back.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Animated Scene.
+</title>
+<style type="text/css">
+.status { color: red; }
+</style>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.camera');
+o3djs.require('o3djs.pack');
+o3djs.require('o3djs.scene');
+
+// Events
+// init() once the page has finished loading.
+window.onload = init;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_viewInfo;
+var g_pack;
+var g_finished = false; // for selenium testing
+var g_clock = 0;
+var g_timeMult = 1;
+var g_animTimeParam;
+var g_animEndTime = 249 / 30; // 249 30hz frames.
+var g_loadInfo;
+var g_downloadPercent = -1;
+
+/**
+ * Sets the status message
+ * @param {string} msg The message.
+ */
+function setStatus(msg) {
+ var element = document.getElementById('status');
+ if (element) {
+ element.innerHTML = msg;
+ }
+}
+
+/**
+ * Loads a scene into the transform graph.
+ * @param {!o3d.Pack} pack Pack to load scene into.
+ * @param {string} fileName filename of the scene.
+ * @param {!o3d.Transform} parent parent node in the transform graph to
+ * which to load the scene into.
+ */
+function loadScene(pack, fileName, parent) {
+ // Get our full path to the scene
+ var scenePath = o3djs.util.getCurrentURI() + fileName;
+
+ // Load the file given the full path, and call the callback function
+ // when its done loading.
+ g_loadInfo = o3djs.scene.loadScene(
+ g_client, pack, parent, scenePath, callback,
+ { opt_animSource: g_animTimeParam});
+
+ /**
+ * Our callback is called once the scene has been loaded into memory
+ * from the web or locally.
+ * @param {!o3d.Pack} pack The pack that was passed in above.
+ * @param {!o3d.Transform} parent The parent that was passed in above.
+ * @param {*} exception null if loading succeeded.
+ */
+ function callback(pack, parent, exception) {
+ g_loadInfo = null;
+ if (exception) {
+ setStatus('could **not** load ' + fileName + '. ' + exception);
+ return;
+ }
+ // Get a CameraInfo (an object with a view and projection matrix)
+ // using our javascript library function
+ var cameraInfo = o3djs.camera.getViewAndProjectionFromCameras(
+ parent,
+ g_client.width,
+ g_client.height);
+ // Copy the view and projection to the draw context.
+ g_viewInfo.drawContext.view = cameraInfo.view;
+ g_viewInfo.drawContext.projection = cameraInfo.projection;
+
+ // Generate draw elements and setup material draw lists.
+ o3djs.pack.preparePack(pack, g_viewInfo);
+
+ // Reset the clock.
+ g_clock = 0;
+
+ setStatus('');
+
+ g_finished = true; // for selenium testing.
+ }
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and loads the scene into the transform graph.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = o3dElement.client;
+
+ // Creates a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Create a param to bind to the animation.
+ var paramObject = g_pack.createObject('ParamObject');
+ g_animTimeParam = paramObject.createParam('myClock', 'ParamFloat');
+
+ // Creates a transform to put our data on.
+ var myDataRoot = g_pack.createObject('Transform');
+
+ // Connects our root to the client root.
+ myDataRoot.parent = g_client.root;
+
+ // Load the scene into the transform graph as a child myDataRoot
+ loadScene(g_pack, 'assets/kitty_151_idle_stand05_cff1.o3dtgz', myDataRoot);
+
+ // Setup an onrender callback for animation.
+ g_client.setRenderCallback(onRender);
+}
+
+// update the animation clock
+function onRender(render_event) {
+ // Get the number of seconds since the last render.
+ var elapsedTime = render_event.elapsedTime;
+ g_clock += elapsedTime * g_timeMult;
+
+ // Repeat the animation over and over.
+ g_clock = g_clock % g_animEndTime;
+
+ // Set the time to display.
+ g_animTimeParam.value = g_clock;
+
+ if (g_loadInfo) {
+ var progressInfo = g_loadInfo.getKnownProgressInfoSoFar();
+ if (progressInfo.percent != g_downloadPercent) {
+ g_downloadPercent = progressInfo.percent;
+ setStatus('Loading... ' + progressInfo.percent + '%' +
+ ' (' + progressInfo.downloaded +
+ ' of ' + progressInfo.totalBytes + progressInfo.suffix + ')');
+ }
+ }
+}
+
+/**
+ * Removes any callbacks so they don't get called after the page has unloaded.
+ */
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+</script>
+</head>
+<body>
+<h1>Animated scene.</h1>
+Loads an scene with animation and plays it back.
+<div class="status" id="status">Loading...</div>
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 600px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/samples/animation.html b/o3d/samples/animation.html
new file mode 100644
index 0000000..d0af19d
--- /dev/null
+++ b/o3d/samples/animation.html
@@ -0,0 +1,411 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+O3D Animation.
+
+Shows various things being animated by O3D.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Animation.
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.effect');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+
+// Events
+// init() once the page has finished loading.
+window.onload = init;
+
+// global variables
+var g_framesRendered = 0;
+var g_o3d;
+var g_math;
+var g_client;
+var g_viewInfo;
+var g_pack;
+var g_finished = false; // for selenium
+var g_groupTransforms = [];
+var GROUPS_ACROSS = 2;
+var UNITS_ACROSS_GROUP = 2;
+var TOTAL_ACROSS = GROUPS_ACROSS * UNITS_ACROSS_GROUP;
+var HALF_WIDTH = TOTAL_ACROSS * 0.0;
+var UNIT_SPACING = 200;
+
+/**
+ * Creates an oscillating animation to animate a single float from 0 to
+ * endOutput over endInput seconds with a little bit of ease in, ease out.
+ *
+ * @param {!o3d.Pack} pack Pack to associate created objects with.
+ * @param {!o3d.ParamObject} paramObject Object that has param to animate.
+ * @param {string} paramName Name of the param to animate.
+ * @param {number} endInput Number of seconds to take to get
+ * @param {number} endOutput Target value.
+ */
+function attachParamFloatAnimation(pack,
+ paramObject,
+ paramName,
+ endInput,
+ endOutput) {
+ // Create a FunctionEval through which to evaluate the curve.
+ var functionEval = pack.createObject('FunctionEval');
+
+ // Bind the param we want to get its value from our FunctionEval's output.
+ paramObject.getParam(paramName).bind(functionEval.getParam('output'));
+
+ // Create a curve
+ var curve = pack.createObject('Curve');
+
+ // Set the functionEval to use the curve as it's function.
+ functionEval.functionObject = curve;
+
+ // Create 2 keys for the curve.
+ var key1 = curve.createKey('BezierCurveKey');
+ key1.input = 0;
+ key1.output = 0;
+ key1.outTangent = [0, endInput * 2 / 3];
+ var key2 = curve.createKey('BezierCurveKey');
+ key2.inTangent = [endInput / 3, endOutput];
+ key2.input = endInput;
+ key2.output = endOutput;
+
+ // Set the curve to oscillate.
+ curve.postInfinity = g_o3d.Curve.OSCILLATE;
+
+ // Make a SecondCounter to provide an input to the functionEval.
+ var counter = pack.createObject('SecondCounter');
+
+ // Bind the counter's count to the input of the FunctionEval.
+ functionEval.getParam('input').bind(counter.getParam('count'));
+}
+
+/**
+ * Creates an oscillating animation to animate a single float of a float4 from 0
+ * to endOutput over endInput seconds with a little bit of ease in, ease out.
+ *
+ * @param {!o3d.Pack} pack Pack to associate created objects with.
+ * @param {!o3d.ParamObject} paramObject Object that has param to animate.
+ * @param {string} paramName Name of the param Float4 to animate.
+ * @param {string} innerParamName Name of the individual float to animate.
+ * @param {number} endInput Duration of animation.
+ * @param {number} endOutput Target value.
+ * @return {!o3d.ParamObject} The created ParamOperation.
+ */
+function attach1FloatOfFloat4Animation(pack,
+ paramObject,
+ paramName,
+ innerParamName,
+ endInput,
+ endOutput) {
+
+ var paramOp = pack.createObject('ParamOp4FloatsToFloat4');
+ paramObject.getParam(paramName).bind(paramOp.getParam('output'));
+ attachParamFloatAnimation(pack, paramOp, innerParamName, endInput, endOutput);
+ return paramOp;
+}
+
+/**
+ * Creates an animation to animate one of the 9 values of a TRSToMatrix4 and
+ * binds it to a transform.
+ *
+ * @param {!o3d.Pack} pack Pack to associate created objects with.
+ * @param {!o3d.Transform} transform Transform to animate.
+ * @param {number} endInput Duration of animation.
+ * @param {number} endOutput Target value.
+ * @param {string} paramName Name of param to animate.
+ * @return {!o3d.TRSToMatrix4} The created TRSToMatrix4.
+ */
+function attachTRSAnimation(pack, transform, endInput, endOutput, paramName) {
+ var trs = pack.createObject('TRSToMatrix4');
+ transform.getParam('localMatrix').bind(trs.getParam('output'));
+ attachParamFloatAnimation(pack, trs, paramName, endInput, endOutput);
+ return trs;
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and sets up some shapes with animations.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = o3dElement.client;
+
+ // Creates a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Create our projection matrix, with a vertical field of view of 45
+ // degrees a near clipping plane of 0.1 and far clipping plane of 10000.
+ g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(45),
+ g_client.width / g_client.height,
+ 0.1,
+ 10000);
+
+ // Set our view
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [100, 50, 400], // Eye.
+ [0, 0, 0], // Target.
+ [0, 1, 0]); // Up.
+
+ var effect = g_pack.createObject('Effect');
+ effect.loadFromFXString(document.getElementById('shader').value);
+
+ // Create a Material for the effect.
+ var material = g_pack.createObject('Material');
+
+ // Apply our effect to this material.
+ material.effect = effect;
+
+ // Set the material's drawList for translucent objects.
+ material.drawList = g_viewInfo.zOrderedDrawList;
+
+ // Create the parameters the effect needs on the material.
+ effect.createUniformParameters(material);
+
+ // Set the light position
+ var light_pos_param = material.getParam('light_pos');
+ light_pos_param.value = [100, 75, 400];
+
+ // Set the phong components of the light source
+ var light_ambient_param = material.getParam('light_ambient');
+ var light_diffuse_param = material.getParam('light_diffuse');
+ var light_specular_param = material.getParam('light_specular');
+
+ light_ambient_param.value = [0.1, 0.1, 0.1, 1]; // Gray
+ light_diffuse_param.value = [1, 1, 1, 1]; // White
+ light_specular_param.value = [0.5, 0.5, 0.5, 1]; // White
+
+ // Set the shininess of the material (for specular lighting)
+ var shininess_param = material.getParam('shininess');
+ shininess_param.value = 5.0;
+
+ // Position of the camera.
+ // (should be the same as the 'eye' position given below)
+ var camera_pos_param = material.getParam('camera_pos');
+ // Camera is at (0, 0, 3).
+ camera_pos_param.value = [0, 0, 3];
+
+ var data = [ { paramName: 'translateY',
+ endOutput: 50,
+ color: [1, 0, 0, 1] },
+ { paramName: 'rotateY',
+ endOutput: Math.PI,
+ color: [0, 1, 0, 1] },
+ { paramName: 'scaleY',
+ endOutput: 3,
+ color: [1, 1, 0, 1] },
+ { paramName: '',
+ endOutput: Math.PI * 3,
+ color: [1, 1, 0, 1] } ];
+
+ for (var ii = 0; ii < 4; ++ii) {
+ var xPos = (ii - 1.5) * 100;
+ // Create a shape.
+ var shape;
+ switch (ii) {
+ case 0:
+ case 2:
+ shape = o3djs.primitives.createSphere(g_pack, material, 40, 10, 12);
+ break;
+ case 1:
+ case 3:
+ shape = o3djs.primitives.createCube(g_pack, material, 60);
+ break;
+ }
+
+ var transform = g_pack.createObject('Transform');
+ transform.parent = g_client.root;
+ transform.addShape(shape);
+
+ // Change the color of each one
+ transform.createParam('colorMult', 'ParamFloat4').value = data[ii].color;
+
+ switch (ii) {
+ case 0:
+ case 1:
+ case 2:
+ var trs = attachTRSAnimation(g_pack,
+ transform,
+ ii * 0.6 + 0.5,
+ data[ii].endOutput,
+ data[ii].paramName);
+
+ // space them out.
+ trs.translateX = xPos;
+ break;
+
+ case 3: {
+ var paramOp = attach1FloatOfFloat4Animation(g_pack,
+ transform,
+ 'colorMult',
+ 'input3',
+ 0.5,
+ 1);
+ paramOp.input1 = 1;
+ paramOp.input2 = 1;
+ transform.translate(xPos, 0, 0);
+ break;
+ }
+ }
+ }
+
+ g_finished = true; // for selenium
+}
+
+</script>
+</head>
+<body>
+<h1>Animation</h1>
+Once the scene is setup no Javascript is running.
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 800px; height: 600px"></div>
+<!-- End of O3D plugin -->
+<!-- Don't render the textarea -->
+<div style="display:none">
+<textarea id="shader" name="fx" cols="80" rows="20">
+// The 4x4 world view projection matrix.
+float4x4 worldViewProjection : WorldViewProjection;
+float4x4 worldInverseTranspose : WorldInverseTranspose;
+float4x4 world : World;
+
+// positions of the light and camera
+float3 light_pos;
+float3 camera_pos;
+
+// lighting components of the light source
+float4 light_ambient;
+float4 light_diffuse;
+float4 light_specular;
+
+// shininess of the material. (for specular lighting)
+float shininess;
+
+float4 colorMult;
+
+// input parameters for our vertex shader
+struct a2v {
+ float4 pos : POSITION;
+ float3 normal : NORMAL;
+ float4 col : COLOR;
+};
+
+// input parameters for our pixel shader
+// also the output parameters for our vertex shader
+struct v2f {
+ float4 pos : POSITION;
+ float4 pos2 : TEXCOORD0;
+ float3 norm : TEXCOORD1;
+ float3 light : TEXCOORD2;
+ float4 col : COLOR;
+};
+
+/**
+ * vsMain - our vertex shader
+ *
+ * @param IN.pos Position vector of vertex
+ * @param IN.normal Normal of vertex
+ * @param IN.col Color of vertex
+ */
+v2f vsMain(a2v IN) {
+ /**
+ * We use the standard phong illumination equation here.
+ * We restrict (clamp) the dot products so that we
+ * don't get any negative values.
+ * All vectors are normalized for proper calculations.
+ *
+ * The output color is the summation of the
+ * ambient, diffuse, and specular contributions.
+ *
+ * Note that we have to transform each vertex and normal
+ * by the view projection matrix first.
+ */
+ v2f OUT;
+
+ OUT.pos = mul(IN.pos, worldViewProjection);
+ OUT.pos2 = OUT.pos;
+ OUT.norm = mul(float4(IN.normal, 0), worldInverseTranspose).xyz;
+ OUT.light = light_pos - mul(IN.pos, world).xyz;
+ OUT.col = IN.col;
+ return OUT;
+}
+/**
+ * psMain - pixel shader
+ *
+ * @param IN.pos Position vector of vertex
+ * @param IN.col Color of vertex
+ */
+float4 psMain(v2f IN): COLOR {
+ float3 light = normalize(IN.light);
+ float3 normal = normalize(IN.norm);
+ float3 r = normalize(reflect(normal, light));
+ float3 v = normalize(IN.pos2.xyz);
+ float4 litR = lit(dot(normal, light), dot(r, v), shininess);
+ return float4(((light_ambient + light_diffuse * litR.y) * colorMult +
+ light_specular * litR.z).xyz, colorMult.w);
+}
+
+// Here we tell our effect file *which* functions are
+// our vertex and pixel shaders.
+
+// #o3d VertexShaderEntryPoint vsMain
+// #o3d PixelShaderEntryPoint psMain
+// #o3d MatrixLoadOrder RowMajor
+</textarea>
+</div>
+</body>
+</html>
diff --git a/o3d/samples/archive-textures.html b/o3d/samples/archive-textures.html
new file mode 100644
index 0000000..00ca148
--- /dev/null
+++ b/o3d/samples/archive-textures.html
@@ -0,0 +1,297 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+
+In this tutorial, we show how to progressively load in textures from a tar.gz archive.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Progressive Texture Loading
+</title>
+<script type="text/javascript" src="o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.io');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.effect');
+
+// Events
+// Run the init() once the page has finished loading.
+window.onload = init;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_pack;
+var g_viewInfo;
+var g_finished = false; // for selenium testing
+var g_repeatDownload = false; // for selenium testing
+var g_streamingStarted = false;
+var g_fileCount = 0;
+var g_textureCount = 0;
+
+// This is for offsetting our textures from each other as they arrive
+var g_x = -200.0;
+var g_y = -200.0;
+
+function makeShape(texture, effect) {
+ // Create a Material for the effect.
+ var myMaterial = g_pack.createObject('Material');
+
+ // Set the material's drawList for opaque objects.
+ myMaterial.drawList = g_viewInfo.performanceDrawList;
+
+ // Apply our effect to this material.
+ myMaterial.effect = effect;
+
+ // Create the parameters the effect needs on the material.
+ effect.createUniformParameters(myMaterial);
+
+ // Creates a quad.
+ var myShape = o3djs.primitives.createPlane(g_pack,
+ myMaterial,
+ 300, // width
+ 300, // height
+ 1, // quads across
+ 1); // quads down
+
+ // Get the material's sampler parameter and put a sampler on it.
+ var sampler_param = myMaterial.getParam('texSampler0');
+ var sampler = g_pack.createObject('Sampler');
+ sampler_param.value = sampler;
+
+ // Set the texture to use.
+ sampler.texture = texture;
+
+ // adjust the scale of our transform to match the aspect ratio of
+ // the texture. Of course we could also have waited until now to build
+ // our plane and set its width and height to match instead of scaling
+ // here.
+ var textureWidth = texture.width;
+ var textureHeight = texture.height;
+ var hScale = 1;
+ var vScale = 1;
+ if (textureWidth > textureHeight) {
+ vScale = textureHeight / textureWidth;
+ } else if (textureHeight > textureWidth) {
+ hScale = textureWidth / textureHeight;
+ }
+ // We now attach our quad to the root of the transform graph.
+ // We do this after the texture has loaded, otherwise we'd be attempting
+ // to display something invalid.
+
+ // Make a transform for each quad.
+ var transform = g_pack.createObject('Transform');
+
+ transform.translate(g_x, g_textureCount, g_y);
+ transform.scale(hScale, 1, vScale);
+ transform.addShape(myShape);
+ transform.parent = g_client.root;
+
+ // Diagonally offset the next texture we load
+ g_x += 5.0;
+ g_y += 5.0;
+
+ return myShape;
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Spawns a raw-data request, creating a textured quad for each true
+ * in the downloaded archive_textures.tar.gz file.
+ * @param {Object} effect O3D effect object for created quads.
+ */
+function createArchiveRequest(effect) {
+ // Start a request for loading the tar.gz archive containing a bunch of
+ // image files. We'll then make textures from each one...
+ var loadInfo = o3djs.io.loadArchiveAdvanced(
+ g_pack,
+ './assets/archive_textures.o3dtgz',
+ onFileAvailable,
+ function(request, exception) {
+ // We are finished with the request so remove it.
+ g_pack.removeObject(request);
+
+ // Check for errors.
+ if (!exception) {
+ // When run in selenium this sample will continuously spawn a data
+ // request so that it can verify that a page refresh during a
+ // download does not crash or hang the browser.
+ if (g_repeatDownload) {
+ createArchiveRequest(effect);
+ }
+ g_finished = true;
+ } else {
+ alert('Failed to load archive!\n' + exception);
+ }
+ });
+
+ // This is called for each file in the archive as it arrives
+ function onFileAvailable(rawData) {
+ g_streamingStarted = true;
+
+ // Create a texture from the RawData object that was just made available.
+ var texture = g_pack.createTextureFromRawData(rawData, true);
+
+ // Free the raw data object immediately since we're done with it.
+ // If we don't call this the RawData will stay around so we can use it
+ // later. For this app we won't need it later so we discard it now.
+ rawData.discard();
+
+ // Verify that the texture was created correctly and count it if so...
+ if (texture) g_textureCount++; // for selenium testing
+
+ // Display progress information.
+ var n = loadInfo.getTotalBytesDownloaded();
+ var streamLength = loadInfo.getTotalKnownBytesToStreamSoFar();
+
+ var progressInfo = n + ' bytes of ' + streamLength;
+ var fileInfo = 'file name = ' + rawData.uri
+ + ' :: length = ' + rawData.length + ' bytes';
+
+ var info = document.getElementById('info');
+
+ var textureInfo = 'texture = ' + texture.width + 'x' + texture.height;
+
+ // If the page is reloaded before all of the textures have been created,
+ // IE will still invoke this call-back (even after the 'info' element has
+ // been removed).
+ if (info) {
+ info.innerHTML = info.innerHTML + progressInfo + " :: " + fileInfo +
+ " :: " + textureInfo + "<br>";
+ }
+
+ g_fileCount++;
+
+ if (texture) {
+ var shape = makeShape(texture, effect);
+ }
+ }
+}
+
+/**
+ * Initializes the O3D global variables.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initialize global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = o3dElement.client;
+}
+
+/**
+ * Loads the O3D effect, and loads a tar.gz archive containing a bunch of image
+ * files. We'll create textures from them as they come in.
+ */
+function startLoad() {
+ // Disallow streaming of the content more than once.
+ document.getElementById("startLoad").disabled = true;
+ var clearId = window.setInterval(function() {
+ // Wait until the page has finished loading, and the client O3D areas have
+ // been created.
+ if (!g_client) {
+ return;
+ } else {
+ window.clearInterval(clearId);
+ }
+
+ // Create a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Set up an orthographic projection.
+ g_viewInfo.drawContext.projection = g_math.matrix4.orthographic(
+ -g_client.width * 0.5,
+ g_client.width * 0.5,
+ -g_client.height * 0.5,
+ g_client.height * 0.5,
+ 0.1,
+ 1000);
+
+ // Move the camera above the origin looking down.
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [0, 500, 0], // eye
+ [0, 0, 0], // target
+ [0, 0, -1]); // up
+
+ // Create and load the effect.
+ var effect = g_pack.createObject('Effect');
+ o3djs.effect.loadEffect(effect, 'shaders/texture-only.shader');
+
+ createArchiveRequest(effect);
+ }, 200);
+}
+
+</script>
+</head>
+<body>
+<h1>Progressive archive loading</h1>
+This tutorial shows how to load textures progressively from a compressed archive.
+<br><br/>
+
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 600px; height: 600px"></div>
+<!-- End of O3D plugin -->
+
+<br><br/>
+
+<!-- This div is for logging the progress of the progressive loading -->
+<div>
+<input type="button" id="startLoad" value="Start Loading" onclick="startLoad()" />
+</div>
+<div id="info">
+ <h1>File Loading Progress</h1>
+</div>
+
+
+</body>
+</html>
diff --git a/o3d/samples/assets/android.png b/o3d/samples/assets/android.png
new file mode 100644
index 0000000..8ffbc61
--- /dev/null
+++ b/o3d/samples/assets/android.png
Binary files differ
diff --git a/o3d/samples/assets/archive_textures.o3dtgz b/o3d/samples/assets/archive_textures.o3dtgz
new file mode 100644
index 0000000..1d1cf35
--- /dev/null
+++ b/o3d/samples/assets/archive_textures.o3dtgz
Binary files differ
diff --git a/o3d/samples/assets/block.png b/o3d/samples/assets/block.png
new file mode 100644
index 0000000..b0e45c6
--- /dev/null
+++ b/o3d/samples/assets/block.png
Binary files differ
diff --git a/o3d/samples/assets/brush.png b/o3d/samples/assets/brush.png
new file mode 100644
index 0000000..6e9079f
--- /dev/null
+++ b/o3d/samples/assets/brush.png
Binary files differ
diff --git a/o3d/samples/assets/egg.png b/o3d/samples/assets/egg.png
new file mode 100644
index 0000000..caa2718
--- /dev/null
+++ b/o3d/samples/assets/egg.png
Binary files differ
diff --git a/o3d/samples/assets/empty.txt b/o3d/samples/assets/empty.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/o3d/samples/assets/empty.txt
diff --git a/o3d/samples/assets/fullscreen.png b/o3d/samples/assets/fullscreen.png
new file mode 100644
index 0000000..27a20c2
--- /dev/null
+++ b/o3d/samples/assets/fullscreen.png
Binary files differ
diff --git a/o3d/samples/assets/gauge.png b/o3d/samples/assets/gauge.png
new file mode 100644
index 0000000..4813779
--- /dev/null
+++ b/o3d/samples/assets/gauge.png
Binary files differ
diff --git a/o3d/samples/assets/gaugeback.png b/o3d/samples/assets/gaugeback.png
new file mode 100644
index 0000000..5d713f0
--- /dev/null
+++ b/o3d/samples/assets/gaugeback.png
Binary files differ
diff --git a/o3d/samples/assets/gears_init.js b/o3d/samples/assets/gears_init.js
new file mode 100644
index 0000000..5f44f09b
--- /dev/null
+++ b/o3d/samples/assets/gears_init.js
@@ -0,0 +1,86 @@
+// Copyright 2007, Google Inc.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// 3. Neither the name of Google Inc. nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Sets up google.gears.*, which is *the only* supported way to access Gears.
+//
+// Circumvent this file at your own risk!
+//
+// In the future, Gears may automatically define google.gears.* without this
+// file. Gears may use these objects to transparently fix bugs and compatibility
+// issues. Applications that use the code below will continue to work seamlessly
+// when that happens.
+
+(function() {
+ // We are already defined. Hooray!
+ if (window.google && google.gears) {
+ return;
+ }
+
+ var factory = null;
+
+ // Firefox
+ if (typeof GearsFactory != 'undefined') {
+ factory = new GearsFactory();
+ } else {
+ // IE
+ try {
+ factory = new ActiveXObject('Gears.Factory');
+ // privateSetGlobalObject is only required and supported on WinCE.
+ if (factory.getBuildInfo().indexOf('ie_mobile') != -1) {
+ factory.privateSetGlobalObject(this);
+ }
+ } catch (e) {
+ // Safari
+ if ((typeof navigator.mimeTypes != 'undefined')
+ && navigator.mimeTypes["application/x-googlegears"]) {
+ factory = document.createElement("object");
+ factory.style.display = "none";
+ factory.width = 0;
+ factory.height = 0;
+ factory.type = "application/x-googlegears";
+ document.documentElement.appendChild(factory);
+ }
+ }
+ }
+
+ // *Do not* define any objects if Gears is not installed. This mimics the
+ // behavior of Gears defining the objects in the future.
+ if (!factory) {
+ return;
+ }
+
+ // Now set up the objects, being careful not to overwrite anything.
+ //
+ // Note: In Internet Explorer for Windows Mobile, you can't add properties to
+ // the window object. However, global objects are automatically added as
+ // properties of the window object in all browsers.
+ if (!window.google) {
+ google = {};
+ }
+
+ if (!google.gears) {
+ google.gears = {factory: factory};
+ }
+})();
diff --git a/o3d/samples/assets/google-square.png b/o3d/samples/assets/google-square.png
new file mode 100644
index 0000000..4917f34
--- /dev/null
+++ b/o3d/samples/assets/google-square.png
Binary files differ
diff --git a/o3d/samples/assets/iconback.png b/o3d/samples/assets/iconback.png
new file mode 100644
index 0000000..28ccf80
--- /dev/null
+++ b/o3d/samples/assets/iconback.png
Binary files differ
diff --git a/o3d/samples/assets/normalmap.dds b/o3d/samples/assets/normalmap.dds
new file mode 100644
index 0000000..ed1fdd7
--- /dev/null
+++ b/o3d/samples/assets/normalmap.dds
Binary files differ
diff --git a/o3d/samples/assets/one-pixel-white.tga b/o3d/samples/assets/one-pixel-white.tga
new file mode 100644
index 0000000..fa26977
--- /dev/null
+++ b/o3d/samples/assets/one-pixel-white.tga
Binary files differ
diff --git a/o3d/samples/assets/orange-flower.png b/o3d/samples/assets/orange-flower.png
new file mode 100644
index 0000000..6b68390
--- /dev/null
+++ b/o3d/samples/assets/orange-flower.png
Binary files differ
diff --git a/o3d/samples/assets/particle-anim.png b/o3d/samples/assets/particle-anim.png
new file mode 100644
index 0000000..e78cfe7
--- /dev/null
+++ b/o3d/samples/assets/particle-anim.png
Binary files differ
diff --git a/o3d/samples/assets/pillar.png b/o3d/samples/assets/pillar.png
new file mode 100644
index 0000000..11fe2a4
--- /dev/null
+++ b/o3d/samples/assets/pillar.png
Binary files differ
diff --git a/o3d/samples/assets/purple-flower.png b/o3d/samples/assets/purple-flower.png
new file mode 100644
index 0000000..1c1c24c
--- /dev/null
+++ b/o3d/samples/assets/purple-flower.png
Binary files differ
diff --git a/o3d/samples/assets/radar.png b/o3d/samples/assets/radar.png
new file mode 100644
index 0000000..113ca84
--- /dev/null
+++ b/o3d/samples/assets/radar.png
Binary files differ
diff --git a/o3d/samples/assets/ripple.png b/o3d/samples/assets/ripple.png
new file mode 100644
index 0000000..78c2d76
--- /dev/null
+++ b/o3d/samples/assets/ripple.png
Binary files differ
diff --git a/o3d/samples/assets/rock_bumps.jpg b/o3d/samples/assets/rock_bumps.jpg
new file mode 100644
index 0000000..0744fe4
--- /dev/null
+++ b/o3d/samples/assets/rock_bumps.jpg
Binary files differ
diff --git a/o3d/samples/assets/rock_texture.jpg b/o3d/samples/assets/rock_texture.jpg
new file mode 100644
index 0000000..d298941
--- /dev/null
+++ b/o3d/samples/assets/rock_texture.jpg
Binary files differ
diff --git a/o3d/samples/assets/shaving_cream.jpg b/o3d/samples/assets/shaving_cream.jpg
new file mode 100644
index 0000000..0d2bd09
--- /dev/null
+++ b/o3d/samples/assets/shaving_cream.jpg
Binary files differ
diff --git a/o3d/samples/assets/shaving_cream.png b/o3d/samples/assets/shaving_cream.png
new file mode 100644
index 0000000..9d9b4b0
--- /dev/null
+++ b/o3d/samples/assets/shaving_cream.png
Binary files differ
diff --git a/o3d/samples/assets/square.png b/o3d/samples/assets/square.png
new file mode 100644
index 0000000..fe6ca68
--- /dev/null
+++ b/o3d/samples/assets/square.png
Binary files differ
diff --git a/o3d/samples/assets/teapot_vertices.js b/o3d/samples/assets/teapot_vertices.js
new file mode 100644
index 0000000..5e31812
--- /dev/null
+++ b/o3d/samples/assets/teapot_vertices.js
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+var g_teapotVertices = [17.8349, 0, 30.574, 17.586, 0, 31.5146, 17.8747, 0, 31.8281, 18.4619, 0, 31.5146, 19.1088, 0, 30.574, 16.4527, -7.00018, 30.574, 16.2231, -6.90252, 31.5146, 16.4894, -7.01581, 31.8281, 17.0311, -7.24628, 31.5146, 17.6278, -7.5002, 30.574, 12.6627, -12.6627, 30.574, 12.4861, -12.4861, 31.5146, 12.691, -12.691, 31.8281, 13.1079, -13.1079, 31.5146, 13.5672, -13.5672, 30.574, 7.00018, -16.4527, 30.574, 6.90252, -16.2231, 31.5146, 7.01581, -16.4894, 31.8281, 7.24628, -17.0311, 31.5146, 7.5002, -17.6278, 30.574, 0, -17.8349, 30.574, 0, -17.586, 31.5146, 0, -17.8747, 31.8281, 0, -18.4619, 31.5146, 0, -19.1088, 30.574, -7.48387, -16.4527, 30.574, -7.10658, -16.2231, 31.5146, -7.07627, -16.4894, 31.8281, -7.25384, -17.0311, 31.5146, -7.5002, -17.6278, 30.574, -13.0927, -12.6627, 30.574, -12.6675, -12.4861, 31.5146, -12.7448, -12.691, 31.8281, -13.1146, -13.1079, 31.5146, -13.5672, -13.5672, 30.574, -16.6139, -7.00018, 30.574, -16.2911, -6.90252, 31.5146, -16.5095, -7.01581, 31.8281, -17.0336, -7.24628, 31.5146, -17.6278, -7.5002, 30.574, -17.8349, 0, 30.574, -17.586, 0, 31.5146, -17.8747, 0, 31.8281, -18.4619, 0, 31.5146, -19.1088, 0, 30.574, -16.4527, 7.00018, 30.574, -16.2231, 6.90252, 31.5146, -16.4894, 7.01581, 31.8281, -17.0311, 7.24628, 31.5146, -17.6278, 7.5002, 30.574, -12.6627, 12.6627, 30.574, -12.4861, 12.4861, 31.5146, -12.691, 12.691, 31.8281, -13.1079, 13.1079, 31.5146, -13.5672, 13.5672, 30.574, -7.00018, 16.4527, 30.574, -6.90252, 16.2231, 31.5146, -7.01581, 16.4894, 31.8281, -7.24628, 17.0311, 31.5146, -7.5002, 17.6278, 30.574, 0, 17.8349, 30.574, 0, 17.586, 31.5146, 0, 17.8747, 31.8281, 0, 18.4619, 31.5146, 0, 19.1088, 30.574, 7.00018, 16.4527, 30.574, 6.90252, 16.2231, 31.5146, 7.01581, 16.4894, 31.8281, 7.24628, 17.0311, 31.5146, 7.5002, 17.6278, 30.574, 12.6627, 12.6627, 30.574, 12.4861, 12.4861, 31.5146, 12.691, 12.691, 31.8281, 13.1079, 13.1079, 31.5146, 13.5672, 13.5672, 30.574, 16.4527, 7.00018, 30.574, 16.2231, 6.90252, 31.5146, 16.4894, 7.01581, 31.8281, 17.0311, 7.24628, 31.5146, 17.6278, 7.5002, 30.574, 21.4476, 0, 25.5729, 23.4879, 0, 20.6614, 24.931, 0, 15.929, 25.4784, 0, 11.4653, 19.7854, -8.41819, 25.5729, 21.6676, -9.21899, 20.6614, 22.9988, -9.78541, 15.929, 23.5038, -10.0003, 11.4653, 15.2278, -15.2278, 25.5729, 16.6764, -16.6764, 20.6614, 17.701, -17.701, 15.929, 18.0896, -18.0896, 11.4653, 8.41819, -19.7854, 25.5729, 9.21899, -21.6676, 20.6614, 9.78541, -22.9988, 15.929, 10.0003, -23.5038, 11.4653, 0, -21.4476, 25.5729, 0, -23.4879, 20.6614, 0, -24.931, 15.929, 0, -25.4784, 11.4653, -8.41819, -19.7854, 25.5729, -9.21899, -21.6676, 20.6614, -9.78541, -22.9988, 15.929, -10.0003, -23.5038, 11.4653, -15.2278, -15.2278, 25.5729, -16.6764, -16.6764, 20.6614, -17.701, -17.701, 15.929, -18.0896, -18.0896, 11.4653, -19.7854, -8.41819, 25.5729, -21.6676, -9.21899, 20.6614, -22.9988, -9.78541, 15.929, -23.5038, -10.0003, 11.4653, -21.4476, 0, 25.5729, -23.4879, 0, 20.6614, -24.931, 0, 15.929, -25.4784, 0, 11.4653, -19.7854, 8.41819, 25.5729, -21.6676, 9.21899, 20.6614, -22.9988, 9.78541, 15.929, -23.5038, 10.0003, 11.4653, -15.2278, 15.2278, 25.5729, -16.6764, 16.6764, 20.6614, -17.701, 17.701, 15.929, -18.0896, 18.0896, 11.4653, -8.41819, 19.7854, 25.5729, -9.21899, 21.6676, 20.6614, -9.78541, 22.9988, 15.929, -10.0003, 23.5038, 11.4653, 0, 21.4476, 25.5729, 0, 23.4879, 20.6614, 0, 24.931, 15.929, 0, 25.4784, 11.4653, 8.41819, 19.7854, 25.5729, 9.21899, 21.6676, 20.6614, 9.78541, 22.9988, 15.929, 10.0003, 23.5038, 11.4653, 15.2278, 15.2278, 25.5729, 16.6764, 16.6764, 20.6614, 17.701, 17.701, 15.929, 18.0896, 18.0896, 11.4653, 19.7854, 8.41819, 25.5729, 21.6676, 9.21899, 20.6614, 22.9988, 9.78541, 15.929, 23.5038, 10.0003, 11.4653, 24.4831, 0, 7.6883, 22.2936, 0, 4.89662, 20.104, 0, 3.00067, 19.1088, 0, 1.91088, 22.5857, -9.60962, 7.6883, 20.5658, -8.75023, 4.89662, 18.546, -7.89083, 3.00067, 17.6278, -7.5002, 1.91088, 17.383, -17.383, 7.6883, 15.8284, -15.8284, 4.89662, 14.2739, -14.2739, 3.00067, 13.5672, -13.5672, 1.91088, 9.60962, -22.5857, 7.6883, 8.75023, -20.5658, 4.89662, 7.89083, -18.546, 3.00067, 7.5002, -17.6278, 1.91088, 0, -24.4831, 7.6883, 0, -22.2936, 4.89662, 0, -20.104, 3.00067, 0, -19.1088, 1.91088, -9.60962, -22.5857, 7.6883, -8.75023, -20.5658, 4.89662, -7.89083, -18.546, 3.00067, -7.5002, -17.6278, 1.91088, -17.383, -17.383, 7.6883, -15.8284, -15.8284, 4.89662, -14.2739, -14.2739, 3.00067, -13.5672, -13.5672, 1.91088, -22.5857, -9.60962, 7.6883, -20.5658, -8.75023, 4.89662, -18.546, -7.89083, 3.00067, -17.6278, -7.5002, 1.91088, -24.4831, 0, 7.6883, -22.2936, 0, 4.89662, -20.104, 0, 3.00067, -19.1088, 0, 1.91088, -22.5857, 9.60962, 7.6883, -20.5658, 8.75023, 4.89662, -18.546, 7.89083, 3.00067, -17.6278, 7.5002, 1.91088, -17.383, 17.383, 7.6883, -15.8284, 15.8284, 4.89662, -14.2739, 14.2739, 3.00067, -13.5672, 13.5672, 1.91088, -9.60962, 22.5857, 7.6883, -8.75023, 20.5658, 4.89662, -7.89083, 18.546, 3.00067, -7.5002, 17.6278, 1.91088, 0, 24.4831, 7.6883, 0, 22.2936, 4.89662, 0, 20.104, 3.00067, 0, 19.1088, 1.91088, 9.60962, 22.5857, 7.6883, 8.75023, 20.5658, 4.89662, 7.89083, 18.546, 3.00067, 7.5002, 17.6278, 1.91088, 17.383, 17.383, 7.6883, 15.8284, 15.8284, 4.89662, 14.2739, 14.2739, 3.00067, 13.5672, 13.5672, 1.91088, 22.5857, 9.60962, 7.6883, 20.5658, 8.75023, 4.89662, 18.546, 7.89083, 3.00067, 17.6278, 7.5002, 1.91088, 18.6758, 0, 1.20923, 16.3619, 0, 0.597149, 10.6442, 0, 0.164216, 0, 0, 0, 17.2285, -7.33027, 1.20923, 15.0938, -6.42204, 0.597149, 9.81926, -4.17784, 0.164216, 13.2598, -13.2598, 1.20923, 11.6169, -11.6169, 0.597149, 7.55737, -7.55737, 0.164216, 7.33027, -17.2285, 1.20923, 6.42204, -15.0938, 0.597149, 4.17784, -9.81926, 0.164216, 0, -18.6758, 1.20923, 0, -16.3619, 0.597149, 0, -10.6442, 0.164216, -7.33027, -17.2285, 1.20923, -6.42204, -15.0938, 0.597149, -4.17784, -9.81926, 0.164216, -13.2598, -13.2598, 1.20923, -11.6169, -11.6169, 0.597149, -7.55737, -7.55737, 0.164216, -17.2285, -7.33027, 1.20923, -15.0938, -6.42204, 0.597149, -9.81926, -4.17784, 0.164216, -18.6758, 0, 1.20923, -16.3619, 0, 0.597149, -10.6442, 0, 0.164216, -17.2285, 7.33027, 1.20923, -15.0938, 6.42204, 0.597149, -9.81926, 4.17784, 0.164216, -13.2598, 13.2598, 1.20923, -11.6169, 11.6169, 0.597149, -7.55737, 7.55737, 0.164216, -7.33027, 17.2285, 1.20923, -6.42204, 15.0938, 0.597149, -4.17784, 9.81926, 0.164216, 0, 18.6758, 1.20923, 0, 16.3619, 0.597149, 0, 10.6442, 0.164216, 7.33027, 17.2285, 1.20923, 6.42204, 15.0938, 0.597149, 4.17784, 9.81926, 0.164216, 13.2598, 13.2598, 1.20923, 11.6169, 11.6169, 0.597149, 7.55737, 7.55737, 0.164216, 17.2285, 7.33027, 1.20923, 15.0938, 6.42204, 0.597149, 9.81926, 4.17784, 0.164216, -20.3827, 0, 25.7969, -26.3343, 0, 25.7521, -30.7333, 0, 25.4386, -33.4603, 0, 24.5876, -34.3958, 0, 22.9305, -20.1836, -2.14974, 26.2447, -26.5116, -2.14974, 26.1929, -31.1563, -2.14974, 25.8304, -34.017, -2.14974, 24.8465, -34.9929, -2.14974, 22.9305, -19.7457, -2.86632, 27.23, -26.9016, -2.86632, 27.1628, -32.0868, -2.86632, 26.6926, -35.2418, -2.86632, 25.4162, -36.3067, -2.86632, 22.9305, -19.3078, -2.14974, 28.2153, -27.2916, -2.14974, 28.1327, -33.0174, -2.14974, 27.5547, -36.4665, -2.14974, 25.9858, -37.6204, -2.14974, 22.9305, -19.1088, 0, 28.6632, -27.4689, 0, 28.5736, -33.4404, 0, 27.9466, -37.0233, 0, 26.2447, -38.2176, 0, 22.9305, -19.3078, 2.14974, 28.2153, -27.2916, 2.14974, 28.1327, -33.0174, 2.14974, 27.5547, -36.4665, 2.14974, 25.9858, -37.6204, 2.14974, 22.9305, -19.7457, 2.86632, 27.23, -26.9016, 2.86632, 27.1628, -32.0868, 2.86632, 26.6926, -35.2418, 2.86632, 25.4162, -36.3067, 2.86632, 22.9305, -20.1836, 2.14974, 26.2447, -26.5116, 2.14974, 26.1929, -31.1563, 2.14974, 25.8304, -34.017, 2.14974, 24.8465, -34.9929, 2.14974, 22.9305, -33.8982, 0, 20.3329, -32.3257, 0, 17.1979, -29.5589, 0, 14.0629, -25.4784, 0, 11.4653, -34.4409, -2.14974, 20.0822, -32.7113, -2.14974, 16.8153, -29.6942, -2.14974, 13.5905, -25.2793, -2.14974, 10.8681, -35.6349, -2.86632, 19.5305, -33.5598, -2.86632, 15.9737, -29.9918, -2.86632, 12.5513, -24.8414, -2.86632, 9.55439, -36.8289, -2.14974, 18.9788, -34.4082, -2.14974, 15.1321, -30.2895, -2.14974, 11.5122, -24.4035, -2.14974, 8.24066, -37.3716, 0, 18.7281, -34.7939, 0, 14.7496, -30.4248, 0, 11.0398, -24.2045, 0, 7.64351, -36.8289, 2.14974, 18.9788, -34.4082, 2.14974, 15.1321, -30.2895, 2.14974, 11.5122, -24.4035, 2.14974, 8.24066, -35.6349, 2.86632, 19.5305, -33.5598, 2.86632, 15.9737, -29.9918, 2.86632, 12.5513, -24.8414, 2.86632, 9.55439, -34.4409, 2.14974, 20.0822, -32.7113, 2.14974, 16.8153, -29.6942, 2.14974, 13.5905, -25.2793, 2.14974, 10.8681, 21.6566, 0, 18.1533, 27.7674, 0, 19.5566, 30.4148, 0, 22.9305, 31.8679, 0, 27.021, 34.3958, 0, 30.574, 21.6566, -4.72942, 16.5112, 28.234, -4.27036, 18.339, 31.0119, -3.26044, 22.2214, 32.5956, -2.25051, 26.7644, 35.5901, -1.79145, 30.574, 21.6566, -6.3059, 12.8984, 29.2603, -5.69382, 15.6602, 32.3257, -4.34725, 20.6614, 34.1967, -3.00067, 26.1999, 38.2176, -2.3886, 30.574, 21.6566, -4.72942, 9.28567, 30.2867, -4.27036, 12.9815, 33.6394, -3.26044, 19.1013, 35.7979, -2.25051, 25.6354, 40.845, -1.79145, 30.574, 21.6566, 0, 7.64351, 30.7532, 0, 11.7638, 34.2366, 0, 18.3922, 36.5256, 0, 25.3788, 42.0393, 0, 30.574, 21.6566, 4.72942, 9.28567, 30.2867, 4.27036, 12.9815, 33.6394, 3.26044, 19.1013, 35.7979, 2.25051, 25.6354, 40.845, 1.79145, 30.574, 21.6566, 6.3059, 12.8984, 29.2603, 5.69382, 15.6602, 32.3257, 4.34725, 20.6614, 34.1967, 3.00067, 26.1999, 38.2176, 2.3886, 30.574, 21.6566, 4.72942, 16.5112, 28.234, 4.27036, 18.339, 31.0119, 3.26044, 22.2214, 32.5956, 2.25051, 26.7644, 35.5901, 1.79145, 30.574, 35.3114, 0, 31.1115, 35.9882, 0, 31.2906, 36.1872, 0, 31.1115, 35.6697, 0, 30.574, 36.5905, -1.67948, 31.1377, 37.1887, -1.43316, 31.3326, 37.2066, -1.18683, 31.1482, 36.4659, -1.07487, 30.574, 39.4044, -2.23931, 31.1955, 39.8299, -1.91088, 31.425, 39.4492, -1.58245, 31.229, 38.2176, -1.43316, 30.574, 42.2183, -1.67948, 31.2532, 42.471, -1.43316, 31.5174, 41.6917, -1.18683, 31.3099, 39.9692, -1.07487, 30.574, 43.4973, 0, 31.2794, 43.6715, 0, 31.5593, 42.7111, 0, 31.3466, 40.7654, 0, 30.574, 42.2183, 1.67948, 31.2532, 42.471, 1.43316, 31.5174, 41.6917, 1.18683, 31.3099, 39.9692, 1.07487, 30.574, 39.4044, 2.23931, 31.1955, 39.8299, 1.91088, 31.425, 39.4492, 1.58245, 31.229, 38.2176, 1.43316, 30.574, 36.5905, 1.67948, 31.1377, 37.1887, 1.43316, 31.3326, 37.2066, 1.18683, 31.1482, 36.4659, 1.07487, 30.574, 0, 0, 40.1284, 4.33928, 0, 39.5014, 4.14023, 0, 37.9787, 2.50803, 0, 36.0977, 2.54784, 0, 34.3958, 4.0045, -1.7077, 39.5014, 3.82071, -1.62907, 37.9787, 2.31416, -0.985912, 36.0977, 2.35038, -1.00003, 34.3958, 3.08492, -3.08492, 39.5014, 2.94315, -2.94315, 37.9787, 1.78204, -1.78204, 36.0977, 1.80896, -1.80896, 34.3958, 1.7077, -4.0045, 39.5014, 1.62907, -3.82071, 37.9787, 0.985912, -2.31416, 36.0977, 1.00003, -2.35038, 34.3958, 0, -4.33928, 39.5014, 0, -4.14023, 37.9787, 0, -2.50803, 36.0977, 0, -2.54784, 34.3958, -1.7077, -4.0045, 39.5014, -1.62907, -3.82071, 37.9787, -0.985912, -2.31416, 36.0977, -1.00003, -2.35038, 34.3958, -3.08492, -3.08492, 39.5014, -2.94315, -2.94315, 37.9787, -1.78204, -1.78204, 36.0977, -1.80896, -1.80896, 34.3958, -4.0045, -1.7077, 39.5014, -3.82071, -1.62907, 37.9787, -2.31416, -0.985912, 36.0977, -2.35038, -1.00003, 34.3958, -4.33928, 0, 39.5014, -4.14023, 0, 37.9787, -2.50803, 0, 36.0977, -2.54784, 0, 34.3958, -4.0045, 1.7077, 39.5014, -3.82071, 1.62907, 37.9787, -2.31416, 0.985912, 36.0977, -2.35038, 1.00003, 34.3958, -3.08492, 3.08492, 39.5014, -2.94315, 2.94315, 37.9787, -1.78204, 1.78204, 36.0977, -1.80896, 1.80896, 34.3958, -1.7077, 4.0045, 39.5014, -1.62907, 3.82071, 37.9787, -0.985912, 2.31416, 36.0977, -1.00003, 2.35038, 34.3958, 0, 4.33928, 39.5014, 0, 4.14023, 37.9787, 0, 2.50803, 36.0977, 0, 2.54784, 34.3958, 1.7077, 4.0045, 39.5014, 1.62907, 3.82071, 37.9787, 0.985912, 2.31416, 36.0977, 1.00003, 2.35038, 34.3958, 3.08492, 3.08492, 39.5014, 2.94315, 2.94315, 37.9787, 1.78204, 1.78204, 36.0977, 1.80896, 1.80896, 34.3958, 4.0045, 1.7077, 39.5014, 3.82071, 1.62907, 37.9787, 2.31416, 0.985912, 36.0977, 2.35038, 1.00003, 34.3958, 5.81225, 0, 33.2612, 10.5098, 0, 32.4849, 14.7297, 0, 31.7086, 16.5609, 0, 30.574, 5.3618, -2.28131, 33.2612, 9.69532, -4.12511, 32.4849, 13.5881, -5.7814, 31.7086, 15.2775, -6.50017, 30.574, 4.1267, -4.1267, 33.2612, 7.46198, -7.46198, 32.4849, 10.4581, -10.4581, 31.7086, 11.7583, -11.7583, 30.574, 2.28131, -5.3618, 33.2612, 4.12511, -9.69532, 32.4849, 5.7814, -13.5881, 31.7086, 6.50017, -15.2775, 30.574, 0, -5.81225, 33.2612, 0, -10.5098, 32.4849, 0, -14.7297, 31.7086, 0, -16.5609, 30.574, -2.28131, -5.3618, 33.2612, -4.12511, -9.69532, 32.4849, -5.7814, -13.5881, 31.7086, -6.50017, -15.2775, 30.574, -4.1267, -4.1267, 33.2612, -7.46198, -7.46198, 32.4849, -10.4581, -10.4581, 31.7086, -11.7583, -11.7583, 30.574, -5.3618, -2.28131, 33.2612, -9.69532, -4.12511, 32.4849, -13.5881, -5.7814, 31.7086, -15.2775, -6.50017, 30.574, -5.81225, 0, 33.2612, -10.5098, 0, 32.4849, -14.7297, 0, 31.7086, -16.5609, 0, 30.574, -5.3618, 2.28131, 33.2612, -9.69532, 4.12511, 32.4849, -13.5881, 5.7814, 31.7086, -15.2775, 6.50017, 30.574, -4.1267, 4.1267, 33.2612, -7.46198, 7.46198, 32.4849, -10.4581, 10.4581, 31.7086, -11.7583, 11.7583, 30.574, -2.28131, 5.3618, 33.2612, -4.12511, 9.69532, 32.4849, -5.7814, 13.5881, 31.7086, -6.50017, 15.2775, 30.574, 0, 5.81225, 33.2612, 0, 10.5098, 32.4849, 0, 14.7297, 31.7086, 0, 16.5609, 30.574, 2.28131, 5.3618, 33.2612, 4.12511, 9.69532, 32.4849, 5.7814, 13.5881, 31.7086, 6.50017, 15.2775, 30.574, 4.1267, 4.1267, 33.2612, 7.46198, 7.46198, 32.4849, 10.4581, 10.4581, 31.7086, 11.7583, 11.7583, 30.574, 5.3618, 2.28131, 33.2612, 9.69532, 4.12511, 32.4849, 13.5881, 5.7814, 31.7086, 15.2775, 6.50017, 30.574];
diff --git a/o3d/samples/assets/texture_b3.jpg b/o3d/samples/assets/texture_b3.jpg
new file mode 100644
index 0000000..01170c8
--- /dev/null
+++ b/o3d/samples/assets/texture_b3.jpg
Binary files differ
diff --git a/o3d/samples/beachdemo/assets/pe_fire.jpg b/o3d/samples/beachdemo/assets/pe_fire.jpg
new file mode 100644
index 0000000..2a2c959
--- /dev/null
+++ b/o3d/samples/beachdemo/assets/pe_fire.jpg
Binary files differ
diff --git a/o3d/samples/beachdemo/assets/pe_mist.png b/o3d/samples/beachdemo/assets/pe_mist.png
new file mode 100644
index 0000000..41d895a
--- /dev/null
+++ b/o3d/samples/beachdemo/assets/pe_mist.png
Binary files differ
diff --git a/o3d/samples/beachdemo/assets/sky-cubemap.dds b/o3d/samples/beachdemo/assets/sky-cubemap.dds
new file mode 100644
index 0000000..2bfdada
--- /dev/null
+++ b/o3d/samples/beachdemo/assets/sky-cubemap.dds
Binary files differ
diff --git a/o3d/samples/beachdemo/beachdemo.html b/o3d/samples/beachdemo/beachdemo.html
new file mode 100644
index 0000000..b4e61aa
--- /dev/null
+++ b/o3d/samples/beachdemo/beachdemo.html
@@ -0,0 +1,1228 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+ <title>
+ Beach Demo
+ </title>
+<style type="text/css">
+html, body {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ height: 100%;
+}
+textarea {
+ width: 95%;
+ height: 95%;
+}
+#proppanel {
+ font-size: xx-small;
+ background-color: lightblue;
+ font-family: Arial, san-serif;
+}
+#materialpanel table {
+ width: 100%;
+}
+#proppanel .even {
+ background-color: #ccddff;
+}
+#proppanel .odd {
+ background-color: #ffffff;
+}
+
+#materialpanel {
+ font-size: xx-small;
+ font-family: Arial, san-serif;
+}
+#materialpanel table {
+ width: 100%;
+}
+#materialpanel input[type=text] {
+ font-size: x-small;
+}
+#materialpanel .even {
+ background-color: #ccddff;
+}
+#materialpanel .odd {
+ background-color: #ccddff;
+}
+
+#materialpanel label {
+}
+#materialpanel .field {
+ white-space: nowrap;
+}
+#materialpanel label {
+}
+#effectpanel {
+ background-color: lightgreen;
+}
+#effecttabs {
+}
+#effecttabs .tab {
+}
+#effecttabs .selected {
+ background-color: yellow;
+}
+</style>
+</head>
+<body onload="init()" onunload="uninit()">
+<script type="text/javascript" src="../o3djs/base.js"></script>
+<script type="text/javascript" src="beachdemo.js"></script>
+<div style="width: 100%; height: 100%;">
+ <div id="upperpanel" style="width: 100%; height: 100%;">
+ <div
+ id="o3d"
+ style="
+ width: 100%;
+ height: 100%;
+ float: left;
+ background-color: blue;
+ "></div>
+ <div
+ id="materialpanel"
+ style="
+ width: 20%;
+ height: 100%;
+ overflow: auto;
+ display: none;
+ "><table><tr><td><input type="text" value="foogoo"/></td></tr></table></div>
+ <div
+ id="proppanel"
+ style="
+ width: 20%;
+ height: 100%;
+ overflow: auto;
+ display: none;
+ "></div>
+ </div>
+ <div
+ id="effectpanel"
+ style="
+ width: 100%;
+ height: 30%;
+ display: none;
+ overflow: auto;
+ ">
+<div style="height:10%;">
+ <input type="button" value="Compile" id="compileButton"></input>
+ <span id="effecttabs"></span>
+</div>
+<div style="height:90%;"><textarea id="effecttextarea"></textarea>
+</div>
+ </div>
+</div>
+<script type="text/o3deffect" id="watershader">
+// The 4x4 world view projection matrix.
+float4x4 world : WORLD;
+float4x4 worldInverseTranspose : WORLDINVERSETRANSPOSE;
+float4x4 view : VIEW;
+float4x4 viewProjection : VIEWPROJECTION;
+float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+float3 viewPosition;
+float4 waterColor;
+float reflectionRefractionOffset;
+float clock;
+
+samplerCUBE environmentSampler;
+sampler2D fresnelSampler; // TODO: should be 1D.
+sampler2D refractionSampler; // This is a render target.
+sampler2D reflectionSampler; // This is a render target.
+sampler2D noiseSampler;
+sampler2D noiseSampler2;
+sampler2D noiseSampler3;
+
+// input parameters for our vertex shader
+struct VertexShaderInput {
+ float4 position : POSITION;
+ float2 texcoord : TEXCOORD0;
+};
+
+// input parameters for our pixel shader
+// also the output parameters for our vertex shader
+struct PixelShaderInput {
+ float4 position : POSITION;
+ float2 texcoord : TEXCOORD0;
+ float3 viewVector : TEXCOORD1;
+ float3 screenPosition : TEXCOORD2;
+};
+
+/**
+ * Vertex Shader
+ */
+PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ /**
+ * We transform each vertex by the world view projection matrix to bring
+ * it from world space to projection space.
+ *
+ * We return its color unchanged.
+ */
+ PixelShaderInput output;
+
+ float4 worldPosition = mul(input.position, world);
+ output.position = mul(worldPosition, viewProjection);
+ output.viewVector = normalize(worldPosition.xyz - viewPosition.xyz);
+ output.texcoord = input.texcoord;
+
+ float4 tpos = mul(float4(worldPosition.x, worldPosition.y, 0, 1),
+ viewProjection);
+ output.screenPosition = tpos.xyz / tpos.w;
+ output.screenPosition.xy = 0.5 + 0.5 * output.screenPosition.xy *
+ float2(1, -1);
+ output.screenPosition.z = reflectionRefractionOffset /
+ output.screenPosition.z;
+
+ return output;
+}
+/**
+ * Pixel Shader - pixel shader does nothing but return the color.
+ */
+float4 pixelShaderFunction(PixelShaderInput input): COLOR {
+ float3 viewVector = float3(input.viewVector.x,
+ input.viewVector.z,
+ -input.viewVector.y);
+
+ float2 texcoord = input.texcoord * 4;
+ float3 n1 = tex2D(noiseSampler,
+ texcoord +
+ float2(clock * 0.01, clock * 0.02));
+ float3 n2 = tex2D(noiseSampler2,
+ texcoord +
+ float2(clock * 0.03, clock * 0.01));
+ float3 n3 = tex2D(noiseSampler3,
+ texcoord +
+ float2(clock * 0.005, clock * 0.007));
+
+ float3 N = normalize(n1 + n2 * 2 + n3 * 4 + float3(-3.5, 16, -3.5));
+
+ float3 R = normalize(reflect(viewVector, N));
+ R.y = R.y < 0.01 ? 0.01 : R.y;
+
+ //float f = tex1D(fresnelSampler, dot(R, N));
+ float f = tex2D(fresnelSampler, float2(dot(R, N), 0.5)).x;
+ float4 reflection = tex2D(
+ reflectionSampler,
+ input.screenPosition.xy - input.screenPosition.z * N.xy + float2(0, 0.1));
+ // I still don't understand where my math is wrong such that I need this 0.1
+ // fudge factor.
+
+ // Lookup the sky color
+ float3 skyReflection = texCUBE(environmentSampler, R);
+
+ // lerping with reflection.a means that where there is terrain reflected
+ // we get terrain, otherwise we get sky.
+ float3 color = lerp(skyReflection,
+ reflection.rgb, reflection.a);
+
+ // lookup the refraction color.
+ float3 refraction = tex2D(
+ refractionSampler,
+ (input.screenPosition.xy - input.screenPosition.z * N.xz +
+ float2(0, 0.05)) * float2(1, 0.95)); // fudge
+
+ float3 finalColor = lerp(refraction, color, f);
+
+ // Uncomment any one of the lines below to see just part of the water
+ // calculation.
+
+ //finalColor = skyReflection; // sky only.
+ //finalColor = reflection.xyz; // reflection only.
+ //finalColor = refraction; // refraction only.
+ //finalColor = lerp(float3(0,1,0), float3(1,0,0), f); // reflection/refraction mix
+ //finalColor = color; // sky only.
+ return float4(finalColor, 1);
+}
+
+// Here we tell our effect file the functions
+// which specify our vertex and pixel shaders.
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
+</script>
+<script type="text/o3deffect" id="watercolorandskyshader">
+// The 4x4 world view projection matrix.
+float4x4 world : WORLD;
+float4x4 worldInverseTranspose : WORLDINVERSETRANSPOSE;
+float4x4 view : VIEW;
+float4x4 viewProjection : VIEWPROJECTION;
+float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+float3 viewPosition;
+float4 waterColor;
+float reflectionRefractionOffset;
+float clock;
+
+samplerCUBE environmentSampler;
+sampler2D fresnelSampler; // TODO: should be 1D.
+sampler2D noiseSampler;
+sampler2D noiseSampler2;
+sampler2D noiseSampler3;
+
+// input parameters for our vertex shader
+struct VertexShaderInput {
+ float4 position : POSITION;
+ float2 texcoord : TEXCOORD0;
+};
+
+// input parameters for our pixel shader
+// also the output parameters for our vertex shader
+struct PixelShaderInput {
+ float4 position : POSITION;
+ float2 texcoord : TEXCOORD0;
+ float3 viewVector : TEXCOORD1;
+ float3 screenPosition : TEXCOORD2;
+};
+
+/**
+ * Vertex Shader
+ */
+PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ /**
+ * We transform each vertex by the world view projection matrix to bring
+ * it from world space to projection space.
+ *
+ * We return its color unchanged.
+ */
+ PixelShaderInput output;
+
+ float4 worldPosition = mul(input.position, world);
+ output.position = mul(worldPosition, viewProjection);
+ output.viewVector = normalize(worldPosition.xyz - viewPosition.xyz);
+ output.texcoord = input.texcoord;
+
+ float4 tpos = mul(float4(worldPosition.x, worldPosition.y, 0, 1),
+ viewProjection);
+ output.screenPosition = tpos.xyz / tpos.w;
+ output.screenPosition.xy = 0.5 + 0.5 * output.screenPosition.xy *
+ float2(1, -1);
+ output.screenPosition.z = reflectionRefractionOffset /
+ output.screenPosition.z;
+
+ return output;
+}
+/**
+ * Pixel Shader - pixel shader does nothing but return the color.
+ */
+float4 pixelShaderFunction(PixelShaderInput input): COLOR {
+ float3 viewVector = float3(input.viewVector.x,
+ input.viewVector.z,
+ -input.viewVector.y);
+
+ float2 texcoord = input.texcoord * 4;
+ float3 n1 = tex2D(noiseSampler,
+ texcoord +
+ float2(clock * 0.01, clock * 0.02));
+ float3 n2 = tex2D(noiseSampler2,
+ texcoord +
+ float2(clock * 0.03, clock * 0.01));
+ float3 n3 = tex2D(noiseSampler3,
+ texcoord +
+ float2(clock * 0.005, clock * 0.007));
+
+ float3 N = normalize(n1 + n2 * 2 + n3 * 4 + float3(-3.5, 16, -3.5));
+
+ float3 R = normalize(reflect(viewVector, N));
+ R.y = R.y < 0.01 ? 0.01 : R.y;
+
+ //float f = tex1D(fresnelSampler, dot(R, N));
+ float f = tex2D(fresnelSampler, float2(dot(R, N), 0.5)).x;
+
+ // Lookup the sky color
+ float3 skyReflection = texCUBE(environmentSampler, R);
+
+ float3 color = lerp(waterColor.xyz, skyReflection, f);
+
+ return float4(color, 1);
+}
+
+// Here we tell our effect file the functions
+// which specify our vertex and pixel shaders.
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
+</script>
+<script type="text/o3deffect" id="waterstyle2">
+// The 4x4 world view projection matrix.
+float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+
+// input parameters for our vertex shader
+struct VertexShaderInput {
+ float4 position : POSITION;
+};
+
+// input parameters for our pixel shader
+// also the output parameters for our vertex shader
+struct PixelShaderInput {
+ float4 position : POSITION;
+};
+
+/**
+ * Vertex Shader
+ */
+PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ /**
+ * We transform each vertex by the world view projection matrix to bring
+ * it from world space to projection space.
+ *
+ * We return its color unchanged.
+ */
+ PixelShaderInput output;
+
+ output.position = mul(input.position, worldViewProjection);
+ return output;
+}
+/**
+ * Pixel Shader - pixel shader does nothing but return the color.
+ */
+float4 pixelShaderFunction(PixelShaderInput input): COLOR {
+ return float4(0, 0, 0.5, 1);
+}
+
+// Here we tell our effect file the functions
+// which specify our vertex and pixel shaders.
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
+</script>
+<script type="text/o3deffect" id="underwatershader">
+uniform float4x4 world : WORLD;
+uniform float4x4 viewProjection : VIEWPROJECTION;
+uniform float4 waterColor;
+uniform float3 sunVector;
+uniform float fadeFudge;
+
+sampler diffuseSampler;
+
+// input parameters for our vertex shader
+struct VertexShaderInput {
+ float4 position : POSITION;
+ float3 normal : NORMAL;
+ float2 texcoord : TEXCOORD0;
+};
+
+// input parameters for our pixel shader
+// also the output parameters for our vertex shader
+struct PixelShaderInput {
+ float4 position : POSITION;
+ float2 texcoord : TEXCOORD0;
+ float fade : TEXCOORD1;
+ float4 color : TEXCOORD2;
+ float4 worldPosition : TEXCOORD3;
+};
+
+PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ PixelShaderInput output;
+ float4 worldPosition = mul(input.position, world);
+ output.position = mul(float4(worldPosition.xyz, 1), viewProjection);
+ output.worldPosition = worldPosition;
+ output.texcoord = input.texcoord;
+ output.color = dot(sunVector,
+ normalize(mul(float4(input.normal, 0), world)));
+ output.fade = 0.2 + 0.8 * saturate(worldPosition.z * fadeFudge);
+
+ return output;
+}
+
+float4 pixelShaderFunction(PixelShaderInput input) : COLOR {
+ float4 color = tex2D(diffuseSampler, input.texcoord);
+// float4 diffuse = tex2D(diffuseSampler, input.texcoord);
+// float4 color = lerp(diffuse * input.color, waterColor, input.fade);
+ float alpha = input.worldPosition.z < 100 ? color.a : 0;
+ return float4(color.xyz, alpha);
+}
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
+</script>
+<script type="text/o3deffect" id="skydomeshader">
+uniform float4x4 worldViewProjectionInverse : VIEWPROJECTIONINVERSE;
+uniform float4x4 viewInverse : VIEWINVERSE;
+samplerCUBE environmentSampler;
+
+struct VertexShaderInput {
+ float4 position : POSITION;
+};
+
+struct PixelShaderInput {
+ float4 position : POSITION;
+ float3 worldPosition : TEXCOORD0;
+};
+
+PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ PixelShaderInput output;
+ output.position = input.position;
+ float4 temp = mul(input.position, worldViewProjectionInverse);
+ output.worldPosition = temp.xyz / temp.w;
+ return output;
+}
+
+float4 pixelShaderFunction(PixelShaderInput input) : COLOR {
+ float3 viewVector = normalize(viewInverse[3].xyz - input.worldPosition);
+ return texCUBE(environmentSampler, float3(viewVector.x,
+ abs(-viewVector.z) + 0.01,
+ -viewVector.y));
+}
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
+</script>
+<script type="text/o3deffect" id="waterfallshader">
+uniform float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+uniform float4x4 world : WORLD;
+uniform float4x4 viewInverse : VIEWINVERSE;
+uniform float4x4 worldInverseTranspose : WORLDINVERSETRANSPOSE;
+uniform float3 lightWorldPos;
+uniform float vOffset;
+
+sampler diffuseSampler;
+
+struct VertexShaderInput {
+ float4 position : POSITION;
+ float4 normal : NORMAL;
+ float2 texcoord : TEXCOORD0;
+};
+
+struct PixelShaderInput {
+ float4 position : POSITION;
+ float3 normal : TEXCOORD0;
+ float3 worldPosition : TEXCOORD1;
+ float2 texcoord : TEXCOORD2;
+};
+
+PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ PixelShaderInput output;
+ output.position = mul(input.position, worldViewProjection);
+ float3 worldPosition = mul(input.position, world).xyz;
+ output.normal = mul(input.normal, worldInverseTranspose).xyz;
+ output.worldPosition = worldPosition;
+ output.texcoord = input.texcoord;
+ return output;
+}
+
+float4 pixelShaderFunction(PixelShaderInput input) : COLOR {
+ float3 surfaceToLight = normalize(lightWorldPos - input.worldPosition);
+ float3 worldNormal = normalize(input.normal);
+ float3 surfaceToView = normalize(viewInverse[3].xyz - input.worldPosition);
+ float3 halfVector = normalize(surfaceToLight + surfaceToView);
+ float4 litResult = lit(dot(worldNormal, surfaceToLight),
+ dot(worldNormal, halfVector), 0);
+ float4 diffuse = tex2D(diffuseSampler, float2(input.texcoord.x,
+ input.texcoord.y + vOffset));
+ float4 outColor = (diffuse * litResult.y);
+ return diffuse;
+ //return float4(outColor.rgb, 1);
+}
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
+</script>
+<script type="text/o3deffect" id="imageshader">
+uniform float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+
+float4 colorMult;
+sampler diffuseSampler;
+
+
+struct VertexShaderInput {
+ float4 position : POSITION;
+ float2 texcoord : TEXCOORD0;
+};
+
+struct PixelShaderInput {
+ float4 position : POSITION;
+ float2 texcoord : TEXCOORD2;
+};
+
+PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ PixelShaderInput output;
+ output.position = mul(input.position, worldViewProjection);
+ output.texcoord = input.texcoord;
+ return output;
+}
+
+float4 pixelShaderFunction(PixelShaderInput input) : COLOR {
+ return tex2D(diffuseSampler, input.texcoord) * colorMult;
+}
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
+</script>
+<script type="text/o3deffect" id="simpleshader">
+uniform float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+uniform float4 simpleColor;
+
+struct VertexShaderInput {
+ float4 position : POSITION;
+};
+
+struct PixelShaderInput {
+ float4 position : POSITION;
+};
+
+PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ PixelShaderInput output;
+ output.position = mul(input.position, worldViewProjection);
+ return output;
+}
+
+float4 pixelShaderFunction(PixelShaderInput input) : COLOR {
+ return simpleColor;
+}
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
+</script>
+<script type="text/o3deffect" id="particleshader">
+float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+float4x4 viewInverse : VIEWINVERSE;
+float timeRange;
+float time;
+
+// We need to implement 1D!
+sampler rampSampler;
+sampler colorSampler;
+
+struct VertexShaderInput {
+ float4 uvLifeTimeStartTime : POSITION; // u, v, lifeTime, startTime
+ float4 positionSpinSpeed : TEXCOORD0; // position.xyz, spinSpeed
+ float4 velocityStartSize : TEXCOORD1; // velocity.xyz, startSize
+ float4 accelerationEndSize : TEXCOORD2; // acceleration.xyz, endSize
+ float4 colorMult : COLOR; //
+};
+
+struct PixelShaderInput {
+ float4 position : POSITION;
+ float2 texcoord : TEXCOORD0;
+ float1 percentLife : TEXCOORD1;
+ float4 colorMult: TEXCOORD2;
+};
+
+PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ PixelShaderInput output;
+
+ float2 uv = input.uvLifeTimeStartTime.xy;
+ float lifeTime = input.uvLifeTimeStartTime.z;
+ float startTime = input.uvLifeTimeStartTime.w;
+ float3 position = input.positionSpinSpeed.xyz;
+ float spinSpeed = input.positionSpinSpeed.w;
+ float3 velocity = input.velocityStartSize.xyz;
+ float startSize = input.velocityStartSize.w;
+ float3 acceleration = input.accelerationEndSize.xyz;
+ float endSize = input.accelerationEndSize.w;
+
+ output.texcoord = float4(uv + 0.5, 0, 0);
+ output.colorMult = input.colorMult;
+
+ float localTime = (time - startTime) % timeRange;
+ float percentLife = localTime / lifeTime;
+
+ float3 basisX = viewInverse[0].xyz;
+ float3 basisZ = viewInverse[1].xyz;
+
+ float size = lerp(startSize, endSize, percentLife);
+ float s = sin(spinSpeed * localTime);
+ float c = cos(spinSpeed * localTime);
+
+ float2 rotatedPoint = float2(uv.x * c + uv.y * s, -uv.x * s + uv.y * c);
+ float3 localPosition = float3(basisX * rotatedPoint.x +
+ basisZ * rotatedPoint.y) * size +
+ velocity * localTime +
+ acceleration * pow(localTime, 2) + position;
+
+ output.position = mul(float4(localPosition, 1) , worldViewProjection);
+ output.percentLife = percentLife;
+ return output;
+}
+
+float4 pixelShaderFunction(PixelShaderInput input): COLOR {
+ float4 colorMult = tex2D(rampSampler, float2(input.percentLife, 0.5)) *
+ input.colorMult;
+ float4 color = tex2D(colorSampler, input.texcoord) * colorMult;
+ return color;
+}
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
+</script>
+<script type="text/o3deffect" id="diffuse_bump">
+uniform float4x4 world : WORLD;
+uniform float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+uniform float3 lightWorldPos;
+uniform float4 lightColor;
+uniform float clipHeight;
+uniform float4x4 viewInverse : VIEWINVERSE;
+uniform float4x4 worldInverseTranspose : WORLDINVERSETRANSPOSE;
+uniform float4 emissive;
+uniform float4 ambient;
+sampler2D diffuseSampler;
+uniform float4 specular;
+sampler2D bumpSampler;
+uniform float shininess;
+
+struct InVertex {
+ float4 position : POSITION;
+ float4 normal : NORMAL;
+ float2 diffuseUV : TEXCOORD0;
+ float3 tangent : TANGENT;
+ float3 binormal : BINORMAL;
+};
+
+struct OutVertex {
+ float4 position : POSITION;
+ float2 diffuseUV : TEXCOORD0;
+ float3 tangent : TEXCOORD1;
+ float3 binormal : TEXCOORD2;
+ float4 worldPosition: TEXCOORD3;
+ float3 normal : TEXCOORD4;
+};
+
+OutVertex vertexShaderFunction(InVertex input) {
+ OutVertex output;
+ output.position = mul(input.position, worldViewProjection);
+ output.worldPosition = mul(input.position, world);
+ output.diffuseUV = input.diffuseUV;
+ output.normal = mul(float4(input.normal.xyz,0), worldInverseTranspose).xyz;
+ output.binormal = mul(float4(input.binormal,0), worldInverseTranspose).xyz;
+ output.tangent = mul(float4(input.tangent,0), worldInverseTranspose).xyz;
+ return output;
+}
+
+float4 pixelShaderFunction(OutVertex input) : COLOR {
+ float4 diffuse = tex2D(diffuseSampler, input.diffuseUV);
+ float3x3 tangentToWorld = float3x3(input.tangent,
+ input.binormal,
+ input.normal);
+ float3 tangentNormal = tex2D(bumpSampler, input.diffuseUV.xy).xyz -
+ float3(0.5, 0.5, 0.5);
+ float3 normal = mul(tangentNormal, tangentToWorld);
+ normal = normalize(normal);
+ float3 surfaceToLight = normalize(lightWorldPos - input.worldPosition);
+ float3 surfaceToView = normalize(viewInverse[3] - input.worldPosition);
+ float3 halfVector = normalize(surfaceToLight + surfaceToView);
+ float4 litR = lit(dot(normal, surfaceToLight),
+ dot(normal, halfVector),
+ shininess);
+ float alpha = input.worldPosition.z > clipHeight ? 0 : diffuse.a;
+ return float4((emissive + lightColor *
+ (ambient * diffuse +
+ diffuse * litR.y +
+ specular * litR.z)).rgb, alpha);
+}
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
+</script>
+<script type="text/o3deffect" id="diffuse_bump_blend">
+uniform float4x4 world : WORLD;
+uniform float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+uniform float3 lightWorldPos;
+uniform float4 lightColor;
+uniform float clipHeight;
+uniform float4x4 viewInverse : VIEWINVERSE;
+uniform float4x4 worldInverseTranspose : WORLDINVERSETRANSPOSE;
+uniform float4 emissive;
+uniform float4 ambient;
+sampler2D diffuseSampler;
+sampler2D diffuse2Sampler;
+uniform float4 specular;
+sampler2D bumpSampler;
+uniform float shininess;
+
+struct InVertex {
+ float4 position : POSITION;
+ float4 normal : NORMAL;
+ float2 diffuseUV : TEXCOORD0;
+ float2 diffuse2UV : TEXCOORD1;
+ float3 tangent : TANGENT;
+ float3 tangent2 : TANGENT1;
+ float3 binormal : BINORMAL;
+ float3 binormal2 : BINORMAL1;
+ float4 color : COLOR;
+};
+
+struct OutVertex {
+ float4 position : POSITION;
+ float4 diffuseUV : TEXCOORD0;
+ float3 tangent : TEXCOORD1;
+ float3 tangent2 : TEXCOORD2;
+ float3 binormal : TEXCOORD3;
+ float3 binormal2 : TEXCOORD4;
+ float4 worldPosition: TEXCOORD5;
+ float3 normal : TEXCOORD6;
+ float4 color : COLOR;
+};
+
+OutVertex vertexShaderFunction(InVertex input) {
+ OutVertex output;
+ output.position = mul(input.position, worldViewProjection);
+ output.worldPosition = mul(input.position, world);
+ output.diffuseUV = float4(input.diffuseUV, input.diffuse2UV);
+ output.normal = mul(float4(input.normal.xyz,0), worldInverseTranspose).xyz;
+ output.binormal = mul(float4(input.binormal,0), worldInverseTranspose).xyz;
+ output.tangent = mul(float4(input.tangent,0), worldInverseTranspose).xyz;
+ output.binormal2 = mul(float4(input.binormal2,0), worldInverseTranspose).xyz;
+ output.tangent2 = mul(float4(input.tangent2,0), worldInverseTranspose).xyz;
+ output.color = output.color;
+ return output;
+}
+
+float4 pixelShaderFunction(OutVertex input) : COLOR {
+ float4 diffuse1 = tex2D(diffuseSampler, input.diffuseUV.xy);
+ float4 diffuse2 = tex2D(diffuse2Sampler, input.diffuseUV.zw);
+ float3x3 tangentToWorld = float3x3(input.tangent,
+ input.binormal,
+ input.normal);
+ float3 tangentNormal = tex2D(bumpSampler, input.diffuseUV.xy).xyz -
+ float3(0.5, 0.5, 0.5);
+ float3 normal = mul(tangentNormal, tangentToWorld);
+ normal = normalize(normal);
+ tangentToWorld = float3x3(input.tangent2, input.binormal2, input.normal);
+ tangentNormal = tex2D(bumpSampler, input.diffuseUV.zw).xyz -
+ float3(0.5, 0.5, 0.5);
+ float3 normal2 = mul(tangentNormal, tangentToWorld);
+ normal2 = normalize(normal2);
+ float3 surfaceToLight = normalize(lightWorldPos - input.worldPosition);
+ float3 surfaceToView = normalize(viewInverse[3] - input.worldPosition);
+ float3 halfVector = normalize(surfaceToLight + surfaceToView);
+ float4 litR = lit(dot(normal, surfaceToLight),
+ dot(normal, halfVector),
+ shininess);
+ float4 diffuse = lerp(diffuse1, diffuse2, input.color.a);
+ float alpha = input.worldPosition.z > clipHeight ? 0 : diffuse.a;
+ return float4((emissive + lightColor *
+ (ambient * diffuse +
+ diffuse * litR.y +
+ specular * litR.z)).rgb, alpha);
+}
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
+</script>
+<script type="text/o3deffect" id="diffuse_bump_blend_underwater">
+uniform float4x4 world : WORLD;
+uniform float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+uniform float3 lightWorldPos;
+uniform float4 lightColor;
+uniform float4x4 viewInverse : VIEWINVERSE;
+uniform float4x4 worldInverseTranspose : WORLDINVERSETRANSPOSE;
+uniform float4 emissive;
+uniform float4 ambient;
+sampler2D diffuseSampler;
+sampler2D diffuse2Sampler;
+uniform float4 specular;
+sampler2D bumpSampler;
+uniform float shininess;
+
+struct InVertex {
+ float4 position : POSITION;
+ float4 normal : NORMAL;
+ float2 diffuseUV : TEXCOORD0;
+ float2 diffuse2UV : TEXCOORD1;
+ float3 tangent : TANGENT;
+ float3 tangent2 : TANGENT1;
+ float3 binormal : BINORMAL;
+ float3 binormal2 : BINORMAL1;
+ float4 color : COLOR;
+};
+
+struct OutVertex {
+ float4 position : POSITION;
+ float4 diffuseUV : TEXCOORD0;
+ float3 tangent : TEXCOORD1;
+ float3 tangent2 : TEXCOORD2;
+ float3 binormal : TEXCOORD3;
+ float3 binormal2 : TEXCOORD4;
+ float4 worldPosition: TEXCOORD5;
+ float3 normal : TEXCOORD6;
+ float4 color : COLOR;
+};
+
+OutVertex vertexShaderFunction(InVertex input) {
+ OutVertex output;
+ output.position = mul(input.position, worldViewProjection);
+ output.worldPosition = mul(input.position, world);
+ output.diffuseUV = float4(input.diffuseUV, input.diffuse2UV);
+ output.normal = mul(float4(input.normal.xyz,0), worldInverseTranspose).xyz;
+ output.binormal = mul(float4(input.binormal,0), worldInverseTranspose).xyz;
+ output.tangent = mul(float4(input.tangent,0), worldInverseTranspose).xyz;
+ output.binormal2 = mul(float4(input.binormal2,0), worldInverseTranspose).xyz;
+ output.tangent2 = mul(float4(input.tangent2,0), worldInverseTranspose).xyz;
+ output.color = output.color;
+ return output;
+}
+
+float4 pixelShaderFunction(OutVertex input) : COLOR {
+ float4 diffuse1 = tex2D(diffuseSampler, input.diffuseUV.xy);
+ float4 diffuse2 = tex2D(diffuse2Sampler, input.diffuseUV.zw);
+ float3x3 tangentToWorld = float3x3(input.tangent,
+ input.binormal,
+ input.normal);
+ float3 tangentNormal = tex2D(bumpSampler, input.diffuseUV.xy).xyz -
+ float3(0.5, 0.5, 0.5);
+ float3 normal = mul(tangentNormal, tangentToWorld);
+ normal = normalize(normal);
+ tangentToWorld = float3x3(input.tangent2, input.binormal2, input.normal);
+ tangentNormal = tex2D(bumpSampler, input.diffuseUV.zw).xyz -
+ float3(0.5, 0.5, 0.5);
+ float3 normal2 = mul(tangentNormal, tangentToWorld);
+ normal2 = normalize(normal2);
+ float3 surfaceToLight = normalize(lightWorldPos - input.worldPosition);
+ float3 surfaceToView = normalize(viewInverse[3] - input.worldPosition);
+ float3 halfVector = normalize(surfaceToLight + surfaceToView);
+ float4 litR = lit(dot(normal, surfaceToLight),
+ dot(normal, halfVector),
+ shininess);
+ float4 diffuse = lerp(diffuse1, diffuse2, input.color.a);
+ float alpha = input.worldPosition.z > 0 ? 0 : diffuse.a;
+ return float4((emissive + lightColor *
+ (ambient * diffuse +
+ diffuse * litR.y +
+ specular * litR.z)).rgb, alpha);
+}
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
+</script>
+<script type="text/o3deffect" id="diffuse_bump_2textures">
+uniform float4x4 world : WORLD;
+uniform float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+uniform float3 lightWorldPos;
+uniform float4 lightColor;
+uniform float4x4 viewInverse : VIEWINVERSE;
+uniform float4x4 worldInverseTranspose : WORLDINVERSETRANSPOSE;
+uniform float4 emissive;
+uniform float4 ambient;
+sampler2D diffuseSampler;
+sampler2D diffuse2Sampler;
+uniform float4 specular;
+sampler2D bumpSampler;
+uniform float shininess;
+
+struct InVertex {
+ float4 position : POSITION;
+ float4 normal : NORMAL;
+ float2 diffuseUV : TEXCOORD0;
+ float3 tangent : TANGENT;
+ float3 binormal : BINORMAL;
+ float4 color : COLOR;
+};
+
+struct OutVertex {
+ float4 position : POSITION;
+ float2 diffuseUV : TEXCOORD0;
+ float3 tangent : TEXCOORD1;
+ float3 binormal : TEXCOORD2;
+ float4 worldPosition: TEXCOORD3;
+ float3 normal : TEXCOORD4;
+ float4 color : COLOR;
+};
+
+OutVertex vertexShaderFunction(InVertex input) {
+ OutVertex output;
+ output.position = mul(input.position, worldViewProjection);
+ output.worldPosition = mul(input.position, world);
+ output.diffuseUV = input.diffuseUV;
+ output.normal = mul(float4(input.normal.xyz,0), worldInverseTranspose).xyz;
+ output.binormal = mul(float4(input.binormal,0), worldInverseTranspose).xyz;
+ output.tangent = mul(float4(input.tangent,0), worldInverseTranspose).xyz;
+ output.color = input.color;
+ return output;
+}
+
+float4 pixelShaderFunction(OutVertex input) : COLOR {
+ float4 diffuse = tex2D(diffuseSampler, input.diffuseUV);
+ float4 diffuse2 = tex2D(diffuse2Sampler, input.diffuseUV);
+ diffuse = lerp(diffuse, diffuse2, input.color);
+ float3x3 tangentToWorld = float3x3(input.tangent,
+ input.binormal,
+ input.normal);
+ float3 tangentNormal = tex2D(bumpSampler, input.diffuseUV.xy).xyz -
+ float3(0.5, 0.5, 0.5);
+ float3 normal = mul(tangentNormal, tangentToWorld);
+ normal = normalize(normal);
+ float3 surfaceToLight = normalize(lightWorldPos - input.worldPosition);
+ float3 surfaceToView = normalize(viewInverse[3] - input.worldPosition);
+ float3 halfVector = normalize(surfaceToLight + surfaceToView);
+ float4 litR = lit(dot(normal, surfaceToLight),
+ dot(normal, halfVector),
+ shininess);
+ float alpha = input.worldPosition.z < 0 ? diffuse.a : 0;
+ return float4((emissive +
+ (ambient * diffuse +
+ diffuse * litR.y +
+ specular * litR.z)).rgb, alpha);
+}
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
+</script>
+<script type="text/o3deffect" id="diffuse">
+uniform float4x4 world : WORLD;
+uniform float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+uniform float3 lightWorldPos;
+uniform float4 lightColor;
+uniform float clipHeight;
+uniform float4x4 viewInverse : VIEWINVERSE;
+uniform float4x4 worldInverseTranspose : WORLDINVERSETRANSPOSE;
+uniform float4 emissive;
+uniform float4 ambient;
+sampler2D diffuseSampler;
+uniform float4 specular;
+uniform float shininess;
+
+struct InVertex {
+ float4 position : POSITION;
+ float4 normal : NORMAL;
+ float2 diffuseUV : TEXCOORD0;
+};
+
+struct OutVertex {
+ float4 position : POSITION;
+ float2 diffuseUV : TEXCOORD0;
+ float4 worldPosition: TEXCOORD1;
+ float3 normal : TEXCOORD2;
+};
+
+OutVertex vertexShaderFunction(InVertex input) {
+ OutVertex output;
+ output.position = mul(input.position, worldViewProjection);
+ output.worldPosition = mul(input.position, world);
+ output.diffuseUV = input.diffuseUV;
+ output.normal = mul(float4(input.normal.xyz,0), worldInverseTranspose).xyz;
+ return output;
+}
+
+float4 pixelShaderFunction(OutVertex input) : COLOR {
+ float4 diffuse = tex2D(diffuseSampler, input.diffuseUV);
+ float3 normal = normalize(input.normal);
+ float3 surfaceToLight = normalize(lightWorldPos - input.worldPosition);
+ float3 surfaceToView = normalize(viewInverse[3] - input.worldPosition);
+ float3 halfVector = normalize(surfaceToLight + surfaceToView);
+ float4 litR = lit(dot(normal, surfaceToLight), dot(normal, halfVector), shininess);
+ float alpha = input.worldPosition.z > clipHeight ? 0 : diffuse.a;
+ return float4((emissive + lightColor * (ambient * diffuse + diffuse * litR.y + specular * litR.z)).rgb, alpha);
+}
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
+</script>
+<script type="text/o3deffect" id="diffuse_bump_specular">
+uniform float4x4 world : WORLD;
+uniform float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+uniform float3 lightWorldPos;
+uniform float4 lightColor;
+uniform float clipHeight;
+uniform float4x4 viewInverse : VIEWINVERSE;
+uniform float4x4 worldInverseTranspose : WORLDINVERSETRANSPOSE;
+uniform float4 emissive;
+uniform float4 ambient;
+sampler2D diffuseSampler;
+sampler2D specularSampler;
+sampler2D bumpSampler;
+uniform float shininess;
+
+struct InVertex {
+ float4 position : POSITION;
+ float4 normal : NORMAL;
+ float2 diffuseUV : TEXCOORD0;
+ float3 tangent : TANGENT;
+ float3 binormal : BINORMAL;
+};
+
+struct OutVertex {
+ float4 position : POSITION;
+ float2 diffuseUV : TEXCOORD0;
+ float3 tangent : TEXCOORD1;
+ float3 binormal : TEXCOORD2;
+ float4 worldPosition: TEXCOORD3;
+ float3 normal : TEXCOORD4;
+};
+
+OutVertex vertexShaderFunction(InVertex input) {
+ OutVertex output;
+ output.position = mul(input.position, worldViewProjection);
+ output.worldPosition = mul(input.position, world);
+ output.diffuseUV = input.diffuseUV;
+ output.normal = mul(float4(input.normal.xyz,0), worldInverseTranspose).xyz;
+ output.binormal = mul(float4(input.binormal,0), worldInverseTranspose).xyz;
+ output.tangent = mul(float4(input.tangent,0), worldInverseTranspose).xyz;
+ return output;
+}
+
+float4 pixelShaderFunction(OutVertex input) : COLOR {
+ float4 diffuse = tex2D(diffuseSampler, input.diffuseUV);
+ float4 specular = tex2D(specularSampler, input.diffuseUV);
+ float3x3 tangentToWorld = float3x3(input.tangent, input.binormal, input.normal);
+ float3 tangentNormal = tex2D(bumpSampler, input.diffuseUV.xy).xyz -
+ float3(0.5, 0.5, 0.5);
+ float3 normal = mul(tangentNormal, tangentToWorld);
+ normal = normalize(normal);
+ float3 surfaceToLight = normalize(lightWorldPos - input.worldPosition);
+ float3 surfaceToView = normalize(viewInverse[3] - input.worldPosition);
+ float3 halfVector = normalize(surfaceToLight + surfaceToView);
+ float4 litR = lit(dot(normal, surfaceToLight),
+ dot(normal, halfVector),
+ shininess);
+ float alpha = input.worldPosition.z > clipHeight ? 0 : diffuse.a;
+ return float4((emissive + lightColor *
+ (ambient * diffuse +
+ diffuse * litR.y +
+ specular * litR.z)).rgb, alpha);
+}
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
+</script>
+<script type="text/o3deffect" id="just_color">
+uniform float4x4 world : WORLD;
+uniform float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+uniform float3 lightWorldPos;
+uniform float4 lightColor;
+uniform float clipHeight;
+uniform float4x4 viewInverse : VIEWINVERSE;
+uniform float4x4 worldInverseTranspose : WORLDINVERSETRANSPOSE;
+uniform float4 emissive;
+uniform float4 ambient;
+uniform float4 diffuse;
+uniform float4 specular;
+uniform float shininess;
+
+struct InVertex {
+ float4 position : POSITION;
+ float4 normal : NORMAL;
+};
+
+struct OutVertex {
+ float4 position : POSITION;
+ float4 worldPosition: TEXCOORD0;
+ float3 normal : TEXCOORD1;
+};
+
+OutVertex vertexShaderFunction(InVertex input) {
+ OutVertex output;
+ output.position = mul(input.position, worldViewProjection);
+ output.worldPosition = mul(input.position, world);
+ output.normal = mul(float4(input.normal.xyz,0), worldInverseTranspose).xyz;
+ return output;
+}
+
+float4 pixelShaderFunction(OutVertex input) : COLOR {
+ float3 normal = normalize(input.normal);
+ float3 surfaceToLight = normalize(lightWorldPos - input.worldPosition);
+ float3 surfaceToView = normalize(viewInverse[3] - input.worldPosition);
+ float3 halfVector = normalize(surfaceToLight + surfaceToView);
+ float4 litR = lit(dot(normal, surfaceToLight),
+ dot(normal, halfVector),
+ shininess);
+ float alpha = input.worldPosition.z > clipHeight ? 0 : diffuse.a;
+ return float4((emissive + lightColor *
+ (ambient * diffuse +
+ diffuse * litR.y +
+ specular * litR.z)).rgb, alpha);
+}
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
+</script>
+<script type="text/o3deffect" id="proxy">
+uniform float4x4 world : WORLD;
+uniform float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+uniform float3 lightWorldPos;
+uniform float4 lightColor;
+uniform float4x4 viewInverse : VIEWINVERSE;
+uniform float4x4 worldInverseTranspose : WORLDINVERSETRANSPOSE;
+uniform float4 emissive;
+uniform float4 ambient;
+uniform float4 diffuse;
+uniform float4 specular;
+uniform float shininess;
+uniform float offset;
+
+struct InVertex {
+ float4 position : POSITION;
+ float4 normal : NORMAL;
+};
+
+struct OutVertex {
+ float4 position : POSITION;
+ float4 worldPosition: TEXCOORD0;
+ float3 normal : TEXCOORD1;
+};
+
+OutVertex vertexShaderFunction(InVertex input) {
+ OutVertex output;
+ output.position = mul(input.position, worldViewProjection);
+ output.worldPosition = mul(input.position, world);
+ output.normal = mul(float4(input.normal.xyz,0), worldInverseTranspose).xyz;
+ return output;
+}
+
+float4 pixelShaderFunction(OutVertex input) : COLOR {
+ float3 normal = normalize(input.normal);
+ float3 surfaceToLight = normalize(lightWorldPos - input.worldPosition);
+ float3 surfaceToView = normalize(viewInverse[3] - input.worldPosition);
+ float3 halfVector = normalize(surfaceToLight + surfaceToView);
+ float4 litR = lit(dot(normal, surfaceToLight),
+ dot(normal, halfVector),
+ shininess);
+ float alpha = input.worldPosition.z > offset ? 0 : diffuse.a;
+ return float4((emissive + lightColor *
+ (ambient * diffuse +
+ diffuse * litR.y +
+ specular * litR.z)).rgb, alpha);
+}
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
+</script>
+</body>
+</html>
+
+
diff --git a/o3d/samples/beachdemo/beachdemo.js b/o3d/samples/beachdemo/beachdemo.js
new file mode 100644
index 0000000..b8b3f3e
--- /dev/null
+++ b/o3d/samples/beachdemo/beachdemo.js
@@ -0,0 +1,2663 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview The beachdemo javascript.
+ */
+
+
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.pack');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.quaternions');
+o3djs.require('o3djs.dump');
+o3djs.require('o3djs.camera');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.loader');
+o3djs.require('o3djs.picking');
+o3djs.require('o3djs.canvas');
+o3djs.require('o3djs.fps');
+o3djs.require('o3djs.debug');
+o3djs.require('o3djs.particles');
+
+var RENDER_TARGET_WIDTH = 256;
+var RENDER_TARGET_HEIGHT = 256;
+var PROXY_HEIGHT = 5150;
+
+// client.root
+// |
+// g_mainRoot
+// |
+// +-----+--------+----------------+
+// | | |
+// g_baseRoot g_waterTransform g_skyDomeTransform
+// |
+// g_sceneRoot
+
+var g_re;
+var g_animateCamera = false;
+var g_cameraDuration = 1;
+var g_cameraPoint;
+var g_cameraPointIndex = 0;
+var g_cameraTimer = 0;
+var g_demoTimer = 0;
+var g_runDemo = true;
+var g_oldCameraClock = 0;
+var g_speedTransforms = [[], [], [], []];
+var g_sceneRoot;
+var g_baseRoot;
+var g_reflectionClipHeight = 100;
+var g_mainClipHeight = 100000000;
+var g_o3d;
+var g_hudFadeTime;
+var g_helpVisible = false;
+var g_math;
+var g_key;
+var g_paint;
+var g_sceneUrl;
+var g_quaternions;
+var g_waterMode = 0;
+var g_updateRenderTargets = true;
+var g_compileEffect;
+var g_reflectRefract = false;
+var g_environmentSampler;
+var g_materialPanelElement;
+var g_propPanelElement;
+var g_effectPanelElement;
+var g_upperPanelElement;
+var g_effectTabsElement;
+var g_effectTextAreaElement;
+var g_editableEffects = [];
+var g_editableEffectsSource = [];
+var g_currentEditEffect;
+var g_faderColorParam;
+var g_faderTransform;
+var g_renderTargetDisplayRoot;
+var g_sceneElement;
+var g_client;
+var g_scenePack;
+var g_proxyPack;
+var g_mainPack;
+var g_fadeParams = [];
+var g_mainViewInfo; // main view
+var g_hudRoot; // root transform for hud.
+var g_mainRoot;
+var g_proxyRoot;
+var g_waterLevel = 500;
+var g_reflectionViewInfo;
+var g_refractionViewInfo;
+var g_hudViewInfo;
+var g_loader;
+var g_loadInfo;
+var g_reflectionClipState;
+var g_refractionClipState;
+var g_mainRenderGraphRoot;
+var g_reflectionSurfaceSet;
+var g_refractionSurfaceSet;
+var g_skyDomeTransform;
+var g_waterTransform;
+var g_reflectionTexture;
+var g_refractionTexture;
+var g_globalParams;
+var g_globalClockParam;
+var g_clipHeightParam;
+var g_lightPositionParam;
+var g_lightDirectionParam;
+var g_lightColorParam;
+var g_proxyOffsetParam;
+var g_originalLightColor;
+var g_viewPositionParam;
+var g_underwaterMaterials;
+var g_whiteTexture;
+var g_whiteSampler;
+var g_waterMaterial;
+var g_waterEffect;
+var g_waterColorAndSkyEffect;
+var g_waterStyle2Effect;
+var g_torchMaterial;
+var g_torchEmitter;
+var g_torchTexture;
+var g_mistTexture;
+var g_topMistEmitter;
+var g_bottomMistEmitter;
+var g_rippleEmitter;
+var g_skyDomeMaterial;
+var g_o3dWidth = -1;
+var g_o3dHeight = -1;
+var g_o3dElement;
+var g_cameraInfos = [];
+var g_cameraMoveSpeedMultiplier = 50;
+var g_keyCurrentlyDown = 0; // If any key is currently down this is true.
+var g_keyDown = []; // which keys are down by key code.
+var g_keyDownKeyCodeFunctions = {};
+var g_keyUpKeyCodeFunctions = {};
+var g_materialSwapTable = [];
+var g_showingSimpleMaterialsMode = 0;
+var g_simpleEffects = [];
+var g_originalSampler = { };
+var g_dragStartContext;
+var g_dragging = false;
+var g_imageShape;
+var g_imageMaterial;
+var g_imageEffect;
+var g_waterColor = [0.13, 0.19, 0.22, 1];
+var g_hudQuad;
+var g_fpsManager;
+var g_fpsVisible = false;
+var g_particleSystem;
+var g_particleLoader;
+var g_downloadPercent = -1;
+var g_showError = false;
+var g_sceneEffects = [];
+var g_sceneTexturesByURI;
+
+var g_camera = {
+ farPlane: 80000,
+ nearPlane: 10,
+ up: [0, 0, 1],
+ fieldOfView: Math.PI / 4, // 45 degrees
+ eye: [-9662, -10927, 1920],
+ targetVector: [0.43, 0.90, 0.02],
+ xAxis: [0.8335, -0.5522, -0.0157],
+ minFieldOfView: 5 * Math.PI / 180,
+ maxFieldOfView: 70 * Math.PI / 180
+};
+
+var g_cameraPoints = [
+ { duration: 15,
+ start: {eye: [-7952.043, -3027.629, 1782.494],
+ targetVector: [0.054, 0.997, -0.059],
+ fieldOfView: 45},
+ end: {eye: [4579.533, -3707.403, 1782.494],
+ targetVector: [0.221, 0.963, 0.156],
+ fieldOfView: 45}},
+ { duration: 5,
+ start: {eye: [-9862.542, -11676.196, 1888.062],
+ targetVector: [0.552, 0.834, -0.007],
+ fieldOfView: 45},
+ end: {eye: [-4809.674, -4048.170, 1822.536],
+ targetVector: [0.552, 0.834, -0.007],
+ fieldOfView: 45}},
+ { duration: 5,
+ start: {eye: [2728.344, -6393.682, 2241.729],
+ targetVector: [-0.312, 0.949, 0.039],
+ fieldOfView: 45},
+ end: {eye: [-1683.553, 3379.889, 3616.049],
+ targetVector: [-0.118, 0.796, 0.594],
+ fieldOfView: 45}},
+ { duration: 5,
+ start: {eye: [1499.756, -2208.060, 380.914],
+ targetVector: [-0.537, 0.788, 0.303],
+ fieldOfView: 45},
+ end: {eye: [7333.003, -6937.257, 4163.998],
+ targetVector: [-0.811, 0.509, -0.290],
+ fieldOfView: 45}},
+ { duration: 5,
+ start: {eye: [4746.377, 1086.006, 3433.715],
+ targetVector: [-0.982, 0.188, -0.036],
+ fieldOfView: 45},
+ end: {eye: [4746.377, 1086.006, 3433.715],
+ targetVector: [-0.996, 0.072, 0.044],
+ fieldOfView: 6.49667876045379}},
+ { duration: 5,
+ start: {eye: [-4173.890, -4212.830, 398.267],
+ targetVector: [-0.339, 0.900, 0.272],
+ fieldOfView: 45},
+ end: {eye: [-4149.606, -4391.048, 2110.549],
+ targetVector: [0.007, 0.998, 0.065],
+ fieldOfView: 45}},
+ { duration: 5,
+ start: {eye: [-4809.674, -4048.170, 1822.536],
+ targetVector: [0.294, 0.956, -0.022],
+ fieldOfView: 45},
+ end: {eye: [-4535.282, -187.079, 2537.158],
+ targetVector: [0.146, 0.971, 0.190],
+ fieldOfView: 45}}];
+
+// The artists followed no rules. The assumption by the o3djs libraries
+// is that textures with non-one alpha should be drawn with alpha
+// blending on in the zOrderedDrawPass, otherwise they should be drawn
+// with alpha blending off in the performanceDrawPass but the artists gave
+// us textures that have non-one alpha even though they are meant to be
+// drawn opaque.
+//
+// The next most common way to decide whether to use opaque or
+// transparent blending is a naming convention but the arists
+// didn't do that either.
+//
+// For some cases it doesn't really matter but, (1) drawing with alpha
+// blending on is much slower than off and (2) drawing in the
+// zOrderedDrawPass has to sort which is slow and sometimes things
+// can get sorted wrong if they are too large relative to each other.
+//
+// So, here's a hard coded list to set the materials to the correct
+// drawList :-(
+function makeInfo(list, reflect, refract, main, type, effect) {
+ return {
+ list: list,
+ reflect: reflect,
+ refract: refract,
+ main: main,
+ type: type,
+ effect: effect};
+}
+var g_materialLists = {
+ // ---------------------------- list reflect refract main adj
+ '_6_-_Default': makeInfo(0, true, false, true, 1,
+ 'just_color'),
+ 'default': makeInfo(1, false, false, true, 1, // palmTreeB
+ 'diffuse_bump'),
+ 'Folg_BushA_mat': makeInfo(1, true, false, true, 1,
+ 'diffuse_bump'),
+ 'Folg_BushB_mat': makeInfo(1, true, false, true, 1,
+ 'diffuse_bump'),
+ 'Folg_BushC_mat': makeInfo(1, true, false, true, 1,
+ 'diffuse_bump'),
+ 'Folg_coralD_mat': makeInfo(1, false, true, false, 1,
+ 'diffuse'),
+ 'Folg_coralG_mat': makeInfo(1, false, true, false, 1,
+ 'diffuse'),
+ 'Folg_coralRockA_mat': makeInfo(0, false, true, false, 2,
+ 'diffuse_bump_2textures'),
+ 'Folg_coralRockB_mat': makeInfo(0, false, true, false, 2,
+ 'diffuse_bump_2textures'),
+ 'Folg_FernA_mat': makeInfo(1, true, false, true, 1,
+ 'diffuse'),
+ 'Folg_hangingFerns_mat': makeInfo(1, true, false, true, 1,
+ 'diffuse_bump'),
+ 'Folg_largeFernA_mat': makeInfo(1, true, false, true, 1,
+ 'diffuse'),
+ 'Folg_LeafyPlantA_mat': makeInfo(1, true, false, true, 1,
+ 'diffuse_bump'),
+ 'Folg_palmTreeA': makeInfo(1, false, false, true, 1,
+ 'diffuse'),
+ 'Prop_brokenShip_mat': makeInfo(0, true, true, true, 0,
+ 'diffuse'),
+ 'Prop_pillarA_mat': makeInfo(0, false, false, true, 0,
+ 'diffuse_bump_specular'),
+ 'prop_tikiMaskA': makeInfo(0, false, false, true, 0,
+ 'diffuse_bump_specular'),
+ 'Prop_TorchA_mat': makeInfo(0, false, false, true, 0,
+ 'diffuse'),
+ 'Prop_wallA_mat': makeInfo(0, false, false, true, 0,
+ 'diffuse_bump'),
+ 'Props_Bridge_mat': makeInfo(0, true, false, true, 0,
+ 'diffuse'),
+ 'Rock_Dark': makeInfo(0, true, true, true, 2,
+ 'diffuse_bump'),
+ 'Sand_Dark': makeInfo(0, false, true, false, 0,
+ 'diffuse_bump_2textures'),
+ 'Standard_2': makeInfo(0, true, true, false, 0, // palmfrawns
+ 'diffuse'),
+ 'Standard_3': makeInfo(1, false, true, true, 0, // waterfall
+ ''),
+ 'Rock_Dark_Island': makeInfo(0, true, true, true, 2, // Island
+ 'diffuse_bump_blend')};
+
+var g_randSeed = 0;
+var g_randRange = Math.pow(2, 32);
+
+/**
+ * Dumps a vector with a name label in a format useful for javascript.
+ * @param {string} name The name.
+ * @param {!o3d.math.Vector3} v The vector.
+ */
+function dumpVector(name, v) {
+ o3djs.dump.dump(
+ ' ' + name + ': [' +
+ v[0].toFixed(3) + ', ' +
+ v[1].toFixed(3) + ', ' +
+ v[2].toFixed(3) + '],\n');
+}
+
+/**
+ * Dump the camera info in a format useful for javascript.
+ */
+function dumpCameraInfo() {
+ o3djs.dump.dump('{');
+ dumpVector('eye', g_camera.eye);
+ dumpVector('targetVector', g_camera.targetVector);
+ o3djs.dump.dump(' fieldOfView: ' +
+ g_math.radToDeg(g_camera.fieldOfView) + '};\n');
+}
+
+/**
+ * Returns a deterministic pseudorandom number bewteen 0 and 1.
+ * @return {number} a random number between 0 and 1.
+ */
+function pseudoRandom() {
+ return (g_randSeed = (134775813 * g_randSeed + 1) % g_randRange) /
+ g_randRange;
+}
+
+// ***************************** Mouse functions *******************************
+
+/**
+ * Handler for onmousedown.
+ * @param {event} e A mouse event.
+ */
+function onMouseDown(e) {
+ if (!g_keyCurrentlyDown) {
+ g_dragging = true;
+ g_dragStartContext = {
+ view: o3djs.math.copyMatrix(g_mainViewInfo.drawContext.view),
+ projection: o3djs.math.copyMatrix(g_mainViewInfo.drawContext.projection),
+ offsetX: g_client.width * 0.5 - e.x,
+ offsetY: g_client.height * 0.5 - e.y
+ };
+ stopAnimatedCamera();
+ }
+}
+
+/**
+ * Handler for onmousemove.
+ * @param {event} e A mouse event.
+ */
+function onMouseMove(e) {
+ if (g_dragging) {
+ // Compute the world ray based on the view we had when we started dragging.
+ var worldRay = o3djs.picking.clientPositionToWorldRayEx(
+ g_o3dWidth - (e.x + g_dragStartContext.offsetX),
+ g_o3dHeight - (e.y + g_dragStartContext.offsetY),
+ g_dragStartContext.view,
+ g_dragStartContext.projection,
+ g_o3dWidth,
+ g_o3dHeight);
+
+ g_camera.targetVector = g_math.normalize(g_math.subVector(worldRay.near,
+ g_camera.eye));
+ updateCamera();
+ stopAnimatedCamera();
+ }
+}
+
+/**
+ * Handler for onmouseup.
+ * @param {event} e A mouse event.
+ */
+function onMouseUp(e) {
+ g_dragging = false;
+}
+
+/**
+ * Hander for the scroll wheel.
+ * @param {Event} e Mouse event.
+ */
+function onWheel(e) {
+ if (e.deltaY) {
+ var target = g_camera.minFieldOfView;
+ if (e.deltaY < 0) {
+ target = g_camera.maxFieldOfView;
+ }
+
+ g_camera.fieldOfView = g_math.lerpVector(target, g_camera.fieldOfView, 0.9);
+
+ updateProjection();
+ stopAnimatedCamera();
+ }
+}
+
+// *************************** Keyboard functions ******************************
+
+/**
+ * Tracks key down events.
+ * @param {Event} e keyboard event.
+ */
+function onKeyDown(e) {
+ if (!g_dragging && !g_keyDown[e.keyCode]) {
+ ++g_keyCurrentlyDown;
+ g_keyDown[e.keyCode] = true;
+
+ var keyFunction = g_keyDownKeyCodeFunctions[e.keyCode];
+ if (keyFunction) {
+ keyFunction(e);
+ }
+ }
+}
+
+/**
+ * Tracks key up events.
+ * @param {Event} e keyboard event.
+ */
+function onKeyUp(e) {
+ if (g_keyDown[e.keyCode]) {
+ --g_keyCurrentlyDown;
+ g_keyDown[e.keyCode] = false;
+
+ var keyFunction = g_keyUpKeyCodeFunctions[e.keyCode];
+ if (keyFunction) {
+ keyFunction(e);
+ }
+ }
+}
+
+/**
+ * Converts a keyCode or charCode to a keyCode.
+ * @param {number|string} code The key code or char code.
+ * @return {number} the key code.
+ */
+function convertToKeyCode(code) {
+ if (typeof(code) == 'string') {
+ code = code.charCodeAt(0);
+ if (code >= 'a'.charCodeAt(0)) {
+ code += 65 - 'a'.charCodeAt(0);
+ }
+ }
+ return code;
+}
+
+/**
+ * Registers a key code with a key up function.
+ * @param {number|string} keyCode The key code to register a function with.
+ * @param {!function(!event): void} keyFunction A function that will be passed
+ * the event for the key.
+ */
+function registerKeyDownFunction(keyCode, keyFunction) {
+ g_keyDownKeyCodeFunctions[convertToKeyCode(keyCode)] = keyFunction;
+}
+
+/**
+ * Registers a key code with a key down function.
+ * @param {number|string} keyCode The key code to register a function with.
+ * @param {!function(!event): void} keyFunction A function that will be passed
+ * the event for the key.
+ */
+function registerKeyUpFunction(keyCode, keyFunction) {
+ g_keyUpKeyCodeFunctions[convertToKeyCode(keyCode)] = keyFunction;
+}
+
+/**
+ * Registers a key code with a both a key down and key up function.
+ * @param {number|string} keyCode The key code to register a function with.
+ * @param {!function(!event): void} keyUpFunction A function that will be passed
+ * the event for the key being released.
+ * @param {!function(!event): void} keyDownFunction A function that will be
+ * passed the event for the key being down..
+ */
+function registerKeyUpDownFunction(keyCode, keyUpFunction, keyDownFunction) {
+ registerKeyUpFunction(keyCode, keyUpFunction);
+ registerKeyUpFunction(keyCode, keyDownFunction);
+}
+
+/**
+ * Registers key handlers.
+ */
+function registerKeyHandlers() {
+ registerKeyDownFunction('0', keySetCamera);
+ registerKeyDownFunction('1', keySetCamera);
+ registerKeyDownFunction('2', keySetCamera);
+ registerKeyDownFunction('3', keySetCamera);
+ registerKeyDownFunction('4', keySetCamera);
+ registerKeyDownFunction('5', keySetCamera);
+ registerKeyDownFunction('6', keySetCamera);
+ registerKeyDownFunction('7', keySetCamera);
+ registerKeyDownFunction('8', keySetCamera);
+ registerKeyDownFunction('9', keySetCamera);
+
+ registerKeyDownFunction('h', toggleHelp);
+ registerKeyDownFunction('p', togglePropsPanel);
+ registerKeyDownFunction('m', toggleMaterialPanel);
+ registerKeyDownFunction('e', toggleEffectPanel);
+ registerKeyDownFunction('r', toggleRenderTargets);
+ registerKeyDownFunction('f', toggleFps);
+ registerKeyDownFunction('c', toggleSimpleMaterials);
+ registerKeyDownFunction('o', toggleWaterEffect);
+ registerKeyDownFunction('q', toggleDemoCamera);
+
+ // Comment the line below in to enable dumping camera info.
+ // This can be used to generate camera points for the animated
+ // camera but is only compatible with Firefox.
+ //registerKeyDownFunction('z', dumpCameraInfo);
+}
+
+// **************************** Camera Functions *******************************
+
+/**
+ * Updates the camera (the view matrix of the drawContext) with the current
+ * camera settings.
+ */
+function updateCamera() {
+ var target = g_math.addVector(g_camera.eye, g_camera.targetVector);
+ var view = g_math.matrix4.lookAt(g_camera.eye,
+ target,
+ g_camera.up);
+ g_viewPositionParam.value = g_camera.eye;
+ g_mainViewInfo.drawContext.view = view;
+ g_reflectionViewInfo.drawContext.view = view;
+ g_refractionViewInfo.drawContext.view = view;
+ var cameraMatrix = g_math.inverse4(view);
+ g_camera.xAxis = cameraMatrix[0].splice(0, 3);
+ g_updateRenderTargets = true;
+}
+
+/**
+ * Updates the projection matrix of the drawContext with the current camera
+ * settings.
+ */
+function updateProjection() {
+ // Create a perspective projection matrix.
+ g_mainViewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_camera.fieldOfView, g_o3dWidth / g_o3dHeight, g_camera.nearPlane,
+ g_camera.farPlane);
+
+ g_reflectionViewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_camera.fieldOfView, g_o3dWidth / g_o3dHeight,
+ g_camera.nearPlane, g_camera.farPlane);
+
+ g_refractionViewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_camera.fieldOfView, g_o3dWidth / g_o3dHeight,
+ g_camera.nearPlane, g_camera.farPlane);
+
+ g_hudViewInfo.drawContext.projection = g_math.matrix4.orthographic(
+ 0 + 0.5,
+ g_o3dWidth + 0.5,
+ g_o3dHeight + 0.5,
+ 0 + 0.5,
+ 0.001,
+ 1000);
+ g_updateRenderTargets = true;
+}
+
+/**
+ * Update the fader plane for the current client area size.
+ */
+function updateFaderPlane() {
+ // Scale fader plane to cover screen.
+ // If we made a custom shader for this this wouldn't be needed.
+ g_faderTransform.identity();
+ g_faderTransform.translate(0, 0, -10);
+ g_faderTransform.scale(g_client.width, g_client.height, 1);
+}
+
+/**
+ * Sets the camera to a preset.
+ * @param {number} cameraIndex Index of camera preset.
+ */
+function setCamera(cameraIndex) {
+ var cameraInfo = g_cameraInfos[cameraIndex];
+ // pull out camera info from view matrix.
+ var cameraMatrix = g_math.inverse4(cameraInfo.view);
+ g_camera.eye = cameraMatrix[3].splice(0, 3);
+ g_camera.targetVector = g_math.negativeVector(cameraMatrix[2].splice(0, 3));
+ //g_camera.fieldOfView = cameraInfo.fieldOfViewRadians;
+ g_camera.fieldOfView = o3djs.math.degToRad(45);
+
+ updateCamera();
+ updateProjection();
+ stopAnimatedCamera();
+}
+
+/**
+ * Moves the camera in its local X axis.
+ * @param {number} direction Position or negative amount to move.
+ */
+function moveCameraLeftRight(direction) {
+ direction *= g_cameraMoveSpeedMultiplier;
+ g_camera.eye = g_math.addVector(
+ g_camera.eye,
+ g_mathVectorScalar.mul(g_camera.xAxis, direction));
+ updateCamera();
+ stopAnimatedCamera();
+}
+
+/**
+ * Moves the camera in its local Z axis.
+ * @param {number} direction Position or negative amount to move.
+ */
+function moveCameraForwardBack(direction) {
+ direction *= g_cameraMoveSpeedMultiplier;
+ g_camera.eye = g_math.addVector(
+ g_camera.eye,
+ g_math.mulVectorScalar(g_camera.targetVector, direction));
+ updateCamera();
+ stopAnimatedCamera();
+}
+
+// ************************ Effect Editor Functions ****************************
+
+/**
+ * Starts editing an effect.
+ * @param {number} effectId The clientId of the effect.
+ */
+function editEffect(effectId) {
+ if (g_currentEditEffect) {
+ // Save the current edit.
+ // TODO: would it be better to have a textarea per effect and
+ // hide / unhide them?
+ g_editableEffectsSource[g_currentEditEffect.clientId] =
+ g_effectTextAreaElement.value;
+ }
+
+ var effect = g_client.getObjectById(effectId);
+ g_effectTextAreaElement.value = g_editableEffectsSource[effectId];
+
+ g_currentEditEffect = effect;
+}
+
+/**
+ * Edits an effect from the value on an option element
+ */
+function editEffectFromElement() {
+ var element = o3djs.util.getElementById('effectselect');
+ editEffect(parseInt(element.value));
+}
+
+/**
+ * Compiles the current effect.
+ */
+function compileEffect() {
+ if (g_currentEditEffect) {
+ var source = g_effectTextAreaElement.value;
+
+ // Turn off the default error callback so we can get the error ourselves.
+ g_client.clearErrorCallback();
+ g_client.clearLastError();
+ g_compileEffect.loadFromFXString(source);
+ var error = g_client.lastError;
+ o3djs.base.setErrorHandler(g_client);
+ if (error) {
+ alert(error);
+ } else {
+ g_currentEditEffect.loadFromFXString(source);
+ // TODO: call createUniformParameters for all materials
+ // using this effect then call setupMaterialEditor so it will
+ // display new parameters.
+
+ // Tell the render targets to update.
+ g_updateRenderTargets = true;
+ }
+ }
+}
+
+/**
+ * Setup effect editor.
+ */
+function setupEffectEditor() {
+ // create an effect for testing.
+ g_compileEffect = g_mainPack.createObject('Effect');
+
+ var compileButton = o3djs.util.getElementById('compileButton');
+ compileButton.onclick = compileEffect;
+
+ // create pseudo tabs.
+ // TODO: Make it look prettier.
+ var html = '<select id="effectselect">';
+ for (var ii = 0; ii < g_editableEffects.length; ++ii) {
+ var effect = g_editableEffects[ii];
+ g_editableEffectsSource[effect.clientId] = effect.source;
+ html += '' +
+ '<option value="' + effect.clientId + '">' + effect.name + '</option>';
+ }
+ g_effectTabsElement.innerHTML = html + '</select>';
+ var element = o3djs.util.getElementById('effectselect');
+ element.onchange = editEffectFromElement;
+ element.onblur = editEffectFromElement;
+
+ // Setup the first effect.
+ editEffect(g_editableEffects[0].clientId);
+}
+
+// ************************* Prop Editor Functions *****************************
+
+/**
+ * Setups the prop editor.
+ */
+function setupPropEditor() {
+ var propPrefixes = {watersurface: true};
+ var transforms = g_scenePack.getObjectsByClassName('o3d.Transform');
+ for (var tt = 0; tt < transforms.length; ++tt) {
+ var transform = transforms[tt];
+ if (transform.shapes.length > 0) {
+ var name = transform.name;
+ //if (!isNaN(name.substring(name.length -1))) {
+ // var prefix = name.replace(/\d*$/, '');
+ // if (prefix.length > 0) {
+ // propPrefixes[prefix] = true;
+ // }
+ //}
+ propPrefixes[name] = true;
+ }
+ }
+
+ var html = '<table>';
+ var count = 0;
+ for (var prefix in propPrefixes) {
+ html += '' +
+ '<tr class="' + ((count % 2 == 0) ? 'even' : 'odd') + '"><td>' +
+ '<input id="prop_' + prefix + '" ' +
+ 'type="checkbox" CHECKED />' +
+ prefix +
+ '</td></tr>';
+ ++count;
+ }
+ g_propPanelElement.innerHTML = html + '</table>';
+ for (var prefix in propPrefixes) {
+ var input = o3djs.util.getElementById('prop_' + prefix);
+ input.onclick = o3djs.util.curry(toggleProp, prefix);
+ }
+}
+
+/**
+ * Toggles props.
+ * Goes through all transforms in the client and if their name starts with
+ * prefix sets their visibility to true or false.
+ * @param {string} prefix Prefix of props to toggle.
+ */
+function toggleProp(prefix) {
+ var element = o3djs.util.getElementById('prop_' + prefix);
+ var visible = element.checked;
+ // We should probably cache all the transforms since this is an expensive
+ // operation.
+ var transforms = g_client.getObjectsByClassName('o3d.Transform');
+ for (var tt = 0; tt < transforms.length; ++tt) {
+ var transform = transforms[tt];
+ if (transform.name.substring(0, prefix.length) === prefix) {
+ transform.visible = visible;
+ }
+ }
+ // Tell the render targets to update.
+ g_updateRenderTargets = true;
+}
+
+// *********************** Material Editor Functions ***************************
+
+/**
+ * Escapes a string, changing < to &lt;
+ * @param {string} str to escape.
+ * @return {string} escaped string.
+ */
+function escapeHTML(str) {
+ return str.replace(/</g, '&lt;');
+}
+
+/**
+ * Gets a param value as a string
+ * @param {!o3d.Param} param Param to get value from.
+ * @return {string} value of param as a string.
+ */
+function getParamAsString(param) {
+ if (param.isAClassName('o3d.ParamFloat')) {
+ return param.value.toFixed(5);
+ } else if (param.isAClassName('o3d.ParamFloat4')) {
+ var value = param.value;
+ for (var ii = 0; ii < value.length; ++ii) {
+ value[ii] = value[ii].toFixed(2);
+ }
+ return value.toString();
+ } else {
+ return '--na--';
+ }
+}
+
+/**
+ * Reads the current value of the input and sets the matching param to that
+ * value.
+ * @param {number} paramId Id of param and input.
+ */
+function updateParam(paramId) {
+ var param = g_client.getObjectById(paramId);
+ var element = o3djs.util.getElementById('param_' + paramId);
+ var value = element.value;
+ var error = false;
+ var v;
+ if (param.isAClassName('o3d.ParamFloat')) {
+ if (isNaN(value)) {
+ error = true;
+ }
+ v = parseFloat(value);
+ } else if (param.isAClassName('o3d.ParamFloat4')) {
+ var values = value.split(/ *, *| +/);
+ if (values.length != 4) {
+ error = true;
+ } else {
+ v = [];
+ for (var ii = 0; ii < values.length; ++ii) {
+ if (isNaN(values[ii])) {
+ error = true;
+ break;
+ }
+ v[ii] = parseFloat(values[ii]);
+ }
+ }
+ }
+
+ if (!error) {
+ param.value = v;
+ // Tell the render targets to update.
+ g_updateRenderTargets = true;
+ }
+
+ element.style.backgroundColor = error ? '#fcc' : '';
+}
+
+/**
+ * Creates the html to edit the given param object.
+ * @param {!o3d.ParamObject} paramObject The param object to create html for.
+ * @param {string} rowClass name of class for row.
+ * @return {string} the generated HTML.
+ */
+function createHTMLForParamObject(paramObject, rowClass) {
+ var html = '' +
+ '<tr class="' + rowClass + '">' +
+ '<td class="name" colspan="2">' + escapeHTML(paramObject.name) + '</td>' +
+ '</tr>';
+ var params = paramObject.params;
+ for (var pp = 0; pp < params.length; ++pp) {
+ var param = params[pp];
+ // Skip builtins and ones with an input connection.
+ if (param.name.substring(0, 4) !== 'o3d.' &&
+ param.inputConnection == null &&
+ (param.isAClassName('o3d.ParamFloat') ||
+ param.isAClassName('o3d.ParamFloat4'))) {
+ html += '' +
+ '<tr>' +
+ '<td class="field">' +
+ '<label>' + escapeHTML(param.name) + '</label>' +
+ '</td>' +
+ '<td class="value">' +
+ '<input type="text" id="param_' + param.clientId + '" ' +
+ 'value="' + getParamAsString(param) + '"></input>' +
+ '</td>' +
+ '</tr>';
+ }
+ }
+ return html;
+}
+
+/**
+ * Sets the onblur and onchange handlers in the html for a given param object.
+ * @param {!o3d.ParamObject} paramObject The param object to create html for.
+ */
+function setHTMLHandlersForParamObject(paramObject) {
+ var params = paramObject.params;
+ for (var pp = 0; pp < params.length; ++pp) {
+ var param = params[pp];
+ // Skip builtins and ones with an input connection.
+ if (param.name.substring(0, 4) !== 'o3d.' &&
+ param.inputConnection == null &&
+ (param.isAClassName('o3d.ParamFloat') ||
+ param.isAClassName('o3d.ParamFloat4'))) {
+ var input = o3djs.util.getElementById('param_' + param.clientId);
+ input.onblur = o3djs.util.curry(updateParam, param.clientId);
+ input.onchange = o3djs.util.curry(updateParam, param.clientId);
+ }
+ }
+}
+
+/**
+ * Sets up html with event handers to edit the material parameters.
+ */
+function setupMaterialEditor() {
+ var html = '<table>';
+ var materials = g_scenePack.getObjectsByClassName('o3d.Material');
+ var count = 0;
+ materials.unshift(g_globalParams);
+ materials.unshift(g_waterMaterial);
+ materials.unshift(g_underwaterMaterials[0]);
+ materials.unshift(g_underwaterMaterials[1]);
+ for (var mm = 0; mm < materials.length; ++mm) {
+ var material = materials[mm];
+ html += createHTMLForParamObject(material, count % 2 == 0 ? 'even' : 'odd');
+ ++count;
+ }
+ g_materialPanelElement.innerHTML = html + '</table>';
+
+ for (var mm = 0; mm < materials.length; ++mm) {
+ var material = materials[mm];
+ setHTMLHandlersForParamObject(material)
+ }
+}
+
+// ************************* Specific Key Handlers *****************************
+
+function setupWaterHeavyUpdateOnlyOnViewChange() {
+ g_waterMaterial.effect = g_waterEffect;
+}
+
+function setupWaterHeavyUpdateAlways() {
+ g_waterMaterial.effect = g_waterEffect;
+}
+
+function setupWaterJustSkyAndColor() {
+ g_waterMaterial.effect = g_waterColorAndSkyEffect;
+}
+
+function setupWaterStyle2() {
+ g_waterMaterial.effect = g_waterStyle2Effect;
+}
+
+/**
+ * Toggles the water effect.
+ * @param {Event} e Event for key that was pressed.
+ */
+function toggleWaterEffect(e) {
+ ++g_waterMode;
+ if (g_waterMode == 4) {
+ g_waterMode = 0;
+ }
+
+ switch (g_waterMode) {
+ case 0:
+ setupWaterHeavyUpdateOnlyOnViewChange();
+ break;
+ case 1:
+ setupWaterHeavyUpdateAlways();
+ break;
+ case 2:
+ setupWaterJustSkyAndColor();
+ break;
+ case 3:
+ setupWaterStyle2();
+ break;
+ }
+}
+
+/**
+ * Toggles the animted camera.
+ * @param {Event} e Event for key that was pressed.
+ */
+function toggleDemoCamera(e) {
+ g_runDemo = !g_runDemo;
+ if (g_runDemo) {
+ g_animateCamera = true;
+ } else {
+ stopAnimatedCamera();
+ }
+}
+
+/**
+ * Restores and original sampler on params of a certain name.
+ * @param {!o3d.ParamObject} paramObject Object to restore samplers on.
+ * @param {string} samplerName Name of sampler parameter to restore.
+ */
+function restoreOriginalSampler(paramObject, samplerName) {
+ var param = paramObject.getParam(samplerName);
+ if (param) {
+ param.value = g_originalSampler[param.clientId];
+ }
+}
+
+/**
+ * Replaces samplers on params of a certain name with a white sampler.
+ * @param {!o3d.ParamObject} paramObject Object to replace samplers on.
+ * @param {string} samplerName Name of sampler parameter to replace samplers on.
+ */
+function replaceSamplerWithWhiteSampler(paramObject, samplerName) {
+ var param = paramObject.getParam(samplerName);
+ if (param) {
+ g_originalSampler[param.clientId] = param.value;
+ param.value = g_whiteSampler;
+ }
+}
+
+/**
+ * Toggles the materials to simple effects.
+ * @param {Event} e Event for key that was pressed.
+ */
+function toggleSimpleMaterials(e) {
+ g_updateRenderTargets = true;
+
+ var materials = g_scenePack.getObjectsByClassName('o3d.Material');
+ materials.unshift(g_waterMaterial);
+ materials.unshift(g_underwaterMaterials[0]);
+ materials.unshift(g_underwaterMaterials[1]);
+
+ ++g_showingSimpleMaterialsMode;
+ g_showingSimpleMaterialsMode = g_showingSimpleMaterialsMode % 4;
+
+ switch (g_showingSimpleMaterialsMode) {
+ case 1: {
+ g_originalLightColor = g_lightColorParam.value;
+ g_lightColorParam.value = [1, 1, 1, 1];
+ var drawElements = g_scenePack.getObjectsByClassName('o3d.DrawElement');
+ for (var ii = 0; ii < drawElements.length; ++ii) {
+ replaceSamplerWithWhiteSampler(drawElements[ii], 'diffuseSampler');
+ }
+ break;
+ }
+ case 2: {
+ g_lightColorParam.value = g_originalLightColor;
+ var drawElements = g_scenePack.getObjectsByClassName('o3d.DrawElement');
+ for (var ii = 0; ii < drawElements.length; ++ii) {
+ restoreOriginalSampler(drawElements[ii], 'diffuseSampler');
+ }
+ break;
+ }
+ }
+
+ for (var mm = 0; mm < materials.length; ++mm) {
+ var material = materials[mm];
+ switch (g_showingSimpleMaterialsMode) {
+ case 0: {
+ material.effect = g_materialSwapTable[material.clientId];
+ break;
+ }
+ case 1: {
+ replaceSamplerWithWhiteSampler(material, 'diffuseSampler');
+ replaceSamplerWithWhiteSampler(material, 'diffuse2Sampler');
+ break;
+ }
+ case 2: {
+ restoreOriginalSampler(material, 'diffuseSampler');
+ restoreOriginalSampler(material, 'diffuse2Sampler');
+
+ var effect = material.effect;
+ g_materialSwapTable[material.clientId] = effect;
+ if (!g_simpleEffects[effect.clientId]) {
+ pseudoRandom(); // eat a random number to get pleasing colors.
+ pseudoRandom(); // eat a random number to get pleasing colors.
+ var newEffect = g_mainPack.createObject('Effect');
+ newEffect.loadFromFXString(
+ o3djs.util.getElementContentById('simpleshader'));
+ newEffect.createUniformParameters(newEffect);
+ newEffect.getParam('simpleColor').value = [
+ pseudoRandom(),
+ pseudoRandom(),
+ pseudoRandom(),
+ 1];
+ g_simpleEffects[effect.clientId] = newEffect;
+ }
+ material.effect = g_simpleEffects[effect.clientId];
+ break;
+ }
+ case 3: {
+ material.effect = g_imageEffect;
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * Toggles the render target display.
+ * @param {Event} e Event for key that was pressed.
+ */
+function toggleRenderTargets(e) {
+ g_renderTargetDisplayRoot.visible = !g_renderTargetDisplayRoot.visible;
+}
+
+/**
+ * Toggles the fps display.
+ * @param {Event} e Event for key that was pressed.
+ */
+function toggleFps(e) {
+ g_fpsVisible = !g_fpsVisible;
+ g_fpsManager.setVisible(g_fpsVisible);
+}
+
+function togglePropsPanel(e) {
+ if (g_propPanelElement.style.display === '') {
+ g_propPanelElement.style.display = 'none';
+ g_sceneElement.style.width = '100%';
+ } else {
+ g_materialPanelElement.style.display = 'none';
+ g_propPanelElement.style.display = '';
+ g_sceneElement.style.width = '80%';
+ }
+}
+
+/**
+ * Toggles the material panel.
+ * @param {Event} e Event for key that was pressed.
+ */
+function toggleMaterialPanel(e) {
+ if (g_materialPanelElement.style.display === '') {
+ g_materialPanelElement.style.display = 'none';
+ g_sceneElement.style.width = '100%';
+ } else {
+ g_propPanelElement.style.display = 'none';
+ g_materialPanelElement.style.display = '';
+ g_sceneElement.style.width = '80%';
+ }
+}
+
+/**
+ * Toggles the effect panel.
+ * @param {Event} e Event for key that was pressed.
+ */
+function toggleEffectPanel(e) {
+ if (g_effectPanelElement.style.display === '') {
+ g_effectPanelElement.style.display = 'none';
+ g_upperPanelElement.style.height = '100%';
+ } else {
+ g_effectPanelElement.style.display = '';
+ g_upperPanelElement.style.height = '70%';
+ }
+}
+
+/**
+ * Sets the camera to a camera preset from a key press.
+ * @param {Event} e Event for key that was pressed. Expects 0-9.
+ */
+function keySetCamera(e) {
+ var index = e.keyCode - 49;
+ if (index < 0) {
+ index = 9;
+ }
+ var cameraInfo = g_cameraInfos[index];
+ if (cameraInfo) {
+ setCamera(index);
+ }
+}
+
+// ***************************** Scene Functions *******************************
+
+/**
+ * Sets the position of the sun, updating shader parameters.
+ * @param {!o3djs.math.Vector3} position The position of the sun.
+ */
+function setSunPosition(position) {
+ g_lightPositionParam.value = position;
+ g_lightDirectionParam.value = g_math.negativeVector(
+ g_math.normalize(position));
+ g_lightDirectionParam.value = g_math.normalize(position);
+}
+
+// ********************************** Misc *************************************
+
+/**
+ * Sets a param if it exists.
+ * @param {!o3d.ParamObject} paramObject The object that has the param.
+ * @param {string} paramName name of param.
+ * @param {*} value the value for the param.
+ */
+function setParam(paramObject, paramName, value) {
+ var param = paramObject.getParam(paramName);
+ if (param) {
+ param.value = value;
+ }
+}
+
+/**
+ * Binds a param if it exists.
+ * @param {!o3d.ParamObject} paramObject The object that has the param.
+ * @param {string} paramName name of param.
+ * @param {!o3d.Param} sourceParam The param to bind to.
+ */
+function bindParam(paramObject, paramName, sourceParam) {
+ var param = paramObject.getParam(paramName);
+ if (param) {
+ param.bind(sourceParam);
+ }
+}
+
+/**
+ * Prints out a transform tree.
+ * @param {!o3d.Transform} transform transform to print.
+ * @param {string} prefix Prefix to print.
+ */
+function dumpTransforms(transform, prefix) {
+ var materialName = '';
+ var shapes = transform.shapes;
+ if (shapes.length > 0) {
+ materialName = ' (' + shapes[0].elements[0].material.name + ')';
+ }
+ o3djs.dump.dump(prefix + transform.name + materialName + '\n');
+ var children = transform.children;
+ for (var cc = 0; cc < children.length; ++cc) {
+ dumpTransforms(children[cc], prefix + ' ');
+ }
+}
+
+/**
+ * Adds transforms at each level of the scene to group things by where they
+ * need to be rendered, refraction, main, both.
+ * @param {!o3d.Transform} transform Transform to scan.
+ */
+function getSpeedTransforms(transform) {
+ // 0 : neither, 1 : main, 2 : reflect, 3 : both
+ var speedTransforms = [];
+ var children = transform.children;
+ for (var cc = 0; cc < children.length; ++cc) {
+ var child = children[cc];
+ var check = child;
+
+ // If a child has a single child of the same but with the suffix
+ // '_PIVOT' use that as the check node.
+ var checkChildren = child.children;
+ if (checkChildren.length == 1 &&
+ checkChildren[0].name == child.name + '_PIVOT') {
+ check = checkChildren[0];
+ }
+ // If check has a shape that has a primitive that uses one of the
+ // materials on the list then attach it to a speed transform.
+ var grouped = false;
+ var shapes = check.shapes;
+ if (shapes.length > 0) {
+ // gets assume 1 shape, 1 element
+ var material = shapes[0].elements[0].material;
+ var materialInfo = g_materialLists[material.name];
+ if (materialInfo) {
+ grouped = true;
+ var index = (materialInfo.main ? 1 : 0) +
+ (materialInfo.reflect ? 2 : 0);
+ var speedTransform = speedTransforms[index];
+ if (!speedTransform) {
+ speedTransform = g_mainPack.createObject('Transform');
+ speedTransform.name = 'speed_' + index;
+ speedTransform.parent = transform;
+ speedTransforms[index] = speedTransform;
+ }
+ child.parent = speedTransform;
+ }
+ }
+
+ if (!grouped) {
+ getSpeedTransforms(child);
+ }
+ }
+
+ // Now add speed transforms to global list.
+ for (var ii = 0; ii < 4; ++ii) {
+ if (speedTransforms[ii]) {
+ g_speedTransforms[ii].push(speedTransforms[ii]);
+ }
+ }
+}
+
+/**
+ * Gets a texture from g_scenePack.
+ * @param {string} textureName Name of texture.
+ * @return {!o3d.Texture} The requested texture.
+ */
+function getTexture(textureName) {
+ // I'm searching by URI because the old conditioner sadly renamed all the
+ // textures making it next to impossible to find things :-(
+ if (!g_sceneTexturesByURI) {
+ g_sceneTexturesByURI = { };
+ var textures = g_scenePack.getObjectsByClassName('o3d.Texture');
+ for (var tt = 0; tt < textures.length; ++tt) {
+ var texture = textures[tt];
+ var uri = texture.getParam('uri').value;
+ g_sceneTexturesByURI[uri] = texture;
+ }
+ }
+
+ return g_sceneTexturesByURI['images/' + textureName];
+}
+
+/**
+ * Adds a texture to a material.
+ * @param {!o3d.Material} material Material to add texture to.
+ * @param {string} samplerName Name of sampler parameter to attach texture to.
+ * @param {string} textureName Name of texture.
+ */
+function addTexture(material, samplerName, textureName) {
+ var param = material.createParam(samplerName, 'ParamSampler');
+ var sampler = g_scenePack.createObject('Sampler');
+ param.value = sampler;
+ sampler.texture = getTexture(textureName);
+}
+
+/**
+ * Sets up the materials in the scene.
+ */
+function setupSceneMaterials() {
+ var drawLists = [g_mainViewInfo.performanceDrawList,
+ g_mainViewInfo.zOrderedDrawList];
+
+ var adjust = [
+ {shininess: 50, specular: [0.5, 0.5, 0.5, 1]},
+ {shininess: 100, specular: [0.3, 0.5, 0.3, 1]},
+ {shininess: 80, specular: [0.3, 0.3, 0.3, 1]}];
+
+ // Setup the materials. Because Collada can't really handle
+ // the materials needed we pretty much have to do this manaually. It would
+ // have been good to make a rule for it but I have no time.
+ for (var name in g_materialLists) {
+ var info = g_materialLists[name];
+ var materials = g_scenePack.getObjects(name, 'o3d.Material');
+ for (var mm = 0; mm < materials.length; ++mm) {
+ var material = materials[mm];
+ if (info.effect) {
+ var effect = g_sceneEffects[info.effect];
+ if (!effect) {
+ effect = g_scenePack.createObject('Effect');
+ effect.name = info.effect;
+ var fxString = o3djs.util.getElementContentById(info.effect);
+ effect.loadFromFXString(fxString);
+ g_sceneEffects[info.effect] = effect;
+ g_editableEffects.push(effect);
+ }
+ material.effect = effect;
+ material.createParam('lightWorldPos', 'ParamFloat3');
+ material.createParam('lightColor', 'ParamFloat4');
+ material.createParam('clipHeight', 'ParamFloat');
+
+ // special handling for island and seafloor materials.
+ if (info.effect == 'diffuse_bump_blend') {
+ addTexture(material, 'diffuse2Sampler', 'image1.dds');
+ }
+ }
+ material.drawList = drawLists[info.list];
+
+ // Manually connect all the materials' lightWorldPos params or a global
+ // light param.
+ bindParam(material, 'lightWorldPos', g_lightPositionParam);
+ bindParam(material, 'lightColor', g_lightColorParam);
+ bindParam(material, 'clipHeight', g_clipHeightParam);
+ setParam(material, 'ambient', [0.2, 0.2, 0.2, 1]);
+
+ var type = info.type;
+ setParam(material, 'shininess', adjust[type].shininess);
+ setParam(material, 'specular', adjust[type].specular);
+ }
+ }
+}
+
+/**
+ * Loads the proxy.
+ */
+function loadProxy() {
+ function callback(pack, parent, exception) {
+ g_loadInfo = null;
+ if (exception) {
+ showError(exception);
+ } else {
+ loadMainScene();
+
+ o3djs.pack.preparePack(pack, g_mainViewInfo);
+
+ var material = pack.getObjectsByClassName('o3d.Material')[0];
+ var effect = g_mainPack.createObject('Effect');
+ effect.loadFromFXString(o3djs.util.getElementContentById('proxy'));
+ effect.createUniformParameters(material);
+ setParam(material, 'lightWorldPos', [0, -100000, 200000]);
+ setParam(material, 'ambient', [0, 0, 0, 0]);
+ setParam(material, 'diffuse', [0.7, 0.7, 0.7, 0.5]);
+ setParam(material, 'specular', [0, 0, 0, 0]);
+ bindParam(material, 'offset', g_proxyOffsetParam);
+ material.effect = effect;
+ material.drawList = g_mainViewInfo.zOrderedDrawList;
+ var state = pack.createObject('State');
+ material.state = state;
+ state.getStateParam('AlphaReference').value = 0.0;
+ state.getStateParam('CullMode').value = g_o3d.State.CULL_CCW;
+ var material2 = pack.createObject('Material');
+ effect.createUniformParameters(material2);
+ material2.copyParams(material);
+ bindParam(material2, 'offset', g_proxyOffsetParam);
+ material2.effect = effect;
+ material2.drawList = g_mainViewInfo.zOrderedDrawList;
+
+ state = pack.createObject('State');
+ material2.state = state;
+ state.getStateParam('AlphaReference').value = 0.0;
+ state.getStateParam('CullMode').value = g_o3d.State.CULL_CW;
+
+ parent.createDrawElements(pack, material2);
+ }
+ }
+
+ g_proxyPack = g_client.createPack();
+ g_proxyRoot = g_proxyPack.createObject('Transform');
+ g_proxyRoot.parent = g_baseRoot;
+
+ try {
+ var url = o3djs.util.getAbsoluteURI('assets/beach-low-poly.o3dtgz');
+ g_loadInfo = o3djs.scene.loadScene(g_client, g_proxyPack, g_proxyRoot,
+ url, callback, {opt_async: true});
+ } catch (e) {
+ showError(e);
+ }
+}
+
+/**
+ * Loads the main scene.
+ */
+function loadMainScene() {
+ function callback(pack, parent, exception) {
+ g_loadInfo = null;
+ if (exception) {
+ showError(exception);
+ } else {
+ g_proxyRoot.visible = false;
+
+ setupWaterfall();
+
+ // Generate draw elements and setup material draw lists.
+ parent.createDrawElements(pack, null);
+
+ setupSceneMaterials();
+
+ // Turn off culling since we can see the entire world checking culling
+ // is a waste of CPU time.
+ var elements = g_scenePack.getObjectsByClassName('o3d.Element');
+ for (var ee = 0; ee < elements.length; ++ee) {
+ elements[ee].cull = false;
+ o3djs.element.setBoundingBoxAndZSortPoint(elements[ee]);
+ }
+
+ // Add missing streams to terrain.
+ var terrainNames = [
+ 'terrainSpireA_002',
+ 'terrainSpireA_003',
+ 'terrainLargeRock',
+ 'terrainSpireA_001'];
+ var semantics = [
+ g_o3d.Stream.TEXCOORD,
+ g_o3d.Stream.BINORMAL,
+ g_o3d.Stream.TANGENT];
+ for (var tt = 0; tt < terrainNames.length; ++tt) {
+ var streamBank = g_scenePack.getObjects(terrainNames[tt],
+ 'o3d.StreamBank')[0];
+ for (var ii = 0; ii < semantics.length; ++ii) {
+ var stream = streamBank.getVertexStream(semantics[ii], 0);
+ streamBank.setVertexStream(semantics[ii], 1, stream.field, 0)
+ }
+ }
+
+ g_cameraInfos = o3djs.camera.getCameraInfos(parent,
+ g_o3dWidth,
+ g_o3dHeight);
+ setCamera(1);
+ setupUnderwater();
+
+ getSpeedTransforms(g_sceneRoot);
+
+ //o3djs.dump.dump("--------\n");
+ //dumpTransforms(g_sceneRoot, '');
+
+ setupMaterialEditor();
+ setupEffectEditor();
+ setupPropEditor();
+
+ registerKeyHandlers();
+
+ if (false) {
+ o3djs.dump.dump('---dump g_scenePack shapes---\n');
+ var shapes = g_scenePack.getObjectsByClassName('o3d.Shape');
+ for (var t = 0; t < shapes.length; t++) {
+ var shape = shapes[t];
+ o3djs.dump.dump('shape ' + t + ': ' + shape.name + '\n');
+ //o3djs.dump.dumpShape(shape);
+ }
+ }
+
+ if (false) {
+ o3djs.dump.dump('---dump g_scenePack materials---\n');
+ var materials = g_scenePack.getObjectsByClassName('o3d.Material');
+ for (var t = 0; t < materials.length; t++) {
+ var material = materials[t];
+ o3djs.dump.dump (
+ ' ' + t + ' : ' + material.className +
+ ' : "' + material.name + '"\n');
+ var params = material.params;
+ for (var p = 0; p < params.length; ++p) {
+ var param = params[p];
+ if (param.className == 'o3d.ParamSampler') {
+ o3djs.dump.dump(' ' + p + ': ' +
+ param.value.texture.name + '\n');
+ }
+ }
+ //o3djs.dump.dumpParams(materials[t], ' ');
+ }
+ }
+
+ if (false) {
+ o3djs.dump.dump('---dump g_scenePack textures---\n');
+ var textures = g_scenePack.getObjectsByClassName('o3d.Texture');
+ for (var t = 0; t < textures.length; t++) {
+ o3djs.dump.dump(t + ': ');
+ o3djs.dump.dumpTexture(textures[t]);
+ }
+
+ o3djs.dump.dump('---dump g_scenePack effects---\n');
+ var effects = g_scenePack.getObjectsByClassName('o3d.Effect');
+ for (var t = 0; t < effects.length; t++) {
+ o3djs.dump.dump (' ' + t + ' : ' + effects[t].className +
+ ' : "' + effects[t].name + '"\n');
+ o3djs.dump.dumpParams(effects[t], ' ');
+ }
+ }
+ }
+ }
+
+ try {
+ // We need to make a subloader because we can't make the particles
+ // until both the scene and the particle textures are loaded.
+ g_loadInfo = g_loader.loadInfo;
+ g_particleLoader = g_loader.createLoader(setupParticles);
+ g_particleLoader.loadTexture(
+ g_mainPack,
+ o3djs.util.getAbsoluteURI('assets/pe_fire.jpg'),
+ function(texture, success) {
+ g_torchTexture = texture;
+ });
+ g_particleLoader.loadTexture(
+ g_mainPack,
+ o3djs.util.getAbsoluteURI('assets/pe_mist.png'),
+ function(texture, success) {
+ g_mistTexture = texture;
+ });
+
+ var url = o3djs.util.getAbsoluteURI('assets/beachdemo.o3dtgz');
+ g_particleLoader.loadScene(
+ g_client, g_scenePack, g_sceneRoot, url, callback, {opt_async: true});
+ g_particleLoader.finish()
+ g_loader.finish();
+ } catch (e) {
+ showError(e);
+ }
+}
+
+/**
+ * Records the client's size if it's changed.
+ */
+function setClientSize() {
+ var newWidth = parseInt(g_client.width);
+ var newHeight = parseInt(g_client.height);
+
+ if (newWidth != g_o3dWidth || newHeight != g_o3dHeight) {
+ g_o3dWidth = newWidth;
+ g_o3dHeight = newHeight;
+
+ updateProjection();
+ g_fpsManager.resize(g_o3dWidth, g_o3dHeight);
+ g_fpsManager.setPosition(g_o3dWidth - 80, 10);
+ updateFaderPlane();
+ }
+}
+
+/**
+ * Moves the camera based on key state.
+ */
+function handleCameraKeys() {
+ var moveX = 0;
+ var moveY = 0;
+
+ if (g_keyDown[37] || g_keyDown[65]) {
+ moveX = -1;
+ }
+ if (g_keyDown[39] || g_keyDown[68]) {
+ moveX = 1;
+ }
+ if (g_keyDown[38] || g_keyDown[87]) {
+ moveY = 1;
+ }
+ if (g_keyDown[40] || g_keyDown[83]) {
+ moveY = -1;
+ }
+
+ if (moveX) {
+ moveCameraLeftRight(moveX);
+ stopAnimatedCamera();
+ }
+
+ if (moveY) {
+ moveCameraForwardBack(moveY);
+ stopAnimatedCamera();
+ }
+}
+
+/**
+ * Sets the speed transforms visible or invisible to turn on/off whole groups of
+ * shapes not needed for certain rendering.
+ * @param {boolean} main Turn on stuff marked for main.
+ * @param {boolean} reflect Turn on stuff marked for reflect.
+ * @param {boolean} force Force visible to true.
+ */
+function setSpeedTransforms(main, reflect, force) {
+ var mask = (main ? 1 : 0) + (reflect ? 2 : 0);
+ for (var ii = 0; ii < 4; ++ii) {
+ var visible = ((ii & mask) != 0) || force;
+ var speedTransforms = g_speedTransforms[ii];
+ for (var jj = 0; jj < speedTransforms.length; ++jj) {
+ speedTransforms[jj].visible = visible;
+ }
+ }
+}
+
+/**
+ * Eases in a number.
+ * @param {number} value Value to ease in. Must be 0 to 1.
+ * @return {number} Ease in version of value.
+ */
+function easeIn(value) {
+ return 1 - Math.cos(value * Math.PI * 0.5);
+}
+
+/**
+ * Eases out a number.
+ * @param {number} value Value to ease out. Must be 0 to 1.
+ * @return {number} Ease out version of value.
+ */
+function easeOut(value) {
+ return Math.sin(value * Math.PI * 0.5);
+}
+
+/**
+ * Ease in and out a number.
+ * @param {number} value Value to ease in out. Must be 0 to 1.
+ * @return {number} Ease in out version of value.
+ */
+function easeInOut(value) {
+ if (value < 0.5) {
+ return easeIn(value * 2) * 0.5;
+ } else {
+ return easeOut(value * 2 - 1) * 0.5 + 0.5;
+ }
+}
+
+/**
+ * Stops the animated camera.
+ */
+function stopAnimatedCamera() {
+ g_animateCamera = false;
+ g_demoTimer = 30;
+ g_cameraTimer = 0;
+ g_faderTransform.visible = false;
+}
+
+/**
+ * Animates the camera.
+ * @param {number} elapsedTime Elapsed time in seconds.
+ */
+function animateCamera(elapsedTime) {
+ if (g_animateCamera && window.g_finished) {
+ g_cameraTimer -= elapsedTime;
+
+ if (g_cameraTimer <= 0) {
+ ++g_cameraPointIndex;
+ if (g_cameraPointIndex >= g_cameraPoints.length) {
+ g_cameraPointIndex = 0;
+ }
+ g_cameraPoint = g_cameraPoints[g_cameraPointIndex];
+ g_cameraDuration = g_cameraPoint.duration * 3;
+ g_cameraTimer = g_cameraDuration;
+ }
+
+ var lerp = 1;
+ if (g_cameraTimer > 1) {
+ var moveDuration = g_cameraDuration - 1;
+ var timer = g_cameraTimer - 1;
+ lerp = easeInOut(1 - timer / moveDuration);
+
+ if (g_cameraTimer > g_cameraDuration - 1) {
+ var fade = g_cameraTimer - (g_cameraDuration - 1);
+ g_faderTransform.visible = true;
+ g_faderColorParam.value = [0, 0, 0, fade];
+ } else {
+ g_faderTransform.visible = false;
+ }
+ } else {
+ g_faderTransform.visible = true;
+ g_faderColorParam.value = [0, 0, 0, 1 - g_cameraTimer];
+ }
+
+ g_camera.eye = g_math.lerpVector(g_cameraPoint.start.eye,
+ g_cameraPoint.end.eye,
+ lerp);
+ g_camera.targetVector = g_math.lerpVector(g_cameraPoint.start.targetVector,
+ g_cameraPoint.end.targetVector,
+ lerp);
+ g_camera.fieldOfView = g_math.degToRad(
+ g_math.lerpScalar(g_cameraPoint.start.fieldOfView,
+ g_cameraPoint.end.fieldOfView,
+ lerp));
+
+ updateCamera();
+ updateProjection();
+ } else {
+ if (g_runDemo) {
+ g_demoTimer -= elapsedTime;
+ if (g_demoTimer <= 0) {
+ g_animateCamera = true;
+ }
+ }
+ }
+}
+
+/**
+ * Called every frame.
+ * @param {!o3d.RenderEvent} renderEvent Info about this frame.
+ */
+function onRender(renderEvent) {
+ // save off the render event so look at it from the debugger.
+ g_re = renderEvent;
+
+ var elapsedTime = renderEvent.elapsedTime * window.g_timeMult;
+ if (g_hudFadeTime > 0) {
+ g_hudFadeTime -= elapsedTime;
+ if (g_hudFadeTime <= 0) {
+ g_hudQuad.transform.visible = false;
+ }
+ }
+
+ // This is for selenium so that the hud is predictable.
+ if (g_hudFadeTime > 0 && window.g_timeMult == 0) {
+ g_hudFadeTime = 0;
+ g_hudQuad.transform.visible = false;
+ }
+
+ // Normally I'd have used a SecondCounter but so we can run this in
+ // selenium I set it up this way to be easy.
+ window.g_clock += elapsedTime;
+ g_globalClockParam.value = window.g_clock;
+
+ if (g_loadInfo) {
+ var progressInfo = g_loadInfo.getKnownProgressInfoSoFar();
+ g_proxyOffsetParam.value = progressInfo.percent / 100 * PROXY_HEIGHT;
+ if (progressInfo.percent != g_downloadPercent) {
+ g_downloadPercent = progressInfo.percent;
+ setHudText('Loading... ' + progressInfo.percent + '%' +
+ ' (' + progressInfo.downloaded +
+ ' of ' + progressInfo.totalBytes + progressInfo.suffix + ')');
+ }
+ }
+
+ // This if is for selenium to make the camera predictable.
+ if (window.g_timeMult) {
+ animateCamera(elapsedTime);
+ } else {
+ setCamera(1);
+ }
+
+ handleCameraKeys();
+ setClientSize();
+ g_fpsManager.update(renderEvent);
+
+ if (g_updateRenderTargets || g_waterMode == 1) {
+ g_updateRenderTargets = false;
+
+ // Render the reflection texture.
+ setSpeedTransforms(false, true, false);
+ g_clipHeightParam.value = g_reflectionClipHeight;
+ g_client.root.identity();
+ g_client.root.scale(1, 1, -1); // flip the scene
+ g_client.renderTree(g_reflectionSurfaceSet);
+
+ // Render the refraction texture.
+ setSpeedTransforms(true, true, true);
+ g_client.root.identity();
+ g_client.root.scale(1, 1, 1 /* 0.75 */); // squish the scene.
+ g_client.renderTree(g_refractionSurfaceSet);
+ }
+
+ // Render the main scene.
+ setSpeedTransforms(true, false, false);
+ g_clipHeightParam.value = g_mainClipHeight;
+ g_client.root.identity();
+ g_client.renderTree(g_mainViewInfo.root);
+
+ // Render the HUD.
+ g_client.renderTree(g_hudViewInfo.root);
+
+ // Render the FPS display.
+ g_client.renderTree(g_fpsManager.viewInfo.root);
+}
+
+function onAllLoadingFinished() {
+ g_loader = null;
+ g_animateCamera = true;
+
+ showHint();
+
+ window.g_finished = true; // for selenium testing.
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ // These are here so they are shared by both V8 and the browser.
+ window.g_finished = false; // for selenium
+ window.g_timeMult = 1;
+ window.g_clock = 0;
+
+ // Comment out the line below to run the sample in the browser JavaScript
+ // engine. This may be helpful for debugging.
+ o3djs.util.setMainEngine(o3djs.util.Engine.V8);
+
+ o3djs.util.addScriptUri('');
+ o3djs.util.makeClients(initStep2, 'LargeGeometry');
+}
+
+/**
+ * Initializes O3D and loads the scene into the transform graph.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ g_materialPanelElement = o3djs.util.getElementById('materialpanel');
+ g_propPanelElement = o3djs.util.getElementById('proppanel');
+ g_effectPanelElement = o3djs.util.getElementById('effectpanel');
+ g_upperPanelElement = o3djs.util.getElementById('upperpanel');
+ g_effectTabsElement = o3djs.util.getElementById('effecttabs');
+ g_effectTextAreaElement = o3djs.util.getElementById('effecttextarea');
+ g_sceneElement = o3djs.util.getElementById('o3d');
+
+ g_o3dElement = clientElements[0];
+ g_o3d = g_o3dElement.o3d;
+ g_math = o3djs.math;
+ g_quaternions = o3djs.quaternions;
+ window.g_client = g_client = g_o3dElement.client;
+
+ g_mainPack = g_client.createPack();
+ g_scenePack = g_client.createPack();
+
+ // Create Render Targets for the reflection and refraction.
+ g_reflectionTexture = g_mainPack.createTexture2D(RENDER_TARGET_WIDTH,
+ RENDER_TARGET_HEIGHT,
+ g_o3d.Texture.ARGB8, 1,
+ true);
+ var reflectionSurface = g_reflectionTexture.getRenderSurface(0, g_mainPack);
+ g_refractionTexture = g_mainPack.createTexture2D(RENDER_TARGET_WIDTH,
+ RENDER_TARGET_HEIGHT,
+ g_o3d.Texture.XRGB8, 1,
+ true);
+ var refractionSurface = g_refractionTexture.getRenderSurface(0, g_mainPack);
+ var depthSurface = g_mainPack.createDepthStencilSurface(RENDER_TARGET_WIDTH,
+ RENDER_TARGET_HEIGHT);
+
+ g_mainRoot = g_mainPack.createObject('Transform');
+ g_baseRoot = g_scenePack.createObject('Transform');
+ g_baseRoot.parent = g_mainRoot;
+ g_sceneRoot = g_scenePack.createObject('Transform');
+ g_sceneRoot.parent = g_baseRoot;
+ g_mainRoot.parent = g_client.root;
+ g_sceneRoot.translate(0, 0, -g_waterLevel);
+
+ // Setup the render graph to generate them.
+ g_reflectionSurfaceSet = g_mainPack.createObject('RenderSurfaceSet');
+ g_reflectionSurfaceSet.renderSurface = reflectionSurface;
+ g_reflectionSurfaceSet.renderDepthStencilSurface = depthSurface;
+
+ g_refractionSurfaceSet = g_mainPack.createObject('RenderSurfaceSet');
+ g_refractionSurfaceSet.renderSurface = refractionSurface;
+ g_refractionSurfaceSet.renderDepthStencilSurface = depthSurface;
+
+ // Create states to set clipping.
+ g_reflectionClipState = g_mainPack.createObject('State');
+ g_reflectionClipState.getStateParam('AlphaTestEnable').value = true;
+ g_reflectionClipState.getStateParam('AlphaComparisonFunction').value =
+ g_o3d.State.CMP_GREATER;
+ var reflectionStateSet = g_mainPack.createObject('StateSet');
+ reflectionStateSet.state = g_reflectionClipState;
+ reflectionStateSet.parent = g_reflectionSurfaceSet;
+
+ var fStrength = 4.0;
+ g_refractionClipState = g_mainPack.createObject('State');
+ g_refractionClipState.getStateParam('AlphaTestEnable').value = true;
+ g_refractionClipState.getStateParam('AlphaComparisonFunction').value =
+ g_o3d.State.CMP_GREATER;
+
+ var refractionStateSet = g_mainPack.createObject('StateSet');
+ refractionStateSet.state = g_refractionClipState;
+ refractionStateSet.parent = g_refractionSurfaceSet;
+
+ // Create the render graph for the main view.
+ g_mainViewInfo = o3djs.rendergraph.createBasicView(
+ g_mainPack,
+ g_mainRoot);
+
+ // Create a render graph for the reflection map
+ g_reflectionViewInfo = o3djs.rendergraph.createExtraView(g_mainViewInfo);
+ g_reflectionViewInfo.root.parent = reflectionStateSet;
+ g_reflectionViewInfo.treeTraversal.transform = g_baseRoot;
+ g_reflectionViewInfo.performanceState.getStateParam('CullMode').value =
+ g_o3d.State.CULL_CCW;
+ g_reflectionViewInfo.performanceState.getStateParam(
+ 'ColorWriteEnable').value = 15;
+ g_reflectionViewInfo.zOrderedState.getStateParam('CullMode').value =
+ g_o3d.State.CULL_CCW;
+ g_reflectionViewInfo.zOrderedState.getStateParam(
+ 'ColorWriteEnable').value = 15;
+
+ // Create a render graph for the refraction map
+ g_refractionViewInfo = o3djs.rendergraph.createBasicView(
+ g_mainPack,
+ g_baseRoot,
+ refractionStateSet);
+
+ // Create a render graph for the HUD
+ g_hudRoot = g_mainPack.createObject('Transform');
+ g_hudViewInfo = o3djs.rendergraph.createBasicView(
+ g_mainPack,
+ g_hudRoot);
+ g_hudViewInfo.clearBuffer.clearColorFlag = false;
+
+ g_hudViewInfo.zOrderedState.getStateParam('CullMode').value =
+ g_o3d.State.CULL_NONE;
+
+ g_hudViewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [0, 0, 1], // eye
+ [0, 0, 0], // target
+ [0, 1, 0]); // up
+
+ //g_reflectionViewInfo.clearBuffer.clearColor = [0.5, 1, 0.5, 1];
+ //g_refractionViewInfo.clearBuffer.clearColor = [0.5, 0.5, 1, 1];
+ g_reflectionViewInfo.clearBuffer.clearColor = [0, 0, 0, 0];
+ g_refractionViewInfo.clearBuffer.clearColor = g_waterColor;
+
+ // Set some names so it's easier to debug.
+ g_mainViewInfo.performanceDrawList.name = 'performanceDrawList';
+ g_mainViewInfo.zOrderedDrawList.name = 'zOrderedDrawList';
+
+ // Turn off culling for transparent stuff so we can see the backs of leaves.
+ g_mainViewInfo.zOrderedState.getStateParam('CullMode').value =
+ g_o3d.State.CULL_NONE;
+ g_mainViewInfo.zOrderedState.getStateParam('AlphaReference').value = 0.7;
+
+ // Turn on alpha test in the performance list for our clipping plane.
+ g_mainViewInfo.performanceState.getStateParam('AlphaTestEnable').value = true;
+ g_mainViewInfo.performanceState.getStateParam(
+ 'AlphaComparisonFunction').value = g_o3d.State.CMP_GREATER;
+
+ g_fpsManager = o3djs.fps.createFPSManager(g_mainPack,
+ g_client.width,
+ g_client.height);
+ g_fpsManager.setVisible(false);
+
+ // Create a param object to hold a few params to drive things globally.
+ g_globalParams = g_mainPack.createObject('ParamObject');
+ g_globalParams.name = 'global params';
+ g_globalClockParam = g_globalParams.createParam('clock', 'ParamFloat');
+ g_lightPositionParam = g_globalParams.createParam('lightWorldPos',
+ 'ParamFloat3');
+ g_lightDirectionParam = g_globalParams.createParam('lightDirection',
+ 'ParamFloat3');
+ g_lightColorParam = g_globalParams.createParam('lightColor',
+ 'ParamFloat4');
+ g_lightColorParam.value = [2.0, 1.8, 1.4, 1];
+ setSunPosition([0, -100000, 200000]);
+
+ g_clipHeightParam = g_globalParams.createParam('clipHeight', 'ParamFloat');
+ g_proxyOffsetParam = g_globalParams.createParam('offset', 'ParamFloat');
+
+ g_particleSystem = o3djs.particles.createParticleSystem(g_mainPack,
+ g_mainViewInfo,
+ g_globalClockParam,
+ pseudoRandom);
+
+ // Since we set the state for the draw pass to 'AlphaReference' = 0.7
+ // We need to set it back to 0.0 for the particles.
+ for (var ii = 0; ii < g_particleSystem.particleStates.length; ++ii) {
+ g_particleSystem.particleStates[ii].getStateParam(
+ 'AlphaReference').value = 0.0;
+ }
+
+ g_editableEffects.push(g_particleSystem.effects[0]);
+ g_editableEffects.push(g_particleSystem.effects[1]);
+
+ g_loader = o3djs.loader.createLoader(onAllLoadingFinished);
+
+ setupWater();
+ setupHud();
+
+ loadProxy();
+
+ // It's important to create stuff in g_mainPack and not g_scenePack because
+ // g_scenePack will be scanned and modified after loading.
+
+ setClientSize();
+ updateCamera();
+ updateProjection();
+
+ o3djs.event.addEventListener(g_o3dElement, 'mousedown', onMouseDown);
+ o3djs.event.addEventListener(g_o3dElement, 'mousemove', onMouseMove);
+ o3djs.event.addEventListener(g_o3dElement, 'mouseup', onMouseUp);
+ o3djs.event.addEventListener(g_o3dElement, 'wheel', onWheel);
+ o3djs.event.addEventListener(g_o3dElement, 'keydown', onKeyDown);
+ o3djs.event.addEventListener(g_o3dElement, 'keyup', onKeyUp);
+
+ // If we don't check the size of the client area every frame we don't get a
+ // chance to adjust the perspective matrix fast enough to keep up with the
+ // browser resizing us.
+ g_client.setRenderCallback(onRender);
+
+ // Because we don't render the render targets every frame of the OS has
+ // to reset them their contents will get lost. In that case O3D will notify
+ // us through this callback so we can re-render our render targets.
+ g_client.setLostResourcesCallback(function() {
+ g_updateRenderTargets = true;
+ });
+}
+
+/**
+ * Loads a texture.
+ *
+ * @param {!o3djs.loader.Loader} loader Loader to use to load texture.
+ * @param {!o3d.Pack} pack Pack to load texture in.
+ * @param {!o3d.Material} material Material to attach sampler to.
+ * @param {string} samplerName Name of sampler.
+ * @param {string} textureName filename of texture.
+ * @return {!o3d.Sampler} Sampler attached to material.
+ */
+function loadTexture(loader, pack, material, samplerName, textureName) {
+ var samplerParam = material.getParam(samplerName);
+ var sampler = pack.createObject('Sampler');
+ samplerParam.value = sampler;
+
+ var url = o3djs.util.getAbsoluteURI('assets/' + textureName);
+ loader.loadTexture(pack, url, function(texture, success) {
+ sampler.texture = texture;
+ });
+
+ return sampler;
+}
+
+/**
+ * Create the waterfall effect.
+ */
+function setupWaterfall() {
+ // A prefix for waterfall materials would have been better.
+ var material = g_scenePack.getObjects('Standard_3', 'o3d.Material')[0];
+
+ // Create an effect with a v offset parameter so we can scroll the
+ // UVs.
+ var effect = g_mainPack.createObject('Effect');
+ effect.name = 'waterfall';
+ effect.loadFromFXString(o3djs.util.getElementContentById('waterfallshader'));
+ effect.createUniformParameters(material);
+
+ g_editableEffects.push(effect);
+
+ // Set the waterfall to use additive blending.
+ var state = g_mainPack.createObject('State');
+ state.getStateParam('SourceBlendFunction').value =
+ g_o3d.State.BLENDFUNC_SOURCE_ALPHA;
+ state.getStateParam('DestinationBlendFunction').value =
+ g_o3d.State.BLENDFUNC_ONE;
+ state.getStateParam('AlphaReference').value = 0.0;
+ //state.getStateParam('ZWriteEnable').value = false;
+
+ material.state = state;
+ material.drawList = g_mainViewInfo.zOrderedDrawList;
+ material.effect = effect;
+
+ // Create a counter to scroll the Vs.
+ // var counter = g_mainPack.createObject('SecondCounter');
+ // material.getParam('vOffset').bind(counter.getParam('count'));
+ //
+ // For selenium testing we need a global clock.
+ material.getParam('vOffset').bind(g_globalClockParam);
+
+}
+
+/**
+ * Setup underwater.
+ * Must be called after the scene has loaded.
+ * NOTE: The coral needs a new shader that supports normal maps
+ * but it's a low priority to fix right now.
+ */
+function setupUnderwater() {
+ var effect = g_mainPack.createObject('Effect');
+ effect.name = 'underwater';
+ effect.loadFromFXString(o3djs.util.getElementContentById('underwatershader'));
+ g_editableEffects.push(effect);
+
+ // make 2 materials, one for zOrdered, one for performance.
+ var materials = [];
+ for (var ii = 0; ii < 2; ++ii) {
+ var material = g_mainPack.createObject('Material');
+ // Copy the water params so this material gets access to the noise samplers.
+ // and the clock regardless of whether or not it uses them. That way you
+ // can access them as you edit the shader live.
+ material.copyParams(g_waterMaterial);
+ material.effect = effect;
+ effect.createUniformParameters(material);
+
+ material.getParam('sunVector').bind(g_lightDirectionParam);
+ material.getParam('waterColor').value = g_waterColor;
+ material.getParam('fadeFudge').value = -1 / 1800;
+ material.getParam('clock').bind(g_globalClockParam);
+
+ g_fadeParams[ii] = material.getParam('fadeFudge');
+ materials[ii] = material;
+ }
+
+ materials[0].drawList = g_refractionViewInfo.performanceDrawList;
+ materials[0].name = 'underwaterOpaque';
+ materials[1].drawList = g_refractionViewInfo.zOrderedDrawList;
+ materials[1].name = 'underwaterTransparent';
+
+ g_underwaterMaterials = materials;
+
+ // put a draw element on each element in the scene to draw it with the
+ // underwater shader.
+ var elements = g_scenePack.getObjectsByClassName('o3d.Element');
+ for (var ee = 0; ee < elements.length; ++ee) {
+ var element = elements[ee];
+ var originalMaterial = element.material;
+ var materialInfo = g_materialLists[originalMaterial.name];
+ if ((!materialInfo || materialInfo.refract) &&
+ element.name != 'Seafloor|Sand_Dark' &&
+ (!materialInfo || materialInfo.effect != 'diffuse_bump_2textures')) {
+ // Use the sampler from the original material.
+ var originalSamplerParam = originalMaterial.getParam('diffuseSampler');
+ if (originalSamplerParam) {
+ var drawElement = element.createDrawElement(
+ g_scenePack,
+ originalMaterial.drawList == g_mainViewInfo.performanceDrawList ?
+ materials[0] : materials[1]);
+ // create a Sampler Param on this draw element to use instead of the
+ // material's.
+ var samplerParam = drawElement.createParam('diffuseSampler',
+ 'ParamSampler');
+ samplerParam.value = originalSamplerParam.value;
+ }
+ }
+ }
+
+ // Special case the sand and coral rocks.
+ var materialNames = {
+ 'Sand_Dark': {texture: 'image3.dds'},
+ 'Folg_coralRockA_mat': {texture: 'image30.dds'},
+ 'Folg_coralRockB_mat': {texture: 'image30.dds'}};
+ for (var name in materialNames) {
+ var info = materialNames[name];
+ var material = g_scenePack.getObjects(name, 'o3d.Material')[0];
+ material.drawList = g_refractionViewInfo.performanceDrawList;
+ addTexture(material, 'diffuse2Sampler', info.texture);
+ }
+}
+
+/**
+ * Create the water effect.
+ */
+function setupWater() {
+ var waterEffects = ['watershader', 'watercolorandskyshader', 'waterstyle2'];
+ var effects = [];
+ for (var ee = 0; ee < waterEffects.length; ++ee) {
+ var name = waterEffects[ee]
+ var effect = g_mainPack.createObject('Effect');
+ effect.name = name;
+ effect.loadFromFXString(o3djs.util.getElementContentById(name));
+ effects[ee] = effect;
+ g_editableEffects.push(effect);
+ }
+ g_waterEffect = effects[0];
+ g_waterColorAndSkyEffect = effects[1];
+ g_waterStyle2Effect = effects[2];
+
+ var effect = g_waterEffect;
+
+ var material = g_mainPack.createObject('Material');
+ g_waterMaterial = material;
+ material.name = 'water';
+ material.drawList = g_mainViewInfo.performanceDrawList;
+ material.effect = effect;
+ effect.createUniformParameters(material);
+
+ // We could reuse the one from the waterfall but let's make 2 anyway.
+ // var counter = g_mainPack.createObject('SecondCounter');
+ // For selenium testing we need a global clock.
+
+ material.getParam('waterColor').value = g_waterColor;
+ material.getParam('reflectionRefractionOffset').value = 0.1;
+ //material.getParam('clock').bind(counter.getParam('count'));
+ material.getParam('clock').bind(g_globalClockParam);
+ g_viewPositionParam = material.getParam('viewPosition');
+
+ var sampler = g_mainPack.createObject('Sampler');
+ sampler.texture = g_refractionTexture;
+ sampler.addressModeU = g_o3d.Sampler.MIRROR;
+ sampler.addressModeV = g_o3d.Sampler.MIRROR;
+ material.getParam('refractionSampler').value = sampler;
+ sampler = g_mainPack.createObject('Sampler');
+ sampler.texture = g_reflectionTexture;
+ sampler.addressModeU = g_o3d.Sampler.MIRROR;
+ sampler.addressModeV = g_o3d.Sampler.MIRROR;
+ material.getParam('reflectionSampler').value = sampler;
+
+ var shape = o3djs.primitives.createPlane(g_mainPack, material,
+ 100000, 100000, 100, 100,
+ [[1, 0, 0, 0],
+ [0, 0, 1, 0],
+ [0, -1, 0, 0],
+ [0, 0, 0, 1]]);
+
+ g_waterTransform = g_mainPack.createObject('Transform');
+ g_waterTransform.name = 'watersurface';
+ g_waterTransform.addShape(shape);
+
+ function waterAssetsLoaded() {
+ g_waterTransform.parent = g_mainRoot;
+ setupSkyDome();
+ }
+
+ // Create a loader for the water so we can know when all its assets have
+ // loaded.
+ var loader = g_loader.createLoader(waterAssetsLoaded);
+
+ g_environmentSampler = loadTexture(loader, g_mainPack, material,
+ 'environmentSampler',
+ 'sky-cubemap.dds');
+
+ // Create some textures.
+ var textureInfo = [
+ {width: 128, height: 128, type: 0, name: 'noiseSampler'},
+ {width: 64, height: 64, type: 0, name: 'noiseSampler2'},
+ {width: 32, height: 32, type: 0, name: 'noiseSampler3'},
+ {width: 32, height: 1, type: 1, name: 'fresnelSampler'}
+ ];
+
+ for (var tt = 0; tt < textureInfo.length; ++tt) {
+ var info = textureInfo[tt];
+ var pixels = [];
+
+ switch (info.type) {
+ case 0:
+ // Create a noise texture.
+ for (var yy = 0; yy < info.height; ++yy) {
+ for (var xx = 0; xx < info.width; ++xx) {
+ for (var cc = 0; cc < 3; ++cc) {
+ pixels.push(pseudoRandom());
+ }
+ }
+ }
+ break;
+ case 1:
+ // Create a ramp texture. (this needs to be a fresnel ramp?)
+ for (var yy = 0; yy < info.height; ++yy) {
+ for (var xx = 0; xx < info.width; ++xx) {
+ // TODO: figure this out.
+ var color = Math.pow(1 - xx / info.width, 10);
+ for (var cc = 0; cc < 3; ++cc) {
+ pixels.push(color);
+ }
+ }
+ }
+ break;
+ }
+ var texture = g_mainPack.createTexture2D(
+ info.width, info.height, g_o3d.Texture.XRGB8, 1, false);
+ texture.set(0, pixels);
+ var sampler = g_mainPack.createObject('Sampler');
+ sampler.texture = texture;
+ material.getParam(info.name).value = sampler;
+ }
+
+ loader.finish();
+}
+
+/**
+ * Create particles.
+ */
+function setupParticles() {
+ setupTorches();
+ setupMist();
+}
+
+/**
+ * Create the torches.
+ */
+function setupTorches() {
+ g_torchEmitter = g_particleSystem.createParticleEmitter(g_torchTexture);
+ g_torchEmitter.setState(o3djs.particles.ParticleStateIds.ADD);
+ g_torchEmitter.setColorRamp(
+ [1, 1, 0, 1,
+ 1, 0, 0, 1,
+ 0, 0, 0, 1,
+ 0, 0, 0, 0.5,
+ 0, 0, 0, 0]);
+ g_torchEmitter.setParameters({
+ numParticles: 40,
+ lifeTime: 2,
+ timeRange: 2,
+ startSize: 50,
+ endSize: 90,
+ positionRange: [10, 10, 10],
+ velocity: [0, 0, 60], velocityRange: [15, 15, 15],
+ acceleration: [0, 0, -20],
+ spinSpeedRange: 4}
+ );
+
+ g_torchMaterial = g_torchEmitter.material;
+
+ // Add one to each torch.
+ var shape = g_torchEmitter.shape;
+ g_scenePack.getObjects('particle_torch01',
+ 'o3d.Transform')[0].addShape(shape);
+ g_scenePack.getObjects('particle_torch02',
+ 'o3d.Transform')[0].addShape(shape);
+ g_scenePack.getObjects('particle_torch03',
+ 'o3d.Transform')[0].addShape(shape);
+ g_scenePack.getObjects('particle_torch04',
+ 'o3d.Transform')[0].addShape(shape);
+}
+
+/**
+ * Create the mist.
+ */
+function setupMist() {
+ g_topMistEmitter = g_particleSystem.createParticleEmitter(g_mistTexture);
+ g_topMistEmitter.setState(o3djs.particles.ParticleStateIds.ADD);
+ g_topMistEmitter.setColorRamp(
+ [1, 1, 1, 2,
+ 1, 1, 1, 0]);
+ g_topMistEmitter.setParameters({
+ numParticles: 20,
+ timeRange: 3,
+ lifeTime: 3, lifeTimeRange: 1,
+ startSize: 400,
+ endSize: 600,
+ position: [-100, -100, 0], positionRange: [25, 25, 0],
+ velocity: [0, 0, 150], velocityRange: [15, 15, 15],
+ worldAcceleration: [0, 0, -500],
+ spinSpeedRange: 8}
+ );
+
+ // Add one to each top.
+ var shape = g_topMistEmitter.shape;
+ g_scenePack.getObjects('particle_falltop01',
+ 'o3d.Transform')[0].addShape(shape);
+ g_scenePack.getObjects('particle_falltop02',
+ 'o3d.Transform')[0].addShape(shape);
+ g_scenePack.getObjects('particle_falltop03',
+ 'o3d.Transform')[0].addShape(shape);
+
+ g_bottomMistEmitter = g_particleSystem.createParticleEmitter(g_mistTexture);
+ g_bottomMistEmitter.setState(o3djs.particles.ParticleStateIds.ADD);
+ g_bottomMistEmitter.setColorRamp(
+ [1, 1, 1, 1,
+ 1, 1, 1, 0]);
+ g_bottomMistEmitter.setParameters({
+ numParticles: 40,
+ lifeTime: 2,
+ timeRange: 2,
+ startSize: 800,
+ endSize: 1500,
+ position: [0, 0, 100], positionRange: [200, 200, 10],
+ velocityRange: [200, 200, 0],
+ acceleration: [0, 0, -20],
+ spinSpeedRange: 4}
+ );
+
+ // Add one to each bottom.
+ shape = g_bottomMistEmitter.shape;
+
+ g_scenePack.getObjects('particle_fallbottom01',
+ 'o3d.Transform')[0].addShape(shape);
+ g_scenePack.getObjects('particle_fallbottom02',
+ 'o3d.Transform')[0].addShape(shape);
+ g_scenePack.getObjects('particle_fallbottom03',
+ 'o3d.Transform')[0].addShape(shape);
+
+ g_rippleEmitter = g_particleSystem.createParticleEmitter(g_mistTexture);
+ g_rippleEmitter.setState(o3djs.particles.ParticleStateIds.ADD);
+ g_rippleEmitter.setColorRamp(
+ [0.7, 0.8, 1, 0.5,
+ 1, 1, 1, 0]);
+ g_rippleEmitter.setParameters({
+ numParticles: 20,
+ lifeTime: 2,
+ timeRange: 2,
+ startSize: 50,
+ endSize: 10000,
+ position: [0, 0, 10], positionRange: [250, 250, 0],
+ orientation: o3djs.quaternions.rotationX(Math.PI / 2),
+ billboard: false});
+
+ // Add one to each bottom.
+ shape = g_rippleEmitter.shape;
+
+ g_scenePack.getObjects('particle_fallbottom01',
+ 'o3d.Transform')[0].addShape(shape);
+ g_scenePack.getObjects('particle_fallbottom02',
+ 'o3d.Transform')[0].addShape(shape);
+ g_scenePack.getObjects('particle_fallbottom03',
+ 'o3d.Transform')[0].addShape(shape);
+}
+
+function setupSkyDome() {
+ // Create the skydome effect.
+ var effect = g_mainPack.createObject('Effect');
+ effect.name = 'skydome';
+ effect.loadFromFXString(o3djs.util.getElementContentById('skydomeshader'));
+ g_editableEffects.push(effect);
+
+ var material = g_mainPack.createObject('Material');
+ g_skyDomeMaterial = material;
+ material.name = 'skydome';
+ material.drawList = g_mainViewInfo.performanceDrawList;
+ material.effect = effect;
+ effect.createUniformParameters(material);
+
+ material.getParam('environmentSampler').value = g_environmentSampler;
+
+ // Create a special quad to draw the sky. We won't transform this quad
+ // at all. It's already in clip-space.
+ var shape = o3djs.primitives.createPlane(g_mainPack, material,
+ 2, 2, 1, 1,
+ [[1, 0, 0, 0],
+ [0, 0, 1, 0],
+ [0, -1, 0, 0],
+ [0, 0, 0.99999, 1]]);
+
+ g_skyDomeTransform = g_mainPack.createObject('Transform');
+ g_skyDomeTransform.parent = g_mainRoot;
+ g_skyDomeTransform.addShape(shape);
+}
+
+/**
+ * Creates an Image object which is a transform and a child scaleTransform
+ * scaled to match the texture
+ *
+ * @constructor
+ * @param {!o3d.Transform} parent Transform to parent image too.
+ * @param {!o3d.Texture} texture The texture.
+ * @param {boolean} opt_topLeft If true the origin of the image will be it's
+ * topleft corner, the default is the center of the image.
+ */
+function Image(parent, texture, opt_topLeft) {
+ // create a transform for positioning
+ this.transform = g_mainPack.createObject('Transform');
+ this.transform.parent = parent;
+
+ // create a transform for scaling to the size of the image just so
+ // we don't have to manage that manually in the transform above.
+ this.scaleTransform = g_mainPack.createObject('Transform');
+ this.scaleTransform.parent = this.transform;
+
+ // setup the sampler for the texture
+ this.sampler = g_mainPack.createObject('Sampler');
+ this.sampler.addressModeU = g_o3d.Sampler.CLAMP;
+ this.sampler.addressModeV = g_o3d.Sampler.CLAMP;
+ this.paramSampler = this.scaleTransform.createParam('diffuseSampler',
+ 'ParamSampler');
+ this.paramSampler.value = this.sampler;
+
+ this.sampler.texture = texture;
+ this.scaleTransform.addShape(g_imageShape);
+ if (opt_topLeft) {
+ this.scaleTransform.translate(texture.width / 2, texture.height / 2, 0);
+ }
+ this.scaleTransform.scale(texture.width, -texture.height, 1);
+ this.colorParam = this.scaleTransform.createParam('colorMult', 'ParamFloat4');
+ this.colorParam.value = [1, 1, 1, 1];
+}
+
+/**
+ * Sets up the hud.
+ */
+function setupHud() {
+ var effect = g_mainPack.createObject('Effect');
+ effect.name = 'hud';
+ effect.loadFromFXString(o3djs.util.getElementContentById('imageshader'));
+ g_editableEffects.push(effect);
+ // Make the default colorMult 1, 1, 1, 1 uncase it is not supplied by the
+ // material.
+ effect.createParam('colorMult', 'ParamFloat4').value = [1, 1, 1, 1];
+
+ g_imageEffect = effect;
+
+ var g_imageMaterial = g_mainPack.createObject('Material');
+ g_imageMaterial.drawList = g_hudViewInfo.zOrderedDrawList;
+ g_imageMaterial.effect = effect;
+ effect.createUniformParameters(g_imageMaterial);
+ g_imageMaterial.getParam('colorMult').value = [1, 1, 1, 1];
+
+ g_renderTargetDisplayRoot = g_mainPack.createObject('Transform');
+ g_renderTargetDisplayRoot.parent = g_hudRoot;
+ g_renderTargetDisplayRoot.visible = false;
+
+ g_imageShape = o3djs.primitives.createPlane(g_mainPack, g_imageMaterial,
+ 1, 1, 1, 1,
+ [[1, 0, 0, 0],
+ [0, 0, 1, 0],
+ [0, 1, 0, 0],
+ [0, 0, 0, 1]]);
+
+ // Because it's easier to make a texture here than manage another effect
+ var backTexture = g_mainPack.createTexture2D(
+ 1, 1, g_o3d.Texture.XRGB8, 1, false);
+ backTexture.set(0, [1, 1, 1]);
+
+ g_whiteTexture = backTexture;
+ g_whiteSampler = g_mainPack.createObject('Sampler');
+ g_whiteSampler.texture = g_whiteTexture;
+
+ // Make images to show the render targets.
+ for (var ii = 0; ii < 2; ++ii) {
+ var renderTargetTexture = (ii == 0) ? g_reflectionTexture :
+ g_refractionTexture;
+ var scale = 1;
+ var x = 10;
+ var y = 10 + ii * (g_reflectionTexture.height * scale + 10);
+ var borderSize = 2;
+ var image;
+ // make a back image to create a border around render target.
+ image = new Image(g_renderTargetDisplayRoot, backTexture, true);
+ image.transform.translate(x - borderSize, y - borderSize, -3);
+ image.transform.scale(renderTargetTexture.width * scale + borderSize * 2,
+ renderTargetTexture.height * scale + borderSize * 2,
+ 1);
+ image = new Image(g_renderTargetDisplayRoot, renderTargetTexture, true);
+ image.transform.translate(x, y, -2);
+ image.transform.scale(scale, scale, 1);
+ }
+
+ // Make a fader plane.
+ {
+ var image = new Image(g_hudRoot, backTexture, true);
+ g_faderTransform = image.transform;
+ g_faderTransform.visible = false;
+ g_faderColorParam = image.colorParam;
+ updateFaderPlane();
+ }
+
+ // Make a canvas for text.
+ var canvasLib = o3djs.canvas.create(g_mainPack,
+ g_hudRoot,
+ g_hudViewInfo);
+
+ g_hudQuad = canvasLib.createXYQuad(20, 20, -1, 512, 512, true);
+ g_paint = g_mainPack.createObject('CanvasPaint');
+
+ g_paint.setOutline(3, [1, 1, 1, 1]);
+ g_paint.textAlign = g_o3d.CanvasPaint.LEFT;
+ g_paint.textSize = 16;
+ g_paint.textTypeface = 'Arial';
+ g_paint.color = [0, 0, 0, 1];
+
+ setHudText('Loading...');
+}
+
+/**
+ * Sets the text on the hud.
+ * @param {string} text The text to display.
+ */
+function setHudText(text) {
+ if (g_showError) {
+ return;
+ }
+ var canvas = g_hudQuad.canvas;
+ canvas.clear([0, 0, 0, 0]);
+ canvas.saveMatrix();
+ var lines = text.split('\n');
+ for (var ll = 0; ll < lines.length; ++ll) {
+ var tabs = lines[ll].split('\t');
+ for (var tt = 0; tt < tabs.length; ++tt) {
+ canvas.drawText(tabs[tt], 10 + tt * 120, 30 + 20 * ll, g_paint);
+ }
+ }
+ canvas.restoreMatrix();
+
+ g_hudQuad.updateTexture();
+}
+
+/**
+ * Show a hint message.
+ */
+function showHint() {
+ g_hudQuad.transform.visible = true;
+ g_hudFadeTime = 0.0;
+ setHudText('press H for help.');
+}
+
+/**
+ * Show a help message.
+ */
+function toggleHelp() {
+ g_hudFadeTime = 0.0;
+ g_helpVisible = !g_helpVisible;
+ g_hudQuad.transform.visible = true;
+ if (g_helpVisible) {
+ setHudText('1 - 4\t: Camera Preset\n' +
+ 'Mouse\t: Look Around\n' +
+ 'Wheel\t: Field of View\n' +
+ 'Arrows\t: Move Camera\n' +
+ 'p\t: Toggle Props\n' +
+ 'm\t: Edit Materials\n' +
+ 'e\t: Edit Effects\n' +
+ 'r\t: Show Render Targets\n' +
+ 'c\t: Use Simple Shaders\n' +
+ 'f\t: Show FPS\n' +
+ 'h\t: Show Help\n' +
+ 'o\t: Change Water Effect\n' +
+ 'q\t: Toggle demo camera\n');
+ } else {
+ showHint();
+ }
+}
+
+/**
+ * Show error.
+ * @param {string} msg Msg to display.
+ */
+function showError(msg) {
+ g_hudQuad.transform.visible = true;
+ setHudText('Error: Could not load scene.\n' + msg);
+ g_showError = true;
+}
+
+/**
+ * Removes any callbacks so they don't get called after the page has unloaded.
+ */
+function uninit() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+
diff --git a/o3d/samples/box2d-3d/box2d-3d.html b/o3d/samples/box2d-3d/box2d-3d.html
new file mode 100644
index 0000000..cfaa622
--- /dev/null
+++ b/o3d/samples/box2d-3d/box2d-3d.html
@@ -0,0 +1,201 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <title>Box2DJS in 3D</title>
+
+ <!--=============================-->
+ <!-- Copy this part to your app. -->
+ <!-- START -->
+ <!--=============================-->
+ <!-- libs -->
+ <script type="text/javascript" src="third_party/prototype-1.6.0.2.js"></script>
+
+ <!-- box2djs -->
+ <script type="text/javascript" src='third_party/box2d/common/b2Settings.js'></script>
+ <script type="text/javascript" src='third_party/box2d/common/math/b2Vec2.js'></script>
+ <script type="text/javascript" src='third_party/box2d/common/math/b2Mat22.js'></script>
+ <script type="text/javascript" src='third_party/box2d/common/math/b2Math.js'></script>
+ <script type="text/javascript" src='third_party/box2d/collision/b2AABB.js'></script>
+ <script type="text/javascript" src='third_party/box2d/collision/b2Bound.js'></script>
+ <script type="text/javascript" src='third_party/box2d/collision/b2BoundValues.js'></script>
+ <script type="text/javascript" src='third_party/box2d/collision/b2Pair.js'></script>
+ <script type="text/javascript" src='third_party/box2d/collision/b2PairCallback.js'></script>
+ <script type="text/javascript" src='third_party/box2d/collision/b2BufferedPair.js'></script>
+ <script type="text/javascript" src='third_party/box2d/collision/b2PairManager.js'></script>
+ <script type="text/javascript" src='third_party/box2d/collision/b2BroadPhase.js'></script>
+ <script type="text/javascript" src='third_party/box2d/collision/b2Collision.js'></script>
+ <script type="text/javascript" src='third_party/box2d/collision/Features.js'></script>
+ <script type="text/javascript" src='third_party/box2d/collision/b2ContactID.js'></script>
+ <script type="text/javascript" src='third_party/box2d/collision/b2ContactPoint.js'></script>
+ <script type="text/javascript" src='third_party/box2d/collision/b2Distance.js'></script>
+ <script type="text/javascript" src='third_party/box2d/collision/b2Manifold.js'></script>
+ <script type="text/javascript" src='third_party/box2d/collision/b2OBB.js'></script>
+ <script type="text/javascript" src='third_party/box2d/collision/b2Proxy.js'></script>
+ <script type="text/javascript" src='third_party/box2d/collision/ClipVertex.js'></script>
+ <script type="text/javascript" src='third_party/box2d/collision/shapes/b2Shape.js'></script>
+ <script type="text/javascript" src='third_party/box2d/collision/shapes/b2ShapeDef.js'></script>
+ <script type="text/javascript" src='third_party/box2d/collision/shapes/b2BoxDef.js'></script>
+ <script type="text/javascript" src='third_party/box2d/collision/shapes/b2CircleDef.js'></script>
+ <script type="text/javascript" src='third_party/box2d/collision/shapes/b2CircleShape.js'></script>
+ <script type="text/javascript" src='third_party/box2d/collision/shapes/b2MassData.js'></script>
+ <script type="text/javascript" src='third_party/box2d/collision/shapes/b2PolyDef.js'></script>
+ <script type="text/javascript" src='third_party/box2d/collision/shapes/b2PolyShape.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/b2Body.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/b2BodyDef.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/b2CollisionFilter.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/b2Island.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/b2TimeStep.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/contacts/b2ContactNode.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/contacts/b2Contact.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/contacts/b2ContactConstraint.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/contacts/b2ContactConstraintPoint.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/contacts/b2ContactRegister.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/contacts/b2ContactSolver.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/contacts/b2CircleContact.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/contacts/b2Conservative.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/contacts/b2NullContact.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/contacts/b2PolyAndCircleContact.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/contacts/b2PolyContact.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/b2ContactManager.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/b2World.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/b2WorldListener.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2JointNode.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2Joint.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2JointDef.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2DistanceJoint.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2DistanceJointDef.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2Jacobian.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2GearJoint.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2GearJointDef.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2MouseJoint.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2MouseJointDef.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2PrismaticJoint.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2PrismaticJointDef.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2PulleyJoint.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2PulleyJointDef.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2RevoluteJoint.js'></script>
+ <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2RevoluteJointDef.js'></script>
+ <!--=============================-->
+ <!-- Copy this part to your app. -->
+ <!-- END -->
+ <!--=============================-->
+<script type="text/javascript" src="../o3djs/base.js"></script>
+
+ <!-- demos -->
+ <script src='demos/manager.js'></script>
+ <script src='demos/draw_world.js'></script>
+ <script src='demos/demo_base.js'></script>
+ <script src='demos/top.js'></script>
+ <script src='demos/stack.js'></script>
+ <script src='demos/compound.js'></script>
+ <script src='demos/pendulum.js'></script>
+ <script src='demos/crank.js'></script>
+ <script src='demos/demos.js'></script>
+ <style type="text/css">
+ html, body {
+ border: 0;
+ margin: 0;
+ height: 100%;
+ }
+ </style>
+ </head>
+<body onload="init()" onunload="uninit()">
+<table style="width: 100%; height: 100%;"><tr valign="top" style="height: 50px;"><td>
+<h1>Box2DJS in 3D</h1>
+<p>Based on <a href="http://box2d-js.sourceforge.net/">box2d-js</a>.<br/>
+Left Click to create a new object.<br/>
+Right Click (shift-click on OSX) to switch to the next demo.
+</p>
+</td></tr>
+<tr style="height: 100%;"><td>
+<div id="o3d" style="width: 100%; height: 100%;"></div>
+</td></tr></table>
+<div style="display:none;">
+<textarea id="shader">
+uniform float4x4 worldViewProj : WorldViewProjection;
+uniform float3 lightWorldPos;
+uniform float4 lightColor;
+uniform float4x4 world : World;
+uniform float4x4 view : View;
+uniform float4x4 worldIT : WorldInverseTranspose;
+uniform float4 emissive;
+uniform float4 ambient;
+uniform float4 colorMult;
+sampler2D diffuseSampler;
+uniform float4 specular;
+uniform float shininess;
+
+struct InVertex {
+ float4 position : POSITION;
+ float4 normal : NORMAL;
+ float2 diffuseUV : TEXCOORD0;
+};
+
+struct OutVertex {
+ float4 position : POSITION;
+ float2 diffuseUV : TEXCOORD0;
+ float3 n : TEXCOORD1;
+ float3 l : TEXCOORD2;
+ float3 v : TEXCOORD3;
+};
+
+OutVertex vs(InVertex IN) {
+ OutVertex OUT;
+ OUT.position = mul(IN.position, worldViewProj);
+ OUT.diffuseUV = IN.diffuseUV;
+ OUT.n = mul(IN.normal, worldIT).xyz;
+ OUT.l = lightWorldPos - mul(IN.position, world).xyz;
+ OUT.v = (view[3] - mul(IN.position, world)).xyz;
+ return OUT;
+}
+
+float4 ps(OutVertex IN) : COLOR {
+ float4 diffuse = tex2D(diffuseSampler, IN.diffuseUV) * colorMult;
+ float3 l = normalize(IN.l); float3 n = normalize(IN.n);
+ float3 r = normalize(reflect(n, l));
+ float3 v = normalize(IN.v);
+ float4 litR = lit(dot(n, l), dot(r, v), shininess);
+ return float4((emissive + lightColor *
+ (ambient + diffuse * litR.y + specular * litR.z)).rgb,
+ diffuse.a);
+}
+
+// #o3d VertexShaderEntryPoint vs
+// #o3d PixelShaderEntryPoint ps
+// #o3d MatrixLoadOrder RowMajor
+</textarea>
+</div>
+</body>
+</html>
diff --git a/o3d/samples/box2d-3d/demos/LICENSE.txt b/o3d/samples/box2d-3d/demos/LICENSE.txt
new file mode 100644
index 0000000..c224b40
--- /dev/null
+++ b/o3d/samples/box2d-3d/demos/LICENSE.txt
@@ -0,0 +1,14 @@
+The zlib/libpng License
+
+Copyright (c) 2008 ANDO Yasushi
+
+This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source distribution.
+
diff --git a/o3d/samples/box2d-3d/demos/README.o3d b/o3d/samples/box2d-3d/demos/README.o3d
new file mode 100644
index 0000000..2d3f954
--- /dev/null
+++ b/o3d/samples/box2d-3d/demos/README.o3d
@@ -0,0 +1,5 @@
+This directory contains a subset of Box2D-JS, available at
+http://box2d-js.sourceforge.net/ under the zlib/libpng license (see
+License.txt).
+The software was modified in certain places to make it work with O3D.
+Individual files are marked to indicate modifications occured.
diff --git a/o3d/samples/box2d-3d/demos/compound.js b/o3d/samples/box2d-3d/demos/compound.js
new file mode 100644
index 0000000..e63b983
--- /dev/null
+++ b/o3d/samples/box2d-3d/demos/compound.js
@@ -0,0 +1,71 @@
+// This file comes from Box2D-JS, Copyright (c) 2008 ANDO Yasushi.
+// The original version is available at http://box2d-js.sourceforge.net/ under the
+// zlib/libpng license (see License.txt).
+// This version has been modified to make it work with O3D.
+
+demos.compound = {};
+demos.compound.createCompoundBall = function(world, x, y) {
+ var ballSd1 = new b2CircleDef();
+ ballSd1.density = 1.0;
+ ballSd1.radius = 20;
+ ballSd1.restitution = 0.2;
+ ballSd1.localPosition.Set(-20, 0);
+ var ballSd2 = new b2CircleDef();
+ ballSd2.density = 1.0;
+ ballSd2.radius = 20;
+ ballSd2.restitution = 0.2;
+ ballSd2.localPosition.Set(20, 0);
+ var ballBd = new b2BodyDef();
+ ballBd.AddShape(ballSd1);
+ ballBd.AddShape(ballSd2);
+ ballBd.position.Set(x, y);
+ // NOTE: Added the following line to create a 3d object to display.
+ ballBd.userData = g.mgr.createCompoundCylinder(20, -20, 20, 20);
+ return world.CreateBody(ballBd);
+}
+
+demos.compound.createCompoundPoly = function(world, x, y) {
+ var points = [[-30, 0], [30, 0], [0, 15]];
+ var polySd1 = new b2PolyDef();
+ polySd1.vertexCount = points.length;
+ for (var i = 0; i < points.length; i++) {
+ polySd1.vertices[i].Set(points[i][0], points[i][1]);
+ }
+ polySd1.localRotation = 0.3524 * Math.PI;
+ var R1 = new b2Mat22(polySd1.localRotation);
+ polySd1.localPosition = b2Math.b2MulMV(R1, new b2Vec2(30, 0));
+ polySd1.density = 1.0;
+ var polySd2 = new b2PolyDef();
+ polySd2.vertexCount = points.length;
+ for (var i = 0; i < points.length; i++) {
+ polySd2.vertices[i].Set(points[i][0], points[i][1]);
+ }
+ polySd2.localRotation = -0.3524 * Math.PI;
+ var R2 = new b2Mat22(polySd2.localRotation);
+ polySd2.localPosition = b2Math.b2MulMV(R2, new b2Vec2(-30, 0));
+ var polyBd = new b2BodyDef();
+ polyBd.AddShape(polySd1);
+ polyBd.AddShape(polySd2);
+ polyBd.position.Set(x,y);
+ // NOTE: Added the following line to create a 3d object to display.
+ polyBd.userData = g.mgr.createCompoundWedge(points,
+ polySd1.localPosition,
+ polySd1.localRotation,
+ points,
+ polySd2.localPosition,
+ polySd2.localRotation);
+ return world.CreateBody(polyBd)
+}
+
+demos.compound.initWorld = function(world) {
+ var i;
+ for (i = 1; i <= 4; i++) {
+ demos.compound.createCompoundPoly(world, 150 + 3 * Math.random(), 40 * i);
+ }
+ for (i = 1; i <= 4; i++) {
+ demos.compound.createCompoundBall(world, 350 + 3 * Math.random(), 45 * i);
+ }
+}
+demos.InitWorlds.push(demos.compound.initWorld);
+
+
diff --git a/o3d/samples/box2d-3d/demos/crank.js b/o3d/samples/box2d-3d/demos/crank.js
new file mode 100644
index 0000000..007c1dc
--- /dev/null
+++ b/o3d/samples/box2d-3d/demos/crank.js
@@ -0,0 +1,79 @@
+// This file comes from Box2D-JS, Copyright (c) 2008 ANDO Yasushi.
+// The original version is available at http://box2d-js.sourceforge.net/ under the
+// zlib/libpng license (see License.txt).
+// This version has been modified to make it work with O3D.
+
+demos.crank = {};
+demos.crank.initWorld = function(world) {
+ var ground = world.m_groundBody;
+
+ // Define crank.
+ var sd = new b2BoxDef();
+ sd.extents.Set(5, 25);
+ sd.density = 1.0;
+
+ var bd = new b2BodyDef();
+ bd.userData = g.mgr.createBox(5, 25);
+ bd.AddShape(sd);
+
+ var rjd = new b2RevoluteJointDef();
+
+ var prevBody = ground;
+
+ bd.position.Set(500/2, 210);
+ bd.userData = g.mgr.createBox(5, 25);
+ var body = world.CreateBody(bd);
+
+ rjd.anchorPoint.Set(500/2, 235);
+ rjd.body1 = prevBody;
+ rjd.body2 = body;
+ rjd.motorSpeed = -1.0 * Math.PI;
+ rjd.motorTorque = 500000000.0;
+ rjd.enableMotor = true;
+ world.CreateJoint(rjd);
+
+ prevBody = body;
+
+ // Define follower.
+ sd.extents.Set(5, 45);
+ bd.position.Set(500/2, 140);
+ bd.userData = g.mgr.createBox(5, 45);
+ body = world.CreateBody(bd);
+
+ rjd.anchorPoint.Set(500/2, 185);
+ rjd.body1 = prevBody;
+ rjd.body2 = body;
+ rjd.enableMotor = false;
+ world.CreateJoint(rjd);
+
+ prevBody = body;
+
+ // Define piston
+ sd.extents.Set(20, 20);
+ bd.position.Set(500/2, 95);
+ bd.userData = g.mgr.createBox(20, 20);
+ body = world.CreateBody(bd);
+
+ rjd.anchorPoint.Set(500/2, 95);
+ rjd.body1 = prevBody;
+ rjd.body2 = body;
+ world.CreateJoint(rjd);
+
+ var pjd = new b2PrismaticJointDef();
+ pjd.anchorPoint.Set(500/2, 95);
+ pjd.body1 = ground;
+ pjd.body2 = body;
+ pjd.axis.Set(0.0, 1.0);
+ pjd.motorSpeed = 0.0; // joint friction
+ pjd.motorForce = 100000.0;
+ pjd.enableMotor = true;
+
+ world.CreateJoint(pjd);
+
+ // Create a payload
+ sd.density = 2.0;
+ bd.position.Set(500/2, 10);
+ bd.userData = g.mgr.createBox(20, 20);
+ world.CreateBody(bd);
+}
+demos.InitWorlds.push(demos.crank.initWorld);
diff --git a/o3d/samples/box2d-3d/demos/demo_base.js b/o3d/samples/box2d-3d/demos/demo_base.js
new file mode 100644
index 0000000..3379691
--- /dev/null
+++ b/o3d/samples/box2d-3d/demos/demo_base.js
@@ -0,0 +1,59 @@
+// This file comes from Box2D-JS, Copyright (c) 2008 ANDO Yasushi.
+// The original version is available at http://box2d-js.sourceforge.net/ under the
+// zlib/libpng license (see License.txt).
+// This version has been modified to make it work with O3D.
+
+function createWorld() {
+ var worldAABB = new b2AABB();
+ worldAABB.minVertex.Set(-1000, -1000);
+ worldAABB.maxVertex.Set(1000, 1000);
+ var gravity = new b2Vec2(0, 300);
+ var doSleep = true;
+ var world = new b2World(worldAABB, gravity, doSleep);
+ createGround(world);
+ createBox(world, 0, 125, 10, 250);
+ createBox(world, 500, 125, 10, 250);
+ return world;
+}
+
+function createGround(world) {
+ var groundSd = new b2BoxDef();
+ groundSd.extents.Set(250, 50);
+ groundSd.restitution = 0.2;
+ var groundBd = new b2BodyDef();
+ groundBd.AddShape(groundSd);
+ groundBd.position.Set(250, 340);
+ // NOTE: Added the following line to create a 3d object to display.
+ groundBd.userData = g.mgr.createBox(250, 50);
+ return world.CreateBody(groundBd)
+}
+
+function createBall(world, x, y) {
+ var ballSd = new b2CircleDef();
+ ballSd.density = 1.0;
+ ballSd.radius = 20;
+ ballSd.restitution = 1.0;
+ ballSd.friction = 0;
+ var ballBd = new b2BodyDef();
+ ballBd.AddShape(ballSd);
+ ballBd.position.Set(x,y);
+ // NOTE: Added the following line to create a 3d object to display.
+ ballBd.userData = g.mgr.createCylinder(ballSd.radius);
+ return world.CreateBody(ballBd);
+}
+
+function createBox(world, x, y, width, height, fixed) {
+ if (typeof(fixed) == 'undefined') fixed = true;
+ var boxSd = new b2BoxDef();
+ if (!fixed) boxSd.density = 1.0;
+ boxSd.extents.Set(width, height);
+ var boxBd = new b2BodyDef();
+ // NOTE: Added the following line to create a 3d object to display.
+ boxBd.userData = g.mgr.createBox(width, height);
+ boxBd.AddShape(boxSd);
+ boxBd.position.Set(x,y);
+ return world.CreateBody(boxBd)
+}
+
+var demos = {};
+demos.InitWorlds = [];
diff --git a/o3d/samples/box2d-3d/demos/demos.js b/o3d/samples/box2d-3d/demos/demos.js
new file mode 100644
index 0000000..8834790
--- /dev/null
+++ b/o3d/samples/box2d-3d/demos/demos.js
@@ -0,0 +1,271 @@
+// This file comes from Box2D-JS, Copyright (c) 2008 ANDO Yasushi.
+// The original version is available at http://box2d-js.sourceforge.net/ under the
+// zlib/libpng license (see License.txt).
+// This version has been modified to make it work with O3D.
+
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.picking');
+
+var initId = 0;
+var world;
+var canvasWidth;
+var canvasHeight;
+var canvasTop;
+var canvasLeft;
+
+function setupWorld(did) {
+ var transforms = g.client.getObjectsByClassName('o3d.Transform');
+ for (var tt = 0; tt < transforms.length; ++tt) {
+ var transform = transforms[tt];
+ if (transform.clientId != g.root.clientId &&
+ transform.clientId != g.client.root.clientId) {
+ transform.parent = null;
+ g.pack.removeObject(transform);
+ }
+ }
+ if (!did) did = 0;
+ world = createWorld();
+ initId += did;
+ initId %= demos.InitWorlds.length;
+ if (initId < 0) initId = demos.InitWorlds.length + initId;
+ demos.InitWorlds[initId](world);
+}
+function setupNextWorld() { setupWorld(1); }
+function setupPrevWorld() { setupWorld(-1); }
+function step(elapsedTime) {
+ // NOTE: changed to use the renderEvent's elapsed time instead of
+ // javascript timers. The check below makes the physics never do a time step
+ // slower that 1/30th of a second because collision bugs will happen.
+ if (elapsedTime > 1 / 30) {
+ elapsedTime = 1 / 30;
+ }
+ var stepping = false;
+ var timeStep = elapsedTime;
+ var iteration = 1;
+ if (world) {
+ world.Step(timeStep, iteration);
+ drawWorld(world);
+ }
+}
+
+// When the sample is running in V8, window.g is the browser's global object and
+// g is v8's. When the sample is running only in the browser, they are the same
+// object.
+window.g = {};
+
+// A global object to track stuff with.
+var g = {
+ o3dWidth: -1, // width of our client area
+ o3dHeight: -1, // height of our client area
+ materials: [], // all our materials
+ shapes: [] // all our shapes
+};
+
+/**
+ * Sets up the o3d stuff.
+ * @param {HTML element} o3dElement The o3d object element.
+ */
+function setupO3D(o3dElement) {
+ // Initializes global variables and libraries.
+ g.o3dElement = o3dElement;
+ g.o3d = o3dElement.o3d;
+ g.math = o3djs.math;
+ g.client = o3dElement.client;
+ g.mgr = new O3DManager();
+
+ // The browser needs to be able to see these globals so that it can
+ // call unload later.
+ window.g.client = g.client;
+
+ // Get the width and height of our client area.
+ g.o3dWidth = g.client.width;
+ g.o3dHeight = g.client.height;
+
+ // Creates a pack to manage our resources/assets
+ g.pack = g.client.createPack();
+
+ // Create the render graph for a view.
+ g.viewInfo = o3djs.rendergraph.createBasicView(
+ g.pack,
+ g.client.root,
+ g.client.renderGraphRoot);
+
+ // Create an object to hold global params.
+ g.globalParamObject = g.pack.createObject('ParamObject');
+ g.lightWorldPosParam = g.globalParamObject.createParam('lightWorldPos',
+ 'ParamFloat3');
+ g.lightColorParam = g.globalParamObject.createParam('lightColor',
+ 'ParamFloat4');
+ g.lightWorldPosParam.value = [200, -1500, -1000];
+ g.lightColorParam.value = [1, 1, 1.0, 1.0];
+
+ // Create Materials.
+ g.materials = [];
+ var material = g.pack.createObject('Material');
+ var effect = g.pack.createObject('Effect');
+ effect.loadFromFXString(document.getElementById('shader').value);
+ material.effect = effect;
+ effect.createUniformParameters(material);
+ material.getParam('lightWorldPos').bind(g.lightWorldPosParam);
+ material.getParam('lightColor').bind(g.lightColorParam);
+ material.getParam('emissive').set(0, 0, 0, 1);
+ material.getParam('ambient').set(0, 0, 0, 1);
+ material.getParam('specular').set(1, 1, 1, 1);
+ material.getParam('shininess').value = 20;
+ material.drawList = g.viewInfo.performanceDrawList;
+ g.materials[0] = material;
+
+ // Create a kind of checkboardish texture.
+ var pixels = [];
+ for (var y = 0; y < 32; ++y) {
+ for (var x = 0; x < 32; ++x) {
+ var offset = (y * 32 + x) * 3; // rgb
+ var u = x / 32 * Math.PI * 0.5;
+ var v = y / 32 * Math.PI * 0.5;
+ pixels[offset + 0] = 1;//Math.sin(Math.PI * 32 / x) * 0.2 + 0.8; // red
+ pixels[offset + 1] = Math.floor(x / 8) % 2 * 0.2 + 0.8; // green
+ pixels[offset + 2] = Math.floor(y / 8) % 2 * 0.5 + 0.5; // blue
+ }
+ }
+ var texture = g.pack.createTexture2D(32, 32, g.o3d.Texture.XRGB8, 1, false);
+ texture.set(0, pixels);
+ var samplerParam = material.getParam('diffuseSampler');
+ var sampler = g.pack.createObject('Sampler');
+ samplerParam.value = sampler;
+ sampler.texture = texture;
+
+ // Creates a transform to put our data on.
+ g.root = g.pack.createObject('Transform');
+ g.root.parent = g.client.root;
+
+ // make a cube for drawing lines
+ g.lineShape = o3djs.primitives.createRainbowCube(
+ g.pack,
+ g.materials[0],
+ 1,
+ g.math.matrix4.translation([0, 0.5, 0]));
+
+ // Set the projection matrix
+ g.viewInfo.drawContext.projection = g.math.matrix4.perspective(
+ g.math.degToRad(45),
+ g.o3dWidth / g.o3dHeight,
+ 0.1,
+ 10000);
+
+ // Set the view matrix.
+ g.viewInfo.drawContext.view = g.math.matrix4.lookAt(
+ [100, -50, -600],
+ [250, 80, 0],
+ [0, -1, 0]);
+
+ // Make copies of the view and projection matrix to get fast access to them.
+ g.projectionMatrix = g.math.matrix4.copy(g.viewInfo.drawContext.projection);
+ g.viewMatrix = g.math.matrix4.copy(g.viewInfo.drawContext.view);
+
+ // Make a bounding box to use for picking.
+ g.worldBox = g.o3d.BoundingBox([0, -100, 0],
+ [500, 250, 5]);
+
+ // Steps the physics with a time of zero. This is left over from the orignial
+ // code.
+ step(0);
+
+ g.client.setRenderCallback(o3dOnRender);
+}
+
+/**
+ * Called just before rendering each frame.
+ * @param {o3d.RenderEvent} renderEvent The render event passed by
+ * o3d.
+ */
+function o3dOnRender(renderEvent) {
+ var newWidth = g.client.width;
+ var newHeight = g.client.height;
+ if (newWidth != g.o3dWidth || newHeight != g.o3dHeight) {
+ g.o3dWidth = newWidth;
+ g.o3dHeight = newHeight;
+
+ // Adjust the projection matrix.
+ var projectionMatrix = g.math.matrix4.perspective(g.math.degToRad(45),
+ g.o3dWidth / g.o3dHeight,
+ 0.1,
+ 10000);
+ g.viewInfo.drawContext.projection = projectionMatrix;
+ g.projectionMatrix = projectionMatrix;
+ }
+ step(renderEvent.elapsedTime);
+}
+
+/**
+ * This is the original setup code modified to work with o3d.
+ * @param {Array} clientElements Array of o3d objects.
+ */
+function setupStep2(clientElements) {
+ var canvasElm = clientElements[0];
+ canvasElm.id = 'canvas';
+ setupO3D(canvasElm);
+ setupWorld();
+ canvasWidth = g.client.width;
+ canvasHeight = g.client.height;
+ canvasTop = parseInt(canvasElm.style.top);
+ canvasLeft = parseInt(canvasElm.style.left);
+ o3djs.event.addEventListener(canvasElm, 'mousedown', function(e) {
+ if (e.shiftKey || e.button == g.o3d.Event.BUTTON_RIGHT) {
+ setupPrevWorld();
+ } else {
+ createNewRandomObject(world, e);
+ }
+ });
+
+ window.g_finished = true; // for selenium
+}
+
+/**
+ * Creates a new random object using 3d picking to figure out where to start it.
+ * @param {Object} world A B2World object.
+ * @param {Object} offset Mouse position relative to o3d area.
+ */
+function createNewRandomObject(world, offset) {
+ var worldRay = o3djs.picking.clientPositionToWorldRayEx(
+ offset.x,
+ offset.y,
+ g.viewMatrix,
+ g.projectionMatrix,
+ g.o3dWidth,
+ g.o3dHeight);
+ var rayIntersectionInfo = g.worldBox.intersectRay(worldRay.near,
+ worldRay.far);
+ if (rayIntersectionInfo.intersected) {
+ var position = rayIntersectionInfo.position;
+ if (Math.random() < 0.5) {
+ demos.top.createBall(world, position[0], position[1]);
+ } else {
+ createBox(world, position[0], position[1], 10, 10, false);
+ }
+ }
+}
+
+function init() {
+ window.g_finished = false; // for selenium.
+
+ // Runs the sample in V8. Comment out this line to run it in the browser
+ // JavaScript engine, for example if you want to debug it. This sample cannot
+ // currently run in V8 on IE because it access the DOM.
+ if (!o3djs.base.IsMSIE()) {
+ o3djs.util.setMainEngine(o3djs.util.Engine.V8);
+ o3djs.util.addScriptUri('third_party');
+ o3djs.util.addScriptUri('demos');
+ o3djs.util.addScriptUri('style');
+ }
+
+ o3djs.util.makeClients(setupStep2);
+}
+
+function uninit() {
+ if (g.client) {
+ g.client.cleanup();
+ }
+}
diff --git a/o3d/samples/box2d-3d/demos/draw_world.js b/o3d/samples/box2d-3d/demos/draw_world.js
new file mode 100644
index 0000000..b49ef1a
--- /dev/null
+++ b/o3d/samples/box2d-3d/demos/draw_world.js
@@ -0,0 +1,90 @@
+// This file comes from Box2D-JS, Copyright (c) 2008 ANDO Yasushi.
+// The original version is available at http://box2d-js.sourceforge.net/ under the
+// zlib/libpng license (see License.txt).
+// This version has been modified to make it work with O3D.
+
+// NOTE: Changed this file pretty significantly. The original uses a
+// canvas element and drew lines on it. This one just updates the positions and
+// orientations of o3d objects to match the physics as well as using a
+// scaled box to stand in for joints.
+
+/**
+ * Draws all the object in the world.
+ * @param {Object} world A B2World object managing the physics world.
+ */
+function drawWorld(world) {
+ for (var j = world.m_jointList; j; j = j.m_next) {
+ drawJoint(j);
+ }
+ for (var b = world.m_bodyList; b; b = b.m_next) {
+ var o3dShape = b.GetUserData();
+ if (o3dShape) {
+ o3dShape.updateTransform(b);
+ }
+ }
+}
+/**
+ * Scales a joint transform to represnet a line.
+ * @param {Object} transformInfo An object with o3d information for the
+ * joint.
+ * @param {Object} p1 An object with x and y fields that specify the origin of
+ * the joint in 2d.
+ * @param {Object} p2 An object with x an dy fields that specify the far end of
+ * the joint in 2d.
+ */
+function scaleJointTransform(transformInfo, p1, p2) {
+ var dx = p2.x - p1.x;
+ var dy = p2.y - p1.y;
+ var length = Math.sqrt(dx * dx + dy * dy);
+ var transform = transformInfo.transform;
+ transform.identity();
+ transform.translate(p1.x, p1.y, 0);
+ transform.rotateZ(Math.atan2(-dx, dy));
+ transform.scale(2, length, 2);
+}
+
+/**
+ * Draws a joint
+ * @param {Object} joint A b2Joint object representing a joint.
+ */
+function drawJoint(joint) {
+ var transformInfo = joint.m_o3dTransformInfo;
+ if (!transformInfo) {
+ // This joint did not already have something in o3d to represent it
+ // so we create one here.
+ var transform = g.pack.createObject('Transform');
+ transform.parent = g.root;
+ transform.addShape(g.lineShape);
+ transformInfo = {
+ transform: transform
+ };
+ joint.m_o3dTransformInfo = transformInfo;
+ }
+ var b1 = joint.m_body1;
+ var b2 = joint.m_body2;
+ var x1 = b1.m_position;
+ var x2 = b2.m_position;
+ var p1 = joint.GetAnchor1();
+ var p2 = joint.GetAnchor2();
+ switch (joint.m_type) {
+ case b2Joint.e_distanceJoint:
+ scaleJointTransform(transformInfo, p1, p2);
+ break;
+
+ case b2Joint.e_pulleyJoint:
+ break;
+
+ default:
+ if (b1 == world.m_groundBody) {
+ scaleJointTransform(transformInfo, p1, x2);
+ }
+ else if (b2 == world.m_groundBody) {
+ scaleJointTransform(transformInfo, p1, x1);
+ }
+ else {
+ scaleJointTransform(transformInfo, x1, x2);
+ }
+ break;
+ }
+}
+
diff --git a/o3d/samples/box2d-3d/demos/manager.js b/o3d/samples/box2d-3d/demos/manager.js
new file mode 100644
index 0000000..8fc177b
--- /dev/null
+++ b/o3d/samples/box2d-3d/demos/manager.js
@@ -0,0 +1,210 @@
+// This file comes from Box2D-JS, Copyright (c) 2008 ANDO Yasushi.
+// The original version is available at http://box2d-js.sourceforge.net/ under the
+// zlib/libpng license (see License.txt).
+// This version has been modified to make it work with O3D.
+
+/**
+ * O3DManager manages o3d objects for this demo.
+ * @constructor
+ */
+function O3DManager() {
+ this.shapes = [];
+}
+
+/**
+ * Gets or creates a cylinder shape. If a cylinder of the same radius already
+ * exists that one will be returned, otherwise a new one will be created.
+ * @param {number} radius Radius of cylinder to create.
+ * @return {o3d.Shape} The shape.
+ */
+O3DManager.prototype.createCylinder = function(radius) {
+ var id = 'cylinder-' + radius;
+ var shape = this.shapes[id];
+ if (!shape) {
+ shape = o3djs.primitives.createCylinder(g.pack,
+ g.materials[0],
+ radius, 40, 20, 1,
+ [[1, 0, 0, 0],
+ [0, 0, 1, 0],
+ [0, -1, 0, 0],
+ [0, 0, 0, 1]]);
+ this.shapes[id] = shape;
+ }
+ return new O3DShape({shape: shape});
+};
+
+/**
+ * Gets or creates a compound cylinder shape. If a compound cylinder shape of
+ * the same parameters already exists that one will be returned, otherwise a new
+ * one will be created.
+ * @param {number} radius1 Radius of first cylinder.
+ * @param {number} offset1 X Offset for first cylinder.
+ * @param {number} radius2 Radius of second cylinder.
+ * @param {number} offset2 X Offset for second cylinder.
+ * @return {o3d.Shape} The shape.
+ */
+O3DManager.prototype.createCompoundCylinder = function(radius1,
+ offset1,
+ radius2,
+ offset2) {
+ var id = 'compoundCylinder-' + radius1 + '-' + offset1 +
+ '-' + radius2 + '-' + offset2;
+ var shape = this.shapes[id];
+ if (!shape) {
+ shape = o3djs.primitives.createCylinder(
+ g.pack, g.materials[0], radius1, 40, 20, 1,
+ [[1, 0, 0, 0],
+ [0, 0, 1, 0],
+ [0, -1, 0, 0],
+ [offset1, 0, 0, 1]]);
+ shape2 = o3djs.primitives.createCylinder(
+ g.pack, g.materials[0], radius2, 40, 20, 1,
+ [[1, 0, 0, 0],
+ [0, 0, 1, 0],
+ [0, -1, 0, 0],
+ [offset2, 0, 0, 1]]);
+ shape2.elements[0].owner = shape;
+ g.pack.removeObject(shape2);
+ this.shapes[id] = shape;
+ }
+ return new O3DShape({shape: shape});
+};
+
+/**
+ * Gets or creates a box shape. If a box of the same width and height already
+ * exists that one will be returned, otherwise a new one will be created.
+ * @param {number} width Width of box.
+ * @param {number} height Height of box.
+ * @return {o3d.Shape} The shape.
+ */
+O3DManager.prototype.createBox = function(width, height) {
+ var name = 'box-' + width + '-' + height;
+ var shape = this.shapes[name];
+ if (!shape) {
+ shape = o3djs.primitives.createBox(g.pack,
+ g.materials[0],
+ width * 2, height * 2, 40);
+ this.shapes[name] = shape;
+ }
+ return new O3DShape({shape: shape});
+};
+
+/**
+ * Gets or creates a wedge shape. If a wedge of the same parametrs already
+ * exists that one will be returned, otherwise a new one will be created.
+ * @param {Array} points Array of points in the format
+ * [[x1, y1], [x2, y2], [x3, y3]] that describe a 2d triangle.
+ * @return {o3d.Shape} The shape.
+ */
+O3DManager.prototype.createWedge = function(points) {
+ var name = 'wedge';
+ for (var pp = 0; pp < points.length; ++pp) {
+ name += '-' + points[pp][0] + '-' + points[pp][1];
+ }
+ var shape = this.shapes[name];
+ if (!shape) {
+ shape = o3djs.primitives.createPrism(g.pack,
+ g.materials[0],
+ points, 40);
+ this.shapes[name] = shape;
+ }
+ return new O3DShape({shape: shape});
+};
+
+/**
+ * Gets or creates a compound wedge shape (2 wedges). If a compound wedge of the
+ * same parametrs already exists that one will be returned, otherwise a new one
+ * will be created.
+ * @param {Array} points1 Array of points that describe a 2d triangle for the
+ * first wedge in the format [[x1, y1], [x2, y2], [x3, y3]] .
+ * @param {Object} position1 An object with x and y properties used to offset
+ * the first wedge.
+ * @param {number} rotation1 Rotation in radians to rotate the first wedge on
+ * the z axis.
+ * @param {Array} points2 Array of points that describe a 2d triangle for the
+ * second wedge in the format [[x1, y1], [x2, y2], [x3, y3]] .
+ * @param {Object} position2 An object with x and y properties used to offset
+ * the second wedge.
+ * @param {number} rotation2 Rotation in radians to rotate the second wedge on
+ * the z axis.
+ * @return {o3d.Shape} The shape.
+ */
+O3DManager.prototype.createCompoundWedge = function(points1,
+ position1,
+ rotation1,
+ points2,
+ position2,
+ rotation2) {
+ var name = 'compoundWedge';
+ for (var pp = 0; pp < points1.length; ++pp) {
+ name += '-' + points1[pp][0] + '-' + points1[pp][1];
+ }
+ name += '-' + position1.x + '-' + position1.y + '-' + rotation1;
+ for (var pp = 0; pp < points2.length; ++pp) {
+ name += '-' + points2[pp][0] + '-' + points2[pp][1];
+ }
+ name += '-' + position2.x + '-' + position2.y + '-' + rotation2;
+ var shape = this.shapes[name];
+ if (!shape) {
+ shape = o3djs.primitives.createPrism(
+ g.pack,
+ g.materials[0],
+ points1,
+ 40,
+ g.math.matrix4.mul(
+ g.math.matrix4.rotationZ(rotation1),
+ g.math.matrix4.translation([position1.x, position1.y, 0])));
+ shape2 = o3djs.primitives.createPrism(
+ g.pack,
+ g.materials[0],
+ points2,
+ 40,
+ g.math.matrix4.mul(
+ g.math.matrix4.rotationZ(rotation2),
+ g.math.matrix4.translation([position2.x, position2.y, 0])));
+ shape2.elements[0].owner = shape;
+ g.pack.removeObject(shape2);
+ this.shapes[name] = shape;
+ }
+ return new O3DShape({shape: shape});
+};
+
+/**
+ * An O3DShape manages an O3D shape for the demo.
+ * @constructor
+ * @param {Object} spec An object that contains the fields needed to create the
+ * O3DShape. Currently only the field "shape" is needed.
+ */
+function O3DShape(spec) {
+ this.init(spec);
+}
+
+/**
+ * Initializes an O3DShape
+ * @param {Object} spec An object that contains the fields needed to create the
+ * O3DShape. Currently only the field "shape" is needed.
+ */
+O3DShape.prototype.init = function(spec) {
+ this.transform = g.pack.createObject('Transform');
+ this.transform.parent = g.root;
+ this.transform.addShape(spec.shape);
+ this.transform.createParam('colorMult', 'ParamFloat4').value =
+ [Math.random() * 0.8 + 0.2,
+ Math.random() * 0.8 + 0.2,
+ Math.random() * 0.8 + 0.2,
+ 1];
+};
+
+/**
+ * Updates the position and orientation of an O3DShape.
+ * @param {Object} body A B2Body object from the Box2djs library.
+ */
+O3DShape.prototype.updateTransform = function(body) {
+ var transform = this.transform;
+ var position = body.GetOriginPosition();
+ transform.identity();
+ transform.translate(position.x, position.y, 0);
+ transform.rotateZ(body.GetRotation());
+};
+
+
diff --git a/o3d/samples/box2d-3d/demos/pendulum.js b/o3d/samples/box2d-3d/demos/pendulum.js
new file mode 100644
index 0000000..730d039
--- /dev/null
+++ b/o3d/samples/box2d-3d/demos/pendulum.js
@@ -0,0 +1,25 @@
+// This file comes from Box2D-JS, Copyright (c) 2008 ANDO Yasushi.
+// The original version is available at http://box2d-js.sourceforge.net/ under the
+// zlib/libpng license (see License.txt).
+// This version has been modified to make it work with O3D.
+
+demos.pendulum = {};
+demos.pendulum.initWorld = function(world) {
+ var i;
+ var ground = world.GetGroundBody();
+ var jointDef = new b2RevoluteJointDef();
+ var L = 150;
+ for (i = 0; i < 4; i++) {
+ jointDef.anchorPoint.Set(250 + 40 * i, 200 - L);
+ jointDef.body1 = ground;
+ jointDef.body2 = createBall(world, 250 + 40 * i, 200);
+ world.CreateJoint(jointDef);
+ }
+ jointDef.anchorPoint.Set(250 - 40, 200 - L);
+ jointDef.body1 = ground;
+ jointDef.body2 = createBall(world, 250 - 40 - L, 200 - L);
+ world.CreateJoint(jointDef);
+}
+demos.InitWorlds.push(demos.pendulum.initWorld);
+
+
diff --git a/o3d/samples/box2d-3d/demos/stack.js b/o3d/samples/box2d-3d/demos/stack.js
new file mode 100644
index 0000000..ffee25b
--- /dev/null
+++ b/o3d/samples/box2d-3d/demos/stack.js
@@ -0,0 +1,37 @@
+// This file comes from Box2D-JS, Copyright (c) 2008 ANDO Yasushi.
+// The original version is available at http://box2d-js.sourceforge.net/ under the
+// zlib/libpng license (see License.txt).
+// This version has been modified to make it work with O3D.
+
+demos.stack = {};
+demos.stack.initWorld = function(world) {
+ var sd = new b2BoxDef();
+ var bd = new b2BodyDef();
+ bd.AddShape(sd);
+ sd.density = 1.0;
+ sd.friction = 0.5;
+ sd.extents.Set(10, 10);
+
+ var i;
+ for (i = 0; i < 8; i++) {
+ bd.position.Set(500/2-Math.random()*2-1, (250-5-i*22));
+ // NOTE: Added the following line to create a 3d object to display.
+ bd.userData = g.mgr.createBox(10, 10);
+ world.CreateBody(bd);
+ }
+ for (i = 0; i < 8; i++) {
+ bd.position.Set(500/2-100-Math.random()*5+i, (250-5-i*22));
+ // NOTE: Added the following line to create a 3d object to display.
+ bd.userData = g.mgr.createBox(10, 10);
+ world.CreateBody(bd);
+ }
+ for (i = 0; i < 8; i++) {
+ bd.position.Set(500/2+100+Math.random()*5-i, (250-5-i*22));
+ // NOTE: Added the following line to create a 3d object to display.
+ bd.userData = g.mgr.createBox(10, 10);
+ world.CreateBody(bd);
+ }
+}
+demos.InitWorlds.push(demos.stack.initWorld);
+
+
diff --git a/o3d/samples/box2d-3d/demos/top.js b/o3d/samples/box2d-3d/demos/top.js
new file mode 100644
index 0000000..e569e7e
--- /dev/null
+++ b/o3d/samples/box2d-3d/demos/top.js
@@ -0,0 +1,53 @@
+// This file comes from Box2D-JS, Copyright (c) 2008 ANDO Yasushi.
+// The original version is available at http://box2d-js.sourceforge.net/ under the
+// zlib/libpng license (see License.txt).
+// This version has been modified to make it work with O3D.
+
+demos.top = {};
+demos.top.createBall = function(world, x, y, rad, fixed) {
+ var ballSd = new b2CircleDef();
+ if (!fixed) ballSd.density = 1.0;
+ ballSd.radius = rad || 10;
+ ballSd.restitution = 0.2;
+ var ballBd = new b2BodyDef();
+ ballBd.AddShape(ballSd);
+ ballBd.position.Set(x,y);
+ // NOTE: Added the following line to create a 3d object to display.
+ ballBd.userData = g.mgr.createCylinder(ballSd.radius);
+ return world.CreateBody(ballBd);
+};
+demos.top.createPoly = function(world, x, y, points, fixed) {
+ var polySd = new b2PolyDef();
+ if (!fixed) polySd.density = 1.0;
+ polySd.vertexCount = points.length;
+ for (var i = 0; i < points.length; i++) {
+ polySd.vertices[i].Set(points[i][0], points[i][1]);
+ }
+ var polyBd = new b2BodyDef();
+ if (points.length == 3) {
+ // NOTE: Added the following line to create a 3d object to display.
+ polyBd.userData = g.mgr.createWedge(points);
+ }
+ polyBd.AddShape(polySd);
+ polyBd.position.Set(x,y);
+ return world.CreateBody(polyBd)
+};
+demos.top.initWorld = function(world) {
+ demos.top.createBall(world, 350, 100, 50, true);
+ demos.top.createPoly(world, 100, 100, [[0, 0], [10, 30], [-10, 30]], true);
+ demos.top.createPoly(world, 150, 150, [[0, 0], [10, 30], [-10, 30]], true);
+ var pendulum = createBox(world, 150, 100, 20, 20, false);
+ var jointDef = new b2RevoluteJointDef();
+ jointDef.body1 = pendulum;
+ jointDef.body2 = world.GetGroundBody();
+ jointDef.anchorPoint = pendulum.GetCenterPosition();
+ world.CreateJoint(jointDef);
+
+ var seesaw = demos.top.createPoly(world, 300, 200, [[0, 0], [100, 30], [-100, 30]]);
+ jointDef.body1 = seesaw;
+ jointDef.anchorPoint = seesaw.GetCenterPosition();
+ world.CreateJoint(jointDef);
+};
+demos.InitWorlds.push(demos.top.initWorld);
+
+
diff --git a/o3d/samples/box2d-3d/third_party/box2d/LICENSE.txt b/o3d/samples/box2d-3d/third_party/box2d/LICENSE.txt
new file mode 100644
index 0000000..c224b40
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/LICENSE.txt
@@ -0,0 +1,14 @@
+The zlib/libpng License
+
+Copyright (c) 2008 ANDO Yasushi
+
+This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source distribution.
+
diff --git a/o3d/samples/box2d-3d/third_party/box2d/README.o3d b/o3d/samples/box2d-3d/third_party/box2d/README.o3d
new file mode 100644
index 0000000..0f3fdc7
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/README.o3d
@@ -0,0 +1,5 @@
+This directory contains a subset of Box2D-JS, available at
+http://box2d-js.sourceforge.net/ under the zlib/libpng license (see
+License.txt).
+The software was not modified, but only the portions under js/box2d in the
+original distribution are here.
diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/ClipVertex.js b/o3d/samples/box2d-3d/third_party/box2d/collision/ClipVertex.js
new file mode 100644
index 0000000..7c944dd
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/collision/ClipVertex.js
@@ -0,0 +1,35 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+var ClipVertex = Class.create();
+ClipVertex.prototype =
+{
+ v: new b2Vec2(),
+ id: new b2ContactID(),
+ initialize: function() {
+ // initialize instance variables for references
+ this.v = new b2Vec2();
+ this.id = new b2ContactID();
+ //
+}};
+
+
diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/Features.js b/o3d/samples/box2d-3d/third_party/box2d/collision/Features.js
new file mode 100644
index 0000000..b0042a6
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/collision/Features.js
@@ -0,0 +1,61 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+// We use contact ids to facilitate warm starting.
+var Features = Class.create();
+Features.prototype =
+{
+ //
+ set_referenceFace: function(value){
+ this._referenceFace = value;
+ this._m_id._key = (this._m_id._key & 0xffffff00) | (this._referenceFace & 0x000000ff)
+ },
+ get_referenceFace: function(){
+ return this._referenceFace;
+ },
+ _referenceFace: 0,
+ //
+ set_incidentEdge: function(value){
+ this._incidentEdge = value;
+ this._m_id._key = (this._m_id._key & 0xffff00ff) | ((this._incidentEdge << 8) & 0x0000ff00)
+ },
+ get_incidentEdge: function(){
+ return this._incidentEdge;
+ },
+ _incidentEdge: 0,
+ //
+ set_incidentVertex: function(value){
+ this._incidentVertex = value;
+ this._m_id._key = (this._m_id._key & 0xff00ffff) | ((this._incidentVertex << 16) & 0x00ff0000)
+ },
+ get_incidentVertex: function(){
+ return this._incidentVertex;
+ },
+ _incidentVertex: 0,
+ //
+ set_flip: function(value){
+ this._flip = value;
+ this._m_id._key = (this._m_id._key & 0x00ffffff) | ((this._flip << 24) & 0xff000000)
+ },
+ get_flip: function(){
+ return this._flip;
+ },
+ _flip: 0,
+ _m_id: null,
+ initialize: function() {}};
diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2AABB.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2AABB.js
new file mode 100644
index 0000000..c22ccc3
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2AABB.js
@@ -0,0 +1,45 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+// A manifold for two touching convex shapes.
+var b2AABB = Class.create();
+b2AABB.prototype =
+{
+ IsValid: function(){
+ //var d = b2Math.SubtractVV(this.maxVertex, this.minVertex);
+ var dX = this.maxVertex.x;
+ var dY = this.maxVertex.y;
+ dX = this.maxVertex.x;
+ dY = this.maxVertex.y;
+ dX -= this.minVertex.x;
+ dY -= this.minVertex.y;
+ var valid = dX >= 0.0 && dY >= 0.0;
+ valid = valid && this.minVertex.IsValid() && this.maxVertex.IsValid();
+ return valid;
+ },
+
+ minVertex: new b2Vec2(),
+ maxVertex: new b2Vec2(),
+ initialize: function() {
+ // initialize instance variables for references
+ this.minVertex = new b2Vec2();
+ this.maxVertex = new b2Vec2();
+ //
+}};
diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2Bound.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2Bound.js
new file mode 100644
index 0000000..b203323
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2Bound.js
@@ -0,0 +1,43 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+var b2Bound = Class.create();
+b2Bound.prototype = {
+ IsLower: function(){ return (this.value & 1) == 0; },
+ IsUpper: function(){ return (this.value & 1) == 1; },
+ Swap: function(b){
+ var tempValue = this.value;
+ var tempProxyId = this.proxyId;
+ var tempStabbingCount = this.stabbingCount;
+
+ this.value = b.value;
+ this.proxyId = b.proxyId;
+ this.stabbingCount = b.stabbingCount;
+
+ b.value = tempValue;
+ b.proxyId = tempProxyId;
+ b.stabbingCount = tempStabbingCount;
+ },
+
+ value: 0,
+ proxyId: 0,
+ stabbingCount: 0,
+
+ initialize: function() {}}
diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2BoundValues.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2BoundValues.js
new file mode 100644
index 0000000..25294f2
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2BoundValues.js
@@ -0,0 +1,31 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+var b2BoundValues = Class.create();
+b2BoundValues.prototype = {
+ lowerValues: [0,0],
+ upperValues: [0,0],
+
+ initialize: function() {
+ // initialize instance variables for references
+ this.lowerValues = [0,0];
+ this.upperValues = [0,0];
+ //
+}}
diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2BroadPhase.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2BroadPhase.js
new file mode 100644
index 0000000..f562329
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2BroadPhase.js
@@ -0,0 +1,898 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+/*
+This broad phase uses the Sweep and Prune algorithm in:
+Collision Detection in Interactive 3D Environments by Gino van den Bergen
+Also, some ideas, such integral values for fast compares comes from
+Bullet (http:/www.bulletphysics.com).
+*/
+
+
+// Notes:
+// - we use bound arrays instead of linked lists for cache coherence.
+// - we use quantized integral values for fast compares.
+// - we use short indices rather than pointers to save memory.
+// - we use a stabbing count for fast overlap queries (less than order N).
+// - we also use a time stamp on each proxy to speed up the registration of
+// overlap query results.
+// - where possible, we compare bound indices instead of values to reduce
+// cache misses (TODO_ERIN).
+// - no broadphase is perfect and neither is this one: it is not great for huge
+// worlds (use a multi-SAP instead), it is not great for large objects.
+
+var b2BroadPhase = Class.create();
+b2BroadPhase.prototype =
+{
+//public:
+ initialize: function(worldAABB, callback){
+ // initialize instance variables for references
+ this.m_pairManager = new b2PairManager();
+ this.m_proxyPool = new Array(b2Settings.b2_maxPairs);
+ this.m_bounds = new Array(2*b2Settings.b2_maxProxies);
+ this.m_queryResults = new Array(b2Settings.b2_maxProxies);
+ this.m_quantizationFactor = new b2Vec2();
+ //
+
+ //b2Settings.b2Assert(worldAABB.IsValid());
+ var i = 0;
+
+ this.m_pairManager.Initialize(this, callback);
+
+ this.m_worldAABB = worldAABB;
+
+ this.m_proxyCount = 0;
+
+ // query results
+ for (i = 0; i < b2Settings.b2_maxProxies; i++){
+ this.m_queryResults[i] = 0;
+ }
+
+ // bounds array
+ this.m_bounds = new Array(2);
+ for (i = 0; i < 2; i++){
+ this.m_bounds[i] = new Array(2*b2Settings.b2_maxProxies);
+ for (var j = 0; j < 2*b2Settings.b2_maxProxies; j++){
+ this.m_bounds[i][j] = new b2Bound();
+ }
+ }
+
+ //var d = b2Math.SubtractVV(worldAABB.maxVertex, worldAABB.minVertex);
+ var dX = worldAABB.maxVertex.x;
+ var dY = worldAABB.maxVertex.y;
+ dX -= worldAABB.minVertex.x;
+ dY -= worldAABB.minVertex.y;
+
+ this.m_quantizationFactor.x = b2Settings.USHRT_MAX / dX;
+ this.m_quantizationFactor.y = b2Settings.USHRT_MAX / dY;
+
+ var tProxy;
+ for (i = 0; i < b2Settings.b2_maxProxies - 1; ++i)
+ {
+ tProxy = new b2Proxy();
+ this.m_proxyPool[i] = tProxy;
+ tProxy.SetNext(i + 1);
+ tProxy.timeStamp = 0;
+ tProxy.overlapCount = b2BroadPhase.b2_invalid;
+ tProxy.userData = null;
+ }
+ tProxy = new b2Proxy();
+ this.m_proxyPool[b2Settings.b2_maxProxies-1] = tProxy;
+ tProxy.SetNext(b2Pair.b2_nullProxy);
+ tProxy.timeStamp = 0;
+ tProxy.overlapCount = b2BroadPhase.b2_invalid;
+ tProxy.userData = null;
+ this.m_freeProxy = 0;
+
+ this.m_timeStamp = 1;
+ this.m_queryResultCount = 0;
+ },
+ //~b2BroadPhase();
+
+ // Use this to see if your proxy is in range. If it is not in range,
+ // it should be destroyed. Otherwise you may get O(m^2) pairs, where m
+ // is the number of proxies that are out of range.
+ InRange: function(aabb){
+ //var d = b2Math.b2MaxV(b2Math.SubtractVV(aabb.minVertex, this.m_worldAABB.maxVertex), b2Math.SubtractVV(this.m_worldAABB.minVertex, aabb.maxVertex));
+ var dX;
+ var dY;
+ var d2X;
+ var d2Y;
+
+ dX = aabb.minVertex.x;
+ dY = aabb.minVertex.y;
+ dX -= this.m_worldAABB.maxVertex.x;
+ dY -= this.m_worldAABB.maxVertex.y;
+
+ d2X = this.m_worldAABB.minVertex.x;
+ d2Y = this.m_worldAABB.minVertex.y;
+ d2X -= aabb.maxVertex.x;
+ d2Y -= aabb.maxVertex.y;
+
+ dX = b2Math.b2Max(dX, d2X);
+ dY = b2Math.b2Max(dY, d2Y);
+
+ return b2Math.b2Max(dX, dY) < 0.0;
+ },
+
+ // Get a single proxy. Returns NULL if the id is invalid.
+ GetProxy: function(proxyId){
+ if (proxyId == b2Pair.b2_nullProxy || this.m_proxyPool[proxyId].IsValid() == false)
+ {
+ return null;
+ }
+
+ return this.m_proxyPool[ proxyId ];
+ },
+
+ // Create and destroy proxies. These call Flush first.
+ CreateProxy: function(aabb, userData){
+ var index = 0;
+ var proxy;
+
+ //b2Settings.b2Assert(this.m_proxyCount < b2_maxProxies);
+ //b2Settings.b2Assert(this.m_freeProxy != b2Pair.b2_nullProxy);
+
+ var proxyId = this.m_freeProxy;
+ proxy = this.m_proxyPool[ proxyId ];
+ this.m_freeProxy = proxy.GetNext();
+
+ proxy.overlapCount = 0;
+ proxy.userData = userData;
+
+ var boundCount = 2 * this.m_proxyCount;
+
+ var lowerValues = new Array();
+ var upperValues = new Array();
+ this.ComputeBounds(lowerValues, upperValues, aabb);
+
+ for (var axis = 0; axis < 2; ++axis)
+ {
+ var bounds = this.m_bounds[axis];
+ var lowerIndex = 0;
+ var upperIndex = 0;
+ var lowerIndexOut = [lowerIndex];
+ var upperIndexOut = [upperIndex];
+ this.Query(lowerIndexOut, upperIndexOut, lowerValues[axis], upperValues[axis], bounds, boundCount, axis);
+ lowerIndex = lowerIndexOut[0];
+ upperIndex = upperIndexOut[0];
+
+ // Replace memmove calls
+ //memmove(bounds + upperIndex + 2, bounds + upperIndex, (edgeCount - upperIndex) * sizeof(b2Bound));
+ var tArr = new Array();
+ var j = 0;
+ var tEnd = boundCount - upperIndex
+ var tBound1;
+ var tBound2;
+ // make temp array
+ for (j = 0; j < tEnd; j++){
+ tArr[j] = new b2Bound();
+ tBound1 = tArr[j];
+ tBound2 = bounds[upperIndex+j];
+ tBound1.value = tBound2.value;
+ tBound1.proxyId = tBound2.proxyId;
+ tBound1.stabbingCount = tBound2.stabbingCount;
+ }
+ // move temp array back in to bounds
+ tEnd = tArr.length;
+ var tIndex = upperIndex+2;
+ for (j = 0; j < tEnd; j++){
+ //bounds[tIndex+j] = tArr[j];
+ tBound2 = tArr[j];
+ tBound1 = bounds[tIndex+j]
+ tBound1.value = tBound2.value;
+ tBound1.proxyId = tBound2.proxyId;
+ tBound1.stabbingCount = tBound2.stabbingCount;
+ }
+ //memmove(bounds + lowerIndex + 1, bounds + lowerIndex, (upperIndex - lowerIndex) * sizeof(b2Bound));
+ // make temp array
+ tArr = new Array();
+ tEnd = upperIndex - lowerIndex;
+ for (j = 0; j < tEnd; j++){
+ tArr[j] = new b2Bound();
+ tBound1 = tArr[j];
+ tBound2 = bounds[lowerIndex+j];
+ tBound1.value = tBound2.value;
+ tBound1.proxyId = tBound2.proxyId;
+ tBound1.stabbingCount = tBound2.stabbingCount;
+ }
+ // move temp array back in to bounds
+ tEnd = tArr.length;
+ tIndex = lowerIndex+1;
+ for (j = 0; j < tEnd; j++){
+ //bounds[tIndex+j] = tArr[j];
+ tBound2 = tArr[j];
+ tBound1 = bounds[tIndex+j]
+ tBound1.value = tBound2.value;
+ tBound1.proxyId = tBound2.proxyId;
+ tBound1.stabbingCount = tBound2.stabbingCount;
+ }
+
+ // The upper index has increased because of the lower bound insertion.
+ ++upperIndex;
+
+ // Copy in the new bounds.
+ bounds[lowerIndex].value = lowerValues[axis];
+ bounds[lowerIndex].proxyId = proxyId;
+ bounds[upperIndex].value = upperValues[axis];
+ bounds[upperIndex].proxyId = proxyId;
+
+ bounds[lowerIndex].stabbingCount = lowerIndex == 0 ? 0 : bounds[lowerIndex-1].stabbingCount;
+ bounds[upperIndex].stabbingCount = bounds[upperIndex-1].stabbingCount;
+
+ // Adjust the stabbing count between the new bounds.
+ for (index = lowerIndex; index < upperIndex; ++index)
+ {
+ bounds[index].stabbingCount++;
+ }
+
+ // Adjust the all the affected bound indices.
+ for (index = lowerIndex; index < boundCount + 2; ++index)
+ {
+ var proxy2 = this.m_proxyPool[ bounds[index].proxyId ];
+ if (bounds[index].IsLower())
+ {
+ proxy2.lowerBounds[axis] = index;
+ }
+ else
+ {
+ proxy2.upperBounds[axis] = index;
+ }
+ }
+ }
+
+ ++this.m_proxyCount;
+
+ //b2Settings.b2Assert(this.m_queryResultCount < b2Settings.b2_maxProxies);
+
+ for (var i = 0; i < this.m_queryResultCount; ++i)
+ {
+ //b2Settings.b2Assert(this.m_queryResults[i] < b2_maxProxies);
+ //b2Settings.b2Assert(this.m_proxyPool[this.m_queryResults[i]].IsValid());
+
+ this.m_pairManager.AddBufferedPair(proxyId, this.m_queryResults[i]);
+ }
+
+ this.m_pairManager.Commit();
+
+ // Prepare for next query.
+ this.m_queryResultCount = 0;
+ this.IncrementTimeStamp();
+
+ return proxyId;
+ },
+
+ DestroyProxy: function(proxyId){
+
+ //b2Settings.b2Assert(0 < this.m_proxyCount && this.m_proxyCount <= b2_maxProxies);
+
+ var proxy = this.m_proxyPool[ proxyId ];
+ //b2Settings.b2Assert(proxy.IsValid());
+
+ var boundCount = 2 * this.m_proxyCount;
+
+ for (var axis = 0; axis < 2; ++axis)
+ {
+ var bounds = this.m_bounds[axis];
+
+ var lowerIndex = proxy.lowerBounds[axis];
+ var upperIndex = proxy.upperBounds[axis];
+ var lowerValue = bounds[lowerIndex].value;
+ var upperValue = bounds[upperIndex].value;
+
+ // replace memmove calls
+ //memmove(bounds + lowerIndex, bounds + lowerIndex + 1, (upperIndex - lowerIndex - 1) * sizeof(b2Bound));
+ var tArr = new Array();
+ var j = 0;
+ var tEnd = upperIndex - lowerIndex - 1;
+ var tBound1;
+ var tBound2;
+ // make temp array
+ for (j = 0; j < tEnd; j++){
+ tArr[j] = new b2Bound();
+ tBound1 = tArr[j];
+ tBound2 = bounds[lowerIndex+1+j];
+ tBound1.value = tBound2.value;
+ tBound1.proxyId = tBound2.proxyId;
+ tBound1.stabbingCount = tBound2.stabbingCount;
+ }
+ // move temp array back in to bounds
+ tEnd = tArr.length;
+ var tIndex = lowerIndex;
+ for (j = 0; j < tEnd; j++){
+ //bounds[tIndex+j] = tArr[j];
+ tBound2 = tArr[j];
+ tBound1 = bounds[tIndex+j]
+ tBound1.value = tBound2.value;
+ tBound1.proxyId = tBound2.proxyId;
+ tBound1.stabbingCount = tBound2.stabbingCount;
+ }
+ //memmove(bounds + upperIndex-1, bounds + upperIndex + 1, (edgeCount - upperIndex - 1) * sizeof(b2Bound));
+ // make temp array
+ tArr = new Array();
+ tEnd = boundCount - upperIndex - 1;
+ for (j = 0; j < tEnd; j++){
+ tArr[j] = new b2Bound();
+ tBound1 = tArr[j];
+ tBound2 = bounds[upperIndex+1+j];
+ tBound1.value = tBound2.value;
+ tBound1.proxyId = tBound2.proxyId;
+ tBound1.stabbingCount = tBound2.stabbingCount;
+ }
+ // move temp array back in to bounds
+ tEnd = tArr.length;
+ tIndex = upperIndex-1;
+ for (j = 0; j < tEnd; j++){
+ //bounds[tIndex+j] = tArr[j];
+ tBound2 = tArr[j];
+ tBound1 = bounds[tIndex+j]
+ tBound1.value = tBound2.value;
+ tBound1.proxyId = tBound2.proxyId;
+ tBound1.stabbingCount = tBound2.stabbingCount;
+ }
+
+ // Fix bound indices.
+ tEnd = boundCount - 2;
+ for (var index = lowerIndex; index < tEnd; ++index)
+ {
+ var proxy2 = this.m_proxyPool[ bounds[index].proxyId ];
+ if (bounds[index].IsLower())
+ {
+ proxy2.lowerBounds[axis] = index;
+ }
+ else
+ {
+ proxy2.upperBounds[axis] = index;
+ }
+ }
+
+ // Fix stabbing count.
+ tEnd = upperIndex - 1;
+ for (var index2 = lowerIndex; index2 < tEnd; ++index2)
+ {
+ bounds[index2].stabbingCount--;
+ }
+
+ // this.Query for pairs to be removed. lowerIndex and upperIndex are not needed.
+ // make lowerIndex and upper output using an array and do this for others if compiler doesn't pick them up
+ this.Query([0], [0], lowerValue, upperValue, bounds, boundCount - 2, axis);
+ }
+
+ //b2Settings.b2Assert(this.m_queryResultCount < b2Settings.b2_maxProxies);
+
+ for (var i = 0; i < this.m_queryResultCount; ++i)
+ {
+ //b2Settings.b2Assert(this.m_proxyPool[this.m_queryResults[i]].IsValid());
+
+ this.m_pairManager.RemoveBufferedPair(proxyId, this.m_queryResults[i]);
+ }
+
+ this.m_pairManager.Commit();
+
+ // Prepare for next query.
+ this.m_queryResultCount = 0;
+ this.IncrementTimeStamp();
+
+ // Return the proxy to the pool.
+ proxy.userData = null;
+ proxy.overlapCount = b2BroadPhase.b2_invalid;
+ proxy.lowerBounds[0] = b2BroadPhase.b2_invalid;
+ proxy.lowerBounds[1] = b2BroadPhase.b2_invalid;
+ proxy.upperBounds[0] = b2BroadPhase.b2_invalid;
+ proxy.upperBounds[1] = b2BroadPhase.b2_invalid;
+
+ proxy.SetNext(this.m_freeProxy);
+ this.m_freeProxy = proxyId;
+ --this.m_proxyCount;
+ },
+
+
+ // Call this.MoveProxy times like, then when you are done
+ // call this.Commit to finalized the proxy pairs (for your time step).
+ MoveProxy: function(proxyId, aabb){
+ var axis = 0;
+ var index = 0;
+ var bound;
+ var prevBound
+ var nextBound
+ var nextProxyId = 0;
+ var nextProxy;
+
+ if (proxyId == b2Pair.b2_nullProxy || b2Settings.b2_maxProxies <= proxyId)
+ {
+ //b2Settings.b2Assert(false);
+ return;
+ }
+
+ if (aabb.IsValid() == false)
+ {
+ //b2Settings.b2Assert(false);
+ return;
+ }
+
+ var boundCount = 2 * this.m_proxyCount;
+
+ var proxy = this.m_proxyPool[ proxyId ];
+ // Get new bound values
+ var newValues = new b2BoundValues();
+ this.ComputeBounds(newValues.lowerValues, newValues.upperValues, aabb);
+
+ // Get old bound values
+ var oldValues = new b2BoundValues();
+ for (axis = 0; axis < 2; ++axis)
+ {
+ oldValues.lowerValues[axis] = this.m_bounds[axis][proxy.lowerBounds[axis]].value;
+ oldValues.upperValues[axis] = this.m_bounds[axis][proxy.upperBounds[axis]].value;
+ }
+
+ for (axis = 0; axis < 2; ++axis)
+ {
+ var bounds = this.m_bounds[axis];
+
+ var lowerIndex = proxy.lowerBounds[axis];
+ var upperIndex = proxy.upperBounds[axis];
+
+ var lowerValue = newValues.lowerValues[axis];
+ var upperValue = newValues.upperValues[axis];
+
+ var deltaLower = lowerValue - bounds[lowerIndex].value;
+ var deltaUpper = upperValue - bounds[upperIndex].value;
+
+ bounds[lowerIndex].value = lowerValue;
+ bounds[upperIndex].value = upperValue;
+
+ //
+ // Expanding adds overlaps
+ //
+
+ // Should we move the lower bound down?
+ if (deltaLower < 0)
+ {
+ index = lowerIndex;
+ while (index > 0 && lowerValue < bounds[index-1].value)
+ {
+ bound = bounds[index];
+ prevBound = bounds[index - 1];
+
+ var prevProxyId = prevBound.proxyId;
+ var prevProxy = this.m_proxyPool[ prevBound.proxyId ];
+
+ prevBound.stabbingCount++;
+
+ if (prevBound.IsUpper() == true)
+ {
+ if (this.TestOverlap(newValues, prevProxy))
+ {
+ this.m_pairManager.AddBufferedPair(proxyId, prevProxyId);
+ }
+
+ prevProxy.upperBounds[axis]++;
+ bound.stabbingCount++;
+ }
+ else
+ {
+ prevProxy.lowerBounds[axis]++;
+ bound.stabbingCount--;
+ }
+
+ proxy.lowerBounds[axis]--;
+
+ // swap
+ //var temp = bound;
+ //bound = prevEdge;
+ //prevEdge = temp;
+ bound.Swap(prevBound);
+ //b2Math.b2Swap(bound, prevEdge);
+ --index;
+ }
+ }
+
+ // Should we move the upper bound up?
+ if (deltaUpper > 0)
+ {
+ index = upperIndex;
+ while (index < boundCount-1 && bounds[index+1].value <= upperValue)
+ {
+ bound = bounds[ index ];
+ nextBound = bounds[ index + 1 ];
+ nextProxyId = nextBound.proxyId;
+ nextProxy = this.m_proxyPool[ nextProxyId ];
+
+ nextBound.stabbingCount++;
+
+ if (nextBound.IsLower() == true)
+ {
+ if (this.TestOverlap(newValues, nextProxy))
+ {
+ this.m_pairManager.AddBufferedPair(proxyId, nextProxyId);
+ }
+
+ nextProxy.lowerBounds[axis]--;
+ bound.stabbingCount++;
+ }
+ else
+ {
+ nextProxy.upperBounds[axis]--;
+ bound.stabbingCount--;
+ }
+
+ proxy.upperBounds[axis]++;
+ // swap
+ //var temp = bound;
+ //bound = nextEdge;
+ //nextEdge = temp;
+ bound.Swap(nextBound);
+ //b2Math.b2Swap(bound, nextEdge);
+ index++;
+ }
+ }
+
+ //
+ // Shrinking removes overlaps
+ //
+
+ // Should we move the lower bound up?
+ if (deltaLower > 0)
+ {
+ index = lowerIndex;
+ while (index < boundCount-1 && bounds[index+1].value <= lowerValue)
+ {
+ bound = bounds[ index ];
+ nextBound = bounds[ index + 1 ];
+
+ nextProxyId = nextBound.proxyId;
+ nextProxy = this.m_proxyPool[ nextProxyId ];
+
+ nextBound.stabbingCount--;
+
+ if (nextBound.IsUpper())
+ {
+ if (this.TestOverlap(oldValues, nextProxy))
+ {
+ this.m_pairManager.RemoveBufferedPair(proxyId, nextProxyId);
+ }
+
+ nextProxy.upperBounds[axis]--;
+ bound.stabbingCount--;
+ }
+ else
+ {
+ nextProxy.lowerBounds[axis]--;
+ bound.stabbingCount++;
+ }
+
+ proxy.lowerBounds[axis]++;
+ // swap
+ //var temp = bound;
+ //bound = nextEdge;
+ //nextEdge = temp;
+ bound.Swap(nextBound);
+ //b2Math.b2Swap(bound, nextEdge);
+ index++;
+ }
+ }
+
+ // Should we move the upper bound down?
+ if (deltaUpper < 0)
+ {
+ index = upperIndex;
+ while (index > 0 && upperValue < bounds[index-1].value)
+ {
+ bound = bounds[index];
+ prevBound = bounds[index - 1];
+
+ prevProxyId = prevBound.proxyId;
+ prevProxy = this.m_proxyPool[ prevProxyId ];
+
+ prevBound.stabbingCount--;
+
+ if (prevBound.IsLower() == true)
+ {
+ if (this.TestOverlap(oldValues, prevProxy))
+ {
+ this.m_pairManager.RemoveBufferedPair(proxyId, prevProxyId);
+ }
+
+ prevProxy.lowerBounds[axis]++;
+ bound.stabbingCount--;
+ }
+ else
+ {
+ prevProxy.upperBounds[axis]++;
+ bound.stabbingCount++;
+ }
+
+ proxy.upperBounds[axis]--;
+ // swap
+ //var temp = bound;
+ //bound = prevEdge;
+ //prevEdge = temp;
+ bound.Swap(prevBound);
+ //b2Math.b2Swap(bound, prevEdge);
+ index--;
+ }
+ }
+ }
+ },
+
+ Commit: function(){
+ this.m_pairManager.Commit();
+ },
+
+ // this.Query an AABB for overlapping proxies, returns the user data and
+ // the count, up to the supplied maximum count.
+ QueryAABB: function(aabb, userData, maxCount){
+ var lowerValues = new Array();
+ var upperValues = new Array();
+ this.ComputeBounds(lowerValues, upperValues, aabb);
+
+ var lowerIndex = 0;
+ var upperIndex = 0;
+ var lowerIndexOut = [lowerIndex];
+ var upperIndexOut = [upperIndex];
+ this.Query(lowerIndexOut, upperIndexOut, lowerValues[0], upperValues[0], this.m_bounds[0], 2*this.m_proxyCount, 0);
+ this.Query(lowerIndexOut, upperIndexOut, lowerValues[1], upperValues[1], this.m_bounds[1], 2*this.m_proxyCount, 1);
+
+ //b2Settings.b2Assert(this.m_queryResultCount < b2Settings.b2_maxProxies);
+
+ var count = 0;
+ for (var i = 0; i < this.m_queryResultCount && count < maxCount; ++i, ++count)
+ {
+ //b2Settings.b2Assert(this.m_queryResults[i] < b2Settings.b2_maxProxies);
+ var proxy = this.m_proxyPool[ this.m_queryResults[i] ];
+ //b2Settings.b2Assert(proxy.IsValid());
+ userData[i] = proxy.userData;
+ }
+
+ // Prepare for next query.
+ this.m_queryResultCount = 0;
+ this.IncrementTimeStamp();
+
+ return count;
+ },
+
+ Validate: function(){
+ var pair;
+ var proxy1;
+ var proxy2;
+ var overlap;
+
+ for (var axis = 0; axis < 2; ++axis)
+ {
+ var bounds = this.m_bounds[axis];
+
+ var boundCount = 2 * this.m_proxyCount;
+ var stabbingCount = 0;
+
+ for (var i = 0; i < boundCount; ++i)
+ {
+ var bound = bounds[i];
+ //b2Settings.b2Assert(i == 0 || bounds[i-1].value <= bound->value);
+ //b2Settings.b2Assert(bound->proxyId != b2_nullProxy);
+ //b2Settings.b2Assert(this.m_proxyPool[bound->proxyId].IsValid());
+
+ if (bound.IsLower() == true)
+ {
+ //b2Settings.b2Assert(this.m_proxyPool[bound.proxyId].lowerBounds[axis] == i);
+ stabbingCount++;
+ }
+ else
+ {
+ //b2Settings.b2Assert(this.m_proxyPool[bound.proxyId].upperBounds[axis] == i);
+ stabbingCount--;
+ }
+
+ //b2Settings.b2Assert(bound.stabbingCount == stabbingCount);
+ }
+ }
+
+ },
+
+//private:
+ ComputeBounds: function(lowerValues, upperValues, aabb)
+ {
+ //b2Settings.b2Assert(aabb.maxVertex.x > aabb.minVertex.x);
+ //b2Settings.b2Assert(aabb.maxVertex.y > aabb.minVertex.y);
+
+ //var minVertex = b2Math.b2ClampV(aabb.minVertex, this.m_worldAABB.minVertex, this.m_worldAABB.maxVertex);
+ var minVertexX = aabb.minVertex.x;
+ var minVertexY = aabb.minVertex.y;
+ minVertexX = b2Math.b2Min(minVertexX, this.m_worldAABB.maxVertex.x);
+ minVertexY = b2Math.b2Min(minVertexY, this.m_worldAABB.maxVertex.y);
+ minVertexX = b2Math.b2Max(minVertexX, this.m_worldAABB.minVertex.x);
+ minVertexY = b2Math.b2Max(minVertexY, this.m_worldAABB.minVertex.y);
+
+ //var maxVertex = b2Math.b2ClampV(aabb.maxVertex, this.m_worldAABB.minVertex, this.m_worldAABB.maxVertex);
+ var maxVertexX = aabb.maxVertex.x;
+ var maxVertexY = aabb.maxVertex.y;
+ maxVertexX = b2Math.b2Min(maxVertexX, this.m_worldAABB.maxVertex.x);
+ maxVertexY = b2Math.b2Min(maxVertexY, this.m_worldAABB.maxVertex.y);
+ maxVertexX = b2Math.b2Max(maxVertexX, this.m_worldAABB.minVertex.x);
+ maxVertexY = b2Math.b2Max(maxVertexY, this.m_worldAABB.minVertex.y);
+
+ // Bump lower bounds downs and upper bounds up. This ensures correct sorting of
+ // lower/upper bounds that would have equal values.
+ // TODO_ERIN implement fast float to uint16 conversion.
+ lowerValues[0] = /*uint*/(this.m_quantizationFactor.x * (minVertexX - this.m_worldAABB.minVertex.x)) & (b2Settings.USHRT_MAX - 1);
+ upperValues[0] = (/*uint*/(this.m_quantizationFactor.x * (maxVertexX - this.m_worldAABB.minVertex.x))& 0x0000ffff) | 1;
+
+ lowerValues[1] = /*uint*/(this.m_quantizationFactor.y * (minVertexY - this.m_worldAABB.minVertex.y)) & (b2Settings.USHRT_MAX - 1);
+ upperValues[1] = (/*uint*/(this.m_quantizationFactor.y * (maxVertexY - this.m_worldAABB.minVertex.y))& 0x0000ffff) | 1;
+ },
+
+ // This one is only used for validation.
+ TestOverlapValidate: function(p1, p2){
+
+ for (var axis = 0; axis < 2; ++axis)
+ {
+ var bounds = this.m_bounds[axis];
+
+ //b2Settings.b2Assert(p1.lowerBounds[axis] < 2 * this.m_proxyCount);
+ //b2Settings.b2Assert(p1.upperBounds[axis] < 2 * this.m_proxyCount);
+ //b2Settings.b2Assert(p2.lowerBounds[axis] < 2 * this.m_proxyCount);
+ //b2Settings.b2Assert(p2.upperBounds[axis] < 2 * this.m_proxyCount);
+
+ if (bounds[p1.lowerBounds[axis]].value > bounds[p2.upperBounds[axis]].value)
+ return false;
+
+ if (bounds[p1.upperBounds[axis]].value < bounds[p2.lowerBounds[axis]].value)
+ return false;
+ }
+
+ return true;
+ },
+
+ TestOverlap: function(b, p)
+ {
+ for (var axis = 0; axis < 2; ++axis)
+ {
+ var bounds = this.m_bounds[axis];
+
+ //b2Settings.b2Assert(p.lowerBounds[axis] < 2 * this.m_proxyCount);
+ //b2Settings.b2Assert(p.upperBounds[axis] < 2 * this.m_proxyCount);
+
+ if (b.lowerValues[axis] > bounds[p.upperBounds[axis]].value)
+ return false;
+
+ if (b.upperValues[axis] < bounds[p.lowerBounds[axis]].value)
+ return false;
+ }
+
+ return true;
+ },
+
+ Query: function(lowerQueryOut, upperQueryOut, lowerValue, upperValue, bounds, boundCount, axis){
+
+ var lowerQuery = b2BroadPhase.BinarySearch(bounds, boundCount, lowerValue);
+ var upperQuery = b2BroadPhase.BinarySearch(bounds, boundCount, upperValue);
+
+ // Easy case: lowerQuery <= lowerIndex(i) < upperQuery
+ // Solution: search query range for min bounds.
+ for (var j = lowerQuery; j < upperQuery; ++j)
+ {
+ if (bounds[j].IsLower())
+ {
+ this.IncrementOverlapCount(bounds[j].proxyId);
+ }
+ }
+
+ // Hard case: lowerIndex(i) < lowerQuery < upperIndex(i)
+ // Solution: use the stabbing count to search down the bound array.
+ if (lowerQuery > 0)
+ {
+ var i = lowerQuery - 1;
+ var s = bounds[i].stabbingCount;
+
+ // Find the s overlaps.
+ while (s)
+ {
+ //b2Settings.b2Assert(i >= 0);
+
+ if (bounds[i].IsLower())
+ {
+ var proxy = this.m_proxyPool[ bounds[i].proxyId ];
+ if (lowerQuery <= proxy.upperBounds[axis])
+ {
+ this.IncrementOverlapCount(bounds[i].proxyId);
+ --s;
+ }
+ }
+ --i;
+ }
+ }
+
+ lowerQueryOut[0] = lowerQuery;
+ upperQueryOut[0] = upperQuery;
+ },
+
+
+ IncrementOverlapCount: function(proxyId){
+ var proxy = this.m_proxyPool[ proxyId ];
+ if (proxy.timeStamp < this.m_timeStamp)
+ {
+ proxy.timeStamp = this.m_timeStamp;
+ proxy.overlapCount = 1;
+ }
+ else
+ {
+ proxy.overlapCount = 2;
+ //b2Settings.b2Assert(this.m_queryResultCount < b2Settings.b2_maxProxies);
+ this.m_queryResults[this.m_queryResultCount] = proxyId;
+ ++this.m_queryResultCount;
+ }
+ },
+ IncrementTimeStamp: function(){
+ if (this.m_timeStamp == b2Settings.USHRT_MAX)
+ {
+ for (var i = 0; i < b2Settings.b2_maxProxies; ++i)
+ {
+ this.m_proxyPool[i].timeStamp = 0;
+ }
+ this.m_timeStamp = 1;
+ }
+ else
+ {
+ ++this.m_timeStamp;
+ }
+ },
+
+//public:
+ m_pairManager: new b2PairManager(),
+
+ m_proxyPool: new Array(b2Settings.b2_maxPairs),
+ m_freeProxy: 0,
+
+ m_bounds: new Array(2*b2Settings.b2_maxProxies),
+
+ m_queryResults: new Array(b2Settings.b2_maxProxies),
+ m_queryResultCount: 0,
+
+ m_worldAABB: null,
+ m_quantizationFactor: new b2Vec2(),
+ m_proxyCount: 0,
+ m_timeStamp: 0};
+b2BroadPhase.s_validate = false;
+b2BroadPhase.b2_invalid = b2Settings.USHRT_MAX;
+b2BroadPhase.b2_nullEdge = b2Settings.USHRT_MAX;
+b2BroadPhase.BinarySearch = function(bounds, count, value)
+ {
+ var low = 0;
+ var high = count - 1;
+ while (low <= high)
+ {
+ var mid = Math.floor((low + high) / 2);
+ if (bounds[mid].value > value)
+ {
+ high = mid - 1;
+ }
+ else if (bounds[mid].value < value)
+ {
+ low = mid + 1;
+ }
+ else
+ {
+ return /*uint*/(mid);
+ }
+ }
+
+ return /*uint*/(low);
+ };
diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2BufferedPair.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2BufferedPair.js
new file mode 100644
index 0000000..fe1419e
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2BufferedPair.js
@@ -0,0 +1,26 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+var b2BufferedPair = Class.create();
+b2BufferedPair.prototype = {
+ proxyId1: 0,
+ proxyId2: 0,
+
+ initialize: function() {}}
diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2Collision.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2Collision.js
new file mode 100644
index 0000000..31719e3
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2Collision.js
@@ -0,0 +1,738 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+var b2Collision = Class.create();
+b2Collision.prototype = {
+
+ // Null feature
+
+
+
+
+ // Find the separation between poly1 and poly2 for a give edge normal on poly1.
+
+
+
+
+ // Find the max separation between poly1 and poly2 using edge normals
+ // from poly1.
+
+
+
+
+
+
+
+ // Find edge normal of max separation on A - return if separating axis is found
+ // Find edge normal of max separation on B - return if separation axis is found
+ // Choose reference edge(minA, minB)
+ // Find incident edge
+ // Clip
+ // The normal points from 1 to 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ initialize: function() {}}
+b2Collision.b2_nullFeature = 0x000000ff;
+b2Collision.ClipSegmentToLine = function(vOut, vIn, normal, offset)
+ {
+ // Start with no output points
+ var numOut = 0;
+
+ var vIn0 = vIn[0].v;
+ var vIn1 = vIn[1].v;
+
+ // Calculate the distance of end points to the line
+ var distance0 = b2Math.b2Dot(normal, vIn[0].v) - offset;
+ var distance1 = b2Math.b2Dot(normal, vIn[1].v) - offset;
+
+ // If the points are behind the plane
+ if (distance0 <= 0.0) vOut[numOut++] = vIn[0];
+ if (distance1 <= 0.0) vOut[numOut++] = vIn[1];
+
+ // If the points are on different sides of the plane
+ if (distance0 * distance1 < 0.0)
+ {
+ // Find intersection point of edge and plane
+ var interp = distance0 / (distance0 - distance1);
+ // expanded for performance
+ var tVec = vOut[numOut].v;
+ tVec.x = vIn0.x + interp * (vIn1.x - vIn0.x);
+ tVec.y = vIn0.y + interp * (vIn1.y - vIn0.y);
+ if (distance0 > 0.0)
+ {
+ vOut[numOut].id = vIn[0].id;
+ }
+ else
+ {
+ vOut[numOut].id = vIn[1].id;
+ }
+ ++numOut;
+ }
+
+ return numOut;
+ };
+b2Collision.EdgeSeparation = function(poly1, edge1, poly2)
+ {
+ var vert1s = poly1.m_vertices;
+ var count2 = poly2.m_vertexCount;
+ var vert2s = poly2.m_vertices;
+
+ // Convert normal from into poly2's frame.
+ //b2Settings.b2Assert(edge1 < poly1.m_vertexCount);
+
+ //var normal = b2Math.b2MulMV(poly1.m_R, poly1->m_normals[edge1]);
+ var normalX = poly1.m_normals[edge1].x;
+ var normalY = poly1.m_normals[edge1].y;
+ var tX = normalX;
+ var tMat = poly1.m_R;
+ normalX = tMat.col1.x * tX + tMat.col2.x * normalY;
+ normalY = tMat.col1.y * tX + tMat.col2.y * normalY;
+ // ^^^^^^^ normal.MulM(poly1.m_R);
+
+ //var normalLocal2 = b2Math.b2MulTMV(poly2.m_R, normal);
+ var normalLocal2X = normalX;
+ var normalLocal2Y = normalY;
+ tMat = poly2.m_R;
+ tX = normalLocal2X * tMat.col1.x + normalLocal2Y * tMat.col1.y;
+ normalLocal2Y = normalLocal2X * tMat.col2.x + normalLocal2Y * tMat.col2.y;
+ normalLocal2X = tX;
+ // ^^^^^ normalLocal2.MulTM(poly2.m_R);
+
+ // Find support vertex on poly2 for -normal.
+ var vertexIndex2 = 0;
+ var minDot = Number.MAX_VALUE;
+ for (var i = 0; i < count2; ++i)
+ {
+ //var dot = b2Math.b2Dot(vert2s[i], normalLocal2);
+ var tVec = vert2s[i];
+ var dot = tVec.x * normalLocal2X + tVec.y * normalLocal2Y;
+ if (dot < minDot)
+ {
+ minDot = dot;
+ vertexIndex2 = i;
+ }
+ }
+
+ //b2Vec2 v1 = poly1->m_position + b2Mul(poly1->m_R, vert1s[edge1]);
+ tMat = poly1.m_R;
+ var v1X = poly1.m_position.x + (tMat.col1.x * vert1s[edge1].x + tMat.col2.x * vert1s[edge1].y)
+ var v1Y = poly1.m_position.y + (tMat.col1.y * vert1s[edge1].x + tMat.col2.y * vert1s[edge1].y)
+
+ //b2Vec2 v2 = poly2->m_position + b2Mul(poly2->m_R, vert2s[vertexIndex2]);
+ tMat = poly2.m_R;
+ var v2X = poly2.m_position.x + (tMat.col1.x * vert2s[vertexIndex2].x + tMat.col2.x * vert2s[vertexIndex2].y)
+ var v2Y = poly2.m_position.y + (tMat.col1.y * vert2s[vertexIndex2].x + tMat.col2.y * vert2s[vertexIndex2].y)
+
+ //var separation = b2Math.b2Dot( b2Math.SubtractVV( v2, v1 ) , normal);
+ v2X -= v1X;
+ v2Y -= v1Y;
+ //var separation = b2Math.b2Dot( v2 , normal);
+ var separation = v2X * normalX + v2Y * normalY;
+ return separation;
+ };
+b2Collision.FindMaxSeparation = function(edgeIndex /*int ptr*/, poly1, poly2, conservative)
+ {
+ var count1 = poly1.m_vertexCount;
+
+ // Vector pointing from the origin of poly1 to the origin of poly2.
+ //var d = b2Math.SubtractVV( poly2.m_position, poly1.m_position );
+ var dX = poly2.m_position.x - poly1.m_position.x;
+ var dY = poly2.m_position.y - poly1.m_position.y;
+
+ //var dLocal1 = b2Math.b2MulTMV(poly1.m_R, d);
+ var dLocal1X = (dX * poly1.m_R.col1.x + dY * poly1.m_R.col1.y);
+ var dLocal1Y = (dX * poly1.m_R.col2.x + dY * poly1.m_R.col2.y);
+
+ // Get support vertex hint for our search
+ var edge = 0;
+ var maxDot = -Number.MAX_VALUE;
+ for (var i = 0; i < count1; ++i)
+ {
+ //var dot = b2Math.b2Dot(poly.m_normals[i], dLocal1);
+ var dot = (poly1.m_normals[i].x * dLocal1X + poly1.m_normals[i].y * dLocal1Y);
+ if (dot > maxDot)
+ {
+ maxDot = dot;
+ edge = i;
+ }
+ }
+
+ // Get the separation for the edge normal.
+ var s = b2Collision.EdgeSeparation(poly1, edge, poly2);
+ if (s > 0.0 && conservative == false)
+ {
+ return s;
+ }
+
+ // Check the separation for the neighboring edges.
+ var prevEdge = edge - 1 >= 0 ? edge - 1 : count1 - 1;
+ var sPrev = b2Collision.EdgeSeparation(poly1, prevEdge, poly2);
+ if (sPrev > 0.0 && conservative == false)
+ {
+ return sPrev;
+ }
+
+ var nextEdge = edge + 1 < count1 ? edge + 1 : 0;
+ var sNext = b2Collision.EdgeSeparation(poly1, nextEdge, poly2);
+ if (sNext > 0.0 && conservative == false)
+ {
+ return sNext;
+ }
+
+ // Find the best edge and the search direction.
+ var bestEdge = 0;
+ var bestSeparation;
+ var increment = 0;
+ if (sPrev > s && sPrev > sNext)
+ {
+ increment = -1;
+ bestEdge = prevEdge;
+ bestSeparation = sPrev;
+ }
+ else if (sNext > s)
+ {
+ increment = 1;
+ bestEdge = nextEdge;
+ bestSeparation = sNext;
+ }
+ else
+ {
+ // pointer out
+ edgeIndex[0] = edge;
+ return s;
+ }
+
+ while (true)
+ {
+
+ if (increment == -1)
+ edge = bestEdge - 1 >= 0 ? bestEdge - 1 : count1 - 1;
+ else
+ edge = bestEdge + 1 < count1 ? bestEdge + 1 : 0;
+
+ s = b2Collision.EdgeSeparation(poly1, edge, poly2);
+ if (s > 0.0 && conservative == false)
+ {
+ return s;
+ }
+
+ if (s > bestSeparation)
+ {
+ bestEdge = edge;
+ bestSeparation = s;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ // pointer out
+ edgeIndex[0] = bestEdge;
+ return bestSeparation;
+ };
+b2Collision.FindIncidentEdge = function(c, poly1, edge1, poly2)
+ {
+ var count1 = poly1.m_vertexCount;
+ var vert1s = poly1.m_vertices;
+ var count2 = poly2.m_vertexCount;
+ var vert2s = poly2.m_vertices;
+
+ // Get the vertices associated with edge1.
+ var vertex11 = edge1;
+ var vertex12 = edge1 + 1 == count1 ? 0 : edge1 + 1;
+
+ // Get the normal of edge1.
+ var tVec = vert1s[vertex12];
+ //var normal1Local1 = b2Math.b2CrossVF( b2Math.SubtractVV( vert1s[vertex12], vert1s[vertex11] ), 1.0);
+ var normal1Local1X = tVec.x;
+ var normal1Local1Y = tVec.y;
+ tVec = vert1s[vertex11];
+ normal1Local1X -= tVec.x;
+ normal1Local1Y -= tVec.y;
+ var tX = normal1Local1X;
+ normal1Local1X = normal1Local1Y;
+ normal1Local1Y = -tX;
+ // ^^^^ normal1Local1.CrossVF(1.0);
+
+ var invLength = 1.0 / Math.sqrt(normal1Local1X*normal1Local1X + normal1Local1Y*normal1Local1Y);
+ normal1Local1X *= invLength;
+ normal1Local1Y *= invLength;
+ // ^^^^normal1Local1.Normalize();
+ //var normal1 = b2Math.b2MulMV(poly1.m_R, normal1Local1);
+ var normal1X = normal1Local1X;
+ var normal1Y = normal1Local1Y;
+
+ tX = normal1X;
+ var tMat = poly1.m_R;
+ normal1X = tMat.col1.x * tX + tMat.col2.x * normal1Y;
+ normal1Y = tMat.col1.y * tX + tMat.col2.y * normal1Y;
+ // ^^^^ normal1.MulM(poly1.m_R);
+
+ //var normal1Local2 = b2Math.b2MulTMV(poly2.m_R, normal1);
+ var normal1Local2X = normal1X;
+ var normal1Local2Y = normal1Y;
+ tMat = poly2.m_R;
+ tX = normal1Local2X * tMat.col1.x + normal1Local2Y * tMat.col1.y;
+ normal1Local2Y = normal1Local2X * tMat.col2.x + normal1Local2Y * tMat.col2.y;
+ normal1Local2X = tX;
+ // ^^^^ normal1Local2.MulTM(poly2.m_R);
+
+ // Find the incident edge on poly2.
+ var vertex21 = 0;
+ var vertex22 = 0;
+ var minDot = Number.MAX_VALUE;
+ for (var i = 0; i < count2; ++i)
+ {
+ var i1 = i;
+ var i2 = i + 1 < count2 ? i + 1 : 0;
+
+ //var normal2Local2 = b2Math.b2CrossVF( b2Math.SubtractVV( vert2s[i2], vert2s[i1] ), 1.0);
+ tVec = vert2s[i2];
+ var normal2Local2X = tVec.x;
+ var normal2Local2Y = tVec.y;
+ tVec = vert2s[i1];
+ normal2Local2X -= tVec.x;
+ normal2Local2Y -= tVec.y;
+ tX = normal2Local2X;
+ normal2Local2X = normal2Local2Y;
+ normal2Local2Y = -tX;
+ // ^^^^ normal2Local2.CrossVF(1.0);
+
+ invLength = 1.0 / Math.sqrt(normal2Local2X*normal2Local2X + normal2Local2Y*normal2Local2Y);
+ normal2Local2X *= invLength;
+ normal2Local2Y *= invLength;
+ // ^^^^ normal2Local2.Normalize();
+
+ //var dot = b2Math.b2Dot(normal2Local2, normal1Local2);
+ var dot = normal2Local2X * normal1Local2X + normal2Local2Y * normal1Local2Y;
+ if (dot < minDot)
+ {
+ minDot = dot;
+ vertex21 = i1;
+ vertex22 = i2;
+ }
+ }
+
+ var tClip;
+ // Build the clip vertices for the incident edge.
+ tClip = c[0];
+ //tClip.v = b2Math.AddVV(poly2.m_position, b2Math.b2MulMV(poly2.m_R, vert2s[vertex21]));
+ tVec = tClip.v;
+ tVec.SetV(vert2s[vertex21]);
+ tVec.MulM(poly2.m_R);
+ tVec.Add(poly2.m_position);
+
+ tClip.id.features.referenceFace = edge1;
+ tClip.id.features.incidentEdge = vertex21;
+ tClip.id.features.incidentVertex = vertex21;
+
+ tClip = c[1];
+ //tClip.v = b2Math.AddVV(poly2.m_position, b2Math.b2MulMV(poly2.m_R, vert2s[vertex22]));
+ tVec = tClip.v;
+ tVec.SetV(vert2s[vertex22]);
+ tVec.MulM(poly2.m_R);
+ tVec.Add(poly2.m_position);
+ tClip.id.features.referenceFace = edge1;
+ tClip.id.features.incidentEdge = vertex21;
+ tClip.id.features.incidentVertex = vertex22;
+ };
+b2Collision.b2CollidePolyTempVec = new b2Vec2();
+b2Collision.b2CollidePoly = function(manifold, polyA, polyB, conservative)
+ {
+ manifold.pointCount = 0;
+
+ var edgeA = 0;
+ var edgeAOut = [edgeA];
+ var separationA = b2Collision.FindMaxSeparation(edgeAOut, polyA, polyB, conservative);
+ edgeA = edgeAOut[0];
+ if (separationA > 0.0 && conservative == false)
+ return;
+
+ var edgeB = 0;
+ var edgeBOut = [edgeB];
+ var separationB = b2Collision.FindMaxSeparation(edgeBOut, polyB, polyA, conservative);
+ edgeB = edgeBOut[0];
+ if (separationB > 0.0 && conservative == false)
+ return;
+
+ var poly1;
+ var poly2;
+ var edge1 = 0;
+ var flip = 0;
+ var k_relativeTol = 0.98;
+ var k_absoluteTol = 0.001;
+
+ // TODO_ERIN use "radius" of poly for absolute tolerance.
+ if (separationB > k_relativeTol * separationA + k_absoluteTol)
+ {
+ poly1 = polyB;
+ poly2 = polyA;
+ edge1 = edgeB;
+ flip = 1;
+ }
+ else
+ {
+ poly1 = polyA;
+ poly2 = polyB;
+ edge1 = edgeA;
+ flip = 0;
+ }
+
+ var incidentEdge = [new ClipVertex(), new ClipVertex()];
+ b2Collision.FindIncidentEdge(incidentEdge, poly1, edge1, poly2);
+
+ var count1 = poly1.m_vertexCount;
+ var vert1s = poly1.m_vertices;
+
+ var v11 = vert1s[edge1];
+ var v12 = edge1 + 1 < count1 ? vert1s[edge1+1] : vert1s[0];
+
+ //var dv = b2Math.SubtractVV(v12, v11);
+ var dvX = v12.x - v11.x;
+ var dvY = v12.y - v11.y;
+
+ //var sideNormal = b2Math.b2MulMV(poly1.m_R, b2Math.SubtractVV(v12, v11));
+ var sideNormalX = v12.x - v11.x;
+ var sideNormalY = v12.y - v11.y;
+
+ var tX = sideNormalX;
+ var tMat = poly1.m_R;
+ sideNormalX = tMat.col1.x * tX + tMat.col2.x * sideNormalY;
+ sideNormalY = tMat.col1.y * tX + tMat.col2.y * sideNormalY;
+ // ^^^^ sideNormal.MulM(poly1.m_R);
+
+ var invLength = 1.0 / Math.sqrt(sideNormalX*sideNormalX + sideNormalY*sideNormalY);
+ sideNormalX *= invLength;
+ sideNormalY *= invLength;
+ // ^^^^ sideNormal.Normalize();
+
+ //var frontNormal = b2Math.b2CrossVF(sideNormal, 1.0);
+ var frontNormalX = sideNormalX;
+ var frontNormalY = sideNormalY;
+ tX = frontNormalX;
+ frontNormalX = frontNormalY;
+ frontNormalY = -tX;
+ // ^^^^ frontNormal.CrossVF(1.0);
+
+ // Expanded for performance
+ //v11 = b2Math.AddVV(poly1.m_position, b2Math.b2MulMV(poly1.m_R, v11));
+ var v11X = v11.x;
+ var v11Y = v11.y;
+ tX = v11X;
+ tMat = poly1.m_R;
+ v11X = tMat.col1.x * tX + tMat.col2.x * v11Y;
+ v11Y = tMat.col1.y * tX + tMat.col2.y * v11Y;
+ // ^^^^ v11.MulM(poly1.m_R);
+ v11X += poly1.m_position.x;
+ v11Y += poly1.m_position.y;
+ //v12 = b2Math.AddVV(poly1.m_position, b2Math.b2MulMV(poly1.m_R, v12));
+ var v12X = v12.x;
+ var v12Y = v12.y;
+ tX = v12X;
+ tMat = poly1.m_R;
+ v12X = tMat.col1.x * tX + tMat.col2.x * v12Y;
+ v12Y = tMat.col1.y * tX + tMat.col2.y * v12Y;
+ // ^^^^ v12.MulM(poly1.m_R);
+ v12X += poly1.m_position.x;
+ v12Y += poly1.m_position.y;
+
+ //var frontOffset = b2Math.b2Dot(frontNormal, v11);
+ var frontOffset = frontNormalX * v11X + frontNormalY * v11Y;
+ //var sideOffset1 = -b2Math.b2Dot(sideNormal, v11);
+ var sideOffset1 = -(sideNormalX * v11X + sideNormalY * v11Y);
+ //var sideOffset2 = b2Math.b2Dot(sideNormal, v12);
+ var sideOffset2 = sideNormalX * v12X + sideNormalY * v12Y;
+
+ // Clip incident edge against extruded edge1 side edges.
+ var clipPoints1 = [new ClipVertex(), new ClipVertex()];
+ var clipPoints2 = [new ClipVertex(), new ClipVertex()];
+
+ var np = 0;
+
+ // Clip to box side 1
+ b2Collision.b2CollidePolyTempVec.Set(-sideNormalX, -sideNormalY);
+ np = b2Collision.ClipSegmentToLine(clipPoints1, incidentEdge, b2Collision.b2CollidePolyTempVec, sideOffset1);
+
+ if (np < 2)
+ return;
+
+ // Clip to negative box side 1
+ b2Collision.b2CollidePolyTempVec.Set(sideNormalX, sideNormalY);
+ np = b2Collision.ClipSegmentToLine(clipPoints2, clipPoints1, b2Collision.b2CollidePolyTempVec, sideOffset2);
+
+ if (np < 2)
+ return;
+
+ // Now clipPoints2 contains the clipped points.
+ if (flip){
+ manifold.normal.Set(-frontNormalX, -frontNormalY);
+ }
+ else{
+ manifold.normal.Set(frontNormalX, frontNormalY);
+ }
+ // ^^^^ manifold.normal = flip ? frontNormal.Negative() : frontNormal;
+
+ var pointCount = 0;
+ for (var i = 0; i < b2Settings.b2_maxManifoldPoints; ++i)
+ {
+ //var separation = b2Math.b2Dot(frontNormal, clipPoints2[i].v) - frontOffset;
+ var tVec = clipPoints2[i].v;
+ var separation = (frontNormalX * tVec.x + frontNormalY * tVec.y) - frontOffset;
+
+ if (separation <= 0.0 || conservative == true)
+ {
+ var cp = manifold.points[ pointCount ];
+ cp.separation = separation;
+ cp.position.SetV( clipPoints2[i].v );
+ cp.id.Set( clipPoints2[i].id );
+ cp.id.features.flip = flip;
+ ++pointCount;
+ }
+ }
+
+ manifold.pointCount = pointCount;
+ };
+b2Collision.b2CollideCircle = function(manifold, circle1, circle2, conservative)
+ {
+ manifold.pointCount = 0;
+
+ //var d = b2Math.SubtractVV(circle2.m_position, circle1.m_position);
+ var dX = circle2.m_position.x - circle1.m_position.x;
+ var dY = circle2.m_position.y - circle1.m_position.y;
+ //var distSqr = b2Math.b2Dot(d, d);
+ var distSqr = dX * dX + dY * dY;
+ var radiusSum = circle1.m_radius + circle2.m_radius;
+ if (distSqr > radiusSum * radiusSum && conservative == false)
+ {
+ return;
+ }
+
+ var separation;
+ if (distSqr < Number.MIN_VALUE)
+ {
+ separation = -radiusSum;
+ manifold.normal.Set(0.0, 1.0);
+ }
+ else
+ {
+ var dist = Math.sqrt(distSqr);
+ separation = dist - radiusSum;
+ var a = 1.0 / dist;
+ manifold.normal.x = a * dX;
+ manifold.normal.y = a * dY;
+ }
+
+ manifold.pointCount = 1;
+ var tPoint = manifold.points[0];
+ tPoint.id.set_key(0);
+ tPoint.separation = separation;
+ //tPoint.position = b2Math.SubtractVV(circle2.m_position, b2Math.MulFV(circle2.m_radius, manifold.normal));
+ tPoint.position.x = circle2.m_position.x - (circle2.m_radius * manifold.normal.x);
+ tPoint.position.y = circle2.m_position.y - (circle2.m_radius * manifold.normal.y);
+ };
+b2Collision.b2CollidePolyAndCircle = function(manifold, poly, circle, conservative)
+ {
+ manifold.pointCount = 0;
+ var tPoint;
+
+ var dX;
+ var dY;
+
+ // Compute circle position in the frame of the polygon.
+ //var xLocal = b2Math.b2MulTMV(poly.m_R, b2Math.SubtractVV(circle.m_position, poly.m_position));
+ var xLocalX = circle.m_position.x - poly.m_position.x;
+ var xLocalY = circle.m_position.y - poly.m_position.y;
+ var tMat = poly.m_R;
+ var tX = xLocalX * tMat.col1.x + xLocalY * tMat.col1.y;
+ xLocalY = xLocalX * tMat.col2.x + xLocalY * tMat.col2.y;
+ xLocalX = tX;
+
+ var dist;
+
+ // Find the min separating edge.
+ var normalIndex = 0;
+ var separation = -Number.MAX_VALUE;
+ var radius = circle.m_radius;
+ for (var i = 0; i < poly.m_vertexCount; ++i)
+ {
+ //var s = b2Math.b2Dot(poly.m_normals[i], b2Math.SubtractVV(xLocal, poly.m_vertices[i]));
+ var s = poly.m_normals[i].x * (xLocalX-poly.m_vertices[i].x) + poly.m_normals[i].y * (xLocalY-poly.m_vertices[i].y);
+ if (s > radius)
+ {
+ // Early out.
+ return;
+ }
+
+ if (s > separation)
+ {
+ separation = s;
+ normalIndex = i;
+ }
+ }
+
+ // If the center is inside the polygon ...
+ if (separation < Number.MIN_VALUE)
+ {
+ manifold.pointCount = 1;
+ //manifold.normal = b2Math.b2MulMV(poly.m_R, poly.m_normals[normalIndex]);
+ var tVec = poly.m_normals[normalIndex];
+ manifold.normal.x = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y;
+ manifold.normal.y = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y;
+
+ tPoint = manifold.points[0];
+ tPoint.id.features.incidentEdge = normalIndex;
+ tPoint.id.features.incidentVertex = b2Collision.b2_nullFeature;
+ tPoint.id.features.referenceFace = b2Collision.b2_nullFeature;
+ tPoint.id.features.flip = 0;
+ tPoint.position.x = circle.m_position.x - radius * manifold.normal.x;
+ tPoint.position.y = circle.m_position.y - radius * manifold.normal.y;
+ //tPoint.position = b2Math.SubtractVV(circle.m_position , b2Math.MulFV(radius , manifold.normal));
+ tPoint.separation = separation - radius;
+ return;
+ }
+
+ // Project the circle center onto the edge segment.
+ var vertIndex1 = normalIndex;
+ var vertIndex2 = vertIndex1 + 1 < poly.m_vertexCount ? vertIndex1 + 1 : 0;
+ //var e = b2Math.SubtractVV(poly.m_vertices[vertIndex2] , poly.m_vertices[vertIndex1]);
+ var eX = poly.m_vertices[vertIndex2].x - poly.m_vertices[vertIndex1].x;
+ var eY = poly.m_vertices[vertIndex2].y - poly.m_vertices[vertIndex1].y;
+ //var length = e.Normalize();
+ var length = Math.sqrt(eX*eX + eY*eY);
+ eX /= length;
+ eY /= length;
+
+ // If the edge length is zero ...
+ if (length < Number.MIN_VALUE)
+ {
+ //d = b2Math.SubtractVV(xLocal , poly.m_vertices[vertIndex1]);
+ dX = xLocalX - poly.m_vertices[vertIndex1].x;
+ dY = xLocalY - poly.m_vertices[vertIndex1].y;
+ //dist = d.Normalize();
+ dist = Math.sqrt(dX*dX + dY*dY);
+ dX /= dist;
+ dY /= dist;
+ if (dist > radius)
+ {
+ return;
+ }
+
+ manifold.pointCount = 1;
+ //manifold.normal = b2Math.b2MulMV(poly.m_R, d);
+ manifold.normal.Set(tMat.col1.x * dX + tMat.col2.x * dY, tMat.col1.y * dX + tMat.col2.y * dY);
+ tPoint = manifold.points[0];
+ tPoint.id.features.incidentEdge = b2Collision.b2_nullFeature;
+ tPoint.id.features.incidentVertex = vertIndex1;
+ tPoint.id.features.referenceFace = b2Collision.b2_nullFeature;
+ tPoint.id.features.flip = 0;
+ //tPoint.position = b2Math.SubtractVV(circle.m_position , b2Math.MulFV(radius , manifold.normal));
+ tPoint.position.x = circle.m_position.x - radius * manifold.normal.x;
+ tPoint.position.y = circle.m_position.y - radius * manifold.normal.y;
+ tPoint.separation = dist - radius;
+ return;
+ }
+
+ // Project the center onto the edge.
+ //var u = b2Math.b2Dot(b2Math.SubtractVV(xLocal , poly.m_vertices[vertIndex1]) , e);
+ var u = (xLocalX-poly.m_vertices[vertIndex1].x) * eX + (xLocalY-poly.m_vertices[vertIndex1].y) * eY;
+
+ tPoint = manifold.points[0];
+ tPoint.id.features.incidentEdge = b2Collision.b2_nullFeature;
+ tPoint.id.features.incidentVertex = b2Collision.b2_nullFeature;
+ tPoint.id.features.referenceFace = b2Collision.b2_nullFeature;
+ tPoint.id.features.flip = 0;
+
+ var pX, pY;
+ if (u <= 0.0)
+ {
+ pX = poly.m_vertices[vertIndex1].x;
+ pY = poly.m_vertices[vertIndex1].y;
+ tPoint.id.features.incidentVertex = vertIndex1;
+ }
+ else if (u >= length)
+ {
+ pX = poly.m_vertices[vertIndex2].x;
+ pY = poly.m_vertices[vertIndex2].y;
+ tPoint.id.features.incidentVertex = vertIndex2;
+ }
+ else
+ {
+ //p = b2Math.AddVV(poly.m_vertices[vertIndex1] , b2Math.MulFV(u, e));
+ pX = eX * u + poly.m_vertices[vertIndex1].x;
+ pY = eY * u + poly.m_vertices[vertIndex1].y;
+ tPoint.id.features.incidentEdge = vertIndex1;
+ }
+
+ //d = b2Math.SubtractVV(xLocal , p);
+ dX = xLocalX - pX;
+ dY = xLocalY - pY;
+ //dist = d.Normalize();
+ dist = Math.sqrt(dX*dX + dY*dY);
+ dX /= dist;
+ dY /= dist;
+ if (dist > radius)
+ {
+ return;
+ }
+
+ manifold.pointCount = 1;
+ //manifold.normal = b2Math.b2MulMV(poly.m_R, d);
+ manifold.normal.Set(tMat.col1.x * dX + tMat.col2.x * dY, tMat.col1.y * dX + tMat.col2.y * dY);
+ //tPoint.position = b2Math.SubtractVV(circle.m_position , b2Math.MulFV(radius , manifold.normal));
+ tPoint.position.x = circle.m_position.x - radius * manifold.normal.x;
+ tPoint.position.y = circle.m_position.y - radius * manifold.normal.y;
+ tPoint.separation = dist - radius;
+ };
+b2Collision.b2TestOverlap = function(a, b)
+ {
+ var t1 = b.minVertex;
+ var t2 = a.maxVertex;
+ //d1 = b2Math.SubtractVV(b.minVertex, a.maxVertex);
+ var d1X = t1.x - t2.x;
+ var d1Y = t1.y - t2.y;
+ //d2 = b2Math.SubtractVV(a.minVertex, b.maxVertex);
+ t1 = a.minVertex;
+ t2 = b.maxVertex;
+ var d2X = t1.x - t2.x;
+ var d2Y = t1.y - t2.y;
+
+ if (d1X > 0.0 || d1Y > 0.0)
+ return false;
+
+ if (d2X > 0.0 || d2Y > 0.0)
+ return false;
+
+ return true;
+ };
diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2ContactID.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2ContactID.js
new file mode 100644
index 0000000..f0962e9
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2ContactID.js
@@ -0,0 +1,52 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+// We use contact ids to facilitate warm starting.
+var b2ContactID = Class.create();
+b2ContactID.prototype =
+{
+ initialize: function(){
+ // initialize instance variables for references
+ this.features = new Features();
+ //
+
+ this.features._m_id = this;
+
+ },
+ Set: function(id){
+ this.set_key(id._key);
+ },
+ Copy: function(){
+ var id = new b2ContactID();
+ id.set_key(this._key);
+ return id;
+ },
+ get_key: function(){
+ return this._key;
+ },
+ set_key: function(value) {
+ this._key = value;
+ this.features._referenceFace = this._key & 0x000000ff;
+ this.features._incidentEdge = ((this._key & 0x0000ff00) >> 8) & 0x000000ff;
+ this.features._incidentVertex = ((this._key & 0x00ff0000) >> 16) & 0x000000ff;
+ this.features._flip = ((this._key & 0xff000000) >> 24) & 0x000000ff;
+ },
+ features: new Features(),
+ _key: 0};
diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2ContactPoint.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2ContactPoint.js
new file mode 100644
index 0000000..2ba6359
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2ContactPoint.js
@@ -0,0 +1,35 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+// We use contact ids to facilitate warm starting.
+var b2ContactPoint = Class.create();
+b2ContactPoint.prototype =
+{
+ position: new b2Vec2(),
+ separation: null,
+ normalImpulse: null,
+ tangentImpulse: null,
+ id: new b2ContactID(),
+ initialize: function() {
+ // initialize instance variables for references
+ this.position = new b2Vec2();
+ this.id = new b2ContactID();
+ //
+}};
diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2Distance.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2Distance.js
new file mode 100644
index 0000000..66bd84a
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2Distance.js
@@ -0,0 +1,333 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+var b2Distance = Class.create();
+b2Distance.prototype =
+{
+
+ // GJK using Voronoi regions (Christer Ericson) and region selection
+ // optimizations (Casey Muratori).
+
+ // The origin is either in the region of points[1] or in the edge region. The origin is
+ // not in region of points[0] because that is the old point.
+
+ // Possible regions:
+ // - points[2]
+ // - edge points[0]-points[2]
+ // - edge points[1]-points[2]
+ // - inside the triangle
+
+
+
+
+
+
+ initialize: function() {}};
+b2Distance.ProcessTwo = function(p1Out, p2Out, p1s, p2s, points)
+ {
+ // If in point[1] region
+ //b2Vec2 r = -points[1];
+ var rX = -points[1].x;
+ var rY = -points[1].y;
+ //b2Vec2 d = points[1] - points[0];
+ var dX = points[0].x - points[1].x;
+ var dY = points[0].y - points[1].y;
+ //float32 length = d.Normalize();
+ var length = Math.sqrt(dX*dX + dY*dY);
+ dX /= length;
+ dY /= length;
+
+ //float32 lambda = b2Dot(r, d);
+ var lambda = rX * dX + rY * dY;
+ if (lambda <= 0.0 || length < Number.MIN_VALUE)
+ {
+ // The simplex is reduced to a point.
+ //*p1Out = p1s[1];
+ p1Out.SetV(p1s[1]);
+ //*p2Out = p2s[1];
+ p2Out.SetV(p2s[1]);
+ //p1s[0] = p1s[1];
+ p1s[0].SetV(p1s[1]);
+ //p2s[0] = p2s[1];
+ p2s[0].SetV(p2s[1]);
+ points[0].SetV(points[1]);
+ return 1;
+ }
+
+ // Else in edge region
+ lambda /= length;
+ //*p1Out = p1s[1] + lambda * (p1s[0] - p1s[1]);
+ p1Out.x = p1s[1].x + lambda * (p1s[0].x - p1s[1].x);
+ p1Out.y = p1s[1].y + lambda * (p1s[0].y - p1s[1].y);
+ //*p2Out = p2s[1] + lambda * (p2s[0] - p2s[1]);
+ p2Out.x = p2s[1].x + lambda * (p2s[0].x - p2s[1].x);
+ p2Out.y = p2s[1].y + lambda * (p2s[0].y - p2s[1].y);
+ return 2;
+ };
+b2Distance.ProcessThree = function(p1Out, p2Out, p1s, p2s, points)
+ {
+ //b2Vec2 a = points[0];
+ var aX = points[0].x;
+ var aY = points[0].y;
+ //b2Vec2 b = points[1];
+ var bX = points[1].x;
+ var bY = points[1].y;
+ //b2Vec2 c = points[2];
+ var cX = points[2].x;
+ var cY = points[2].y;
+
+ //b2Vec2 ab = b - a;
+ var abX = bX - aX;
+ var abY = bY - aY;
+ //b2Vec2 ac = c - a;
+ var acX = cX - aX;
+ var acY = cY - aY;
+ //b2Vec2 bc = c - b;
+ var bcX = cX - bX;
+ var bcY = cY - bY;
+
+ //float32 sn = -b2Dot(a, ab), sd = b2Dot(b, ab);
+ var sn = -(aX * abX + aY * abY);
+ var sd = (bX * abX + bY * abY);
+ //float32 tn = -b2Dot(a, ac), td = b2Dot(c, ac);
+ var tn = -(aX * acX + aY * acY);
+ var td = (cX * acX + cY * acY);
+ //float32 un = -b2Dot(b, bc), ud = b2Dot(c, bc);
+ var un = -(bX * bcX + bY * bcY);
+ var ud = (cX * bcX + cY * bcY);
+
+ // In vertex c region?
+ if (td <= 0.0 && ud <= 0.0)
+ {
+ // Single point
+ //*p1Out = p1s[2];
+ p1Out.SetV(p1s[2]);
+ //*p2Out = p2s[2];
+ p2Out.SetV(p2s[2]);
+ //p1s[0] = p1s[2];
+ p1s[0].SetV(p1s[2]);
+ //p2s[0] = p2s[2];
+ p2s[0].SetV(p2s[2]);
+ points[0].SetV(points[2]);
+ return 1;
+ }
+
+ // Should not be in vertex a or b region.
+ //b2Settings.b2Assert(sn > 0.0 || tn > 0.0);
+ //b2Settings.b2Assert(sd > 0.0 || un > 0.0);
+
+ //float32 n = b2Cross(ab, ac);
+ var n = abX * acY - abY * acX;
+
+ // Should not be in edge ab region.
+ //float32 vc = n * b2Cross(a, b);
+ var vc = n * (aX * bY - aY * bX);
+ //b2Settings.b2Assert(vc > 0.0 || sn > 0.0 || sd > 0.0);
+
+ // In edge bc region?
+ //float32 va = n * b2Cross(b, c);
+ var va = n * (bX * cY - bY * cX);
+ if (va <= 0.0 && un >= 0.0 && ud >= 0.0)
+ {
+ //b2Settings.b2Assert(un + ud > 0.0);
+
+ //float32 lambda = un / (un + ud);
+ var lambda = un / (un + ud);
+ //*p1Out = p1s[1] + lambda * (p1s[2] - p1s[1]);
+ p1Out.x = p1s[1].x + lambda * (p1s[2].x - p1s[1].x);
+ p1Out.y = p1s[1].y + lambda * (p1s[2].y - p1s[1].y);
+ //*p2Out = p2s[1] + lambda * (p2s[2] - p2s[1]);
+ p2Out.x = p2s[1].x + lambda * (p2s[2].x - p2s[1].x);
+ p2Out.y = p2s[1].y + lambda * (p2s[2].y - p2s[1].y);
+ //p1s[0] = p1s[2];
+ p1s[0].SetV(p1s[2]);
+ //p2s[0] = p2s[2];
+ p2s[0].SetV(p2s[2]);
+ //points[0] = points[2];
+ points[0].SetV(points[2]);
+ return 2;
+ }
+
+ // In edge ac region?
+ //float32 vb = n * b2Cross(c, a);
+ var vb = n * (cX * aY - cY * aX);
+ if (vb <= 0.0 && tn >= 0.0 && td >= 0.0)
+ {
+ //b2Settings.b2Assert(tn + td > 0.0);
+
+ //float32 lambda = tn / (tn + td);
+ var lambda = tn / (tn + td);
+ //*p1Out = p1s[0] + lambda * (p1s[2] - p1s[0]);
+ p1Out.x = p1s[0].x + lambda * (p1s[2].x - p1s[0].x);
+ p1Out.y = p1s[0].y + lambda * (p1s[2].y - p1s[0].y);
+ //*p2Out = p2s[0] + lambda * (p2s[2] - p2s[0]);
+ p2Out.x = p2s[0].x + lambda * (p2s[2].x - p2s[0].x);
+ p2Out.y = p2s[0].y + lambda * (p2s[2].y - p2s[0].y);
+ //p1s[1] = p1s[2];
+ p1s[1].SetV(p1s[2]);
+ //p2s[1] = p2s[2];
+ p2s[1].SetV(p2s[2]);
+ //points[1] = points[2];
+ points[1].SetV(points[2]);
+ return 2;
+ }
+
+ // Inside the triangle, compute barycentric coordinates
+ //float32 denom = va + vb + vc;
+ var denom = va + vb + vc;
+ //b2Settings.b2Assert(denom > 0.0);
+ denom = 1.0 / denom;
+ //float32 u = va * denom;
+ var u = va * denom;
+ //float32 v = vb * denom;
+ var v = vb * denom;
+ //float32 w = 1.0f - u - v;
+ var w = 1.0 - u - v;
+ //*p1Out = u * p1s[0] + v * p1s[1] + w * p1s[2];
+ p1Out.x = u * p1s[0].x + v * p1s[1].x + w * p1s[2].x;
+ p1Out.y = u * p1s[0].y + v * p1s[1].y + w * p1s[2].y;
+ //*p2Out = u * p2s[0] + v * p2s[1] + w * p2s[2];
+ p2Out.x = u * p2s[0].x + v * p2s[1].x + w * p2s[2].x;
+ p2Out.y = u * p2s[0].y + v * p2s[1].y + w * p2s[2].y;
+ return 3;
+ };
+b2Distance.InPoinsts = function(w, points, pointCount)
+ {
+ for (var i = 0; i < pointCount; ++i)
+ {
+ if (w.x == points[i].x && w.y == points[i].y)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ };
+b2Distance.Distance = function(p1Out, p2Out, shape1, shape2)
+ {
+ //b2Vec2 p1s[3], p2s[3];
+ var p1s = new Array(3);
+ var p2s = new Array(3);
+ //b2Vec2 points[3];
+ var points = new Array(3);
+ //int32 pointCount = 0;
+ var pointCount = 0;
+
+ //*p1Out = shape1->m_position;
+ p1Out.SetV(shape1.m_position);
+ //*p2Out = shape2->m_position;
+ p2Out.SetV(shape2.m_position);
+
+ var vSqr = 0.0;
+ var maxIterations = 20;
+ for (var iter = 0; iter < maxIterations; ++iter)
+ {
+ //b2Vec2 v = *p2Out - *p1Out;
+ var vX = p2Out.x - p1Out.x;
+ var vY = p2Out.y - p1Out.y;
+ //b2Vec2 w1 = shape1->Support(v);
+ var w1 = shape1.Support(vX, vY);
+ //b2Vec2 w2 = shape2->Support(-v);
+ var w2 = shape2.Support(-vX, -vY);
+ //float32 vSqr = b2Dot(v, v);
+ vSqr = (vX*vX + vY*vY);
+ //b2Vec2 w = w2 - w1;
+ var wX = w2.x - w1.x;
+ var wY = w2.y - w1.y;
+ //float32 vw = b2Dot(v, w);
+ var vw = (vX*wX + vY*wY);
+ //if (vSqr - b2Dot(v, w) <= 0.01f * vSqr)
+ if (vSqr - b2Dot(vX * wX + vY * wY) <= 0.01 * vSqr)
+ {
+ if (pointCount == 0)
+ {
+ //*p1Out = w1;
+ p1Out.SetV(w1);
+ //*p2Out = w2;
+ p2Out.SetV(w2);
+ }
+ b2Distance.g_GJK_Iterations = iter;
+ return Math.sqrt(vSqr);
+ }
+
+ switch (pointCount)
+ {
+ case 0:
+ //p1s[0] = w1;
+ p1s[0].SetV(w1);
+ //p2s[0] = w2;
+ p2s[0].SetV(w2);
+ points[0] = w;
+ //*p1Out = p1s[0];
+ p1Out.SetV(p1s[0]);
+ //*p2Out = p2s[0];
+ p2Out.SetV(p2s[0]);
+ ++pointCount;
+ break;
+
+ case 1:
+ //p1s[1] = w1;
+ p1s[1].SetV(w1);
+ //p2s[1] = w2;
+ p2s[1].SetV(w2);
+ //points[1] = w;
+ points[1].x = wX;
+ points[1].y = wY;
+ pointCount = b2Distance.ProcessTwo(p1Out, p2Out, p1s, p2s, points);
+ break;
+
+ case 2:
+ //p1s[2] = w1;
+ p1s[2].SetV(w1);
+ //p2s[2] = w2;
+ p2s[2].SetV(w2);
+ //points[2] = w;
+ points[2].x = wX;
+ points[2].y = wY;
+ pointCount = b2Distance.ProcessThree(p1Out, p2Out, p1s, p2s, points);
+ break;
+ }
+
+ // If we have three points, then the origin is in the corresponding triangle.
+ if (pointCount == 3)
+ {
+ b2Distance.g_GJK_Iterations = iter;
+ return 0.0;
+ }
+
+ //float32 maxSqr = -FLT_MAX;
+ var maxSqr = -Number.MAX_VALUE;
+ for (var i = 0; i < pointCount; ++i)
+ {
+ //maxSqr = b2Math.b2Max(maxSqr, b2Dot(points[i], points[i]));
+ maxSqr = b2Math.b2Max(maxSqr, (points[i].x*points[i].x + points[i].y*points[i].y));
+ }
+
+ if (pointCount == 3 || vSqr <= 100.0 * Number.MIN_VALUE * maxSqr)
+ {
+ b2Distance.g_GJK_Iterations = iter;
+ return Math.sqrt(vSqr);
+ }
+ }
+
+ b2Distance.g_GJK_Iterations = maxIterations;
+ return Math.sqrt(vSqr);
+ };
+b2Distance.g_GJK_Iterations = 0;
diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2Manifold.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2Manifold.js
new file mode 100644
index 0000000..14b0979
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2Manifold.js
@@ -0,0 +1,34 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+// A manifold for two touching convex shapes.
+var b2Manifold = Class.create();
+b2Manifold.prototype =
+{
+ initialize: function(){
+ this.points = new Array(b2Settings.b2_maxManifoldPoints);
+ for (var i = 0; i < b2Settings.b2_maxManifoldPoints; i++){
+ this.points[i] = new b2ContactPoint();
+ }
+ this.normal = new b2Vec2();
+ },
+ points: null,
+ normal: null,
+ pointCount: 0};
diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2OBB.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2OBB.js
new file mode 100644
index 0000000..0214be8e
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2OBB.js
@@ -0,0 +1,34 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+// A manifold for two touching convex shapes.
+var b2OBB = Class.create();
+b2OBB.prototype =
+{
+ R: new b2Mat22(),
+ center: new b2Vec2(),
+ extents: new b2Vec2(),
+ initialize: function() {
+ // initialize instance variables for references
+ this.R = new b2Mat22();
+ this.center = new b2Vec2();
+ this.extents = new b2Vec2();
+ //
+}};
diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2Pair.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2Pair.js
new file mode 100644
index 0000000..56e615b
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2Pair.js
@@ -0,0 +1,60 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+// The pair manager is used by the broad-phase to quickly add/remove/find pairs
+// of overlapping proxies. It is based closely on code provided by Pierre Terdiman.
+// http:
+
+
+
+
+
+var b2Pair = Class.create();
+b2Pair.prototype =
+{
+
+
+ SetBuffered: function() { this.status |= b2Pair.e_pairBuffered; },
+ ClearBuffered: function() { this.status &= ~b2Pair.e_pairBuffered; },
+ IsBuffered: function(){ return (this.status & b2Pair.e_pairBuffered) == b2Pair.e_pairBuffered; },
+
+ SetRemoved: function() { this.status |= b2Pair.e_pairRemoved; },
+ ClearRemoved: function() { this.status &= ~b2Pair.e_pairRemoved; },
+ IsRemoved: function(){ return (this.status & b2Pair.e_pairRemoved) == b2Pair.e_pairRemoved; },
+
+ SetFinal: function() { this.status |= b2Pair.e_pairFinal; },
+ IsFinal: function(){ return (this.status & b2Pair.e_pairFinal) == b2Pair.e_pairFinal; },
+
+ userData: null,
+ proxyId1: 0,
+ proxyId2: 0,
+ next: 0,
+ status: 0,
+
+ // STATIC
+
+ // enum
+
+ initialize: function() {}};
+b2Pair.b2_nullPair = b2Settings.USHRT_MAX;
+b2Pair.b2_nullProxy = b2Settings.USHRT_MAX;
+b2Pair.b2_tableCapacity = b2Settings.b2_maxPairs;
+b2Pair.b2_tableMask = b2Pair.b2_tableCapacity - 1;
+b2Pair.e_pairBuffered = 0x0001;
+b2Pair.e_pairRemoved = 0x0002;
+b2Pair.e_pairFinal = 0x0004;
diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2PairCallback.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2PairCallback.js
new file mode 100644
index 0000000..f68628c
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2PairCallback.js
@@ -0,0 +1,34 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+var b2PairCallback = Class.create();
+b2PairCallback.prototype =
+{
+ //virtual ~b2PairCallback() {}
+
+ // This returns the new pair user data.
+ PairAdded: function(proxyUserData1, proxyUserData2){return null},
+
+ // This should free the pair's user data. In extreme circumstances, it is possible
+ // this will be called with null pairUserData because the pair never existed.
+ PairRemoved: function(proxyUserData1, proxyUserData2, pairUserData){},
+ initialize: function() {}};
+
+
diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2PairManager.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2PairManager.js
new file mode 100644
index 0000000..88d4c90
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2PairManager.js
@@ -0,0 +1,386 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+// The pair manager is used by the broad-phase to quickly add/remove/find pairs
+// of overlapping proxies. It is based closely on code provided by Pierre Terdiman.
+// http:
+
+
+
+
+
+var b2PairManager = Class.create();
+b2PairManager.prototype =
+{
+//public:
+ initialize: function(){
+ var i = 0;
+ //b2Settings.b2Assert(b2Math.b2IsPowerOfTwo(b2Pair.b2_tableCapacity) == true);
+ //b2Settings.b2Assert(b2Pair.b2_tableCapacity >= b2Settings.b2_maxPairs);
+ this.m_hashTable = new Array(b2Pair.b2_tableCapacity);
+ for (i = 0; i < b2Pair.b2_tableCapacity; ++i)
+ {
+ this.m_hashTable[i] = b2Pair.b2_nullPair;
+ }
+ this.m_pairs = new Array(b2Settings.b2_maxPairs);
+ for (i = 0; i < b2Settings.b2_maxPairs; ++i)
+ {
+ this.m_pairs[i] = new b2Pair();
+ }
+ this.m_pairBuffer = new Array(b2Settings.b2_maxPairs);
+ for (i = 0; i < b2Settings.b2_maxPairs; ++i)
+ {
+ this.m_pairBuffer[i] = new b2BufferedPair();
+ }
+
+ for (i = 0; i < b2Settings.b2_maxPairs; ++i)
+ {
+ this.m_pairs[i].proxyId1 = b2Pair.b2_nullProxy;
+ this.m_pairs[i].proxyId2 = b2Pair.b2_nullProxy;
+ this.m_pairs[i].userData = null;
+ this.m_pairs[i].status = 0;
+ this.m_pairs[i].next = (i + 1);
+ }
+ this.m_pairs[b2Settings.b2_maxPairs-1].next = b2Pair.b2_nullPair;
+ this.m_pairCount = 0;
+ },
+ //~b2PairManager();
+
+ Initialize: function(broadPhase, callback){
+ this.m_broadPhase = broadPhase;
+ this.m_callback = callback;
+ },
+
+ /*
+ As proxies are created and moved, many pairs are created and destroyed. Even worse, the same
+ pair may be added and removed multiple times in a single time step of the physics engine. To reduce
+ traffic in the pair manager, we try to avoid destroying pairs in the pair manager until the
+ end of the physics step. This is done by buffering all the this.RemovePair requests. this.AddPair
+ requests are processed immediately because we need the hash table entry for quick lookup.
+
+ All user user callbacks are delayed until the buffered pairs are confirmed in this.Commit.
+ This is very important because the user callbacks may be very expensive and client logic
+ may be harmed if pairs are added and removed within the same time step.
+
+ Buffer a pair for addition.
+ We may add a pair that is not in the pair manager or pair buffer.
+ We may add a pair that is already in the pair manager and pair buffer.
+ If the added pair is not a new pair, then it must be in the pair buffer (because this.RemovePair was called).
+ */
+ AddBufferedPair: function(proxyId1, proxyId2){
+ //b2Settings.b2Assert(id1 != b2_nullProxy && id2 != b2_nullProxy);
+ //b2Settings.b2Assert(this.m_pairBufferCount < b2_maxPairs);
+
+ var pair = this.AddPair(proxyId1, proxyId2);
+
+ // If this pair is not in the pair buffer ...
+ if (pair.IsBuffered() == false)
+ {
+ // This must be a newly added pair.
+ //b2Settings.b2Assert(pair.IsFinal() == false);
+
+ // Add it to the pair buffer.
+ pair.SetBuffered();
+ this.m_pairBuffer[this.m_pairBufferCount].proxyId1 = pair.proxyId1;
+ this.m_pairBuffer[this.m_pairBufferCount].proxyId2 = pair.proxyId2;
+ ++this.m_pairBufferCount;
+
+ //b2Settings.b2Assert(this.m_pairBufferCount <= this.m_pairCount);
+ }
+
+ // Confirm this pair for the subsequent call to this.Commit.
+ pair.ClearRemoved();
+
+ if (b2BroadPhase.s_validate)
+ {
+ this.ValidateBuffer();
+ }
+ },
+
+ // Buffer a pair for removal.
+ RemoveBufferedPair: function(proxyId1, proxyId2){
+ //b2Settings.b2Assert(id1 != b2_nullProxy && id2 != b2_nullProxy);
+ //b2Settings.b2Assert(this.m_pairBufferCount < b2_maxPairs);
+
+ var pair = this.Find(proxyId1, proxyId2);
+
+ if (pair == null)
+ {
+ // The pair never existed. This is legal (due to collision filtering).
+ return;
+ }
+
+ // If this pair is not in the pair buffer ...
+ if (pair.IsBuffered() == false)
+ {
+ // This must be an old pair.
+ //b2Settings.b2Assert(pair.IsFinal() == true);
+
+ pair.SetBuffered();
+ this.m_pairBuffer[this.m_pairBufferCount].proxyId1 = pair.proxyId1;
+ this.m_pairBuffer[this.m_pairBufferCount].proxyId2 = pair.proxyId2;
+ ++this.m_pairBufferCount;
+
+ //b2Settings.b2Assert(this.m_pairBufferCount <= this.m_pairCount);
+ }
+
+ pair.SetRemoved();
+
+ if (b2BroadPhase.s_validate)
+ {
+ this.ValidateBuffer();
+ }
+ },
+
+ Commit: function(){
+ var i = 0;
+
+ var removeCount = 0;
+
+ var proxies = this.m_broadPhase.m_proxyPool;
+
+ for (i = 0; i < this.m_pairBufferCount; ++i)
+ {
+ var pair = this.Find(this.m_pairBuffer[i].proxyId1, this.m_pairBuffer[i].proxyId2);
+ //b2Settings.b2Assert(pair.IsBuffered());
+ pair.ClearBuffered();
+
+ //b2Settings.b2Assert(pair.proxyId1 < b2Settings.b2_maxProxies && pair.proxyId2 < b2Settings.b2_maxProxies);
+
+ var proxy1 = proxies[ pair.proxyId1 ];
+ var proxy2 = proxies[ pair.proxyId2 ];
+
+ //b2Settings.b2Assert(proxy1.IsValid());
+ //b2Settings.b2Assert(proxy2.IsValid());
+
+ if (pair.IsRemoved())
+ {
+ // It is possible a pair was added then removed before a commit. Therefore,
+ // we should be careful not to tell the user the pair was removed when the
+ // the user didn't receive a matching add.
+ if (pair.IsFinal() == true)
+ {
+ this.m_callback.PairRemoved(proxy1.userData, proxy2.userData, pair.userData);
+ }
+
+ // Store the ids so we can actually remove the pair below.
+ this.m_pairBuffer[removeCount].proxyId1 = pair.proxyId1;
+ this.m_pairBuffer[removeCount].proxyId2 = pair.proxyId2;
+ ++removeCount;
+ }
+ else
+ {
+ //b2Settings.b2Assert(this.m_broadPhase.TestOverlap(proxy1, proxy2) == true);
+
+ if (pair.IsFinal() == false)
+ {
+ pair.userData = this.m_callback.PairAdded(proxy1.userData, proxy2.userData);
+ pair.SetFinal();
+ }
+ }
+ }
+
+ for (i = 0; i < removeCount; ++i)
+ {
+ this.RemovePair(this.m_pairBuffer[i].proxyId1, this.m_pairBuffer[i].proxyId2);
+ }
+
+ this.m_pairBufferCount = 0;
+
+ if (b2BroadPhase.s_validate)
+ {
+ this.ValidateTable();
+ }
+ },
+
+//private:
+
+ // Add a pair and return the new pair. If the pair already exists,
+ // no new pair is created and the old one is returned.
+ AddPair: function(proxyId1, proxyId2){
+
+ if (proxyId1 > proxyId2){
+ var temp = proxyId1;
+ proxyId1 = proxyId2;
+ proxyId2 = temp;
+ //b2Math.b2Swap(p1, p2);
+ }
+
+ var hash = b2PairManager.Hash(proxyId1, proxyId2) & b2Pair.b2_tableMask;
+
+ //var pairIndex = this.FindHash(proxyId1, proxyId2, hash);
+ var pair = pair = this.FindHash(proxyId1, proxyId2, hash);
+
+ if (pair != null)
+ {
+ return pair;
+ }
+
+ //b2Settings.b2Assert(this.m_pairCount < b2Settings.b2_maxPairs && this.m_freePair != b2_nullPair);
+
+ var pIndex = this.m_freePair;
+ pair = this.m_pairs[pIndex];
+ this.m_freePair = pair.next;
+
+ pair.proxyId1 = proxyId1;
+ pair.proxyId2 = proxyId2;
+ pair.status = 0;
+ pair.userData = null;
+ pair.next = this.m_hashTable[hash];
+
+ this.m_hashTable[hash] = pIndex;
+
+ ++this.m_pairCount;
+
+ return pair;
+ },
+
+ // Remove a pair, return the pair's userData.
+ RemovePair: function(proxyId1, proxyId2){
+
+ //b2Settings.b2Assert(this.m_pairCount > 0);
+
+ if (proxyId1 > proxyId2){
+ var temp = proxyId1;
+ proxyId1 = proxyId2;
+ proxyId2 = temp;
+ //b2Math.b2Swap(proxyId1, proxyId2);
+ }
+
+ var hash = b2PairManager.Hash(proxyId1, proxyId2) & b2Pair.b2_tableMask;
+
+ var node = this.m_hashTable[hash];
+ var pNode = null;
+
+ while (node != b2Pair.b2_nullPair)
+ {
+ if (b2PairManager.Equals(this.m_pairs[node], proxyId1, proxyId2))
+ {
+ var index = node;
+
+ //*node = this.m_pairs[*node].next;
+ if (pNode){
+ pNode.next = this.m_pairs[node].next;
+ }
+ else{
+ this.m_hashTable[hash] = this.m_pairs[node].next;
+ }
+
+ var pair = this.m_pairs[ index ];
+ var userData = pair.userData;
+
+ // Scrub
+ pair.next = this.m_freePair;
+ pair.proxyId1 = b2Pair.b2_nullProxy;
+ pair.proxyId2 = b2Pair.b2_nullProxy;
+ pair.userData = null;
+ pair.status = 0;
+
+ this.m_freePair = index;
+ --this.m_pairCount;
+ return userData;
+ }
+ else
+ {
+ //node = &this.m_pairs[*node].next;
+ pNode = this.m_pairs[node];
+ node = pNode.next;
+ }
+ }
+
+ //b2Settings.b2Assert(false);
+ return null;
+ },
+
+ Find: function(proxyId1, proxyId2){
+
+ if (proxyId1 > proxyId2){
+ var temp = proxyId1;
+ proxyId1 = proxyId2;
+ proxyId2 = temp;
+ //b2Math.b2Swap(proxyId1, proxyId2);
+ }
+
+ var hash = b2PairManager.Hash(proxyId1, proxyId2) & b2Pair.b2_tableMask;
+
+ return this.FindHash(proxyId1, proxyId2, hash);
+ },
+ FindHash: function(proxyId1, proxyId2, hash){
+ var index = this.m_hashTable[hash];
+
+ while( index != b2Pair.b2_nullPair && b2PairManager.Equals(this.m_pairs[index], proxyId1, proxyId2) == false)
+ {
+ index = this.m_pairs[index].next;
+ }
+
+ if ( index == b2Pair.b2_nullPair )
+ {
+ return null;
+ }
+
+ //b2Settings.b2Assert(index < b2_maxPairs);
+
+ return this.m_pairs[ index ];
+ },
+
+ ValidateBuffer: function(){
+ // DEBUG
+ },
+
+ ValidateTable: function(){
+ // DEBUG
+ },
+
+//public:
+ m_broadPhase: null,
+ m_callback: null,
+ m_pairs: null,
+ m_freePair: 0,
+ m_pairCount: 0,
+
+ m_pairBuffer: null,
+ m_pairBufferCount: 0,
+
+ m_hashTable: null
+
+
+// static
+ // Thomas Wang's hash, see: http:
+
+
+
+};
+b2PairManager.Hash = function(proxyId1, proxyId2)
+ {
+ var key = ((proxyId2 << 16) & 0xffff0000) | proxyId1;
+ key = ~key + ((key << 15) & 0xFFFF8000);
+ key = key ^ ((key >> 12) & 0x000fffff);
+ key = key + ((key << 2) & 0xFFFFFFFC);
+ key = key ^ ((key >> 4) & 0x0fffffff);
+ key = key * 2057;
+ key = key ^ ((key >> 16) & 0x0000ffff);
+ return key;
+ };
+b2PairManager.Equals = function(pair, proxyId1, proxyId2)
+ {
+ return (pair.proxyId1 == proxyId1 && pair.proxyId2 == proxyId2);
+ };
+b2PairManager.EqualsPair = function(pair1, pair2)
+ {
+ return pair1.proxyId1 == pair2.proxyId1 && pair1.proxyId2 == pair2.proxyId2;
+ };
diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2Proxy.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2Proxy.js
new file mode 100644
index 0000000..60726d0
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2Proxy.js
@@ -0,0 +1,40 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+var b2Proxy = Class.create();
+b2Proxy.prototype = {
+ GetNext: function(){ return this.lowerBounds[0]; },
+ SetNext: function(next) { this.lowerBounds[0] = next /*& 0x0000ffff*/; },
+
+ IsValid: function(){ return this.overlapCount != b2BroadPhase.b2_invalid; },
+
+ lowerBounds: [/*uint*/(0), /*uint*/(0)],
+ upperBounds: [/*uint*/(0), /*uint*/(0)],
+ overlapCount: 0,
+ timeStamp: 0,
+
+ userData: null,
+
+ initialize: function() {
+ // initialize instance variables for references
+ this.lowerBounds = [/*uint*/(0), /*uint*/(0)];
+ this.upperBounds = [/*uint*/(0), /*uint*/(0)];
+ //
+}}
diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2BoxDef.js b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2BoxDef.js
new file mode 100644
index 0000000..9cc24f7
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2BoxDef.js
@@ -0,0 +1,49 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+
+
+var b2BoxDef = Class.create();
+Object.extend(b2BoxDef.prototype, b2ShapeDef.prototype);
+Object.extend(b2BoxDef.prototype,
+{
+ initialize: function()
+ {
+ // The constructor for b2ShapeDef
+ this.type = b2Shape.e_unknownShape;
+ this.userData = null;
+ this.localPosition = new b2Vec2(0.0, 0.0);
+ this.localRotation = 0.0;
+ this.friction = 0.2;
+ this.restitution = 0.0;
+ this.density = 0.0;
+ this.categoryBits = 0x0001;
+ this.maskBits = 0xFFFF;
+ this.groupIndex = 0;
+ //
+
+ this.type = b2Shape.e_boxShape;
+ this.extents = new b2Vec2(1.0, 1.0);
+ },
+
+ extents: null});
+
diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2CircleDef.js b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2CircleDef.js
new file mode 100644
index 0000000..dd1c582
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2CircleDef.js
@@ -0,0 +1,49 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+
+
+var b2CircleDef = Class.create();
+Object.extend(b2CircleDef.prototype, b2ShapeDef.prototype);
+Object.extend(b2CircleDef.prototype,
+{
+ initialize: function()
+ {
+ // The constructor for b2ShapeDef
+ this.type = b2Shape.e_unknownShape;
+ this.userData = null;
+ this.localPosition = new b2Vec2(0.0, 0.0);
+ this.localRotation = 0.0;
+ this.friction = 0.2;
+ this.restitution = 0.0;
+ this.density = 0.0;
+ this.categoryBits = 0x0001;
+ this.maskBits = 0xFFFF;
+ this.groupIndex = 0;
+ //
+
+ this.type = b2Shape.e_circleShape;
+ this.radius = 1.0;
+ },
+
+ radius: null});
+
diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2CircleShape.js b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2CircleShape.js
new file mode 100644
index 0000000..4456168
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2CircleShape.js
@@ -0,0 +1,198 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+
+
+var b2CircleShape = Class.create();
+Object.extend(b2CircleShape.prototype, b2Shape.prototype);
+Object.extend(b2CircleShape.prototype,
+{
+ TestPoint: function(p){
+ //var d = b2Math.SubtractVV(p, this.m_position);
+ var d = new b2Vec2();
+ d.SetV(p);
+ d.Subtract(this.m_position);
+ return b2Math.b2Dot(d, d) <= this.m_radius * this.m_radius;
+ },
+
+ //--------------- Internals Below -------------------
+
+ initialize: function(def, body, localCenter){
+ // initialize instance variables for references
+ this.m_R = new b2Mat22();
+ this.m_position = new b2Vec2();
+ //
+
+ // The constructor for b2Shape
+ this.m_userData = def.userData;
+
+ this.m_friction = def.friction;
+ this.m_restitution = def.restitution;
+ this.m_body = body;
+
+ this.m_proxyId = b2Pair.b2_nullProxy;
+
+ this.m_maxRadius = 0.0;
+
+ this.m_categoryBits = def.categoryBits;
+ this.m_maskBits = def.maskBits;
+ this.m_groupIndex = def.groupIndex;
+ //
+
+ // initialize instance variables for references
+ this.m_localPosition = new b2Vec2();
+ //
+
+ //super(def, body);
+
+ //b2Settings.b2Assert(def.type == b2Shape.e_circleShape);
+ var circle = def;
+
+ //this.m_localPosition = def.localPosition - localCenter;
+ this.m_localPosition.Set(def.localPosition.x - localCenter.x, def.localPosition.y - localCenter.y);
+ this.m_type = b2Shape.e_circleShape;
+ this.m_radius = circle.radius;
+
+ this.m_R.SetM(this.m_body.m_R);
+ //b2Vec2 r = b2Mul(this.m_body->this.m_R, this.m_localPosition);
+ var rX = this.m_R.col1.x * this.m_localPosition.x + this.m_R.col2.x * this.m_localPosition.y;
+ var rY = this.m_R.col1.y * this.m_localPosition.x + this.m_R.col2.y * this.m_localPosition.y;
+ //this.m_position = this.m_body->this.m_position + r;
+ this.m_position.x = this.m_body.m_position.x + rX;
+ this.m_position.y = this.m_body.m_position.y + rY;
+ //this.m_maxRadius = r.Length() + this.m_radius;
+ this.m_maxRadius = Math.sqrt(rX*rX+rY*rY) + this.m_radius;
+
+ var aabb = new b2AABB();
+ aabb.minVertex.Set(this.m_position.x - this.m_radius, this.m_position.y - this.m_radius);
+ aabb.maxVertex.Set(this.m_position.x + this.m_radius, this.m_position.y + this.m_radius);
+
+ var broadPhase = this.m_body.m_world.m_broadPhase;
+ if (broadPhase.InRange(aabb))
+ {
+ this.m_proxyId = broadPhase.CreateProxy(aabb, this);
+ }
+ else
+ {
+ this.m_proxyId = b2Pair.b2_nullProxy;
+ }
+
+ if (this.m_proxyId == b2Pair.b2_nullProxy)
+ {
+ this.m_body.Freeze();
+ }
+ },
+
+ Synchronize: function(position1, R1, position2, R2){
+ this.m_R.SetM(R2);
+ //this.m_position = position2 + b2Mul(R2, this.m_localPosition);
+ this.m_position.x = (R2.col1.x * this.m_localPosition.x + R2.col2.x * this.m_localPosition.y) + position2.x;
+ this.m_position.y = (R2.col1.y * this.m_localPosition.x + R2.col2.y * this.m_localPosition.y) + position2.y;
+
+ if (this.m_proxyId == b2Pair.b2_nullProxy)
+ {
+ return;
+ }
+
+ // Compute an AABB that covers the swept shape (may miss some rotation effect).
+ //b2Vec2 p1 = position1 + b2Mul(R1, this.m_localPosition);
+ var p1X = position1.x + (R1.col1.x * this.m_localPosition.x + R1.col2.x * this.m_localPosition.y);
+ var p1Y = position1.y + (R1.col1.y * this.m_localPosition.x + R1.col2.y * this.m_localPosition.y);
+ //b2Vec2 lower = b2Min(p1, this.m_position);
+ var lowerX = Math.min(p1X, this.m_position.x);
+ var lowerY = Math.min(p1Y, this.m_position.y);
+ //b2Vec2 upper = b2Max(p1, this.m_position);
+ var upperX = Math.max(p1X, this.m_position.x);
+ var upperY = Math.max(p1Y, this.m_position.y);
+
+ var aabb = new b2AABB();
+ aabb.minVertex.Set(lowerX - this.m_radius, lowerY - this.m_radius);
+ aabb.maxVertex.Set(upperX + this.m_radius, upperY + this.m_radius);
+
+ var broadPhase = this.m_body.m_world.m_broadPhase;
+ if (broadPhase.InRange(aabb))
+ {
+ broadPhase.MoveProxy(this.m_proxyId, aabb);
+ }
+ else
+ {
+ this.m_body.Freeze();
+ }
+ },
+
+ QuickSync: function(position, R){
+ this.m_R.SetM(R);
+ //this.m_position = position + b2Mul(R, this.m_localPosition);
+ this.m_position.x = (R.col1.x * this.m_localPosition.x + R.col2.x * this.m_localPosition.y) + position.x;
+ this.m_position.y = (R.col1.y * this.m_localPosition.x + R.col2.y * this.m_localPosition.y) + position.y;
+ },
+
+
+ ResetProxy: function(broadPhase)
+ {
+ if (this.m_proxyId == b2Pair.b2_nullProxy)
+ {
+ return;
+ }
+
+ var proxy = broadPhase.GetProxy(this.m_proxyId);
+
+ broadPhase.DestroyProxy(this.m_proxyId);
+ proxy = null;
+
+ var aabb = new b2AABB();
+ aabb.minVertex.Set(this.m_position.x - this.m_radius, this.m_position.y - this.m_radius);
+ aabb.maxVertex.Set(this.m_position.x + this.m_radius, this.m_position.y + this.m_radius);
+
+ if (broadPhase.InRange(aabb))
+ {
+ this.m_proxyId = broadPhase.CreateProxy(aabb, this);
+ }
+ else
+ {
+ this.m_proxyId = b2Pair.b2_nullProxy;
+ }
+
+ if (this.m_proxyId == b2Pair.b2_nullProxy)
+ {
+ this.m_body.Freeze();
+ }
+ },
+
+
+ Support: function(dX, dY, out)
+ {
+ //b2Vec2 u = d;
+ //u.Normalize();
+ var len = Math.sqrt(dX*dX + dY*dY);
+ dX /= len;
+ dY /= len;
+ //return this.m_position + this.m_radius * u;
+ out.Set( this.m_position.x + this.m_radius*dX,
+ this.m_position.y + this.m_radius*dY);
+ },
+
+
+ // Local position in parent body
+ m_localPosition: new b2Vec2(),
+ m_radius: null});
+
diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2MassData.js b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2MassData.js
new file mode 100644
index 0000000..2cd1af5
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2MassData.js
@@ -0,0 +1,36 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+
+
+var b2MassData = Class.create();
+b2MassData.prototype =
+{
+ mass: 0.0,
+ center: new b2Vec2(0,0),
+ I: 0.0,
+
+ initialize: function() {
+ // initialize instance variables for references
+ this.center = new b2Vec2(0,0);
+ //
+}}
diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2PolyDef.js b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2PolyDef.js
new file mode 100644
index 0000000..6f13c28
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2PolyDef.js
@@ -0,0 +1,58 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+
+
+var b2PolyDef = Class.create();
+Object.extend(b2PolyDef.prototype, b2ShapeDef.prototype);
+Object.extend(b2PolyDef.prototype,
+{
+ initialize: function()
+ {
+ // The constructor for b2ShapeDef
+ this.type = b2Shape.e_unknownShape;
+ this.userData = null;
+ this.localPosition = new b2Vec2(0.0, 0.0);
+ this.localRotation = 0.0;
+ this.friction = 0.2;
+ this.restitution = 0.0;
+ this.density = 0.0;
+ this.categoryBits = 0x0001;
+ this.maskBits = 0xFFFF;
+ this.groupIndex = 0;
+ //
+
+ // initialize instance variables for references
+ this.vertices = new Array(b2Settings.b2_maxPolyVertices);
+ //
+
+ this.type = b2Shape.e_polyShape;
+ this.vertexCount = 0;
+
+ for (var i = 0; i < b2Settings.b2_maxPolyVertices; i++){
+ this.vertices[i] = new b2Vec2();
+ }
+ },
+
+ vertices: new Array(b2Settings.b2_maxPolyVertices),
+ vertexCount: 0});
+
diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2PolyShape.js b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2PolyShape.js
new file mode 100644
index 0000000..39ca4a6
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2PolyShape.js
@@ -0,0 +1,492 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+// A convex polygon. The position of the polygon (m_position) is the
+// position of the centroid. The vertices of the incoming polygon are pre-rotated
+// according to the local rotation. The vertices are also shifted to be centered
+// on the centroid. Since the local rotation is absorbed into the vertex
+// coordinates, the polygon rotation is equal to the body rotation. However,
+// the polygon position is centered on the polygon centroid. This simplifies
+// some collision algorithms.
+
+var b2PolyShape = Class.create();
+Object.extend(b2PolyShape.prototype, b2Shape.prototype);
+Object.extend(b2PolyShape.prototype,
+{
+ TestPoint: function(p){
+
+ //var pLocal = b2Math.b2MulTMV(this.m_R, b2Math.SubtractVV(p, this.m_position));
+ var pLocal = new b2Vec2();
+ pLocal.SetV(p);
+ pLocal.Subtract(this.m_position);
+ pLocal.MulTM(this.m_R);
+
+ for (var i = 0; i < this.m_vertexCount; ++i)
+ {
+ //var dot = b2Math.b2Dot(this.m_normals[i], b2Math.SubtractVV(pLocal, this.m_vertices[i]));
+ var tVec = new b2Vec2();
+ tVec.SetV(pLocal);
+ tVec.Subtract(this.m_vertices[i]);
+
+ var dot = b2Math.b2Dot(this.m_normals[i], tVec);
+ if (dot > 0.0)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ },
+
+ //--------------- Internals Below -------------------
+ // Temp vec for b2Shape.PolyCentroid
+
+ initialize: function(def, body, newOrigin){
+ // initialize instance variables for references
+ this.m_R = new b2Mat22();
+ this.m_position = new b2Vec2();
+ //
+
+ // The constructor for b2Shape
+ this.m_userData = def.userData;
+
+ this.m_friction = def.friction;
+ this.m_restitution = def.restitution;
+ this.m_body = body;
+
+ this.m_proxyId = b2Pair.b2_nullProxy;
+
+ this.m_maxRadius = 0.0;
+
+ this.m_categoryBits = def.categoryBits;
+ this.m_maskBits = def.maskBits;
+ this.m_groupIndex = def.groupIndex;
+ //
+
+ // initialize instance variables for references
+ this.syncAABB = new b2AABB();
+ this.syncMat = new b2Mat22();
+ this.m_localCentroid = new b2Vec2();
+ this.m_localOBB = new b2OBB();
+ //
+
+
+ //super(def, body);
+
+ var i = 0;
+
+
+ var hX;
+ var hY;
+
+ var tVec;
+
+ var aabb = new b2AABB();
+
+ // Vertices
+ this.m_vertices = new Array(b2Settings.b2_maxPolyVertices);
+ this.m_coreVertices = new Array(b2Settings.b2_maxPolyVertices);
+ //for (i = 0; i < b2Settings.b2_maxPolyVertices; i++)
+ // this.m_vertices[i] = new b2Vec2();
+
+ // Normals
+ this.m_normals = new Array(b2Settings.b2_maxPolyVertices);
+ //for (i = 0; i < b2Settings.b2_maxPolyVertices; i++)
+ // this.m_normals[i] = new b2Vec2();
+
+ //b2Settings.b2Assert(def.type == b2Shape.e_boxShape || def.type == b2Shape.e_polyShape);
+ this.m_type = b2Shape.e_polyShape;
+
+ var localR = new b2Mat22(def.localRotation);
+
+ // Get the vertices transformed into the body frame.
+ if (def.type == b2Shape.e_boxShape)
+ {
+ //this.m_localCentroid = def.localPosition - newOrigin;
+ this.m_localCentroid.x = def.localPosition.x - newOrigin.x;
+ this.m_localCentroid.y = def.localPosition.y - newOrigin.y;
+
+ var box = def;
+ this.m_vertexCount = 4;
+ hX = box.extents.x;
+ hY = box.extents.y;
+
+ //hc.x = b2Max(0.0f, h.x - 2.0f * b2_linearSlop);
+ var hcX = Math.max(0.0, hX - 2.0 * b2Settings.b2_linearSlop);
+ //hc.y = b2Max(0.0f, h.y - 2.0f * b2_linearSlop);
+ var hcY = Math.max(0.0, hY - 2.0 * b2Settings.b2_linearSlop);
+
+ //this.m_vertices[0] = b2Mul(localR, b2Vec2(h.x, h.y));
+ tVec = this.m_vertices[0] = new b2Vec2();
+ tVec.x = localR.col1.x * hX + localR.col2.x * hY;
+ tVec.y = localR.col1.y * hX + localR.col2.y * hY;
+ //this.m_vertices[1] = b2Mul(localR, b2Vec2(-h.x, h.y));
+ tVec = this.m_vertices[1] = new b2Vec2();
+ tVec.x = localR.col1.x * -hX + localR.col2.x * hY;
+ tVec.y = localR.col1.y * -hX + localR.col2.y * hY;
+ //this.m_vertices[2] = b2Mul(localR, b2Vec2(-h.x, -h.y));
+ tVec = this.m_vertices[2] = new b2Vec2();
+ tVec.x = localR.col1.x * -hX + localR.col2.x * -hY;
+ tVec.y = localR.col1.y * -hX + localR.col2.y * -hY;
+ //this.m_vertices[3] = b2Mul(localR, b2Vec2(h.x, -h.y));
+ tVec = this.m_vertices[3] = new b2Vec2();
+ tVec.x = localR.col1.x * hX + localR.col2.x * -hY;
+ tVec.y = localR.col1.y * hX + localR.col2.y * -hY;
+
+ //this.m_coreVertices[0] = b2Mul(localR, b2Vec2(hc.x, hc.y));
+ tVec = this.m_coreVertices[0] = new b2Vec2();
+ tVec.x = localR.col1.x * hcX + localR.col2.x * hcY;
+ tVec.y = localR.col1.y * hcX + localR.col2.y * hcY;
+ //this.m_coreVertices[1] = b2Mul(localR, b2Vec2(-hc.x, hc.y));
+ tVec = this.m_coreVertices[1] = new b2Vec2();
+ tVec.x = localR.col1.x * -hcX + localR.col2.x * hcY;
+ tVec.y = localR.col1.y * -hcX + localR.col2.y * hcY;
+ //this.m_coreVertices[2] = b2Mul(localR, b2Vec2(-hc.x, -hc.y));
+ tVec = this.m_coreVertices[2] = new b2Vec2();
+ tVec.x = localR.col1.x * -hcX + localR.col2.x * -hcY;
+ tVec.y = localR.col1.y * -hcX + localR.col2.y * -hcY;
+ //this.m_coreVertices[3] = b2Mul(localR, b2Vec2(hc.x, -hc.y));
+ tVec = this.m_coreVertices[3] = new b2Vec2();
+ tVec.x = localR.col1.x * hcX + localR.col2.x * -hcY;
+ tVec.y = localR.col1.y * hcX + localR.col2.y * -hcY;
+ }
+ else
+ {
+ var poly = def;
+
+ this.m_vertexCount = poly.vertexCount;
+ //b2Settings.b2Assert(3 <= this.m_vertexCount && this.m_vertexCount <= b2Settings.b2_maxPolyVertices);
+ //b2Vec2 centroid = b2Shape.PolyCentroid(poly->vertices, poly->vertexCount);
+ b2Shape.PolyCentroid(poly.vertices, poly.vertexCount, b2PolyShape.tempVec);
+ var centroidX = b2PolyShape.tempVec.x;
+ var centroidY = b2PolyShape.tempVec.y;
+ //this.m_localCentroid = def->localPosition + b2Mul(localR, centroid) - newOrigin;
+ this.m_localCentroid.x = def.localPosition.x + (localR.col1.x * centroidX + localR.col2.x * centroidY) - newOrigin.x;
+ this.m_localCentroid.y = def.localPosition.y + (localR.col1.y * centroidX + localR.col2.y * centroidY) - newOrigin.y;
+
+ for (i = 0; i < this.m_vertexCount; ++i)
+ {
+ this.m_vertices[i] = new b2Vec2();
+ this.m_coreVertices[i] = new b2Vec2();
+
+ //this.m_vertices[i] = b2Mul(localR, poly->vertices[i] - centroid);
+ hX = poly.vertices[i].x - centroidX;
+ hY = poly.vertices[i].y - centroidY;
+ this.m_vertices[i].x = localR.col1.x * hX + localR.col2.x * hY;
+ this.m_vertices[i].y = localR.col1.y * hX + localR.col2.y * hY;
+
+ //b2Vec2 u = this.m_vertices[i];
+ var uX = this.m_vertices[i].x;
+ var uY = this.m_vertices[i].y;
+ //float32 length = u.Length();
+ var length = Math.sqrt(uX*uX + uY*uY);
+ if (length > Number.MIN_VALUE)
+ {
+ uX *= 1.0 / length;
+ uY *= 1.0 / length;
+ }
+
+ //this.m_coreVertices[i] = this.m_vertices[i] - 2.0f * b2_linearSlop * u;
+ this.m_coreVertices[i].x = this.m_vertices[i].x - 2.0 * b2Settings.b2_linearSlop * uX;
+ this.m_coreVertices[i].y = this.m_vertices[i].y - 2.0 * b2Settings.b2_linearSlop * uY;
+ }
+
+ }
+
+ // Compute bounding box. TODO_ERIN optimize OBB
+ //var minVertex = new b2Vec2(Number.MAX_VALUE, Number.MAX_VALUE);
+ var minVertexX = Number.MAX_VALUE;
+ var minVertexY = Number.MAX_VALUE;
+ var maxVertexX = -Number.MAX_VALUE;
+ var maxVertexY = -Number.MAX_VALUE;
+ this.m_maxRadius = 0.0;
+ for (i = 0; i < this.m_vertexCount; ++i)
+ {
+ var v = this.m_vertices[i];
+ //minVertex = b2Math.b2MinV(minVertex, this.m_vertices[i]);
+ minVertexX = Math.min(minVertexX, v.x);
+ minVertexY = Math.min(minVertexY, v.y);
+ //maxVertex = b2Math.b2MaxV(maxVertex, this.m_vertices[i]);
+ maxVertexX = Math.max(maxVertexX, v.x);
+ maxVertexY = Math.max(maxVertexY, v.y);
+ //this.m_maxRadius = b2Max(this.m_maxRadius, v.Length());
+ this.m_maxRadius = Math.max(this.m_maxRadius, v.Length());
+ }
+
+ this.m_localOBB.R.SetIdentity();
+ //this.m_localOBB.center = 0.5 * (minVertex + maxVertex);
+ this.m_localOBB.center.Set((minVertexX + maxVertexX) * 0.5, (minVertexY + maxVertexY) * 0.5);
+ //this.m_localOBB.extents = 0.5 * (maxVertex - minVertex);
+ this.m_localOBB.extents.Set((maxVertexX - minVertexX) * 0.5, (maxVertexY - minVertexY) * 0.5);
+
+ // Compute the edge normals and next index map.
+ var i1 = 0;
+ var i2 = 0;
+ for (i = 0; i < this.m_vertexCount; ++i)
+ {
+ this.m_normals[i] = new b2Vec2();
+ i1 = i;
+ i2 = i + 1 < this.m_vertexCount ? i + 1 : 0;
+ //b2Vec2 edge = this.m_vertices[i2] - this.m_vertices[i1];
+ //var edgeX = this.m_vertices[i2].x - this.m_vertices[i1].x;
+ //var edgeY = this.m_vertices[i2].y - this.m_vertices[i1].y;
+ //this.m_normals[i] = b2Cross(edge, 1.0f);
+ this.m_normals[i].x = this.m_vertices[i2].y - this.m_vertices[i1].y;
+ this.m_normals[i].y = -(this.m_vertices[i2].x - this.m_vertices[i1].x);
+ this.m_normals[i].Normalize();
+ }
+
+ // Ensure the polygon in convex. TODO_ERIN compute convex hull.
+ for (i = 0; i < this.m_vertexCount; ++i)
+ {
+ i1 = i;
+ i2 = i + 1 < this.m_vertexCount ? i + 1 : 0;
+
+ //b2Settings.b2Assert(b2Math.b2CrossVV(this.m_normals[i1], this.m_normals[i2]) > Number.MIN_VALUE);
+ }
+
+ this.m_R.SetM(this.m_body.m_R);
+ //this.m_position.SetV( this.m_body.m_position + b2Mul(this.m_body->this.m_R, this.m_localCentroid) );
+ this.m_position.x = this.m_body.m_position.x + (this.m_R.col1.x * this.m_localCentroid.x + this.m_R.col2.x * this.m_localCentroid.y);
+ this.m_position.y = this.m_body.m_position.y + (this.m_R.col1.y * this.m_localCentroid.x + this.m_R.col2.y * this.m_localCentroid.y);
+
+ //var R = b2Math.b2MulMM(this.m_R, this.m_localOBB.R);
+ //R.col1 = b2MulMV(this.m_R, this.m_localOBB.R.col1);
+ b2PolyShape.tAbsR.col1.x = this.m_R.col1.x * this.m_localOBB.R.col1.x + this.m_R.col2.x * this.m_localOBB.R.col1.y;
+ b2PolyShape.tAbsR.col1.y = this.m_R.col1.y * this.m_localOBB.R.col1.x + this.m_R.col2.y * this.m_localOBB.R.col1.y;
+ //R.col2 = b2MulMV(this.m_R, this.m_localOBB.R.col2)
+ b2PolyShape.tAbsR.col2.x = this.m_R.col1.x * this.m_localOBB.R.col2.x + this.m_R.col2.x * this.m_localOBB.R.col2.y;
+ b2PolyShape.tAbsR.col2.y = this.m_R.col1.y * this.m_localOBB.R.col2.x + this.m_R.col2.y * this.m_localOBB.R.col2.y;
+ //var absR = b2Math.b2AbsM(R);
+ b2PolyShape.tAbsR.Abs()
+
+ //h = b2Math.b2MulMV(b2PolyShape.tAbsR, this.m_localOBB.extents);
+ hX = b2PolyShape.tAbsR.col1.x * this.m_localOBB.extents.x + b2PolyShape.tAbsR.col2.x * this.m_localOBB.extents.y;
+ hY = b2PolyShape.tAbsR.col1.y * this.m_localOBB.extents.x + b2PolyShape.tAbsR.col2.y * this.m_localOBB.extents.y;
+
+ //var position = this.m_position + b2Mul(this.m_R, this.m_localOBB.center);
+ var positionX = this.m_position.x + (this.m_R.col1.x * this.m_localOBB.center.x + this.m_R.col2.x * this.m_localOBB.center.y);
+ var positionY = this.m_position.y + (this.m_R.col1.y * this.m_localOBB.center.x + this.m_R.col2.y * this.m_localOBB.center.y);
+
+ //aabb.minVertex = b2Math.SubtractVV(this.m_position, h);
+ aabb.minVertex.x = positionX - hX;
+ aabb.minVertex.y = positionY - hY;
+ //aabb.maxVertex = b2Math.AddVV(this.m_position, h);
+ aabb.maxVertex.x = positionX + hX;
+ aabb.maxVertex.y = positionY + hY;
+
+ var broadPhase = this.m_body.m_world.m_broadPhase;
+ if (broadPhase.InRange(aabb))
+ {
+ this.m_proxyId = broadPhase.CreateProxy(aabb, this);
+ }
+ else
+ {
+ this.m_proxyId = b2Pair.b2_nullProxy;
+ }
+
+ if (this.m_proxyId == b2Pair.b2_nullProxy)
+ {
+ this.m_body.Freeze();
+ }
+ },
+
+ // Temp AABB for Synch function
+ syncAABB: new b2AABB(),
+ syncMat: new b2Mat22(),
+ Synchronize: function(position1, R1, position2, R2){
+ // The body transform is copied for convenience.
+ this.m_R.SetM(R2);
+ //this.m_position = this.m_body->this.m_position + b2Mul(this.m_body->this.m_R, this.m_localCentroid)
+ this.m_position.x = this.m_body.m_position.x + (R2.col1.x * this.m_localCentroid.x + R2.col2.x * this.m_localCentroid.y);
+ this.m_position.y = this.m_body.m_position.y + (R2.col1.y * this.m_localCentroid.x + R2.col2.y * this.m_localCentroid.y);
+
+ if (this.m_proxyId == b2Pair.b2_nullProxy)
+ {
+ return;
+ }
+
+ //b2AABB aabb1, aabb2;
+ var hX;
+ var hY;
+
+ //b2Mat22 obbR = b2Mul(R1, this.m_localOBB.R);
+ var v1 = R1.col1;
+ var v2 = R1.col2;
+ var v3 = this.m_localOBB.R.col1;
+ var v4 = this.m_localOBB.R.col2;
+ //this.syncMat.col1 = b2MulMV(R1, this.m_localOBB.R.col1);
+ this.syncMat.col1.x = v1.x * v3.x + v2.x * v3.y;
+ this.syncMat.col1.y = v1.y * v3.x + v2.y * v3.y;
+ //this.syncMat.col2 = b2MulMV(R1, this.m_localOBB.R.col2);
+ this.syncMat.col2.x = v1.x * v4.x + v2.x * v4.y;
+ this.syncMat.col2.y = v1.y * v4.x + v2.y * v4.y;
+ //b2Mat22 absR = b2Abs(obbR);
+ this.syncMat.Abs();
+ //b2Vec2 center = position1 + b2Mul(R1, this.m_localCentroid + this.m_localOBB.center);
+ hX = this.m_localCentroid.x + this.m_localOBB.center.x;
+ hY = this.m_localCentroid.y + this.m_localOBB.center.y;
+ var centerX = position1.x + (R1.col1.x * hX + R1.col2.x * hY);
+ var centerY = position1.y + (R1.col1.y * hX + R1.col2.y * hY);
+ //b2Vec2 h = b2Mul(this.syncMat, this.m_localOBB.extents);
+ hX = this.syncMat.col1.x * this.m_localOBB.extents.x + this.syncMat.col2.x * this.m_localOBB.extents.y;
+ hY = this.syncMat.col1.y * this.m_localOBB.extents.x + this.syncMat.col2.y * this.m_localOBB.extents.y;
+ //aabb1.minVertex = center - h;
+ this.syncAABB.minVertex.x = centerX - hX;
+ this.syncAABB.minVertex.y = centerY - hY;
+ //aabb1.maxVertex = center + h;
+ this.syncAABB.maxVertex.x = centerX + hX;
+ this.syncAABB.maxVertex.y = centerY + hY;
+
+ //b2Mat22 obbR = b2Mul(R2, this.m_localOBB.R);
+ v1 = R2.col1;
+ v2 = R2.col2;
+ v3 = this.m_localOBB.R.col1;
+ v4 = this.m_localOBB.R.col2;
+ //this.syncMat.col1 = b2MulMV(R1, this.m_localOBB.R.col1);
+ this.syncMat.col1.x = v1.x * v3.x + v2.x * v3.y;
+ this.syncMat.col1.y = v1.y * v3.x + v2.y * v3.y;
+ //this.syncMat.col2 = b2MulMV(R1, this.m_localOBB.R.col2);
+ this.syncMat.col2.x = v1.x * v4.x + v2.x * v4.y;
+ this.syncMat.col2.y = v1.y * v4.x + v2.y * v4.y;
+ //b2Mat22 absR = b2Abs(obbR);
+ this.syncMat.Abs();
+ //b2Vec2 center = position2 + b2Mul(R2, this.m_localCentroid + this.m_localOBB.center);
+ hX = this.m_localCentroid.x + this.m_localOBB.center.x;
+ hY = this.m_localCentroid.y + this.m_localOBB.center.y;
+ centerX = position2.x + (R2.col1.x * hX + R2.col2.x * hY);
+ centerY = position2.y + (R2.col1.y * hX + R2.col2.y * hY);
+ //b2Vec2 h = b2Mul(absR, this.m_localOBB.extents);
+ hX = this.syncMat.col1.x * this.m_localOBB.extents.x + this.syncMat.col2.x * this.m_localOBB.extents.y;
+ hY = this.syncMat.col1.y * this.m_localOBB.extents.x + this.syncMat.col2.y * this.m_localOBB.extents.y;
+ //aabb2.minVertex = center - h;
+ //aabb2.maxVertex = center + h;
+
+ //aabb.minVertex = b2Min(aabb1.minVertex, aabb2.minVertex);
+ this.syncAABB.minVertex.x = Math.min(this.syncAABB.minVertex.x, centerX - hX);
+ this.syncAABB.minVertex.y = Math.min(this.syncAABB.minVertex.y, centerY - hY);
+ //aabb.maxVertex = b2Max(aabb1.maxVertex, aabb2.maxVertex);
+ this.syncAABB.maxVertex.x = Math.max(this.syncAABB.maxVertex.x, centerX + hX);
+ this.syncAABB.maxVertex.y = Math.max(this.syncAABB.maxVertex.y, centerY + hY);
+
+ var broadPhase = this.m_body.m_world.m_broadPhase;
+ if (broadPhase.InRange(this.syncAABB))
+ {
+ broadPhase.MoveProxy(this.m_proxyId, this.syncAABB);
+ }
+ else
+ {
+ this.m_body.Freeze();
+ }
+ },
+
+ QuickSync: function(position, R){
+ //this.m_R = R;
+ this.m_R.SetM(R);
+ //this.m_position = position + b2Mul(R, this.m_localCentroid);
+ this.m_position.x = position.x + (R.col1.x * this.m_localCentroid.x + R.col2.x * this.m_localCentroid.y);
+ this.m_position.y = position.y + (R.col1.y * this.m_localCentroid.x + R.col2.y * this.m_localCentroid.y);
+ },
+
+ ResetProxy: function(broadPhase){
+
+ if (this.m_proxyId == b2Pair.b2_nullProxy)
+ {
+ return;
+ }
+
+ var proxy = broadPhase.GetProxy(this.m_proxyId);
+
+ broadPhase.DestroyProxy(this.m_proxyId);
+ proxy = null;
+
+ var R = b2Math.b2MulMM(this.m_R, this.m_localOBB.R);
+ var absR = b2Math.b2AbsM(R);
+ var h = b2Math.b2MulMV(absR, this.m_localOBB.extents);
+ //var position = this.m_position + b2Mul(this.m_R, this.m_localOBB.center);
+ var position = b2Math.b2MulMV(this.m_R, this.m_localOBB.center);
+ position.Add(this.m_position);
+
+ var aabb = new b2AABB();
+ //aabb.minVertex = position - h;
+ aabb.minVertex.SetV(position);
+ aabb.minVertex.Subtract(h);
+ //aabb.maxVertex = position + h;
+ aabb.maxVertex.SetV(position);
+ aabb.maxVertex.Add(h);
+
+ if (broadPhase.InRange(aabb))
+ {
+ this.m_proxyId = broadPhase.CreateProxy(aabb, this);
+ }
+ else
+ {
+ this.m_proxyId = b2Pair.b2_nullProxy;
+ }
+
+ if (this.m_proxyId == b2Pair.b2_nullProxy)
+ {
+ this.m_body.Freeze();
+ }
+ },
+
+
+ Support: function(dX, dY, out)
+ {
+ //b2Vec2 dLocal = b2MulT(this.m_R, d);
+ var dLocalX = (dX*this.m_R.col1.x + dY*this.m_R.col1.y);
+ var dLocalY = (dX*this.m_R.col2.x + dY*this.m_R.col2.y);
+
+ var bestIndex = 0;
+ //float32 bestValue = b2Dot(this.m_vertices[0], dLocal);
+ var bestValue = (this.m_coreVertices[0].x * dLocalX + this.m_coreVertices[0].y * dLocalY);
+ for (var i = 1; i < this.m_vertexCount; ++i)
+ {
+ //float32 value = b2Dot(this.m_vertices[i], dLocal);
+ var value = (this.m_coreVertices[i].x * dLocalX + this.m_coreVertices[i].y * dLocalY);
+ if (value > bestValue)
+ {
+ bestIndex = i;
+ bestValue = value;
+ }
+ }
+
+ //return this.m_position + b2Mul(this.m_R, this.m_vertices[bestIndex]);
+ out.Set( this.m_position.x + (this.m_R.col1.x * this.m_coreVertices[bestIndex].x + this.m_R.col2.x * this.m_coreVertices[bestIndex].y),
+ this.m_position.y + (this.m_R.col1.y * this.m_coreVertices[bestIndex].x + this.m_R.col2.y * this.m_coreVertices[bestIndex].y));
+
+ },
+
+
+ // Local position of the shape centroid in parent body frame.
+ m_localCentroid: new b2Vec2(),
+
+ // Local position oriented bounding box. The OBB center is relative to
+ // shape centroid.
+ m_localOBB: new b2OBB(),
+ m_vertices: null,
+ m_coreVertices: null,
+ m_vertexCount: 0,
+ m_normals: null});
+
+b2PolyShape.tempVec = new b2Vec2();
+b2PolyShape.tAbsR = new b2Mat22();
diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2Shape.js b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2Shape.js
new file mode 100644
index 0000000..17ebf8f
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2Shape.js
@@ -0,0 +1,339 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+
+
+
+// Shapes are created automatically when a body is created.
+// Client code does not normally interact with shapes.
+var b2Shape = Class.create();
+b2Shape.prototype =
+{
+ TestPoint: function(p){return false},
+
+ GetUserData: function(){return this.m_userData;},
+
+ GetType: function(){
+ return this.m_type;
+ },
+
+ // Get the parent body of this shape.
+ GetBody: function(){
+ return this.m_body;
+ },
+
+ GetPosition: function(){
+ return this.m_position;
+ },
+ GetRotationMatrix: function(){
+ return this.m_R;
+ },
+
+ // Remove and then add proxy from the broad-phase.
+ // This is used to refresh the collision filters.
+ ResetProxy: function(broadPhase){},
+
+ // Get the next shape in the parent body's shape list.
+ GetNext: function(){
+ return this.m_next;
+ },
+
+ //--------------- Internals Below -------------------
+
+
+
+
+ initialize: function(def, body){
+ // initialize instance variables for references
+ this.m_R = new b2Mat22();
+ this.m_position = new b2Vec2();
+ //
+
+ this.m_userData = def.userData;
+
+ this.m_friction = def.friction;
+ this.m_restitution = def.restitution;
+ this.m_body = body;
+
+ this.m_proxyId = b2Pair.b2_nullProxy;
+
+ this.m_maxRadius = 0.0;
+
+ this.m_categoryBits = def.categoryBits;
+ this.m_maskBits = def.maskBits;
+ this.m_groupIndex = def.groupIndex;
+ },
+
+ // Internal use only. Do not call.
+ //b2Shape::~b2Shape()
+ //{
+ // this.m_body->m_world->m_broadPhase->this.DestroyProxy(this.m_proxyId);
+ //}
+
+
+ DestroyProxy: function()
+ {
+ if (this.m_proxyId != b2Pair.b2_nullProxy)
+ {
+ this.m_body.m_world.m_broadPhase.DestroyProxy(this.m_proxyId);
+ this.m_proxyId = b2Pair.b2_nullProxy;
+ }
+ },
+
+
+ // Internal use only. Do not call.
+ Synchronize: function(position1, R1, position2, R2){},
+ QuickSync: function(position, R){},
+ Support: function(dX, dY, out){},
+ GetMaxRadius: function(){
+ return this.m_maxRadius;
+ },
+
+ m_next: null,
+
+ m_R: new b2Mat22(),
+ m_position: new b2Vec2(),
+
+ m_type: 0,
+
+ m_userData: null,
+
+ m_body: null,
+
+ m_friction: null,
+ m_restitution: null,
+
+ m_maxRadius: null,
+
+ m_proxyId: 0,
+ m_categoryBits: 0,
+ m_maskBits: 0,
+ m_groupIndex: 0
+
+
+
+ // b2ShapeType
+
+
+
+
+
+
+
+
+
+
+
+
+};
+
+
+b2Shape.Create = function(def, body, center){
+ switch (def.type)
+ {
+ case b2Shape.e_circleShape:
+ {
+ //void* mem = body->m_world->m_blockAllocator.Allocate(sizeof(b2CircleShape));
+ return new b2CircleShape(def, body, center);
+ }
+
+ case b2Shape.e_boxShape:
+ case b2Shape.e_polyShape:
+ {
+ //void* mem = body->m_world->m_blockAllocator.Allocate(sizeof(b2PolyShape));
+ return new b2PolyShape(def, body, center);
+ }
+ }
+
+ //b2Settings.b2Assert(false);
+ return null;
+ };
+b2Shape.Destroy = function(shape)
+ {
+ /*b2BlockAllocator& allocator = shape->m_body->m_world->m_blockAllocator;
+
+ switch (shape.m_type)
+ {
+ case b2Shape.e_circleShape:
+ shape->~b2Shape();
+ allocator.Free(shape, sizeof(b2CircleShape));
+ break;
+
+ case b2Shape.e_polyShape:
+ shape->~b2Shape();
+ allocator.Free(shape, sizeof(b2PolyShape));
+ break;
+
+ default:
+ b2Assert(false);
+ }
+
+ shape = NULL;*/
+
+ // FROM DESTRUCTOR
+ if (shape.m_proxyId != b2Pair.b2_nullProxy)
+ shape.m_body.m_world.m_broadPhase.DestroyProxy(shape.m_proxyId);
+ };
+b2Shape.e_unknownShape = -1;
+b2Shape.e_circleShape = 0;
+b2Shape.e_boxShape = 1;
+b2Shape.e_polyShape = 2;
+b2Shape.e_meshShape = 3;
+b2Shape.e_shapeTypeCount = 4;
+b2Shape.PolyMass = function(massData, vs, count, rho)
+ {
+ //b2Settings.b2Assert(count >= 3);
+
+ //var center = new b2Vec2(0.0, 0.0);
+ var center = new b2Vec2();
+ center.SetZero();
+
+ var area = 0.0;
+ var I = 0.0;
+
+ // pRef is the reference point for forming triangles.
+ // It's location doesn't change the result (except for rounding error).
+ var pRef = new b2Vec2(0.0, 0.0);
+
+ var inv3 = 1.0 / 3.0;
+
+ for (var i = 0; i < count; ++i)
+ {
+ // Triangle vertices.
+ var p1 = pRef;
+ var p2 = vs[i];
+ var p3 = i + 1 < count ? vs[i+1] : vs[0];
+
+ var e1 = b2Math.SubtractVV(p2, p1);
+ var e2 = b2Math.SubtractVV(p3, p1);
+
+ var D = b2Math.b2CrossVV(e1, e2);
+
+ var triangleArea = 0.5 * D;
+ area += triangleArea;
+
+ // Area weighted centroid
+ // center += triangleArea * inv3 * (p1 + p2 + p3);
+ var tVec = new b2Vec2();
+ tVec.SetV(p1);
+ tVec.Add(p2);
+ tVec.Add(p3);
+ tVec.Multiply(inv3*triangleArea);
+ center.Add(tVec);
+
+ var px = p1.x;
+ var py = p1.y;
+ var ex1 = e1.x;
+ var ey1 = e1.y;
+ var ex2 = e2.x;
+ var ey2 = e2.y;
+
+ var intx2 = inv3 * (0.25 * (ex1*ex1 + ex2*ex1 + ex2*ex2) + (px*ex1 + px*ex2)) + 0.5*px*px;
+ var inty2 = inv3 * (0.25 * (ey1*ey1 + ey2*ey1 + ey2*ey2) + (py*ey1 + py*ey2)) + 0.5*py*py;
+
+ I += D * (intx2 + inty2);
+ }
+
+ // Total mass
+ massData.mass = rho * area;
+
+ // Center of mass
+ //b2Settings.b2Assert(area > Number.MIN_VALUE);
+ center.Multiply( 1.0 / area );
+ massData.center = center;
+
+ // Inertia tensor relative to the center.
+ I = rho * (I - area * b2Math.b2Dot(center, center));
+ massData.I = I;
+ };
+b2Shape.PolyCentroid = function(vs, count, out)
+ {
+ //b2Settings.b2Assert(count >= 3);
+
+ //b2Vec2 c; c.Set(0.0f, 0.0f);
+ var cX = 0.0;
+ var cY = 0.0;
+ //float32 area = 0.0f;
+ var area = 0.0;
+
+ // pRef is the reference point for forming triangles.
+ // It's location doesn't change the result (except for rounding error).
+ //b2Vec2 pRef(0.0f, 0.0f);
+ var pRefX = 0.0;
+ var pRefY = 0.0;
+ /*
+ // This code would put the reference point inside the polygon.
+ for (var i = 0; i < count; ++i)
+ {
+ //pRef += vs[i];
+ pRef.x += vs[i].x;
+ pRef.y += vs[i].y;
+ }
+ pRef.x *= 1.0 / count;
+ pRef.y *= 1.0 / count;
+ */
+
+ //const float32 inv3 = 1.0f / 3.0f;
+ var inv3 = 1.0 / 3.0;
+
+ for (var i = 0; i < count; ++i)
+ {
+ // Triangle vertices.
+ //b2Vec2 p1 = pRef;
+ var p1X = pRefX;
+ var p1Y = pRefY;
+ //b2Vec2 p2 = vs[i];
+ var p2X = vs[i].x;
+ var p2Y = vs[i].y;
+ //b2Vec2 p3 = i + 1 < count ? vs[i+1] : vs[0];
+ var p3X = i + 1 < count ? vs[i+1].x : vs[0].x;
+ var p3Y = i + 1 < count ? vs[i+1].y : vs[0].y;
+
+ //b2Vec2 e1 = p2 - p1;
+ var e1X = p2X - p1X;
+ var e1Y = p2Y - p1Y;
+ //b2Vec2 e2 = p3 - p1;
+ var e2X = p3X - p1X;
+ var e2Y = p3Y - p1Y;
+
+ //float32 D = b2Cross(e1, e2);
+ var D = (e1X * e2Y - e1Y * e2X);
+
+ //float32 triangleArea = 0.5f * D;
+ var triangleArea = 0.5 * D;
+ area += triangleArea;
+
+ // Area weighted centroid
+ //c += triangleArea * inv3 * (p1 + p2 + p3);
+ cX += triangleArea * inv3 * (p1X + p2X + p3X);
+ cY += triangleArea * inv3 * (p1Y + p2Y + p3Y);
+ }
+
+ // Centroid
+ //b2Settings.b2Assert(area > Number.MIN_VALUE);
+ cX *= 1.0 / area;
+ cY *= 1.0 / area;
+
+ // Replace return with 'out' vector
+ //return c;
+ out.Set(cX, cY);
+ };
diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2ShapeDef.js b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2ShapeDef.js
new file mode 100644
index 0000000..8f48f07
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2ShapeDef.js
@@ -0,0 +1,109 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+
+
+var b2ShapeDef = Class.create();
+b2ShapeDef.prototype =
+{
+ initialize: function()
+ {
+ this.type = b2Shape.e_unknownShape;
+ this.userData = null;
+ this.localPosition = new b2Vec2(0.0, 0.0);
+ this.localRotation = 0.0;
+ this.friction = 0.2;
+ this.restitution = 0.0;
+ this.density = 0.0;
+ this.categoryBits = 0x0001;
+ this.maskBits = 0xFFFF;
+ this.groupIndex = 0;
+ },
+
+ //virtual ~b2ShapeDef() {}
+
+ ComputeMass: function(massData)
+ {
+
+ massData.center = new b2Vec2(0.0, 0.0)
+
+ if (this.density == 0.0)
+ {
+ massData.mass = 0.0;
+ massData.center.Set(0.0, 0.0);
+ massData.I = 0.0;
+ };
+
+ switch (this.type)
+ {
+ case b2Shape.e_circleShape:
+ {
+ var circle = this;
+ massData.mass = this.density * b2Settings.b2_pi * circle.radius * circle.radius;
+ massData.center.Set(0.0, 0.0);
+ massData.I = 0.5 * (massData.mass) * circle.radius * circle.radius;
+ }
+ break;
+
+ case b2Shape.e_boxShape:
+ {
+ var box = this;
+ massData.mass = 4.0 * this.density * box.extents.x * box.extents.y;
+ massData.center.Set(0.0, 0.0);
+ massData.I = massData.mass / 3.0 * b2Math.b2Dot(box.extents, box.extents);
+ }
+ break;
+
+ case b2Shape.e_polyShape:
+ {
+ var poly = this;
+ b2Shape.PolyMass(massData, poly.vertices, poly.vertexCount, this.density);
+ }
+ break;
+
+ default:
+ massData.mass = 0.0;
+ massData.center.Set(0.0, 0.0);
+ massData.I = 0.0;
+ break;
+ }
+ },
+
+ type: 0,
+ userData: null,
+ localPosition: null,
+ localRotation: null,
+ friction: null,
+ restitution: null,
+ density: null,
+
+ // The collision category bits. Normally you would just set one bit.
+ categoryBits: 0,
+
+ // The collision mask bits. This states the categories that this
+ // shape would accept for collision.
+ maskBits: 0,
+
+ // Collision groups allow a certain group of objects to never collide (negative)
+ // or always collide (positive). Zero means no collision group. Non-zero group
+ // filtering always wins against the mask bits.
+ groupIndex: 0};
diff --git a/o3d/samples/box2d-3d/third_party/box2d/common/b2Settings.js b/o3d/samples/box2d-3d/third_party/box2d/common/b2Settings.js
new file mode 100644
index 0000000..890fde2
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/common/b2Settings.js
@@ -0,0 +1,72 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+var b2Settings = Class.create();
+b2Settings.prototype = {
+
+
+
+ // Define your unit system here. The default system is
+ // meters-kilograms-seconds. For the tuning to work well,
+ // your dynamic objects should be bigger than a pebble and smaller
+ // than a house.
+ //static public const b2Settings.b2_lengthUnitsPerMeter = 1.0;
+
+ // Use this for pixels:
+
+ // Global tuning constants based on MKS units.
+
+ // Collision
+
+ // Dynamics
+
+ // Sleep
+
+ // assert
+
+ initialize: function() {}}
+b2Settings.USHRT_MAX = 0x0000ffff;
+b2Settings.b2_pi = Math.PI;
+b2Settings.b2_massUnitsPerKilogram = 1.0;
+b2Settings.b2_timeUnitsPerSecond = 1.0;
+b2Settings.b2_lengthUnitsPerMeter = 30.0;
+b2Settings.b2_maxManifoldPoints = 2;
+b2Settings.b2_maxShapesPerBody = 64;
+b2Settings.b2_maxPolyVertices = 8;
+b2Settings.b2_maxProxies = 1024;
+b2Settings.b2_maxPairs = 8 * b2Settings.b2_maxProxies;
+b2Settings.b2_linearSlop = 0.005 * b2Settings.b2_lengthUnitsPerMeter;
+b2Settings.b2_angularSlop = 2.0 / 180.0 * b2Settings.b2_pi;
+b2Settings.b2_velocityThreshold = 1.0 * b2Settings.b2_lengthUnitsPerMeter / b2Settings.b2_timeUnitsPerSecond;
+b2Settings.b2_maxLinearCorrection = 0.2 * b2Settings.b2_lengthUnitsPerMeter;
+b2Settings.b2_maxAngularCorrection = 8.0 / 180.0 * b2Settings.b2_pi;
+b2Settings.b2_contactBaumgarte = 0.2;
+b2Settings.b2_timeToSleep = 0.5 * b2Settings.b2_timeUnitsPerSecond;
+b2Settings.b2_linearSleepTolerance = 0.01 * b2Settings.b2_lengthUnitsPerMeter / b2Settings.b2_timeUnitsPerSecond;
+b2Settings.b2_angularSleepTolerance = 2.0 / 180.0 / b2Settings.b2_timeUnitsPerSecond;
+b2Settings.b2Assert = function(a)
+ {
+ if (!a){
+ var nullVec;
+ nullVec.x++;
+ }
+ };
diff --git a/o3d/samples/box2d-3d/third_party/box2d/common/math/b2Mat22.js b/o3d/samples/box2d-3d/third_party/box2d/common/math/b2Mat22.js
new file mode 100644
index 0000000..d5fb1fc
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/common/math/b2Mat22.js
@@ -0,0 +1,130 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+var b2Mat22 = Class.create();
+b2Mat22.prototype =
+{
+ initialize: function(angle, c1, c2)
+ {
+ if (angle==null) angle = 0;
+ // initialize instance variables for references
+ this.col1 = new b2Vec2();
+ this.col2 = new b2Vec2();
+ //
+
+ if (c1!=null && c2!=null){
+ this.col1.SetV(c1);
+ this.col2.SetV(c2);
+ }
+ else{
+ var c = Math.cos(angle);
+ var s = Math.sin(angle);
+ this.col1.x = c; this.col2.x = -s;
+ this.col1.y = s; this.col2.y = c;
+ }
+ },
+
+ Set: function(angle)
+ {
+ var c = Math.cos(angle);
+ var s = Math.sin(angle);
+ this.col1.x = c; this.col2.x = -s;
+ this.col1.y = s; this.col2.y = c;
+ },
+
+ SetVV: function(c1, c2)
+ {
+ this.col1.SetV(c1);
+ this.col2.SetV(c2);
+ },
+
+ Copy: function(){
+ return new b2Mat22(0, this.col1, this.col2);
+ },
+
+ SetM: function(m)
+ {
+ this.col1.SetV(m.col1);
+ this.col2.SetV(m.col2);
+ },
+
+ AddM: function(m)
+ {
+ this.col1.x += m.col1.x;
+ this.col1.y += m.col1.y;
+ this.col2.x += m.col2.x;
+ this.col2.y += m.col2.y;
+ },
+
+ SetIdentity: function()
+ {
+ this.col1.x = 1.0; this.col2.x = 0.0;
+ this.col1.y = 0.0; this.col2.y = 1.0;
+ },
+
+ SetZero: function()
+ {
+ this.col1.x = 0.0; this.col2.x = 0.0;
+ this.col1.y = 0.0; this.col2.y = 0.0;
+ },
+
+ Invert: function(out)
+ {
+ var a = this.col1.x;
+ var b = this.col2.x;
+ var c = this.col1.y;
+ var d = this.col2.y;
+ //var B = new b2Mat22();
+ var det = a * d - b * c;
+ //b2Settings.b2Assert(det != 0.0);
+ det = 1.0 / det;
+ out.col1.x = det * d; out.col2.x = -det * b;
+ out.col1.y = -det * c; out.col2.y = det * a;
+ return out;
+ },
+
+ // this.Solve A * x = b
+ Solve: function(out, bX, bY)
+ {
+ //float32 a11 = this.col1.x, a12 = this.col2.x, a21 = this.col1.y, a22 = this.col2.y;
+ var a11 = this.col1.x;
+ var a12 = this.col2.x;
+ var a21 = this.col1.y;
+ var a22 = this.col2.y;
+ //float32 det = a11 * a22 - a12 * a21;
+ var det = a11 * a22 - a12 * a21;
+ //b2Settings.b2Assert(det != 0.0);
+ det = 1.0 / det;
+ out.x = det * (a22 * bX - a12 * bY);
+ out.y = det * (a11 * bY - a21 * bX);
+
+ return out;
+ },
+
+ Abs: function()
+ {
+ this.col1.Abs();
+ this.col2.Abs();
+ },
+
+ col1: new b2Vec2(),
+ col2: new b2Vec2()};
diff --git a/o3d/samples/box2d-3d/third_party/box2d/common/math/b2Math.js b/o3d/samples/box2d-3d/third_party/box2d/common/math/b2Math.js
new file mode 100644
index 0000000..85185bf
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/common/math/b2Math.js
@@ -0,0 +1,218 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+var b2Math = Class.create();
+b2Math.prototype = {
+
+
+ /*static public function b2InvSqrt(x)
+ {
+ float32 xhalf = 0.5f * x;
+ int32 i = *(int32*)&x;
+ i = 0x5f3759df - (i >> 1);
+ x = *(float32*)&i;
+ x = x * (1.5f - xhalf * x * x);
+ return x;
+ }*/
+
+
+
+
+
+
+
+
+
+
+
+ // A * B
+
+ // A^T * B
+
+
+
+
+
+
+
+
+
+
+
+ // b2Math.b2Random number in range [-1,1]
+
+ /*inline float32 b2Math.b2Random(float32 lo, float32 hi)
+ {
+ float32 r = (float32)rand();
+ r /= RAND_MAX;
+ r = (hi - lo) * r + lo;
+ return r;
+ }*/
+
+ // "Next Largest Power of 2
+ // Given a binary integer value x, the next largest power of 2 can be computed by a SWAR algorithm
+ // that recursively "folds" the upper bits into the lower bits. This process yields a bit vector with
+ // the same most significant 1, but all 1's below it. Adding 1 to that value yields the next
+ // largest power of 2. For a 32-bit value:"
+
+
+
+ // Temp vector functions to reduce calls to 'new'
+ /*static public var tempVec = new b2Vec2();
+
+
+ static public var tempAABB = new b2AABB(); */
+
+
+
+ initialize: function() {}}
+b2Math.b2IsValid = function(x)
+ {
+ return isFinite(x);
+ };
+b2Math.b2Dot = function(a, b)
+ {
+ return a.x * b.x + a.y * b.y;
+ };
+b2Math.b2CrossVV = function(a, b)
+ {
+ return a.x * b.y - a.y * b.x;
+ };
+b2Math.b2CrossVF = function(a, s)
+ {
+ var v = new b2Vec2(s * a.y, -s * a.x);
+ return v;
+ };
+b2Math.b2CrossFV = function(s, a)
+ {
+ var v = new b2Vec2(-s * a.y, s * a.x);
+ return v;
+ };
+b2Math.b2MulMV = function(A, v)
+ {
+ var u = new b2Vec2(A.col1.x * v.x + A.col2.x * v.y, A.col1.y * v.x + A.col2.y * v.y);
+ return u;
+ };
+b2Math.b2MulTMV = function(A, v)
+ {
+ var u = new b2Vec2(b2Math.b2Dot(v, A.col1), b2Math.b2Dot(v, A.col2));
+ return u;
+ };
+b2Math.AddVV = function(a, b)
+ {
+ var v = new b2Vec2(a.x + b.x, a.y + b.y);
+ return v;
+ };
+b2Math.SubtractVV = function(a, b)
+ {
+ var v = new b2Vec2(a.x - b.x, a.y - b.y);
+ return v;
+ };
+b2Math.MulFV = function(s, a)
+ {
+ var v = new b2Vec2(s * a.x, s * a.y);
+ return v;
+ };
+b2Math.AddMM = function(A, B)
+ {
+ var C = new b2Mat22(0, b2Math.AddVV(A.col1, B.col1), b2Math.AddVV(A.col2, B.col2));
+ return C;
+ };
+b2Math.b2MulMM = function(A, B)
+ {
+ var C = new b2Mat22(0, b2Math.b2MulMV(A, B.col1), b2Math.b2MulMV(A, B.col2));
+ return C;
+ };
+b2Math.b2MulTMM = function(A, B)
+ {
+ var c1 = new b2Vec2(b2Math.b2Dot(A.col1, B.col1), b2Math.b2Dot(A.col2, B.col1));
+ var c2 = new b2Vec2(b2Math.b2Dot(A.col1, B.col2), b2Math.b2Dot(A.col2, B.col2));
+ var C = new b2Mat22(0, c1, c2);
+ return C;
+ };
+b2Math.b2Abs = function(a)
+ {
+ return a > 0.0 ? a : -a;
+ };
+b2Math.b2AbsV = function(a)
+ {
+ var b = new b2Vec2(b2Math.b2Abs(a.x), b2Math.b2Abs(a.y));
+ return b;
+ };
+b2Math.b2AbsM = function(A)
+ {
+ var B = new b2Mat22(0, b2Math.b2AbsV(A.col1), b2Math.b2AbsV(A.col2));
+ return B;
+ };
+b2Math.b2Min = function(a, b)
+ {
+ return a < b ? a : b;
+ };
+b2Math.b2MinV = function(a, b)
+ {
+ var c = new b2Vec2(b2Math.b2Min(a.x, b.x), b2Math.b2Min(a.y, b.y));
+ return c;
+ };
+b2Math.b2Max = function(a, b)
+ {
+ return a > b ? a : b;
+ };
+b2Math.b2MaxV = function(a, b)
+ {
+ var c = new b2Vec2(b2Math.b2Max(a.x, b.x), b2Math.b2Max(a.y, b.y));
+ return c;
+ };
+b2Math.b2Clamp = function(a, low, high)
+ {
+ return b2Math.b2Max(low, b2Math.b2Min(a, high));
+ };
+b2Math.b2ClampV = function(a, low, high)
+ {
+ return b2Math.b2MaxV(low, b2Math.b2MinV(a, high));
+ };
+b2Math.b2Swap = function(a, b)
+ {
+ var tmp = a[0];
+ a[0] = b[0];
+ b[0] = tmp;
+ };
+b2Math.b2Random = function()
+ {
+ return Math.random() * 2 - 1;
+ };
+b2Math.b2NextPowerOfTwo = function(x)
+ {
+ x |= (x >> 1) & 0x7FFFFFFF;
+ x |= (x >> 2) & 0x3FFFFFFF;
+ x |= (x >> 4) & 0x0FFFFFFF;
+ x |= (x >> 8) & 0x00FFFFFF;
+ x |= (x >> 16)& 0x0000FFFF;
+ return x + 1;
+ };
+b2Math.b2IsPowerOfTwo = function(x)
+ {
+ var result = x > 0 && (x & (x - 1)) == 0;
+ return result;
+ };
+b2Math.tempVec2 = new b2Vec2();
+b2Math.tempVec3 = new b2Vec2();
+b2Math.tempVec4 = new b2Vec2();
+b2Math.tempVec5 = new b2Vec2();
+b2Math.tempMat = new b2Mat22();
diff --git a/o3d/samples/box2d-3d/third_party/box2d/common/math/b2Vec2.js b/o3d/samples/box2d-3d/third_party/box2d/common/math/b2Vec2.js
new file mode 100644
index 0000000..24acd20
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/common/math/b2Vec2.js
@@ -0,0 +1,131 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+// b2Vec2 has no constructor so that it
+// can be placed in a union.
+var b2Vec2 = Class.create();
+b2Vec2.prototype =
+{
+ initialize: function(x_, y_) {this.x=x_; this.y=y_;},
+
+ SetZero: function() { this.x = 0.0; this.y = 0.0; },
+ Set: function(x_, y_) {this.x=x_; this.y=y_;},
+ SetV: function(v) {this.x=v.x; this.y=v.y;},
+
+ Negative: function(){ return new b2Vec2(-this.x, -this.y); },
+
+
+ Copy: function(){
+ return new b2Vec2(this.x,this.y);
+ },
+
+ Add: function(v)
+ {
+ this.x += v.x; this.y += v.y;
+ },
+
+ Subtract: function(v)
+ {
+ this.x -= v.x; this.y -= v.y;
+ },
+
+ Multiply: function(a)
+ {
+ this.x *= a; this.y *= a;
+ },
+
+ MulM: function(A)
+ {
+ var tX = this.x;
+ this.x = A.col1.x * tX + A.col2.x * this.y;
+ this.y = A.col1.y * tX + A.col2.y * this.y;
+ },
+
+ MulTM: function(A)
+ {
+ var tX = b2Math.b2Dot(this, A.col1);
+ this.y = b2Math.b2Dot(this, A.col2);
+ this.x = tX;
+ },
+
+ CrossVF: function(s)
+ {
+ var tX = this.x;
+ this.x = s * this.y;
+ this.y = -s * tX;
+ },
+
+ CrossFV: function(s)
+ {
+ var tX = this.x;
+ this.x = -s * this.y;
+ this.y = s * tX;
+ },
+
+ MinV: function(b)
+ {
+ this.x = this.x < b.x ? this.x : b.x;
+ this.y = this.y < b.y ? this.y : b.y;
+ },
+
+ MaxV: function(b)
+ {
+ this.x = this.x > b.x ? this.x : b.x;
+ this.y = this.y > b.y ? this.y : b.y;
+ },
+
+ Abs: function()
+ {
+ this.x = Math.abs(this.x);
+ this.y = Math.abs(this.y);
+ },
+
+ Length: function()
+ {
+ return Math.sqrt(this.x * this.x + this.y * this.y);
+ },
+
+ Normalize: function()
+ {
+ var length = this.Length();
+ if (length < Number.MIN_VALUE)
+ {
+ return 0.0;
+ }
+ var invLength = 1.0 / length;
+ this.x *= invLength;
+ this.y *= invLength;
+
+ return length;
+ },
+
+ IsValid: function()
+ {
+ return b2Math.b2IsValid(this.x) && b2Math.b2IsValid(this.y);
+ },
+
+ x: null,
+ y: null};
+b2Vec2.Make = function(x_, y_)
+ {
+ return new b2Vec2(x_, y_);
+ };
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2Body.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2Body.js
new file mode 100644
index 0000000..6716d2f
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2Body.js
@@ -0,0 +1,469 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+
+// A rigid body. Internal computation are done in terms
+// of the center of mass position. The center of mass may
+// be offset from the body's origin.
+var b2Body = Class.create();
+b2Body.prototype =
+{
+ // Set the position of the body's origin and rotation (radians).
+ // This breaks any contacts and wakes the other bodies.
+ SetOriginPosition: function(position, rotation){
+ if (this.IsFrozen())
+ {
+ return;
+ }
+
+ this.m_rotation = rotation;
+ this.m_R.Set(this.m_rotation);
+ this.m_position = b2Math.AddVV(position , b2Math.b2MulMV(this.m_R, this.m_center));
+
+ this.m_position0.SetV(this.m_position);
+ this.m_rotation0 = this.m_rotation;
+
+ for (var s = this.m_shapeList; s != null; s = s.m_next)
+ {
+ s.Synchronize(this.m_position, this.m_R, this.m_position, this.m_R);
+ }
+
+ this.m_world.m_broadPhase.Commit();
+ },
+
+ // Get the position of the body's origin. The body's origin does not
+ // necessarily coincide with the center of mass. It depends on how the
+ // shapes are created.
+ GetOriginPosition: function(){
+ return b2Math.SubtractVV(this.m_position, b2Math.b2MulMV(this.m_R, this.m_center));
+ },
+
+ // Set the position of the body's center of mass and rotation (radians).
+ // This breaks any contacts and wakes the other bodies.
+ SetCenterPosition: function(position, rotation){
+ if (this.IsFrozen())
+ {
+ return;
+ }
+
+ this.m_rotation = rotation;
+ this.m_R.Set(this.m_rotation);
+ this.m_position.SetV( position );
+
+ this.m_position0.SetV(this.m_position);
+ this.m_rotation0 = this.m_rotation;
+
+ for (var s = this.m_shapeList; s != null; s = s.m_next)
+ {
+ s.Synchronize(this.m_position, this.m_R, this.m_position, this.m_R);
+ }
+
+ this.m_world.m_broadPhase.Commit();
+ },
+
+ // Get the position of the body's center of mass. The body's center of mass
+ // does not necessarily coincide with the body's origin. It depends on how the
+ // shapes are created.
+ GetCenterPosition: function(){
+ return this.m_position;
+ },
+
+ // Get the rotation in radians.
+ GetRotation: function(){
+ return this.m_rotation;
+ },
+
+ GetRotationMatrix: function(){
+ return this.m_R;
+ },
+
+ // Set/Get the linear velocity of the center of mass.
+ SetLinearVelocity: function(v){
+ this.m_linearVelocity.SetV(v);
+ },
+ GetLinearVelocity: function(){
+ return this.m_linearVelocity;
+ },
+
+ // Set/Get the angular velocity.
+ SetAngularVelocity: function(w){
+ this.m_angularVelocity = w;
+ },
+ GetAngularVelocity: function(){
+ return this.m_angularVelocity;
+ },
+
+ // Apply a force at a world point. Additive.
+ ApplyForce: function(force, point)
+ {
+ if (this.IsSleeping() == false)
+ {
+ this.m_force.Add( force );
+ this.m_torque += b2Math.b2CrossVV(b2Math.SubtractVV(point, this.m_position), force);
+ }
+ },
+
+ // Apply a torque. Additive.
+ ApplyTorque: function(torque)
+ {
+ if (this.IsSleeping() == false)
+ {
+ this.m_torque += torque;
+ }
+ },
+
+ // Apply an impulse at a point. This immediately modifies the velocity.
+ ApplyImpulse: function(impulse, point)
+ {
+ if (this.IsSleeping() == false)
+ {
+ this.m_linearVelocity.Add( b2Math.MulFV(this.m_invMass, impulse) );
+ this.m_angularVelocity += ( this.m_invI * b2Math.b2CrossVV( b2Math.SubtractVV(point, this.m_position), impulse) );
+ }
+ },
+
+ GetMass: function(){
+ return this.m_mass;
+ },
+
+ GetInertia: function(){
+ return this.m_I;
+ },
+
+ // Get the world coordinates of a point give the local coordinates
+ // relative to the body's center of mass.
+ GetWorldPoint: function(localPoint){
+ return b2Math.AddVV(this.m_position , b2Math.b2MulMV(this.m_R, localPoint));
+ },
+
+ // Get the world coordinates of a vector given the local coordinates.
+ GetWorldVector: function(localVector){
+ return b2Math.b2MulMV(this.m_R, localVector);
+ },
+
+ // Returns a local point relative to the center of mass given a world point.
+ GetLocalPoint: function(worldPoint){
+ return b2Math.b2MulTMV(this.m_R, b2Math.SubtractVV(worldPoint, this.m_position));
+ },
+
+ // Returns a local vector given a world vector.
+ GetLocalVector: function(worldVector){
+ return b2Math.b2MulTMV(this.m_R, worldVector);
+ },
+
+ // Is this body static (immovable)?
+ IsStatic: function(){
+ return (this.m_flags & b2Body.e_staticFlag) == b2Body.e_staticFlag;
+ },
+
+ IsFrozen: function()
+ {
+ return (this.m_flags & b2Body.e_frozenFlag) == b2Body.e_frozenFlag;
+ },
+
+ // Is this body sleeping (not simulating).
+ IsSleeping: function(){
+ return (this.m_flags & b2Body.e_sleepFlag) == b2Body.e_sleepFlag;
+ },
+
+ // You can disable sleeping on this particular body.
+ AllowSleeping: function(flag)
+ {
+ if (flag)
+ {
+ this.m_flags |= b2Body.e_allowSleepFlag;
+ }
+ else
+ {
+ this.m_flags &= ~b2Body.e_allowSleepFlag;
+ this.WakeUp();
+ }
+ },
+
+ // Wake up this body so it will begin simulating.
+ WakeUp: function(){
+ this.m_flags &= ~b2Body.e_sleepFlag;
+ this.m_sleepTime = 0.0;
+ },
+
+ // Get the list of all shapes attached to this body.
+ GetShapeList: function(){
+ return this.m_shapeList;
+ },
+
+ GetContactList: function()
+ {
+ return this.m_contactList;
+ },
+
+ GetJointList: function()
+ {
+ return this.m_jointList;
+ },
+
+ // Get the next body in the world's body list.
+ GetNext: function(){
+ return this.m_next;
+ },
+
+ GetUserData: function(){
+ return this.m_userData;
+ },
+
+ //--------------- Internals Below -------------------
+
+ initialize: function(bd, world){
+ // initialize instance variables for references
+ this.sMat0 = new b2Mat22();
+ this.m_position = new b2Vec2();
+ this.m_R = new b2Mat22(0);
+ this.m_position0 = new b2Vec2();
+ //
+
+ var i = 0;
+ var sd;
+ var massData;
+
+ this.m_flags = 0;
+ this.m_position.SetV( bd.position );
+ this.m_rotation = bd.rotation;
+ this.m_R.Set(this.m_rotation);
+ this.m_position0.SetV(this.m_position);
+ this.m_rotation0 = this.m_rotation;
+ this.m_world = world;
+
+ this.m_linearDamping = b2Math.b2Clamp(1.0 - bd.linearDamping, 0.0, 1.0);
+ this.m_angularDamping = b2Math.b2Clamp(1.0 - bd.angularDamping, 0.0, 1.0);
+
+ this.m_force = new b2Vec2(0.0, 0.0);
+ this.m_torque = 0.0;
+
+ this.m_mass = 0.0;
+
+ var massDatas = new Array(b2Settings.b2_maxShapesPerBody);
+ for (i = 0; i < b2Settings.b2_maxShapesPerBody; i++){
+ massDatas[i] = new b2MassData();
+ }
+
+ // Compute the shape mass properties, the bodies total mass and COM.
+ this.m_shapeCount = 0;
+ this.m_center = new b2Vec2(0.0, 0.0);
+ for (i = 0; i < b2Settings.b2_maxShapesPerBody; ++i)
+ {
+ sd = bd.shapes[i];
+ if (sd == null) break;
+ massData = massDatas[ i ];
+ sd.ComputeMass(massData);
+ this.m_mass += massData.mass;
+ //this.m_center += massData->mass * (sd->localPosition + massData->center);
+ this.m_center.x += massData.mass * (sd.localPosition.x + massData.center.x);
+ this.m_center.y += massData.mass * (sd.localPosition.y + massData.center.y);
+ ++this.m_shapeCount;
+ }
+
+ // Compute center of mass, and shift the origin to the COM.
+ if (this.m_mass > 0.0)
+ {
+ this.m_center.Multiply( 1.0 / this.m_mass );
+ this.m_position.Add( b2Math.b2MulMV(this.m_R, this.m_center) );
+ }
+ else
+ {
+ this.m_flags |= b2Body.e_staticFlag;
+ }
+
+ // Compute the moment of inertia.
+ this.m_I = 0.0;
+ for (i = 0; i < this.m_shapeCount; ++i)
+ {
+ sd = bd.shapes[i];
+ massData = massDatas[ i ];
+ this.m_I += massData.I;
+ var r = b2Math.SubtractVV( b2Math.AddVV(sd.localPosition, massData.center), this.m_center );
+ this.m_I += massData.mass * b2Math.b2Dot(r, r);
+ }
+
+ if (this.m_mass > 0.0)
+ {
+ this.m_invMass = 1.0 / this.m_mass;
+ }
+ else
+ {
+ this.m_invMass = 0.0;
+ }
+
+ if (this.m_I > 0.0 && bd.preventRotation == false)
+ {
+ this.m_invI = 1.0 / this.m_I;
+ }
+ else
+ {
+ this.m_I = 0.0;
+ this.m_invI = 0.0;
+ }
+
+ // Compute the center of mass velocity.
+ this.m_linearVelocity = b2Math.AddVV(bd.linearVelocity, b2Math.b2CrossFV(bd.angularVelocity, this.m_center));
+ this.m_angularVelocity = bd.angularVelocity;
+
+ this.m_jointList = null;
+ this.m_contactList = null;
+ this.m_prev = null;
+ this.m_next = null;
+
+ // Create the shapes.
+ this.m_shapeList = null;
+ for (i = 0; i < this.m_shapeCount; ++i)
+ {
+ sd = bd.shapes[i];
+ var shape = b2Shape.Create(sd, this, this.m_center);
+ shape.m_next = this.m_shapeList;
+ this.m_shapeList = shape;
+ }
+
+ this.m_sleepTime = 0.0;
+ if (bd.allowSleep)
+ {
+ this.m_flags |= b2Body.e_allowSleepFlag;
+ }
+ if (bd.isSleeping)
+ {
+ this.m_flags |= b2Body.e_sleepFlag;
+ }
+
+ if ((this.m_flags & b2Body.e_sleepFlag) || this.m_invMass == 0.0)
+ {
+ this.m_linearVelocity.Set(0.0, 0.0);
+ this.m_angularVelocity = 0.0;
+ }
+
+ this.m_userData = bd.userData;
+ },
+ // does not support destructors
+ /*~b2Body(){
+ b2Shape* s = this.m_shapeList;
+ while (s)
+ {
+ b2Shape* s0 = s;
+ s = s->this.m_next;
+
+ b2Shape::this.Destroy(s0);
+ }
+ }*/
+
+ Destroy: function(){
+ var s = this.m_shapeList;
+ while (s)
+ {
+ var s0 = s;
+ s = s.m_next;
+
+ b2Shape.Destroy(s0);
+ }
+ },
+
+ // Temp mat
+ sMat0: new b2Mat22(),
+ SynchronizeShapes: function(){
+ //b2Mat22 R0(this.m_rotation0);
+ this.sMat0.Set(this.m_rotation0);
+ for (var s = this.m_shapeList; s != null; s = s.m_next)
+ {
+ s.Synchronize(this.m_position0, this.sMat0, this.m_position, this.m_R);
+ }
+ },
+
+ QuickSyncShapes: function(){
+ for (var s = this.m_shapeList; s != null; s = s.m_next)
+ {
+ s.QuickSync(this.m_position, this.m_R);
+ }
+ },
+
+ // This is used to prevent connected bodies from colliding.
+ // It may lie, depending on the collideConnected flag.
+ IsConnected: function(other){
+ for (var jn = this.m_jointList; jn != null; jn = jn.next)
+ {
+ if (jn.other == other)
+ return jn.joint.m_collideConnected == false;
+ }
+
+ return false;
+ },
+
+ Freeze: function(){
+ this.m_flags |= b2Body.e_frozenFlag;
+ this.m_linearVelocity.SetZero();
+ this.m_angularVelocity = 0.0;
+
+ for (var s = this.m_shapeList; s != null; s = s.m_next)
+ {
+ s.DestroyProxy();
+ }
+ },
+
+ m_flags: 0,
+
+ m_position: new b2Vec2(),
+ m_rotation: null,
+ m_R: new b2Mat22(0),
+
+ // Conservative advancement data.
+ m_position0: new b2Vec2(),
+ m_rotation0: null,
+
+ m_linearVelocity: null,
+ m_angularVelocity: null,
+
+ m_force: null,
+ m_torque: null,
+
+ m_center: null,
+
+ m_world: null,
+ m_prev: null,
+ m_next: null,
+
+ m_shapeList: null,
+ m_shapeCount: 0,
+
+ m_jointList: null,
+ m_contactList: null,
+
+ m_mass: null,
+ m_invMass: null,
+ m_I: null,
+ m_invI: null,
+
+ m_linearDamping: null,
+ m_angularDamping: null,
+
+ m_sleepTime: null,
+
+ m_userData: null};
+b2Body.e_staticFlag = 0x0001;
+b2Body.e_frozenFlag = 0x0002;
+b2Body.e_islandFlag = 0x0004;
+b2Body.e_sleepFlag = 0x0008;
+b2Body.e_allowSleepFlag = 0x0010;
+b2Body.e_destroyFlag = 0x0020;
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2BodyDef.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2BodyDef.js
new file mode 100644
index 0000000..c586b37
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2BodyDef.js
@@ -0,0 +1,69 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+var b2BodyDef = Class.create();
+b2BodyDef.prototype =
+{
+ initialize: function()
+ {
+ // initialize instance variables for references
+ this.shapes = new Array();
+ //
+
+ this.userData = null;
+ for (var i = 0; i < b2Settings.b2_maxShapesPerBody; i++){
+ this.shapes[i] = null;
+ }
+ this.position = new b2Vec2(0.0, 0.0);
+ this.rotation = 0.0;
+ this.linearVelocity = new b2Vec2(0.0, 0.0);
+ this.angularVelocity = 0.0;
+ this.linearDamping = 0.0;
+ this.angularDamping = 0.0;
+ this.allowSleep = true;
+ this.isSleeping = false;
+ this.preventRotation = false;
+ },
+
+ userData: null,
+ shapes: new Array(),
+ position: null,
+ rotation: null,
+ linearVelocity: null,
+ angularVelocity: null,
+ linearDamping: null,
+ angularDamping: null,
+ allowSleep: null,
+ isSleeping: null,
+ preventRotation: null,
+
+ AddShape: function(shape)
+ {
+ for (var i = 0; i < b2Settings.b2_maxShapesPerBody; ++i)
+ {
+ if (this.shapes[i] == null)
+ {
+ this.shapes[i] = shape;
+ break;
+ }
+ }
+ }};
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2CollisionFilter.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2CollisionFilter.js
new file mode 100644
index 0000000..6724f25
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2CollisionFilter.js
@@ -0,0 +1,42 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+
+
+var b2CollisionFilter = Class.create();
+b2CollisionFilter.prototype =
+{
+
+ // Return true if contact calculations should be performed between these two shapes.
+ ShouldCollide: function(shape1, shape2){
+ if (shape1.m_groupIndex == shape2.m_groupIndex && shape1.m_groupIndex != 0)
+ {
+ return shape1.m_groupIndex > 0;
+ }
+
+ var collide = (shape1.m_maskBits & shape2.m_categoryBits) != 0 && (shape1.m_categoryBits & shape2.m_maskBits) != 0;
+ return collide;
+ },
+
+
+ initialize: function() {}};
+b2CollisionFilter.b2_defaultFilter = new b2CollisionFilter;
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2ContactManager.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2ContactManager.js
new file mode 100644
index 0000000..cfb2df7
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2ContactManager.js
@@ -0,0 +1,337 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+var b2ContactManager = Class.create();
+Object.extend(b2ContactManager.prototype, b2PairCallback.prototype);
+Object.extend(b2ContactManager.prototype,
+{
+ initialize: function(){
+ // The constructor for b2PairCallback
+ //
+
+ // initialize instance variables for references
+ this.m_nullContact = new b2NullContact();
+ //
+
+ this.m_world = null;
+ this.m_destroyImmediate = false;
+ },
+
+ // This is a callback from the broadphase when two AABB proxies begin
+ // to overlap. We create a b2Contact to manage the narrow phase.
+ PairAdded: function(proxyUserData1, proxyUserData2){
+ var shape1 = proxyUserData1;
+ var shape2 = proxyUserData2;
+
+ var body1 = shape1.m_body;
+ var body2 = shape2.m_body;
+
+ if (body1.IsStatic() && body2.IsStatic())
+ {
+ return this.m_nullContact;
+ }
+
+ if (shape1.m_body == shape2.m_body)
+ {
+ return this.m_nullContact;
+ }
+
+ if (body2.IsConnected(body1))
+ {
+ return this.m_nullContact;
+ }
+
+ if (this.m_world.m_filter != null && this.m_world.m_filter.ShouldCollide(shape1, shape2) == false)
+ {
+ return this.m_nullContact;
+ }
+
+ // Ensure that body2 is dynamic (body1 is static or dynamic).
+ if (body2.m_invMass == 0.0)
+ {
+ var tempShape = shape1;
+ shape1 = shape2;
+ shape2 = tempShape;
+ //b2Math.b2Swap(shape1, shape2);
+ var tempBody = body1;
+ body1 = body2;
+ body2 = tempBody;
+ //b2Math.b2Swap(body1, body2);
+ }
+
+ // Call the factory.
+ var contact = b2Contact.Create(shape1, shape2, this.m_world.m_blockAllocator);
+
+ if (contact == null)
+ {
+ return this.m_nullContact;
+ }
+ else
+ {
+ // Insert into the world.
+ contact.m_prev = null;
+ contact.m_next = this.m_world.m_contactList;
+ if (this.m_world.m_contactList != null)
+ {
+ this.m_world.m_contactList.m_prev = contact;
+ }
+ this.m_world.m_contactList = contact;
+ this.m_world.m_contactCount++;
+ }
+
+ return contact;
+ },
+
+ // This is a callback from the broadphase when two AABB proxies cease
+ // to overlap. We destroy the b2Contact.
+ PairRemoved: function(proxyUserData1, proxyUserData2, pairUserData){
+
+ if (pairUserData == null)
+ {
+ return;
+ }
+
+ var c = pairUserData;
+ if (c != this.m_nullContact)
+ {
+ //b2Settings.b2Assert(this.m_world.m_contactCount > 0);
+ if (this.m_destroyImmediate == true)
+ {
+ this.DestroyContact(c);
+ c = null;
+ }
+ else
+ {
+ c.m_flags |= b2Contact.e_destroyFlag;
+ }
+ }
+ },
+
+ DestroyContact: function(c)
+ {
+
+ //b2Settings.b2Assert(this.m_world.m_contactCount > 0);
+
+ // Remove from the world.
+ if (c.m_prev)
+ {
+ c.m_prev.m_next = c.m_next;
+ }
+
+ if (c.m_next)
+ {
+ c.m_next.m_prev = c.m_prev;
+ }
+
+ if (c == this.m_world.m_contactList)
+ {
+ this.m_world.m_contactList = c.m_next;
+ }
+
+ // If there are contact points, then disconnect from the island graph.
+ if (c.GetManifoldCount() > 0)
+ {
+ var body1 = c.m_shape1.m_body;
+ var body2 = c.m_shape2.m_body;
+ var node1 = c.m_node1;
+ var node2 = c.m_node2;
+
+ // Wake up touching bodies.
+ body1.WakeUp();
+ body2.WakeUp();
+
+ // Remove from body 1
+ if (node1.prev)
+ {
+ node1.prev.next = node1.next;
+ }
+
+ if (node1.next)
+ {
+ node1.next.prev = node1.prev;
+ }
+
+ if (node1 == body1.m_contactList)
+ {
+ body1.m_contactList = node1.next;
+ }
+
+ node1.prev = null;
+ node1.next = null;
+
+ // Remove from body 2
+ if (node2.prev)
+ {
+ node2.prev.next = node2.next;
+ }
+
+ if (node2.next)
+ {
+ node2.next.prev = node2.prev;
+ }
+
+ if (node2 == body2.m_contactList)
+ {
+ body2.m_contactList = node2.next;
+ }
+
+ node2.prev = null;
+ node2.next = null;
+ }
+
+ // Call the factory.
+ b2Contact.Destroy(c, this.m_world.m_blockAllocator);
+ --this.m_world.m_contactCount;
+ },
+
+
+ // Destroy any contacts marked for deferred destruction.
+ CleanContactList: function()
+ {
+ var c = this.m_world.m_contactList;
+ while (c != null)
+ {
+ var c0 = c;
+ c = c.m_next;
+
+ if (c0.m_flags & b2Contact.e_destroyFlag)
+ {
+ this.DestroyContact(c0);
+ c0 = null;
+ }
+ }
+ },
+
+
+ // This is the top level collision call for the time step. Here
+ // all the narrow phase collision is processed for the world
+ // contact list.
+ Collide: function()
+ {
+ var body1;
+ var body2;
+ var node1;
+ var node2;
+
+ for (var c = this.m_world.m_contactList; c != null; c = c.m_next)
+ {
+ if (c.m_shape1.m_body.IsSleeping() &&
+ c.m_shape2.m_body.IsSleeping())
+ {
+ continue;
+ }
+
+ var oldCount = c.GetManifoldCount();
+ c.Evaluate();
+
+ var newCount = c.GetManifoldCount();
+
+ if (oldCount == 0 && newCount > 0)
+ {
+ //b2Settings.b2Assert(c.GetManifolds().pointCount > 0);
+
+ // Connect to island graph.
+ body1 = c.m_shape1.m_body;
+ body2 = c.m_shape2.m_body;
+ node1 = c.m_node1;
+ node2 = c.m_node2;
+
+ // Connect to body 1
+ node1.contact = c;
+ node1.other = body2;
+
+ node1.prev = null;
+ node1.next = body1.m_contactList;
+ if (node1.next != null)
+ {
+ node1.next.prev = c.m_node1;
+ }
+ body1.m_contactList = c.m_node1;
+
+ // Connect to body 2
+ node2.contact = c;
+ node2.other = body1;
+
+ node2.prev = null;
+ node2.next = body2.m_contactList;
+ if (node2.next != null)
+ {
+ node2.next.prev = node2;
+ }
+ body2.m_contactList = node2;
+ }
+ else if (oldCount > 0 && newCount == 0)
+ {
+ // Disconnect from island graph.
+ body1 = c.m_shape1.m_body;
+ body2 = c.m_shape2.m_body;
+ node1 = c.m_node1;
+ node2 = c.m_node2;
+
+ // Remove from body 1
+ if (node1.prev)
+ {
+ node1.prev.next = node1.next;
+ }
+
+ if (node1.next)
+ {
+ node1.next.prev = node1.prev;
+ }
+
+ if (node1 == body1.m_contactList)
+ {
+ body1.m_contactList = node1.next;
+ }
+
+ node1.prev = null;
+ node1.next = null;
+
+ // Remove from body 2
+ if (node2.prev)
+ {
+ node2.prev.next = node2.next;
+ }
+
+ if (node2.next)
+ {
+ node2.next.prev = node2.prev;
+ }
+
+ if (node2 == body2.m_contactList)
+ {
+ body2.m_contactList = node2.next;
+ }
+
+ node2.prev = null;
+ node2.next = null;
+ }
+ }
+ },
+
+ m_world: null,
+
+ // This lets us provide broadphase proxy pair user data for
+ // contacts that shouldn't exist.
+ m_nullContact: new b2NullContact(),
+ m_destroyImmediate: null});
+
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2Island.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2Island.js
new file mode 100644
index 0000000..ee15d71
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2Island.js
@@ -0,0 +1,331 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+/*
+Position Correction Notes
+=========================
+I tried the several algorithms for position correction of the 2D revolute joint.
+I looked at these systems:
+- simple pendulum (1m diameter sphere on massless 5m stick) with initial angular velocity of 100 rad/s.
+- suspension bridge with 30 1m long planks of length 1m.
+- multi-link chain with 30 1m long links.
+
+Here are the algorithms:
+
+Baumgarte - A fraction of the position error is added to the velocity error. There is no
+separate position solver.
+
+Pseudo Velocities - After the velocity solver and position integration,
+the position error, Jacobian, and effective mass are recomputed. Then
+the velocity constraints are solved with pseudo velocities and a fraction
+of the position error is added to the pseudo velocity error. The pseudo
+velocities are initialized to zero and there is no warm-starting. After
+the position solver, the pseudo velocities are added to the positions.
+This is also called the First Order World method or the Position LCP method.
+
+Modified Nonlinear Gauss-Seidel (NGS) - Like Pseudo Velocities except the
+position error is re-computed for each constraint and the positions are updated
+after the constraint is solved. The radius vectors (aka Jacobians) are
+re-computed too (otherwise the algorithm has horrible instability). The pseudo
+velocity states are not needed because they are effectively zero at the beginning
+of each iteration. Since we have the current position error, we allow the
+iterations to terminate early if the error becomes smaller than b2_linearSlop.
+
+Full NGS or just NGS - Like Modified NGS except the effective mass are re-computed
+each time a constraint is solved.
+
+Here are the results:
+Baumgarte - this is the cheapest algorithm but it has some stability problems,
+especially with the bridge. The chain links separate easily close to the root
+and they jitter struggle to pull together. This is one of the most common
+methods in the field. The big drawback is that the position correction artificially
+affects the momentum, thus leading to instabilities and false bounce. I used a
+bias factor of 0.2. A larger bias factor makes the bridge less stable, a smaller
+factor makes joints and contacts more spongy.
+
+Pseudo Velocities - the is more stable than the Baumgarte method. The bridge is
+stable. However, joints still separate with large angular velocities. Drag the
+simple pendulum in a circle quickly and the joint will separate. The chain separates
+easily and does not recover. I used a bias factor of 0.2. A larger value lead to
+the bridge collapsing when a heavy cube drops on it.
+
+Modified NGS - this algorithm is better in some ways than Baumgarte and Pseudo
+Velocities, but in other ways it is worse. The bridge and chain are much more
+stable, but the simple pendulum goes unstable at high angular velocities.
+
+Full NGS - stable in all tests. The joints display good stiffness. The bridge
+still sags, but this is better than infinite forces.
+
+Recommendations
+Pseudo Velocities are not really worthwhile because the bridge and chain cannot
+recover from joint separation. In other cases the benefit over Baumgarte is small.
+
+Modified NGS is not a robust method for the revolute joint due to the violent
+instability seen in the simple pendulum. Perhaps it is viable with other constraint
+types, especially scalar constraints where the effective mass is a scalar.
+
+This leaves Baumgarte and Full NGS. Baumgarte has small, but manageable instabilities
+and is very fast. I don't think we can escape Baumgarte, especially in highly
+demanding cases where high constraint fidelity is not needed.
+
+Full NGS is robust and easy on the eyes. I recommend this option for
+higher fidelity simulation and certainly for suspension bridges and long chains.
+Full NGS might be a good choice for ragdolls, especially motorized ragdolls where
+joint separation can be problematic. The number of NGS iterations can be reduced
+for better performance without harming robustness much.
+
+Each joint in a can be handled differently in the position solver. So I recommend
+a system where the user can select the algorithm on a per joint basis. I would
+probably default to the slower Full NGS and let the user select the faster
+Baumgarte method in performance critical scenarios.
+*/
+
+
+var b2Island = Class.create();
+b2Island.prototype =
+{
+ initialize: function(bodyCapacity, contactCapacity, jointCapacity, allocator)
+ {
+ var i = 0;
+
+ this.m_bodyCapacity = bodyCapacity;
+ this.m_contactCapacity = contactCapacity;
+ this.m_jointCapacity = jointCapacity;
+ this.m_bodyCount = 0;
+ this.m_contactCount = 0;
+ this.m_jointCount = 0;
+
+
+ //this.m_bodies = (b2Body**)allocator->Allocate(bodyCapacity * sizeof(b2Body*));
+ this.m_bodies = new Array(bodyCapacity);
+ for (i = 0; i < bodyCapacity; i++)
+ this.m_bodies[i] = null;
+
+ //this.m_contacts = (b2Contact**)allocator->Allocate(contactCapacity * sizeof(b2Contact*));
+ this.m_contacts = new Array(contactCapacity);
+ for (i = 0; i < contactCapacity; i++)
+ this.m_contacts[i] = null;
+
+ //this.m_joints = (b2Joint**)allocator->Allocate(jointCapacity * sizeof(b2Joint*));
+ this.m_joints = new Array(jointCapacity);
+ for (i = 0; i < jointCapacity; i++)
+ this.m_joints[i] = null;
+
+ this.m_allocator = allocator;
+ },
+ //~b2Island();
+
+ Clear: function()
+ {
+ this.m_bodyCount = 0;
+ this.m_contactCount = 0;
+ this.m_jointCount = 0;
+ },
+
+ Solve: function(step, gravity)
+ {
+ var i = 0;
+ var b;
+
+ for (i = 0; i < this.m_bodyCount; ++i)
+ {
+ b = this.m_bodies[i];
+
+ if (b.m_invMass == 0.0)
+ continue;
+
+ b.m_linearVelocity.Add( b2Math.MulFV (step.dt, b2Math.AddVV(gravity, b2Math.MulFV( b.m_invMass, b.m_force ) ) ) );
+ b.m_angularVelocity += step.dt * b.m_invI * b.m_torque;
+
+ //b.m_linearVelocity *= b.m_linearDamping;
+ b.m_linearVelocity.Multiply(b.m_linearDamping);
+ b.m_angularVelocity *= b.m_angularDamping;
+
+ // Store positions for conservative advancement.
+ b.m_position0.SetV(b.m_position);
+ b.m_rotation0 = b.m_rotation;
+ }
+
+ var contactSolver = new b2ContactSolver(this.m_contacts, this.m_contactCount, this.m_allocator);
+
+ // Pre-solve
+ contactSolver.PreSolve();
+
+ for (i = 0; i < this.m_jointCount; ++i)
+ {
+ this.m_joints[i].PrepareVelocitySolver();
+ }
+
+ // this.Solve velocity constraints.
+ for (i = 0; i < step.iterations; ++i)
+ {
+ contactSolver.SolveVelocityConstraints();
+
+ for (var j = 0; j < this.m_jointCount; ++j)
+ {
+ this.m_joints[j].SolveVelocityConstraints(step);
+ }
+ }
+
+ // Integrate positions.
+ for (i = 0; i < this.m_bodyCount; ++i)
+ {
+ b = this.m_bodies[i];
+
+ if (b.m_invMass == 0.0)
+ continue;
+
+ //b.m_position.Add( b2Math.MulFV (step.dt, b.m_linearVelocity) );
+ b.m_position.x += step.dt * b.m_linearVelocity.x;
+ b.m_position.y += step.dt * b.m_linearVelocity.y;
+ b.m_rotation += step.dt * b.m_angularVelocity;
+
+ b.m_R.Set(b.m_rotation);
+ }
+
+ for (i = 0; i < this.m_jointCount; ++i)
+ {
+ this.m_joints[i].PreparePositionSolver();
+ }
+
+ // this.Solve position constraints.
+ if (b2World.s_enablePositionCorrection)
+ {
+ for (b2Island.m_positionIterationCount = 0; b2Island.m_positionIterationCount < step.iterations; ++b2Island.m_positionIterationCount)
+ {
+ var contactsOkay = contactSolver.SolvePositionConstraints(b2Settings.b2_contactBaumgarte);
+
+ var jointsOkay = true;
+ for (i = 0; i < this.m_jointCount; ++i)
+ {
+ var jointOkay = this.m_joints[i].SolvePositionConstraints();
+ jointsOkay = jointsOkay && jointOkay;
+ }
+
+ if (contactsOkay && jointsOkay)
+ {
+ break;
+ }
+ }
+ }
+
+ // Post-solve.
+ contactSolver.PostSolve();
+
+ // Synchronize shapes and reset forces.
+ for (i = 0; i < this.m_bodyCount; ++i)
+ {
+ b = this.m_bodies[i];
+
+ if (b.m_invMass == 0.0)
+ continue;
+
+ b.m_R.Set(b.m_rotation);
+
+ b.SynchronizeShapes();
+ b.m_force.Set(0.0, 0.0);
+ b.m_torque = 0.0;
+ }
+ },
+
+ UpdateSleep: function(dt)
+ {
+ var i = 0;
+ var b;
+
+ var minSleepTime = Number.MAX_VALUE;
+
+ var linTolSqr = b2Settings.b2_linearSleepTolerance * b2Settings.b2_linearSleepTolerance;
+ var angTolSqr = b2Settings.b2_angularSleepTolerance * b2Settings.b2_angularSleepTolerance;
+
+ for (i = 0; i < this.m_bodyCount; ++i)
+ {
+ b = this.m_bodies[i];
+ if (b.m_invMass == 0.0)
+ {
+ continue;
+ }
+
+ if ((b.m_flags & b2Body.e_allowSleepFlag) == 0)
+ {
+ b.m_sleepTime = 0.0;
+ minSleepTime = 0.0;
+ }
+
+ if ((b.m_flags & b2Body.e_allowSleepFlag) == 0 ||
+ b.m_angularVelocity * b.m_angularVelocity > angTolSqr ||
+ b2Math.b2Dot(b.m_linearVelocity, b.m_linearVelocity) > linTolSqr)
+ {
+ b.m_sleepTime = 0.0;
+ minSleepTime = 0.0;
+ }
+ else
+ {
+ b.m_sleepTime += dt;
+ minSleepTime = b2Math.b2Min(minSleepTime, b.m_sleepTime);
+ }
+ }
+
+ if (minSleepTime >= b2Settings.b2_timeToSleep)
+ {
+ for (i = 0; i < this.m_bodyCount; ++i)
+ {
+ b = this.m_bodies[i];
+ b.m_flags |= b2Body.e_sleepFlag;
+ }
+ }
+ },
+
+ AddBody: function(body)
+ {
+ //b2Settings.b2Assert(this.m_bodyCount < this.m_bodyCapacity);
+ this.m_bodies[this.m_bodyCount++] = body;
+ },
+
+ AddContact: function(contact)
+ {
+ //b2Settings.b2Assert(this.m_contactCount < this.m_contactCapacity);
+ this.m_contacts[this.m_contactCount++] = contact;
+ },
+
+ AddJoint: function(joint)
+ {
+ //b2Settings.b2Assert(this.m_jointCount < this.m_jointCapacity);
+ this.m_joints[this.m_jointCount++] = joint;
+ },
+
+ m_allocator: null,
+
+ m_bodies: null,
+ m_contacts: null,
+ m_joints: null,
+
+ m_bodyCount: 0,
+ m_jointCount: 0,
+ m_contactCount: 0,
+
+ m_bodyCapacity: 0,
+ m_contactCapacity: 0,
+ m_jointCapacity: 0,
+
+ m_positionError: null};
+b2Island.m_positionIterationCount = 0;
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2TimeStep.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2TimeStep.js
new file mode 100644
index 0000000..599390ed
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2TimeStep.js
@@ -0,0 +1,27 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+var b2TimeStep = Class.create();
+b2TimeStep.prototype =
+{
+ dt: null,
+ inv_dt: null,
+ iterations: 0,
+ initialize: function() {}};
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2World.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2World.js
new file mode 100644
index 0000000..b90ae43
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2World.js
@@ -0,0 +1,522 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+var b2World = Class.create();
+b2World.prototype =
+{
+ initialize: function(worldAABB, gravity, doSleep){
+ // initialize instance variables for references
+ this.step = new b2TimeStep();
+ this.m_contactManager = new b2ContactManager();
+ //
+
+
+ this.m_listener = null;
+ this.m_filter = b2CollisionFilter.b2_defaultFilter;
+
+ this.m_bodyList = null;
+ this.m_contactList = null;
+ this.m_jointList = null;
+
+ this.m_bodyCount = 0;
+ this.m_contactCount = 0;
+ this.m_jointCount = 0;
+
+ this.m_bodyDestroyList = null;
+
+ this.m_allowSleep = doSleep;
+
+ this.m_gravity = gravity;
+
+ this.m_contactManager.m_world = this;
+ this.m_broadPhase = new b2BroadPhase(worldAABB, this.m_contactManager);
+
+ var bd = new b2BodyDef();
+ this.m_groundBody = this.CreateBody(bd);
+ },
+ //~b2World(){
+ // this.DestroyBody(this.m_groundBody);
+ // delete this.m_broadPhase;
+ //}
+
+ // Set a callback to notify you when a joint is implicitly destroyed
+ // when an attached body is destroyed.
+ SetListener: function(listener){
+ this.m_listener = listener;
+ },
+
+ // Register a collision filter to provide specific control over collision.
+ // Otherwise the default filter is used (b2CollisionFilter).
+ SetFilter: function(filter){
+ this.m_filter = filter;
+ },
+
+ // Create and destroy rigid bodies. Destruction is deferred until the
+ // the next call to this.Step. This is done so that bodies may be destroyed
+ // while you iterate through the contact list.
+ CreateBody: function(def){
+ //void* mem = this.m_blockAllocator.Allocate(sizeof(b2Body));
+ var b = new b2Body(def, this);
+ b.m_prev = null;
+
+ b.m_next = this.m_bodyList;
+ if (this.m_bodyList)
+ {
+ this.m_bodyList.m_prev = b;
+ }
+ this.m_bodyList = b;
+ ++this.m_bodyCount;
+
+ return b;
+ },
+ // Body destruction is deferred to make contact processing more robust.
+ DestroyBody: function(b)
+ {
+
+ if (b.m_flags & b2Body.e_destroyFlag)
+ {
+ return;
+ }
+
+ // Remove from normal body list.
+ if (b.m_prev)
+ {
+ b.m_prev.m_next = b.m_next;
+ }
+
+ if (b.m_next)
+ {
+ b.m_next.m_prev = b.m_prev;
+ }
+
+ if (b == this.m_bodyList)
+ {
+ this.m_bodyList = b.m_next;
+ }
+
+ b.m_flags |= b2Body.e_destroyFlag;
+ //b2Settings.b2Assert(this.m_bodyCount > 0);
+ --this.m_bodyCount;
+
+ //b->~b2Body();
+ //b.Destroy();
+ // Add to the deferred destruction list.
+ b.m_prev = null;
+ b.m_next = this.m_bodyDestroyList;
+ this.m_bodyDestroyList = b;
+ },
+
+ CleanBodyList: function()
+ {
+ this.m_contactManager.m_destroyImmediate = true;
+
+ var b = this.m_bodyDestroyList;
+ while (b)
+ {
+ //b2Settings.b2Assert((b.m_flags & b2Body.e_destroyFlag) != 0);
+
+ // Preserve the next pointer.
+ var b0 = b;
+ b = b.m_next;
+
+ // Delete the attached joints
+ var jn = b0.m_jointList;
+ while (jn)
+ {
+ var jn0 = jn;
+ jn = jn.next;
+
+ if (this.m_listener)
+ {
+ this.m_listener.NotifyJointDestroyed(jn0.joint);
+ }
+
+ this.DestroyJoint(jn0.joint);
+ }
+
+ b0.Destroy();
+ //this.m_blockAllocator.Free(b0, sizeof(b2Body));
+ }
+
+ // Reset the list.
+ this.m_bodyDestroyList = null;
+
+ this.m_contactManager.m_destroyImmediate = false;
+ },
+
+ CreateJoint: function(def){
+ var j = b2Joint.Create(def, this.m_blockAllocator);
+
+ // Connect to the world list.
+ j.m_prev = null;
+ j.m_next = this.m_jointList;
+ if (this.m_jointList)
+ {
+ this.m_jointList.m_prev = j;
+ }
+ this.m_jointList = j;
+ ++this.m_jointCount;
+
+ // Connect to the bodies
+ j.m_node1.joint = j;
+ j.m_node1.other = j.m_body2;
+ j.m_node1.prev = null;
+ j.m_node1.next = j.m_body1.m_jointList;
+ if (j.m_body1.m_jointList) j.m_body1.m_jointList.prev = j.m_node1;
+ j.m_body1.m_jointList = j.m_node1;
+
+ j.m_node2.joint = j;
+ j.m_node2.other = j.m_body1;
+ j.m_node2.prev = null;
+ j.m_node2.next = j.m_body2.m_jointList;
+ if (j.m_body2.m_jointList) j.m_body2.m_jointList.prev = j.m_node2;
+ j.m_body2.m_jointList = j.m_node2;
+
+ // If the joint prevents collisions, then reset collision filtering.
+ if (def.collideConnected == false)
+ {
+ // Reset the proxies on the body with the minimum number of shapes.
+ var b = def.body1.m_shapeCount < def.body2.m_shapeCount ? def.body1 : def.body2;
+ for (var s = b.m_shapeList; s; s = s.m_next)
+ {
+ s.ResetProxy(this.m_broadPhase);
+ }
+ }
+
+ return j;
+ },
+ DestroyJoint: function(j)
+ {
+
+ var collideConnected = j.m_collideConnected;
+
+ // Remove from the world.
+ if (j.m_prev)
+ {
+ j.m_prev.m_next = j.m_next;
+ }
+
+ if (j.m_next)
+ {
+ j.m_next.m_prev = j.m_prev;
+ }
+
+ if (j == this.m_jointList)
+ {
+ this.m_jointList = j.m_next;
+ }
+
+ // Disconnect from island graph.
+ var body1 = j.m_body1;
+ var body2 = j.m_body2;
+
+ // Wake up touching bodies.
+ body1.WakeUp();
+ body2.WakeUp();
+
+ // Remove from body 1
+ if (j.m_node1.prev)
+ {
+ j.m_node1.prev.next = j.m_node1.next;
+ }
+
+ if (j.m_node1.next)
+ {
+ j.m_node1.next.prev = j.m_node1.prev;
+ }
+
+ if (j.m_node1 == body1.m_jointList)
+ {
+ body1.m_jointList = j.m_node1.next;
+ }
+
+ j.m_node1.prev = null;
+ j.m_node1.next = null;
+
+ // Remove from body 2
+ if (j.m_node2.prev)
+ {
+ j.m_node2.prev.next = j.m_node2.next;
+ }
+
+ if (j.m_node2.next)
+ {
+ j.m_node2.next.prev = j.m_node2.prev;
+ }
+
+ if (j.m_node2 == body2.m_jointList)
+ {
+ body2.m_jointList = j.m_node2.next;
+ }
+
+ j.m_node2.prev = null;
+ j.m_node2.next = null;
+
+ b2Joint.Destroy(j, this.m_blockAllocator);
+
+ //b2Settings.b2Assert(this.m_jointCount > 0);
+ --this.m_jointCount;
+
+ // If the joint prevents collisions, then reset collision filtering.
+ if (collideConnected == false)
+ {
+ // Reset the proxies on the body with the minimum number of shapes.
+ var b = body1.m_shapeCount < body2.m_shapeCount ? body1 : body2;
+ for (var s = b.m_shapeList; s; s = s.m_next)
+ {
+ s.ResetProxy(this.m_broadPhase);
+ }
+ }
+ },
+
+ // The world provides a single ground body with no collision shapes. You
+ // can use this to simplify the creation of joints.
+ GetGroundBody: function(){
+ return this.m_groundBody;
+ },
+
+
+ step: new b2TimeStep(),
+ // this.Step
+ Step: function(dt, iterations){
+
+ var b;
+ var other;
+
+
+ this.step.dt = dt;
+ this.step.iterations = iterations;
+ if (dt > 0.0)
+ {
+ this.step.inv_dt = 1.0 / dt;
+ }
+ else
+ {
+ this.step.inv_dt = 0.0;
+ }
+
+ this.m_positionIterationCount = 0;
+
+ // Handle deferred contact destruction.
+ this.m_contactManager.CleanContactList();
+
+ // Handle deferred body destruction.
+ this.CleanBodyList();
+
+ // Update contacts.
+ this.m_contactManager.Collide();
+
+ // Size the island for the worst case.
+ var island = new b2Island(this.m_bodyCount, this.m_contactCount, this.m_jointCount, this.m_stackAllocator);
+
+ // Clear all the island flags.
+ for (b = this.m_bodyList; b != null; b = b.m_next)
+ {
+ b.m_flags &= ~b2Body.e_islandFlag;
+ }
+ for (var c = this.m_contactList; c != null; c = c.m_next)
+ {
+ c.m_flags &= ~b2Contact.e_islandFlag;
+ }
+ for (var j = this.m_jointList; j != null; j = j.m_next)
+ {
+ j.m_islandFlag = false;
+ }
+
+ // Build and simulate all awake islands.
+ var stackSize = this.m_bodyCount;
+ //var stack = (b2Body**)this.m_stackAllocator.Allocate(stackSize * sizeof(b2Body*));
+ var stack = new Array(this.m_bodyCount);
+ for (var k = 0; k < this.m_bodyCount; k++)
+ stack[k] = null;
+
+ for (var seed = this.m_bodyList; seed != null; seed = seed.m_next)
+ {
+ if (seed.m_flags & (b2Body.e_staticFlag | b2Body.e_islandFlag | b2Body.e_sleepFlag | b2Body.e_frozenFlag))
+ {
+ continue;
+ }
+
+ // Reset island and stack.
+ island.Clear();
+ var stackCount = 0;
+ stack[stackCount++] = seed;
+ seed.m_flags |= b2Body.e_islandFlag;;
+
+ // Perform a depth first search (DFS) on the constraint graph.
+ while (stackCount > 0)
+ {
+ // Grab the next body off the stack and add it to the island.
+ b = stack[--stackCount];
+ island.AddBody(b);
+
+ // Make sure the body is awake.
+ b.m_flags &= ~b2Body.e_sleepFlag;
+
+ // To keep islands, we don't
+ // propagate islands across static bodies.
+ if (b.m_flags & b2Body.e_staticFlag)
+ {
+ continue;
+ }
+
+ // Search all contacts connected to this body.
+ for (var cn = b.m_contactList; cn != null; cn = cn.next)
+ {
+ if (cn.contact.m_flags & b2Contact.e_islandFlag)
+ {
+ continue;
+ }
+
+ island.AddContact(cn.contact);
+ cn.contact.m_flags |= b2Contact.e_islandFlag;
+
+ other = cn.other;
+ if (other.m_flags & b2Body.e_islandFlag)
+ {
+ continue;
+ }
+
+ //b2Settings.b2Assert(stackCount < stackSize);
+ stack[stackCount++] = other;
+ other.m_flags |= b2Body.e_islandFlag;
+ }
+
+ // Search all joints connect to this body.
+ for (var jn = b.m_jointList; jn != null; jn = jn.next)
+ {
+ if (jn.joint.m_islandFlag == true)
+ {
+ continue;
+ }
+
+ island.AddJoint(jn.joint);
+ jn.joint.m_islandFlag = true;
+
+ other = jn.other;
+ if (other.m_flags & b2Body.e_islandFlag)
+ {
+ continue;
+ }
+
+ //b2Settings.b2Assert(stackCount < stackSize);
+ stack[stackCount++] = other;
+ other.m_flags |= b2Body.e_islandFlag;
+ }
+ }
+
+ island.Solve(this.step, this.m_gravity);
+
+ this.m_positionIterationCount = b2Math.b2Max(this.m_positionIterationCount, b2Island.m_positionIterationCount);
+
+ if (this.m_allowSleep)
+ {
+ island.UpdateSleep(dt);
+ }
+
+ // Post solve cleanup.
+ for (var i = 0; i < island.m_bodyCount; ++i)
+ {
+ // Allow static bodies to participate in other islands.
+ b = island.m_bodies[i];
+ if (b.m_flags & b2Body.e_staticFlag)
+ {
+ b.m_flags &= ~b2Body.e_islandFlag;
+ }
+
+ // Handle newly frozen bodies.
+ if (b.IsFrozen() && this.m_listener)
+ {
+ var response = this.m_listener.NotifyBoundaryViolated(b);
+ if (response == b2WorldListener.b2_destroyBody)
+ {
+ this.DestroyBody(b);
+ b = null;
+ island.m_bodies[i] = null;
+ }
+ }
+ }
+ }
+
+ this.m_broadPhase.Commit();
+
+ //this.m_stackAllocator.Free(stack);
+ },
+
+ // this.Query the world for all shapes that potentially overlap the
+ // provided AABB. You provide a shape pointer buffer of specified
+ // size. The number of shapes found is returned.
+ Query: function(aabb, shapes, maxCount){
+
+ //void** results = (void**)this.m_stackAllocator.Allocate(maxCount * sizeof(void*));
+ var results = new Array();
+ var count = this.m_broadPhase.QueryAABB(aabb, results, maxCount);
+
+ for (var i = 0; i < count; ++i)
+ {
+ shapes[i] = results[i];
+ }
+
+ //this.m_stackAllocator.Free(results);
+ return count;
+ },
+
+ // You can use these to iterate over all the bodies, joints, and contacts.
+ GetBodyList: function(){
+ return this.m_bodyList;
+ },
+ GetJointList: function(){
+ return this.m_jointList;
+ },
+ GetContactList: function(){
+ return this.m_contactList;
+ },
+
+ //--------------- Internals Below -------------------
+
+ m_blockAllocator: null,
+ m_stackAllocator: null,
+
+ m_broadPhase: null,
+ m_contactManager: new b2ContactManager(),
+
+ m_bodyList: null,
+ m_contactList: null,
+ m_jointList: null,
+
+ m_bodyCount: 0,
+ m_contactCount: 0,
+ m_jointCount: 0,
+
+ // These bodies will be destroyed at the next time this.step.
+ m_bodyDestroyList: null,
+
+ m_gravity: null,
+ m_allowSleep: null,
+
+ m_groundBody: null,
+
+ m_listener: null,
+ m_filter: null,
+
+ m_positionIterationCount: 0};
+b2World.s_enablePositionCorrection = 1;
+b2World.s_enableWarmStarting = 1;
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2WorldListener.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2WorldListener.js
new file mode 100644
index 0000000..9167570
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2WorldListener.js
@@ -0,0 +1,52 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+
+
+var b2WorldListener = Class.create();
+b2WorldListener.prototype =
+{
+
+ // If a body is destroyed, then any joints attached to it are also destroyed.
+ // This prevents memory leaks, but you may unexpectedly be left with an
+ // orphaned joint pointer.
+ // Box2D will notify you when a joint is implicitly destroyed.
+ // It is NOT called if you directly destroy a joint.
+ // Implement this abstract class and provide it to b2World via
+ // b2World::SetListener().
+ // DO NOT modify the Box2D world inside this callback.
+ NotifyJointDestroyed: function(joint){},
+
+ // This is called when a body's shape passes outside of the world boundary. If you
+ // override this and pass back e_destroyBody, you must nullify your copies of the
+ // body pointer.
+ NotifyBoundaryViolated: function(body)
+ {
+ //NOT_USED(body);
+ return b2WorldListener.b2_freezeBody;
+ },
+
+
+
+ initialize: function() {}};
+b2WorldListener.b2_freezeBody = 0;
+b2WorldListener.b2_destroyBody = 1;
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2CircleContact.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2CircleContact.js
new file mode 100644
index 0000000..dc8df84
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2CircleContact.js
@@ -0,0 +1,102 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+var b2CircleContact = Class.create();
+Object.extend(b2CircleContact.prototype, b2Contact.prototype);
+Object.extend(b2CircleContact.prototype,
+{
+
+ initialize: function(s1, s2) {
+ // The constructor for b2Contact
+ // initialize instance variables for references
+ this.m_node1 = new b2ContactNode();
+ this.m_node2 = new b2ContactNode();
+ //
+ this.m_flags = 0;
+
+ if (!s1 || !s2){
+ this.m_shape1 = null;
+ this.m_shape2 = null;
+ return;
+ }
+
+ this.m_shape1 = s1;
+ this.m_shape2 = s2;
+
+ this.m_manifoldCount = 0;
+
+ this.m_friction = Math.sqrt(this.m_shape1.m_friction * this.m_shape2.m_friction);
+ this.m_restitution = b2Math.b2Max(this.m_shape1.m_restitution, this.m_shape2.m_restitution);
+
+ this.m_prev = null;
+ this.m_next = null;
+
+ this.m_node1.contact = null;
+ this.m_node1.prev = null;
+ this.m_node1.next = null;
+ this.m_node1.other = null;
+
+ this.m_node2.contact = null;
+ this.m_node2.prev = null;
+ this.m_node2.next = null;
+ this.m_node2.other = null;
+ //
+
+ // initialize instance variables for references
+ this.m_manifold = [new b2Manifold()];
+ //
+
+ //super(shape1, shape2);
+
+ //b2Settings.b2Assert(this.m_shape1.m_type == b2Shape.e_circleShape);
+ //b2Settings.b2Assert(this.m_shape2.m_type == b2Shape.e_circleShape);
+ this.m_manifold[0].pointCount = 0;
+ this.m_manifold[0].points[0].normalImpulse = 0.0;
+ this.m_manifold[0].points[0].tangentImpulse = 0.0;
+ },
+ //~b2CircleContact() {}
+
+ Evaluate: function(){
+ b2Collision.b2CollideCircle(this.m_manifold[0], this.m_shape1, this.m_shape2, false);
+
+ if (this.m_manifold[0].pointCount > 0)
+ {
+ this.m_manifoldCount = 1;
+ }
+ else
+ {
+ this.m_manifoldCount = 0;
+ }
+ },
+
+ GetManifolds: function()
+ {
+ return this.m_manifold;
+ },
+
+ m_manifold: [new b2Manifold()]});
+
+b2CircleContact.Create = function(shape1, shape2, allocator){
+ return new b2CircleContact(shape1, shape2);
+ };
+b2CircleContact.Destroy = function(contact, allocator){
+ //
+ };
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2Conservative.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2Conservative.js
new file mode 100644
index 0000000..fe1cd4b
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2Conservative.js
@@ -0,0 +1,228 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+var b2Conservative = Class.create();
+b2Conservative.prototype = {
+
+ // Temp vars
+
+
+
+ initialize: function() {}}
+b2Conservative.R1 = new b2Mat22();
+b2Conservative.R2 = new b2Mat22();
+b2Conservative.x1 = new b2Vec2();
+b2Conservative.x2 = new b2Vec2();
+b2Conservative.Conservative = function(shape1, shape2){
+ var body1 = shape1.GetBody();
+ var body2 = shape2.GetBody();
+
+ //b2Vec2 v1 = body1->m_position - body1->m_position0;
+ var v1X = body1.m_position.x - body1.m_position0.x;
+ var v1Y = body1.m_position.y - body1.m_position0.y;
+ //float32 omega1 = body1->m_rotation - body1->m_rotation0;
+ var omega1 = body1.m_rotation - body1.m_rotation0;
+ //b2Vec2 v2 = body2->m_position - body2->m_position0;
+ var v2X = body2.m_position.x - body2.m_position0.x;
+ var v2Y = body2.m_position.y - body2.m_position0.y;
+ //float32 omega2 = body2->m_rotation - body2->m_rotation0;
+ var omega2 = body2.m_rotation - body2.m_rotation0;
+
+ //float32 r1 = shape1->GetMaxRadius();
+ var r1 = shape1.GetMaxRadius();
+ //float32 r2 = shape2->GetMaxRadius();
+ var r2 = shape2.GetMaxRadius();
+
+ //b2Vec2 p1Start = body1->m_position0;
+ var p1StartX = body1.m_position0.x;
+ var p1StartY = body1.m_position0.y;
+ //float32 a1Start = body1->m_rotation0;
+ var a1Start = body1.m_rotation0;
+
+ //b2Vec2 p2Start = body2->m_position0;
+ var p2StartX = body2.m_position0.x;
+ var p2StartY = body2.m_position0.y;
+ //float32 a2Start = body2->m_rotation0;
+ var a2Start = body2.m_rotation0;
+
+ //b2Vec2 p1 = p1Start;
+ var p1X = p1StartX;
+ var p1Y = p1StartY;
+ //float32 a1 = a1Start;
+ var a1 = a1Start;
+ //b2Vec2 p2 = p2Start;
+ var p2X = p2StartX;
+ var p2Y = p2StartY;
+ //float32 a2 = a2Start;
+ var a2 = a2Start;
+
+ //b2Mat22 b2Conservative.R1(a1), b2Conservative.R2(a2);
+ b2Conservative.R1.Set(a1);
+ b2Conservative.R2.Set(a2);
+
+ //shape1->QuickSync(p1, b2Conservative.R1);
+ shape1.QuickSync(p1, b2Conservative.R1);
+ //shape2->QuickSync(p2, b2Conservative.R2);
+ shape2.QuickSync(p2, b2Conservative.R2);
+
+ //float32 s1 = 0.0f;
+ var s1 = 0.0;
+ //const int32 maxIterations = 10;
+ var maxIterations = 10;
+ //b2Vec2 d;
+ var dX;
+ var dY;
+ //float32 invRelativeVelocity = 0.0f;
+ var invRelativeVelocity = 0.0;
+ //bool hit = true;
+ var hit = true;
+ //b2Vec2 b2Conservative.x1, b2Conservative.x2; moved to static var
+ for (var iter = 0; iter < maxIterations; ++iter)
+ {
+ // Get the accurate distance between shapes.
+ //float32 distance = b2Distance.Distance(&b2Conservative.x1, &b2Conservative.x2, shape1, shape2);
+ var distance = b2Distance.Distance(b2Conservative.x1, b2Conservative.x2, shape1, shape2);
+ if (distance < b2Settings.b2_linearSlop)
+ {
+ if (iter == 0)
+ {
+ hit = false;
+ }
+ else
+ {
+ hit = true;
+ }
+ break;
+ }
+
+ if (iter == 0)
+ {
+ //b2Vec2 d = b2Conservative.x2 - b2Conservative.x1;
+ dX = b2Conservative.x2.x - b2Conservative.x1.x;
+ dY = b2Conservative.x2.y - b2Conservative.x1.y;
+ //d.Normalize();
+ var dLen = Math.sqrt(dX*dX + dY*dY);
+ //float32 relativeVelocity = b2Dot(d, v1 - v2) + b2Abs(omega1) * r1 + b2Abs(omega2) * r2;
+ var relativeVelocity = (dX*(v1X-v2X) + dY*(v1Y - v2Y)) + Math.abs(omega1) * r1 + Math.abs(omega2) * r2;
+ if (Math.abs(relativeVelocity) < Number.MIN_VALUE)
+ {
+ hit = false;
+ break;
+ }
+
+ invRelativeVelocity = 1.0 / relativeVelocity;
+ }
+
+ // Get the conservative movement.
+ //float32 ds = distance * invRelativeVelocity;
+ var ds = distance * invRelativeVelocity;
+ //float32 s2 = s1 + ds;
+ var s2 = s1 + ds;
+
+ if (s2 < 0.0 || 1.0 < s2)
+ {
+ hit = false;
+ break;
+ }
+
+ if (s2 < (1.0 + 100.0 * Number.MIN_VALUE) * s1)
+ {
+ hit = true;
+ break;
+ }
+
+ s1 = s2;
+
+ // Move forward conservatively.
+ //p1 = p1Start + s1 * v1;
+ p1X = p1StartX + s1 * v1.x;
+ p1Y = p1StartY + s1 * v1.y;
+ //a1 = a1Start + s1 * omega1;
+ a1 = a1Start + s1 * omega1;
+ //p2 = p2Start + s1 * v2;
+ p2X = p2StartX + s1 * v2.x;
+ p2Y = p2StartY + s1 * v2.y;
+ //a2 = a2Start + s1 * omega2;
+ a2 = a2Start + s1 * omega2;
+
+ b2Conservative.R1.Set(a1);
+ b2Conservative.R2.Set(a2);
+ shape1.QuickSync(p1, b2Conservative.R1);
+ shape2.QuickSync(p2, b2Conservative.R2);
+ }
+
+ if (hit)
+ {
+ // Hit, move bodies to safe position and re-sync shapes.
+ //b2Vec2 d = b2Conservative.x2 - b2Conservative.x1;
+ dX = b2Conservative.x2.x - b2Conservative.x1.x;
+ dY = b2Conservative.x2.y - b2Conservative.x1.y;
+ //float32 length = d.Length();
+ var length = Math.sqrt(dX*dX + dY*dY);
+ if (length > FLT_EPSILON)
+ {
+ d *= b2_linearSlop / length;
+ }
+
+ if (body1.IsStatic())
+ {
+ //body1.m_position = p1;
+ body1.m_position.x = p1X;
+ body1.m_position.y = p1Y;
+ }
+ else
+ {
+ //body1.m_position = p1 - d;
+ body1.m_position.x = p1X - dX;
+ body1.m_position.y = p1Y - dY;
+ }
+ body1.m_rotation = a1;
+ body1.m_R.Set(a1);
+ body1.QuickSyncShapes();
+
+ if (body2.IsStatic())
+ {
+ //body2->m_position = p2;
+ body2.m_position.x = p2X;
+ body2.m_position.y = p2Y;
+ }
+ else
+ {
+ //body2->m_position = p2 + d;
+ body2.m_position.x = p2X + dX;
+ body2.m_position.y = p2Y + dY;
+ }
+ //body2.m_position = p2 + d;
+ body2.m_position.x = p2X + dX;
+ body2.m_position.y = p2Y + dY;
+ body2.m_rotation = a2;
+ body2.m_R.Set(a2);
+ body2.QuickSyncShapes();
+
+ return true;
+ }
+
+ // No hit, restore shapes.
+ shape1.QuickSync(body1.m_position, body1.m_R);
+ shape2.QuickSync(body2.m_position, body2.m_R);
+ return false;
+ };
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2Contact.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2Contact.js
new file mode 100644
index 0000000..283feaf
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2Contact.js
@@ -0,0 +1,201 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+//typedef b2Contact* b2ContactCreateFcn(b2Shape* shape1, b2Shape* shape2, b2BlockAllocator* allocator);
+//typedef void b2ContactDestroyFcn(b2Contact* contact, b2BlockAllocator* allocator);
+
+
+
+var b2Contact = Class.create();
+b2Contact.prototype =
+{
+ GetManifolds: function(){return null},
+ GetManifoldCount: function()
+ {
+ return this.m_manifoldCount;
+ },
+
+ GetNext: function(){
+ return this.m_next;
+ },
+
+ GetShape1: function(){
+ return this.m_shape1;
+ },
+
+ GetShape2: function(){
+ return this.m_shape2;
+ },
+
+ //--------------- Internals Below -------------------
+
+ // this.m_flags
+ // enum
+
+
+ initialize: function(s1, s2)
+ {
+ // initialize instance variables for references
+ this.m_node1 = new b2ContactNode();
+ this.m_node2 = new b2ContactNode();
+ //
+
+ this.m_flags = 0;
+
+ if (!s1 || !s2){
+ this.m_shape1 = null;
+ this.m_shape2 = null;
+ return;
+ }
+
+ this.m_shape1 = s1;
+ this.m_shape2 = s2;
+
+ this.m_manifoldCount = 0;
+
+ this.m_friction = Math.sqrt(this.m_shape1.m_friction * this.m_shape2.m_friction);
+ this.m_restitution = b2Math.b2Max(this.m_shape1.m_restitution, this.m_shape2.m_restitution);
+
+ this.m_prev = null;
+ this.m_next = null;
+
+ this.m_node1.contact = null;
+ this.m_node1.prev = null;
+ this.m_node1.next = null;
+ this.m_node1.other = null;
+
+ this.m_node2.contact = null;
+ this.m_node2.prev = null;
+ this.m_node2.next = null;
+ this.m_node2.other = null;
+ },
+
+ //virtual ~b2Contact() {}
+
+ Evaluate: function(){},
+
+ m_flags: 0,
+
+ // World pool and list pointers.
+ m_prev: null,
+ m_next: null,
+
+ // Nodes for connecting bodies.
+ m_node1: new b2ContactNode(),
+ m_node2: new b2ContactNode(),
+
+ m_shape1: null,
+ m_shape2: null,
+
+ m_manifoldCount: 0,
+
+ // Combined friction
+ m_friction: null,
+ m_restitution: null};
+b2Contact.e_islandFlag = 0x0001;
+b2Contact.e_destroyFlag = 0x0002;
+b2Contact.AddType = function(createFcn, destroyFcn, type1, type2)
+ {
+ //b2Settings.b2Assert(b2Shape.e_unknownShape < type1 && type1 < b2Shape.e_shapeTypeCount);
+ //b2Settings.b2Assert(b2Shape.e_unknownShape < type2 && type2 < b2Shape.e_shapeTypeCount);
+
+ b2Contact.s_registers[type1][type2].createFcn = createFcn;
+ b2Contact.s_registers[type1][type2].destroyFcn = destroyFcn;
+ b2Contact.s_registers[type1][type2].primary = true;
+
+ if (type1 != type2)
+ {
+ b2Contact.s_registers[type2][type1].createFcn = createFcn;
+ b2Contact.s_registers[type2][type1].destroyFcn = destroyFcn;
+ b2Contact.s_registers[type2][type1].primary = false;
+ }
+ };
+b2Contact.InitializeRegisters = function(){
+ b2Contact.s_registers = new Array(b2Shape.e_shapeTypeCount);
+ for (var i = 0; i < b2Shape.e_shapeTypeCount; i++){
+ b2Contact.s_registers[i] = new Array(b2Shape.e_shapeTypeCount);
+ for (var j = 0; j < b2Shape.e_shapeTypeCount; j++){
+ b2Contact.s_registers[i][j] = new b2ContactRegister();
+ }
+ }
+
+ b2Contact.AddType(b2CircleContact.Create, b2CircleContact.Destroy, b2Shape.e_circleShape, b2Shape.e_circleShape);
+ b2Contact.AddType(b2PolyAndCircleContact.Create, b2PolyAndCircleContact.Destroy, b2Shape.e_polyShape, b2Shape.e_circleShape);
+ b2Contact.AddType(b2PolyContact.Create, b2PolyContact.Destroy, b2Shape.e_polyShape, b2Shape.e_polyShape);
+
+ };
+b2Contact.Create = function(shape1, shape2, allocator){
+ if (b2Contact.s_initialized == false)
+ {
+ b2Contact.InitializeRegisters();
+ b2Contact.s_initialized = true;
+ }
+
+ var type1 = shape1.m_type;
+ var type2 = shape2.m_type;
+
+ //b2Settings.b2Assert(b2Shape.e_unknownShape < type1 && type1 < b2Shape.e_shapeTypeCount);
+ //b2Settings.b2Assert(b2Shape.e_unknownShape < type2 && type2 < b2Shape.e_shapeTypeCount);
+
+ var createFcn = b2Contact.s_registers[type1][type2].createFcn;
+ if (createFcn)
+ {
+ if (b2Contact.s_registers[type1][type2].primary)
+ {
+ return createFcn(shape1, shape2, allocator);
+ }
+ else
+ {
+ var c = createFcn(shape2, shape1, allocator);
+ for (var i = 0; i < c.GetManifoldCount(); ++i)
+ {
+ var m = c.GetManifolds()[ i ];
+ m.normal = m.normal.Negative();
+ }
+ return c;
+ }
+ }
+ else
+ {
+ return null;
+ }
+ };
+b2Contact.Destroy = function(contact, allocator){
+ //b2Settings.b2Assert(b2Contact.s_initialized == true);
+
+ if (contact.GetManifoldCount() > 0)
+ {
+ contact.m_shape1.m_body.WakeUp();
+ contact.m_shape2.m_body.WakeUp();
+ }
+
+ var type1 = contact.m_shape1.m_type;
+ var type2 = contact.m_shape2.m_type;
+
+ //b2Settings.b2Assert(b2Shape.e_unknownShape < type1 && type1 < b2Shape.e_shapeTypeCount);
+ //b2Settings.b2Assert(b2Shape.e_unknownShape < type2 && type2 < b2Shape.e_shapeTypeCount);
+
+ var destroyFcn = b2Contact.s_registers[type1][type2].destroyFcn;
+ destroyFcn(contact, allocator);
+ };
+b2Contact.s_registers = null;
+b2Contact.s_initialized = false;
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactConstraint.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactConstraint.js
new file mode 100644
index 0000000..79b82e1
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactConstraint.js
@@ -0,0 +1,45 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+var b2ContactConstraint = Class.create();
+b2ContactConstraint.prototype =
+{
+ initialize: function(){
+ // initialize instance variables for references
+ this.normal = new b2Vec2();
+ //
+
+ this.points = new Array(b2Settings.b2_maxManifoldPoints);
+ for (var i = 0; i < b2Settings.b2_maxManifoldPoints; i++){
+ this.points[i] = new b2ContactConstraintPoint();
+ }
+
+
+ },
+ points: null,
+ normal: new b2Vec2(),
+ manifold: null,
+ body1: null,
+ body2: null,
+ friction: null,
+ restitution: null,
+ pointCount: 0};
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactConstraintPoint.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactConstraintPoint.js
new file mode 100644
index 0000000..bc3dca6
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactConstraintPoint.js
@@ -0,0 +1,40 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+var b2ContactConstraintPoint = Class.create();
+b2ContactConstraintPoint.prototype =
+{
+ localAnchor1: new b2Vec2(),
+ localAnchor2: new b2Vec2(),
+ normalImpulse: null,
+ tangentImpulse: null,
+ positionImpulse: null,
+ normalMass: null,
+ tangentMass: null,
+ separation: null,
+ velocityBias: null,
+ initialize: function() {
+ // initialize instance variables for references
+ this.localAnchor1 = new b2Vec2();
+ this.localAnchor2 = new b2Vec2();
+ //
+}};
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactNode.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactNode.js
new file mode 100644
index 0000000..c10e482
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactNode.js
@@ -0,0 +1,33 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+var b2ContactNode = Class.create();
+b2ContactNode.prototype =
+{
+ other: null,
+ contact: null,
+ prev: null,
+ next: null,
+ initialize: function() {}};
+
+
+
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactRegister.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactRegister.js
new file mode 100644
index 0000000..524c352
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactRegister.js
@@ -0,0 +1,30 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+var b2ContactRegister = Class.create();
+b2ContactRegister.prototype =
+{
+ createFcn: null,
+ destroyFcn: null,
+ primary: null,
+ initialize: function() {}};
+
+
+
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactSolver.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactSolver.js
new file mode 100644
index 0000000..0b0d3f4
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactSolver.js
@@ -0,0 +1,537 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+var b2ContactSolver = Class.create();
+b2ContactSolver.prototype =
+{
+ initialize: function(contacts, contactCount, allocator){
+ // initialize instance variables for references
+ this.m_constraints = new Array();
+ //
+
+ this.m_allocator = allocator;
+
+ var i = 0;
+ var tVec;
+ var tMat;
+
+ this.m_constraintCount = 0;
+ for (i = 0; i < contactCount; ++i)
+ {
+ this.m_constraintCount += contacts[i].GetManifoldCount();
+ }
+
+ // fill array
+ for (i = 0; i < this.m_constraintCount; i++){
+ this.m_constraints[i] = new b2ContactConstraint();
+ }
+
+ var count = 0;
+ for (i = 0; i < contactCount; ++i)
+ {
+ var contact = contacts[i];
+ var b1 = contact.m_shape1.m_body;
+ var b2 = contact.m_shape2.m_body;
+ var manifoldCount = contact.GetManifoldCount();
+ var manifolds = contact.GetManifolds();
+ var friction = contact.m_friction;
+ var restitution = contact.m_restitution;
+
+ //var v1 = b1.m_linearVelocity.Copy();
+ var v1X = b1.m_linearVelocity.x;
+ var v1Y = b1.m_linearVelocity.y;
+ //var v2 = b2.m_linearVelocity.Copy();
+ var v2X = b2.m_linearVelocity.x;
+ var v2Y = b2.m_linearVelocity.y;
+ var w1 = b1.m_angularVelocity;
+ var w2 = b2.m_angularVelocity;
+
+ for (var j = 0; j < manifoldCount; ++j)
+ {
+ var manifold = manifolds[ j ];
+
+ //b2Settings.b2Assert(manifold.pointCount > 0);
+
+ //var normal = manifold.normal.Copy();
+ var normalX = manifold.normal.x;
+ var normalY = manifold.normal.y;
+
+ //b2Settings.b2Assert(count < this.m_constraintCount);
+ var c = this.m_constraints[ count ];
+ c.body1 = b1;
+ c.body2 = b2;
+ c.manifold = manifold;
+ //c.normal = normal;
+ c.normal.x = normalX;
+ c.normal.y = normalY;
+ c.pointCount = manifold.pointCount;
+ c.friction = friction;
+ c.restitution = restitution;
+
+ for (var k = 0; k < c.pointCount; ++k)
+ {
+ var cp = manifold.points[ k ];
+ var ccp = c.points[ k ];
+
+ ccp.normalImpulse = cp.normalImpulse;
+ ccp.tangentImpulse = cp.tangentImpulse;
+ ccp.separation = cp.separation;
+
+ //var r1 = b2Math.SubtractVV( cp.position, b1.m_position );
+ var r1X = cp.position.x - b1.m_position.x;
+ var r1Y = cp.position.y - b1.m_position.y;
+ //var r2 = b2Math.SubtractVV( cp.position, b2.m_position );
+ var r2X = cp.position.x - b2.m_position.x;
+ var r2Y = cp.position.y - b2.m_position.y;
+
+ //ccp.localAnchor1 = b2Math.b2MulTMV(b1.m_R, r1);
+ tVec = ccp.localAnchor1;
+ tMat = b1.m_R;
+ tVec.x = r1X * tMat.col1.x + r1Y * tMat.col1.y;
+ tVec.y = r1X * tMat.col2.x + r1Y * tMat.col2.y;
+
+ //ccp.localAnchor2 = b2Math.b2MulTMV(b2.m_R, r2);
+ tVec = ccp.localAnchor2;
+ tMat = b2.m_R;
+ tVec.x = r2X * tMat.col1.x + r2Y * tMat.col1.y;
+ tVec.y = r2X * tMat.col2.x + r2Y * tMat.col2.y;
+
+ var r1Sqr = r1X * r1X + r1Y * r1Y;
+ var r2Sqr = r2X * r2X + r2Y * r2Y;
+
+ //var rn1 = b2Math.b2Dot(r1, normal);
+ var rn1 = r1X*normalX + r1Y*normalY;
+ //var rn2 = b2Math.b2Dot(r2, normal);
+ var rn2 = r2X*normalX + r2Y*normalY;
+ var kNormal = b1.m_invMass + b2.m_invMass;
+ kNormal += b1.m_invI * (r1Sqr - rn1 * rn1) + b2.m_invI * (r2Sqr - rn2 * rn2);
+ //b2Settings.b2Assert(kNormal > Number.MIN_VALUE);
+ ccp.normalMass = 1.0 / kNormal;
+
+ //var tangent = b2Math.b2CrossVF(normal, 1.0);
+ var tangentX = normalY
+ var tangentY = -normalX;
+
+ //var rt1 = b2Math.b2Dot(r1, tangent);
+ var rt1 = r1X*tangentX + r1Y*tangentY;
+ //var rt2 = b2Math.b2Dot(r2, tangent);
+ var rt2 = r2X*tangentX + r2Y*tangentY;
+ var kTangent = b1.m_invMass + b2.m_invMass;
+ kTangent += b1.m_invI * (r1Sqr - rt1 * rt1) + b2.m_invI * (r2Sqr - rt2 * rt2);
+ //b2Settings.b2Assert(kTangent > Number.MIN_VALUE);
+ ccp.tangentMass = 1.0 / kTangent;
+
+ // Setup a velocity bias for restitution.
+ ccp.velocityBias = 0.0;
+ if (ccp.separation > 0.0)
+ {
+ ccp.velocityBias = -60.0 * ccp.separation;
+ }
+ //var vRel = b2Math.b2Dot(c.normal, b2Math.SubtractVV( b2Math.SubtractVV( b2Math.AddVV( v2, b2Math.b2CrossFV(w2, r2)), v1 ), b2Math.b2CrossFV(w1, r1)));
+ var tX = v2X + (-w2*r2Y) - v1X - (-w1*r1Y);
+ var tY = v2Y + (w2*r2X) - v1Y - (w1*r1X);
+ //var vRel = b2Dot(c.normal, tX/Y);
+ var vRel = c.normal.x*tX + c.normal.y*tY;
+ if (vRel < -b2Settings.b2_velocityThreshold)
+ {
+ ccp.velocityBias += -c.restitution * vRel;
+ }
+ }
+
+ ++count;
+ }
+ }
+
+ //b2Settings.b2Assert(count == this.m_constraintCount);
+ },
+ //~b2ContactSolver();
+
+ PreSolve: function(){
+ var tVec;
+ var tVec2;
+ var tMat;
+
+ // Warm start.
+ for (var i = 0; i < this.m_constraintCount; ++i)
+ {
+ var c = this.m_constraints[ i ];
+
+ var b1 = c.body1;
+ var b2 = c.body2;
+ var invMass1 = b1.m_invMass;
+ var invI1 = b1.m_invI;
+ var invMass2 = b2.m_invMass;
+ var invI2 = b2.m_invI;
+ //var normal = new b2Vec2(c.normal.x, c.normal.y);
+ var normalX = c.normal.x;
+ var normalY = c.normal.y;
+ //var tangent = b2Math.b2CrossVF(normal, 1.0);
+ var tangentX = normalY;
+ var tangentY = -normalX;
+
+ var j = 0;
+ var tCount = 0;
+ if (b2World.s_enableWarmStarting)
+ {
+ tCount = c.pointCount;
+ for (j = 0; j < tCount; ++j)
+ {
+ var ccp = c.points[ j ];
+ //var P = b2Math.AddVV( b2Math.MulFV(ccp.normalImpulse, normal), b2Math.MulFV(ccp.tangentImpulse, tangent));
+ var PX = ccp.normalImpulse*normalX + ccp.tangentImpulse*tangentX;
+ var PY = ccp.normalImpulse*normalY + ccp.tangentImpulse*tangentY;
+
+ //var r1 = b2Math.b2MulMV(b1.m_R, ccp.localAnchor1);
+ tMat = b1.m_R;
+ tVec = ccp.localAnchor1;
+ var r1X = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y;
+ var r1Y = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y;
+
+ //var r2 = b2Math.b2MulMV(b2.m_R, ccp.localAnchor2);
+ tMat = b2.m_R;
+ tVec = ccp.localAnchor2;
+ var r2X = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y;
+ var r2Y = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y;
+
+ //b1.m_angularVelocity -= invI1 * b2Math.b2CrossVV(r1, P);
+ b1.m_angularVelocity -= invI1 * (r1X * PY - r1Y * PX);
+ //b1.m_linearVelocity.Subtract( b2Math.MulFV(invMass1, P) );
+ b1.m_linearVelocity.x -= invMass1 * PX;
+ b1.m_linearVelocity.y -= invMass1 * PY;
+ //b2.m_angularVelocity += invI2 * b2Math.b2CrossVV(r2, P);
+ b2.m_angularVelocity += invI2 * (r2X * PY - r2Y * PX);
+ //b2.m_linearVelocity.Add( b2Math.MulFV(invMass2, P) );
+ b2.m_linearVelocity.x += invMass2 * PX;
+ b2.m_linearVelocity.y += invMass2 * PY;
+
+ ccp.positionImpulse = 0.0;
+ }
+ }
+ else{
+ tCount = c.pointCount;
+ for (j = 0; j < tCount; ++j)
+ {
+ var ccp2 = c.points[ j ];
+ ccp2.normalImpulse = 0.0;
+ ccp2.tangentImpulse = 0.0;
+
+ ccp2.positionImpulse = 0.0;
+ }
+ }
+ }
+ },
+ SolveVelocityConstraints: function(){
+ var j = 0;
+ var ccp;
+ var r1X;
+ var r1Y;
+ var r2X;
+ var r2Y;
+ var dvX;
+ var dvY;
+ var lambda;
+ var newImpulse;
+ var PX;
+ var PY;
+
+ var tMat;
+ var tVec;
+
+ for (var i = 0; i < this.m_constraintCount; ++i)
+ {
+ var c = this.m_constraints[ i ];
+ var b1 = c.body1;
+ var b2 = c.body2;
+ var b1_angularVelocity = b1.m_angularVelocity;
+ var b1_linearVelocity = b1.m_linearVelocity;
+ var b2_angularVelocity = b2.m_angularVelocity;
+ var b2_linearVelocity = b2.m_linearVelocity;
+
+ var invMass1 = b1.m_invMass;
+ var invI1 = b1.m_invI;
+ var invMass2 = b2.m_invMass;
+ var invI2 = b2.m_invI;
+ //var normal = new b2Vec2(c.normal.x, c.normal.y);
+ var normalX = c.normal.x;
+ var normalY = c.normal.y;
+ //var tangent = b2Math.b2CrossVF(normal, 1.0);
+ var tangentX = normalY;
+ var tangentY = -normalX;
+
+ // Solver normal constraints
+ var tCount = c.pointCount;
+ for (j = 0; j < tCount; ++j)
+ {
+ ccp = c.points[ j ];
+
+ //r1 = b2Math.b2MulMV(b1.m_R, ccp.localAnchor1);
+ tMat = b1.m_R;
+ tVec = ccp.localAnchor1;
+ r1X = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y
+ r1Y = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y
+ //r2 = b2Math.b2MulMV(b2.m_R, ccp.localAnchor2);
+ tMat = b2.m_R;
+ tVec = ccp.localAnchor2;
+ r2X = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y
+ r2Y = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y
+
+ // Relative velocity at contact
+ //var dv = b2Math.SubtractVV( b2Math.AddVV( b2.m_linearVelocity, b2Math.b2CrossFV(b2.m_angularVelocity, r2)), b2Math.SubtractVV(b1.m_linearVelocity, b2Math.b2CrossFV(b1.m_angularVelocity, r1)));
+ //dv = b2Math.SubtractVV(b2Math.SubtractVV( b2Math.AddVV( b2.m_linearVelocity, b2Math.b2CrossFV(b2.m_angularVelocity, r2)), b1.m_linearVelocity), b2Math.b2CrossFV(b1.m_angularVelocity, r1));
+ dvX = b2_linearVelocity.x + (-b2_angularVelocity * r2Y) - b1_linearVelocity.x - (-b1_angularVelocity * r1Y);
+ dvY = b2_linearVelocity.y + (b2_angularVelocity * r2X) - b1_linearVelocity.y - (b1_angularVelocity * r1X);
+
+ // Compute normal impulse
+ //var vn = b2Math.b2Dot(dv, normal);
+ var vn = dvX * normalX + dvY * normalY;
+ lambda = -ccp.normalMass * (vn - ccp.velocityBias);
+
+ // b2Clamp the accumulated impulse
+ newImpulse = b2Math.b2Max(ccp.normalImpulse + lambda, 0.0);
+ lambda = newImpulse - ccp.normalImpulse;
+
+ // Apply contact impulse
+ //P = b2Math.MulFV(lambda, normal);
+ PX = lambda * normalX;
+ PY = lambda * normalY;
+
+ //b1.m_linearVelocity.Subtract( b2Math.MulFV( invMass1, P ) );
+ b1_linearVelocity.x -= invMass1 * PX;
+ b1_linearVelocity.y -= invMass1 * PY;
+ b1_angularVelocity -= invI1 * (r1X * PY - r1Y * PX);
+
+ //b2.m_linearVelocity.Add( b2Math.MulFV( invMass2, P ) );
+ b2_linearVelocity.x += invMass2 * PX;
+ b2_linearVelocity.y += invMass2 * PY;
+ b2_angularVelocity += invI2 * (r2X * PY - r2Y * PX);
+
+ ccp.normalImpulse = newImpulse;
+
+
+
+ // MOVED FROM BELOW
+ // Relative velocity at contact
+ //var dv = b2.m_linearVelocity + b2Cross(b2.m_angularVelocity, r2) - b1.m_linearVelocity - b2Cross(b1.m_angularVelocity, r1);
+ //dv = b2Math.SubtractVV(b2Math.SubtractVV(b2Math.AddVV(b2.m_linearVelocity, b2Math.b2CrossFV(b2.m_angularVelocity, r2)), b1.m_linearVelocity), b2Math.b2CrossFV(b1.m_angularVelocity, r1));
+ dvX = b2_linearVelocity.x + (-b2_angularVelocity * r2Y) - b1_linearVelocity.x - (-b1_angularVelocity * r1Y);
+ dvY = b2_linearVelocity.y + (b2_angularVelocity * r2X) - b1_linearVelocity.y - (b1_angularVelocity * r1X);
+
+ // Compute tangent impulse
+ var vt = dvX*tangentX + dvY*tangentY;
+ lambda = ccp.tangentMass * (-vt);
+
+ // b2Clamp the accumulated impulse
+ var maxFriction = c.friction * ccp.normalImpulse;
+ newImpulse = b2Math.b2Clamp(ccp.tangentImpulse + lambda, -maxFriction, maxFriction);
+ lambda = newImpulse - ccp.tangentImpulse;
+
+ // Apply contact impulse
+ //P = b2Math.MulFV(lambda, tangent);
+ PX = lambda * tangentX;
+ PY = lambda * tangentY;
+
+ //b1.m_linearVelocity.Subtract( b2Math.MulFV( invMass1, P ) );
+ b1_linearVelocity.x -= invMass1 * PX;
+ b1_linearVelocity.y -= invMass1 * PY;
+ b1_angularVelocity -= invI1 * (r1X * PY - r1Y * PX);
+
+ //b2.m_linearVelocity.Add( b2Math.MulFV( invMass2, P ) );
+ b2_linearVelocity.x += invMass2 * PX;
+ b2_linearVelocity.y += invMass2 * PY;
+ b2_angularVelocity += invI2 * (r2X * PY - r2Y * PX);
+
+ ccp.tangentImpulse = newImpulse;
+ }
+
+
+
+ // Solver tangent constraints
+ // MOVED ABOVE FOR EFFICIENCY
+ /*for (j = 0; j < tCount; ++j)
+ {
+ ccp = c.points[ j ];
+
+ //r1 = b2Math.b2MulMV(b1.m_R, ccp.localAnchor1);
+ tMat = b1.m_R;
+ tVec = ccp.localAnchor1;
+ r1X = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y
+ r1Y = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y
+ //r2 = b2Math.b2MulMV(b2.m_R, ccp.localAnchor2);
+ tMat = b2.m_R;
+ tVec = ccp.localAnchor2;
+ r2X = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y
+ r2Y = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y
+
+ // Relative velocity at contact
+ //var dv = b2.m_linearVelocity + b2Cross(b2.m_angularVelocity, r2) - b1.m_linearVelocity - b2Cross(b1.m_angularVelocity, r1);
+ //dv = b2Math.SubtractVV(b2Math.SubtractVV(b2Math.AddVV(b2.m_linearVelocity, b2Math.b2CrossFV(b2.m_angularVelocity, r2)), b1.m_linearVelocity), b2Math.b2CrossFV(b1.m_angularVelocity, r1));
+ dvX = b2_linearVelocity.x + (-b2_angularVelocity * r2Y) - b1_linearVelocity.x - (-b1_angularVelocity * r1Y);
+ dvY = b2_linearVelocity.y + (b2_angularVelocity * r2X) - b1_linearVelocity.y - (b1_angularVelocity * r1X);
+
+ // Compute tangent impulse
+ var vt = dvX*tangentX + dvY*tangentY;
+ lambda = ccp.tangentMass * (-vt);
+
+ // b2Clamp the accumulated impulse
+ var maxFriction = c.friction * ccp.normalImpulse;
+ newImpulse = b2Math.b2Clamp(ccp.tangentImpulse + lambda, -maxFriction, maxFriction);
+ lambda = newImpulse - ccp.tangentImpulse;
+
+ // Apply contact impulse
+ //P = b2Math.MulFV(lambda, tangent);
+ PX = lambda * tangentX;
+ PY = lambda * tangentY;
+
+ //b1.m_linearVelocity.Subtract( b2Math.MulFV( invMass1, P ) );
+ b1_linearVelocity.x -= invMass1 * PX;
+ b1_linearVelocity.y -= invMass1 * PY;
+ b1_angularVelocity -= invI1 * (r1X * PY - r1Y * PX);
+
+ //b2.m_linearVelocity.Add( b2Math.MulFV( invMass2, P ) );
+ b2_linearVelocity.x += invMass2 * PX;
+ b2_linearVelocity.y += invMass2 * PY;
+ b2_angularVelocity += invI2 * (r2X * PY - r2Y * PX);
+
+ ccp.tangentImpulse = newImpulse;
+ }*/
+
+ // Update angular velocity
+ b1.m_angularVelocity = b1_angularVelocity;
+ b2.m_angularVelocity = b2_angularVelocity;
+ }
+ },
+ SolvePositionConstraints: function(beta){
+ var minSeparation = 0.0;
+
+ var tMat;
+ var tVec;
+
+ for (var i = 0; i < this.m_constraintCount; ++i)
+ {
+ var c = this.m_constraints[ i ];
+ var b1 = c.body1;
+ var b2 = c.body2;
+ var b1_position = b1.m_position;
+ var b1_rotation = b1.m_rotation;
+ var b2_position = b2.m_position;
+ var b2_rotation = b2.m_rotation;
+
+ var invMass1 = b1.m_invMass;
+ var invI1 = b1.m_invI;
+ var invMass2 = b2.m_invMass;
+ var invI2 = b2.m_invI;
+ //var normal = new b2Vec2(c.normal.x, c.normal.y);
+ var normalX = c.normal.x;
+ var normalY = c.normal.y;
+ //var tangent = b2Math.b2CrossVF(normal, 1.0);
+ var tangentX = normalY;
+ var tangentY = -normalX;
+
+ // Solver normal constraints
+ var tCount = c.pointCount;
+ for (var j = 0; j < tCount; ++j)
+ {
+ var ccp = c.points[ j ];
+
+ //r1 = b2Math.b2MulMV(b1.m_R, ccp.localAnchor1);
+ tMat = b1.m_R;
+ tVec = ccp.localAnchor1;
+ var r1X = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y
+ var r1Y = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y
+ //r2 = b2Math.b2MulMV(b2.m_R, ccp.localAnchor2);
+ tMat = b2.m_R;
+ tVec = ccp.localAnchor2;
+ var r2X = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y
+ var r2Y = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y
+
+ //var p1 = b2Math.AddVV(b1.m_position, r1);
+ var p1X = b1_position.x + r1X;
+ var p1Y = b1_position.y + r1Y;
+
+ //var p2 = b2Math.AddVV(b2.m_position, r2);
+ var p2X = b2_position.x + r2X;
+ var p2Y = b2_position.y + r2Y;
+
+ //var dp = b2Math.SubtractVV(p2, p1);
+ var dpX = p2X - p1X;
+ var dpY = p2Y - p1Y;
+
+ // Approximate the current separation.
+ //var separation = b2Math.b2Dot(dp, normal) + ccp.separation;
+ var separation = (dpX*normalX + dpY*normalY) + ccp.separation;
+
+ // Track max constraint error.
+ minSeparation = b2Math.b2Min(minSeparation, separation);
+
+ // Prevent large corrections and allow slop.
+ var C = beta * b2Math.b2Clamp(separation + b2Settings.b2_linearSlop, -b2Settings.b2_maxLinearCorrection, 0.0);
+
+ // Compute normal impulse
+ var dImpulse = -ccp.normalMass * C;
+
+ // b2Clamp the accumulated impulse
+ var impulse0 = ccp.positionImpulse;
+ ccp.positionImpulse = b2Math.b2Max(impulse0 + dImpulse, 0.0);
+ dImpulse = ccp.positionImpulse - impulse0;
+
+ //var impulse = b2Math.MulFV( dImpulse, normal );
+ var impulseX = dImpulse * normalX;
+ var impulseY = dImpulse * normalY;
+
+ //b1.m_position.Subtract( b2Math.MulFV( invMass1, impulse ) );
+ b1_position.x -= invMass1 * impulseX;
+ b1_position.y -= invMass1 * impulseY;
+ b1_rotation -= invI1 * (r1X * impulseY - r1Y * impulseX);
+ b1.m_R.Set(b1_rotation);
+
+ //b2.m_position.Add( b2Math.MulFV( invMass2, impulse ) );
+ b2_position.x += invMass2 * impulseX;
+ b2_position.y += invMass2 * impulseY;
+ b2_rotation += invI2 * (r2X * impulseY - r2Y * impulseX);
+ b2.m_R.Set(b2_rotation);
+ }
+ // Update body rotations
+ b1.m_rotation = b1_rotation;
+ b2.m_rotation = b2_rotation;
+ }
+
+ return minSeparation >= -b2Settings.b2_linearSlop;
+ },
+ PostSolve: function(){
+ for (var i = 0; i < this.m_constraintCount; ++i)
+ {
+ var c = this.m_constraints[ i ];
+ var m = c.manifold;
+
+ for (var j = 0; j < c.pointCount; ++j)
+ {
+ var mPoint = m.points[j];
+ var cPoint = c.points[j];
+ mPoint.normalImpulse = cPoint.normalImpulse;
+ mPoint.tangentImpulse = cPoint.tangentImpulse;
+ }
+ }
+ },
+
+ m_allocator: null,
+ m_constraints: new Array(),
+ m_constraintCount: 0};
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2NullContact.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2NullContact.js
new file mode 100644
index 0000000..faa2bb4
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2NullContact.js
@@ -0,0 +1,65 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+var b2NullContact = Class.create();
+Object.extend(b2NullContact.prototype, b2Contact.prototype);
+Object.extend(b2NullContact.prototype,
+{
+ initialize: function(s1, s2) {
+ // The constructor for b2Contact
+ // initialize instance variables for references
+ this.m_node1 = new b2ContactNode();
+ this.m_node2 = new b2ContactNode();
+ //
+ this.m_flags = 0;
+
+ if (!s1 || !s2){
+ this.m_shape1 = null;
+ this.m_shape2 = null;
+ return;
+ }
+
+ this.m_shape1 = s1;
+ this.m_shape2 = s2;
+
+ this.m_manifoldCount = 0;
+
+ this.m_friction = Math.sqrt(this.m_shape1.m_friction * this.m_shape2.m_friction);
+ this.m_restitution = b2Math.b2Max(this.m_shape1.m_restitution, this.m_shape2.m_restitution);
+
+ this.m_prev = null;
+ this.m_next = null;
+
+ this.m_node1.contact = null;
+ this.m_node1.prev = null;
+ this.m_node1.next = null;
+ this.m_node1.other = null;
+
+ this.m_node2.contact = null;
+ this.m_node2.prev = null;
+ this.m_node2.next = null;
+ this.m_node2.other = null;
+ //
+},
+ Evaluate: function() {},
+ GetManifolds: function(){ return null; }});
+
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2PolyAndCircleContact.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2PolyAndCircleContact.js
new file mode 100644
index 0000000..1f0a7e3
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2PolyAndCircleContact.js
@@ -0,0 +1,103 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+var b2PolyAndCircleContact = Class.create();
+Object.extend(b2PolyAndCircleContact.prototype, b2Contact.prototype);
+Object.extend(b2PolyAndCircleContact.prototype, {
+
+
+ initialize: function(s1, s2) {
+ // The constructor for b2Contact
+ // initialize instance variables for references
+ this.m_node1 = new b2ContactNode();
+ this.m_node2 = new b2ContactNode();
+ //
+ this.m_flags = 0;
+
+ if (!s1 || !s2){
+ this.m_shape1 = null;
+ this.m_shape2 = null;
+ return;
+ }
+
+ this.m_shape1 = s1;
+ this.m_shape2 = s2;
+
+ this.m_manifoldCount = 0;
+
+ this.m_friction = Math.sqrt(this.m_shape1.m_friction * this.m_shape2.m_friction);
+ this.m_restitution = b2Math.b2Max(this.m_shape1.m_restitution, this.m_shape2.m_restitution);
+
+ this.m_prev = null;
+ this.m_next = null;
+
+ this.m_node1.contact = null;
+ this.m_node1.prev = null;
+ this.m_node1.next = null;
+ this.m_node1.other = null;
+
+ this.m_node2.contact = null;
+ this.m_node2.prev = null;
+ this.m_node2.next = null;
+ this.m_node2.other = null;
+ //
+
+ // initialize instance variables for references
+ this.m_manifold = [new b2Manifold()];
+ //
+
+ //super(shape1, shape2);
+
+ b2Settings.b2Assert(this.m_shape1.m_type == b2Shape.e_polyShape);
+ b2Settings.b2Assert(this.m_shape2.m_type == b2Shape.e_circleShape);
+ this.m_manifold[0].pointCount = 0;
+ this.m_manifold[0].points[0].normalImpulse = 0.0;
+ this.m_manifold[0].points[0].tangentImpulse = 0.0;
+ },
+ //~b2PolyAndCircleContact() {}
+
+ Evaluate: function(){
+ b2Collision.b2CollidePolyAndCircle(this.m_manifold[0], this.m_shape1, this.m_shape2, false);
+
+ if (this.m_manifold[0].pointCount > 0)
+ {
+ this.m_manifoldCount = 1;
+ }
+ else
+ {
+ this.m_manifoldCount = 0;
+ }
+ },
+
+ GetManifolds: function()
+ {
+ return this.m_manifold;
+ },
+
+ m_manifold: [new b2Manifold()]})
+
+b2PolyAndCircleContact.Create = function(shape1, shape2, allocator){
+ return new b2PolyAndCircleContact(shape1, shape2);
+ };
+b2PolyAndCircleContact.Destroy = function(contact, allocator){
+ //
+ };
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2PolyContact.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2PolyContact.js
new file mode 100644
index 0000000..e11bf40
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2PolyContact.js
@@ -0,0 +1,163 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+var b2PolyContact = Class.create();
+Object.extend(b2PolyContact.prototype, b2Contact.prototype);
+Object.extend(b2PolyContact.prototype,
+{
+
+ initialize: function(s1, s2) {
+ // The constructor for b2Contact
+ // initialize instance variables for references
+ this.m_node1 = new b2ContactNode();
+ this.m_node2 = new b2ContactNode();
+ //
+ this.m_flags = 0;
+
+ if (!s1 || !s2){
+ this.m_shape1 = null;
+ this.m_shape2 = null;
+ return;
+ }
+
+ this.m_shape1 = s1;
+ this.m_shape2 = s2;
+
+ this.m_manifoldCount = 0;
+
+ this.m_friction = Math.sqrt(this.m_shape1.m_friction * this.m_shape2.m_friction);
+ this.m_restitution = b2Math.b2Max(this.m_shape1.m_restitution, this.m_shape2.m_restitution);
+
+ this.m_prev = null;
+ this.m_next = null;
+
+ this.m_node1.contact = null;
+ this.m_node1.prev = null;
+ this.m_node1.next = null;
+ this.m_node1.other = null;
+
+ this.m_node2.contact = null;
+ this.m_node2.prev = null;
+ this.m_node2.next = null;
+ this.m_node2.other = null;
+ //
+
+ // initialize instance variables for references
+ this.m0 = new b2Manifold();
+ this.m_manifold = [new b2Manifold()];
+ //
+
+ //super(shape1, shape2);
+ //b2Settings.b2Assert(this.m_shape1.m_type == b2Shape.e_polyShape);
+ //b2Settings.b2Assert(this.m_shape2.m_type == b2Shape.e_polyShape);
+ this.m_manifold[0].pointCount = 0;
+ },
+ //~b2PolyContact() {}
+
+ // store temp manifold to reduce calls to new
+ m0: new b2Manifold(),
+
+ Evaluate: function(){
+ var tMani = this.m_manifold[0];
+ // replace memcpy
+ // memcpy(&this.m0, &this.m_manifold, sizeof(b2Manifold));
+ //this.m0.points = new Array(tMani.pointCount);
+ var tPoints = this.m0.points;
+
+ for (var k = 0; k < tMani.pointCount; k++){
+ var tPoint = tPoints[k];
+ var tPoint0 = tMani.points[k];
+ //tPoint.separation = tPoint0.separation;
+ tPoint.normalImpulse = tPoint0.normalImpulse;
+ tPoint.tangentImpulse = tPoint0.tangentImpulse;
+ //tPoint.position.SetV( tPoint0.position );
+
+ tPoint.id = tPoint0.id.Copy();
+
+ /*this.m0.points[k].id.features = new Features();
+ this.m0.points[k].id.features.referenceFace = this.m_manifold[0].points[k].id.features.referenceFace;
+ this.m0.points[k].id.features.incidentEdge = this.m_manifold[0].points[k].id.features.incidentEdge;
+ this.m0.points[k].id.features.incidentVertex = this.m_manifold[0].points[k].id.features.incidentVertex;
+ this.m0.points[k].id.features.flip = this.m_manifold[0].points[k].id.features.flip;*/
+ }
+ //this.m0.normal.SetV( tMani.normal );
+ this.m0.pointCount = tMani.pointCount;
+
+ b2Collision.b2CollidePoly(tMani, this.m_shape1, this.m_shape2, false);
+
+ // Match contact ids to facilitate warm starting.
+ if (tMani.pointCount > 0)
+ {
+ var match = [false, false];
+
+ // Match old contact ids to new contact ids and copy the
+ // stored impulses to warm start the solver.
+ for (var i = 0; i < tMani.pointCount; ++i)
+ {
+ var cp = tMani.points[ i ];
+
+ cp.normalImpulse = 0.0;
+ cp.tangentImpulse = 0.0;
+ var idKey = cp.id.key;
+
+ for (var j = 0; j < this.m0.pointCount; ++j)
+ {
+
+ if (match[j] == true)
+ continue;
+
+ var cp0 = this.m0.points[j];
+ var id0 = cp0.id;
+
+ if (id0.key == idKey)
+ {
+ match[j] = true;
+ cp.normalImpulse = cp0.normalImpulse;
+ cp.tangentImpulse = cp0.tangentImpulse;
+ break;
+ }
+ }
+ }
+
+ this.m_manifoldCount = 1;
+ }
+ else
+ {
+ this.m_manifoldCount = 0;
+ }
+ },
+
+ GetManifolds: function()
+ {
+ return this.m_manifold;
+ },
+
+ m_manifold: [new b2Manifold()]});
+
+b2PolyContact.Create = function(shape1, shape2, allocator){
+ //void* mem = allocator->Allocate(sizeof(b2PolyContact));
+ return new b2PolyContact(shape1, shape2);
+ };
+b2PolyContact.Destroy = function(contact, allocator){
+ //((b2PolyContact*)contact)->~b2PolyContact();
+ //allocator->Free(contact, sizeof(b2PolyContact));
+ };
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2DistanceJoint.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2DistanceJoint.js
new file mode 100644
index 0000000..b91e4779
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2DistanceJoint.js
@@ -0,0 +1,264 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+// C = norm(p2 - p1) - L
+// u = (p2 - p1) / norm(p2 - p1)
+// Cdot = dot(u, v2 + cross(w2, r2) - v1 - cross(w1, r1))
+// J = [-u -cross(r1, u) u cross(r2, u)]
+// K = J * invM * JT
+// = invMass1 + invI1 * cross(r1, u)^2 + invMass2 + invI2 * cross(r2, u)^2
+
+var b2DistanceJoint = Class.create();
+Object.extend(b2DistanceJoint.prototype, b2Joint.prototype);
+Object.extend(b2DistanceJoint.prototype,
+{
+ //--------------- Internals Below -------------------
+
+ initialize: function(def){
+ // The constructor for b2Joint
+ // initialize instance variables for references
+ this.m_node1 = new b2JointNode();
+ this.m_node2 = new b2JointNode();
+ //
+ this.m_type = def.type;
+ this.m_prev = null;
+ this.m_next = null;
+ this.m_body1 = def.body1;
+ this.m_body2 = def.body2;
+ this.m_collideConnected = def.collideConnected;
+ this.m_islandFlag = false;
+ this.m_userData = def.userData;
+ //
+
+ // initialize instance variables for references
+ this.m_localAnchor1 = new b2Vec2();
+ this.m_localAnchor2 = new b2Vec2();
+ this.m_u = new b2Vec2();
+ //
+
+ //super(def);
+
+ var tMat;
+ var tX;
+ var tY;
+ //this.m_localAnchor1 = b2MulT(this.m_body1->m_R, def->anchorPoint1 - this.m_body1->m_position);
+ tMat = this.m_body1.m_R;
+ tX = def.anchorPoint1.x - this.m_body1.m_position.x;
+ tY = def.anchorPoint1.y - this.m_body1.m_position.y;
+ this.m_localAnchor1.x = tX*tMat.col1.x + tY*tMat.col1.y;
+ this.m_localAnchor1.y = tX*tMat.col2.x + tY*tMat.col2.y;
+ //this.m_localAnchor2 = b2MulT(this.m_body2->m_R, def->anchorPoint2 - this.m_body2->m_position);
+ tMat = this.m_body2.m_R;
+ tX = def.anchorPoint2.x - this.m_body2.m_position.x;
+ tY = def.anchorPoint2.y - this.m_body2.m_position.y;
+ this.m_localAnchor2.x = tX*tMat.col1.x + tY*tMat.col1.y;
+ this.m_localAnchor2.y = tX*tMat.col2.x + tY*tMat.col2.y;
+
+ //b2Vec2 d = def->anchorPoint2 - def->anchorPoint1;
+ tX = def.anchorPoint2.x - def.anchorPoint1.x;
+ tY = def.anchorPoint2.y - def.anchorPoint1.y;
+ //this.m_length = d.Length();
+ this.m_length = Math.sqrt(tX*tX + tY*tY);
+ this.m_impulse = 0.0;
+ },
+
+ PrepareVelocitySolver: function(){
+
+ var tMat;
+
+ // Compute the effective mass matrix.
+ //b2Vec2 r1 = b2Mul(this.m_body1->m_R, this.m_localAnchor1);
+ tMat = this.m_body1.m_R;
+ var r1X = tMat.col1.x * this.m_localAnchor1.x + tMat.col2.x * this.m_localAnchor1.y;
+ var r1Y = tMat.col1.y * this.m_localAnchor1.x + tMat.col2.y * this.m_localAnchor1.y;
+ //b2Vec2 r2 = b2Mul(this.m_body2->m_R, this.m_localAnchor2);
+ tMat = this.m_body2.m_R;
+ var r2X = tMat.col1.x * this.m_localAnchor2.x + tMat.col2.x * this.m_localAnchor2.y;
+ var r2Y = tMat.col1.y * this.m_localAnchor2.x + tMat.col2.y * this.m_localAnchor2.y;
+ //this.m_u = this.m_body2->m_position + r2 - this.m_body1->m_position - r1;
+ this.m_u.x = this.m_body2.m_position.x + r2X - this.m_body1.m_position.x - r1X;
+ this.m_u.y = this.m_body2.m_position.y + r2Y - this.m_body1.m_position.y - r1Y;
+
+ // Handle singularity.
+ //float32 length = this.m_u.Length();
+ var length = Math.sqrt(this.m_u.x*this.m_u.x + this.m_u.y*this.m_u.y);
+ if (length > b2Settings.b2_linearSlop)
+ {
+ //this.m_u *= 1.0 / length;
+ this.m_u.Multiply( 1.0 / length );
+ }
+ else
+ {
+ this.m_u.SetZero();
+ }
+
+ //float32 cr1u = b2Cross(r1, this.m_u);
+ var cr1u = (r1X * this.m_u.y - r1Y * this.m_u.x);
+ //float32 cr2u = b2Cross(r2, this.m_u);
+ var cr2u = (r2X * this.m_u.y - r2Y * this.m_u.x);
+ //this.m_mass = this.m_body1->m_invMass + this.m_body1->m_invI * cr1u * cr1u + this.m_body2->m_invMass + this.m_body2->m_invI * cr2u * cr2u;
+ this.m_mass = this.m_body1.m_invMass + this.m_body1.m_invI * cr1u * cr1u + this.m_body2.m_invMass + this.m_body2.m_invI * cr2u * cr2u;
+ //b2Settings.b2Assert(this.m_mass > Number.MIN_VALUE);
+ this.m_mass = 1.0 / this.m_mass;
+
+ if (b2World.s_enableWarmStarting)
+ {
+ //b2Vec2 P = this.m_impulse * this.m_u;
+ var PX = this.m_impulse * this.m_u.x;
+ var PY = this.m_impulse * this.m_u.y;
+ //this.m_body1.m_linearVelocity -= this.m_body1.m_invMass * P;
+ this.m_body1.m_linearVelocity.x -= this.m_body1.m_invMass * PX;
+ this.m_body1.m_linearVelocity.y -= this.m_body1.m_invMass * PY;
+ //this.m_body1.m_angularVelocity -= this.m_body1.m_invI * b2Cross(r1, P);
+ this.m_body1.m_angularVelocity -= this.m_body1.m_invI * (r1X * PY - r1Y * PX);
+ //this.m_body2.m_linearVelocity += this.m_body2.m_invMass * P;
+ this.m_body2.m_linearVelocity.x += this.m_body2.m_invMass * PX;
+ this.m_body2.m_linearVelocity.y += this.m_body2.m_invMass * PY;
+ //this.m_body2.m_angularVelocity += this.m_body2.m_invI * b2Cross(r2, P);
+ this.m_body2.m_angularVelocity += this.m_body2.m_invI * (r2X * PY - r2Y * PX);
+ }
+ else
+ {
+ this.m_impulse = 0.0;
+ }
+
+ },
+
+
+
+ SolveVelocityConstraints: function(step){
+
+ var tMat;
+
+ //b2Vec2 r1 = b2Mul(this.m_body1->m_R, this.m_localAnchor1);
+ tMat = this.m_body1.m_R;
+ var r1X = tMat.col1.x * this.m_localAnchor1.x + tMat.col2.x * this.m_localAnchor1.y;
+ var r1Y = tMat.col1.y * this.m_localAnchor1.x + tMat.col2.y * this.m_localAnchor1.y;
+ //b2Vec2 r2 = b2Mul(this.m_body2->m_R, this.m_localAnchor2);
+ tMat = this.m_body2.m_R;
+ var r2X = tMat.col1.x * this.m_localAnchor2.x + tMat.col2.x * this.m_localAnchor2.y;
+ var r2Y = tMat.col1.y * this.m_localAnchor2.x + tMat.col2.y * this.m_localAnchor2.y;
+
+ // Cdot = dot(u, v + cross(w, r))
+ //b2Vec2 v1 = this.m_body1->m_linearVelocity + b2Cross(this.m_body1->m_angularVelocity, r1);
+ var v1X = this.m_body1.m_linearVelocity.x + (-this.m_body1.m_angularVelocity * r1Y);
+ var v1Y = this.m_body1.m_linearVelocity.y + (this.m_body1.m_angularVelocity * r1X);
+ //b2Vec2 v2 = this.m_body2->m_linearVelocity + b2Cross(this.m_body2->m_angularVelocity, r2);
+ var v2X = this.m_body2.m_linearVelocity.x + (-this.m_body2.m_angularVelocity * r2Y);
+ var v2Y = this.m_body2.m_linearVelocity.y + (this.m_body2.m_angularVelocity * r2X);
+ //float32 Cdot = b2Dot(this.m_u, v2 - v1);
+ var Cdot = (this.m_u.x * (v2X - v1X) + this.m_u.y * (v2Y - v1Y));
+ //float32 impulse = -this.m_mass * Cdot;
+ var impulse = -this.m_mass * Cdot;
+ this.m_impulse += impulse;
+
+ //b2Vec2 P = impulse * this.m_u;
+ var PX = impulse * this.m_u.x;
+ var PY = impulse * this.m_u.y;
+ //this.m_body1->m_linearVelocity -= this.m_body1->m_invMass * P;
+ this.m_body1.m_linearVelocity.x -= this.m_body1.m_invMass * PX;
+ this.m_body1.m_linearVelocity.y -= this.m_body1.m_invMass * PY;
+ //this.m_body1->m_angularVelocity -= this.m_body1->m_invI * b2Cross(r1, P);
+ this.m_body1.m_angularVelocity -= this.m_body1.m_invI * (r1X * PY - r1Y * PX);
+ //this.m_body2->m_linearVelocity += this.m_body2->m_invMass * P;
+ this.m_body2.m_linearVelocity.x += this.m_body2.m_invMass * PX;
+ this.m_body2.m_linearVelocity.y += this.m_body2.m_invMass * PY;
+ //this.m_body2->m_angularVelocity += this.m_body2->m_invI * b2Cross(r2, P);
+ this.m_body2.m_angularVelocity += this.m_body2.m_invI * (r2X * PY - r2Y * PX);
+ },
+
+ SolvePositionConstraints: function(){
+
+ var tMat;
+
+ //b2Vec2 r1 = b2Mul(this.m_body1->m_R, this.m_localAnchor1);
+ tMat = this.m_body1.m_R;
+ var r1X = tMat.col1.x * this.m_localAnchor1.x + tMat.col2.x * this.m_localAnchor1.y;
+ var r1Y = tMat.col1.y * this.m_localAnchor1.x + tMat.col2.y * this.m_localAnchor1.y;
+ //b2Vec2 r2 = b2Mul(this.m_body2->m_R, this.m_localAnchor2);
+ tMat = this.m_body2.m_R;
+ var r2X = tMat.col1.x * this.m_localAnchor2.x + tMat.col2.x * this.m_localAnchor2.y;
+ var r2Y = tMat.col1.y * this.m_localAnchor2.x + tMat.col2.y * this.m_localAnchor2.y;
+ //b2Vec2 d = this.m_body2->m_position + r2 - this.m_body1->m_position - r1;
+ var dX = this.m_body2.m_position.x + r2X - this.m_body1.m_position.x - r1X;
+ var dY = this.m_body2.m_position.y + r2Y - this.m_body1.m_position.y - r1Y;
+
+ //float32 length = d.Normalize();
+ var length = Math.sqrt(dX*dX + dY*dY);
+ dX /= length;
+ dY /= length;
+ //float32 C = length - this.m_length;
+ var C = length - this.m_length;
+ C = b2Math.b2Clamp(C, -b2Settings.b2_maxLinearCorrection, b2Settings.b2_maxLinearCorrection);
+
+ var impulse = -this.m_mass * C;
+ //this.m_u = d;
+ this.m_u.Set(dX, dY);
+ //b2Vec2 P = impulse * this.m_u;
+ var PX = impulse * this.m_u.x;
+ var PY = impulse * this.m_u.y;
+
+ //this.m_body1->m_position -= this.m_body1->m_invMass * P;
+ this.m_body1.m_position.x -= this.m_body1.m_invMass * PX;
+ this.m_body1.m_position.y -= this.m_body1.m_invMass * PY;
+ //this.m_body1->m_rotation -= this.m_body1->m_invI * b2Cross(r1, P);
+ this.m_body1.m_rotation -= this.m_body1.m_invI * (r1X * PY - r1Y * PX);
+ //this.m_body2->m_position += this.m_body2->m_invMass * P;
+ this.m_body2.m_position.x += this.m_body2.m_invMass * PX;
+ this.m_body2.m_position.y += this.m_body2.m_invMass * PY;
+ //this.m_body2->m_rotation -= this.m_body2->m_invI * b2Cross(r2, P);
+ this.m_body2.m_rotation += this.m_body2.m_invI * (r2X * PY - r2Y * PX);
+
+ this.m_body1.m_R.Set(this.m_body1.m_rotation);
+ this.m_body2.m_R.Set(this.m_body2.m_rotation);
+
+ return b2Math.b2Abs(C) < b2Settings.b2_linearSlop;
+
+ },
+
+ GetAnchor1: function(){
+ return b2Math.AddVV(this.m_body1.m_position , b2Math.b2MulMV(this.m_body1.m_R, this.m_localAnchor1));
+ },
+ GetAnchor2: function(){
+ return b2Math.AddVV(this.m_body2.m_position , b2Math.b2MulMV(this.m_body2.m_R, this.m_localAnchor2));
+ },
+
+ GetReactionForce: function(invTimeStep)
+ {
+ //var F = (this.m_impulse * invTimeStep) * this.m_u;
+ var F = new b2Vec2();
+ F.SetV(this.m_u);
+ F.Multiply(this.m_impulse * invTimeStep);
+ return F;
+ },
+
+ GetReactionTorque: function(invTimeStep)
+ {
+ //NOT_USED(invTimeStep);
+ return 0.0;
+ },
+
+ m_localAnchor1: new b2Vec2(),
+ m_localAnchor2: new b2Vec2(),
+ m_u: new b2Vec2(),
+ m_impulse: null,
+ m_mass: null,
+ m_length: null});
+
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2DistanceJointDef.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2DistanceJointDef.js
new file mode 100644
index 0000000..0c89b7a
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2DistanceJointDef.js
@@ -0,0 +1,49 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+var b2DistanceJointDef = Class.create();
+Object.extend(b2DistanceJointDef.prototype, b2JointDef.prototype);
+Object.extend(b2DistanceJointDef.prototype,
+{
+ initialize: function()
+ {
+ // The constructor for b2JointDef
+ this.type = b2Joint.e_unknownJoint;
+ this.userData = null;
+ this.body1 = null;
+ this.body2 = null;
+ this.collideConnected = false;
+ //
+
+ // initialize instance variables for references
+ this.anchorPoint1 = new b2Vec2();
+ this.anchorPoint2 = new b2Vec2();
+ //
+
+ this.type = b2Joint.e_distanceJoint;
+ //this.anchorPoint1.Set(0.0, 0.0);
+ //this.anchorPoint2.Set(0.0, 0.0);
+ },
+
+ anchorPoint1: new b2Vec2(),
+ anchorPoint2: new b2Vec2()});
+
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2GearJoint.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2GearJoint.js
new file mode 100644
index 0000000..daace0b
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2GearJoint.js
@@ -0,0 +1,307 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+
+var b2GearJoint = Class.create();
+Object.extend(b2GearJoint.prototype, b2Joint.prototype);
+Object.extend(b2GearJoint.prototype,
+{
+ GetAnchor1: function(){
+ //return this.m_body1.m_position + b2MulMV(this.m_body1.m_R, this.m_localAnchor1);
+ var tMat = this.m_body1.m_R;
+ return new b2Vec2( this.m_body1.m_position.x + (tMat.col1.x * this.m_localAnchor1.x + tMat.col2.x * this.m_localAnchor1.y),
+ this.m_body1.m_position.y + (tMat.col1.y * this.m_localAnchor1.x + tMat.col2.y * this.m_localAnchor1.y));
+ },
+ GetAnchor2: function(){
+ //return this.m_body2->m_position + b2Mul(this.m_body2->m_R, this.m_localAnchor2);
+ var tMat = this.m_body2.m_R;
+ return new b2Vec2( this.m_body2.m_position.x + (tMat.col1.x * this.m_localAnchor2.x + tMat.col2.x * this.m_localAnchor2.y),
+ this.m_body2.m_position.y + (tMat.col1.y * this.m_localAnchor2.x + tMat.col2.y * this.m_localAnchor2.y));
+ },
+
+ GetReactionForce: function(invTimeStep){
+ //b2Vec2 F(0.0f, 0.0f);
+ return new b2Vec2();
+ },
+ GetReactionTorque: function(invTimeStep){
+ return 0.0;
+ },
+
+ GetRatio: function(){
+ return this.m_ratio;
+ },
+
+ //--------------- Internals Below -------------------
+
+ initialize: function(def){
+ // The constructor for b2Joint
+ // initialize instance variables for references
+ this.m_node1 = new b2JointNode();
+ this.m_node2 = new b2JointNode();
+ //
+ this.m_type = def.type;
+ this.m_prev = null;
+ this.m_next = null;
+ this.m_body1 = def.body1;
+ this.m_body2 = def.body2;
+ this.m_collideConnected = def.collideConnected;
+ this.m_islandFlag = false;
+ this.m_userData = def.userData;
+ //
+
+ // initialize instance variables for references
+ this.m_groundAnchor1 = new b2Vec2();
+ this.m_groundAnchor2 = new b2Vec2();
+ this.m_localAnchor1 = new b2Vec2();
+ this.m_localAnchor2 = new b2Vec2();
+ this.m_J = new b2Jacobian();
+ //
+
+ // parent constructor
+ //super(def);
+
+ //b2Settings.b2Assert(def.joint1.m_type == b2Joint.e_revoluteJoint || def.joint1.m_type == b2Joint.e_prismaticJoint);
+ //b2Settings.b2Assert(def.joint2.m_type == b2Joint.e_revoluteJoint || def.joint2.m_type == b2Joint.e_prismaticJoint);
+ //b2Settings.b2Assert(def.joint1.m_body1.IsStatic());
+ //b2Settings.b2Assert(def.joint2.m_body1.IsStatic());
+
+ this.m_revolute1 = null;
+ this.m_prismatic1 = null;
+ this.m_revolute2 = null;
+ this.m_prismatic2 = null;
+
+ var coordinate1;
+ var coordinate2;
+
+ this.m_ground1 = def.joint1.m_body1;
+ this.m_body1 = def.joint1.m_body2;
+ if (def.joint1.m_type == b2Joint.e_revoluteJoint)
+ {
+ this.m_revolute1 = def.joint1;
+ this.m_groundAnchor1.SetV( this.m_revolute1.m_localAnchor1 );
+ this.m_localAnchor1.SetV( this.m_revolute1.m_localAnchor2 );
+ coordinate1 = this.m_revolute1.GetJointAngle();
+ }
+ else
+ {
+ this.m_prismatic1 = def.joint1;
+ this.m_groundAnchor1.SetV( this.m_prismatic1.m_localAnchor1 );
+ this.m_localAnchor1.SetV( this.m_prismatic1.m_localAnchor2 );
+ coordinate1 = this.m_prismatic1.GetJointTranslation();
+ }
+
+ this.m_ground2 = def.joint2.m_body1;
+ this.m_body2 = def.joint2.m_body2;
+ if (def.joint2.m_type == b2Joint.e_revoluteJoint)
+ {
+ this.m_revolute2 = def.joint2;
+ this.m_groundAnchor2.SetV( this.m_revolute2.m_localAnchor1 );
+ this.m_localAnchor2.SetV( this.m_revolute2.m_localAnchor2 );
+ coordinate2 = this.m_revolute2.GetJointAngle();
+ }
+ else
+ {
+ this.m_prismatic2 = def.joint2;
+ this.m_groundAnchor2.SetV( this.m_prismatic2.m_localAnchor1 );
+ this.m_localAnchor2.SetV( this.m_prismatic2.m_localAnchor2 );
+ coordinate2 = this.m_prismatic2.GetJointTranslation();
+ }
+
+ this.m_ratio = def.ratio;
+
+ this.m_constant = coordinate1 + this.m_ratio * coordinate2;
+
+ this.m_impulse = 0.0;
+
+ },
+
+ PrepareVelocitySolver: function(){
+ var g1 = this.m_ground1;
+ var g2 = this.m_ground2;
+ var b1 = this.m_body1;
+ var b2 = this.m_body2;
+
+ // temp vars
+ var ugX;
+ var ugY;
+ var rX;
+ var rY;
+ var tMat;
+ var tVec;
+ var crug;
+
+ var K = 0.0;
+ this.m_J.SetZero();
+
+ if (this.m_revolute1)
+ {
+ this.m_J.angular1 = -1.0;
+ K += b1.m_invI;
+ }
+ else
+ {
+ //b2Vec2 ug = b2MulMV(g1->m_R, this.m_prismatic1->m_localXAxis1);
+ tMat = g1.m_R;
+ tVec = this.m_prismatic1.m_localXAxis1;
+ ugX = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y;
+ ugY = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y;
+ //b2Vec2 r = b2MulMV(b1->m_R, this.m_localAnchor1);
+ tMat = b1.m_R;
+ rX = tMat.col1.x * this.m_localAnchor1.x + tMat.col2.x * this.m_localAnchor1.y;
+ rY = tMat.col1.y * this.m_localAnchor1.x + tMat.col2.y * this.m_localAnchor1.y;
+
+ //var crug = b2Cross(r, ug);
+ crug = rX * ugY - rY * ugX;
+ //this.m_J.linear1 = -ug;
+ this.m_J.linear1.Set(-ugX, -ugY);
+ this.m_J.angular1 = -crug;
+ K += b1.m_invMass + b1.m_invI * crug * crug;
+ }
+
+ if (this.m_revolute2)
+ {
+ this.m_J.angular2 = -this.m_ratio;
+ K += this.m_ratio * this.m_ratio * b2.m_invI;
+ }
+ else
+ {
+ //b2Vec2 ug = b2Mul(g2->m_R, this.m_prismatic2->m_localXAxis1);
+ tMat = g2.m_R;
+ tVec = this.m_prismatic2.m_localXAxis1;
+ ugX = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y;
+ ugY = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y;
+ //b2Vec2 r = b2Mul(b2->m_R, this.m_localAnchor2);
+ tMat = b2.m_R;
+ rX = tMat.col1.x * this.m_localAnchor2.x + tMat.col2.x * this.m_localAnchor2.y;
+ rY = tMat.col1.y * this.m_localAnchor2.x + tMat.col2.y * this.m_localAnchor2.y;
+ //float32 crug = b2Cross(r, ug);
+ crug = rX * ugY - rY * ugX;
+ //this.m_J.linear2 = -this.m_ratio * ug;
+ this.m_J.linear2.Set(-this.m_ratio*ugX, -this.m_ratio*ugY);
+ this.m_J.angular2 = -this.m_ratio * crug;
+ K += this.m_ratio * this.m_ratio * (b2.m_invMass + b2.m_invI * crug * crug);
+ }
+
+ // Compute effective mass.
+ //b2Settings.b2Assert(K > 0.0);
+ this.m_mass = 1.0 / K;
+
+ // Warm starting.
+ //b1.m_linearVelocity += b1.m_invMass * this.m_impulse * this.m_J.linear1;
+ b1.m_linearVelocity.x += b1.m_invMass * this.m_impulse * this.m_J.linear1.x;
+ b1.m_linearVelocity.y += b1.m_invMass * this.m_impulse * this.m_J.linear1.y;
+ b1.m_angularVelocity += b1.m_invI * this.m_impulse * this.m_J.angular1;
+ //b2.m_linearVelocity += b2.m_invMass * this.m_impulse * this.m_J.linear2;
+ b2.m_linearVelocity.x += b2.m_invMass * this.m_impulse * this.m_J.linear2.x;
+ b2.m_linearVelocity.y += b2.m_invMass * this.m_impulse * this.m_J.linear2.y;
+ b2.m_angularVelocity += b2.m_invI * this.m_impulse * this.m_J.angular2;
+ },
+
+
+ SolveVelocityConstraints: function(step){
+ var b1 = this.m_body1;
+ var b2 = this.m_body2;
+
+ var Cdot = this.m_J.Compute( b1.m_linearVelocity, b1.m_angularVelocity,
+ b2.m_linearVelocity, b2.m_angularVelocity);
+
+ var impulse = -this.m_mass * Cdot;
+ this.m_impulse += impulse;
+
+ b1.m_linearVelocity.x += b1.m_invMass * impulse * this.m_J.linear1.x;
+ b1.m_linearVelocity.y += b1.m_invMass * impulse * this.m_J.linear1.y;
+ b1.m_angularVelocity += b1.m_invI * impulse * this.m_J.angular1;
+ b2.m_linearVelocity.x += b2.m_invMass * impulse * this.m_J.linear2.x;
+ b2.m_linearVelocity.y += b2.m_invMass * impulse * this.m_J.linear2.y;
+ b2.m_angularVelocity += b2.m_invI * impulse * this.m_J.angular2;
+ },
+
+ SolvePositionConstraints: function(){
+ var linearError = 0.0;
+
+ var b1 = this.m_body1;
+ var b2 = this.m_body2;
+
+ var coordinate1;
+ var coordinate2;
+ if (this.m_revolute1)
+ {
+ coordinate1 = this.m_revolute1.GetJointAngle();
+ }
+ else
+ {
+ coordinate1 = this.m_prismatic1.GetJointTranslation();
+ }
+
+ if (this.m_revolute2)
+ {
+ coordinate2 = this.m_revolute2.GetJointAngle();
+ }
+ else
+ {
+ coordinate2 = this.m_prismatic2.GetJointTranslation();
+ }
+
+ var C = this.m_constant - (coordinate1 + this.m_ratio * coordinate2);
+
+ var impulse = -this.m_mass * C;
+
+ b1.m_position.x += b1.m_invMass * impulse * this.m_J.linear1.x;
+ b1.m_position.y += b1.m_invMass * impulse * this.m_J.linear1.y;
+ b1.m_rotation += b1.m_invI * impulse * this.m_J.angular1;
+ b2.m_position.x += b2.m_invMass * impulse * this.m_J.linear2.x;
+ b2.m_position.y += b2.m_invMass * impulse * this.m_J.linear2.y;
+ b2.m_rotation += b2.m_invI * impulse * this.m_J.angular2;
+ b1.m_R.Set(b1.m_rotation);
+ b2.m_R.Set(b2.m_rotation);
+
+ return linearError < b2Settings.b2_linearSlop;
+ },
+
+ m_ground1: null,
+ m_ground2: null,
+
+ // One of these is NULL.
+ m_revolute1: null,
+ m_prismatic1: null,
+
+ // One of these is NULL.
+ m_revolute2: null,
+ m_prismatic2: null,
+
+ m_groundAnchor1: new b2Vec2(),
+ m_groundAnchor2: new b2Vec2(),
+
+ m_localAnchor1: new b2Vec2(),
+ m_localAnchor2: new b2Vec2(),
+
+ m_J: new b2Jacobian(),
+
+ m_constant: null,
+ m_ratio: null,
+
+ // Effective mass
+ m_mass: null,
+
+ // Impulse for accumulation/warm starting.
+ m_impulse: null});
+
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2GearJointDef.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2GearJointDef.js
new file mode 100644
index 0000000..aaf30a3
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2GearJointDef.js
@@ -0,0 +1,50 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+
+// A gear joint is used to connect two joints together. Either joint
+// can be a revolute or prismatic joint. You specify a gear ratio
+// to bind the motions together:
+// coordinate1 + ratio * coordinate2 = constant
+// The ratio can be negative or positive. If one joint is a revolute joint
+// and the other joint is a prismatic joint, then the ratio will have units
+// of length or units of 1/length.
+//
+// RESTRICITON: The revolute and prismatic joints must be attached to
+// a fixed body (which must be body1 on those joints).
+
+var b2GearJointDef = Class.create();
+Object.extend(b2GearJointDef.prototype, b2JointDef.prototype);
+Object.extend(b2GearJointDef.prototype,
+{
+ initialize: function()
+ {
+ this.type = b2Joint.e_gearJoint;
+ this.joint1 = null;
+ this.joint2 = null;
+ this.ratio = 1.0;
+ },
+
+ joint1: null,
+ joint2: null,
+ ratio: null});
+
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2Jacobian.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2Jacobian.js
new file mode 100644
index 0000000..f67a22a
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2Jacobian.js
@@ -0,0 +1,49 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+var b2Jacobian = Class.create();
+b2Jacobian.prototype =
+{
+ linear1: new b2Vec2(),
+ angular1: null,
+ linear2: new b2Vec2(),
+ angular2: null,
+
+ SetZero: function(){
+ this.linear1.SetZero(); this.angular1 = 0.0;
+ this.linear2.SetZero(); this.angular2 = 0.0;
+ },
+ Set: function(x1, a1, x2, a2){
+ this.linear1.SetV(x1); this.angular1 = a1;
+ this.linear2.SetV(x2); this.angular2 = a2;
+ },
+ Compute: function(x1, a1, x2, a2){
+
+ //return b2Math.b2Dot(this.linear1, x1) + this.angular1 * a1 + b2Math.b2Dot(this.linear2, x2) + this.angular2 * a2;
+ return (this.linear1.x*x1.x + this.linear1.y*x1.y) + this.angular1 * a1 + (this.linear2.x*x2.x + this.linear2.y*x2.y) + this.angular2 * a2;
+ },
+ initialize: function() {
+ // initialize instance variables for references
+ this.linear1 = new b2Vec2();
+ this.linear2 = new b2Vec2();
+ //
+}};
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2Joint.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2Joint.js
new file mode 100644
index 0000000..88a5766
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2Joint.js
@@ -0,0 +1,200 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+var b2Joint = Class.create();
+b2Joint.prototype =
+{
+ GetType: function(){
+ return this.m_type;
+ },
+
+ GetAnchor1: function(){return null},
+ GetAnchor2: function(){return null},
+
+ GetReactionForce: function(invTimeStep){return null},
+ GetReactionTorque: function(invTimeStep){return 0.0},
+
+ GetBody1: function()
+ {
+ return this.m_body1;
+ },
+
+ GetBody2: function()
+ {
+ return this.m_body2;
+ },
+
+ GetNext: function(){
+ return this.m_next;
+ },
+
+ GetUserData: function(){
+ return this.m_userData;
+ },
+
+ //--------------- Internals Below -------------------
+
+
+
+ initialize: function(def){
+ // initialize instance variables for references
+ this.m_node1 = new b2JointNode();
+ this.m_node2 = new b2JointNode();
+ //
+
+ this.m_type = def.type;
+ this.m_prev = null;
+ this.m_next = null;
+ this.m_body1 = def.body1;
+ this.m_body2 = def.body2;
+ this.m_collideConnected = def.collideConnected;
+ this.m_islandFlag = false;
+ this.m_userData = def.userData;
+ },
+ //virtual ~b2Joint() {}
+
+ PrepareVelocitySolver: function(){},
+ SolveVelocityConstraints: function(step){},
+
+ // This returns true if the position errors are within tolerance.
+ PreparePositionSolver: function(){},
+ SolvePositionConstraints: function(){return false},
+
+ m_type: 0,
+ m_prev: null,
+ m_next: null,
+ m_node1: new b2JointNode(),
+ m_node2: new b2JointNode(),
+ m_body1: null,
+ m_body2: null,
+
+ m_islandFlag: null,
+ m_collideConnected: null,
+
+ m_userData: null
+
+
+ // ENUMS
+
+ // enum b2JointType
+
+ // enum b2LimitState
+
+};
+b2Joint.Create = function(def, allocator){
+ var joint = null;
+
+ switch (def.type)
+ {
+ case b2Joint.e_distanceJoint:
+ {
+ //void* mem = allocator->Allocate(sizeof(b2DistanceJoint));
+ joint = new b2DistanceJoint(def);
+ }
+ break;
+
+ case b2Joint.e_mouseJoint:
+ {
+ //void* mem = allocator->Allocate(sizeof(b2MouseJoint));
+ joint = new b2MouseJoint(def);
+ }
+ break;
+
+ case b2Joint.e_prismaticJoint:
+ {
+ //void* mem = allocator->Allocate(sizeof(b2PrismaticJoint));
+ joint = new b2PrismaticJoint(def);
+ }
+ break;
+
+ case b2Joint.e_revoluteJoint:
+ {
+ //void* mem = allocator->Allocate(sizeof(b2RevoluteJoint));
+ joint = new b2RevoluteJoint(def);
+ }
+ break;
+
+ case b2Joint.e_pulleyJoint:
+ {
+ //void* mem = allocator->Allocate(sizeof(b2PulleyJoint));
+ joint = new b2PulleyJoint(def);
+ }
+ break;
+
+ case b2Joint.e_gearJoint:
+ {
+ //void* mem = allocator->Allocate(sizeof(b2GearJoint));
+ joint = new b2GearJoint(def);
+ }
+ break;
+
+ default:
+ //b2Settings.b2Assert(false);
+ break;
+ }
+
+ return joint;
+ };
+b2Joint.Destroy = function(joint, allocator){
+ /*joint->~b2Joint();
+ switch (joint.m_type)
+ {
+ case b2Joint.e_distanceJoint:
+ allocator->Free(joint, sizeof(b2DistanceJoint));
+ break;
+
+ case b2Joint.e_mouseJoint:
+ allocator->Free(joint, sizeof(b2MouseJoint));
+ break;
+
+ case b2Joint.e_prismaticJoint:
+ allocator->Free(joint, sizeof(b2PrismaticJoint));
+ break;
+
+ case b2Joint.e_revoluteJoint:
+ allocator->Free(joint, sizeof(b2RevoluteJoint));
+ break;
+
+ case b2Joint.e_pulleyJoint:
+ allocator->Free(joint, sizeof(b2PulleyJoint));
+ break;
+
+ case b2Joint.e_gearJoint:
+ allocator->Free(joint, sizeof(b2GearJoint));
+ break;
+
+ default:
+ b2Assert(false);
+ break;
+ }*/
+ };
+b2Joint.e_unknownJoint = 0;
+b2Joint.e_revoluteJoint = 1;
+b2Joint.e_prismaticJoint = 2;
+b2Joint.e_distanceJoint = 3;
+b2Joint.e_pulleyJoint = 4;
+b2Joint.e_mouseJoint = 5;
+b2Joint.e_gearJoint = 6;
+b2Joint.e_inactiveLimit = 0;
+b2Joint.e_atLowerLimit = 1;
+b2Joint.e_atUpperLimit = 2;
+b2Joint.e_equalLimits = 3;
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2JointDef.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2JointDef.js
new file mode 100644
index 0000000..6b949e6
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2JointDef.js
@@ -0,0 +1,40 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+var b2JointDef = Class.create();
+b2JointDef.prototype =
+{
+
+ initialize: function()
+ {
+ this.type = b2Joint.e_unknownJoint;
+ this.userData = null;
+ this.body1 = null;
+ this.body2 = null;
+ this.collideConnected = false;
+ },
+
+ type: 0,
+ userData: null,
+ body1: null,
+ body2: null,
+ collideConnected: null}
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2JointNode.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2JointNode.js
new file mode 100644
index 0000000..b1c837b
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2JointNode.js
@@ -0,0 +1,33 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+var b2JointNode = Class.create();
+b2JointNode.prototype =
+{
+
+ other: null,
+ joint: null,
+ prev: null,
+ next: null,
+
+
+ initialize: function() {}}
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2MouseJoint.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2MouseJoint.js
new file mode 100644
index 0000000..cf933a9
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2MouseJoint.js
@@ -0,0 +1,234 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+// p = attached point, m = mouse point
+// C = p - m
+// Cdot = v
+// = v + cross(w, r)
+// J = [I r_skew]
+// Identity used:
+// w k % (rx i + ry j) = w * (-ry i + rx j)
+
+var b2MouseJoint = Class.create();
+Object.extend(b2MouseJoint.prototype, b2Joint.prototype);
+Object.extend(b2MouseJoint.prototype,
+{
+ GetAnchor1: function(){
+ return this.m_target;
+ },
+ GetAnchor2: function(){
+ var tVec = b2Math.b2MulMV(this.m_body2.m_R, this.m_localAnchor);
+ tVec.Add(this.m_body2.m_position);
+ return tVec;
+ },
+
+ GetReactionForce: function(invTimeStep)
+ {
+ //b2Vec2 F = invTimeStep * this.m_impulse;
+ var F = new b2Vec2();
+ F.SetV(this.m_impulse);
+ F.Multiply(invTimeStep);
+ return F;
+ },
+
+ GetReactionTorque: function(invTimeStep)
+ {
+ //NOT_USED(invTimeStep);
+ return 0.0;
+ },
+
+ SetTarget: function(target){
+ this.m_body2.WakeUp();
+ this.m_target = target;
+ },
+
+ //--------------- Internals Below -------------------
+
+ initialize: function(def){
+ // The constructor for b2Joint
+ // initialize instance variables for references
+ this.m_node1 = new b2JointNode();
+ this.m_node2 = new b2JointNode();
+ //
+ this.m_type = def.type;
+ this.m_prev = null;
+ this.m_next = null;
+ this.m_body1 = def.body1;
+ this.m_body2 = def.body2;
+ this.m_collideConnected = def.collideConnected;
+ this.m_islandFlag = false;
+ this.m_userData = def.userData;
+ //
+
+ // initialize instance variables for references
+ this.K = new b2Mat22();
+ this.K1 = new b2Mat22();
+ this.K2 = new b2Mat22();
+ this.m_localAnchor = new b2Vec2();
+ this.m_target = new b2Vec2();
+ this.m_impulse = new b2Vec2();
+ this.m_ptpMass = new b2Mat22();
+ this.m_C = new b2Vec2();
+ //
+
+ //super(def);
+
+ this.m_target.SetV(def.target);
+ //this.m_localAnchor = b2Math.b2MulTMV(this.m_body2.m_R, b2Math.SubtractVV( this.m_target, this.m_body2.m_position ) );
+ var tX = this.m_target.x - this.m_body2.m_position.x;
+ var tY = this.m_target.y - this.m_body2.m_position.y;
+ this.m_localAnchor.x = (tX * this.m_body2.m_R.col1.x + tY * this.m_body2.m_R.col1.y);
+ this.m_localAnchor.y = (tX * this.m_body2.m_R.col2.x + tY * this.m_body2.m_R.col2.y);
+
+ this.m_maxForce = def.maxForce;
+ this.m_impulse.SetZero();
+
+ var mass = this.m_body2.m_mass;
+
+ // Frequency
+ var omega = 2.0 * b2Settings.b2_pi * def.frequencyHz;
+
+ // Damping coefficient
+ var d = 2.0 * mass * def.dampingRatio * omega;
+
+ // Spring stiffness
+ var k = mass * omega * omega;
+
+ // magic formulas
+ this.m_gamma = 1.0 / (d + def.timeStep * k);
+ this.m_beta = def.timeStep * k / (d + def.timeStep * k);
+ },
+
+ // Presolve vars
+ K: new b2Mat22(),
+ K1: new b2Mat22(),
+ K2: new b2Mat22(),
+ PrepareVelocitySolver: function(){
+ var b = this.m_body2;
+
+ var tMat;
+
+ // Compute the effective mass matrix.
+ //b2Vec2 r = b2Mul(b.m_R, this.m_localAnchor);
+ tMat = b.m_R;
+ var rX = tMat.col1.x * this.m_localAnchor.x + tMat.col2.x * this.m_localAnchor.y;
+ var rY = tMat.col1.y * this.m_localAnchor.x + tMat.col2.y * this.m_localAnchor.y;
+
+ // this.K = [(1/m1 + 1/m2) * eye(2) - skew(r1) * invI1 * skew(r1) - skew(r2) * invI2 * skew(r2)]
+ // = [1/m1+1/m2 0 ] + invI1 * [r1.y*r1.y -r1.x*r1.y] + invI2 * [r1.y*r1.y -r1.x*r1.y]
+ // [ 0 1/m1+1/m2] [-r1.x*r1.y r1.x*r1.x] [-r1.x*r1.y r1.x*r1.x]
+ var invMass = b.m_invMass;
+ var invI = b.m_invI;
+
+ //b2Mat22 this.K1;
+ this.K1.col1.x = invMass; this.K1.col2.x = 0.0;
+ this.K1.col1.y = 0.0; this.K1.col2.y = invMass;
+
+ //b2Mat22 this.K2;
+ this.K2.col1.x = invI * rY * rY; this.K2.col2.x = -invI * rX * rY;
+ this.K2.col1.y = -invI * rX * rY; this.K2.col2.y = invI * rX * rX;
+
+ //b2Mat22 this.K = this.K1 + this.K2;
+ this.K.SetM(this.K1);
+ this.K.AddM(this.K2);
+ this.K.col1.x += this.m_gamma;
+ this.K.col2.y += this.m_gamma;
+
+ //this.m_ptpMass = this.K.Invert();
+ this.K.Invert(this.m_ptpMass);
+
+ //this.m_C = b.m_position + r - this.m_target;
+ this.m_C.x = b.m_position.x + rX - this.m_target.x;
+ this.m_C.y = b.m_position.y + rY - this.m_target.y;
+
+ // Cheat with some damping
+ b.m_angularVelocity *= 0.98;
+
+ // Warm starting.
+ //b2Vec2 P = this.m_impulse;
+ var PX = this.m_impulse.x;
+ var PY = this.m_impulse.y;
+ //b.m_linearVelocity += invMass * P;
+ b.m_linearVelocity.x += invMass * PX;
+ b.m_linearVelocity.y += invMass * PY;
+ //b.m_angularVelocity += invI * b2Cross(r, P);
+ b.m_angularVelocity += invI * (rX * PY - rY * PX);
+ },
+
+
+ SolveVelocityConstraints: function(step){
+ var body = this.m_body2;
+
+ var tMat;
+
+ // Compute the effective mass matrix.
+ //b2Vec2 r = b2Mul(body.m_R, this.m_localAnchor);
+ tMat = body.m_R;
+ var rX = tMat.col1.x * this.m_localAnchor.x + tMat.col2.x * this.m_localAnchor.y;
+ var rY = tMat.col1.y * this.m_localAnchor.x + tMat.col2.y * this.m_localAnchor.y;
+
+ // Cdot = v + cross(w, r)
+ //b2Vec2 Cdot = body->m_linearVelocity + b2Cross(body->m_angularVelocity, r);
+ var CdotX = body.m_linearVelocity.x + (-body.m_angularVelocity * rY);
+ var CdotY = body.m_linearVelocity.y + (body.m_angularVelocity * rX);
+ //b2Vec2 impulse = -b2Mul(this.m_ptpMass, Cdot + (this.m_beta * step->inv_dt) * this.m_C + this.m_gamma * this.m_impulse);
+ tMat = this.m_ptpMass;
+ var tX = CdotX + (this.m_beta * step.inv_dt) * this.m_C.x + this.m_gamma * this.m_impulse.x;
+ var tY = CdotY + (this.m_beta * step.inv_dt) * this.m_C.y + this.m_gamma * this.m_impulse.y;
+ var impulseX = -(tMat.col1.x * tX + tMat.col2.x * tY);
+ var impulseY = -(tMat.col1.y * tX + tMat.col2.y * tY);
+
+ var oldImpulseX = this.m_impulse.x;
+ var oldImpulseY = this.m_impulse.y;
+ //this.m_impulse += impulse;
+ this.m_impulse.x += impulseX;
+ this.m_impulse.y += impulseY;
+ var length = this.m_impulse.Length();
+ if (length > step.dt * this.m_maxForce)
+ {
+ //this.m_impulse *= step.dt * this.m_maxForce / length;
+ this.m_impulse.Multiply(step.dt * this.m_maxForce / length);
+ }
+ //impulse = this.m_impulse - oldImpulse;
+ impulseX = this.m_impulse.x - oldImpulseX;
+ impulseY = this.m_impulse.y - oldImpulseY;
+
+ //body.m_linearVelocity += body->m_invMass * impulse;
+ body.m_linearVelocity.x += body.m_invMass * impulseX;
+ body.m_linearVelocity.y += body.m_invMass * impulseY;
+ //body.m_angularVelocity += body->m_invI * b2Cross(r, impulse);
+ body.m_angularVelocity += body.m_invI * (rX * impulseY - rY * impulseX);
+ },
+ SolvePositionConstraints: function(){
+ return true;
+ },
+
+ m_localAnchor: new b2Vec2(),
+ m_target: new b2Vec2(),
+ m_impulse: new b2Vec2(),
+
+ m_ptpMass: new b2Mat22(),
+ m_C: new b2Vec2(),
+ m_maxForce: null,
+ m_beta: null,
+ m_gamma: null});
+
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2MouseJointDef.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2MouseJointDef.js
new file mode 100644
index 0000000..b9a3b5c
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2MouseJointDef.js
@@ -0,0 +1,53 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+var b2MouseJointDef = Class.create();
+Object.extend(b2MouseJointDef.prototype, b2JointDef.prototype);
+Object.extend(b2MouseJointDef.prototype,
+{
+ initialize: function()
+ {
+ // The constructor for b2JointDef
+ this.type = b2Joint.e_unknownJoint;
+ this.userData = null;
+ this.body1 = null;
+ this.body2 = null;
+ this.collideConnected = false;
+ //
+
+ // initialize instance variables for references
+ this.target = new b2Vec2();
+ //
+
+ this.type = b2Joint.e_mouseJoint;
+ this.maxForce = 0.0;
+ this.frequencyHz = 5.0;
+ this.dampingRatio = 0.7;
+ this.timeStep = 1.0 / 60.0;
+ },
+
+ target: new b2Vec2(),
+ maxForce: null,
+ frequencyHz: null,
+ dampingRatio: null,
+ timeStep: null});
+
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PrismaticJoint.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PrismaticJoint.js
new file mode 100644
index 0000000..94bfcf8
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PrismaticJoint.js
@@ -0,0 +1,676 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+// Linear constraint (point-to-line)
+// d = p2 - p1 = x2 + r2 - x1 - r1
+// C = dot(ay1, d)
+// Cdot = dot(d, cross(w1, ay1)) + dot(ay1, v2 + cross(w2, r2) - v1 - cross(w1, r1))
+// = -dot(ay1, v1) - dot(cross(d + r1, ay1), w1) + dot(ay1, v2) + dot(cross(r2, ay1), v2)
+// J = [-ay1 -cross(d+r1,ay1) ay1 cross(r2,ay1)]
+//
+// Angular constraint
+// C = a2 - a1 + a_initial
+// Cdot = w2 - w1
+// J = [0 0 -1 0 0 1]
+
+// Motor/Limit linear constraint
+// C = dot(ax1, d)
+// Cdot = = -dot(ax1, v1) - dot(cross(d + r1, ax1), w1) + dot(ax1, v2) + dot(cross(r2, ax1), v2)
+// J = [-ax1 -cross(d+r1,ax1) ax1 cross(r2,ax1)]
+
+
+var b2PrismaticJoint = Class.create();
+Object.extend(b2PrismaticJoint.prototype, b2Joint.prototype);
+Object.extend(b2PrismaticJoint.prototype,
+{
+ GetAnchor1: function(){
+ var b1 = this.m_body1;
+ //return b2Math.AddVV(b1.m_position, b2Math.b2MulMV(b1.m_R, this.m_localAnchor1));
+ var tVec = new b2Vec2();
+ tVec.SetV(this.m_localAnchor1);
+ tVec.MulM(b1.m_R);
+ tVec.Add(b1.m_position);
+ return tVec;
+ },
+ GetAnchor2: function(){
+ var b2 = this.m_body2;
+ //return b2Math.AddVV(b2.m_position, b2Math.b2MulMV(b2.m_R, this.m_localAnchor2));
+ var tVec = new b2Vec2();
+ tVec.SetV(this.m_localAnchor2);
+ tVec.MulM(b2.m_R);
+ tVec.Add(b2.m_position);
+ return tVec;
+ },
+ GetJointTranslation: function(){
+ var b1 = this.m_body1;
+ var b2 = this.m_body2;
+
+ var tMat;
+
+ //var r1 = b2Math.b2MulMV(b1.m_R, this.m_localAnchor1);
+ tMat = b1.m_R;
+ var r1X = tMat.col1.x * this.m_localAnchor1.x + tMat.col2.x * this.m_localAnchor1.y;
+ var r1Y = tMat.col1.y * this.m_localAnchor1.x + tMat.col2.y * this.m_localAnchor1.y;
+ //var r2 = b2Math.b2MulMV(b2.m_R, this.m_localAnchor2);
+ tMat = b2.m_R;
+ var r2X = tMat.col1.x * this.m_localAnchor2.x + tMat.col2.x * this.m_localAnchor2.y;
+ var r2Y = tMat.col1.y * this.m_localAnchor2.x + tMat.col2.y * this.m_localAnchor2.y;
+ //var p1 = b2Math.AddVV(b1.m_position , r1);
+ var p1X = b1.m_position.x + r1X;
+ var p1Y = b1.m_position.y + r1Y;
+ //var p2 = b2Math.AddVV(b2.m_position , r2);
+ var p2X = b2.m_position.x + r2X;
+ var p2Y = b2.m_position.y + r2Y;
+ //var d = b2Math.SubtractVV(p2, p1);
+ var dX = p2X - p1X;
+ var dY = p2Y - p1Y;
+ //var ax1 = b2Math.b2MulMV(b1.m_R, this.m_localXAxis1);
+ tMat = b1.m_R;
+ var ax1X = tMat.col1.x * this.m_localXAxis1.x + tMat.col2.x * this.m_localXAxis1.y;
+ var ax1Y = tMat.col1.y * this.m_localXAxis1.x + tMat.col2.y * this.m_localXAxis1.y;
+
+ //var translation = b2Math.b2Dot(ax1, d);
+ var translation = ax1X*dX + ax1Y*dY;
+ return translation;
+ },
+ GetJointSpeed: function(){
+ var b1 = this.m_body1;
+ var b2 = this.m_body2;
+
+ var tMat;
+
+ //var r1 = b2Math.b2MulMV(b1.m_R, this.m_localAnchor1);
+ tMat = b1.m_R;
+ var r1X = tMat.col1.x * this.m_localAnchor1.x + tMat.col2.x * this.m_localAnchor1.y;
+ var r1Y = tMat.col1.y * this.m_localAnchor1.x + tMat.col2.y * this.m_localAnchor1.y;
+ //var r2 = b2Math.b2MulMV(b2.m_R, this.m_localAnchor2);
+ tMat = b2.m_R;
+ var r2X = tMat.col1.x * this.m_localAnchor2.x + tMat.col2.x * this.m_localAnchor2.y;
+ var r2Y = tMat.col1.y * this.m_localAnchor2.x + tMat.col2.y * this.m_localAnchor2.y;
+ //var p1 = b2Math.AddVV(b1.m_position , r1);
+ var p1X = b1.m_position.x + r1X;
+ var p1Y = b1.m_position.y + r1Y;
+ //var p2 = b2Math.AddVV(b2.m_position , r2);
+ var p2X = b2.m_position.x + r2X;
+ var p2Y = b2.m_position.y + r2Y;
+ //var d = b2Math.SubtractVV(p2, p1);
+ var dX = p2X - p1X;
+ var dY = p2Y - p1Y;
+ //var ax1 = b2Math.b2MulMV(b1.m_R, this.m_localXAxis1);
+ tMat = b1.m_R;
+ var ax1X = tMat.col1.x * this.m_localXAxis1.x + tMat.col2.x * this.m_localXAxis1.y;
+ var ax1Y = tMat.col1.y * this.m_localXAxis1.x + tMat.col2.y * this.m_localXAxis1.y;
+
+ var v1 = b1.m_linearVelocity;
+ var v2 = b2.m_linearVelocity;
+ var w1 = b1.m_angularVelocity;
+ var w2 = b2.m_angularVelocity;
+
+ //var speed = b2Math.b2Dot(d, b2Math.b2CrossFV(w1, ax1)) + b2Math.b2Dot(ax1, b2Math.SubtractVV( b2Math.SubtractVV( b2Math.AddVV( v2 , b2Math.b2CrossFV(w2, r2)) , v1) , b2Math.b2CrossFV(w1, r1)));
+ //var b2D = (dX*(-w1 * ax1Y) + dY*(w1 * ax1X));
+ //var b2D2 = (ax1X * ((( v2.x + (-w2 * r2Y)) - v1.x) - (-w1 * r1Y)) + ax1Y * ((( v2.y + (w2 * r2X)) - v1.y) - (w1 * r1X)));
+ var speed = (dX*(-w1 * ax1Y) + dY*(w1 * ax1X)) + (ax1X * ((( v2.x + (-w2 * r2Y)) - v1.x) - (-w1 * r1Y)) + ax1Y * ((( v2.y + (w2 * r2X)) - v1.y) - (w1 * r1X)));
+
+ return speed;
+ },
+ GetMotorForce: function(invTimeStep){
+ return invTimeStep * this.m_motorImpulse;
+ },
+
+ SetMotorSpeed: function(speed)
+ {
+ this.m_motorSpeed = speed;
+ },
+
+ SetMotorForce: function(force)
+ {
+ this.m_maxMotorForce = force;
+ },
+
+ GetReactionForce: function(invTimeStep)
+ {
+ var tImp = invTimeStep * this.m_limitImpulse;
+ var tMat;
+
+ //var ax1 = b2Math.b2MulMV(this.m_body1.m_R, this.m_localXAxis1);
+ tMat = this.m_body1.m_R;
+ var ax1X = tImp * (tMat.col1.x * this.m_localXAxis1.x + tMat.col2.x * this.m_localXAxis1.y);
+ var ax1Y = tImp * (tMat.col1.y * this.m_localXAxis1.x + tMat.col2.y * this.m_localXAxis1.y);
+ //var ay1 = b2Math.b2MulMV(this.m_body1.m_R, this.m_localYAxis1);
+ var ay1X = tImp * (tMat.col1.x * this.m_localYAxis1.x + tMat.col2.x * this.m_localYAxis1.y);
+ var ay1Y = tImp * (tMat.col1.y * this.m_localYAxis1.x + tMat.col2.y * this.m_localYAxis1.y);
+
+ //return (invTimeStep * this.m_limitImpulse) * ax1 + (invTimeStep * this.m_linearImpulse) * ay1;
+
+ return new b2Vec2(ax1X+ay1X, ax1Y+ay1Y);
+ },
+
+ GetReactionTorque: function(invTimeStep)
+ {
+ return invTimeStep * this.m_angularImpulse;
+ },
+
+
+ //--------------- Internals Below -------------------
+
+ initialize: function(def){
+ // The constructor for b2Joint
+ // initialize instance variables for references
+ this.m_node1 = new b2JointNode();
+ this.m_node2 = new b2JointNode();
+ //
+ this.m_type = def.type;
+ this.m_prev = null;
+ this.m_next = null;
+ this.m_body1 = def.body1;
+ this.m_body2 = def.body2;
+ this.m_collideConnected = def.collideConnected;
+ this.m_islandFlag = false;
+ this.m_userData = def.userData;
+ //
+
+ // initialize instance variables for references
+ this.m_localAnchor1 = new b2Vec2();
+ this.m_localAnchor2 = new b2Vec2();
+ this.m_localXAxis1 = new b2Vec2();
+ this.m_localYAxis1 = new b2Vec2();
+ this.m_linearJacobian = new b2Jacobian();
+ this.m_motorJacobian = new b2Jacobian();
+ //
+
+ //super(def);
+
+ var tMat;
+ var tX;
+ var tY;
+
+ //this.m_localAnchor1 = b2Math.b2MulTMV(this.m_body1.m_R, b2Math.SubtractVV(def.anchorPoint , this.m_body1.m_position));
+ tMat = this.m_body1.m_R;
+ tX = (def.anchorPoint.x - this.m_body1.m_position.x);
+ tY = (def.anchorPoint.y - this.m_body1.m_position.y);
+ this.m_localAnchor1.Set((tX*tMat.col1.x + tY*tMat.col1.y), (tX*tMat.col2.x + tY*tMat.col2.y));
+
+ //this.m_localAnchor2 = b2Math.b2MulTMV(this.m_body2.m_R, b2Math.SubtractVV(def.anchorPoint , this.m_body2.m_position));
+ tMat = this.m_body2.m_R;
+ tX = (def.anchorPoint.x - this.m_body2.m_position.x);
+ tY = (def.anchorPoint.y - this.m_body2.m_position.y);
+ this.m_localAnchor2.Set((tX*tMat.col1.x + tY*tMat.col1.y), (tX*tMat.col2.x + tY*tMat.col2.y));
+
+ //this.m_localXAxis1 = b2Math.b2MulTMV(this.m_body1.m_R, def.axis);
+ tMat = this.m_body1.m_R;
+ tX = def.axis.x;
+ tY = def.axis.y;
+ this.m_localXAxis1.Set((tX*tMat.col1.x + tY*tMat.col1.y), (tX*tMat.col2.x + tY*tMat.col2.y));
+
+ //this.m_localYAxis1 = b2Math.b2CrossFV(1.0, this.m_localXAxis1);
+ this.m_localYAxis1.x = -this.m_localXAxis1.y;
+ this.m_localYAxis1.y = this.m_localXAxis1.x;
+
+ this.m_initialAngle = this.m_body2.m_rotation - this.m_body1.m_rotation;
+
+ this.m_linearJacobian.SetZero();
+ this.m_linearMass = 0.0;
+ this.m_linearImpulse = 0.0;
+
+ this.m_angularMass = 0.0;
+ this.m_angularImpulse = 0.0;
+
+ this.m_motorJacobian.SetZero();
+ this.m_motorMass = 0.0;
+ this.m_motorImpulse = 0.0;
+ this.m_limitImpulse = 0.0;
+ this.m_limitPositionImpulse = 0.0;
+
+ this.m_lowerTranslation = def.lowerTranslation;
+ this.m_upperTranslation = def.upperTranslation;
+ this.m_maxMotorForce = def.motorForce;
+ this.m_motorSpeed = def.motorSpeed;
+ this.m_enableLimit = def.enableLimit;
+ this.m_enableMotor = def.enableMotor;
+ },
+
+ PrepareVelocitySolver: function(){
+ var b1 = this.m_body1;
+ var b2 = this.m_body2;
+
+ var tMat;
+
+ // Compute the effective masses.
+ //b2Vec2 r1 = b2Mul(b1->m_R, this.m_localAnchor1);
+ tMat = b1.m_R;
+ var r1X = tMat.col1.x * this.m_localAnchor1.x + tMat.col2.x * this.m_localAnchor1.y;
+ var r1Y = tMat.col1.y * this.m_localAnchor1.x + tMat.col2.y * this.m_localAnchor1.y;
+ //b2Vec2 r2 = b2Mul(b2->m_R, this.m_localAnchor2);
+ tMat = b2.m_R;
+ var r2X = tMat.col1.x * this.m_localAnchor2.x + tMat.col2.x * this.m_localAnchor2.y;
+ var r2Y = tMat.col1.y * this.m_localAnchor2.x + tMat.col2.y * this.m_localAnchor2.y;
+
+ //float32 invMass1 = b1->m_invMass, invMass2 = b2->m_invMass;
+ var invMass1 = b1.m_invMass;
+ var invMass2 = b2.m_invMass;
+ //float32 invI1 = b1->m_invI, invI2 = b2->m_invI;
+ var invI1 = b1.m_invI;
+ var invI2 = b2.m_invI;
+
+ // Compute point to line constraint effective mass.
+ // J = [-ay1 -cross(d+r1,ay1) ay1 cross(r2,ay1)]
+ //b2Vec2 ay1 = b2Mul(b1->m_R, this.m_localYAxis1);
+ tMat = b1.m_R;
+ var ay1X = tMat.col1.x * this.m_localYAxis1.x + tMat.col2.x * this.m_localYAxis1.y;
+ var ay1Y = tMat.col1.y * this.m_localYAxis1.x + tMat.col2.y * this.m_localYAxis1.y;
+ //b2Vec2 e = b2->m_position + r2 - b1->m_position;
+ var eX = b2.m_position.x + r2X - b1.m_position.x;
+ var eY = b2.m_position.y + r2Y - b1.m_position.y;
+
+ //this.m_linearJacobian.Set(-ay1, -b2Math.b2Cross(e, ay1), ay1, b2Math.b2Cross(r2, ay1));
+ this.m_linearJacobian.linear1.x = -ay1X;
+ this.m_linearJacobian.linear1.y = -ay1Y;
+ this.m_linearJacobian.linear2.x = ay1X;
+ this.m_linearJacobian.linear2.y = ay1Y;
+ this.m_linearJacobian.angular1 = -(eX * ay1Y - eY * ay1X);
+ this.m_linearJacobian.angular2 = r2X * ay1Y - r2Y * ay1X;
+
+ this.m_linearMass = invMass1 + invI1 * this.m_linearJacobian.angular1 * this.m_linearJacobian.angular1 +
+ invMass2 + invI2 * this.m_linearJacobian.angular2 * this.m_linearJacobian.angular2;
+ //b2Settings.b2Assert(this.m_linearMass > Number.MIN_VALUE);
+ this.m_linearMass = 1.0 / this.m_linearMass;
+
+ // Compute angular constraint effective mass.
+ this.m_angularMass = 1.0 / (invI1 + invI2);
+
+ // Compute motor and limit terms.
+ if (this.m_enableLimit || this.m_enableMotor)
+ {
+ // The motor and limit share a Jacobian and effective mass.
+ //b2Vec2 ax1 = b2Mul(b1->m_R, this.m_localXAxis1);
+ tMat = b1.m_R;
+ var ax1X = tMat.col1.x * this.m_localXAxis1.x + tMat.col2.x * this.m_localXAxis1.y;
+ var ax1Y = tMat.col1.y * this.m_localXAxis1.x + tMat.col2.y * this.m_localXAxis1.y;
+ //this.m_motorJacobian.Set(-ax1, -b2Cross(e, ax1), ax1, b2Cross(r2, ax1));
+ this.m_motorJacobian.linear1.x = -ax1X; this.m_motorJacobian.linear1.y = -ax1Y;
+ this.m_motorJacobian.linear2.x = ax1X; this.m_motorJacobian.linear2.y = ax1Y;
+ this.m_motorJacobian.angular1 = -(eX * ax1Y - eY * ax1X);
+ this.m_motorJacobian.angular2 = r2X * ax1Y - r2Y * ax1X;
+
+ this.m_motorMass = invMass1 + invI1 * this.m_motorJacobian.angular1 * this.m_motorJacobian.angular1 +
+ invMass2 + invI2 * this.m_motorJacobian.angular2 * this.m_motorJacobian.angular2;
+ //b2Settings.b2Assert(this.m_motorMass > Number.MIN_VALUE);
+ this.m_motorMass = 1.0 / this.m_motorMass;
+
+ if (this.m_enableLimit)
+ {
+ //b2Vec2 d = e - r1;
+ var dX = eX - r1X;
+ var dY = eY - r1Y;
+ //float32 jointTranslation = b2Dot(ax1, d);
+ var jointTranslation = ax1X * dX + ax1Y * dY;
+ if (b2Math.b2Abs(this.m_upperTranslation - this.m_lowerTranslation) < 2.0 * b2Settings.b2_linearSlop)
+ {
+ this.m_limitState = b2Joint.e_equalLimits;
+ }
+ else if (jointTranslation <= this.m_lowerTranslation)
+ {
+ if (this.m_limitState != b2Joint.e_atLowerLimit)
+ {
+ this.m_limitImpulse = 0.0;
+ }
+ this.m_limitState = b2Joint.e_atLowerLimit;
+ }
+ else if (jointTranslation >= this.m_upperTranslation)
+ {
+ if (this.m_limitState != b2Joint.e_atUpperLimit)
+ {
+ this.m_limitImpulse = 0.0;
+ }
+ this.m_limitState = b2Joint.e_atUpperLimit;
+ }
+ else
+ {
+ this.m_limitState = b2Joint.e_inactiveLimit;
+ this.m_limitImpulse = 0.0;
+ }
+ }
+ }
+
+ if (this.m_enableMotor == false)
+ {
+ this.m_motorImpulse = 0.0;
+ }
+
+ if (this.m_enableLimit == false)
+ {
+ this.m_limitImpulse = 0.0;
+ }
+
+ if (b2World.s_enableWarmStarting)
+ {
+ //b2Vec2 P1 = this.m_linearImpulse * this.m_linearJacobian.linear1 + (this.m_motorImpulse + this.m_limitImpulse) * this.m_motorJacobian.linear1;
+ var P1X = this.m_linearImpulse * this.m_linearJacobian.linear1.x + (this.m_motorImpulse + this.m_limitImpulse) * this.m_motorJacobian.linear1.x;
+ var P1Y = this.m_linearImpulse * this.m_linearJacobian.linear1.y + (this.m_motorImpulse + this.m_limitImpulse) * this.m_motorJacobian.linear1.y;
+ //b2Vec2 P2 = this.m_linearImpulse * this.m_linearJacobian.linear2 + (this.m_motorImpulse + this.m_limitImpulse) * this.m_motorJacobian.linear2;
+ var P2X = this.m_linearImpulse * this.m_linearJacobian.linear2.x + (this.m_motorImpulse + this.m_limitImpulse) * this.m_motorJacobian.linear2.x;
+ var P2Y = this.m_linearImpulse * this.m_linearJacobian.linear2.y + (this.m_motorImpulse + this.m_limitImpulse) * this.m_motorJacobian.linear2.y;
+ //float32 L1 = this.m_linearImpulse * this.m_linearJacobian.angular1 - this.m_angularImpulse + (this.m_motorImpulse + this.m_limitImpulse) * this.m_motorJacobian.angular1;
+ var L1 = this.m_linearImpulse * this.m_linearJacobian.angular1 - this.m_angularImpulse + (this.m_motorImpulse + this.m_limitImpulse) * this.m_motorJacobian.angular1;
+ //float32 L2 = this.m_linearImpulse * this.m_linearJacobian.angular2 + this.m_angularImpulse + (this.m_motorImpulse + this.m_limitImpulse) * this.m_motorJacobian.angular2;
+ var L2 = this.m_linearImpulse * this.m_linearJacobian.angular2 + this.m_angularImpulse + (this.m_motorImpulse + this.m_limitImpulse) * this.m_motorJacobian.angular2;
+
+ //b1->m_linearVelocity += invMass1 * P1;
+ b1.m_linearVelocity.x += invMass1 * P1X;
+ b1.m_linearVelocity.y += invMass1 * P1Y;
+ //b1->m_angularVelocity += invI1 * L1;
+ b1.m_angularVelocity += invI1 * L1;
+
+ //b2->m_linearVelocity += invMass2 * P2;
+ b2.m_linearVelocity.x += invMass2 * P2X;
+ b2.m_linearVelocity.y += invMass2 * P2Y;
+ //b2->m_angularVelocity += invI2 * L2;
+ b2.m_angularVelocity += invI2 * L2;
+ }
+ else
+ {
+ this.m_linearImpulse = 0.0;
+ this.m_angularImpulse = 0.0;
+ this.m_limitImpulse = 0.0;
+ this.m_motorImpulse = 0.0;
+ }
+
+ this.m_limitPositionImpulse = 0.0;
+
+ },
+
+ SolveVelocityConstraints: function(step){
+ var b1 = this.m_body1;
+ var b2 = this.m_body2;
+
+ var invMass1 = b1.m_invMass;
+ var invMass2 = b2.m_invMass;
+ var invI1 = b1.m_invI;
+ var invI2 = b2.m_invI;
+
+ var oldLimitImpulse;
+
+ // Solve linear constraint.
+ var linearCdot = this.m_linearJacobian.Compute(b1.m_linearVelocity, b1.m_angularVelocity, b2.m_linearVelocity, b2.m_angularVelocity);
+ var linearImpulse = -this.m_linearMass * linearCdot;
+ this.m_linearImpulse += linearImpulse;
+
+ //b1->m_linearVelocity += (invMass1 * linearImpulse) * this.m_linearJacobian.linear1;
+ b1.m_linearVelocity.x += (invMass1 * linearImpulse) * this.m_linearJacobian.linear1.x;
+ b1.m_linearVelocity.y += (invMass1 * linearImpulse) * this.m_linearJacobian.linear1.y;
+ //b1->m_angularVelocity += invI1 * linearImpulse * this.m_linearJacobian.angular1;
+ b1.m_angularVelocity += invI1 * linearImpulse * this.m_linearJacobian.angular1;
+
+ //b2->m_linearVelocity += (invMass2 * linearImpulse) * this.m_linearJacobian.linear2;
+ b2.m_linearVelocity.x += (invMass2 * linearImpulse) * this.m_linearJacobian.linear2.x;
+ b2.m_linearVelocity.y += (invMass2 * linearImpulse) * this.m_linearJacobian.linear2.y;
+ //b2.m_angularVelocity += invI2 * linearImpulse * this.m_linearJacobian.angular2;
+ b2.m_angularVelocity += invI2 * linearImpulse * this.m_linearJacobian.angular2;
+
+ // Solve angular constraint.
+ var angularCdot = b2.m_angularVelocity - b1.m_angularVelocity;
+ var angularImpulse = -this.m_angularMass * angularCdot;
+ this.m_angularImpulse += angularImpulse;
+
+ b1.m_angularVelocity -= invI1 * angularImpulse;
+ b2.m_angularVelocity += invI2 * angularImpulse;
+
+ // Solve linear motor constraint.
+ if (this.m_enableMotor && this.m_limitState != b2Joint.e_equalLimits)
+ {
+ var motorCdot = this.m_motorJacobian.Compute(b1.m_linearVelocity, b1.m_angularVelocity, b2.m_linearVelocity, b2.m_angularVelocity) - this.m_motorSpeed;
+ var motorImpulse = -this.m_motorMass * motorCdot;
+ var oldMotorImpulse = this.m_motorImpulse;
+ this.m_motorImpulse = b2Math.b2Clamp(this.m_motorImpulse + motorImpulse, -step.dt * this.m_maxMotorForce, step.dt * this.m_maxMotorForce);
+ motorImpulse = this.m_motorImpulse - oldMotorImpulse;
+
+ //b1.m_linearVelocity += (invMass1 * motorImpulse) * this.m_motorJacobian.linear1;
+ b1.m_linearVelocity.x += (invMass1 * motorImpulse) * this.m_motorJacobian.linear1.x;
+ b1.m_linearVelocity.y += (invMass1 * motorImpulse) * this.m_motorJacobian.linear1.y;
+ //b1.m_angularVelocity += invI1 * motorImpulse * this.m_motorJacobian.angular1;
+ b1.m_angularVelocity += invI1 * motorImpulse * this.m_motorJacobian.angular1;
+
+ //b2->m_linearVelocity += (invMass2 * motorImpulse) * this.m_motorJacobian.linear2;
+ b2.m_linearVelocity.x += (invMass2 * motorImpulse) * this.m_motorJacobian.linear2.x;
+ b2.m_linearVelocity.y += (invMass2 * motorImpulse) * this.m_motorJacobian.linear2.y;
+ //b2->m_angularVelocity += invI2 * motorImpulse * this.m_motorJacobian.angular2;
+ b2.m_angularVelocity += invI2 * motorImpulse * this.m_motorJacobian.angular2;
+ }
+
+ // Solve linear limit constraint.
+ if (this.m_enableLimit && this.m_limitState != b2Joint.e_inactiveLimit)
+ {
+ var limitCdot = this.m_motorJacobian.Compute(b1.m_linearVelocity, b1.m_angularVelocity, b2.m_linearVelocity, b2.m_angularVelocity);
+ var limitImpulse = -this.m_motorMass * limitCdot;
+
+ if (this.m_limitState == b2Joint.e_equalLimits)
+ {
+ this.m_limitImpulse += limitImpulse;
+ }
+ else if (this.m_limitState == b2Joint.e_atLowerLimit)
+ {
+ oldLimitImpulse = this.m_limitImpulse;
+ this.m_limitImpulse = b2Math.b2Max(this.m_limitImpulse + limitImpulse, 0.0);
+ limitImpulse = this.m_limitImpulse - oldLimitImpulse;
+ }
+ else if (this.m_limitState == b2Joint.e_atUpperLimit)
+ {
+ oldLimitImpulse = this.m_limitImpulse;
+ this.m_limitImpulse = b2Math.b2Min(this.m_limitImpulse + limitImpulse, 0.0);
+ limitImpulse = this.m_limitImpulse - oldLimitImpulse;
+ }
+
+ //b1->m_linearVelocity += (invMass1 * limitImpulse) * this.m_motorJacobian.linear1;
+ b1.m_linearVelocity.x += (invMass1 * limitImpulse) * this.m_motorJacobian.linear1.x;
+ b1.m_linearVelocity.y += (invMass1 * limitImpulse) * this.m_motorJacobian.linear1.y;
+ //b1->m_angularVelocity += invI1 * limitImpulse * this.m_motorJacobian.angular1;
+ b1.m_angularVelocity += invI1 * limitImpulse * this.m_motorJacobian.angular1;
+
+ //b2->m_linearVelocity += (invMass2 * limitImpulse) * this.m_motorJacobian.linear2;
+ b2.m_linearVelocity.x += (invMass2 * limitImpulse) * this.m_motorJacobian.linear2.x;
+ b2.m_linearVelocity.y += (invMass2 * limitImpulse) * this.m_motorJacobian.linear2.y;
+ //b2->m_angularVelocity += invI2 * limitImpulse * this.m_motorJacobian.angular2;
+ b2.m_angularVelocity += invI2 * limitImpulse * this.m_motorJacobian.angular2;
+ }
+ },
+
+
+
+ SolvePositionConstraints: function(){
+
+ var limitC;
+ var oldLimitImpulse;
+
+ var b1 = this.m_body1;
+ var b2 = this.m_body2;
+
+ var invMass1 = b1.m_invMass;
+ var invMass2 = b2.m_invMass;
+ var invI1 = b1.m_invI;
+ var invI2 = b2.m_invI;
+
+ var tMat;
+
+ //b2Vec2 r1 = b2Mul(b1->m_R, this.m_localAnchor1);
+ tMat = b1.m_R;
+ var r1X = tMat.col1.x * this.m_localAnchor1.x + tMat.col2.x * this.m_localAnchor1.y;
+ var r1Y = tMat.col1.y * this.m_localAnchor1.x + tMat.col2.y * this.m_localAnchor1.y;
+ //b2Vec2 r2 = b2Mul(b2->m_R, this.m_localAnchor2);
+ tMat = b2.m_R;
+ var r2X = tMat.col1.x * this.m_localAnchor2.x + tMat.col2.x * this.m_localAnchor2.y;
+ var r2Y = tMat.col1.y * this.m_localAnchor2.x + tMat.col2.y * this.m_localAnchor2.y;
+ //b2Vec2 p1 = b1->m_position + r1;
+ var p1X = b1.m_position.x + r1X;
+ var p1Y = b1.m_position.y + r1Y;
+ //b2Vec2 p2 = b2->m_position + r2;
+ var p2X = b2.m_position.x + r2X;
+ var p2Y = b2.m_position.y + r2Y;
+ //b2Vec2 d = p2 - p1;
+ var dX = p2X - p1X;
+ var dY = p2Y - p1Y;
+ //b2Vec2 ay1 = b2Mul(b1->m_R, this.m_localYAxis1);
+ tMat = b1.m_R;
+ var ay1X = tMat.col1.x * this.m_localYAxis1.x + tMat.col2.x * this.m_localYAxis1.y;
+ var ay1Y = tMat.col1.y * this.m_localYAxis1.x + tMat.col2.y * this.m_localYAxis1.y;
+
+ // Solve linear (point-to-line) constraint.
+ //float32 linearC = b2Dot(ay1, d);
+ var linearC = ay1X*dX + ay1Y*dY;
+ // Prevent overly large corrections.
+ linearC = b2Math.b2Clamp(linearC, -b2Settings.b2_maxLinearCorrection, b2Settings.b2_maxLinearCorrection);
+ var linearImpulse = -this.m_linearMass * linearC;
+
+ //b1->m_position += (invMass1 * linearImpulse) * this.m_linearJacobian.linear1;
+ b1.m_position.x += (invMass1 * linearImpulse) * this.m_linearJacobian.linear1.x;
+ b1.m_position.y += (invMass1 * linearImpulse) * this.m_linearJacobian.linear1.y;
+ //b1->m_rotation += invI1 * linearImpulse * this.m_linearJacobian.angular1;
+ b1.m_rotation += invI1 * linearImpulse * this.m_linearJacobian.angular1;
+ //b1->m_R.Set(b1->m_rotation);
+ //b2->m_position += (invMass2 * linearImpulse) * this.m_linearJacobian.linear2;
+ b2.m_position.x += (invMass2 * linearImpulse) * this.m_linearJacobian.linear2.x;
+ b2.m_position.y += (invMass2 * linearImpulse) * this.m_linearJacobian.linear2.y;
+ b2.m_rotation += invI2 * linearImpulse * this.m_linearJacobian.angular2;
+ //b2->m_R.Set(b2->m_rotation);
+
+ var positionError = b2Math.b2Abs(linearC);
+
+ // Solve angular constraint.
+ var angularC = b2.m_rotation - b1.m_rotation - this.m_initialAngle;
+ // Prevent overly large corrections.
+ angularC = b2Math.b2Clamp(angularC, -b2Settings.b2_maxAngularCorrection, b2Settings.b2_maxAngularCorrection);
+ var angularImpulse = -this.m_angularMass * angularC;
+
+ b1.m_rotation -= b1.m_invI * angularImpulse;
+ b1.m_R.Set(b1.m_rotation);
+ b2.m_rotation += b2.m_invI * angularImpulse;
+ b2.m_R.Set(b2.m_rotation);
+
+ var angularError = b2Math.b2Abs(angularC);
+
+ // Solve linear limit constraint.
+ if (this.m_enableLimit && this.m_limitState != b2Joint.e_inactiveLimit)
+ {
+
+ //b2Vec2 r1 = b2Mul(b1->m_R, this.m_localAnchor1);
+ tMat = b1.m_R;
+ r1X = tMat.col1.x * this.m_localAnchor1.x + tMat.col2.x * this.m_localAnchor1.y;
+ r1Y = tMat.col1.y * this.m_localAnchor1.x + tMat.col2.y * this.m_localAnchor1.y;
+ //b2Vec2 r2 = b2Mul(b2->m_R, this.m_localAnchor2);
+ tMat = b2.m_R;
+ r2X = tMat.col1.x * this.m_localAnchor2.x + tMat.col2.x * this.m_localAnchor2.y;
+ r2Y = tMat.col1.y * this.m_localAnchor2.x + tMat.col2.y * this.m_localAnchor2.y;
+ //b2Vec2 p1 = b1->m_position + r1;
+ p1X = b1.m_position.x + r1X;
+ p1Y = b1.m_position.y + r1Y;
+ //b2Vec2 p2 = b2->m_position + r2;
+ p2X = b2.m_position.x + r2X;
+ p2Y = b2.m_position.y + r2Y;
+ //b2Vec2 d = p2 - p1;
+ dX = p2X - p1X;
+ dY = p2Y - p1Y;
+ //b2Vec2 ax1 = b2Mul(b1->m_R, this.m_localXAxis1);
+ tMat = b1.m_R;
+ var ax1X = tMat.col1.x * this.m_localXAxis1.x + tMat.col2.x * this.m_localXAxis1.y;
+ var ax1Y = tMat.col1.y * this.m_localXAxis1.x + tMat.col2.y * this.m_localXAxis1.y;
+
+ //float32 translation = b2Dot(ax1, d);
+ var translation = (ax1X*dX + ax1Y*dY);
+ var limitImpulse = 0.0;
+
+ if (this.m_limitState == b2Joint.e_equalLimits)
+ {
+ // Prevent large angular corrections
+ limitC = b2Math.b2Clamp(translation, -b2Settings.b2_maxLinearCorrection, b2Settings.b2_maxLinearCorrection);
+ limitImpulse = -this.m_motorMass * limitC;
+ positionError = b2Math.b2Max(positionError, b2Math.b2Abs(angularC));
+ }
+ else if (this.m_limitState == b2Joint.e_atLowerLimit)
+ {
+ limitC = translation - this.m_lowerTranslation;
+ positionError = b2Math.b2Max(positionError, -limitC);
+
+ // Prevent large linear corrections and allow some slop.
+ limitC = b2Math.b2Clamp(limitC + b2Settings.b2_linearSlop, -b2Settings.b2_maxLinearCorrection, 0.0);
+ limitImpulse = -this.m_motorMass * limitC;
+ oldLimitImpulse = this.m_limitPositionImpulse;
+ this.m_limitPositionImpulse = b2Math.b2Max(this.m_limitPositionImpulse + limitImpulse, 0.0);
+ limitImpulse = this.m_limitPositionImpulse - oldLimitImpulse;
+ }
+ else if (this.m_limitState == b2Joint.e_atUpperLimit)
+ {
+ limitC = translation - this.m_upperTranslation;
+ positionError = b2Math.b2Max(positionError, limitC);
+
+ // Prevent large linear corrections and allow some slop.
+ limitC = b2Math.b2Clamp(limitC - b2Settings.b2_linearSlop, 0.0, b2Settings.b2_maxLinearCorrection);
+ limitImpulse = -this.m_motorMass * limitC;
+ oldLimitImpulse = this.m_limitPositionImpulse;
+ this.m_limitPositionImpulse = b2Math.b2Min(this.m_limitPositionImpulse + limitImpulse, 0.0);
+ limitImpulse = this.m_limitPositionImpulse - oldLimitImpulse;
+ }
+
+ //b1->m_position += (invMass1 * limitImpulse) * this.m_motorJacobian.linear1;
+ b1.m_position.x += (invMass1 * limitImpulse) * this.m_motorJacobian.linear1.x;
+ b1.m_position.y += (invMass1 * limitImpulse) * this.m_motorJacobian.linear1.y;
+ //b1->m_rotation += invI1 * limitImpulse * this.m_motorJacobian.angular1;
+ b1.m_rotation += invI1 * limitImpulse * this.m_motorJacobian.angular1;
+ b1.m_R.Set(b1.m_rotation);
+ //b2->m_position += (invMass2 * limitImpulse) * this.m_motorJacobian.linear2;
+ b2.m_position.x += (invMass2 * limitImpulse) * this.m_motorJacobian.linear2.x;
+ b2.m_position.y += (invMass2 * limitImpulse) * this.m_motorJacobian.linear2.y;
+ //b2->m_rotation += invI2 * limitImpulse * this.m_motorJacobian.angular2;
+ b2.m_rotation += invI2 * limitImpulse * this.m_motorJacobian.angular2;
+ b2.m_R.Set(b2.m_rotation);
+ }
+
+ return positionError <= b2Settings.b2_linearSlop && angularError <= b2Settings.b2_angularSlop;
+
+ },
+
+ m_localAnchor1: new b2Vec2(),
+ m_localAnchor2: new b2Vec2(),
+ m_localXAxis1: new b2Vec2(),
+ m_localYAxis1: new b2Vec2(),
+ m_initialAngle: null,
+
+ m_linearJacobian: new b2Jacobian(),
+ m_linearMass: null,
+ m_linearImpulse: null,
+
+ m_angularMass: null,
+ m_angularImpulse: null,
+
+ m_motorJacobian: new b2Jacobian(),
+ m_motorMass: null,
+ m_motorImpulse: null,
+ m_limitImpulse: null,
+ m_limitPositionImpulse: null,
+
+ m_lowerTranslation: null,
+ m_upperTranslation: null,
+ m_maxMotorForce: null,
+ m_motorSpeed: null,
+
+ m_enableLimit: null,
+ m_enableMotor: null,
+ m_limitState: 0});
+
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PrismaticJointDef.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PrismaticJointDef.js
new file mode 100644
index 0000000..42d5743
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PrismaticJointDef.js
@@ -0,0 +1,56 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+var b2PrismaticJointDef = Class.create();
+Object.extend(b2PrismaticJointDef.prototype, b2JointDef.prototype);
+Object.extend(b2PrismaticJointDef.prototype,
+{
+ initialize: function()
+ {
+ // The constructor for b2JointDef
+ this.type = b2Joint.e_unknownJoint;
+ this.userData = null;
+ this.body1 = null;
+ this.body2 = null;
+ this.collideConnected = false;
+ //
+
+ this.type = b2Joint.e_prismaticJoint;
+ this.anchorPoint = new b2Vec2(0.0, 0.0);
+ this.axis = new b2Vec2(0.0, 0.0);
+ this.lowerTranslation = 0.0;
+ this.upperTranslation = 0.0;
+ this.motorForce = 0.0;
+ this.motorSpeed = 0.0;
+ this.enableLimit = false;
+ this.enableMotor = false;
+ },
+
+ anchorPoint: null,
+ axis: null,
+ lowerTranslation: null,
+ upperTranslation: null,
+ motorForce: null,
+ motorSpeed: null,
+ enableLimit: null,
+ enableMotor: null});
+
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PulleyJoint.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PulleyJoint.js
new file mode 100644
index 0000000..8b8506d
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PulleyJoint.js
@@ -0,0 +1,618 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+
+
+var b2PulleyJoint = Class.create();
+Object.extend(b2PulleyJoint.prototype, b2Joint.prototype);
+Object.extend(b2PulleyJoint.prototype,
+{
+ GetAnchor1: function(){
+ //return this.m_body1->m_position + b2Mul(this.m_body1->m_R, this.m_localAnchor1);
+ var tMat = this.m_body1.m_R;
+ return new b2Vec2( this.m_body1.m_position.x + (tMat.col1.x * this.m_localAnchor1.x + tMat.col2.x * this.m_localAnchor1.y),
+ this.m_body1.m_position.y + (tMat.col1.y * this.m_localAnchor1.x + tMat.col2.y * this.m_localAnchor1.y));
+ },
+ GetAnchor2: function(){
+ //return this.m_body2->m_position + b2Mul(this.m_body2->m_R, this.m_localAnchor2);
+ var tMat = this.m_body2.m_R;
+ return new b2Vec2( this.m_body2.m_position.x + (tMat.col1.x * this.m_localAnchor2.x + tMat.col2.x * this.m_localAnchor2.y),
+ this.m_body2.m_position.y + (tMat.col1.y * this.m_localAnchor2.x + tMat.col2.y * this.m_localAnchor2.y));
+ },
+
+ GetGroundPoint1: function(){
+ //return this.m_ground->m_position + this.m_groundAnchor1;
+ return new b2Vec2(this.m_ground.m_position.x + this.m_groundAnchor1.x, this.m_ground.m_position.y + this.m_groundAnchor1.y);
+ },
+ GetGroundPoint2: function(){
+ return new b2Vec2(this.m_ground.m_position.x + this.m_groundAnchor2.x, this.m_ground.m_position.y + this.m_groundAnchor2.y);
+ },
+
+ GetReactionForce: function(invTimeStep){
+ //b2Vec2 F(0.0f, 0.0f);
+ return new b2Vec2();
+ },
+ GetReactionTorque: function(invTimeStep){
+ return 0.0;
+ },
+
+ GetLength1: function(){
+ var tMat;
+ //b2Vec2 p = this.m_body1->m_position + b2Mul(this.m_body1->m_R, this.m_localAnchor1);
+ tMat = this.m_body1.m_R;
+ var pX = this.m_body1.m_position.x + (tMat.col1.x * this.m_localAnchor1.x + tMat.col2.x * this.m_localAnchor1.y);
+ var pY = this.m_body1.m_position.y + (tMat.col1.y * this.m_localAnchor1.x + tMat.col2.y * this.m_localAnchor1.y);
+ //b2Vec2 s = this.m_ground->m_position + this.m_groundAnchor1;
+ //b2Vec2 d = p - s;
+ var dX = pX - (this.m_ground.m_position.x + this.m_groundAnchor1.x);
+ var dY = pY - (this.m_ground.m_position.y + this.m_groundAnchor1.y);
+ return Math.sqrt(dX*dX + dY*dY);
+ },
+ GetLength2: function(){
+ var tMat;
+ //b2Vec2 p = this.m_body2->m_position + b2Mul(this.m_body2->m_R, this.m_localAnchor2);
+ tMat = this.m_body2.m_R;
+ var pX = this.m_body2.m_position.x + (tMat.col1.x * this.m_localAnchor2.x + tMat.col2.x * this.m_localAnchor2.y);
+ var pY = this.m_body2.m_position.y + (tMat.col1.y * this.m_localAnchor2.x + tMat.col2.y * this.m_localAnchor2.y);
+ //b2Vec2 s = this.m_ground->m_position + this.m_groundAnchor2;
+ //b2Vec2 d = p - s;
+ var dX = pX - (this.m_ground.m_position.x + this.m_groundAnchor2.x);
+ var dY = pY - (this.m_ground.m_position.y + this.m_groundAnchor2.y);
+ return Math.sqrt(dX*dX + dY*dY);
+ },
+
+ GetRatio: function(){
+ return this.m_ratio;
+ },
+
+ //--------------- Internals Below -------------------
+
+ initialize: function(def){
+ // The constructor for b2Joint
+ // initialize instance variables for references
+ this.m_node1 = new b2JointNode();
+ this.m_node2 = new b2JointNode();
+ //
+ this.m_type = def.type;
+ this.m_prev = null;
+ this.m_next = null;
+ this.m_body1 = def.body1;
+ this.m_body2 = def.body2;
+ this.m_collideConnected = def.collideConnected;
+ this.m_islandFlag = false;
+ this.m_userData = def.userData;
+ //
+
+ // initialize instance variables for references
+ this.m_groundAnchor1 = new b2Vec2();
+ this.m_groundAnchor2 = new b2Vec2();
+ this.m_localAnchor1 = new b2Vec2();
+ this.m_localAnchor2 = new b2Vec2();
+ this.m_u1 = new b2Vec2();
+ this.m_u2 = new b2Vec2();
+ //
+
+
+ // parent
+ //super(def);
+
+ var tMat;
+ var tX;
+ var tY;
+
+ this.m_ground = this.m_body1.m_world.m_groundBody;
+ //this.m_groundAnchor1 = def.groundPoint1 - this.m_ground.m_position;
+ this.m_groundAnchor1.x = def.groundPoint1.x - this.m_ground.m_position.x;
+ this.m_groundAnchor1.y = def.groundPoint1.y - this.m_ground.m_position.y;
+ //this.m_groundAnchor2 = def.groundPoint2 - this.m_ground.m_position;
+ this.m_groundAnchor2.x = def.groundPoint2.x - this.m_ground.m_position.x;
+ this.m_groundAnchor2.y = def.groundPoint2.y - this.m_ground.m_position.y;
+ //this.m_localAnchor1 = b2MulT(this.m_body1.m_R, def.anchorPoint1 - this.m_body1.m_position);
+ tMat = this.m_body1.m_R;
+ tX = def.anchorPoint1.x - this.m_body1.m_position.x;
+ tY = def.anchorPoint1.y - this.m_body1.m_position.y;
+ this.m_localAnchor1.x = tX*tMat.col1.x + tY*tMat.col1.y;
+ this.m_localAnchor1.y = tX*tMat.col2.x + tY*tMat.col2.y;
+ //this.m_localAnchor2 = b2MulT(this.m_body2.m_R, def.anchorPoint2 - this.m_body2.m_position);
+ tMat = this.m_body2.m_R;
+ tX = def.anchorPoint2.x - this.m_body2.m_position.x;
+ tY = def.anchorPoint2.y - this.m_body2.m_position.y;
+ this.m_localAnchor2.x = tX*tMat.col1.x + tY*tMat.col1.y;
+ this.m_localAnchor2.y = tX*tMat.col2.x + tY*tMat.col2.y;
+
+ this.m_ratio = def.ratio;
+
+ //var d1 = def.groundPoint1 - def.anchorPoint1;
+ tX = def.groundPoint1.x - def.anchorPoint1.x;
+ tY = def.groundPoint1.y - def.anchorPoint1.y;
+ var d1Len = Math.sqrt(tX*tX + tY*tY);
+ //var d2 = def.groundPoint2 - def.anchorPoint2;
+ tX = def.groundPoint2.x - def.anchorPoint2.x;
+ tY = def.groundPoint2.y - def.anchorPoint2.y;
+ var d2Len = Math.sqrt(tX*tX + tY*tY);
+
+ var length1 = b2Math.b2Max(0.5 * b2PulleyJoint.b2_minPulleyLength, d1Len);
+ var length2 = b2Math.b2Max(0.5 * b2PulleyJoint.b2_minPulleyLength, d2Len);
+
+ this.m_constant = length1 + this.m_ratio * length2;
+
+ this.m_maxLength1 = b2Math.b2Clamp(def.maxLength1, length1, this.m_constant - this.m_ratio * b2PulleyJoint.b2_minPulleyLength);
+ this.m_maxLength2 = b2Math.b2Clamp(def.maxLength2, length2, (this.m_constant - b2PulleyJoint.b2_minPulleyLength) / this.m_ratio);
+
+ this.m_pulleyImpulse = 0.0;
+ this.m_limitImpulse1 = 0.0;
+ this.m_limitImpulse2 = 0.0;
+
+ },
+
+ PrepareVelocitySolver: function(){
+ var b1 = this.m_body1;
+ var b2 = this.m_body2;
+
+ var tMat;
+
+ //b2Vec2 r1 = b2Mul(b1->m_R, this.m_localAnchor1);
+ tMat = b1.m_R;
+ var r1X = tMat.col1.x * this.m_localAnchor1.x + tMat.col2.x * this.m_localAnchor1.y;
+ var r1Y = tMat.col1.y * this.m_localAnchor1.x + tMat.col2.y * this.m_localAnchor1.y;
+ //b2Vec2 r2 = b2Mul(b2->m_R, this.m_localAnchor2);
+ tMat = b2.m_R;
+ var r2X = tMat.col1.x * this.m_localAnchor2.x + tMat.col2.x * this.m_localAnchor2.y;
+ var r2Y = tMat.col1.y * this.m_localAnchor2.x + tMat.col2.y * this.m_localAnchor2.y;
+
+ //b2Vec2 p1 = b1->m_position + r1;
+ var p1X = b1.m_position.x + r1X;
+ var p1Y = b1.m_position.y + r1Y;
+ //b2Vec2 p2 = b2->m_position + r2;
+ var p2X = b2.m_position.x + r2X;
+ var p2Y = b2.m_position.y + r2Y;
+
+ //b2Vec2 s1 = this.m_ground->m_position + this.m_groundAnchor1;
+ var s1X = this.m_ground.m_position.x + this.m_groundAnchor1.x;
+ var s1Y = this.m_ground.m_position.y + this.m_groundAnchor1.y;
+ //b2Vec2 s2 = this.m_ground->m_position + this.m_groundAnchor2;
+ var s2X = this.m_ground.m_position.x + this.m_groundAnchor2.x;
+ var s2Y = this.m_ground.m_position.y + this.m_groundAnchor2.y;
+
+ // Get the pulley axes.
+ //this.m_u1 = p1 - s1;
+ this.m_u1.Set(p1X - s1X, p1Y - s1Y);
+ //this.m_u2 = p2 - s2;
+ this.m_u2.Set(p2X - s2X, p2Y - s2Y);
+
+ var length1 = this.m_u1.Length();
+ var length2 = this.m_u2.Length();
+
+ if (length1 > b2Settings.b2_linearSlop)
+ {
+ //this.m_u1 *= 1.0f / length1;
+ this.m_u1.Multiply(1.0 / length1);
+ }
+ else
+ {
+ this.m_u1.SetZero();
+ }
+
+ if (length2 > b2Settings.b2_linearSlop)
+ {
+ //this.m_u2 *= 1.0f / length2;
+ this.m_u2.Multiply(1.0 / length2);
+ }
+ else
+ {
+ this.m_u2.SetZero();
+ }
+
+ if (length1 < this.m_maxLength1)
+ {
+ this.m_limitState1 = b2Joint.e_inactiveLimit;
+ this.m_limitImpulse1 = 0.0;
+ }
+ else
+ {
+ this.m_limitState1 = b2Joint.e_atUpperLimit;
+ this.m_limitPositionImpulse1 = 0.0;
+ }
+
+ if (length2 < this.m_maxLength2)
+ {
+ this.m_limitState2 = b2Joint.e_inactiveLimit;
+ this.m_limitImpulse2 = 0.0;
+ }
+ else
+ {
+ this.m_limitState2 = b2Joint.e_atUpperLimit;
+ this.m_limitPositionImpulse2 = 0.0;
+ }
+
+ // Compute effective mass.
+ //var cr1u1 = b2Cross(r1, this.m_u1);
+ var cr1u1 = r1X * this.m_u1.y - r1Y * this.m_u1.x;
+ //var cr2u2 = b2Cross(r2, this.m_u2);
+ var cr2u2 = r2X * this.m_u2.y - r2Y * this.m_u2.x;
+
+ this.m_limitMass1 = b1.m_invMass + b1.m_invI * cr1u1 * cr1u1;
+ this.m_limitMass2 = b2.m_invMass + b2.m_invI * cr2u2 * cr2u2;
+ this.m_pulleyMass = this.m_limitMass1 + this.m_ratio * this.m_ratio * this.m_limitMass2;
+ //b2Settings.b2Assert(this.m_limitMass1 > Number.MIN_VALUE);
+ //b2Settings.b2Assert(this.m_limitMass2 > Number.MIN_VALUE);
+ //b2Settings.b2Assert(this.m_pulleyMass > Number.MIN_VALUE);
+ this.m_limitMass1 = 1.0 / this.m_limitMass1;
+ this.m_limitMass2 = 1.0 / this.m_limitMass2;
+ this.m_pulleyMass = 1.0 / this.m_pulleyMass;
+
+ // Warm starting.
+ //b2Vec2 P1 = (-this.m_pulleyImpulse - this.m_limitImpulse1) * this.m_u1;
+ var P1X = (-this.m_pulleyImpulse - this.m_limitImpulse1) * this.m_u1.x;
+ var P1Y = (-this.m_pulleyImpulse - this.m_limitImpulse1) * this.m_u1.y;
+ //b2Vec2 P2 = (-this.m_ratio * this.m_pulleyImpulse - this.m_limitImpulse2) * this.m_u2;
+ var P2X = (-this.m_ratio * this.m_pulleyImpulse - this.m_limitImpulse2) * this.m_u2.x;
+ var P2Y = (-this.m_ratio * this.m_pulleyImpulse - this.m_limitImpulse2) * this.m_u2.y;
+ //b1.m_linearVelocity += b1.m_invMass * P1;
+ b1.m_linearVelocity.x += b1.m_invMass * P1X;
+ b1.m_linearVelocity.y += b1.m_invMass * P1Y;
+ //b1.m_angularVelocity += b1.m_invI * b2Cross(r1, P1);
+ b1.m_angularVelocity += b1.m_invI * (r1X * P1Y - r1Y * P1X);
+ //b2.m_linearVelocity += b2.m_invMass * P2;
+ b2.m_linearVelocity.x += b2.m_invMass * P2X;
+ b2.m_linearVelocity.y += b2.m_invMass * P2Y;
+ //b2.m_angularVelocity += b2.m_invI * b2Cross(r2, P2);
+ b2.m_angularVelocity += b2.m_invI * (r2X * P2Y - r2Y * P2X);
+ },
+
+ SolveVelocityConstraints: function(step){
+ var b1 = this.m_body1;
+ var b2 = this.m_body2;
+
+ var tMat;
+
+ //var r1 = b2Mul(b1.m_R, this.m_localAnchor1);
+ tMat = b1.m_R;
+ var r1X = tMat.col1.x * this.m_localAnchor1.x + tMat.col2.x * this.m_localAnchor1.y;
+ var r1Y = tMat.col1.y * this.m_localAnchor1.x + tMat.col2.y * this.m_localAnchor1.y;
+ //var r2 = b2Mul(b2.m_R, this.m_localAnchor2);
+ tMat = b2.m_R;
+ var r2X = tMat.col1.x * this.m_localAnchor2.x + tMat.col2.x * this.m_localAnchor2.y;
+ var r2Y = tMat.col1.y * this.m_localAnchor2.x + tMat.col2.y * this.m_localAnchor2.y;
+
+ // temp vars
+ var v1X;
+ var v1Y;
+ var v2X;
+ var v2Y;
+ var P1X;
+ var P1Y;
+ var P2X;
+ var P2Y;
+ var Cdot;
+ var impulse;
+ var oldLimitImpulse;
+
+ //{
+ //b2Vec2 v1 = b1->m_linearVelocity + b2Cross(b1->m_angularVelocity, r1);
+ v1X = b1.m_linearVelocity.x + (-b1.m_angularVelocity * r1Y);
+ v1Y = b1.m_linearVelocity.y + (b1.m_angularVelocity * r1X);
+ //b2Vec2 v2 = b2->m_linearVelocity + b2Cross(b2->m_angularVelocity, r2);
+ v2X = b2.m_linearVelocity.x + (-b2.m_angularVelocity * r2Y);
+ v2Y = b2.m_linearVelocity.y + (b2.m_angularVelocity * r2X);
+
+ //Cdot = -b2Dot(this.m_u1, v1) - this.m_ratio * b2Dot(this.m_u2, v2);
+ Cdot = -(this.m_u1.x * v1X + this.m_u1.y * v1Y) - this.m_ratio * (this.m_u2.x * v2X + this.m_u2.y * v2Y);
+ impulse = -this.m_pulleyMass * Cdot;
+ this.m_pulleyImpulse += impulse;
+
+ //b2Vec2 P1 = -impulse * this.m_u1;
+ P1X = -impulse * this.m_u1.x;
+ P1Y = -impulse * this.m_u1.y;
+ //b2Vec2 P2 = -this.m_ratio * impulse * this.m_u2;
+ P2X = -this.m_ratio * impulse * this.m_u2.x;
+ P2Y = -this.m_ratio * impulse * this.m_u2.y;
+ //b1.m_linearVelocity += b1.m_invMass * P1;
+ b1.m_linearVelocity.x += b1.m_invMass * P1X;
+ b1.m_linearVelocity.y += b1.m_invMass * P1Y;
+ //b1.m_angularVelocity += b1.m_invI * b2Cross(r1, P1);
+ b1.m_angularVelocity += b1.m_invI * (r1X * P1Y - r1Y * P1X);
+ //b2.m_linearVelocity += b2.m_invMass * P2;
+ b2.m_linearVelocity.x += b2.m_invMass * P2X;
+ b2.m_linearVelocity.y += b2.m_invMass * P2Y;
+ //b2.m_angularVelocity += b2.m_invI * b2Cross(r2, P2);
+ b2.m_angularVelocity += b2.m_invI * (r2X * P2Y - r2Y * P2X);
+ //}
+
+ if (this.m_limitState1 == b2Joint.e_atUpperLimit)
+ {
+ //b2Vec2 v1 = b1->m_linearVelocity + b2Cross(b1->m_angularVelocity, r1);
+ v1X = b1.m_linearVelocity.x + (-b1.m_angularVelocity * r1Y);
+ v1Y = b1.m_linearVelocity.y + (b1.m_angularVelocity * r1X);
+
+ //float32 Cdot = -b2Dot(this.m_u1, v1);
+ Cdot = -(this.m_u1.x * v1X + this.m_u1.y * v1Y);
+ impulse = -this.m_limitMass1 * Cdot;
+ oldLimitImpulse = this.m_limitImpulse1;
+ this.m_limitImpulse1 = b2Math.b2Max(0.0, this.m_limitImpulse1 + impulse);
+ impulse = this.m_limitImpulse1 - oldLimitImpulse;
+ //b2Vec2 P1 = -impulse * this.m_u1;
+ P1X = -impulse * this.m_u1.x;
+ P1Y = -impulse * this.m_u1.y;
+ //b1.m_linearVelocity += b1->m_invMass * P1;
+ b1.m_linearVelocity.x += b1.m_invMass * P1X;
+ b1.m_linearVelocity.y += b1.m_invMass * P1Y;
+ //b1.m_angularVelocity += b1->m_invI * b2Cross(r1, P1);
+ b1.m_angularVelocity += b1.m_invI * (r1X * P1Y - r1Y * P1X);
+ }
+
+ if (this.m_limitState2 == b2Joint.e_atUpperLimit)
+ {
+ //b2Vec2 v2 = b2->m_linearVelocity + b2Cross(b2->m_angularVelocity, r2);
+ v2X = b2.m_linearVelocity.x + (-b2.m_angularVelocity * r2Y);
+ v2Y = b2.m_linearVelocity.y + (b2.m_angularVelocity * r2X);
+
+ //float32 Cdot = -b2Dot(this.m_u2, v2);
+ Cdot = -(this.m_u2.x * v2X + this.m_u2.y * v2Y);
+ impulse = -this.m_limitMass2 * Cdot;
+ oldLimitImpulse = this.m_limitImpulse2;
+ this.m_limitImpulse2 = b2Math.b2Max(0.0, this.m_limitImpulse2 + impulse);
+ impulse = this.m_limitImpulse2 - oldLimitImpulse;
+ //b2Vec2 P2 = -impulse * this.m_u2;
+ P2X = -impulse * this.m_u2.x;
+ P2Y = -impulse * this.m_u2.y;
+ //b2->m_linearVelocity += b2->m_invMass * P2;
+ b2.m_linearVelocity.x += b2.m_invMass * P2X;
+ b2.m_linearVelocity.y += b2.m_invMass * P2Y;
+ //b2->m_angularVelocity += b2->m_invI * b2Cross(r2, P2);
+ b2.m_angularVelocity += b2.m_invI * (r2X * P2Y - r2Y * P2X);
+ }
+ },
+
+
+
+ SolvePositionConstraints: function(){
+ var b1 = this.m_body1;
+ var b2 = this.m_body2;
+
+ var tMat;
+
+ //b2Vec2 s1 = this.m_ground->m_position + this.m_groundAnchor1;
+ var s1X = this.m_ground.m_position.x + this.m_groundAnchor1.x;
+ var s1Y = this.m_ground.m_position.y + this.m_groundAnchor1.y;
+ //b2Vec2 s2 = this.m_ground->m_position + this.m_groundAnchor2;
+ var s2X = this.m_ground.m_position.x + this.m_groundAnchor2.x;
+ var s2Y = this.m_ground.m_position.y + this.m_groundAnchor2.y;
+
+ // temp vars
+ var r1X;
+ var r1Y;
+ var r2X;
+ var r2Y;
+ var p1X;
+ var p1Y;
+ var p2X;
+ var p2Y;
+ var length1;
+ var length2;
+ var C;
+ var impulse;
+ var oldLimitPositionImpulse;
+
+ var linearError = 0.0;
+
+ {
+ //var r1 = b2Mul(b1.m_R, this.m_localAnchor1);
+ tMat = b1.m_R;
+ r1X = tMat.col1.x * this.m_localAnchor1.x + tMat.col2.x * this.m_localAnchor1.y;
+ r1Y = tMat.col1.y * this.m_localAnchor1.x + tMat.col2.y * this.m_localAnchor1.y;
+ //var r2 = b2Mul(b2.m_R, this.m_localAnchor2);
+ tMat = b2.m_R;
+ r2X = tMat.col1.x * this.m_localAnchor2.x + tMat.col2.x * this.m_localAnchor2.y;
+ r2Y = tMat.col1.y * this.m_localAnchor2.x + tMat.col2.y * this.m_localAnchor2.y;
+
+ //b2Vec2 p1 = b1->m_position + r1;
+ p1X = b1.m_position.x + r1X;
+ p1Y = b1.m_position.y + r1Y;
+ //b2Vec2 p2 = b2->m_position + r2;
+ p2X = b2.m_position.x + r2X;
+ p2Y = b2.m_position.y + r2Y;
+
+ // Get the pulley axes.
+ //this.m_u1 = p1 - s1;
+ this.m_u1.Set(p1X - s1X, p1Y - s1Y);
+ //this.m_u2 = p2 - s2;
+ this.m_u2.Set(p2X - s2X, p2Y - s2Y);
+
+ length1 = this.m_u1.Length();
+ length2 = this.m_u2.Length();
+
+ if (length1 > b2Settings.b2_linearSlop)
+ {
+ //this.m_u1 *= 1.0f / length1;
+ this.m_u1.Multiply( 1.0 / length1 );
+ }
+ else
+ {
+ this.m_u1.SetZero();
+ }
+
+ if (length2 > b2Settings.b2_linearSlop)
+ {
+ //this.m_u2 *= 1.0f / length2;
+ this.m_u2.Multiply( 1.0 / length2 );
+ }
+ else
+ {
+ this.m_u2.SetZero();
+ }
+
+ C = this.m_constant - length1 - this.m_ratio * length2;
+ linearError = b2Math.b2Max(linearError, Math.abs(C));
+ C = b2Math.b2Clamp(C, -b2Settings.b2_maxLinearCorrection, b2Settings.b2_maxLinearCorrection);
+ impulse = -this.m_pulleyMass * C;
+
+ p1X = -impulse * this.m_u1.x;
+ p1Y = -impulse * this.m_u1.y;
+ p2X = -this.m_ratio * impulse * this.m_u2.x;
+ p2Y = -this.m_ratio * impulse * this.m_u2.y;
+
+ b1.m_position.x += b1.m_invMass * p1X;
+ b1.m_position.y += b1.m_invMass * p1Y;
+ b1.m_rotation += b1.m_invI * (r1X * p1Y - r1Y * p1X);
+ b2.m_position.x += b2.m_invMass * p2X;
+ b2.m_position.y += b2.m_invMass * p2Y;
+ b2.m_rotation += b2.m_invI * (r2X * p2Y - r2Y * p2X);
+
+ b1.m_R.Set(b1.m_rotation);
+ b2.m_R.Set(b2.m_rotation);
+ }
+
+ if (this.m_limitState1 == b2Joint.e_atUpperLimit)
+ {
+ //b2Vec2 r1 = b2Mul(b1->m_R, this.m_localAnchor1);
+ tMat = b1.m_R;
+ r1X = tMat.col1.x * this.m_localAnchor1.x + tMat.col2.x * this.m_localAnchor1.y;
+ r1Y = tMat.col1.y * this.m_localAnchor1.x + tMat.col2.y * this.m_localAnchor1.y;
+ //b2Vec2 p1 = b1->m_position + r1;
+ p1X = b1.m_position.x + r1X;
+ p1Y = b1.m_position.y + r1Y;
+
+ //this.m_u1 = p1 - s1;
+ this.m_u1.Set(p1X - s1X, p1Y - s1Y);
+
+ length1 = this.m_u1.Length();
+
+ if (length1 > b2Settings.b2_linearSlop)
+ {
+ //this.m_u1 *= 1.0 / length1;
+ this.m_u1.x *= 1.0 / length1;
+ this.m_u1.y *= 1.0 / length1;
+ }
+ else
+ {
+ this.m_u1.SetZero();
+ }
+
+ C = this.m_maxLength1 - length1;
+ linearError = b2Math.b2Max(linearError, -C);
+ C = b2Math.b2Clamp(C + b2Settings.b2_linearSlop, -b2Settings.b2_maxLinearCorrection, 0.0);
+ impulse = -this.m_limitMass1 * C;
+ oldLimitPositionImpulse = this.m_limitPositionImpulse1;
+ this.m_limitPositionImpulse1 = b2Math.b2Max(0.0, this.m_limitPositionImpulse1 + impulse);
+ impulse = this.m_limitPositionImpulse1 - oldLimitPositionImpulse;
+
+ //P1 = -impulse * this.m_u1;
+ p1X = -impulse * this.m_u1.x;
+ p1Y = -impulse * this.m_u1.y;
+
+ b1.m_position.x += b1.m_invMass * p1X;
+ b1.m_position.y += b1.m_invMass * p1Y;
+ //b1.m_rotation += b1.m_invI * b2Cross(r1, P1);
+ b1.m_rotation += b1.m_invI * (r1X * p1Y - r1Y * p1X);
+ b1.m_R.Set(b1.m_rotation);
+ }
+
+ if (this.m_limitState2 == b2Joint.e_atUpperLimit)
+ {
+ //b2Vec2 r2 = b2Mul(b2->m_R, this.m_localAnchor2);
+ tMat = b2.m_R;
+ r2X = tMat.col1.x * this.m_localAnchor2.x + tMat.col2.x * this.m_localAnchor2.y;
+ r2Y = tMat.col1.y * this.m_localAnchor2.x + tMat.col2.y * this.m_localAnchor2.y;
+ //b2Vec2 p2 = b2->m_position + r2;
+ p2X = b2.m_position.x + r2X;
+ p2Y = b2.m_position.y + r2Y;
+
+ //this.m_u2 = p2 - s2;
+ this.m_u2.Set(p2X - s2X, p2Y - s2Y);
+
+ length2 = this.m_u2.Length();
+
+ if (length2 > b2Settings.b2_linearSlop)
+ {
+ //this.m_u2 *= 1.0 / length2;
+ this.m_u2.x *= 1.0 / length2;
+ this.m_u2.y *= 1.0 / length2;
+ }
+ else
+ {
+ this.m_u2.SetZero();
+ }
+
+ C = this.m_maxLength2 - length2;
+ linearError = b2Math.b2Max(linearError, -C);
+ C = b2Math.b2Clamp(C + b2Settings.b2_linearSlop, -b2Settings.b2_maxLinearCorrection, 0.0);
+ impulse = -this.m_limitMass2 * C;
+ oldLimitPositionImpulse = this.m_limitPositionImpulse2;
+ this.m_limitPositionImpulse2 = b2Math.b2Max(0.0, this.m_limitPositionImpulse2 + impulse);
+ impulse = this.m_limitPositionImpulse2 - oldLimitPositionImpulse;
+
+ //P2 = -impulse * this.m_u2;
+ p2X = -impulse * this.m_u2.x;
+ p2Y = -impulse * this.m_u2.y;
+
+ //b2.m_position += b2.m_invMass * P2;
+ b2.m_position.x += b2.m_invMass * p2X;
+ b2.m_position.y += b2.m_invMass * p2Y;
+ //b2.m_rotation += b2.m_invI * b2Cross(r2, P2);
+ b2.m_rotation += b2.m_invI * (r2X * p2Y - r2Y * p2X);
+ b2.m_R.Set(b2.m_rotation);
+ }
+
+ return linearError < b2Settings.b2_linearSlop;
+ },
+
+
+
+ m_ground: null,
+ m_groundAnchor1: new b2Vec2(),
+ m_groundAnchor2: new b2Vec2(),
+ m_localAnchor1: new b2Vec2(),
+ m_localAnchor2: new b2Vec2(),
+
+ m_u1: new b2Vec2(),
+ m_u2: new b2Vec2(),
+
+ m_constant: null,
+ m_ratio: null,
+
+ m_maxLength1: null,
+ m_maxLength2: null,
+
+ // Effective masses
+ m_pulleyMass: null,
+ m_limitMass1: null,
+ m_limitMass2: null,
+
+ // Impulses for accumulation/warm starting.
+ m_pulleyImpulse: null,
+ m_limitImpulse1: null,
+ m_limitImpulse2: null,
+
+ // Position impulses for accumulation.
+ m_limitPositionImpulse1: null,
+ m_limitPositionImpulse2: null,
+
+ m_limitState1: 0,
+ m_limitState2: 0
+
+ // static
+});
+
+
+
+b2PulleyJoint.b2_minPulleyLength = b2Settings.b2_lengthUnitsPerMeter;
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PulleyJointDef.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PulleyJointDef.js
new file mode 100644
index 0000000..28d0acb
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PulleyJointDef.js
@@ -0,0 +1,70 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+
+// The pulley joint is connected to two bodies and two fixed ground points.
+// The pulley supports a ratio such that:
+// length1 + ratio * length2 = constant
+// Yes, the force transmitted is scaled by the ratio.
+// The pulley also enforces a maximum length limit on both sides. This is
+// useful to prevent one side of the pulley hitting the top.
+
+var b2PulleyJointDef = Class.create();
+Object.extend(b2PulleyJointDef.prototype, b2JointDef.prototype);
+Object.extend(b2PulleyJointDef.prototype,
+{
+ initialize: function()
+ {
+ // The constructor for b2JointDef
+ this.type = b2Joint.e_unknownJoint;
+ this.userData = null;
+ this.body1 = null;
+ this.body2 = null;
+ this.collideConnected = false;
+ //
+
+ // initialize instance variables for references
+ this.groundPoint1 = new b2Vec2();
+ this.groundPoint2 = new b2Vec2();
+ this.anchorPoint1 = new b2Vec2();
+ this.anchorPoint2 = new b2Vec2();
+ //
+
+ this.type = b2Joint.e_pulleyJoint;
+ this.groundPoint1.Set(-1.0, 1.0);
+ this.groundPoint2.Set(1.0, 1.0);
+ this.anchorPoint1.Set(-1.0, 0.0);
+ this.anchorPoint2.Set(1.0, 0.0);
+ this.maxLength1 = 0.5 * b2PulleyJoint.b2_minPulleyLength;
+ this.maxLength2 = 0.5 * b2PulleyJoint.b2_minPulleyLength;
+ this.ratio = 1.0;
+ this.collideConnected = true;
+ },
+
+ groundPoint1: new b2Vec2(),
+ groundPoint2: new b2Vec2(),
+ anchorPoint1: new b2Vec2(),
+ anchorPoint2: new b2Vec2(),
+ maxLength1: null,
+ maxLength2: null,
+ ratio: null});
+
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2RevoluteJoint.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2RevoluteJoint.js
new file mode 100644
index 0000000..db5738b
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2RevoluteJoint.js
@@ -0,0 +1,491 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+// Point-to-point constraint
+// C = p2 - p1
+// Cdot = v2 - v1
+// = v2 + cross(w2, r2) - v1 - cross(w1, r1)
+// J = [-I -r1_skew I r2_skew ]
+// Identity used:
+// w k % (rx i + ry j) = w * (-ry i + rx j)
+
+// Motor constraint
+// Cdot = w2 - w1
+// J = [0 0 -1 0 0 1]
+// K = invI1 + invI2
+
+var b2RevoluteJoint = Class.create();
+Object.extend(b2RevoluteJoint.prototype, b2Joint.prototype);
+Object.extend(b2RevoluteJoint.prototype,
+{
+ GetAnchor1: function(){
+ var tMat = this.m_body1.m_R;
+ return new b2Vec2( this.m_body1.m_position.x + (tMat.col1.x * this.m_localAnchor1.x + tMat.col2.x * this.m_localAnchor1.y),
+ this.m_body1.m_position.y + (tMat.col1.y * this.m_localAnchor1.x + tMat.col2.y * this.m_localAnchor1.y));
+ },
+ GetAnchor2: function(){
+ var tMat = this.m_body2.m_R;
+ return new b2Vec2( this.m_body2.m_position.x + (tMat.col1.x * this.m_localAnchor2.x + tMat.col2.x * this.m_localAnchor2.y),
+ this.m_body2.m_position.y + (tMat.col1.y * this.m_localAnchor2.x + tMat.col2.y * this.m_localAnchor2.y));
+ },
+ GetJointAngle: function(){
+ return this.m_body2.m_rotation - this.m_body1.m_rotation;
+ },
+ GetJointSpeed: function(){
+ return this.m_body2.m_angularVelocity - this.m_body1.m_angularVelocity;
+ },
+ GetMotorTorque: function(invTimeStep){
+ return invTimeStep * this.m_motorImpulse;
+ },
+
+ SetMotorSpeed: function(speed)
+ {
+ this.m_motorSpeed = speed;
+ },
+
+ SetMotorTorque: function(torque)
+ {
+ this.m_maxMotorTorque = torque;
+ },
+
+ GetReactionForce: function(invTimeStep)
+ {
+ var tVec = this.m_ptpImpulse.Copy();
+ tVec.Multiply(invTimeStep);
+ //return invTimeStep * this.m_ptpImpulse;
+ return tVec;
+ },
+
+ GetReactionTorque: function(invTimeStep)
+ {
+ return invTimeStep * this.m_limitImpulse;
+ },
+
+ //--------------- Internals Below -------------------
+
+ initialize: function(def){
+ // The constructor for b2Joint
+ // initialize instance variables for references
+ this.m_node1 = new b2JointNode();
+ this.m_node2 = new b2JointNode();
+ //
+ this.m_type = def.type;
+ this.m_prev = null;
+ this.m_next = null;
+ this.m_body1 = def.body1;
+ this.m_body2 = def.body2;
+ this.m_collideConnected = def.collideConnected;
+ this.m_islandFlag = false;
+ this.m_userData = def.userData;
+ //
+
+ // initialize instance variables for references
+ this.K = new b2Mat22();
+ this.K1 = new b2Mat22();
+ this.K2 = new b2Mat22();
+ this.K3 = new b2Mat22();
+ this.m_localAnchor1 = new b2Vec2();
+ this.m_localAnchor2 = new b2Vec2();
+ this.m_ptpImpulse = new b2Vec2();
+ this.m_ptpMass = new b2Mat22();
+ //
+
+ //super(def);
+
+ var tMat;
+ var tX;
+ var tY;
+
+ //this.m_localAnchor1 = b2Math.b2MulTMV(this.m_body1.m_R, b2Math.SubtractVV( def.anchorPoint, this.m_body1.m_position));
+ tMat = this.m_body1.m_R;
+ tX = def.anchorPoint.x - this.m_body1.m_position.x;
+ tY = def.anchorPoint.y - this.m_body1.m_position.y;
+ this.m_localAnchor1.x = tX * tMat.col1.x + tY * tMat.col1.y;
+ this.m_localAnchor1.y = tX * tMat.col2.x + tY * tMat.col2.y;
+ //this.m_localAnchor2 = b2Math.b2MulTMV(this.m_body2.m_R, b2Math.SubtractVV( def.anchorPoint, this.m_body2.m_position));
+ tMat = this.m_body2.m_R;
+ tX = def.anchorPoint.x - this.m_body2.m_position.x;
+ tY = def.anchorPoint.y - this.m_body2.m_position.y;
+ this.m_localAnchor2.x = tX * tMat.col1.x + tY * tMat.col1.y;
+ this.m_localAnchor2.y = tX * tMat.col2.x + tY * tMat.col2.y;
+
+ this.m_intialAngle = this.m_body2.m_rotation - this.m_body1.m_rotation;
+
+ this.m_ptpImpulse.Set(0.0, 0.0);
+ this.m_motorImpulse = 0.0;
+ this.m_limitImpulse = 0.0;
+ this.m_limitPositionImpulse = 0.0;
+
+ this.m_lowerAngle = def.lowerAngle;
+ this.m_upperAngle = def.upperAngle;
+ this.m_maxMotorTorque = def.motorTorque;
+ this.m_motorSpeed = def.motorSpeed;
+ this.m_enableLimit = def.enableLimit;
+ this.m_enableMotor = def.enableMotor;
+ },
+
+ // internal vars
+ K: new b2Mat22(),
+ K1: new b2Mat22(),
+ K2: new b2Mat22(),
+ K3: new b2Mat22(),
+ PrepareVelocitySolver: function(){
+ var b1 = this.m_body1;
+ var b2 = this.m_body2;
+
+ var tMat;
+
+ // Compute the effective mass matrix.
+ //b2Vec2 r1 = b2Mul(b1->m_R, this.m_localAnchor1);
+ tMat = b1.m_R;
+ var r1X = tMat.col1.x * this.m_localAnchor1.x + tMat.col2.x * this.m_localAnchor1.y;
+ var r1Y = tMat.col1.y * this.m_localAnchor1.x + tMat.col2.y * this.m_localAnchor1.y;
+ //b2Vec2 r2 = b2Mul(b2->m_R, this.m_localAnchor2);
+ tMat = b2.m_R;
+ var r2X = tMat.col1.x * this.m_localAnchor2.x + tMat.col2.x * this.m_localAnchor2.y;
+ var r2Y = tMat.col1.y * this.m_localAnchor2.x + tMat.col2.y * this.m_localAnchor2.y;
+
+ // this.K = [(1/m1 + 1/m2) * eye(2) - skew(r1) * invI1 * skew(r1) - skew(r2) * invI2 * skew(r2)]
+ // = [1/m1+1/m2 0 ] + invI1 * [r1.y*r1.y -r1.x*r1.y] + invI2 * [r1.y*r1.y -r1.x*r1.y]
+ // [ 0 1/m1+1/m2] [-r1.x*r1.y r1.x*r1.x] [-r1.x*r1.y r1.x*r1.x]
+ var invMass1 = b1.m_invMass;
+ var invMass2 = b2.m_invMass;
+ var invI1 = b1.m_invI;
+ var invI2 = b2.m_invI;
+
+ //var this.K1 = new b2Mat22();
+ this.K1.col1.x = invMass1 + invMass2; this.K1.col2.x = 0.0;
+ this.K1.col1.y = 0.0; this.K1.col2.y = invMass1 + invMass2;
+
+ //var this.K2 = new b2Mat22();
+ this.K2.col1.x = invI1 * r1Y * r1Y; this.K2.col2.x = -invI1 * r1X * r1Y;
+ this.K2.col1.y = -invI1 * r1X * r1Y; this.K2.col2.y = invI1 * r1X * r1X;
+
+ //var this.K3 = new b2Mat22();
+ this.K3.col1.x = invI2 * r2Y * r2Y; this.K3.col2.x = -invI2 * r2X * r2Y;
+ this.K3.col1.y = -invI2 * r2X * r2Y; this.K3.col2.y = invI2 * r2X * r2X;
+
+ //var this.K = b2Math.AddMM(b2Math.AddMM(this.K1, this.K2), this.K3);
+ this.K.SetM(this.K1);
+ this.K.AddM(this.K2);
+ this.K.AddM(this.K3);
+
+ //this.m_ptpMass = this.K.Invert();
+ this.K.Invert(this.m_ptpMass);
+
+ this.m_motorMass = 1.0 / (invI1 + invI2);
+
+ if (this.m_enableMotor == false)
+ {
+ this.m_motorImpulse = 0.0;
+ }
+
+ if (this.m_enableLimit)
+ {
+ var jointAngle = b2.m_rotation - b1.m_rotation - this.m_intialAngle;
+ if (b2Math.b2Abs(this.m_upperAngle - this.m_lowerAngle) < 2.0 * b2Settings.b2_angularSlop)
+ {
+ this.m_limitState = b2Joint.e_equalLimits;
+ }
+ else if (jointAngle <= this.m_lowerAngle)
+ {
+ if (this.m_limitState != b2Joint.e_atLowerLimit)
+ {
+ this.m_limitImpulse = 0.0;
+ }
+ this.m_limitState = b2Joint.e_atLowerLimit;
+ }
+ else if (jointAngle >= this.m_upperAngle)
+ {
+ if (this.m_limitState != b2Joint.e_atUpperLimit)
+ {
+ this.m_limitImpulse = 0.0;
+ }
+ this.m_limitState = b2Joint.e_atUpperLimit;
+ }
+ else
+ {
+ this.m_limitState = b2Joint.e_inactiveLimit;
+ this.m_limitImpulse = 0.0;
+ }
+ }
+ else
+ {
+ this.m_limitImpulse = 0.0;
+ }
+
+ // Warm starting.
+ if (b2World.s_enableWarmStarting)
+ {
+ //b1.m_linearVelocity.Subtract( b2Math.MulFV( invMass1, this.m_ptpImpulse) );
+ b1.m_linearVelocity.x -= invMass1 * this.m_ptpImpulse.x;
+ b1.m_linearVelocity.y -= invMass1 * this.m_ptpImpulse.y;
+ //b1.m_angularVelocity -= invI1 * (b2Math.b2CrossVV(r1, this.m_ptpImpulse) + this.m_motorImpulse + this.m_limitImpulse);
+ b1.m_angularVelocity -= invI1 * ((r1X * this.m_ptpImpulse.y - r1Y * this.m_ptpImpulse.x) + this.m_motorImpulse + this.m_limitImpulse);
+
+ //b2.m_linearVelocity.Add( b2Math.MulFV( invMass2 , this.m_ptpImpulse ));
+ b2.m_linearVelocity.x += invMass2 * this.m_ptpImpulse.x;
+ b2.m_linearVelocity.y += invMass2 * this.m_ptpImpulse.y;
+ //b2.m_angularVelocity += invI2 * (b2Math.b2CrossVV(r2, this.m_ptpImpulse) + this.m_motorImpulse + this.m_limitImpulse);
+ b2.m_angularVelocity += invI2 * ((r2X * this.m_ptpImpulse.y - r2Y * this.m_ptpImpulse.x) + this.m_motorImpulse + this.m_limitImpulse);
+ }
+ else{
+ this.m_ptpImpulse.SetZero();
+ this.m_motorImpulse = 0.0;
+ this.m_limitImpulse = 0.0;
+ }
+
+ this.m_limitPositionImpulse = 0.0;
+ },
+
+
+ SolveVelocityConstraints: function(step){
+ var b1 = this.m_body1;
+ var b2 = this.m_body2;
+
+ var tMat;
+
+ //var r1 = b2Math.b2MulMV(b1.m_R, this.m_localAnchor1);
+ tMat = b1.m_R;
+ var r1X = tMat.col1.x * this.m_localAnchor1.x + tMat.col2.x * this.m_localAnchor1.y;
+ var r1Y = tMat.col1.y * this.m_localAnchor1.x + tMat.col2.y * this.m_localAnchor1.y;
+ //var r2 = b2Math.b2MulMV(b2.m_R, this.m_localAnchor2);
+ tMat = b2.m_R;
+ var r2X = tMat.col1.x * this.m_localAnchor2.x + tMat.col2.x * this.m_localAnchor2.y;
+ var r2Y = tMat.col1.y * this.m_localAnchor2.x + tMat.col2.y * this.m_localAnchor2.y;
+
+ var oldLimitImpulse;
+
+ // Solve point-to-point constraint
+ //b2Vec2 ptpCdot = b2.m_linearVelocity + b2Cross(b2.m_angularVelocity, r2) - b1.m_linearVelocity - b2Cross(b1.m_angularVelocity, r1);
+ var ptpCdotX = b2.m_linearVelocity.x + (-b2.m_angularVelocity * r2Y) - b1.m_linearVelocity.x - (-b1.m_angularVelocity * r1Y);
+ var ptpCdotY = b2.m_linearVelocity.y + (b2.m_angularVelocity * r2X) - b1.m_linearVelocity.y - (b1.m_angularVelocity * r1X);
+
+ //b2Vec2 ptpImpulse = -b2Mul(this.m_ptpMass, ptpCdot);
+ var ptpImpulseX = -(this.m_ptpMass.col1.x * ptpCdotX + this.m_ptpMass.col2.x * ptpCdotY);
+ var ptpImpulseY = -(this.m_ptpMass.col1.y * ptpCdotX + this.m_ptpMass.col2.y * ptpCdotY);
+ this.m_ptpImpulse.x += ptpImpulseX;
+ this.m_ptpImpulse.y += ptpImpulseY;
+
+ //b1->m_linearVelocity -= b1->m_invMass * ptpImpulse;
+ b1.m_linearVelocity.x -= b1.m_invMass * ptpImpulseX;
+ b1.m_linearVelocity.y -= b1.m_invMass * ptpImpulseY;
+ //b1->m_angularVelocity -= b1->m_invI * b2Cross(r1, ptpImpulse);
+ b1.m_angularVelocity -= b1.m_invI * (r1X * ptpImpulseY - r1Y * ptpImpulseX);
+
+ //b2->m_linearVelocity += b2->m_invMass * ptpImpulse;
+ b2.m_linearVelocity.x += b2.m_invMass * ptpImpulseX;
+ b2.m_linearVelocity.y += b2.m_invMass * ptpImpulseY;
+ //b2->m_angularVelocity += b2->m_invI * b2Cross(r2, ptpImpulse);
+ b2.m_angularVelocity += b2.m_invI * (r2X * ptpImpulseY - r2Y * ptpImpulseX);
+
+ if (this.m_enableMotor && this.m_limitState != b2Joint.e_equalLimits)
+ {
+ var motorCdot = b2.m_angularVelocity - b1.m_angularVelocity - this.m_motorSpeed;
+ var motorImpulse = -this.m_motorMass * motorCdot;
+ var oldMotorImpulse = this.m_motorImpulse;
+ this.m_motorImpulse = b2Math.b2Clamp(this.m_motorImpulse + motorImpulse, -step.dt * this.m_maxMotorTorque, step.dt * this.m_maxMotorTorque);
+ motorImpulse = this.m_motorImpulse - oldMotorImpulse;
+ b1.m_angularVelocity -= b1.m_invI * motorImpulse;
+ b2.m_angularVelocity += b2.m_invI * motorImpulse;
+ }
+
+ if (this.m_enableLimit && this.m_limitState != b2Joint.e_inactiveLimit)
+ {
+ var limitCdot = b2.m_angularVelocity - b1.m_angularVelocity;
+ var limitImpulse = -this.m_motorMass * limitCdot;
+
+ if (this.m_limitState == b2Joint.e_equalLimits)
+ {
+ this.m_limitImpulse += limitImpulse;
+ }
+ else if (this.m_limitState == b2Joint.e_atLowerLimit)
+ {
+ oldLimitImpulse = this.m_limitImpulse;
+ this.m_limitImpulse = b2Math.b2Max(this.m_limitImpulse + limitImpulse, 0.0);
+ limitImpulse = this.m_limitImpulse - oldLimitImpulse;
+ }
+ else if (this.m_limitState == b2Joint.e_atUpperLimit)
+ {
+ oldLimitImpulse = this.m_limitImpulse;
+ this.m_limitImpulse = b2Math.b2Min(this.m_limitImpulse + limitImpulse, 0.0);
+ limitImpulse = this.m_limitImpulse - oldLimitImpulse;
+ }
+
+ b1.m_angularVelocity -= b1.m_invI * limitImpulse;
+ b2.m_angularVelocity += b2.m_invI * limitImpulse;
+ }
+ },
+
+
+ SolvePositionConstraints: function(){
+
+ var oldLimitImpulse;
+ var limitC;
+
+ var b1 = this.m_body1;
+ var b2 = this.m_body2;
+
+ var positionError = 0.0;
+
+ var tMat;
+
+ // Solve point-to-point position error.
+ //var r1 = b2Math.b2MulMV(b1.m_R, this.m_localAnchor1);
+ tMat = b1.m_R;
+ var r1X = tMat.col1.x * this.m_localAnchor1.x + tMat.col2.x * this.m_localAnchor1.y;
+ var r1Y = tMat.col1.y * this.m_localAnchor1.x + tMat.col2.y * this.m_localAnchor1.y;
+ //var r2 = b2Math.b2MulMV(b2.m_R, this.m_localAnchor2);
+ tMat = b2.m_R;
+ var r2X = tMat.col1.x * this.m_localAnchor2.x + tMat.col2.x * this.m_localAnchor2.y;
+ var r2Y = tMat.col1.y * this.m_localAnchor2.x + tMat.col2.y * this.m_localAnchor2.y;
+
+ //b2Vec2 p1 = b1->m_position + r1;
+ var p1X = b1.m_position.x + r1X;
+ var p1Y = b1.m_position.y + r1Y;
+ //b2Vec2 p2 = b2->m_position + r2;
+ var p2X = b2.m_position.x + r2X;
+ var p2Y = b2.m_position.y + r2Y;
+
+ //b2Vec2 ptpC = p2 - p1;
+ var ptpCX = p2X - p1X;
+ var ptpCY = p2Y - p1Y;
+
+ //float32 positionError = ptpC.Length();
+ positionError = Math.sqrt(ptpCX*ptpCX + ptpCY*ptpCY);
+
+ // Prevent overly large corrections.
+ //b2Vec2 dpMax(b2_maxLinearCorrection, b2_maxLinearCorrection);
+ //ptpC = b2Clamp(ptpC, -dpMax, dpMax);
+
+ //float32 invMass1 = b1->m_invMass, invMass2 = b2->m_invMass;
+ var invMass1 = b1.m_invMass;
+ var invMass2 = b2.m_invMass;
+ //float32 invI1 = b1->m_invI, invI2 = b2->m_invI;
+ var invI1 = b1.m_invI;
+ var invI2 = b2.m_invI;
+
+ //b2Mat22 this.K1;
+ this.K1.col1.x = invMass1 + invMass2; this.K1.col2.x = 0.0;
+ this.K1.col1.y = 0.0; this.K1.col2.y = invMass1 + invMass2;
+
+ //b2Mat22 this.K2;
+ this.K2.col1.x = invI1 * r1Y * r1Y; this.K2.col2.x = -invI1 * r1X * r1Y;
+ this.K2.col1.y = -invI1 * r1X * r1Y; this.K2.col2.y = invI1 * r1X * r1X;
+
+ //b2Mat22 this.K3;
+ this.K3.col1.x = invI2 * r2Y * r2Y; this.K3.col2.x = -invI2 * r2X * r2Y;
+ this.K3.col1.y = -invI2 * r2X * r2Y; this.K3.col2.y = invI2 * r2X * r2X;
+
+ //b2Mat22 this.K = this.K1 + this.K2 + this.K3;
+ this.K.SetM(this.K1);
+ this.K.AddM(this.K2);
+ this.K.AddM(this.K3);
+ //b2Vec2 impulse = this.K.Solve(-ptpC);
+ this.K.Solve(b2RevoluteJoint.tImpulse, -ptpCX, -ptpCY);
+ var impulseX = b2RevoluteJoint.tImpulse.x;
+ var impulseY = b2RevoluteJoint.tImpulse.y;
+
+ //b1.m_position -= b1.m_invMass * impulse;
+ b1.m_position.x -= b1.m_invMass * impulseX;
+ b1.m_position.y -= b1.m_invMass * impulseY;
+ //b1.m_rotation -= b1.m_invI * b2Cross(r1, impulse);
+ b1.m_rotation -= b1.m_invI * (r1X * impulseY - r1Y * impulseX);
+ b1.m_R.Set(b1.m_rotation);
+
+ //b2.m_position += b2.m_invMass * impulse;
+ b2.m_position.x += b2.m_invMass * impulseX;
+ b2.m_position.y += b2.m_invMass * impulseY;
+ //b2.m_rotation += b2.m_invI * b2Cross(r2, impulse);
+ b2.m_rotation += b2.m_invI * (r2X * impulseY - r2Y * impulseX);
+ b2.m_R.Set(b2.m_rotation);
+
+
+ // Handle limits.
+ var angularError = 0.0;
+
+ if (this.m_enableLimit && this.m_limitState != b2Joint.e_inactiveLimit)
+ {
+ var angle = b2.m_rotation - b1.m_rotation - this.m_intialAngle;
+ var limitImpulse = 0.0;
+
+ if (this.m_limitState == b2Joint.e_equalLimits)
+ {
+ // Prevent large angular corrections
+ limitC = b2Math.b2Clamp(angle, -b2Settings.b2_maxAngularCorrection, b2Settings.b2_maxAngularCorrection);
+ limitImpulse = -this.m_motorMass * limitC;
+ angularError = b2Math.b2Abs(limitC);
+ }
+ else if (this.m_limitState == b2Joint.e_atLowerLimit)
+ {
+ limitC = angle - this.m_lowerAngle;
+ angularError = b2Math.b2Max(0.0, -limitC);
+
+ // Prevent large angular corrections and allow some slop.
+ limitC = b2Math.b2Clamp(limitC + b2Settings.b2_angularSlop, -b2Settings.b2_maxAngularCorrection, 0.0);
+ limitImpulse = -this.m_motorMass * limitC;
+ oldLimitImpulse = this.m_limitPositionImpulse;
+ this.m_limitPositionImpulse = b2Math.b2Max(this.m_limitPositionImpulse + limitImpulse, 0.0);
+ limitImpulse = this.m_limitPositionImpulse - oldLimitImpulse;
+ }
+ else if (this.m_limitState == b2Joint.e_atUpperLimit)
+ {
+ limitC = angle - this.m_upperAngle;
+ angularError = b2Math.b2Max(0.0, limitC);
+
+ // Prevent large angular corrections and allow some slop.
+ limitC = b2Math.b2Clamp(limitC - b2Settings.b2_angularSlop, 0.0, b2Settings.b2_maxAngularCorrection);
+ limitImpulse = -this.m_motorMass * limitC;
+ oldLimitImpulse = this.m_limitPositionImpulse;
+ this.m_limitPositionImpulse = b2Math.b2Min(this.m_limitPositionImpulse + limitImpulse, 0.0);
+ limitImpulse = this.m_limitPositionImpulse - oldLimitImpulse;
+ }
+
+ b1.m_rotation -= b1.m_invI * limitImpulse;
+ b1.m_R.Set(b1.m_rotation);
+ b2.m_rotation += b2.m_invI * limitImpulse;
+ b2.m_R.Set(b2.m_rotation);
+ }
+
+ return positionError <= b2Settings.b2_linearSlop && angularError <= b2Settings.b2_angularSlop;
+ },
+
+ m_localAnchor1: new b2Vec2(),
+ m_localAnchor2: new b2Vec2(),
+ m_ptpImpulse: new b2Vec2(),
+ m_motorImpulse: null,
+ m_limitImpulse: null,
+ m_limitPositionImpulse: null,
+
+ m_ptpMass: new b2Mat22(),
+ m_motorMass: null,
+ m_intialAngle: null,
+ m_lowerAngle: null,
+ m_upperAngle: null,
+ m_maxMotorTorque: null,
+ m_motorSpeed: null,
+
+ m_enableLimit: null,
+ m_enableMotor: null,
+ m_limitState: 0});
+
+b2RevoluteJoint.tImpulse = new b2Vec2();
diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2RevoluteJointDef.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2RevoluteJointDef.js
new file mode 100644
index 0000000..4466785
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2RevoluteJointDef.js
@@ -0,0 +1,55 @@
+/*
+* Copyright (c) 2006-2007 Erin Catto http:
+*
+* This software is provided 'as-is', without any express or implied
+* warranty. In no event will the authors be held liable for any damages
+* arising from the use of this software.
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+* 1. The origin of this software must not be misrepresented; you must not
+* claim that you wrote the original software. If you use this software
+* in a product, an acknowledgment in the product documentation would be
+* appreciated but is not required.
+* 2. Altered source versions must be plainly marked, and must not be
+* misrepresented the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+
+
+
+
+var b2RevoluteJointDef = Class.create();
+Object.extend(b2RevoluteJointDef.prototype, b2JointDef.prototype);
+Object.extend(b2RevoluteJointDef.prototype,
+{
+ initialize: function()
+ {
+ // The constructor for b2JointDef
+ this.type = b2Joint.e_unknownJoint;
+ this.userData = null;
+ this.body1 = null;
+ this.body2 = null;
+ this.collideConnected = false;
+ //
+
+ this.type = b2Joint.e_revoluteJoint;
+ this.anchorPoint = new b2Vec2(0.0, 0.0);
+ this.lowerAngle = 0.0;
+ this.upperAngle = 0.0;
+ this.motorTorque = 0.0;
+ this.motorSpeed = 0.0;
+ this.enableLimit = false;
+ this.enableMotor = false;
+ },
+
+ anchorPoint: null,
+ lowerAngle: null,
+ upperAngle: null,
+ motorTorque: null,
+ motorSpeed: null,
+ enableLimit: null,
+ enableMotor: null});
+
diff --git a/o3d/samples/box2d-3d/third_party/prototype-1.6.0.2.js b/o3d/samples/box2d-3d/third_party/prototype-1.6.0.2.js
new file mode 100644
index 0000000..6385503
--- /dev/null
+++ b/o3d/samples/box2d-3d/third_party/prototype-1.6.0.2.js
@@ -0,0 +1,4221 @@
+/* Prototype JavaScript framework, version 1.6.0.2
+ * (c) 2005-2008 Sam Stephenson
+ *
+ * Prototype is freely distributable under the terms of an MIT-style license.
+ * For details, see the Prototype web site: http://www.prototypejs.org/
+ *
+ *--------------------------------------------------------------------------*/
+
+var Prototype = {
+ Version: '1.6.0.2',
+
+ Browser: {
+ IE: !!(window.attachEvent && !window.opera),
+ Opera: !!window.opera,
+ WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
+ Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
+ MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
+ },
+
+ BrowserFeatures: {
+ XPath: !!document.evaluate,
+ ElementExtensions: !!window.HTMLElement,
+ SpecificElementExtensions:
+ document.createElement('div').__proto__ &&
+ document.createElement('div').__proto__ !==
+ document.createElement('form').__proto__
+ },
+
+ ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
+ JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
+
+ emptyFunction: function() { },
+ K: function(x) { return x }
+};
+
+if (Prototype.Browser.MobileSafari)
+ Prototype.BrowserFeatures.SpecificElementExtensions = false;
+
+
+/* Based on Alex Arnell's inheritance implementation. */
+var Class = {
+ create: function() {
+ var parent = null, properties = $A(arguments);
+ if (Object.isFunction(properties[0]))
+ parent = properties.shift();
+
+ function klass() {
+ this.initialize.apply(this, arguments);
+ }
+
+ Object.extend(klass, Class.Methods);
+ klass.superclass = parent;
+ klass.subclasses = [];
+
+ if (parent) {
+ var subclass = function() { };
+ subclass.prototype = parent.prototype;
+ klass.prototype = new subclass;
+ parent.subclasses.push(klass);
+ }
+
+ for (var i = 0; i < properties.length; i++)
+ klass.addMethods(properties[i]);
+
+ if (!klass.prototype.initialize)
+ klass.prototype.initialize = Prototype.emptyFunction;
+
+ klass.prototype.constructor = klass;
+
+ return klass;
+ }
+};
+
+Class.Methods = {
+ addMethods: function(source) {
+ var ancestor = this.superclass && this.superclass.prototype;
+ var properties = Object.keys(source);
+
+ if (!Object.keys({ toString: true }).length)
+ properties.push("toString", "valueOf");
+
+ for (var i = 0, length = properties.length; i < length; i++) {
+ var property = properties[i], value = source[property];
+ if (ancestor && Object.isFunction(value) &&
+ value.argumentNames().first() == "$super") {
+ var method = value, value = Object.extend((function(m) {
+ return function() { return ancestor[m].apply(this, arguments) };
+ })(property).wrap(method), {
+ valueOf: function() { return method },
+ toString: function() { return method.toString() }
+ });
+ }
+ this.prototype[property] = value;
+ }
+
+ return this;
+ }
+};
+
+var Abstract = { };
+
+Object.extend = function(destination, source) {
+ for (var property in source)
+ destination[property] = source[property];
+ return destination;
+};
+
+Object.extend(Object, {
+ inspect: function(object) {
+ try {
+ if (Object.isUndefined(object)) return 'undefined';
+ if (object === null) return 'null';
+ return object.inspect ? object.inspect() : String(object);
+ } catch (e) {
+ if (e instanceof RangeError) return '...';
+ throw e;
+ }
+ },
+
+ toJSON: function(object) {
+ var type = typeof object;
+ switch (type) {
+ case 'undefined':
+ case 'function':
+ case 'unknown': return;
+ case 'boolean': return object.toString();
+ }
+
+ if (object === null) return 'null';
+ if (object.toJSON) return object.toJSON();
+ if (Object.isElement(object)) return;
+
+ var results = [];
+ for (var property in object) {
+ var value = Object.toJSON(object[property]);
+ if (!Object.isUndefined(value))
+ results.push(property.toJSON() + ': ' + value);
+ }
+
+ return '{' + results.join(', ') + '}';
+ },
+
+ toQueryString: function(object) {
+ return $H(object).toQueryString();
+ },
+
+ toHTML: function(object) {
+ return object && object.toHTML ? object.toHTML() : String.interpret(object);
+ },
+
+ keys: function(object) {
+ var keys = [];
+ for (var property in object)
+ keys.push(property);
+ return keys;
+ },
+
+ values: function(object) {
+ var values = [];
+ for (var property in object)
+ values.push(object[property]);
+ return values;
+ },
+
+ clone: function(object) {
+ return Object.extend({ }, object);
+ },
+
+ isElement: function(object) {
+ return object && object.nodeType == 1;
+ },
+
+ isArray: function(object) {
+ return object != null && typeof object == "object" &&
+ 'splice' in object && 'join' in object;
+ },
+
+ isHash: function(object) {
+ return object instanceof Hash;
+ },
+
+ isFunction: function(object) {
+ return typeof object == "function";
+ },
+
+ isString: function(object) {
+ return typeof object == "string";
+ },
+
+ isNumber: function(object) {
+ return typeof object == "number";
+ },
+
+ isUndefined: function(object) {
+ return typeof object == "undefined";
+ }
+});
+
+Object.extend(Function.prototype, {
+ argumentNames: function() {
+ var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
+ return names.length == 1 && !names[0] ? [] : names;
+ },
+
+ bind: function() {
+ if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
+ var __method = this, args = $A(arguments), object = args.shift();
+ return function() {
+ return __method.apply(object, args.concat($A(arguments)));
+ }
+ },
+
+ bindAsEventListener: function() {
+ var __method = this, args = $A(arguments), object = args.shift();
+ return function(event) {
+ return __method.apply(object, [event || window.event].concat(args));
+ }
+ },
+
+ curry: function() {
+ if (!arguments.length) return this;
+ var __method = this, args = $A(arguments);
+ return function() {
+ return __method.apply(this, args.concat($A(arguments)));
+ }
+ },
+
+ delay: function() {
+ var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
+ return window.setTimeout(function() {
+ return __method.apply(__method, args);
+ }, timeout);
+ },
+
+ wrap: function(wrapper) {
+ var __method = this;
+ return function() {
+ return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
+ }
+ },
+
+ methodize: function() {
+ if (this._methodized) return this._methodized;
+ var __method = this;
+ return this._methodized = function() {
+ return __method.apply(null, [this].concat($A(arguments)));
+ };
+ }
+});
+
+Function.prototype.defer = Function.prototype.delay.curry(0.01);
+
+Date.prototype.toJSON = function() {
+ return '"' + this.getUTCFullYear() + '-' +
+ (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
+ this.getUTCDate().toPaddedString(2) + 'T' +
+ this.getUTCHours().toPaddedString(2) + ':' +
+ this.getUTCMinutes().toPaddedString(2) + ':' +
+ this.getUTCSeconds().toPaddedString(2) + 'Z"';
+};
+
+var Try = {
+ these: function() {
+ var returnValue;
+
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ var lambda = arguments[i];
+ try {
+ returnValue = lambda();
+ break;
+ } catch (e) { }
+ }
+
+ return returnValue;
+ }
+};
+
+RegExp.prototype.match = RegExp.prototype.test;
+
+RegExp.escape = function(str) {
+ return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
+};
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create({
+ initialize: function(callback, frequency) {
+ this.callback = callback;
+ this.frequency = frequency;
+ this.currentlyExecuting = false;
+
+ this.registerCallback();
+ },
+
+ registerCallback: function() {
+ this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+ },
+
+ execute: function() {
+ this.callback(this);
+ },
+
+ stop: function() {
+ if (!this.timer) return;
+ clearInterval(this.timer);
+ this.timer = null;
+ },
+
+ onTimerEvent: function() {
+ if (!this.currentlyExecuting) {
+ try {
+ this.currentlyExecuting = true;
+ this.execute();
+ } finally {
+ this.currentlyExecuting = false;
+ }
+ }
+ }
+});
+Object.extend(String, {
+ interpret: function(value) {
+ return value == null ? '' : String(value);
+ },
+ specialChar: {
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '\\': '\\\\'
+ }
+});
+
+Object.extend(String.prototype, {
+ gsub: function(pattern, replacement) {
+ var result = '', source = this, match;
+ replacement = arguments.callee.prepareReplacement(replacement);
+
+ while (source.length > 0) {
+ if (match = source.match(pattern)) {
+ result += source.slice(0, match.index);
+ result += String.interpret(replacement(match));
+ source = source.slice(match.index + match[0].length);
+ } else {
+ result += source, source = '';
+ }
+ }
+ return result;
+ },
+
+ sub: function(pattern, replacement, count) {
+ replacement = this.gsub.prepareReplacement(replacement);
+ count = Object.isUndefined(count) ? 1 : count;
+
+ return this.gsub(pattern, function(match) {
+ if (--count < 0) return match[0];
+ return replacement(match);
+ });
+ },
+
+ scan: function(pattern, iterator) {
+ this.gsub(pattern, iterator);
+ return String(this);
+ },
+
+ truncate: function(length, truncation) {
+ length = length || 30;
+ truncation = Object.isUndefined(truncation) ? '...' : truncation;
+ return this.length > length ?
+ this.slice(0, length - truncation.length) + truncation : String(this);
+ },
+
+ strip: function() {
+ return this.replace(/^\s+/, '').replace(/\s+$/, '');
+ },
+
+ stripTags: function() {
+ return this.replace(/<\/?[^>]+>/gi, '');
+ },
+
+ stripScripts: function() {
+ return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+ },
+
+ extractScripts: function() {
+ var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+ var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+ return (this.match(matchAll) || []).map(function(scriptTag) {
+ return (scriptTag.match(matchOne) || ['', ''])[1];
+ });
+ },
+
+ evalScripts: function() {
+ return this.extractScripts().map(function(script) { return eval(script) });
+ },
+
+ escapeHTML: function() {
+ var self = arguments.callee;
+ self.text.data = this;
+ return self.div.innerHTML;
+ },
+
+ unescapeHTML: function() {
+ var div = new Element('div');
+ div.innerHTML = this.stripTags();
+ return div.childNodes[0] ? (div.childNodes.length > 1 ?
+ $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
+ div.childNodes[0].nodeValue) : '';
+ },
+
+ toQueryParams: function(separator) {
+ var match = this.strip().match(/([^?#]*)(#.*)?$/);
+ if (!match) return { };
+
+ return match[1].split(separator || '&').inject({ }, function(hash, pair) {
+ if ((pair = pair.split('='))[0]) {
+ var key = decodeURIComponent(pair.shift());
+ var value = pair.length > 1 ? pair.join('=') : pair[0];
+ if (value != undefined) value = decodeURIComponent(value);
+
+ if (key in hash) {
+ if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
+ hash[key].push(value);
+ }
+ else hash[key] = value;
+ }
+ return hash;
+ });
+ },
+
+ toArray: function() {
+ return this.split('');
+ },
+
+ succ: function() {
+ return this.slice(0, this.length - 1) +
+ String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
+ },
+
+ times: function(count) {
+ return count < 1 ? '' : new Array(count + 1).join(this);
+ },
+
+ camelize: function() {
+ var parts = this.split('-'), len = parts.length;
+ if (len == 1) return parts[0];
+
+ var camelized = this.charAt(0) == '-'
+ ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
+ : parts[0];
+
+ for (var i = 1; i < len; i++)
+ camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
+
+ return camelized;
+ },
+
+ capitalize: function() {
+ return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
+ },
+
+ underscore: function() {
+ return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
+ },
+
+ dasherize: function() {
+ return this.gsub(/_/,'-');
+ },
+
+ inspect: function(useDoubleQuotes) {
+ var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
+ var character = String.specialChar[match[0]];
+ return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
+ });
+ if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
+ return "'" + escapedString.replace(/'/g, '\\\'') + "'";
+ },
+
+ toJSON: function() {
+ return this.inspect(true);
+ },
+
+ unfilterJSON: function(filter) {
+ return this.sub(filter || Prototype.JSONFilter, '#{1}');
+ },
+
+ isJSON: function() {
+ var str = this;
+ if (str.blank()) return false;
+ str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
+ return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
+ },
+
+ evalJSON: function(sanitize) {
+ var json = this.unfilterJSON();
+ try {
+ if (!sanitize || json.isJSON()) return eval('(' + json + ')');
+ } catch (e) { }
+ throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
+ },
+
+ include: function(pattern) {
+ return this.indexOf(pattern) > -1;
+ },
+
+ startsWith: function(pattern) {
+ return this.indexOf(pattern) === 0;
+ },
+
+ endsWith: function(pattern) {
+ var d = this.length - pattern.length;
+ return d >= 0 && this.lastIndexOf(pattern) === d;
+ },
+
+ empty: function() {
+ return this == '';
+ },
+
+ blank: function() {
+ return /^\s*$/.test(this);
+ },
+
+ interpolate: function(object, pattern) {
+ return new Template(this, pattern).evaluate(object);
+ }
+});
+
+if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
+ escapeHTML: function() {
+ return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
+ },
+ unescapeHTML: function() {
+ return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
+ }
+});
+
+String.prototype.gsub.prepareReplacement = function(replacement) {
+ if (Object.isFunction(replacement)) return replacement;
+ var template = new Template(replacement);
+ return function(match) { return template.evaluate(match) };
+};
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+Object.extend(String.prototype.escapeHTML, {
+ div: document.createElement('div'),
+ text: document.createTextNode('')
+});
+
+with (String.prototype.escapeHTML) div.appendChild(text);
+
+var Template = Class.create({
+ initialize: function(template, pattern) {
+ this.template = template.toString();
+ this.pattern = pattern || Template.Pattern;
+ },
+
+ evaluate: function(object) {
+ if (Object.isFunction(object.toTemplateReplacements))
+ object = object.toTemplateReplacements();
+
+ return this.template.gsub(this.pattern, function(match) {
+ if (object == null) return '';
+
+ var before = match[1] || '';
+ if (before == '\\') return match[2];
+
+ var ctx = object, expr = match[3];
+ var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
+ match = pattern.exec(expr);
+ if (match == null) return before;
+
+ while (match != null) {
+ var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
+ ctx = ctx[comp];
+ if (null == ctx || '' == match[3]) break;
+ expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
+ match = pattern.exec(expr);
+ }
+
+ return before + String.interpret(ctx);
+ });
+ }
+});
+Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+
+var $break = { };
+
+var Enumerable = {
+ each: function(iterator, context) {
+ var index = 0;
+ iterator = iterator.bind(context);
+ try {
+ this._each(function(value) {
+ iterator(value, index++);
+ });
+ } catch (e) {
+ if (e != $break) throw e;
+ }
+ return this;
+ },
+
+ eachSlice: function(number, iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var index = -number, slices = [], array = this.toArray();
+ while ((index += number) < array.length)
+ slices.push(array.slice(index, index+number));
+ return slices.collect(iterator, context);
+ },
+
+ all: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var result = true;
+ this.each(function(value, index) {
+ result = result && !!iterator(value, index);
+ if (!result) throw $break;
+ });
+ return result;
+ },
+
+ any: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var result = false;
+ this.each(function(value, index) {
+ if (result = !!iterator(value, index))
+ throw $break;
+ });
+ return result;
+ },
+
+ collect: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var results = [];
+ this.each(function(value, index) {
+ results.push(iterator(value, index));
+ });
+ return results;
+ },
+
+ detect: function(iterator, context) {
+ iterator = iterator.bind(context);
+ var result;
+ this.each(function(value, index) {
+ if (iterator(value, index)) {
+ result = value;
+ throw $break;
+ }
+ });
+ return result;
+ },
+
+ findAll: function(iterator, context) {
+ iterator = iterator.bind(context);
+ var results = [];
+ this.each(function(value, index) {
+ if (iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ grep: function(filter, iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var results = [];
+
+ if (Object.isString(filter))
+ filter = new RegExp(filter);
+
+ this.each(function(value, index) {
+ if (filter.match(value))
+ results.push(iterator(value, index));
+ });
+ return results;
+ },
+
+ include: function(object) {
+ if (Object.isFunction(this.indexOf))
+ if (this.indexOf(object) != -1) return true;
+
+ var found = false;
+ this.each(function(value) {
+ if (value == object) {
+ found = true;
+ throw $break;
+ }
+ });
+ return found;
+ },
+
+ inGroupsOf: function(number, fillWith) {
+ fillWith = Object.isUndefined(fillWith) ? null : fillWith;
+ return this.eachSlice(number, function(slice) {
+ while(slice.length < number) slice.push(fillWith);
+ return slice;
+ });
+ },
+
+ inject: function(memo, iterator, context) {
+ iterator = iterator.bind(context);
+ this.each(function(value, index) {
+ memo = iterator(memo, value, index);
+ });
+ return memo;
+ },
+
+ invoke: function(method) {
+ var args = $A(arguments).slice(1);
+ return this.map(function(value) {
+ return value[method].apply(value, args);
+ });
+ },
+
+ max: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var result;
+ this.each(function(value, index) {
+ value = iterator(value, index);
+ if (result == null || value >= result)
+ result = value;
+ });
+ return result;
+ },
+
+ min: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var result;
+ this.each(function(value, index) {
+ value = iterator(value, index);
+ if (result == null || value < result)
+ result = value;
+ });
+ return result;
+ },
+
+ partition: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var trues = [], falses = [];
+ this.each(function(value, index) {
+ (iterator(value, index) ?
+ trues : falses).push(value);
+ });
+ return [trues, falses];
+ },
+
+ pluck: function(property) {
+ var results = [];
+ this.each(function(value) {
+ results.push(value[property]);
+ });
+ return results;
+ },
+
+ reject: function(iterator, context) {
+ iterator = iterator.bind(context);
+ var results = [];
+ this.each(function(value, index) {
+ if (!iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ sortBy: function(iterator, context) {
+ iterator = iterator.bind(context);
+ return this.map(function(value, index) {
+ return {value: value, criteria: iterator(value, index)};
+ }).sort(function(left, right) {
+ var a = left.criteria, b = right.criteria;
+ return a < b ? -1 : a > b ? 1 : 0;
+ }).pluck('value');
+ },
+
+ toArray: function() {
+ return this.map();
+ },
+
+ zip: function() {
+ var iterator = Prototype.K, args = $A(arguments);
+ if (Object.isFunction(args.last()))
+ iterator = args.pop();
+
+ var collections = [this].concat(args).map($A);
+ return this.map(function(value, index) {
+ return iterator(collections.pluck(index));
+ });
+ },
+
+ size: function() {
+ return this.toArray().length;
+ },
+
+ inspect: function() {
+ return '#<Enumerable:' + this.toArray().inspect() + '>';
+ }
+};
+
+Object.extend(Enumerable, {
+ map: Enumerable.collect,
+ find: Enumerable.detect,
+ select: Enumerable.findAll,
+ filter: Enumerable.findAll,
+ member: Enumerable.include,
+ entries: Enumerable.toArray,
+ every: Enumerable.all,
+ some: Enumerable.any
+});
+function $A(iterable) {
+ if (!iterable) return [];
+ if (iterable.toArray) return iterable.toArray();
+ var length = iterable.length || 0, results = new Array(length);
+ while (length--) results[length] = iterable[length];
+ return results;
+}
+
+if (Prototype.Browser.WebKit) {
+ $A = function(iterable) {
+ if (!iterable) return [];
+ if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
+ iterable.toArray) return iterable.toArray();
+ var length = iterable.length || 0, results = new Array(length);
+ while (length--) results[length] = iterable[length];
+ return results;
+ };
+}
+
+Array.from = $A;
+
+Object.extend(Array.prototype, Enumerable);
+
+if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+ _each: function(iterator) {
+ for (var i = 0, length = this.length; i < length; i++)
+ iterator(this[i]);
+ },
+
+ clear: function() {
+ this.length = 0;
+ return this;
+ },
+
+ first: function() {
+ return this[0];
+ },
+
+ last: function() {
+ return this[this.length - 1];
+ },
+
+ compact: function() {
+ return this.select(function(value) {
+ return value != null;
+ });
+ },
+
+ flatten: function() {
+ return this.inject([], function(array, value) {
+ return array.concat(Object.isArray(value) ?
+ value.flatten() : [value]);
+ });
+ },
+
+ without: function() {
+ var values = $A(arguments);
+ return this.select(function(value) {
+ return !values.include(value);
+ });
+ },
+
+ reverse: function(inline) {
+ return (inline !== false ? this : this.toArray())._reverse();
+ },
+
+ reduce: function() {
+ return this.length > 1 ? this : this[0];
+ },
+
+ uniq: function(sorted) {
+ return this.inject([], function(array, value, index) {
+ if (0 == index || (sorted ? array.last() != value : !array.include(value)))
+ array.push(value);
+ return array;
+ });
+ },
+
+ intersect: function(array) {
+ return this.uniq().findAll(function(item) {
+ return array.detect(function(value) { return item === value });
+ });
+ },
+
+ clone: function() {
+ return [].concat(this);
+ },
+
+ size: function() {
+ return this.length;
+ },
+
+ inspect: function() {
+ return '[' + this.map(Object.inspect).join(', ') + ']';
+ },
+
+ toJSON: function() {
+ var results = [];
+ this.each(function(object) {
+ var value = Object.toJSON(object);
+ if (!Object.isUndefined(value)) results.push(value);
+ });
+ return '[' + results.join(', ') + ']';
+ }
+});
+
+// use native browser JS 1.6 implementation if available
+if (Object.isFunction(Array.prototype.forEach))
+ Array.prototype._each = Array.prototype.forEach;
+
+if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
+ i || (i = 0);
+ var length = this.length;
+ if (i < 0) i = length + i;
+ for (; i < length; i++)
+ if (this[i] === item) return i;
+ return -1;
+};
+
+if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
+ i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
+ var n = this.slice(0, i).reverse().indexOf(item);
+ return (n < 0) ? n : i - n - 1;
+};
+
+Array.prototype.toArray = Array.prototype.clone;
+
+function $w(string) {
+ if (!Object.isString(string)) return [];
+ string = string.strip();
+ return string ? string.split(/\s+/) : [];
+}
+
+if (Prototype.Browser.Opera){
+ Array.prototype.concat = function() {
+ var array = [];
+ for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ if (Object.isArray(arguments[i])) {
+ for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
+ array.push(arguments[i][j]);
+ } else {
+ array.push(arguments[i]);
+ }
+ }
+ return array;
+ };
+}
+Object.extend(Number.prototype, {
+ toColorPart: function() {
+ return this.toPaddedString(2, 16);
+ },
+
+ succ: function() {
+ return this + 1;
+ },
+
+ times: function(iterator) {
+ $R(0, this, true).each(iterator);
+ return this;
+ },
+
+ toPaddedString: function(length, radix) {
+ var string = this.toString(radix || 10);
+ return '0'.times(length - string.length) + string;
+ },
+
+ toJSON: function() {
+ return isFinite(this) ? this.toString() : 'null';
+ }
+});
+
+$w('abs round ceil floor').each(function(method){
+ Number.prototype[method] = Math[method].methodize();
+});
+function $H(object) {
+ return new Hash(object);
+};
+
+var Hash = Class.create(Enumerable, (function() {
+
+ function toQueryPair(key, value) {
+ if (Object.isUndefined(value)) return key;
+ return key + '=' + encodeURIComponent(String.interpret(value));
+ }
+
+ return {
+ initialize: function(object) {
+ this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
+ },
+
+ _each: function(iterator) {
+ for (var key in this._object) {
+ var value = this._object[key], pair = [key, value];
+ pair.key = key;
+ pair.value = value;
+ iterator(pair);
+ }
+ },
+
+ set: function(key, value) {
+ return this._object[key] = value;
+ },
+
+ get: function(key) {
+ return this._object[key];
+ },
+
+ unset: function(key) {
+ var value = this._object[key];
+ delete this._object[key];
+ return value;
+ },
+
+ toObject: function() {
+ return Object.clone(this._object);
+ },
+
+ keys: function() {
+ return this.pluck('key');
+ },
+
+ values: function() {
+ return this.pluck('value');
+ },
+
+ index: function(value) {
+ var match = this.detect(function(pair) {
+ return pair.value === value;
+ });
+ return match && match.key;
+ },
+
+ merge: function(object) {
+ return this.clone().update(object);
+ },
+
+ update: function(object) {
+ return new Hash(object).inject(this, function(result, pair) {
+ result.set(pair.key, pair.value);
+ return result;
+ });
+ },
+
+ toQueryString: function() {
+ return this.map(function(pair) {
+ var key = encodeURIComponent(pair.key), values = pair.value;
+
+ if (values && typeof values == 'object') {
+ if (Object.isArray(values))
+ return values.map(toQueryPair.curry(key)).join('&');
+ }
+ return toQueryPair(key, values);
+ }).join('&');
+ },
+
+ inspect: function() {
+ return '#<Hash:{' + this.map(function(pair) {
+ return pair.map(Object.inspect).join(': ');
+ }).join(', ') + '}>';
+ },
+
+ toJSON: function() {
+ return Object.toJSON(this.toObject());
+ },
+
+ clone: function() {
+ return new Hash(this);
+ }
+ }
+})());
+
+Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
+Hash.from = $H;
+var ObjectRange = Class.create(Enumerable, {
+ initialize: function(start, end, exclusive) {
+ this.start = start;
+ this.end = end;
+ this.exclusive = exclusive;
+ },
+
+ _each: function(iterator) {
+ var value = this.start;
+ while (this.include(value)) {
+ iterator(value);
+ value = value.succ();
+ }
+ },
+
+ include: function(value) {
+ if (value < this.start)
+ return false;
+ if (this.exclusive)
+ return value < this.end;
+ return value <= this.end;
+ }
+});
+
+var $R = function(start, end, exclusive) {
+ return new ObjectRange(start, end, exclusive);
+};
+
+var Ajax = {
+ getTransport: function() {
+ return Try.these(
+ function() {return new XMLHttpRequest()},
+ function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+ function() {return new ActiveXObject('Microsoft.XMLHTTP')}
+ ) || false;
+ },
+
+ activeRequestCount: 0
+};
+
+Ajax.Responders = {
+ responders: [],
+
+ _each: function(iterator) {
+ this.responders._each(iterator);
+ },
+
+ register: function(responder) {
+ if (!this.include(responder))
+ this.responders.push(responder);
+ },
+
+ unregister: function(responder) {
+ this.responders = this.responders.without(responder);
+ },
+
+ dispatch: function(callback, request, transport, json) {
+ this.each(function(responder) {
+ if (Object.isFunction(responder[callback])) {
+ try {
+ responder[callback].apply(responder, [request, transport, json]);
+ } catch (e) { }
+ }
+ });
+ }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+ onCreate: function() { Ajax.activeRequestCount++ },
+ onComplete: function() { Ajax.activeRequestCount-- }
+});
+
+Ajax.Base = Class.create({
+ initialize: function(options) {
+ this.options = {
+ method: 'post',
+ asynchronous: true,
+ contentType: 'application/x-www-form-urlencoded',
+ encoding: 'UTF-8',
+ parameters: '',
+ evalJSON: true,
+ evalJS: true
+ };
+ Object.extend(this.options, options || { });
+
+ this.options.method = this.options.method.toLowerCase();
+
+ if (Object.isString(this.options.parameters))
+ this.options.parameters = this.options.parameters.toQueryParams();
+ else if (Object.isHash(this.options.parameters))
+ this.options.parameters = this.options.parameters.toObject();
+ }
+});
+
+Ajax.Request = Class.create(Ajax.Base, {
+ _complete: false,
+
+ initialize: function($super, url, options) {
+ $super(options);
+ this.transport = Ajax.getTransport();
+ this.request(url);
+ },
+
+ request: function(url) {
+ this.url = url;
+ this.method = this.options.method;
+ var params = Object.clone(this.options.parameters);
+
+ if (!['get', 'post'].include(this.method)) {
+ // simulate other verbs over post
+ params['_method'] = this.method;
+ this.method = 'post';
+ }
+
+ this.parameters = params;
+
+ if (params = Object.toQueryString(params)) {
+ // when GET, append parameters to URL
+ if (this.method == 'get')
+ this.url += (this.url.include('?') ? '&' : '?') + params;
+ else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
+ params += '&_=';
+ }
+
+ try {
+ var response = new Ajax.Response(this);
+ if (this.options.onCreate) this.options.onCreate(response);
+ Ajax.Responders.dispatch('onCreate', this, response);
+
+ this.transport.open(this.method.toUpperCase(), this.url,
+ this.options.asynchronous);
+
+ if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
+
+ this.transport.onreadystatechange = this.onStateChange.bind(this);
+ this.setRequestHeaders();
+
+ this.body = this.method == 'post' ? (this.options.postBody || params) : null;
+ this.transport.send(this.body);
+
+ /* Force Firefox to handle ready state 4 for synchronous requests */
+ if (!this.options.asynchronous && this.transport.overrideMimeType)
+ this.onStateChange();
+
+ }
+ catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ onStateChange: function() {
+ var readyState = this.transport.readyState;
+ if (readyState > 1 && !((readyState == 4) && this._complete))
+ this.respondToReadyState(this.transport.readyState);
+ },
+
+ setRequestHeaders: function() {
+ var headers = {
+ 'X-Requested-With': 'XMLHttpRequest',
+ 'X-Prototype-Version': Prototype.Version,
+ 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+ };
+
+ if (this.method == 'post') {
+ headers['Content-type'] = this.options.contentType +
+ (this.options.encoding ? '; charset=' + this.options.encoding : '');
+
+ /* Force "Connection: close" for older Mozilla browsers to work
+ * around a bug where XMLHttpRequest sends an incorrect
+ * Content-length header. See Mozilla Bugzilla #246651.
+ */
+ if (this.transport.overrideMimeType &&
+ (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
+ headers['Connection'] = 'close';
+ }
+
+ // user-defined headers
+ if (typeof this.options.requestHeaders == 'object') {
+ var extras = this.options.requestHeaders;
+
+ if (Object.isFunction(extras.push))
+ for (var i = 0, length = extras.length; i < length; i += 2)
+ headers[extras[i]] = extras[i+1];
+ else
+ $H(extras).each(function(pair) { headers[pair.key] = pair.value });
+ }
+
+ for (var name in headers)
+ this.transport.setRequestHeader(name, headers[name]);
+ },
+
+ success: function() {
+ var status = this.getStatus();
+ return !status || (status >= 200 && status < 300);
+ },
+
+ getStatus: function() {
+ try {
+ return this.transport.status || 0;
+ } catch (e) { return 0 }
+ },
+
+ respondToReadyState: function(readyState) {
+ var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
+
+ if (state == 'Complete') {
+ try {
+ this._complete = true;
+ (this.options['on' + response.status]
+ || this.options['on' + (this.success() ? 'Success' : 'Failure')]
+ || Prototype.emptyFunction)(response, response.headerJSON);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ var contentType = response.getHeader('Content-type');
+ if (this.options.evalJS == 'force'
+ || (this.options.evalJS && this.isSameOrigin() && contentType
+ && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
+ this.evalResponse();
+ }
+
+ try {
+ (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
+ Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ if (state == 'Complete') {
+ // avoid memory leak in MSIE: clean up
+ this.transport.onreadystatechange = Prototype.emptyFunction;
+ }
+ },
+
+ isSameOrigin: function() {
+ var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
+ return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
+ protocol: location.protocol,
+ domain: document.domain,
+ port: location.port ? ':' + location.port : ''
+ }));
+ },
+
+ getHeader: function(name) {
+ try {
+ return this.transport.getResponseHeader(name) || null;
+ } catch (e) { return null }
+ },
+
+ evalResponse: function() {
+ try {
+ return eval((this.transport.responseText || '').unfilterJSON());
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ dispatchException: function(exception) {
+ (this.options.onException || Prototype.emptyFunction)(this, exception);
+ Ajax.Responders.dispatch('onException', this, exception);
+ }
+});
+
+Ajax.Request.Events =
+ ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Response = Class.create({
+ initialize: function(request){
+ this.request = request;
+ var transport = this.transport = request.transport,
+ readyState = this.readyState = transport.readyState;
+
+ if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
+ this.status = this.getStatus();
+ this.statusText = this.getStatusText();
+ this.responseText = String.interpret(transport.responseText);
+ this.headerJSON = this._getHeaderJSON();
+ }
+
+ if(readyState == 4) {
+ var xml = transport.responseXML;
+ this.responseXML = Object.isUndefined(xml) ? null : xml;
+ this.responseJSON = this._getResponseJSON();
+ }
+ },
+
+ status: 0,
+ statusText: '',
+
+ getStatus: Ajax.Request.prototype.getStatus,
+
+ getStatusText: function() {
+ try {
+ return this.transport.statusText || '';
+ } catch (e) { return '' }
+ },
+
+ getHeader: Ajax.Request.prototype.getHeader,
+
+ getAllHeaders: function() {
+ try {
+ return this.getAllResponseHeaders();
+ } catch (e) { return null }
+ },
+
+ getResponseHeader: function(name) {
+ return this.transport.getResponseHeader(name);
+ },
+
+ getAllResponseHeaders: function() {
+ return this.transport.getAllResponseHeaders();
+ },
+
+ _getHeaderJSON: function() {
+ var json = this.getHeader('X-JSON');
+ if (!json) return null;
+ json = decodeURIComponent(escape(json));
+ try {
+ return json.evalJSON(this.request.options.sanitizeJSON ||
+ !this.request.isSameOrigin());
+ } catch (e) {
+ this.request.dispatchException(e);
+ }
+ },
+
+ _getResponseJSON: function() {
+ var options = this.request.options;
+ if (!options.evalJSON || (options.evalJSON != 'force' &&
+ !(this.getHeader('Content-type') || '').include('application/json')) ||
+ this.responseText.blank())
+ return null;
+ try {
+ return this.responseText.evalJSON(options.sanitizeJSON ||
+ !this.request.isSameOrigin());
+ } catch (e) {
+ this.request.dispatchException(e);
+ }
+ }
+});
+
+Ajax.Updater = Class.create(Ajax.Request, {
+ initialize: function($super, container, url, options) {
+ this.container = {
+ success: (container.success || container),
+ failure: (container.failure || (container.success ? null : container))
+ };
+
+ options = Object.clone(options);
+ var onComplete = options.onComplete;
+ options.onComplete = (function(response, json) {
+ this.updateContent(response.responseText);
+ if (Object.isFunction(onComplete)) onComplete(response, json);
+ }).bind(this);
+
+ $super(url, options);
+ },
+
+ updateContent: function(responseText) {
+ var receiver = this.container[this.success() ? 'success' : 'failure'],
+ options = this.options;
+
+ if (!options.evalScripts) responseText = responseText.stripScripts();
+
+ if (receiver = $(receiver)) {
+ if (options.insertion) {
+ if (Object.isString(options.insertion)) {
+ var insertion = { }; insertion[options.insertion] = responseText;
+ receiver.insert(insertion);
+ }
+ else options.insertion(receiver, responseText);
+ }
+ else receiver.update(responseText);
+ }
+ }
+});
+
+Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
+ initialize: function($super, container, url, options) {
+ $super(options);
+ this.onComplete = this.options.onComplete;
+
+ this.frequency = (this.options.frequency || 2);
+ this.decay = (this.options.decay || 1);
+
+ this.updater = { };
+ this.container = container;
+ this.url = url;
+
+ this.start();
+ },
+
+ start: function() {
+ this.options.onComplete = this.updateComplete.bind(this);
+ this.onTimerEvent();
+ },
+
+ stop: function() {
+ this.updater.options.onComplete = undefined;
+ clearTimeout(this.timer);
+ (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+ },
+
+ updateComplete: function(response) {
+ if (this.options.decay) {
+ this.decay = (response.responseText == this.lastText ?
+ this.decay * this.options.decay : 1);
+
+ this.lastText = response.responseText;
+ }
+ this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
+ },
+
+ onTimerEvent: function() {
+ this.updater = new Ajax.Updater(this.container, this.url, this.options);
+ }
+});
+function $(element) {
+ if (arguments.length > 1) {
+ for (var i = 0, elements = [], length = arguments.length; i < length; i++)
+ elements.push($(arguments[i]));
+ return elements;
+ }
+ if (Object.isString(element))
+ element = document.getElementById(element);
+ return Element.extend(element);
+}
+
+if (Prototype.BrowserFeatures.XPath) {
+ document._getElementsByXPath = function(expression, parentElement) {
+ var results = [];
+ var query = document.evaluate(expression, $(parentElement) || document,
+ null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+ for (var i = 0, length = query.snapshotLength; i < length; i++)
+ results.push(Element.extend(query.snapshotItem(i)));
+ return results;
+ };
+}
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Node) var Node = { };
+
+if (!Node.ELEMENT_NODE) {
+ // DOM level 2 ECMAScript Language Binding
+ Object.extend(Node, {
+ ELEMENT_NODE: 1,
+ ATTRIBUTE_NODE: 2,
+ TEXT_NODE: 3,
+ CDATA_SECTION_NODE: 4,
+ ENTITY_REFERENCE_NODE: 5,
+ ENTITY_NODE: 6,
+ PROCESSING_INSTRUCTION_NODE: 7,
+ COMMENT_NODE: 8,
+ DOCUMENT_NODE: 9,
+ DOCUMENT_TYPE_NODE: 10,
+ DOCUMENT_FRAGMENT_NODE: 11,
+ NOTATION_NODE: 12
+ });
+}
+
+(function() {
+ var element = this.Element;
+ this.Element = function(tagName, attributes) {
+ attributes = attributes || { };
+ tagName = tagName.toLowerCase();
+ var cache = Element.cache;
+ if (Prototype.Browser.IE && attributes.name) {
+ tagName = '<' + tagName + ' name="' + attributes.name + '">';
+ delete attributes.name;
+ return Element.writeAttribute(document.createElement(tagName), attributes);
+ }
+ if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
+ return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
+ };
+ Object.extend(this.Element, element || { });
+}).call(window);
+
+Element.cache = { };
+
+Element.Methods = {
+ visible: function(element) {
+ return $(element).style.display != 'none';
+ },
+
+ toggle: function(element) {
+ element = $(element);
+ Element[Element.visible(element) ? 'hide' : 'show'](element);
+ return element;
+ },
+
+ hide: function(element) {
+ $(element).style.display = 'none';
+ return element;
+ },
+
+ show: function(element) {
+ $(element).style.display = '';
+ return element;
+ },
+
+ remove: function(element) {
+ element = $(element);
+ element.parentNode.removeChild(element);
+ return element;
+ },
+
+ update: function(element, content) {
+ element = $(element);
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) return element.update().insert(content);
+ content = Object.toHTML(content);
+ element.innerHTML = content.stripScripts();
+ content.evalScripts.bind(content).defer();
+ return element;
+ },
+
+ replace: function(element, content) {
+ element = $(element);
+ if (content && content.toElement) content = content.toElement();
+ else if (!Object.isElement(content)) {
+ content = Object.toHTML(content);
+ var range = element.ownerDocument.createRange();
+ range.selectNode(element);
+ content.evalScripts.bind(content).defer();
+ content = range.createContextualFragment(content.stripScripts());
+ }
+ element.parentNode.replaceChild(content, element);
+ return element;
+ },
+
+ insert: function(element, insertions) {
+ element = $(element);
+
+ if (Object.isString(insertions) || Object.isNumber(insertions) ||
+ Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
+ insertions = {bottom:insertions};
+
+ var content, insert, tagName, childNodes;
+
+ for (var position in insertions) {
+ content = insertions[position];
+ position = position.toLowerCase();
+ insert = Element._insertionTranslations[position];
+
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) {
+ insert(element, content);
+ continue;
+ }
+
+ content = Object.toHTML(content);
+
+ tagName = ((position == 'before' || position == 'after')
+ ? element.parentNode : element).tagName.toUpperCase();
+
+ childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+
+ if (position == 'top' || position == 'after') childNodes.reverse();
+ childNodes.each(insert.curry(element));
+
+ content.evalScripts.bind(content).defer();
+ }
+
+ return element;
+ },
+
+ wrap: function(element, wrapper, attributes) {
+ element = $(element);
+ if (Object.isElement(wrapper))
+ $(wrapper).writeAttribute(attributes || { });
+ else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
+ else wrapper = new Element('div', wrapper);
+ if (element.parentNode)
+ element.parentNode.replaceChild(wrapper, element);
+ wrapper.appendChild(element);
+ return wrapper;
+ },
+
+ inspect: function(element) {
+ element = $(element);
+ var result = '<' + element.tagName.toLowerCase();
+ $H({'id': 'id', 'className': 'class'}).each(function(pair) {
+ var property = pair.first(), attribute = pair.last();
+ var value = (element[property] || '').toString();
+ if (value) result += ' ' + attribute + '=' + value.inspect(true);
+ });
+ return result + '>';
+ },
+
+ recursivelyCollect: function(element, property) {
+ element = $(element);
+ var elements = [];
+ while (element = element[property])
+ if (element.nodeType == 1)
+ elements.push(Element.extend(element));
+ return elements;
+ },
+
+ ancestors: function(element) {
+ return $(element).recursivelyCollect('parentNode');
+ },
+
+ descendants: function(element) {
+ return $(element).select("*");
+ },
+
+ firstDescendant: function(element) {
+ element = $(element).firstChild;
+ while (element && element.nodeType != 1) element = element.nextSibling;
+ return $(element);
+ },
+
+ immediateDescendants: function(element) {
+ if (!(element = $(element).firstChild)) return [];
+ while (element && element.nodeType != 1) element = element.nextSibling;
+ if (element) return [element].concat($(element).nextSiblings());
+ return [];
+ },
+
+ previousSiblings: function(element) {
+ return $(element).recursivelyCollect('previousSibling');
+ },
+
+ nextSiblings: function(element) {
+ return $(element).recursivelyCollect('nextSibling');
+ },
+
+ siblings: function(element) {
+ element = $(element);
+ return element.previousSiblings().reverse().concat(element.nextSiblings());
+ },
+
+ match: function(element, selector) {
+ if (Object.isString(selector))
+ selector = new Selector(selector);
+ return selector.match($(element));
+ },
+
+ up: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return $(element.parentNode);
+ var ancestors = element.ancestors();
+ return Object.isNumber(expression) ? ancestors[expression] :
+ Selector.findElement(ancestors, expression, index);
+ },
+
+ down: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return element.firstDescendant();
+ return Object.isNumber(expression) ? element.descendants()[expression] :
+ element.select(expression)[index || 0];
+ },
+
+ previous: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
+ var previousSiblings = element.previousSiblings();
+ return Object.isNumber(expression) ? previousSiblings[expression] :
+ Selector.findElement(previousSiblings, expression, index);
+ },
+
+ next: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
+ var nextSiblings = element.nextSiblings();
+ return Object.isNumber(expression) ? nextSiblings[expression] :
+ Selector.findElement(nextSiblings, expression, index);
+ },
+
+ select: function() {
+ var args = $A(arguments), element = $(args.shift());
+ return Selector.findChildElements(element, args);
+ },
+
+ adjacent: function() {
+ var args = $A(arguments), element = $(args.shift());
+ return Selector.findChildElements(element.parentNode, args).without(element);
+ },
+
+ identify: function(element) {
+ element = $(element);
+ var id = element.readAttribute('id'), self = arguments.callee;
+ if (id) return id;
+ do { id = 'anonymous_element_' + self.counter++ } while ($(id));
+ element.writeAttribute('id', id);
+ return id;
+ },
+
+ readAttribute: function(element, name) {
+ element = $(element);
+ if (Prototype.Browser.IE) {
+ var t = Element._attributeTranslations.read;
+ if (t.values[name]) return t.values[name](element, name);
+ if (t.names[name]) name = t.names[name];
+ if (name.include(':')) {
+ return (!element.attributes || !element.attributes[name]) ? null :
+ element.attributes[name].value;
+ }
+ }
+ return element.getAttribute(name);
+ },
+
+ writeAttribute: function(element, name, value) {
+ element = $(element);
+ var attributes = { }, t = Element._attributeTranslations.write;
+
+ if (typeof name == 'object') attributes = name;
+ else attributes[name] = Object.isUndefined(value) ? true : value;
+
+ for (var attr in attributes) {
+ name = t.names[attr] || attr;
+ value = attributes[attr];
+ if (t.values[attr]) name = t.values[attr](element, value);
+ if (value === false || value === null)
+ element.removeAttribute(name);
+ else if (value === true)
+ element.setAttribute(name, name);
+ else element.setAttribute(name, value);
+ }
+ return element;
+ },
+
+ getHeight: function(element) {
+ return $(element).getDimensions().height;
+ },
+
+ getWidth: function(element) {
+ return $(element).getDimensions().width;
+ },
+
+ classNames: function(element) {
+ return new Element.ClassNames(element);
+ },
+
+ hasClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ var elementClassName = element.className;
+ return (elementClassName.length > 0 && (elementClassName == className ||
+ new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
+ },
+
+ addClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ if (!element.hasClassName(className))
+ element.className += (element.className ? ' ' : '') + className;
+ return element;
+ },
+
+ removeClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ element.className = element.className.replace(
+ new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
+ return element;
+ },
+
+ toggleClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ return element[element.hasClassName(className) ?
+ 'removeClassName' : 'addClassName'](className);
+ },
+
+ // removes whitespace-only text node children
+ cleanWhitespace: function(element) {
+ element = $(element);
+ var node = element.firstChild;
+ while (node) {
+ var nextNode = node.nextSibling;
+ if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+ element.removeChild(node);
+ node = nextNode;
+ }
+ return element;
+ },
+
+ empty: function(element) {
+ return $(element).innerHTML.blank();
+ },
+
+ descendantOf: function(element, ancestor) {
+ element = $(element), ancestor = $(ancestor);
+ var originalAncestor = ancestor;
+
+ if (element.compareDocumentPosition)
+ return (element.compareDocumentPosition(ancestor) & 8) === 8;
+
+ if (element.sourceIndex && !Prototype.Browser.Opera) {
+ var e = element.sourceIndex, a = ancestor.sourceIndex,
+ nextAncestor = ancestor.nextSibling;
+ if (!nextAncestor) {
+ do { ancestor = ancestor.parentNode; }
+ while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
+ }
+ if (nextAncestor && nextAncestor.sourceIndex)
+ return (e > a && e < nextAncestor.sourceIndex);
+ }
+
+ while (element = element.parentNode)
+ if (element == originalAncestor) return true;
+ return false;
+ },
+
+ scrollTo: function(element) {
+ element = $(element);
+ var pos = element.cumulativeOffset();
+ window.scrollTo(pos[0], pos[1]);
+ return element;
+ },
+
+ getStyle: function(element, style) {
+ element = $(element);
+ style = style == 'float' ? 'cssFloat' : style.camelize();
+ var value = element.style[style];
+ if (!value) {
+ var css = document.defaultView.getComputedStyle(element, null);
+ value = css ? css[style] : null;
+ }
+ if (style == 'opacity') return value ? parseFloat(value) : 1.0;
+ return value == 'auto' ? null : value;
+ },
+
+ getOpacity: function(element) {
+ return $(element).getStyle('opacity');
+ },
+
+ setStyle: function(element, styles) {
+ element = $(element);
+ var elementStyle = element.style, match;
+ if (Object.isString(styles)) {
+ element.style.cssText += ';' + styles;
+ return styles.include('opacity') ?
+ element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
+ }
+ for (var property in styles)
+ if (property == 'opacity') element.setOpacity(styles[property]);
+ else
+ elementStyle[(property == 'float' || property == 'cssFloat') ?
+ (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
+ property] = styles[property];
+
+ return element;
+ },
+
+ setOpacity: function(element, value) {
+ element = $(element);
+ element.style.opacity = (value == 1 || value === '') ? '' :
+ (value < 0.00001) ? 0 : value;
+ return element;
+ },
+
+ getDimensions: function(element) {
+ element = $(element);
+ var display = $(element).getStyle('display');
+ if (display != 'none' && display != null) // Safari bug
+ return {width: element.offsetWidth, height: element.offsetHeight};
+
+ // All *Width and *Height properties give 0 on elements with display none,
+ // so enable the element temporarily
+ var els = element.style;
+ var originalVisibility = els.visibility;
+ var originalPosition = els.position;
+ var originalDisplay = els.display;
+ els.visibility = 'hidden';
+ els.position = 'absolute';
+ els.display = 'block';
+ var originalWidth = element.clientWidth;
+ var originalHeight = element.clientHeight;
+ els.display = originalDisplay;
+ els.position = originalPosition;
+ els.visibility = originalVisibility;
+ return {width: originalWidth, height: originalHeight};
+ },
+
+ makePositioned: function(element) {
+ element = $(element);
+ var pos = Element.getStyle(element, 'position');
+ if (pos == 'static' || !pos) {
+ element._madePositioned = true;
+ element.style.position = 'relative';
+ // Opera returns the offset relative to the positioning context, when an
+ // element is position relative but top and left have not been defined
+ if (window.opera) {
+ element.style.top = 0;
+ element.style.left = 0;
+ }
+ }
+ return element;
+ },
+
+ undoPositioned: function(element) {
+ element = $(element);
+ if (element._madePositioned) {
+ element._madePositioned = undefined;
+ element.style.position =
+ element.style.top =
+ element.style.left =
+ element.style.bottom =
+ element.style.right = '';
+ }
+ return element;
+ },
+
+ makeClipping: function(element) {
+ element = $(element);
+ if (element._overflow) return element;
+ element._overflow = Element.getStyle(element, 'overflow') || 'auto';
+ if (element._overflow !== 'hidden')
+ element.style.overflow = 'hidden';
+ return element;
+ },
+
+ undoClipping: function(element) {
+ element = $(element);
+ if (!element._overflow) return element;
+ element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
+ element._overflow = null;
+ return element;
+ },
+
+ cumulativeOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ } while (element);
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ positionedOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ if (element) {
+ if (element.tagName == 'BODY') break;
+ var p = Element.getStyle(element, 'position');
+ if (p !== 'static') break;
+ }
+ } while (element);
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ absolutize: function(element) {
+ element = $(element);
+ if (element.getStyle('position') == 'absolute') return;
+ // Position.prepare(); // To be done manually by Scripty when it needs it.
+
+ var offsets = element.positionedOffset();
+ var top = offsets[1];
+ var left = offsets[0];
+ var width = element.clientWidth;
+ var height = element.clientHeight;
+
+ element._originalLeft = left - parseFloat(element.style.left || 0);
+ element._originalTop = top - parseFloat(element.style.top || 0);
+ element._originalWidth = element.style.width;
+ element._originalHeight = element.style.height;
+
+ element.style.position = 'absolute';
+ element.style.top = top + 'px';
+ element.style.left = left + 'px';
+ element.style.width = width + 'px';
+ element.style.height = height + 'px';
+ return element;
+ },
+
+ relativize: function(element) {
+ element = $(element);
+ if (element.getStyle('position') == 'relative') return;
+ // Position.prepare(); // To be done manually by Scripty when it needs it.
+
+ element.style.position = 'relative';
+ var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
+ var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+ element.style.top = top + 'px';
+ element.style.left = left + 'px';
+ element.style.height = element._originalHeight;
+ element.style.width = element._originalWidth;
+ return element;
+ },
+
+ cumulativeScrollOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.scrollTop || 0;
+ valueL += element.scrollLeft || 0;
+ element = element.parentNode;
+ } while (element);
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ getOffsetParent: function(element) {
+ if (element.offsetParent) return $(element.offsetParent);
+ if (element == document.body) return $(element);
+
+ while ((element = element.parentNode) && element != document.body)
+ if (Element.getStyle(element, 'position') != 'static')
+ return $(element);
+
+ return $(document.body);
+ },
+
+ viewportOffset: function(forElement) {
+ var valueT = 0, valueL = 0;
+
+ var element = forElement;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+
+ // Safari fix
+ if (element.offsetParent == document.body &&
+ Element.getStyle(element, 'position') == 'absolute') break;
+
+ } while (element = element.offsetParent);
+
+ element = forElement;
+ do {
+ if (!Prototype.Browser.Opera || element.tagName == 'BODY') {
+ valueT -= element.scrollTop || 0;
+ valueL -= element.scrollLeft || 0;
+ }
+ } while (element = element.parentNode);
+
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ clonePosition: function(element, source) {
+ var options = Object.extend({
+ setLeft: true,
+ setTop: true,
+ setWidth: true,
+ setHeight: true,
+ offsetTop: 0,
+ offsetLeft: 0
+ }, arguments[2] || { });
+
+ // find page position of source
+ source = $(source);
+ var p = source.viewportOffset();
+
+ // find coordinate system to use
+ element = $(element);
+ var delta = [0, 0];
+ var parent = null;
+ // delta [0,0] will do fine with position: fixed elements,
+ // position:absolute needs offsetParent deltas
+ if (Element.getStyle(element, 'position') == 'absolute') {
+ parent = element.getOffsetParent();
+ delta = parent.viewportOffset();
+ }
+
+ // correct by body offsets (fixes Safari)
+ if (parent == document.body) {
+ delta[0] -= document.body.offsetLeft;
+ delta[1] -= document.body.offsetTop;
+ }
+
+ // set position
+ if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
+ if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
+ if (options.setWidth) element.style.width = source.offsetWidth + 'px';
+ if (options.setHeight) element.style.height = source.offsetHeight + 'px';
+ return element;
+ }
+};
+
+Element.Methods.identify.counter = 1;
+
+Object.extend(Element.Methods, {
+ getElementsBySelector: Element.Methods.select,
+ childElements: Element.Methods.immediateDescendants
+});
+
+Element._attributeTranslations = {
+ write: {
+ names: {
+ className: 'class',
+ htmlFor: 'for'
+ },
+ values: { }
+ }
+};
+
+if (Prototype.Browser.Opera) {
+ Element.Methods.getStyle = Element.Methods.getStyle.wrap(
+ function(proceed, element, style) {
+ switch (style) {
+ case 'left': case 'top': case 'right': case 'bottom':
+ if (proceed(element, 'position') === 'static') return null;
+ case 'height': case 'width':
+ // returns '0px' for hidden elements; we want it to return null
+ if (!Element.visible(element)) return null;
+
+ // returns the border-box dimensions rather than the content-box
+ // dimensions, so we subtract padding and borders from the value
+ var dim = parseInt(proceed(element, style), 10);
+
+ if (dim !== element['offset' + style.capitalize()])
+ return dim + 'px';
+
+ var properties;
+ if (style === 'height') {
+ properties = ['border-top-width', 'padding-top',
+ 'padding-bottom', 'border-bottom-width'];
+ }
+ else {
+ properties = ['border-left-width', 'padding-left',
+ 'padding-right', 'border-right-width'];
+ }
+ return properties.inject(dim, function(memo, property) {
+ var val = proceed(element, property);
+ return val === null ? memo : memo - parseInt(val, 10);
+ }) + 'px';
+ default: return proceed(element, style);
+ }
+ }
+ );
+
+ Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
+ function(proceed, element, attribute) {
+ if (attribute === 'title') return element.title;
+ return proceed(element, attribute);
+ }
+ );
+}
+
+else if (Prototype.Browser.IE) {
+ // IE doesn't report offsets correctly for static elements, so we change them
+ // to "relative" to get the values, then change them back.
+ Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
+ function(proceed, element) {
+ element = $(element);
+ var position = element.getStyle('position');
+ if (position !== 'static') return proceed(element);
+ element.setStyle({ position: 'relative' });
+ var value = proceed(element);
+ element.setStyle({ position: position });
+ return value;
+ }
+ );
+
+ $w('positionedOffset viewportOffset').each(function(method) {
+ Element.Methods[method] = Element.Methods[method].wrap(
+ function(proceed, element) {
+ element = $(element);
+ var position = element.getStyle('position');
+ if (position !== 'static') return proceed(element);
+ // Trigger hasLayout on the offset parent so that IE6 reports
+ // accurate offsetTop and offsetLeft values for position: fixed.
+ var offsetParent = element.getOffsetParent();
+ if (offsetParent && offsetParent.getStyle('position') === 'fixed')
+ offsetParent.setStyle({ zoom: 1 });
+ element.setStyle({ position: 'relative' });
+ var value = proceed(element);
+ element.setStyle({ position: position });
+ return value;
+ }
+ );
+ });
+
+ Element.Methods.getStyle = function(element, style) {
+ element = $(element);
+ style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
+ var value = element.style[style];
+ if (!value && element.currentStyle) value = element.currentStyle[style];
+
+ if (style == 'opacity') {
+ if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
+ if (value[1]) return parseFloat(value[1]) / 100;
+ return 1.0;
+ }
+
+ if (value == 'auto') {
+ if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
+ return element['offset' + style.capitalize()] + 'px';
+ return null;
+ }
+ return value;
+ };
+
+ Element.Methods.setOpacity = function(element, value) {
+ function stripAlpha(filter){
+ return filter.replace(/alpha\([^\)]*\)/gi,'');
+ }
+ element = $(element);
+ var currentStyle = element.currentStyle;
+ if ((currentStyle && !currentStyle.hasLayout) ||
+ (!currentStyle && element.style.zoom == 'normal'))
+ element.style.zoom = 1;
+
+ var filter = element.getStyle('filter'), style = element.style;
+ if (value == 1 || value === '') {
+ (filter = stripAlpha(filter)) ?
+ style.filter = filter : style.removeAttribute('filter');
+ return element;
+ } else if (value < 0.00001) value = 0;
+ style.filter = stripAlpha(filter) +
+ 'alpha(opacity=' + (value * 100) + ')';
+ return element;
+ };
+
+ Element._attributeTranslations = {
+ read: {
+ names: {
+ 'class': 'className',
+ 'for': 'htmlFor'
+ },
+ values: {
+ _getAttr: function(element, attribute) {
+ return element.getAttribute(attribute, 2);
+ },
+ _getAttrNode: function(element, attribute) {
+ var node = element.getAttributeNode(attribute);
+ return node ? node.value : "";
+ },
+ _getEv: function(element, attribute) {
+ attribute = element.getAttribute(attribute);
+ return attribute ? attribute.toString().slice(23, -2) : null;
+ },
+ _flag: function(element, attribute) {
+ return $(element).hasAttribute(attribute) ? attribute : null;
+ },
+ style: function(element) {
+ return element.style.cssText.toLowerCase();
+ },
+ title: function(element) {
+ return element.title;
+ }
+ }
+ }
+ };
+
+ Element._attributeTranslations.write = {
+ names: Object.extend({
+ cellpadding: 'cellPadding',
+ cellspacing: 'cellSpacing'
+ }, Element._attributeTranslations.read.names),
+ values: {
+ checked: function(element, value) {
+ element.checked = !!value;
+ },
+
+ style: function(element, value) {
+ element.style.cssText = value ? value : '';
+ }
+ }
+ };
+
+ Element._attributeTranslations.has = {};
+
+ $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
+ 'encType maxLength readOnly longDesc').each(function(attr) {
+ Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
+ Element._attributeTranslations.has[attr.toLowerCase()] = attr;
+ });
+
+ (function(v) {
+ Object.extend(v, {
+ href: v._getAttr,
+ src: v._getAttr,
+ type: v._getAttr,
+ action: v._getAttrNode,
+ disabled: v._flag,
+ checked: v._flag,
+ readonly: v._flag,
+ multiple: v._flag,
+ onload: v._getEv,
+ onunload: v._getEv,
+ onclick: v._getEv,
+ ondblclick: v._getEv,
+ onmousedown: v._getEv,
+ onmouseup: v._getEv,
+ onmouseover: v._getEv,
+ onmousemove: v._getEv,
+ onmouseout: v._getEv,
+ onfocus: v._getEv,
+ onblur: v._getEv,
+ onkeypress: v._getEv,
+ onkeydown: v._getEv,
+ onkeyup: v._getEv,
+ onsubmit: v._getEv,
+ onreset: v._getEv,
+ onselect: v._getEv,
+ onchange: v._getEv
+ });
+ })(Element._attributeTranslations.read.values);
+}
+
+else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
+ Element.Methods.setOpacity = function(element, value) {
+ element = $(element);
+ element.style.opacity = (value == 1) ? 0.999999 :
+ (value === '') ? '' : (value < 0.00001) ? 0 : value;
+ return element;
+ };
+}
+
+else if (Prototype.Browser.WebKit) {
+ Element.Methods.setOpacity = function(element, value) {
+ element = $(element);
+ element.style.opacity = (value == 1 || value === '') ? '' :
+ (value < 0.00001) ? 0 : value;
+
+ if (value == 1)
+ if(element.tagName == 'IMG' && element.width) {
+ element.width++; element.width--;
+ } else try {
+ var n = document.createTextNode(' ');
+ element.appendChild(n);
+ element.removeChild(n);
+ } catch (e) { }
+
+ return element;
+ };
+
+ // Safari returns margins on body which is incorrect if the child is absolutely
+ // positioned. For performance reasons, redefine Element#cumulativeOffset for
+ // KHTML/WebKit only.
+ Element.Methods.cumulativeOffset = function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ if (element.offsetParent == document.body)
+ if (Element.getStyle(element, 'position') == 'absolute') break;
+
+ element = element.offsetParent;
+ } while (element);
+
+ return Element._returnOffset(valueL, valueT);
+ };
+}
+
+if (Prototype.Browser.IE || Prototype.Browser.Opera) {
+ // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
+ Element.Methods.update = function(element, content) {
+ element = $(element);
+
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) return element.update().insert(content);
+
+ content = Object.toHTML(content);
+ var tagName = element.tagName.toUpperCase();
+
+ if (tagName in Element._insertionTranslations.tags) {
+ $A(element.childNodes).each(function(node) { element.removeChild(node) });
+ Element._getContentFromAnonymousElement(tagName, content.stripScripts())
+ .each(function(node) { element.appendChild(node) });
+ }
+ else element.innerHTML = content.stripScripts();
+
+ content.evalScripts.bind(content).defer();
+ return element;
+ };
+}
+
+if ('outerHTML' in document.createElement('div')) {
+ Element.Methods.replace = function(element, content) {
+ element = $(element);
+
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) {
+ element.parentNode.replaceChild(content, element);
+ return element;
+ }
+
+ content = Object.toHTML(content);
+ var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
+
+ if (Element._insertionTranslations.tags[tagName]) {
+ var nextSibling = element.next();
+ var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+ parent.removeChild(element);
+ if (nextSibling)
+ fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
+ else
+ fragments.each(function(node) { parent.appendChild(node) });
+ }
+ else element.outerHTML = content.stripScripts();
+
+ content.evalScripts.bind(content).defer();
+ return element;
+ };
+}
+
+Element._returnOffset = function(l, t) {
+ var result = [l, t];
+ result.left = l;
+ result.top = t;
+ return result;
+};
+
+Element._getContentFromAnonymousElement = function(tagName, html) {
+ var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
+ if (t) {
+ div.innerHTML = t[0] + html + t[1];
+ t[2].times(function() { div = div.firstChild });
+ } else div.innerHTML = html;
+ return $A(div.childNodes);
+};
+
+Element._insertionTranslations = {
+ before: function(element, node) {
+ element.parentNode.insertBefore(node, element);
+ },
+ top: function(element, node) {
+ element.insertBefore(node, element.firstChild);
+ },
+ bottom: function(element, node) {
+ element.appendChild(node);
+ },
+ after: function(element, node) {
+ element.parentNode.insertBefore(node, element.nextSibling);
+ },
+ tags: {
+ TABLE: ['<table>', '</table>', 1],
+ TBODY: ['<table><tbody>', '</tbody></table>', 2],
+ TR: ['<table><tbody><tr>', '</tr></tbody></table>', 3],
+ TD: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
+ SELECT: ['<select>', '</select>', 1]
+ }
+};
+
+(function() {
+ Object.extend(this.tags, {
+ THEAD: this.tags.TBODY,
+ TFOOT: this.tags.TBODY,
+ TH: this.tags.TD
+ });
+}).call(Element._insertionTranslations);
+
+Element.Methods.Simulated = {
+ hasAttribute: function(element, attribute) {
+ attribute = Element._attributeTranslations.has[attribute] || attribute;
+ var node = $(element).getAttributeNode(attribute);
+ return node && node.specified;
+ }
+};
+
+Element.Methods.ByTag = { };
+
+Object.extend(Element, Element.Methods);
+
+if (!Prototype.BrowserFeatures.ElementExtensions &&
+ document.createElement('div').__proto__) {
+ window.HTMLElement = { };
+ window.HTMLElement.prototype = document.createElement('div').__proto__;
+ Prototype.BrowserFeatures.ElementExtensions = true;
+}
+
+Element.extend = (function() {
+ if (Prototype.BrowserFeatures.SpecificElementExtensions)
+ return Prototype.K;
+
+ var Methods = { }, ByTag = Element.Methods.ByTag;
+
+ var extend = Object.extend(function(element) {
+ if (!element || element._extendedByPrototype ||
+ element.nodeType != 1 || element == window) return element;
+
+ var methods = Object.clone(Methods),
+ tagName = element.tagName, property, value;
+
+ // extend methods for specific tags
+ if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
+
+ for (property in methods) {
+ value = methods[property];
+ if (Object.isFunction(value) && !(property in element))
+ element[property] = value.methodize();
+ }
+
+ element._extendedByPrototype = Prototype.emptyFunction;
+ return element;
+
+ }, {
+ refresh: function() {
+ // extend methods for all tags (Safari doesn't need this)
+ if (!Prototype.BrowserFeatures.ElementExtensions) {
+ Object.extend(Methods, Element.Methods);
+ Object.extend(Methods, Element.Methods.Simulated);
+ }
+ }
+ });
+
+ extend.refresh();
+ return extend;
+})();
+
+Element.hasAttribute = function(element, attribute) {
+ if (element.hasAttribute) return element.hasAttribute(attribute);
+ return Element.Methods.Simulated.hasAttribute(element, attribute);
+};
+
+Element.addMethods = function(methods) {
+ var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
+
+ if (!methods) {
+ Object.extend(Form, Form.Methods);
+ Object.extend(Form.Element, Form.Element.Methods);
+ Object.extend(Element.Methods.ByTag, {
+ "FORM": Object.clone(Form.Methods),
+ "INPUT": Object.clone(Form.Element.Methods),
+ "SELECT": Object.clone(Form.Element.Methods),
+ "TEXTAREA": Object.clone(Form.Element.Methods)
+ });
+ }
+
+ if (arguments.length == 2) {
+ var tagName = methods;
+ methods = arguments[1];
+ }
+
+ if (!tagName) Object.extend(Element.Methods, methods || { });
+ else {
+ if (Object.isArray(tagName)) tagName.each(extend);
+ else extend(tagName);
+ }
+
+ function extend(tagName) {
+ tagName = tagName.toUpperCase();
+ if (!Element.Methods.ByTag[tagName])
+ Element.Methods.ByTag[tagName] = { };
+ Object.extend(Element.Methods.ByTag[tagName], methods);
+ }
+
+ function copy(methods, destination, onlyIfAbsent) {
+ onlyIfAbsent = onlyIfAbsent || false;
+ for (var property in methods) {
+ var value = methods[property];
+ if (!Object.isFunction(value)) continue;
+ if (!onlyIfAbsent || !(property in destination))
+ destination[property] = value.methodize();
+ }
+ }
+
+ function findDOMClass(tagName) {
+ var klass;
+ var trans = {
+ "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
+ "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
+ "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
+ "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
+ "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
+ "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
+ "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
+ "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
+ "FrameSet", "IFRAME": "IFrame"
+ };
+ if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
+ if (window[klass]) return window[klass];
+ klass = 'HTML' + tagName + 'Element';
+ if (window[klass]) return window[klass];
+ klass = 'HTML' + tagName.capitalize() + 'Element';
+ if (window[klass]) return window[klass];
+
+ window[klass] = { };
+ window[klass].prototype = document.createElement(tagName).__proto__;
+ return window[klass];
+ }
+
+ if (F.ElementExtensions) {
+ copy(Element.Methods, HTMLElement.prototype);
+ copy(Element.Methods.Simulated, HTMLElement.prototype, true);
+ }
+
+ if (F.SpecificElementExtensions) {
+ for (var tag in Element.Methods.ByTag) {
+ var klass = findDOMClass(tag);
+ if (Object.isUndefined(klass)) continue;
+ copy(T[tag], klass.prototype);
+ }
+ }
+
+ Object.extend(Element, Element.Methods);
+ delete Element.ByTag;
+
+ if (Element.extend.refresh) Element.extend.refresh();
+ Element.cache = { };
+};
+
+document.viewport = {
+ getDimensions: function() {
+ var dimensions = { };
+ var B = Prototype.Browser;
+ $w('width height').each(function(d) {
+ var D = d.capitalize();
+ dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] :
+ (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D];
+ });
+ return dimensions;
+ },
+
+ getWidth: function() {
+ return this.getDimensions().width;
+ },
+
+ getHeight: function() {
+ return this.getDimensions().height;
+ },
+
+ getScrollOffsets: function() {
+ return Element._returnOffset(
+ window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
+ window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
+ }
+};
+/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
+ * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
+ * license. Please see http://www.yui-ext.com/ for more information. */
+
+var Selector = Class.create({
+ initialize: function(expression) {
+ this.expression = expression.strip();
+ this.compileMatcher();
+ },
+
+ shouldUseXPath: function() {
+ if (!Prototype.BrowserFeatures.XPath) return false;
+
+ var e = this.expression;
+
+ // Safari 3 chokes on :*-of-type and :empty
+ if (Prototype.Browser.WebKit &&
+ (e.include("-of-type") || e.include(":empty")))
+ return false;
+
+ // XPath can't do namespaced attributes, nor can it read
+ // the "checked" property from DOM nodes
+ if ((/(\[[\w-]*?:|:checked)/).test(this.expression))
+ return false;
+
+ return true;
+ },
+
+ compileMatcher: function() {
+ if (this.shouldUseXPath())
+ return this.compileXPathMatcher();
+
+ var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
+ c = Selector.criteria, le, p, m;
+
+ if (Selector._cache[e]) {
+ this.matcher = Selector._cache[e];
+ return;
+ }
+
+ this.matcher = ["this.matcher = function(root) {",
+ "var r = root, h = Selector.handlers, c = false, n;"];
+
+ while (e && le != e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ p = ps[i];
+ if (m = e.match(p)) {
+ this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
+ new Template(c[i]).evaluate(m));
+ e = e.replace(m[0], '');
+ break;
+ }
+ }
+ }
+
+ this.matcher.push("return h.unique(n);\n}");
+ eval(this.matcher.join('\n'));
+ Selector._cache[this.expression] = this.matcher;
+ },
+
+ compileXPathMatcher: function() {
+ var e = this.expression, ps = Selector.patterns,
+ x = Selector.xpath, le, m;
+
+ if (Selector._cache[e]) {
+ this.xpath = Selector._cache[e]; return;
+ }
+
+ this.matcher = ['.//*'];
+ while (e && le != e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ if (m = e.match(ps[i])) {
+ this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
+ new Template(x[i]).evaluate(m));
+ e = e.replace(m[0], '');
+ break;
+ }
+ }
+ }
+
+ this.xpath = this.matcher.join('');
+ Selector._cache[this.expression] = this.xpath;
+ },
+
+ findElements: function(root) {
+ root = root || document;
+ if (this.xpath) return document._getElementsByXPath(this.xpath, root);
+ return this.matcher(root);
+ },
+
+ match: function(element) {
+ this.tokens = [];
+
+ var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
+ var le, p, m;
+
+ while (e && le !== e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ p = ps[i];
+ if (m = e.match(p)) {
+ // use the Selector.assertions methods unless the selector
+ // is too complex.
+ if (as[i]) {
+ this.tokens.push([i, Object.clone(m)]);
+ e = e.replace(m[0], '');
+ } else {
+ // reluctantly do a document-wide search
+ // and look for a match in the array
+ return this.findElements(document).include(element);
+ }
+ }
+ }
+ }
+
+ var match = true, name, matches;
+ for (var i = 0, token; token = this.tokens[i]; i++) {
+ name = token[0], matches = token[1];
+ if (!Selector.assertions[name](element, matches)) {
+ match = false; break;
+ }
+ }
+
+ return match;
+ },
+
+ toString: function() {
+ return this.expression;
+ },
+
+ inspect: function() {
+ return "#<Selector:" + this.expression.inspect() + ">";
+ }
+});
+
+Object.extend(Selector, {
+ _cache: { },
+
+ xpath: {
+ descendant: "//*",
+ child: "/*",
+ adjacent: "/following-sibling::*[1]",
+ laterSibling: '/following-sibling::*',
+ tagName: function(m) {
+ if (m[1] == '*') return '';
+ return "[local-name()='" + m[1].toLowerCase() +
+ "' or local-name()='" + m[1].toUpperCase() + "']";
+ },
+ className: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
+ id: "[@id='#{1}']",
+ attrPresence: function(m) {
+ m[1] = m[1].toLowerCase();
+ return new Template("[@#{1}]").evaluate(m);
+ },
+ attr: function(m) {
+ m[1] = m[1].toLowerCase();
+ m[3] = m[5] || m[6];
+ return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
+ },
+ pseudo: function(m) {
+ var h = Selector.xpath.pseudos[m[1]];
+ if (!h) return '';
+ if (Object.isFunction(h)) return h(m);
+ return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
+ },
+ operators: {
+ '=': "[@#{1}='#{3}']",
+ '!=': "[@#{1}!='#{3}']",
+ '^=': "[starts-with(@#{1}, '#{3}')]",
+ '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
+ '*=': "[contains(@#{1}, '#{3}')]",
+ '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
+ '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
+ },
+ pseudos: {
+ 'first-child': '[not(preceding-sibling::*)]',
+ 'last-child': '[not(following-sibling::*)]',
+ 'only-child': '[not(preceding-sibling::* or following-sibling::*)]',
+ 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
+ 'checked': "[@checked]",
+ 'disabled': "[@disabled]",
+ 'enabled': "[not(@disabled)]",
+ 'not': function(m) {
+ var e = m[6], p = Selector.patterns,
+ x = Selector.xpath, le, v;
+
+ var exclusion = [];
+ while (e && le != e && (/\S/).test(e)) {
+ le = e;
+ for (var i in p) {
+ if (m = e.match(p[i])) {
+ v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
+ exclusion.push("(" + v.substring(1, v.length - 1) + ")");
+ e = e.replace(m[0], '');
+ break;
+ }
+ }
+ }
+ return "[not(" + exclusion.join(" and ") + ")]";
+ },
+ 'nth-child': function(m) {
+ return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
+ },
+ 'nth-last-child': function(m) {
+ return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
+ },
+ 'nth-of-type': function(m) {
+ return Selector.xpath.pseudos.nth("position() ", m);
+ },
+ 'nth-last-of-type': function(m) {
+ return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
+ },
+ 'first-of-type': function(m) {
+ m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
+ },
+ 'last-of-type': function(m) {
+ m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
+ },
+ 'only-of-type': function(m) {
+ var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
+ },
+ nth: function(fragment, m) {
+ var mm, formula = m[6], predicate;
+ if (formula == 'even') formula = '2n+0';
+ if (formula == 'odd') formula = '2n+1';
+ if (mm = formula.match(/^(\d+)$/)) // digit only
+ return '[' + fragment + "= " + mm[1] + ']';
+ if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+ if (mm[1] == "-") mm[1] = -1;
+ var a = mm[1] ? Number(mm[1]) : 1;
+ var b = mm[2] ? Number(mm[2]) : 0;
+ predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
+ "((#{fragment} - #{b}) div #{a} >= 0)]";
+ return new Template(predicate).evaluate({
+ fragment: fragment, a: a, b: b });
+ }
+ }
+ }
+ },
+
+ criteria: {
+ tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;',
+ className: 'n = h.className(n, r, "#{1}", c); c = false;',
+ id: 'n = h.id(n, r, "#{1}", c); c = false;',
+ attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
+ attr: function(m) {
+ m[3] = (m[5] || m[6]);
+ return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
+ },
+ pseudo: function(m) {
+ if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
+ return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
+ },
+ descendant: 'c = "descendant";',
+ child: 'c = "child";',
+ adjacent: 'c = "adjacent";',
+ laterSibling: 'c = "laterSibling";'
+ },
+
+ patterns: {
+ // combinators must be listed first
+ // (and descendant needs to be last combinator)
+ laterSibling: /^\s*~\s*/,
+ child: /^\s*>\s*/,
+ adjacent: /^\s*\+\s*/,
+ descendant: /^\s/,
+
+ // selectors follow
+ tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
+ id: /^#([\w\-\*]+)(\b|$)/,
+ className: /^\.([\w\-\*]+)(\b|$)/,
+ pseudo:
+/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
+ attrPresence: /^\[([\w]+)\]/,
+ attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
+ },
+
+ // for Selector.match and Element#match
+ assertions: {
+ tagName: function(element, matches) {
+ return matches[1].toUpperCase() == element.tagName.toUpperCase();
+ },
+
+ className: function(element, matches) {
+ return Element.hasClassName(element, matches[1]);
+ },
+
+ id: function(element, matches) {
+ return element.id === matches[1];
+ },
+
+ attrPresence: function(element, matches) {
+ return Element.hasAttribute(element, matches[1]);
+ },
+
+ attr: function(element, matches) {
+ var nodeValue = Element.readAttribute(element, matches[1]);
+ return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
+ }
+ },
+
+ handlers: {
+ // UTILITY FUNCTIONS
+ // joins two collections
+ concat: function(a, b) {
+ for (var i = 0, node; node = b[i]; i++)
+ a.push(node);
+ return a;
+ },
+
+ // marks an array of nodes for counting
+ mark: function(nodes) {
+ var _true = Prototype.emptyFunction;
+ for (var i = 0, node; node = nodes[i]; i++)
+ node._countedByPrototype = _true;
+ return nodes;
+ },
+
+ unmark: function(nodes) {
+ for (var i = 0, node; node = nodes[i]; i++)
+ node._countedByPrototype = undefined;
+ return nodes;
+ },
+
+ // mark each child node with its position (for nth calls)
+ // "ofType" flag indicates whether we're indexing for nth-of-type
+ // rather than nth-child
+ index: function(parentNode, reverse, ofType) {
+ parentNode._countedByPrototype = Prototype.emptyFunction;
+ if (reverse) {
+ for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
+ var node = nodes[i];
+ if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
+ }
+ } else {
+ for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
+ if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
+ }
+ },
+
+ // filters out duplicates and extends all nodes
+ unique: function(nodes) {
+ if (nodes.length == 0) return nodes;
+ var results = [], n;
+ for (var i = 0, l = nodes.length; i < l; i++)
+ if (!(n = nodes[i])._countedByPrototype) {
+ n._countedByPrototype = Prototype.emptyFunction;
+ results.push(Element.extend(n));
+ }
+ return Selector.handlers.unmark(results);
+ },
+
+ // COMBINATOR FUNCTIONS
+ descendant: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ h.concat(results, node.getElementsByTagName('*'));
+ return results;
+ },
+
+ child: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ for (var j = 0, child; child = node.childNodes[j]; j++)
+ if (child.nodeType == 1 && child.tagName != '!') results.push(child);
+ }
+ return results;
+ },
+
+ adjacent: function(nodes) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ var next = this.nextElementSibling(node);
+ if (next) results.push(next);
+ }
+ return results;
+ },
+
+ laterSibling: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ h.concat(results, Element.nextSiblings(node));
+ return results;
+ },
+
+ nextElementSibling: function(node) {
+ while (node = node.nextSibling)
+ if (node.nodeType == 1) return node;
+ return null;
+ },
+
+ previousElementSibling: function(node) {
+ while (node = node.previousSibling)
+ if (node.nodeType == 1) return node;
+ return null;
+ },
+
+ // TOKEN FUNCTIONS
+ tagName: function(nodes, root, tagName, combinator) {
+ var uTagName = tagName.toUpperCase();
+ var results = [], h = Selector.handlers;
+ if (nodes) {
+ if (combinator) {
+ // fastlane for ordinary descendant combinators
+ if (combinator == "descendant") {
+ for (var i = 0, node; node = nodes[i]; i++)
+ h.concat(results, node.getElementsByTagName(tagName));
+ return results;
+ } else nodes = this[combinator](nodes);
+ if (tagName == "*") return nodes;
+ }
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node.tagName.toUpperCase() === uTagName) results.push(node);
+ return results;
+ } else return root.getElementsByTagName(tagName);
+ },
+
+ id: function(nodes, root, id, combinator) {
+ var targetNode = $(id), h = Selector.handlers;
+ if (!targetNode) return [];
+ if (!nodes && root == document) return [targetNode];
+ if (nodes) {
+ if (combinator) {
+ if (combinator == 'child') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (targetNode.parentNode == node) return [targetNode];
+ } else if (combinator == 'descendant') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Element.descendantOf(targetNode, node)) return [targetNode];
+ } else if (combinator == 'adjacent') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Selector.handlers.previousElementSibling(targetNode) == node)
+ return [targetNode];
+ } else nodes = h[combinator](nodes);
+ }
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node == targetNode) return [targetNode];
+ return [];
+ }
+ return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
+ },
+
+ className: function(nodes, root, className, combinator) {
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ return Selector.handlers.byClassName(nodes, root, className);
+ },
+
+ byClassName: function(nodes, root, className) {
+ if (!nodes) nodes = Selector.handlers.descendant([root]);
+ var needle = ' ' + className + ' ';
+ for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
+ nodeClassName = node.className;
+ if (nodeClassName.length == 0) continue;
+ if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
+ results.push(node);
+ }
+ return results;
+ },
+
+ attrPresence: function(nodes, root, attr, combinator) {
+ if (!nodes) nodes = root.getElementsByTagName("*");
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ var results = [];
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Element.hasAttribute(node, attr)) results.push(node);
+ return results;
+ },
+
+ attr: function(nodes, root, attr, value, operator, combinator) {
+ if (!nodes) nodes = root.getElementsByTagName("*");
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ var handler = Selector.operators[operator], results = [];
+ for (var i = 0, node; node = nodes[i]; i++) {
+ var nodeValue = Element.readAttribute(node, attr);
+ if (nodeValue === null) continue;
+ if (handler(nodeValue, value)) results.push(node);
+ }
+ return results;
+ },
+
+ pseudo: function(nodes, name, value, root, combinator) {
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ if (!nodes) nodes = root.getElementsByTagName("*");
+ return Selector.pseudos[name](nodes, value, root);
+ }
+ },
+
+ pseudos: {
+ 'first-child': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ if (Selector.handlers.previousElementSibling(node)) continue;
+ results.push(node);
+ }
+ return results;
+ },
+ 'last-child': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ if (Selector.handlers.nextElementSibling(node)) continue;
+ results.push(node);
+ }
+ return results;
+ },
+ 'only-child': function(nodes, value, root) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
+ results.push(node);
+ return results;
+ },
+ 'nth-child': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root);
+ },
+ 'nth-last-child': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, true);
+ },
+ 'nth-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, false, true);
+ },
+ 'nth-last-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, true, true);
+ },
+ 'first-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, "1", root, false, true);
+ },
+ 'last-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, "1", root, true, true);
+ },
+ 'only-of-type': function(nodes, formula, root) {
+ var p = Selector.pseudos;
+ return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
+ },
+
+ // handles the an+b logic
+ getIndices: function(a, b, total) {
+ if (a == 0) return b > 0 ? [b] : [];
+ return $R(1, total).inject([], function(memo, i) {
+ if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
+ return memo;
+ });
+ },
+
+ // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
+ nth: function(nodes, formula, root, reverse, ofType) {
+ if (nodes.length == 0) return [];
+ if (formula == 'even') formula = '2n+0';
+ if (formula == 'odd') formula = '2n+1';
+ var h = Selector.handlers, results = [], indexed = [], m;
+ h.mark(nodes);
+ for (var i = 0, node; node = nodes[i]; i++) {
+ if (!node.parentNode._countedByPrototype) {
+ h.index(node.parentNode, reverse, ofType);
+ indexed.push(node.parentNode);
+ }
+ }
+ if (formula.match(/^\d+$/)) { // just a number
+ formula = Number(formula);
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node.nodeIndex == formula) results.push(node);
+ } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+ if (m[1] == "-") m[1] = -1;
+ var a = m[1] ? Number(m[1]) : 1;
+ var b = m[2] ? Number(m[2]) : 0;
+ var indices = Selector.pseudos.getIndices(a, b, nodes.length);
+ for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
+ for (var j = 0; j < l; j++)
+ if (node.nodeIndex == indices[j]) results.push(node);
+ }
+ }
+ h.unmark(nodes);
+ h.unmark(indexed);
+ return results;
+ },
+
+ 'empty': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ // IE treats comments as element nodes
+ if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
+ results.push(node);
+ }
+ return results;
+ },
+
+ 'not': function(nodes, selector, root) {
+ var h = Selector.handlers, selectorType, m;
+ var exclusions = new Selector(selector).findElements(root);
+ h.mark(exclusions);
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!node._countedByPrototype) results.push(node);
+ h.unmark(exclusions);
+ return results;
+ },
+
+ 'enabled': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!node.disabled) results.push(node);
+ return results;
+ },
+
+ 'disabled': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (node.disabled) results.push(node);
+ return results;
+ },
+
+ 'checked': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (node.checked) results.push(node);
+ return results;
+ }
+ },
+
+ operators: {
+ '=': function(nv, v) { return nv == v; },
+ '!=': function(nv, v) { return nv != v; },
+ '^=': function(nv, v) { return nv.startsWith(v); },
+ '$=': function(nv, v) { return nv.endsWith(v); },
+ '*=': function(nv, v) { return nv.include(v); },
+ '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
+ '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
+ },
+
+ split: function(expression) {
+ var expressions = [];
+ expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
+ expressions.push(m[1].strip());
+ });
+ return expressions;
+ },
+
+ matchElements: function(elements, expression) {
+ var matches = $$(expression), h = Selector.handlers;
+ h.mark(matches);
+ for (var i = 0, results = [], element; element = elements[i]; i++)
+ if (element._countedByPrototype) results.push(element);
+ h.unmark(matches);
+ return results;
+ },
+
+ findElement: function(elements, expression, index) {
+ if (Object.isNumber(expression)) {
+ index = expression; expression = false;
+ }
+ return Selector.matchElements(elements, expression || '*')[index || 0];
+ },
+
+ findChildElements: function(element, expressions) {
+ expressions = Selector.split(expressions.join(','));
+ var results = [], h = Selector.handlers;
+ for (var i = 0, l = expressions.length, selector; i < l; i++) {
+ selector = new Selector(expressions[i].strip());
+ h.concat(results, selector.findElements(element));
+ }
+ return (l > 1) ? h.unique(results) : results;
+ }
+});
+
+if (Prototype.Browser.IE) {
+ Object.extend(Selector.handlers, {
+ // IE returns comment nodes on getElementsByTagName("*").
+ // Filter them out.
+ concat: function(a, b) {
+ for (var i = 0, node; node = b[i]; i++)
+ if (node.tagName !== "!") a.push(node);
+ return a;
+ },
+
+ // IE improperly serializes _countedByPrototype in (inner|outer)HTML.
+ unmark: function(nodes) {
+ for (var i = 0, node; node = nodes[i]; i++)
+ node.removeAttribute('_countedByPrototype');
+ return nodes;
+ }
+ });
+}
+
+function $$() {
+ return Selector.findChildElements(document, $A(arguments));
+}
+var Form = {
+ reset: function(form) {
+ $(form).reset();
+ return form;
+ },
+
+ serializeElements: function(elements, options) {
+ if (typeof options != 'object') options = { hash: !!options };
+ else if (Object.isUndefined(options.hash)) options.hash = true;
+ var key, value, submitted = false, submit = options.submit;
+
+ var data = elements.inject({ }, function(result, element) {
+ if (!element.disabled && element.name) {
+ key = element.name; value = $(element).getValue();
+ if (value != null && (element.type != 'submit' || (!submitted &&
+ submit !== false && (!submit || key == submit) && (submitted = true)))) {
+ if (key in result) {
+ // a key is already present; construct an array of values
+ if (!Object.isArray(result[key])) result[key] = [result[key]];
+ result[key].push(value);
+ }
+ else result[key] = value;
+ }
+ }
+ return result;
+ });
+
+ return options.hash ? data : Object.toQueryString(data);
+ }
+};
+
+Form.Methods = {
+ serialize: function(form, options) {
+ return Form.serializeElements(Form.getElements(form), options);
+ },
+
+ getElements: function(form) {
+ return $A($(form).getElementsByTagName('*')).inject([],
+ function(elements, child) {
+ if (Form.Element.Serializers[child.tagName.toLowerCase()])
+ elements.push(Element.extend(child));
+ return elements;
+ }
+ );
+ },
+
+ getInputs: function(form, typeName, name) {
+ form = $(form);
+ var inputs = form.getElementsByTagName('input');
+
+ if (!typeName && !name) return $A(inputs).map(Element.extend);
+
+ for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
+ var input = inputs[i];
+ if ((typeName && input.type != typeName) || (name && input.name != name))
+ continue;
+ matchingInputs.push(Element.extend(input));
+ }
+
+ return matchingInputs;
+ },
+
+ disable: function(form) {
+ form = $(form);
+ Form.getElements(form).invoke('disable');
+ return form;
+ },
+
+ enable: function(form) {
+ form = $(form);
+ Form.getElements(form).invoke('enable');
+ return form;
+ },
+
+ findFirstElement: function(form) {
+ var elements = $(form).getElements().findAll(function(element) {
+ return 'hidden' != element.type && !element.disabled;
+ });
+ var firstByIndex = elements.findAll(function(element) {
+ return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
+ }).sortBy(function(element) { return element.tabIndex }).first();
+
+ return firstByIndex ? firstByIndex : elements.find(function(element) {
+ return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+ });
+ },
+
+ focusFirstElement: function(form) {
+ form = $(form);
+ form.findFirstElement().activate();
+ return form;
+ },
+
+ request: function(form, options) {
+ form = $(form), options = Object.clone(options || { });
+
+ var params = options.parameters, action = form.readAttribute('action') || '';
+ if (action.blank()) action = window.location.href;
+ options.parameters = form.serialize(true);
+
+ if (params) {
+ if (Object.isString(params)) params = params.toQueryParams();
+ Object.extend(options.parameters, params);
+ }
+
+ if (form.hasAttribute('method') && !options.method)
+ options.method = form.method;
+
+ return new Ajax.Request(action, options);
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element = {
+ focus: function(element) {
+ $(element).focus();
+ return element;
+ },
+
+ select: function(element) {
+ $(element).select();
+ return element;
+ }
+};
+
+Form.Element.Methods = {
+ serialize: function(element) {
+ element = $(element);
+ if (!element.disabled && element.name) {
+ var value = element.getValue();
+ if (value != undefined) {
+ var pair = { };
+ pair[element.name] = value;
+ return Object.toQueryString(pair);
+ }
+ }
+ return '';
+ },
+
+ getValue: function(element) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ return Form.Element.Serializers[method](element);
+ },
+
+ setValue: function(element, value) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ Form.Element.Serializers[method](element, value);
+ return element;
+ },
+
+ clear: function(element) {
+ $(element).value = '';
+ return element;
+ },
+
+ present: function(element) {
+ return $(element).value != '';
+ },
+
+ activate: function(element) {
+ element = $(element);
+ try {
+ element.focus();
+ if (element.select && (element.tagName.toLowerCase() != 'input' ||
+ !['button', 'reset', 'submit'].include(element.type)))
+ element.select();
+ } catch (e) { }
+ return element;
+ },
+
+ disable: function(element) {
+ element = $(element);
+ element.blur();
+ element.disabled = true;
+ return element;
+ },
+
+ enable: function(element) {
+ element = $(element);
+ element.disabled = false;
+ return element;
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+var Field = Form.Element;
+var $F = Form.Element.Methods.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element.Serializers = {
+ input: function(element, value) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ return Form.Element.Serializers.inputSelector(element, value);
+ default:
+ return Form.Element.Serializers.textarea(element, value);
+ }
+ },
+
+ inputSelector: function(element, value) {
+ if (Object.isUndefined(value)) return element.checked ? element.value : null;
+ else element.checked = !!value;
+ },
+
+ textarea: function(element, value) {
+ if (Object.isUndefined(value)) return element.value;
+ else element.value = value;
+ },
+
+ select: function(element, index) {
+ if (Object.isUndefined(index))
+ return this[element.type == 'select-one' ?
+ 'selectOne' : 'selectMany'](element);
+ else {
+ var opt, value, single = !Object.isArray(index);
+ for (var i = 0, length = element.length; i < length; i++) {
+ opt = element.options[i];
+ value = this.optionValue(opt);
+ if (single) {
+ if (value == index) {
+ opt.selected = true;
+ return;
+ }
+ }
+ else opt.selected = index.include(value);
+ }
+ }
+ },
+
+ selectOne: function(element) {
+ var index = element.selectedIndex;
+ return index >= 0 ? this.optionValue(element.options[index]) : null;
+ },
+
+ selectMany: function(element) {
+ var values, length = element.length;
+ if (!length) return null;
+
+ for (var i = 0, values = []; i < length; i++) {
+ var opt = element.options[i];
+ if (opt.selected) values.push(this.optionValue(opt));
+ }
+ return values;
+ },
+
+ optionValue: function(opt) {
+ // extend element because hasAttribute may not be native
+ return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
+ initialize: function($super, element, frequency, callback) {
+ $super(callback, frequency);
+ this.element = $(element);
+ this.lastValue = this.getValue();
+ },
+
+ execute: function() {
+ var value = this.getValue();
+ if (Object.isString(this.lastValue) && Object.isString(value) ?
+ this.lastValue != value : String(this.lastValue) != String(value)) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ }
+});
+
+Form.Element.Observer = Class.create(Abstract.TimedObserver, {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.Observer = Class.create(Abstract.TimedObserver, {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = Class.create({
+ initialize: function(element, callback) {
+ this.element = $(element);
+ this.callback = callback;
+
+ this.lastValue = this.getValue();
+ if (this.element.tagName.toLowerCase() == 'form')
+ this.registerFormCallbacks();
+ else
+ this.registerCallback(this.element);
+ },
+
+ onElementEvent: function() {
+ var value = this.getValue();
+ if (this.lastValue != value) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ },
+
+ registerFormCallbacks: function() {
+ Form.getElements(this.element).each(this.registerCallback, this);
+ },
+
+ registerCallback: function(element) {
+ if (element.type) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ Event.observe(element, 'click', this.onElementEvent.bind(this));
+ break;
+ default:
+ Event.observe(element, 'change', this.onElementEvent.bind(this));
+ break;
+ }
+ }
+ }
+});
+
+Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.EventObserver = Class.create(Abstract.EventObserver, {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+if (!window.Event) var Event = { };
+
+Object.extend(Event, {
+ KEY_BACKSPACE: 8,
+ KEY_TAB: 9,
+ KEY_RETURN: 13,
+ KEY_ESC: 27,
+ KEY_LEFT: 37,
+ KEY_UP: 38,
+ KEY_RIGHT: 39,
+ KEY_DOWN: 40,
+ KEY_DELETE: 46,
+ KEY_HOME: 36,
+ KEY_END: 35,
+ KEY_PAGEUP: 33,
+ KEY_PAGEDOWN: 34,
+ KEY_INSERT: 45,
+
+ cache: { },
+
+ relatedTarget: function(event) {
+ var element;
+ switch(event.type) {
+ case 'mouseover': element = event.fromElement; break;
+ case 'mouseout': element = event.toElement; break;
+ default: return null;
+ }
+ return Element.extend(element);
+ }
+});
+
+Event.Methods = (function() {
+ var isButton;
+
+ if (Prototype.Browser.IE) {
+ var buttonMap = { 0: 1, 1: 4, 2: 2 };
+ isButton = function(event, code) {
+ return event.button == buttonMap[code];
+ };
+
+ } else if (Prototype.Browser.WebKit) {
+ isButton = function(event, code) {
+ switch (code) {
+ case 0: return event.which == 1 && !event.metaKey;
+ case 1: return event.which == 1 && event.metaKey;
+ default: return false;
+ }
+ };
+
+ } else {
+ isButton = function(event, code) {
+ return event.which ? (event.which === code + 1) : (event.button === code);
+ };
+ }
+
+ return {
+ isLeftClick: function(event) { return isButton(event, 0) },
+ isMiddleClick: function(event) { return isButton(event, 1) },
+ isRightClick: function(event) { return isButton(event, 2) },
+
+ element: function(event) {
+ var node = Event.extend(event).target;
+ return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node);
+ },
+
+ findElement: function(event, expression) {
+ var element = Event.element(event);
+ if (!expression) return element;
+ var elements = [element].concat(element.ancestors());
+ return Selector.findElement(elements, expression, 0);
+ },
+
+ pointer: function(event) {
+ return {
+ x: event.pageX || (event.clientX +
+ (document.documentElement.scrollLeft || document.body.scrollLeft)),
+ y: event.pageY || (event.clientY +
+ (document.documentElement.scrollTop || document.body.scrollTop))
+ };
+ },
+
+ pointerX: function(event) { return Event.pointer(event).x },
+ pointerY: function(event) { return Event.pointer(event).y },
+
+ stop: function(event) {
+ Event.extend(event);
+ event.preventDefault();
+ event.stopPropagation();
+ event.stopped = true;
+ }
+ };
+})();
+
+Event.extend = (function() {
+ var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
+ m[name] = Event.Methods[name].methodize();
+ return m;
+ });
+
+ if (Prototype.Browser.IE) {
+ Object.extend(methods, {
+ stopPropagation: function() { this.cancelBubble = true },
+ preventDefault: function() { this.returnValue = false },
+ inspect: function() { return "[object Event]" }
+ });
+
+ return function(event) {
+ if (!event) return false;
+ if (event._extendedByPrototype) return event;
+
+ event._extendedByPrototype = Prototype.emptyFunction;
+ var pointer = Event.pointer(event);
+ Object.extend(event, {
+ target: event.srcElement,
+ relatedTarget: Event.relatedTarget(event),
+ pageX: pointer.x,
+ pageY: pointer.y
+ });
+ return Object.extend(event, methods);
+ };
+
+ } else {
+ Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__;
+ Object.extend(Event.prototype, methods);
+ return Prototype.K;
+ }
+})();
+
+Object.extend(Event, (function() {
+ var cache = Event.cache;
+
+ function getEventID(element) {
+ if (element._prototypeEventID) return element._prototypeEventID[0];
+ arguments.callee.id = arguments.callee.id || 1;
+ return element._prototypeEventID = [++arguments.callee.id];
+ }
+
+ function getDOMEventName(eventName) {
+ if (eventName && eventName.include(':')) return "dataavailable";
+ return eventName;
+ }
+
+ function getCacheForID(id) {
+ return cache[id] = cache[id] || { };
+ }
+
+ function getWrappersForEventName(id, eventName) {
+ var c = getCacheForID(id);
+ return c[eventName] = c[eventName] || [];
+ }
+
+ function createWrapper(element, eventName, handler) {
+ var id = getEventID(element);
+ var c = getWrappersForEventName(id, eventName);
+ if (c.pluck("handler").include(handler)) return false;
+
+ var wrapper = function(event) {
+ if (!Event || !Event.extend ||
+ (event.eventName && event.eventName != eventName))
+ return false;
+
+ Event.extend(event);
+ handler.call(element, event);
+ };
+
+ wrapper.handler = handler;
+ c.push(wrapper);
+ return wrapper;
+ }
+
+ function findWrapper(id, eventName, handler) {
+ var c = getWrappersForEventName(id, eventName);
+ return c.find(function(wrapper) { return wrapper.handler == handler });
+ }
+
+ function destroyWrapper(id, eventName, handler) {
+ var c = getCacheForID(id);
+ if (!c[eventName]) return false;
+ c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
+ }
+
+ function destroyCache() {
+ for (var id in cache)
+ for (var eventName in cache[id])
+ cache[id][eventName] = null;
+ }
+
+ if (window.attachEvent) {
+ window.attachEvent("onunload", destroyCache);
+ }
+
+ return {
+ observe: function(element, eventName, handler) {
+ element = $(element);
+ var name = getDOMEventName(eventName);
+
+ var wrapper = createWrapper(element, eventName, handler);
+ if (!wrapper) return element;
+
+ if (element.addEventListener) {
+ element.addEventListener(name, wrapper, false);
+ } else {
+ element.attachEvent("on" + name, wrapper);
+ }
+
+ return element;
+ },
+
+ stopObserving: function(element, eventName, handler) {
+ element = $(element);
+ var id = getEventID(element), name = getDOMEventName(eventName);
+
+ if (!handler && eventName) {
+ getWrappersForEventName(id, eventName).each(function(wrapper) {
+ element.stopObserving(eventName, wrapper.handler);
+ });
+ return element;
+
+ } else if (!eventName) {
+ Object.keys(getCacheForID(id)).each(function(eventName) {
+ element.stopObserving(eventName);
+ });
+ return element;
+ }
+
+ var wrapper = findWrapper(id, eventName, handler);
+ if (!wrapper) return element;
+
+ if (element.removeEventListener) {
+ element.removeEventListener(name, wrapper, false);
+ } else {
+ element.detachEvent("on" + name, wrapper);
+ }
+
+ destroyWrapper(id, eventName, handler);
+
+ return element;
+ },
+
+ fire: function(element, eventName, memo) {
+ element = $(element);
+ if (element == document && document.createEvent && !element.dispatchEvent)
+ element = document.documentElement;
+
+ var event;
+ if (document.createEvent) {
+ event = document.createEvent("HTMLEvents");
+ event.initEvent("dataavailable", true, true);
+ } else {
+ event = document.createEventObject();
+ event.eventType = "ondataavailable";
+ }
+
+ event.eventName = eventName;
+ event.memo = memo || { };
+
+ if (document.createEvent) {
+ element.dispatchEvent(event);
+ } else {
+ element.fireEvent(event.eventType, event);
+ }
+
+ return Event.extend(event);
+ }
+ };
+})());
+
+Object.extend(Event, Event.Methods);
+
+Element.addMethods({
+ fire: Event.fire,
+ observe: Event.observe,
+ stopObserving: Event.stopObserving
+});
+
+Object.extend(document, {
+ fire: Element.Methods.fire.methodize(),
+ observe: Element.Methods.observe.methodize(),
+ stopObserving: Element.Methods.stopObserving.methodize(),
+ loaded: false
+});
+
+(function() {
+ /* Support for the DOMContentLoaded event is based on work by Dan Webb,
+ Matthias Miller, Dean Edwards and John Resig. */
+
+ var timer;
+
+ function fireContentLoadedEvent() {
+ if (document.loaded) return;
+ if (timer) window.clearInterval(timer);
+ document.fire("dom:loaded");
+ document.loaded = true;
+ }
+
+ if (document.addEventListener) {
+ if (Prototype.Browser.WebKit) {
+ timer = window.setInterval(function() {
+ if (/loaded|complete/.test(document.readyState))
+ fireContentLoadedEvent();
+ }, 0);
+
+ Event.observe(window, "load", fireContentLoadedEvent);
+
+ } else {
+ document.addEventListener("DOMContentLoaded",
+ fireContentLoadedEvent, false);
+ }
+
+ } else {
+ document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
+ $("__onDOMContentLoaded").onreadystatechange = function() {
+ if (this.readyState == "complete") {
+ this.onreadystatechange = null;
+ fireContentLoadedEvent();
+ }
+ };
+ }
+})();
+/*------------------------------- DEPRECATED -------------------------------*/
+
+Hash.toQueryString = Object.toQueryString;
+
+var Toggle = { display: Element.toggle };
+
+Element.Methods.childOf = Element.Methods.descendantOf;
+
+var Insertion = {
+ Before: function(element, content) {
+ return Element.insert(element, {before:content});
+ },
+
+ Top: function(element, content) {
+ return Element.insert(element, {top:content});
+ },
+
+ Bottom: function(element, content) {
+ return Element.insert(element, {bottom:content});
+ },
+
+ After: function(element, content) {
+ return Element.insert(element, {after:content});
+ }
+};
+
+var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
+
+// This should be moved to script.aculo.us; notice the deprecated methods
+// further below, that map to the newer Element methods.
+var Position = {
+ // set to true if needed, warning: firefox performance problems
+ // NOT neeeded for page scrolling, only if draggable contained in
+ // scrollable elements
+ includeScrollOffsets: false,
+
+ // must be called before calling withinIncludingScrolloffset, every time the
+ // page is scrolled
+ prepare: function() {
+ this.deltaX = window.pageXOffset
+ || document.documentElement.scrollLeft
+ || document.body.scrollLeft
+ || 0;
+ this.deltaY = window.pageYOffset
+ || document.documentElement.scrollTop
+ || document.body.scrollTop
+ || 0;
+ },
+
+ // caches x/y coordinate pair to use with overlap
+ within: function(element, x, y) {
+ if (this.includeScrollOffsets)
+ return this.withinIncludingScrolloffsets(element, x, y);
+ this.xcomp = x;
+ this.ycomp = y;
+ this.offset = Element.cumulativeOffset(element);
+
+ return (y >= this.offset[1] &&
+ y < this.offset[1] + element.offsetHeight &&
+ x >= this.offset[0] &&
+ x < this.offset[0] + element.offsetWidth);
+ },
+
+ withinIncludingScrolloffsets: function(element, x, y) {
+ var offsetcache = Element.cumulativeScrollOffset(element);
+
+ this.xcomp = x + offsetcache[0] - this.deltaX;
+ this.ycomp = y + offsetcache[1] - this.deltaY;
+ this.offset = Element.cumulativeOffset(element);
+
+ return (this.ycomp >= this.offset[1] &&
+ this.ycomp < this.offset[1] + element.offsetHeight &&
+ this.xcomp >= this.offset[0] &&
+ this.xcomp < this.offset[0] + element.offsetWidth);
+ },
+
+ // within must be called directly before
+ overlap: function(mode, element) {
+ if (!mode) return 0;
+ if (mode == 'vertical')
+ return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+ element.offsetHeight;
+ if (mode == 'horizontal')
+ return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+ element.offsetWidth;
+ },
+
+ // Deprecation layer -- use newer Element methods now (1.5.2).
+
+ cumulativeOffset: Element.Methods.cumulativeOffset,
+
+ positionedOffset: Element.Methods.positionedOffset,
+
+ absolutize: function(element) {
+ Position.prepare();
+ return Element.absolutize(element);
+ },
+
+ relativize: function(element) {
+ Position.prepare();
+ return Element.relativize(element);
+ },
+
+ realOffset: Element.Methods.cumulativeScrollOffset,
+
+ offsetParent: Element.Methods.getOffsetParent,
+
+ page: Element.Methods.viewportOffset,
+
+ clone: function(source, target, options) {
+ options = options || { };
+ return Element.clonePosition(target, source, options);
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
+ function iter(name) {
+ return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
+ }
+
+ instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
+ function(element, className) {
+ className = className.toString().strip();
+ var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
+ return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
+ } : function(element, className) {
+ className = className.toString().strip();
+ var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
+ if (!classNames && !className) return elements;
+
+ var nodes = $(element).getElementsByTagName('*');
+ className = ' ' + className + ' ';
+
+ for (var i = 0, child, cn; child = nodes[i]; i++) {
+ if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
+ (classNames && classNames.all(function(name) {
+ return !name.toString().blank() && cn.include(' ' + name + ' ');
+ }))))
+ elements.push(Element.extend(child));
+ }
+ return elements;
+ };
+
+ return function(className, parentElement) {
+ return $(parentElement || document.body).getElementsByClassName(className);
+ };
+}(Element.Methods);
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+ initialize: function(element) {
+ this.element = $(element);
+ },
+
+ _each: function(iterator) {
+ this.element.className.split(/\s+/).select(function(name) {
+ return name.length > 0;
+ })._each(iterator);
+ },
+
+ set: function(className) {
+ this.element.className = className;
+ },
+
+ add: function(classNameToAdd) {
+ if (this.include(classNameToAdd)) return;
+ this.set($A(this).concat(classNameToAdd).join(' '));
+ },
+
+ remove: function(classNameToRemove) {
+ if (!this.include(classNameToRemove)) return;
+ this.set($A(this).without(classNameToRemove).join(' '));
+ },
+
+ toString: function() {
+ return $A(this).join(' ');
+ }
+};
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+
+/*--------------------------------------------------------------------------*/
+
+Element.addMethods(); \ No newline at end of file
diff --git a/o3d/samples/build.scons b/o3d/samples/build.scons
new file mode 100644
index 0000000..c1324ba
--- /dev/null
+++ b/o3d/samples/build.scons
@@ -0,0 +1,348 @@
+# Copyright 2009, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+import __builtin__
+import os.path
+import re
+Import('env')
+
+class Error(Exception):
+ pass
+
+samples_output_dir = env.Dir('$OBJ_ROOT/samples').abspath
+samples_artifact_dir = env.Dir('$ARTIFACTS_DIR/samples').abspath
+
+copyright_header = """/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+"""
+
+def sample_emitter(env, target, source):
+ target.append(env.subst('${TARGET.base}.js', target=target, source=source))
+ return (target, source)
+
+def sample_action(target, source, env):
+ content = source[0].get_contents()
+ regexp = re.compile(r'(.*)<script(\s+type="text/javascript")' +
+ r'(\s+charset="utf-8")?\s*>(.*)</script>(.*)', re.DOTALL)
+ m = regexp.match(content)
+ if not m:
+ raise Error('Regexp failed on input file %s' % source.abspath)
+ (html_start, type, charset, script, html_end) = m.groups()
+ if not type:
+ raise Error('Found a script that lacked the javascript tag!');
+ if not charset:
+ charset = ''
+
+ html_content = (
+ '%(html_start)s<script %(type)s%(charset)s '
+ 'src="%(js_path)s"></script>%(html_end)s' %
+ {
+ 'html_start' : html_start,
+ 'type' : type,
+ 'charset' : charset,
+ 'js_path' : target[1].name,
+ 'html_end' : html_end
+ })
+
+ print "Writing sample HTML to %s" % target[0].abspath
+ file(target[0].abspath, 'wb').write(html_content)
+ print "Writing sample JS to %s" % target[1].abspath
+ file(target[1].abspath, 'wb').write(copyright_header + script)
+ return None
+
+sample_builder = Builder(action = sample_action, emitter = sample_emitter)
+
+env.Append(BUILDERS = {'SplitSampleHTML' : sample_builder})
+
+# These are the files we process for the interactive sampler.
+# TODO: We don't currently verify that these samples are included in
+# interactive_samples.js, which constructs the sampler, or that all the files
+# in there are mentioned here. The build script should validate that the lists
+# match, and should also run whenever interactive_samples.js changes.
+samples = [
+ '2d.html',
+ 'animated-scene.html',
+ 'animation.html',
+ 'canvas-texturedraw.html',
+ 'canvas.html',
+ 'convolution.html',
+ 'culling.html',
+ 'customcamera.html',
+ 'displayfps.html',
+ 'error-texture.html',
+ 'generate-texture.html',
+ 'hellocube-colors.html',
+ 'hellocube-textures.html',
+ 'hellocube.html',
+ 'helloworld.html',
+ 'hud-2d-overlay.html',
+ 'instance-override.html',
+ 'instancing.html',
+ 'juggler.html',
+ 'julia.html',
+ 'multiple-clients.html',
+ 'multiple-views.html',
+ 'particles.html',
+ 'phongshading.html',
+ 'picking.html',
+ 'primitives.html',
+ 'procedural-texture.html',
+ 'render-mode.html',
+ 'render-targets.html',
+ 'rotatemodel.html',
+ 'scatter-chart.html',
+ 'shader-test.html',
+ 'simple.html',
+ 'simpletexture.html',
+ 'skinning.html',
+ 'sobel.html',
+ 'stencil_example.html',
+ 'texturesamplers.html',
+ 'tutorial-primitive.html',
+ 'vertex-shader.html',
+ 'vertex-shader-animation.html',
+ 'zsorting.html'
+]
+
+# Split the samples into separate HTML and JS files
+for input_file in samples:
+ destdir = os.path.join(samples_artifact_dir, os.path.dirname(input_file))
+# env.SplitSampleHTML(os.path.join(destdir, 'sampler_' + input_file),
+# input_file)
+
+ # We want all the samples unprocessed, as well.
+ env.Replicate(destdir, input_file)
+
+env.Alias('samples_export', samples_artifact_dir)
+
+######################################################################
+#
+# PLEASE NOTE: If you add samples you MUST add their files to the
+# MANIFEST file in this directory for them to be included in the docs.
+#
+######################################################################
+
+# Here we load up the manifest so that we only include the files that
+# are part of the samples, and not any spurious test files or images
+# that might be left around.
+manifest = env.File("MANIFEST").get_contents().strip().split('\n')
+manifest = [x.strip() for x in manifest]
+
+def DeferManifestInstall(env):
+ # Only do this if we tried to convert at least some samples (which
+ # means that at least some existed when we declared their build steps).
+ if len(env.GetPublished('samples', 'asset_files')) > 0:
+ for manifest_item in manifest:
+ # TODO: Why doesn't replicate hookup things correctly?
+ env.Command('$ARTIFACTS_DIR/samples/' + manifest_item,
+ '$MAIN_DIR/samples/' + manifest_item,
+ [Delete('$TARGET'),
+ Copy('$TARGET', '$SOURCE'),
+ Chmod('$TARGET', 0777)])
+
+env.Defer(DeferManifestInstall)
+
+#######
+# Build JSON sample assets from zipped Collada files. Put them in the assets
+# directory in the source tree (so that we can run the samples from the source
+# tree). If one is needed in the scons-out artifacts directory, that's handled
+# by the MANIFEST file.
+
+if env.Bit('mac'):
+ converter_path = env.subst('$ARTIFACTS_DIR/converter/o3dConverter$PROGSUFFIX')
+else:
+ converter_path = env.subst('$ARTIFACTS_DIR/o3dConverter$PROGSUFFIX')
+serializer_version_path = env.subst(
+ '$SCONSTRUCT_DIR/serializer/cross/version.h')
+
+def model_emitter(env, target, source):
+ # Massage the target a little bit.
+ sample_file = env.subst('${TARGET.srcdir}/${TARGET.filebase}.o3dtgz',
+ target=target, source=source)
+ sample_file = sample_file.replace('convert_','')
+ target = [env.File(sample_file)]
+ source.append(serializer_version_path)
+ source.append(converter_path)
+ return (target, source)
+
+def model_action(target, source, env):
+ import shutil
+ import subprocess
+ import stat
+
+ # Invoke converter to generate target.
+ error = subprocess.call([
+ converter_path,
+ '--no-condition',
+ '--up-axis=%s' % env['UP_AXIS'],
+ source[0].abspath,
+ target[0].abspath,
+ ], env={'LD_LIBRARY_PATH': env.Dir('$ARTIFACTS_DIR').abspath})
+ if error != 0:
+ raise Exception('Failed to run o3dConverter on %s to produce %s' %
+ (source[0].abspath, target[0].abspath))
+
+ # Copy generated target to remaining targets. This should be faster than
+ # running the converter several times.
+ for item in target[1:]:
+ shutil.copy(target[0].abspath, item.abspath)
+ os.chmod(item.abspath, stat.S_IWRITE | stat.S_IREAD)
+
+model_builder = Builder(action = model_action, emitter = model_emitter)
+
+env.Append(BUILDERS = {'ConvertJsonSampleAssets' : model_builder})
+
+x_up_env = env.Clone(UP_AXIS='1,0,0')
+y_up_env = env.Clone(UP_AXIS='0,1,0')
+z_up_env = env.Clone(UP_AXIS='0,0,1')
+
+models = [
+ {'path': 'beachdemo/convert_assets/beachdemo.zip', 'env': z_up_env},
+ {'path': 'beachdemo/convert_assets/beach-low-poly.dae', 'env': z_up_env},
+
+
+ {'path': 'home-configurators/convert_cbassets/House_Roofless.kmz',
+ 'env': z_up_env},
+ {'path': 'home-configurators/convert_cbassets/Agra_Rug.kmz', 'env': z_up_env},
+ {'path': 'home-configurators/convert_cbassets/Asimi_Rug.kmz', 'env': z_up_env},
+ {'path': 'home-configurators/convert_cbassets/Camden_Chair.kmz',
+ 'env': z_up_env},
+ {'path': 'home-configurators/convert_cbassets/Elements_Bookshelf.kmz',
+ 'env': z_up_env},
+ {'path': 'home-configurators/convert_cbassets/Ferrara_Rug.kmz',
+ 'env': z_up_env},
+ {'path': 'home-configurators/convert_cbassets/Lounge_Chair.kmz',
+ 'env': z_up_env},
+ {'path': 'home-configurators/convert_cbassets/Lounge_Chaise.kmz',
+ 'env': z_up_env},
+ {'path': 'home-configurators/convert_cbassets/Lounge_Sofa.kmz',
+ 'env': z_up_env},
+ {'path': 'home-configurators/convert_cbassets/Lounge_Storage_Ottoman.kmz',
+ 'env': z_up_env},
+ {'path': 'home-configurators/convert_cbassets/Madison_Dining_Table.kmz',
+ 'env': z_up_env},
+ {'path': 'home-configurators/convert_cbassets/Miles_Side_Chair.kmz',
+ 'env': z_up_env},
+ {'path': 'home-configurators/convert_cbassets/Pullman_Bar_Stool.kmz',
+ 'env': z_up_env},
+ {'path': 'home-configurators/convert_cbassets/Puzzle_TV_Stand.kmz',
+ 'env': z_up_env},
+ {'path': 'home-configurators/convert_cbassets/Stow_Leather_Ottoman.kmz',
+ 'env': z_up_env},
+ {'path': 'home-configurators/convert_cbassets/Tivoli_Dining_Table.kmz',
+ 'env': z_up_env},
+ {'path': 'home-configurators/convert_cbassets/Tivoli_Miles_Dining_Set.kmz',
+ 'env': z_up_env},
+ {'path': 'home-configurators/convert_cbassets/Troy_Chair.kmz',
+ 'env': z_up_env},
+ {'path': 'home-configurators/convert_cbassets/Troy_Ottoman.kmz',
+ 'env': z_up_env},
+ {'path': 'home-configurators/convert_cbassets/Troy_Sofa.kmz',
+ 'env': z_up_env},
+ {'path': 'home-configurators/convert_cbassets/Troy_Storage_Ottoman.kmz',
+ 'env': z_up_env},
+ {'path': 'home-configurators/convert_cbassets/Troy_Twin_Sleeper.kmz',
+ 'env': z_up_env},
+
+
+ {'path': 'io/convert_levels/all_actors.kmz', 'env': y_up_env},
+ {'path': 'io/convert_levels/map1.kmz', 'env': y_up_env},
+
+ {'path': 'simpleviewer/convert_assets/cube.zip', 'env': y_up_env},
+
+ {'path': 'convert_assets/dome1.zip', 'env': y_up_env},
+ {'path': 'convert_assets/dome2.zip', 'env': y_up_env},
+ {'path': 'convert_assets/dome3.zip', 'env': y_up_env},
+ {'path': 'convert_assets/dome4.zip', 'env': y_up_env},
+ {'path': 'convert_assets/kitty_151_idle_stand05_cff1.zip', 'env': y_up_env},
+ {'path': 'convert_assets/part1.zip', 'env': y_up_env},
+ {'path': 'convert_assets/part2.zip', 'env': y_up_env},
+ {'path': 'convert_assets/part3.zip', 'env': y_up_env},
+ {'path': 'convert_assets/seven_shapes.zip', 'env': y_up_env},
+ {'path': 'convert_assets/stencil_frame.zip', 'env': y_up_env},
+ {'path': 'convert_assets/teapot.zip', 'env': y_up_env},
+ {'path': 'convert_assets/yard.zip', 'env': y_up_env},
+
+ {'path': 'waterdemo/convert_assets/bamboo.zip', 'env': y_up_env},
+ {'path': 'waterdemo/convert_assets/coconuts.zip', 'env': y_up_env},
+ {'path': 'waterdemo/convert_assets/driftwood.zip', 'env': y_up_env},
+ {'path': 'waterdemo/convert_assets/island.zip', 'env': y_up_env},
+ {'path': 'waterdemo/convert_assets/lazy_bridge.zip', 'env': y_up_env},
+ {'path': 'waterdemo/convert_assets/palm_leaves.zip', 'env': y_up_env},
+ {'path': 'waterdemo/convert_assets/palm_trees.zip', 'env': y_up_env},
+ {'path': 'waterdemo/convert_assets/rocks.9.zip', 'env': y_up_env},
+ {'path': 'waterdemo/convert_assets/rocks.zip', 'env': y_up_env},
+ ]
+
+# Little dance to do this only once so MODE=all works.
+try:
+ _ = __builtin__.done_json_assets
+except AttributeError:
+ __builtin__.done_json_assets = True
+ for model in models:
+ # We generate the sample assets into the samples directory directly,
+ # and the artifacts directory in the name of sanity -- so we don't
+ # have to do a build whenever we change a .js file in order to test
+ # the change. We only generate them if the sources exist because we
+ # might not map them in all the time.
+ if os.path.exists(
+ model['env'].File("$SAMPLE_ASSETS/" + model['path']).abspath):
+ converted = model['env'].ConvertJsonSampleAssets(
+ source = "$SAMPLE_ASSETS/" + model['path'],
+ target = model['path'])
+ model['env'].Publish('samples', 'asset_files', converted);
diff --git a/o3d/samples/canvas-fonts.html b/o3d/samples/canvas-fonts.html
new file mode 100644
index 0000000..7a284ab
--- /dev/null
+++ b/o3d/samples/canvas-fonts.html
@@ -0,0 +1,194 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+ Sample demonstrating different fonts used for text rendering using
+ the Canvas API.
+-->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+O3D Canvas
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.effect');
+o3djs.require('o3djs.canvas');
+o3djs.require('o3djs.rendergraph');
+
+// Events
+// init() once the page has finished loading.
+// unload() when the page is unloaded.
+window.onload = createClients;
+
+// Globals
+var g_client;
+var g_pack;
+var g_viewInfo;
+var g_math;
+var g_o3d;
+var g_finished = false; // for selenium testing.
+
+function createClients() {
+ o3djs.util.makeClients(init);
+}
+
+function init(clientElements) {
+ // Initialize global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = o3dElement.client;
+
+ // Create a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Set the background color to blue.
+ g_viewInfo.clearBuffer.clearColor = [0.5, 0.1, 1, 1];
+
+ // Setup an orthographic projection camera.
+ g_viewInfo.drawContext.projection = g_math.matrix4.orthographic(
+ 0 + 0.5,
+ g_client.width + 0.5,
+ g_client.height + 0.5,
+ 0 + 0.5,
+ 0.001,
+ 1000);
+
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [0, 0, 1], // Eye.
+ [0, 0, 0], // Target.
+ [0, 1, 0]); // Up.
+
+ // Create an instance of the canvas utilities library.
+ var canvasLib = o3djs.canvas.create(g_pack, g_client.root, g_viewInfo);
+
+ // Create a canvas surface to draw on.
+ var canvasQuad = canvasLib.createXYQuad(50, 50, -1, 700, 500, false);
+
+ canvasQuad.canvas.clear([1, 1, 1, 1]);
+ canvasQuad.updateTexture();
+
+ var backgroundPaint = g_pack.createObject('CanvasPaint');
+ backgroundPaint.color = [1, 0.94, 0.94, 1];
+
+ var textPaint = g_pack.createObject('CanvasPaint');
+ textPaint.color = [0, 0, 0, 1];
+ textPaint.textSize = 24;
+
+ // Set drawTextBox to true to display the box showing the text extents
+ // returned by CanvasPaint.measureText()
+ var drawTextBox = false;
+
+ var typefaceArray = [ 'Arial', 'Arial Black', 'Comic Sans MS', 'Courier New',
+ 'Georgia', 'Impact', 'Palatino', 'Verdana',
+ 'Webdings' ];
+
+ var verticalPosition = 10;
+ var horizontalPosition = 50;
+ for (var ii = 0; ii < typefaceArray.length; ii++) {
+ textPaint.textTypeface = typefaceArray[ii];
+ var lineDimensions = textPaint.measureText(typefaceArray[ii]);
+
+
+ // Add enough vertical spacing to clear the height of the text.
+ verticalPosition += lineDimensions[3] - lineDimensions[1] + 20;
+
+ if (drawTextBox) {
+ // Add the text origin position.
+ textSize[0] += horizontalPosition;
+ textSize[1] += verticalPosition;
+ textSize[2] += horizontalPosition;
+ textSize[3] += verticalPosition;
+
+ canvasQuad.canvas.drawRect(lineDimensions[0],
+ lineDimensions[1],
+ lineDimensions[2],
+ lineDimensions[3],
+ backgroundPaint);
+ }
+
+ canvasQuad.canvas.drawText(typefaceArray[ii],
+ horizontalPosition,
+ verticalPosition,
+ textPaint);
+
+ }
+
+ var internationalText = [ 'ελληνικά', 'Српски' ];
+ textPaint.textTypeface = 'Arial';
+ horizontalPosition = 400;
+ verticalPosition = 10;
+ for (var ii = 0; ii < internationalText.length; ii++) {
+ var lineDimensions = textPaint.measureText(typefaceArray[ii]);
+
+ // Add enough vertical spacing to clear the height of the text.
+ verticalPosition += lineDimensions[3] - lineDimensions[1] + 20;
+
+ canvasQuad.canvas.drawText(internationalText[ii],
+ horizontalPosition,
+ verticalPosition,
+ textPaint);
+ }
+
+ // Update the o3d texture associated with the canvas quad object.
+ canvasQuad.updateTexture();
+
+ g_finished = true;
+}
+
+</script>
+</head>
+
+<body>
+<h1>O3D Canvas Sample: Fonts</h1>
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 800px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
+
diff --git a/o3d/samples/canvas-texturedraw.html b/o3d/samples/canvas-texturedraw.html
new file mode 100644
index 0000000..efdf804
--- /dev/null
+++ b/o3d/samples/canvas-texturedraw.html
@@ -0,0 +1,268 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+ Sample demonstrating drawing into a Canvas surface using an O3D texture.
+-->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+O3D Canvas
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.io');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.effect');
+o3djs.require('o3djs.canvas');
+o3djs.require('o3djs.rendergraph');
+
+// Events
+// init() once the page has finished loading.
+// unload() when the page is unloaded.
+window.onload = createClients;
+window.onunload= unload;
+
+// Globals
+var g_o3d;
+var g_math;
+var g_client;
+var g_pack;
+var g_viewInfo;
+var g_brushTexture;
+var g_canvasQuad;
+var g_o3dElement;
+var g_finished = false; // for selenium testing.
+var g_canvasWidth = 700;
+var g_canvasHeight = 500;
+var g_borderWidth = 50;
+var g_borderHeight = 50;
+var g_pluginWidth;
+var g_pluginHeight;
+var g_clientWidth;
+var g_clientHeight;
+
+/**
+ * Remove any callbacks so they don't get called after the page has unloaded.
+ */
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+
+ // Clear the mouse events.
+ onMouseUp();
+ }
+}
+
+function createClients() {
+ o3djs.util.makeClients(init);
+}
+
+
+/**
+ * This method gets called on a mouse move event. If the mouse cursor is inside
+ * the canvas quad then it draws a copy of the brush texture onto the canvas
+ * surface at the mouse cursor position.
+ * @param e The mouse move event object
+ */
+function drawCursor(e) {
+ if (!g_brushTexture)
+ return;
+
+ var offset = {
+ x: e.x / g_client.width * g_clientWidth,
+ y: e.y / g_client.height * g_clientHeight
+ };
+
+ // Adjust for the top left corner of the canvas.
+ offset.x -= g_borderWidth;
+ offset.y -= g_borderHeight;
+
+ // Setting brushScale to a value other than 1 will draw a scaled copy of
+ // the brush bitmap onto the canvas surface. This is achieved by applying
+ // a scale transformation before calling drawBitmap(), and restoring the
+ // draw matrix immediately after.
+ var brushScale = 1;
+
+ if (offset.x >= 0 && offset.x < g_canvasWidth &&
+ offset.y >= 0 && offset.y < g_canvasHeight) {
+ g_canvasQuad.canvas.saveMatrix();
+ g_canvasQuad.canvas.scale(brushScale, brushScale);
+
+ // Note that the coordinates passed to drawBitmap get scaled by the current
+ // canvas drawing matrix and therefore we adjust them by the scale to get
+ // the bitmap to follow the mouse position.
+ g_canvasQuad.canvas.drawBitmap(
+ g_brushTexture,
+ (offset.x / brushScale - g_brushTexture.width * 0.5),
+ (offset.y / brushScale + g_brushTexture.height * 0.5));
+ g_canvasQuad.canvas.restoreMatrix();
+ g_canvasQuad.updateTexture();
+ }
+}
+
+/**
+ * Fetches the bitmap pointed to by the URL supplied by the user, creates
+ * an O3D Texture object with it, and resets the value of g_brushTexture
+ * to point to the newly created texture.
+ */
+function changeBrushTexture() {
+ var textureUrl = document.getElementById('url').value;
+ o3djs.io.loadTexture(g_pack, textureUrl, function(texture, exception) {
+ // Remove the previous brush texture from the pack.
+ if (exception) {
+ alert(exception);
+ } else {
+ if (g_brushTexture) {
+ g_pack.removeObject(g_brushTexture);
+ }
+
+ g_brushTexture = texture;
+ }
+ });
+}
+
+/**
+ * Event handler that gets called when a mouse click takes place in the
+ * O3D element. It registers a callback for mousemove which handles
+ * drawing and one for mouseup which removes the mousemove event.
+ * @param e The mouse down event.
+ */
+function onMouseDown(e) {
+ o3djs.event.addEventListener(g_o3dElement, 'mousemove', drawCursor);
+ o3djs.event.addEventListener(g_o3dElement, 'mouseup', onMouseUp);
+ // Draw the cursor at the clicked spot.
+ drawCursor(e);
+}
+
+/**
+ * Event handler for mouse up. It clears the mouse move and mouse up event
+ * handlers.
+ */
+function onMouseUp() {
+ o3djs.event.removeEventListener(g_o3dElement, 'mousemove', drawCursor);
+ o3djs.event.removeEventListener(g_o3dElement, 'mouseup', onMouseUp);
+}
+
+function init(clientElements) {
+ // Initializes global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_o3dElement = o3dElement;
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = o3dElement.client;
+
+ // Store the size of the plugin, so that we can adjust coordinates in
+ // full-screen mode. This is necessary because we're not adjusting the aspect
+ // ratio; we'd rather that the canvas filled the available area, rather than
+ // staying a fixed size or aspect ratio.
+ g_clientWidth = g_o3dElement.clientWidth;
+ g_clientHeight = g_o3dElement.clientHeight;
+
+ // Set the texture URL.
+ var path = window.location.href;
+ var index = path.lastIndexOf('/');
+ path = path.substring(0, index+1) + 'assets/brush.png';
+ var url = document.getElementById("url").value = path;
+
+ // Creates a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Set the background color to blue.
+ g_viewInfo.clearBuffer.clearColor = [0.5, 0.1, 1, 1];
+
+ // Setup an orthographic projection camera.
+ g_viewInfo.drawContext.projection = g_math.matrix4.orthographic(
+ 0 + 0.5,
+ g_client.width + 0.5,
+ g_client.height + 0.5,
+ 0 + 0.5,
+ 0.001,
+ 1000);
+
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [0, 0, 1], // Eye.
+ [0, 0, 0], // Target.
+ [0, 1, 0]); // Up.
+
+ // Creates an instance of the canvas utilities library.
+ var canvasLib = o3djs.canvas.create(g_pack, g_client.root, g_viewInfo);
+
+ // Create a canvas surface to draw on.
+ var canvasQuad =
+ canvasLib.createXYQuad(g_borderWidth, g_borderHeight, 0, g_canvasWidth,
+ g_canvasHeight, false);
+
+ canvasQuad.canvas.clear([1, 0, 0, 1]);
+ canvasQuad.updateTexture();
+
+ // Set the initial brush texture.
+ changeBrushTexture();
+
+ // Setup the events to track mouse activity.
+ o3djs.event.addEventListener(o3dElement, 'mousedown', onMouseDown);
+
+ g_canvasQuad = canvasQuad;
+
+ g_finished = true; // for selenium testing.
+}
+
+</script>
+</head>
+
+<body>
+<h1>O3D Canvas Sample: Drawing with bitmaps</h1>
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 800px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+<p><p>
+Brush URL: <input type="text" id="url" size="100">
+<input type="button" onclick="changeBrushTexture();" value="Change Brush"><BR>
+
+<p><p>Click and drag to draw onto the red canvas surface.
+</body>
+</html>
+
diff --git a/o3d/samples/canvas.html b/o3d/samples/canvas.html
new file mode 100644
index 0000000..e76fc9a
--- /dev/null
+++ b/o3d/samples/canvas.html
@@ -0,0 +1,341 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+Sample demonstrating the O3D Canvas API.
+-->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+O3D Canvas
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.effect');
+o3djs.require('o3djs.canvas');
+o3djs.require('o3djs.rendergraph');
+
+// Events
+// init() once the page has finished loading.
+// unload() when the page is unloaded.
+window.onload = createClients;
+window.onunload= unload;
+
+// Globals
+var g_o3d;
+var g_math;
+var g_client;
+var g_pack;
+var g_viewInfo;
+var g_paint;
+var g_counterCanvas;
+var g_clock = 0;
+var g_canvasLib;
+var g_timeMult = 1; // controls how fast the text counter will update
+var g_finished = false; // for selenium testing
+
+/**
+ * Remove any callbacks so they don't get called after the page has unloaded.
+ */
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+
+function createClients() {
+ o3djs.util.makeClients(init);
+}
+
+function init(clientElements) {
+ // Initializes global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = o3dElement.client;
+
+ // Creates a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Set the background color to purple.
+ g_viewInfo.clearBuffer.clearColor = [0.5, 0.1, 1, 1];
+
+ // Setup an orthographic projection camera.
+ g_viewInfo.drawContext.projection = g_math.matrix4.orthographic(
+ 0 + 0.5,
+ g_client.width + 0.5,
+ g_client.height + 0.5,
+ 0 + 0.5,
+ 0.001,
+ 1000);
+
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [0, 0, 1], // eye
+ [0, 0, 0], // target
+ [0, 1, 0]); // up
+
+ // Create the global paint object that's used by multiple draw operations.
+ g_paint = g_pack.createObject('CanvasPaint');
+
+ // Creates an instance of the canvas utilities library.
+ g_canvasLib = o3djs.canvas.create(g_pack, g_client.root, g_viewInfo);
+
+ // Create the quads that populate the scene.
+ createSceneContents();
+
+ g_client.setRenderCallback(updateCounter);
+}
+
+/**
+ * Uses a linear gradient that transitions between 5 colors to
+ * draw a rectangle of the specified width and height inside the
+ * given canvasQuad object.
+ */
+function drawStripedRectangle(canvasQuad, width, height) {
+ // Initialize the array of colors and relative positions that will be used
+ // by the gradient.
+ var colors = [[1, 0, 0, 1],
+ [0, 1, 0, 1],
+ [0, 0, 1, 1],
+ [1, 0, 1, 1],
+ [0, 1, 1, 1]];
+ var numSubdivisions = colors.length;
+ var rectWidth = 1 / numSubdivisions;
+ var paint = g_paint;
+ var positions = [];
+ for (var ii = 0; ii < numSubdivisions; ii++)
+ positions[ii] = ii / (numSubdivisions-1);
+
+ // Create a gradient object that will use the entire width of the rectangle.
+ var gradientShader = g_pack.createObject('CanvasLinearGradient');
+ gradientShader.startPoint = [0, 0];
+ gradientShader.endPoint = [width, 0];
+ gradientShader.colors = colors;
+ gradientShader.positions = positions;
+ paint.shader = gradientShader;
+
+ // Draw the rectangle starting from the upper left corner of the canvas quad.
+ canvasQuad.canvas.drawRect(0, 0, width, height, paint);
+
+ // We don't need the shader anymore so we can delete it.
+ g_pack.removeObject(gradientShader);
+
+ canvasQuad.updateTexture();
+}
+
+// Updates the counter canvas with the new counter value.
+function updateCounter() {
+ var counter = Math.floor(g_clock);
+ g_clock += g_timeMult;
+ if (g_clock > 99) {
+ g_clock = 0;
+ }
+
+ // Clear to completely transparent.
+ g_counterCanvas.canvas.clear([0, 0, 0, 0]);
+
+ // Reuse the global paint object
+ var paint = g_paint;
+ paint.color = [1, 1, 1, 1];
+ paint.textSize = 60;
+ paint.textTypeface = 'Comic Sans MS';
+ paint.textAlign = g_o3d.CanvasPaint.RIGHT;
+ paint.shader = null;
+ g_counterCanvas.canvas.drawText(counter.toString(), 100, 70, paint);
+
+ g_counterCanvas.updateTexture();
+}
+/**
+ * Creates the individual quads that demonstrate different features of the
+ * Canvas API.
+ */
+function createSceneContents() {
+ // Creates a Canvas Quad and position it with z = -2 so that it is drawn
+ // behind the rest of the elements. Draw a rectangle with a linear gradient
+ // going through 5 colors along its width.
+ var stripeQuad = g_canvasLib.createXYQuad(50, 50, -2, 700, 500, false);
+ drawStripedRectangle(stripeQuad, 700, 500);
+
+ // Create a 600 x 400 canvas that can host transparent content.
+ var frontCanvas = g_canvasLib.createXYQuad(100, 100, -1, 600, 400, true);
+
+ // Clear the canvas with semi-transparent yellow.
+ frontCanvas.canvas.clear([1, 1, 0, 0.6]);
+
+ // Create a couple of paint objects that will be reused throughout the
+ // code. Note that a snapshot of the settings of the paint object is taken
+ // at the time the paint is being used for one of the canvas methods.
+ // Subsequent changes to the paint object don't affect already drawn elements.
+ var paint = g_pack.createObject('CanvasPaint');
+ var backgroundPaint = g_pack.createObject('CanvasPaint');
+
+ // Create a linear gradient shader that goes diagonally from white to red
+ // (and repeats). Use the shader to fill a rectangle.
+ var gradientShader = g_pack.createObject('CanvasLinearGradient');
+ var colors = [[1, 0, 0, 1], [1, 1, 1, 1]];
+ var positions = [0, 1];
+ gradientShader.startPoint = [0, 0];
+ gradientShader.endPoint = [100, 100];
+ gradientShader.colors = colors;
+ gradientShader.positions = positions;
+ gradientShader.tileMode = g_o3d.CanvasShader.REPEAT;
+ backgroundPaint.shader = gradientShader;
+ frontCanvas.canvas.drawRect(0, 100, 300, 400, backgroundPaint);
+
+ // Modify the previously created gradient shader to go from blue to red
+ // vertically and use the shader to draw another rectangle.
+ var colors = [[0, 0, 1, 1], [1, 0, 0, 1]];
+ var positions = [0, 1];
+ gradientShader.startPoint = [0, 0];
+ gradientShader.endPoint = [0, 401];
+ gradientShader.colors = colors;
+ gradientShader.positions = positions;
+ gradientShader.tileMode = g_o3d.CanvasShader.REPEAT;
+ backgroundPaint.shader = gradientShader;
+ frontCanvas.canvas.drawRect(300, 100, 600, 400, backgroundPaint);
+
+ // Simple centered text writing, with a shadow.
+ paint.color = [1, 1, 1, 1];
+ paint.textAlign = g_o3d.CanvasPaint.CENTER;
+ paint.textSize = 40;
+ paint.setShadow(3, 3, 3, [0, 0, 0, 1]);
+ frontCanvas.canvas.drawText('Hello O3D', 300, 60, paint);
+
+ // Clear the shadow.
+ paint.setShadow(0, 0, 0, [1, 0, 0, 1]);
+
+ // Draw text along a circular path defined as a series of (x, y) points.
+ var numPathPoints = 30;
+ var path = [];
+ var circle = { x: 150, y: 250, r: 70 };
+ for (var ii = 0; ii < numPathPoints; ii++) {
+ var xx = circle.x + circle.r * Math.cos(2.0 * 3.1415 * ii / numPathPoints);
+ var yy = circle.y + circle.r * Math.sin(2.0 * 3.1415 * ii / numPathPoints);
+ path[ii] = [xx, yy];
+ }
+ paint.textSize = 64;
+ paint.textTypeface = 'Comic Sans MS';
+ paint.textAlign = g_o3d.CanvasPaint.LEFT;
+
+ frontCanvas.canvas.drawTextOnPath('Hello O3D',
+ path,
+ 0,
+ 0,
+ paint);
+
+ paint.textTypeface = 'arial';
+ paint.textSize = 18;
+ var metrics = paint.fontMetrics;
+ var ascent = metrics.ascent;
+ var stringDimensions = paint.measureText('Hello O3D');
+
+ paint.textStyle = g_o3d.CanvasPaint.BOLD;
+ paint.textAlign = g_o3d.CanvasPaint.LEFT;
+
+ // Use the canvas transformation methods to draw text in 4 different
+ // orientations. Notice how we offset the text vertically by -ascent/2
+ // in order to center it vertically along the drawing line.
+ var textBoxPaint = g_pack.createObject('CanvasPaint');
+ var colors = [[1, 0, 0, 1], // red
+ [0, 1, 0, 1], // green
+ [0, 0, 1, 1], // blue
+ [1, 1, 0, 1]] // yellow
+
+ for (var ii = 0; ii < 4; ii++) {
+ // Save the existing drawing transform so that we can get it back when we're
+ // done rendering the text line.
+ frontCanvas.canvas.saveMatrix();
+
+ // First rotate by the desired angle and then translate to the desired
+ // center point. Notice that drawing transforms are pre-multiplied which
+ // means that we first need to apply the translate and then the rotate to
+ // get the rotation to happen before the translation.
+ frontCanvas.canvas.translate(450, 250);
+ frontCanvas.canvas.rotate(ii * 90);
+
+ textBoxPaint.color = colors[ii];
+
+ // Draw a rectangle under the text.
+ frontCanvas.canvas.drawRect(10,
+ -ascent / 2 + 2,
+ stringDimensions[2] - stringDimensions[0] + 10,
+ -ascent / 2 + 12,
+ textBoxPaint);
+
+ paint.color = colors[ii];
+
+ // Draw the actual text with a vertical offset which will center it
+ // along the text drawing line.
+ frontCanvas.canvas.drawText('Hello O3D', 10, -ascent / 2, paint);
+
+ // Restore the original drawing transform.
+ frontCanvas.canvas.restoreMatrix();
+ }
+
+ // Copy the contents of the canvas to the o3d texture.
+ frontCanvas.updateTexture();
+
+ // Create a canvas that will be used to display the counter.
+ g_counterCanvas = g_canvasLib.createXYQuad(0, 0, 0, 200, 150, true);
+
+ // Display the counter.
+ updateCounter();
+
+ g_finished = true;
+}
+
+</script>
+</head>
+
+<body>
+<h1>O3D Canvas Sample</h1>
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 800px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
+
diff --git a/o3d/samples/checkers.html b/o3d/samples/checkers.html
new file mode 100644
index 0000000..60d0d263
--- /dev/null
+++ b/o3d/samples/checkers.html
@@ -0,0 +1,895 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+Checker Game Example
+
+This sample demonstates usage of primitive functions and simple 3d animation techniques.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+3D Checkers Game
+</title>
+<script type="text/javascript" src="o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript" id="o3d">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.effect');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.arcball');
+o3djs.require('o3djs.picking');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.quaternions');
+
+// global variables
+var g_o3dElement;
+var g_client;
+var g_o3d;
+var g_math;
+var g_quaternions;
+var g_pack;
+var g_viewInfo;
+var g_sceneRoot;
+var g_eyeView;
+var g_cubeShape;
+var g_cylinderShape;
+var g_prismShape;
+var g_material;
+var g_aball;
+var g_thisRot;
+var g_lastRot;
+var g_zoomFactor;
+var g_dragging = false;
+var g_treeInfo; // information about the transform graph.
+var g_statusInfoElem;
+
+// Animation globals.
+var g_flashTimer;
+var g_moveTimer;
+var g_moveDuration;
+var g_oldFlashTimer;
+
+// Checkers globals.
+var g_board;
+var g_boardSize;
+var g_boardSquare;
+var g_boardHeight;
+var g_pieceHeight;
+var g_selectedPiece;
+var g_selectedSquare;
+var g_player;
+var g_canJump;
+
+/**
+ * Creates the client area.
+ */
+function initClient() {
+ window.g_finished = false; // for selenium testing.
+
+ // Runs the sample in V8. Comment out this line to run it in the browser
+ // JavaScript engine, for example if you want to debug it.
+ o3djs.util.setMainEngine(o3djs.util.Engine.V8);
+
+ o3djs.util.makeClients(main);
+}
+
+/**
+ * Initializes global variables, positions camera, draws the 3D chart.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function main(clientElements) {
+ // Init global variables.
+ initGlobals(clientElements);
+
+ // Set up the view and projection transformations.
+ initContext();
+
+ // Add the checkers board to the transform hierarchy.
+ createCheckersBoard();
+
+ // Register mouse events handlers
+ o3djs.event.addEventListener(g_o3dElement, 'mousedown', startDragging);
+ o3djs.event.addEventListener(g_o3dElement, 'mousemove', drag);
+ o3djs.event.addEventListener(g_o3dElement, 'mouseup', stopDragging);
+ o3djs.event.addEventListener(g_o3dElement, 'wheel', scrollMe);
+
+ // Set the rendering callback
+ g_client.setRenderCallback(onrender);
+
+ window.g_finished = true; // for selenium testing.
+}
+
+/**
+ * Initializes global variables and libraries.
+ */
+function initGlobals(clientElements) {
+ // init o3d globals.
+ g_o3dElement = clientElements[0];
+ window.g_client = g_client = g_o3dElement.client;
+ g_o3d = g_o3dElement.o3d;
+ g_math = o3djs.math;
+ g_quaternions = o3djs.quaternions;
+
+ // Create an arcball.
+ g_aball = o3djs.arcball.create(g_client.width, g_client.height);
+
+ // Create a pack to manage the objects created.
+ g_pack = g_client.createPack();
+
+ // Create a transform node to act as the 'root' of the scene.
+ // Attach it to the root of the transform graph.
+ g_sceneRoot = g_pack.createObject('Transform');
+ g_sceneRoot.parent = g_client.root;
+
+ // Create the render graph for the view.
+ var clearColor = [.98, .98, .98, 1];
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot,
+ clearColor);
+
+ // Create a material for the objects rendered.
+ g_material = createPhongMaterial([1, 1, 1, 1]);
+
+ // Initialize checkers piece and square data.
+ g_boardSize = 8;
+ g_boardSquare = 10;
+ g_boardHeight = g_boardSquare / 5;
+ g_pieceHeight = g_boardHeight * 0.75;
+ g_selectedPiece = null;
+ g_selectedSquare = null;
+
+ // Create a cube shape for the board squares.
+ g_cubeShape = o3djs.primitives.createCube(
+ g_pack,
+ g_material,
+ 1);
+
+ // Create a cylinder shape for the checkers pieces.
+ g_cylinderShape = o3djs.primitives.createCylinder(
+ g_pack,
+ g_material,
+ g_boardSquare / 2 - 1, // Radius.
+ g_pieceHeight, // Depth.
+ 100, // Number of subdivisions.
+ 1);
+
+ // use an extruded poligon to create a 'crown' for the king piece.
+ var polygon = [[0, 0], [1, 0], [1.5, 1.5], [0.5, 0.5],
+ [0, 2], [-0.5, 0.5], [-1.5, 1.5], [-1, 0]];
+
+ // use the 'prism' primitive for the crown.
+ g_prismShape = o3djs.primitives.createPrism(
+ g_pack,
+ g_material,
+ polygon, // The profile polygon to be extruded.
+ 1); // The depth of the extrusion.
+
+ // Get the status element.
+ g_statusInfoElem = o3djs.util.getElementById('statusInfo');
+
+ // Initialize player data.
+ g_player = 1; // red player starts first.
+ g_canJump = false;
+
+ // Initialize various animation globals.
+ g_flashTimer = 0;
+ g_moveTimer = 0;
+ g_moveDuration = 1.3;
+ g_oldFlashTimer = 0;
+}
+
+/**
+ * Initialize the original view of the scene.
+ */
+function initContext() {
+ g_eyeView = [-5, 120, 100];
+ g_zoomFactor = 1.03;
+ g_dragging = false;
+ g_sceneRoot.identity();
+ g_lastRot = g_math.matrix4.identity();
+ g_thisRot = g_math.matrix4.identity();
+
+ // Set up a perspective transformation for the projection.
+ g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ Math.PI * 40 / 180, // 30 degree frustum.
+ g_o3dElement.clientWidth / g_o3dElement.clientHeight, // Aspect ratio.
+ 1, // Near plane.
+ 10000); // Far plane.
+
+ // Set up our view transformation to look towards the axes origin.
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ g_eyeView, // eye
+ [0, 0, 0], // target
+ [0, 1, 0]); // up
+}
+
+/**
+ * Creates a phong material based on the given single color.
+ * @param {!o3djs.math.Vector4} baseColor An array with 4 entries,
+ * the R,G,B, and A components of a color.
+ * @return {!o3d.Material} A phong material whose overall pigment
+ * is baseColor.
+ */
+function createPhongMaterial(baseColor) {
+ // Create a new, empty Material object.
+ var material = g_pack.createObject('Material');
+
+ var lightPosition = [1000, 2000, 3000];
+ o3djs.effect.attachStandardShader(
+ g_pack, material, lightPosition, 'phong');
+
+ material.drawList = g_viewInfo.performanceDrawList;
+
+ // Assign parameters to the phong material.
+ material.getParam('emissive').value = [0, 0, 0, 1];
+ material.getParam('ambient').value =
+ [.1 * baseColor[0], .1 * baseColor[1], .1 * baseColor[2], 1];
+ material.getParam('diffuse').value =
+ [.9 * baseColor[0], .9 * baseColor[1], .9 * baseColor[2], 1];
+ material.getParam('specular').value = [.5, .5, .5, 1];
+ material.getParam('shininess').value = 50;
+ material.getParam('lightColor').value = [1, 1, 1, 1];
+
+ return material;
+}
+
+/**
+ * Creates a Coord object used to hold a set of 2D coordinates.
+ *
+ * @private
+ * @constructor
+ * @param {number} x X coordinate.
+ * @param {number} y Y coordinate.
+ */
+function Coord(x, y) {
+ this.x = x;
+ this.y = y;
+}
+
+/**
+ * Creates a BoardInfo object to hold board information.
+ *
+ * @private
+ * @constructor
+ * @param {number} x X coordinate on the checkers board.
+ * @param {number} y Y coordinate on the checkers board.
+ */
+function BoardInfo(x, y) {
+ this.x = x;
+ this.y = y;
+ this.square = null;
+ this.type = 0;
+ this.pieceParent = null;
+ this.piece = null;
+ this.king = false;
+}
+
+/**
+ * Returns the initial settings for a given position on the board.
+ *
+ * @param {number} x X coordinate on the checkers board.
+ * @param {number} y Y coordinate on the checkers board.
+ * @return {number} 0 = no piece, 1 = red piece, -1 = white piece
+ */
+function getSquarePiece(x, y) {
+ // if bad coordinates, no piece.
+ if (x >= g_boardSize || x < 0 || y >= g_boardSize || y < 0)
+ return 0;
+ // if on rows 3 and 4 or on a white square - no piece.
+ if (y == 3 || y == 4 || ((x + y) % 2 == 1))
+ return 0;
+ // if on rows 0-2 it is a red piece, otherwise white piece.
+ if (0 <= y && y < 3) return 1;
+ if (5 <= y && y < 8) return -1;
+ // Any not covered case.
+ return 0;
+}
+
+/**
+ * Creates the checkers board.
+ */
+function createCheckersBoard() {
+ g_board = [];
+ // Create a checker board (black at 0,0).
+ for (var i = 0; i < g_boardSize; i += 1) { // columns.
+ g_board[i] = [];
+ for (var j = 0; j < g_boardSize; j += 1) { // rows.
+ // create the transform for the board squares.
+ var square = g_pack.createObject('Transform');
+ square.parent = g_sceneRoot;
+ square.addShape(g_cubeShape);
+ // translate and scale the squares correctly relative to origin.
+ var offset = g_boardSquare * (1 - g_boardSize) / 2;
+ square.translate(offset + g_boardSquare * i ,
+ 0, -(offset + g_boardSquare * j));
+ square.scale(g_boardSquare, g_boardHeight, g_boardSquare);
+ // set the square color.
+ var isBlack = (i + j) % 2 == 0 ? true : false;
+ var squareColor = isBlack ?
+ [0.15, 0.15, 0.15, 1] : [0.85, 0.85, 0.75, 1];
+ square.createParam('diffuse', 'ParamFloat4').value = squareColor;
+
+ // add this square and its info to the board.
+ g_board[i][j] = new BoardInfo(i, j);
+ g_board[i][j].square = square;
+
+ // create the piece for this square and update the board position.
+ // skip if the square has no piece.
+ var pieceType = getSquarePiece(i, j);
+ if (pieceType == 0)
+ continue;
+
+ // create a parent transform for this piece.
+ var pieceParent = g_pack.createObject('Transform');
+ pieceParent.parent = g_sceneRoot;
+
+ // create the checkers piece.
+ var piece = g_pack.createObject('Transform');
+ piece.parent = pieceParent;
+ piece.addShape(g_cylinderShape);
+
+ // place the piece on the correct location on the board.
+ pieceParent.translate(0, (g_pieceHeight + g_boardHeight) / 2, 0);
+ pieceParent.translate(offset + g_boardSquare * i ,
+ 0, -(offset + g_boardSquare * j));
+
+ // pick the piece color (red or white).
+ piece.createParam('diffuse', 'ParamFloat4').value =
+ getPieceColor(pieceType);
+
+ // update the board info to include this piece.
+ g_board[i][j].piece = piece;
+ g_board[i][j].pieceParent = pieceParent;
+ g_board[i][j].type = pieceType;
+ }
+ }
+
+ // Update our tree info.
+ updateTreeInfo();
+
+ // update status.
+ updateStatus('Game starting... RED moves first.', true);
+}
+
+/**
+ * Checks if a piece has become a 'king' piece and updates it.
+ *
+ * @param {number} x X coordinate on the checkers board.
+ * @param {number} y Y coordinate on the checkers board.
+ */
+function checkAndUpdateKing(x, y) {
+ // if the piece is not on the king row, nothing to do.
+ if ( y > 0 && y < g_boardSize - 1 ) return;
+
+ // ignore if no piece or the piece is already king
+ var selSquare = g_board[x][y];
+ if (!selSquare.piece || selSquare.king) return;
+
+ // change the king piece color.
+ selSquare.king = true;
+
+ // create the crown shape.
+ var crown = g_pack.createObject('Transform');
+ crown.parent = selSquare.piece;
+ crown.addShape(g_prismShape);
+ var crownSize = g_pieceHeight;
+ crown.scale(crownSize, crownSize, crownSize);
+ crown.translate(0, g_pieceHeight / 2, 0);
+ crown.createParam('diffuse', 'ParamFloat4').value = [1, 1, 0, 0];
+}
+
+
+/**
+ * Updates the transform tree info.
+ */
+function updateTreeInfo() {
+ if (!g_treeInfo) {
+ g_treeInfo = o3djs.picking.createTransformInfo(g_client.root, null);
+ }
+ g_treeInfo.update();
+}
+
+/**
+ * Check if a player selection is a jump.
+ *
+ * @param {BoardInfo} piece The board info for the piece.
+ * @param {BoardInfo} square The board info for the landing square.
+ * @return {boolean} true if this is a jump.
+ */
+function isJump(piece, square) {
+ return (Math.abs(piece.x - square.x) == 2 &&
+ Math.abs(piece.y - square.y) == 2 );
+}
+
+function isLegalMove(piece, square) {
+ var orig = new Coord(piece.x, piece.y);
+ var dest = new Coord(square.x, square.y);
+
+ // it must be this player's turn to make a move.
+ if (piece.type != g_player) return false;
+
+ // the destination must be un-occupied.
+ if (square.type != 0 ) return false;
+
+ // must move diagonally.
+ var diag = new Coord(dest.x - orig.x, dest.y - orig.y);
+ if (Math.abs(diag.x) != Math.abs(diag.y)) return false;
+
+ // cannot move more than 2.
+ if (Math.abs(diag.x) > 2) return false;
+
+ // make sure the piece is moved in the 'forward' direction
+ // unless this is a 'king' piece.
+ if (!piece.king && g_player * diag.y < 0) return false;
+
+ // if a jump check if valid.
+ if (Math.abs(diag.x) == 2) {
+ var jumpType = g_board[(orig.x + dest.x)/2][(orig.y + dest.y)/2].type;
+ if (jumpType == piece.type || jumpType == 0)
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Update the status message.
+ *
+ * @param {string} statusMsg The message to display.
+ * @param {boolean} opt_hidePlayer Prefix the status with the name of the player.
+ */
+function updateStatus(statusMsg, opt_hidePlayer) {
+ var status = (g_player == 1) ? 'RED: ' : 'WHITE: ';
+ g_statusInfoElem.innerHTML = (opt_hidePlayer ? '' : status) + statusMsg;
+}
+
+
+/**
+ * Check if a valid boad coordinate.
+ *
+ * @param {number} x X coordinate on the checkers board.
+ * @param {number} y Y coordinate on the checkers board.
+ * @return {boolean} True if a valid coordinate.
+ */
+function isValidCoord(x, y) {
+ if ( x < 0 || x >= g_boardSize ) return false;
+ if ( y < 0 || y >= g_boardSize ) return false;
+ return true;
+}
+
+
+/**
+ * Check if a piece has a valid sliding move.
+ *
+ * @param {!BoardInfo} piece The board info for the piece.
+ * @return {boolean} True if the piece can slide.
+ */
+function pieceCanSlide(piece) {
+ var x = piece.x;
+ var y = piece.y;
+
+ for (var i = -1; i <= 1; i += 2) {
+ for (var j = -1; j <= 1; j += 2) {
+ if (isValidCoord(x + i, y + j))
+ if (isLegalMove(piece, g_board[x + i][y + j]))
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Check if a piece has a valid jumping move.
+ *
+ * @param {!BoardInfo} piece The board info for the piece.
+ * @return {boolean} True if the piece can jump.
+ */
+function pieceCanJump(piece) {
+ var x = piece.x;
+ var y = piece.y;
+
+ for (var i = -2; i <= 2; i += 4) {
+ for (var j = -2; j <= 2; j += 4) {
+ if (isValidCoord(x + i, y + j))
+ if (isLegalMove(piece, g_board[x + i][y + j]))
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Check if the current player has any moves available.
+ *
+ * @return {boolean} True if the player can move.
+ */
+function currentPlayerCanMove() {
+ var canSlide = false;
+ for (var x = 0; x < g_boardSize; x += 1) {
+ for (var y = 0; y < g_boardSize; y += 1) {
+ var sel = g_board[x][y];
+ if (sel.piece == null) continue;
+ // if jump set the canJump variable and return.
+ g_canJump = pieceCanJump(sel);
+ if (g_canJump) return true;
+ if (pieceCanSlide(sel)) {
+ canSlide = true;
+ }
+ }
+ }
+ return canSlide;
+}
+
+/**
+ * Check if a forced jump is required for the current player.
+ *
+ * @return {boolean} True if a jump is required.
+ */
+function checkForcedJump() {
+ for (var x = 0; x < g_boardSize; x += 1) {
+ for (var y = 0; y < g_boardSize; y += 1) {
+ var sel = g_board[x][y];
+ if (sel.piece && pieceCanJump(sel)) {
+ g_forcedJump = true;
+ return true;
+ }
+ }
+ }
+ g_forcedJump = false;
+ return false;
+}
+
+/**
+ * Check if the game is over.
+ *
+ * @return {boolean} True if the game is over.
+ */
+function isGameOver() {
+ // the game is over when a player has no pieces left
+ // or it cannot make a valid move.
+ if (!currentPlayerCanMove()) {
+ var statusStr = (g_player == 1) ? 'White Won!' : 'Red Won!';
+ updateStatus(statusStr, true);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Detect a mouse click an element of the checkers board.
+ *
+ * @param {event} e event.
+ */
+function detectSelection(e) {
+ var worldRay = o3djs.picking.clientPositionToWorldRay(e.x,
+ e.y,
+ g_viewInfo.drawContext,
+ g_client.width,
+ g_client.height);
+
+ // check if we picked any objects.
+ var pickInfo = g_treeInfo.pick(worldRay);
+ if (pickInfo) {
+ // get the parent transform of this object.
+ var pickTrans = pickInfo.shapeInfo.parent.transform;
+
+ // check if a board square or a piece.
+ for (var x = 0; x < g_boardSize; x += 1) {
+ for (var y = 0; y < g_boardSize; y += 1) {
+ if (pickTrans === g_board[x][y].piece) {
+ // do not select another player's piece.
+ if (g_player != g_board[x][y].type) return;
+
+ // if a previous piece selection, clear it.
+ if (g_selectedPiece) {
+ g_selectedPiece.piece.getParam('diffuse').value =
+ getPieceColor(g_selectedPiece.type);
+ }
+
+ // check if a forced jump.
+ if (g_canJump) {
+ if (!pieceCanJump(g_board[x][y])) {
+ updateStatus('Must jump, incorrect piece!');
+ return;
+ }
+ }
+
+ SelectPiece(x, y);
+ return;
+ } else if (pickTrans === g_board[x][y].square) {
+ // selected the landing square if a piece move is pending.
+ if (g_selectedPiece) {
+ // check if a forced jump.
+ if (g_canJump && !isJump(g_selectedPiece, g_board[x][y])) {
+ updateStatus('Must jump!');
+ return;
+ }
+ // check if a legal move, then move the piece.
+ if (isLegalMove(g_selectedPiece, g_board[x][y])) {
+ SelectSquare(x, y);
+ } else {
+ updateStatus('Illegal move!');
+ }
+ }
+ return;
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Select a piece on the checkers board.
+ */
+function SelectPiece(x, y) {
+ g_selectedPiece = g_board[x][y];
+ // update status message.
+ updateStatus('Selected piece at (' + x + ',' + y + ')');
+}
+
+/**
+ * Select a square on the checkers board.
+ */
+function SelectSquare(x, y) {
+ updateStatus('Moving piece from (' + g_selectedPiece.x + ',' +
+ g_selectedPiece.y + ') to (' + x + ',' + y + ')');
+ g_selectedSquare = g_board[x][y];
+ g_moveTimer = 0;
+}
+
+/**
+ * Return the color for the given piece type.
+ *
+ * @param {number} type Type of the checkers piece.
+ * @return {Array} Array representing the color.
+ */
+function getPieceColor(type) {
+ return (type == 1) ? [1, 0.15, 0.15, 1] : [1, 1, 1, 1];
+}
+
+/**
+ * Called every frame.
+ * @param {o3d.RenderEvent} renderEvent Rendering Information.
+ */
+function onrender(renderEvent) {
+ g_flashTimer += renderEvent.elapsedTime;
+ g_flashTimer = g_flashTimer % 0.5;
+
+ if (g_selectedPiece) {
+ var origColor = getPieceColor(g_selectedPiece.type);
+ // flash highlight the selected piece as long as selected.
+ if (g_oldFlashTimer > g_flashTimer ) {
+ g_selectedPiece.piece.getParam('diffuse').value = [0.6, 1, 1, 1];
+ } else if (g_flashTimer >= 0.25 && g_oldFlashTimer < 0.25) {
+ g_selectedPiece.piece.getParam('diffuse').value = origColor;
+ }
+
+ // check if we selected a square to move the piece.
+ if (g_selectedSquare) {
+ moveSelectedPiece(renderEvent.elapsedTime);
+ }
+ }
+ g_oldFlashTimer = g_flashTimer;
+}
+
+
+/**
+ * Slides or jumps the selected piece.
+ * This method is used to simulate the animation of the moving piece.
+ * @param {number} elapsedTime The elapsed time in seconds since the last call.
+ */
+function moveSelectedPiece(elapsedTime) {
+ g_moveTimer += elapsedTime;
+ // animate the piece one iteration at the time.
+ var lerp = g_moveTimer / g_moveDuration;
+
+ // get the board coordinates of the curent and future position.
+ var x0 = g_selectedPiece.x;
+ var y0 = g_selectedPiece.y;
+ var x1 = g_selectedSquare.x;
+ var y1 = g_selectedSquare.y;
+
+ // get the coordinates relative to axis origin.
+ var offset = g_boardSquare * (1 - g_boardSize) / 2;
+ var xc0 = offset + g_boardSquare * x0;
+ var zc0 = -(offset + g_boardSquare * y0);
+ var xc1 = offset + g_boardSquare * x1;
+ var zc1 = -(offset + g_boardSquare * y1);
+ var yc = (g_pieceHeight + g_boardHeight) / 2;
+
+ // Our piece's position and rotation.
+ var px;
+ var pz;
+ var jump = 0;
+ var rotation = 0;
+ var done = false;
+
+ if (lerp < 1) {
+ // check if this is a jump.
+ if (isJump(g_selectedPiece, g_selectedSquare)) {
+ // compute the jump height.
+ jump = Math.sin(Math.PI * lerp) * g_pieceHeight * 13;
+ // simulate a spinning of the jumping piece.
+ rotation = -lerp * 4 * Math.PI;
+ }
+ px = xc0 + (xc1 - xc0) * lerp;
+ pz = zc0 + (zc1 - zc0) * lerp;
+ } else {
+ // done with the move.
+ px = xc1;
+ pz = zc1;
+ done = true;
+ }
+
+ // move the piece to the new position.
+ var pieceParent = g_board[x0][y0].pieceParent;
+ pieceParent.identity();
+ pieceParent.translate(px, yc + jump, pz);
+
+ // spin the piece
+ g_selectedPiece.piece.identity();
+ g_selectedPiece.piece.rotateX(rotation);
+
+ if (done) {
+ // stop flashing - restore the original color.
+ var origColor = getPieceColor(g_selectedPiece.type);
+ g_selectedPiece.piece.getParam('diffuse').value = origColor;
+
+ // if a jump destroy the jumped piece.
+ var wasJump = isJump(g_selectedPiece, g_selectedSquare);
+ if (wasJump) {
+ var xj = (x0 + x1)/2;
+ var yj = (y0 + y1)/2;
+ g_board[xj][yj].type = 0;
+ g_board[xj][yj].pieceParent.parent = null;
+ g_board[xj][yj].pieceParent = null;
+ g_board[xj][yj].piece = null;
+ }
+
+ // the new square has a new piece.
+ g_board[x1][y1].type = g_selectedPiece.type;
+ g_board[x1][y1].king = g_selectedPiece.king;
+ g_board[x1][y1].piece = g_selectedPiece.piece;
+ g_board[x1][y1].pieceParent = g_selectedPiece.pieceParent;
+
+ // the original square is now empty.
+ g_board[x0][y0].type = 0;
+ g_board[x0][y0].piece = null;
+ g_board[x0][y0].pieceParent = null;
+
+ // check if the moved piece became king.
+ checkAndUpdateKing(x1, y1);
+
+ g_selectedPiece = null;
+ g_selectedSquare = null;
+ g_moveTimer = 0;
+
+ // update the tree info.
+ updateTreeInfo();
+
+ // check if current player can jump again.
+ if (wasJump && pieceCanJump(g_board[x1][y1])) {
+ g_selectedPiece = g_board[x1][y1];
+ updateStatus('Must jump again ...');
+ } else {
+ // this player is done, switch players.
+ g_player = -g_player;
+ // check if game over; also checks if the current player must jump.
+ if (!isGameOver()) {
+ updateStatus(g_canJump ? 'Must jump...' : 'Next turn...');
+ }
+ }
+ }
+}
+
+
+/**
+ * Zooms the scene in / out by changing the viewpoint.
+ * @param {number} zoom zooming factor.
+ */
+function ZoomInOut(zoom) {
+ for (i = 0; i < g_eyeView.length; i += 1) {
+ g_eyeView[i] = g_eyeView[i] / zoom;
+ }
+
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ g_eyeView, // eye.
+ [0, 0, 0], // target.
+ [0, 1, 0]); // up.
+}
+
+/**
+ * Start mouse dragging.
+ * @param {event} e event.
+ */
+function startDragging(e) {
+ detectSelection(e);
+ g_lastRot = g_thisRot;
+
+ g_aball.click([e.x, e.y]);
+ g_dragging = true;
+}
+
+/**
+ * Use the arcball to rotate the scene.
+ * Computes the rotation matrix.
+ * @param {event} e event.
+ */
+function drag(e) {
+ if (g_dragging) {
+ var rotationQuat = g_aball.drag([e.x, e.y]);
+ var rot_mat = g_quaternions.quaternionToRotation(rotationQuat);
+
+ g_thisRot = g_math.matrix4.mul(g_lastRot, rot_mat);
+ var m = g_sceneRoot.localMatrix;
+ g_math.matrix4.setUpper3x3(m, g_thisRot);
+ g_sceneRoot.localMatrix = m;
+ }
+}
+
+/**
+ * Stop dragging.
+ * @param {event} e event.
+ */
+function stopDragging(e) {
+ g_dragging = false;
+}
+
+/**
+ * Using the mouse wheel zoom in and out of the scene.
+ * @param {event} e event.
+ */
+function scrollMe(e) {
+ var zoom = (e.deltaY < 0) ? 1 / g_zoomFactor : g_zoomFactor;
+ ZoomInOut(zoom);
+ g_client.render();
+}
+
+</script>
+</head>
+
+<body onload="initClient()">
+<h2>3D Checkers Game</h2>
+<div style="font-size:10;"><span id="statusInfo"></span></div>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 600px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+
+</body>
+</html>
diff --git a/o3d/samples/convolution.html b/o3d/samples/convolution.html
new file mode 100644
index 0000000..83fdede
--- /dev/null
+++ b/o3d/samples/convolution.html
@@ -0,0 +1,397 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+This sample shows how to create a separable convolution shader using
+render targets. The kernel here is a Gaussian blur, but the same code
+could be used for any kernel.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+O3D: Convolution Shader Sample
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.camera');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.pack');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.scene');
+
+// Events
+// init() once the page has finished loading.
+window.onload = init;
+window.onunload = uninit;
+
+// constants.
+var RENDER_TARGET_WIDTH = 512;
+var RENDER_TARGET_HEIGHT = 512;
+
+// global variables
+var g_o3d;
+var g_client;
+var g_math;
+var g_pack;
+var g_teapotRoot;
+var g_renderGraphRoot;
+var g_clock = 0;
+var g_timeMult = 1;
+var g_finished = false; // for selenium testing.
+
+/**
+ * Loads a scene into the transform graph.
+ * @param {!o3d.Pack} pack Pack to load scene into.
+ * @param {string} fileName filename of the scene.
+ * @param {!o3d.Transform} parent parent node in the transform graph to
+ * which to load the scene into.
+ * @param {!o3djs.rendergraph.ViewInfo} viewInfo who's view and projection will
+ * be set from the scene after it's loaded.
+ */
+function loadScene(pack, fileName, parent, viewInfo) {
+ // Get our full path to the scene.
+ var scenePath = o3djs.util.getCurrentURI() + fileName;
+
+ // Load the scene given the full path, and call the callback function
+ // when its done loading.
+ o3djs.scene.loadScene(g_client, pack, parent, scenePath, callback);
+
+ /**
+ * Our callback is called once the scene has been loaded into memory from the
+ * web or locally.
+ * @param {!o3d.Pack} pack The pack that was passed in above.
+ * @param {!o3d.Transform} parent The parent that was passed in above.
+ * @param {*} exception null if loading succeeded.
+ */
+ function callback(pack, parent, exception) {
+ if (exception) {
+ alert('Could not load: ' + fileName + '\n' + exception);
+ return;
+ }
+ // Get a CameraInfo (an object with a view and projection matrix)
+ // using our javascript library function
+ var cameraInfo = o3djs.camera.getViewAndProjectionFromCameras(
+ parent,
+ RENDER_TARGET_WIDTH,
+ RENDER_TARGET_HEIGHT);
+
+ // Copy the view and projection to the passed in viewInfo structure..
+ viewInfo.drawContext.view = cameraInfo.view;
+ viewInfo.drawContext.projection = cameraInfo.projection;
+
+ // Generate draw elements and setup material draw lists.
+ o3djs.pack.preparePack(pack, viewInfo);
+
+ g_finished = true; // for selenium testing.
+ }
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and loads the scene into the transform graph.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ var o3d = clientElements[0];
+ g_o3d = o3d.o3d;
+ g_math = o3djs.math;
+ g_client = o3d.client;
+
+ // Creates a pack to manage our resources/assets.
+ g_pack = g_client.createPack();
+
+ // Create the texture required for the color render-target.
+ var texture1 = g_pack.createTexture2D(RENDER_TARGET_WIDTH,
+ RENDER_TARGET_HEIGHT,
+ g_o3d.Texture.XRGB8, 1, true);
+
+ // Create the texture required for the color render-target.
+ var texture2 = g_pack.createTexture2D(RENDER_TARGET_WIDTH,
+ RENDER_TARGET_HEIGHT,
+ g_o3d.Texture.XRGB8, 1, true);
+
+ g_teapotRoot = g_pack.createObject('Transform');
+
+ var renderGraphRoot = g_client.renderGraphRoot;
+
+ var xSigma = 4.0, ySigma = 4.0;
+ var xKernel = buildKernel(xSigma);
+ var yKernel = buildKernel(ySigma);
+
+ var renderSurfaceSet1 = createRenderSurfaceSet(texture1);
+ var renderSurfaceSet2 = createRenderSurfaceSet(texture2);
+
+ // Create the render graph for the teapot view, drawing the teapot into
+ // texture1 (via renderSurfaceSet1).
+ var teapotViewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_teapotRoot,
+ renderSurfaceSet1,
+ [1, 1, 1, 1]);
+
+ // Create a Y convolution pass that convolves texture1 into texture2, using
+ // the X kernel.
+ var renderNode1 = createConvolutionPass(texture1,
+ renderSurfaceSet2,
+ xKernel,
+ 1.0 / texture1.width,
+ 0.0);
+
+ // Create a Y convolution pass that convolves texture2 into the framebuffer,
+ // using the Y kernel.
+ var renderNode2 = createConvolutionPass(texture2,
+ g_client.renderGraphRoot,
+ yKernel,
+ 0.0,
+ 1.0 / texture2.height);
+
+ // Load the scene into the transform graph as a child g_teapotRoot
+ loadScene(g_pack, 'assets/teapot.o3dtgz', g_teapotRoot, teapotViewInfo);
+
+ // Set a render callback.
+ g_client.setRenderCallback(onRender);
+}
+
+// We lop off the sqrt(2 * pi) * sigma term, since we're going to normalize
+// anyway.
+function gauss(x, sigma) {
+ return Math.exp(- (x * x) / (2.0 * sigma * sigma));
+}
+
+function buildKernel(sigma) {
+ var kMaxKernelSize = 25;
+ var kernelSize = 2 * Math.ceil(sigma * 3.0) + 1;
+ if (kernelSize > kMaxKernelSize) {
+ kernelSize = kMaxKernelSize;
+ }
+ var halfWidth = (kernelSize - 1) * 0.5
+ var values = new Array(kernelSize);
+ var sum = 0.0;
+ for (var i = 0; i < kernelSize; ++i) {
+ values[i] = gauss(i - halfWidth, sigma);
+ sum += values[i];
+ }
+ // Now normalize the kernel.
+ for (var i = 0; i < kernelSize; ++i) {
+ values[i] /= sum;
+ }
+ return values;
+}
+
+function createConvolutionMaterial(viewInfo, kernelSize) {
+ var convFXString = document.getElementById('convFX').value;
+ convFXString = convFXString.replace(/KERNEL_WIDTH/g, kernelSize);
+ var convEffect = g_pack.createObject('Effect');
+ convEffect.loadFromFXString(convFXString);
+
+ var convMaterial = g_pack.createObject('Material');
+ convMaterial.drawList = viewInfo.performanceDrawList;
+ convMaterial.effect = convEffect;
+ convEffect.createUniformParameters(convMaterial);
+ return convMaterial;
+}
+
+function createRenderSurfaceSet(texture) {
+ var renderSurface = texture.getRenderSurface(0, g_pack);
+
+ // Create the depth-stencil buffer required when rendering this pass.
+ var depthSurface = g_pack.createDepthStencilSurface(RENDER_TARGET_WIDTH,
+ RENDER_TARGET_HEIGHT);
+
+ var renderSurfaceSet = g_pack.createObject('RenderSurfaceSet');
+ renderSurfaceSet.renderSurface = renderSurface;
+ renderSurfaceSet.renderDepthStencilSurface = depthSurface;
+ renderSurfaceSet.parent = g_client.renderGraphRoot;
+ return renderSurfaceSet;
+}
+
+function createConvolutionPass(srcTexture, renderGraphRoot, kernel, x, y) {
+ // Create a root Transform for the convolution scene.
+ var root = g_pack.createObject('Transform');
+
+ // Create a basic view for the convolution scene.
+ var viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ root,
+ renderGraphRoot,
+ [1, 1, 1, 1]);
+
+ var material = createConvolutionMaterial(viewInfo, kernel.length);
+ var quadShape = o3djs.primitives.createPlane(g_pack,
+ material,
+ 2.0,
+ 2.0,
+ 1,
+ 1);
+
+ // Attach the quad to the root of the convolution graph.
+ root.addShape(quadShape);
+
+ // Rotate the view so we're looking at the XZ plane (where our quad is)
+ // Point the camera along the -Y axis
+ var target = [0, -1, 0];
+ // Put the camera at the origin.
+ var eye = [0, 0, 0];
+ // Define the up-vector as +Z
+ var up = [0, 0, 1];
+ viewInfo.drawContext.view = g_math.matrix4.lookAt(eye, target, up);
+
+ // Create an orthographic projection.
+ viewInfo.drawContext.projection = g_math.matrix4.orthographic(-1, 1, -1, 1, -1, 1);
+
+ // Generate draw elements and setup material draw lists for the
+ // convolution scene.
+ o3djs.pack.preparePack(g_pack, viewInfo);
+
+ setConvolutionParameters(material, srcTexture, kernel, kernel.length, x, y);
+ return renderGraphRoot;
+}
+
+function setConvolutionParameters(material, texture, kernel, kernelSize,
+ xIncrement, yIncrement) {
+ var imageParam = material.getParam('image');
+ var kernelParam = material.getParam('kernel');
+ var imageIncrement = material.getParam('imageIncrement');
+ var sampler = g_pack.createObject('Sampler');
+ sampler.texture = texture;
+ sampler.addressModeU = g_o3d.Sampler.CLAMP;
+ sampler.addressModeV = g_o3d.Sampler.CLAMP;
+ sampler.minFilter = g_o3d.Sampler.POINT;
+ sampler.magFilter = g_o3d.Sampler.POINT;
+ sampler.mipFilter = g_o3d.Sampler.NONE;
+ imageParam.value = sampler;
+ imageIncrement.value = [xIncrement, yIncrement];
+ var paramArray = g_pack.createObject('ParamArray');
+ var halfWidth = (kernelSize - 1) * 0.5;
+ for (var i = 0; i < kernelSize; ++i) {
+ var element = paramArray.createParam(i, 'ParamFloat');
+ element.value = kernel[i];
+ }
+ kernelParam.value = paramArray;
+}
+
+/**
+ * Called every frame.
+ * @param {o3d.RenderEvent} renderEvent Rendering Information.
+ */
+function onRender(renderEvent) {
+ var elapsedTime = renderEvent.elapsedTime;
+ g_clock += elapsedTime * g_timeMult;
+
+ g_teapotRoot.identity();
+ g_teapotRoot.rotateX(g_clock);
+ g_teapotRoot.rotateY(g_clock * 1.3);
+}
+
+/**
+ * Cleanup before exiting.
+ */
+function uninit() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+</script>
+</head>
+<body>
+<h1>Convolution Shader Example</h1>
+<p>This sample shows how to do 2D image processing using render targets. This
+sample uses a convolution shader to do a 2D Gaussian blur, but the
+same code could be used for any separable convolution kernel.</p>
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 512px; height: 512px;"></div>
+<!-- End of O3D plugin -->
+<!--
+ We embed the code for our effect inside this hidden textarea.
+ Effects contain the functions that define
+ the vertex and pixel shaders used by our shape.
+-->
+<!-- Don't render the textarea -->
+<div style="display:none">
+<textarea id="convFX" name="convFX" cols="80" rows="20">
+float4x4 worldViewProjection : WorldViewProjection;
+sampler2D image;
+float kernel[KERNEL_WIDTH];
+float2 imageIncrement;
+
+struct VertexShaderInput {
+ float4 position : POSITION;
+ float2 imageCoord : TEXCOORD0;
+};
+
+struct PixelShaderInput {
+ float4 position : POSITION;
+ float2 imageCoord : TEXCOORD0;
+};
+
+PixelShaderInput ConvolutionVS(VertexShaderInput input) {
+ PixelShaderInput output;
+ output.position = mul(input.position, worldViewProjection);
+
+ // Offset image coords by half of kernel width, in image texels
+ output.imageCoord = input.imageCoord -
+ ((KERNEL_WIDTH - 1) / 2) * imageIncrement;
+
+ return output;
+}
+
+float4 ConvolutionPS(PixelShaderInput input) : COLOR {
+ float2 imageCoord = input.imageCoord;
+ float4 sum = float4(0.0, 0.0, 0.0, 0.0);
+ for (int i = 0; i < KERNEL_WIDTH; ++i) {
+ sum += tex2D(image, imageCoord) * kernel[i];
+ imageCoord += imageIncrement;
+ }
+ return sum;
+}
+
+// #o3d VertexShaderEntryPoint ConvolutionVS
+// #o3d PixelShaderEntryPoint ConvolutionPS
+// #o3d MatrixLoadOrder RowMajor
+</textarea>
+</div>
+</body>
+
+</html>
diff --git a/o3d/samples/culling.html b/o3d/samples/culling.html
new file mode 100644
index 0000000..f1d3566
--- /dev/null
+++ b/o3d/samples/culling.html
@@ -0,0 +1,340 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+O3D Culling.
+
+Make sure things off screen get culled.
+
+By default nothing is culled. If you want object to be culled you must setup
+bounding boxes in the transform graph that fit the needs of your application
+and then turn on culling for those objects you want culled.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Culling.
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.effect');
+
+// global variables
+var g_timeMult = 1.0;
+var g_framesRendered = 0;
+var g_o3d;
+var g_math;
+var g_client;
+var g_viewInfo;
+var g_pack;
+var g_totalTransformsElement;
+var g_transformsProcessedElement;
+var g_transformsCulledElement;
+var g_totalDrawElementsElement;
+var g_totalDrawableThingsElement;
+var g_drawElementsProcessedElement;
+var g_drawElementsCulledElement;
+var g_drawElementsRenderedElement;
+var g_primitivesRenderedElement;
+var g_groupTransforms = [];
+var GROUPS_ACROSS = 2;
+var UNITS_ACROSS_GROUP = 2;
+var TOTAL_ACROSS = GROUPS_ACROSS * UNITS_ACROSS_GROUP;
+var HALF_WIDTH = TOTAL_ACROSS * 0.0;
+var UNIT_SPACING = 200;
+
+function createInstances(pack, shape) {
+ // Make a grid of transforms and put a shape instance on each one.
+ for (var g = 0; g < GROUPS_ACROSS; g++) {
+ for (var h = 0; h < GROUPS_ACROSS; h++) {
+ for (var i = 0; i < GROUPS_ACROSS; i++) {
+ var groupTransform = pack.createObject('Transform');
+ g_groupTransforms[g_groupTransforms.length] = groupTransform;
+ groupTransform.parent = g_client.root;
+ // Turn on culling for this transform.
+ groupTransform.cull = true;
+ var boundingBox = g_o3d.BoundingBox([0, 0, 0],
+ [0, 0, 0]);
+ groupTransform.translate(
+ (g * UNITS_ACROSS_GROUP - HALF_WIDTH) * UNIT_SPACING,
+ (h * UNITS_ACROSS_GROUP - HALF_WIDTH) * UNIT_SPACING,
+ (i * UNITS_ACROSS_GROUP - HALF_WIDTH) * UNIT_SPACING);
+
+ for (var x = 0; x < UNITS_ACROSS_GROUP; x++) {
+ for (var y = 0; y < UNITS_ACROSS_GROUP; y++) {
+ for (var z = 0; z < UNITS_ACROSS_GROUP; z++) {
+ var transform = pack.createObject('Transform');
+ transform.parent = groupTransform;
+ // Turn on culling for this transform.
+ transform.cull = true;
+ transform.addShape(shape);
+ // Add up the bounding boxes of all the elements.
+ var elements = shape.elements;
+ var box = elements[0].boundingBox;
+ for (var ee = 1; ee < elements.length; ee++) {
+ box = box.add(elements[ee].boundingBox);
+ }
+ // Set the transform to have a bounding box that is the sum
+ // of all the elements under it.
+ transform.boundingBox = box;
+ transform.translate(
+ (x - UNITS_ACROSS_GROUP * 0.5) * UNIT_SPACING,
+ (y - UNITS_ACROSS_GROUP * 0.5) * UNIT_SPACING,
+ (z - UNITS_ACROSS_GROUP * 0.5) * UNIT_SPACING);
+ transform.createParam('colorMult', 'ParamFloat4').value = [
+ (g * UNITS_ACROSS_GROUP + x) * (1 / TOTAL_ACROSS),
+ (h * UNITS_ACROSS_GROUP + y) * (1 / TOTAL_ACROSS),
+ (i * UNITS_ACROSS_GROUP + z) * (1 / TOTAL_ACROSS),
+ 1];
+ // Add the box for this bounding box to the box for the group.
+ var box = transform.boundingBox.mul(transform.localMatrix);
+ boundingBox = boundingBox.add(box);
+
+ }
+ }
+ }
+ // Set the bounding box for the group transform to encompass all
+ // the transforms below it.
+ groupTransform.boundingBox = boundingBox;
+ }
+ }
+ }
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ // These are here so that they are visible to both the browser (so
+ // selenium sees them) and the embedded V8 engine.
+ window.g_clock = 0;
+ window.g_timeMult = 1;
+ window.g_finished = false; // for selenium testing.
+
+ // Comment out the line below to run the sample in the browser
+ // JavaScript engine. This may be helpful for debugging.
+ o3djs.util.setMainEngine(o3djs.util.Engine.V8);
+
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and creates one shape.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+
+ // Set window.g_client as well. Otherwise when the sample runs in
+ // V8, selenium won't be able to find this variable (it can only see
+ // the browser environment).
+ window.g_client = g_client = o3dElement.client;
+
+ g_totalDrawableThingsElement =
+ o3djs.util.getElementById('totalDrawableThings');
+ g_totalTransformsElement =
+ o3djs.util.getElementById('totalTransforms');
+ g_transformsProcessedElement =
+ o3djs.util.getElementById('transformsProcessed');
+ g_transformsCulledElement =
+ o3djs.util.getElementById('transformsCulled');
+ g_totalDrawElementsElement =
+ o3djs.util.getElementById('totalDrawElements');
+ g_drawElementsProcessedElement =
+ o3djs.util.getElementById('drawElementsProcessed');
+ g_drawElementsCulledElement =
+ o3djs.util.getElementById('drawElementsCulled');
+ g_drawElementsRenderedElement =
+ o3djs.util.getElementById('drawElementsRendered');
+ g_primitivesRenderedElement =
+ o3djs.util.getElementById('primitivesRendered');
+
+ // Creates a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Create our projection matrix, with a vertical field of view of 45
+ // degrees a near clipping plane of 0.1 and far clipping plane of 10000.
+ g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(45),
+ g_client.width / g_client.height,
+ 0.1,
+ 10000);
+
+ // Create and load the effect.
+ var effect = g_pack.createObject('Effect');
+ o3djs.effect.loadEffect(effect, 'shaders/phong-with-colormult.shader');
+
+ // Create a Material for the effect.
+ var material = g_pack.createObject('Material');
+
+ // Apply our effect to this material.
+ material.effect = effect;
+
+ // Set the material's drawList for opaque objects.
+ material.drawList = g_viewInfo.performanceDrawList;
+
+ // Create the parameters the effect needs on the material.
+ effect.createUniformParameters(material);
+
+ // Set the material parameters.
+ material.getParam('lightWorldPos').value = [1000, 1000, 0];
+ material.getParam('lightIntensity').value = [1, 1, 1, 1];
+ material.getParam('ambientIntensity').value = [0.1, 0.1, 0.1, 1];
+ material.getParam('ambient').value = [1, 1, 1, 1];
+ material.getParam('diffuse').value = [1, 1, 1, 1];
+ material.getParam('specular').value = [0.5, 0.5, 0.5, 1];
+ material.getParam('shininess').value = 50;
+
+ // Create 2 spheres.
+ var shape1 = o3djs.primitives.createSphere(
+ g_pack,
+ material,
+ 40,
+ 20,
+ 20,
+ g_math.matrix4.translation([-50, 0, 0]));
+
+ var shape2 = o3djs.primitives.createSphere(
+ g_pack,
+ material,
+ 20,
+ 20,
+ 20,
+ g_math.matrix4.translation([50, 0, 0]));
+ // Create a shape and move the 2 sphere primitives to the same shape.
+ // This is done to show that each of the primitives under the shape
+ // will get culled separately.
+ var shape = g_pack.createObject('Shape');
+ shape1.elements[0].owner = shape;
+ shape2.elements[0].owner = shape;
+ // delete the old shapes.
+ g_pack.removeObject(shape1);
+ g_pack.removeObject(shape2);
+ var elements = shape.elements;
+ // Turn on culling for the two sphere elements.
+ elements[0].cull = true;
+ elements[1].cull = true;
+
+ createInstances(g_pack, shape);
+
+ g_totalDrawableThingsElement.innerHTML =
+ GROUPS_ACROSS * UNITS_ACROSS_GROUP *
+ GROUPS_ACROSS * UNITS_ACROSS_GROUP *
+ GROUPS_ACROSS * UNITS_ACROSS_GROUP;
+
+ g_totalDrawElementsElement.innerHTML = g_client.getObjectsByClassName(
+ 'o3d.DrawElement').length;
+ g_totalTransformsElement.innerHTML = g_client.getObjectsByClassName(
+ 'o3d.Transform').length;
+
+ // Setup an onrender callback for animation.
+ g_client.setRenderCallback(onrender);
+
+ window.g_finished = true; // for selenium testing.
+}
+
+// spin the camera.
+function onrender(renderEvent) {
+ g_framesRendered++;
+ // Get the number of seconds since the last render.
+ var elapsedTime = renderEvent.elapsedTime;
+
+ // Update g_clock in the browser and cache a V8 copy that can be
+ // accessed efficiently. g_clock must be in the browser for selenium.
+ var clock = window.g_clock + elapsedTime * window.g_timeMult;
+ window.g_clock = clock;
+
+ var x = Math.sin(clock * 0.1) * 300;
+ var z = Math.cos(clock * 0.1) * 300;
+ var y = Math.sin(clock * 0.2) * 300;
+
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [x, y, z],
+ [0, 0, 0],
+ [0, 1, 0]);
+
+ g_transformsProcessedElement.innerHTML = renderEvent.transformsProcessed;
+ g_transformsCulledElement.innerHTML = renderEvent.transformsCulled;
+ g_drawElementsProcessedElement.innerHTML = renderEvent.drawElementsProcessed;
+ g_drawElementsCulledElement.innerHTML = renderEvent.drawElementsCulled;
+ g_drawElementsRenderedElement.innerHTML = renderEvent.drawElementsRendered;
+ g_primitivesRenderedElement.innerHTML = renderEvent.primitivesRendered;
+}
+
+/**
+ * Remove any callbacks so they don't get called after the page has unloaded.
+ */
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+
+</script>
+</head>
+<body onload="init()" onunload="unload()">
+<h1>Culling</h1>
+Objects off screen should get culled.
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 800px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+<table>
+<tr><td>Total Drawable Things:</td><td><span id="totalDrawableThings">-</span></td></tr>
+<tr><td>Total Transforms:</td><td><span id="totalTransforms">-</span></td></tr>
+<tr><td>Transforms Processed:</td><td><span id="transformsProcessed">-</span></td></tr>
+<tr><td>Transforms Culled:</td><td><span id="transformsCulled">-</span></td></tr>
+<tr><td>Total DrawElements:</td><td><span id="totalDrawElements">-</span></td></tr>
+<tr><td>DrawElements Processed:</td><td><span id="drawElementsProcessed">-</span></td></tr>
+<tr><td>DrawElements Culled:</td><td><span id="drawElementsCulled">-</span></td></tr>
+<tr><td>DrawElements Rendered:</td><td><span id="drawElementsRendered">-</span></td></tr>
+<tr><td>Primitives Rendered:</td><td><span id="primitivesRendered">-</span></td></tr>
+</table>
+</body>
+</html>
diff --git a/o3d/samples/customcamera.html b/o3d/samples/customcamera.html
new file mode 100644
index 0000000..83b0df62
--- /dev/null
+++ b/o3d/samples/customcamera.html
@@ -0,0 +1,404 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+O3D Tutorial B4
+
+This tutorial shows how we generate a simple cube mesh
+and view it using a dynamically generated camera which can be animated.
+
+We also show how to dynamically change the perspective matrix
+to adjust to the correct aspect ratio as the o3d window is resized.
+
+The cube can be animated along the target's y-axis and the animation is
+done every time a frame is rendered.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Tutorial B4: Cameras and events
+</title>
+<style type="text/css">
+ html, body {
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ border: none;
+ }
+</style>
+<!-- Our javascript code -->
+<script type="text/javascript" src="o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.effect');
+
+// Events
+// Run the init() function once the page has finished loading.
+// unload() when the page is unloaded.
+window.onload = init;
+window.onunload = unload;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_pack;
+var g_viewInfo;
+var g_o3dElement;
+var g_o3dWidth = -1;
+var g_o3dHeight = -1;
+
+// Our view and projection matrices
+// The view matrix transforms objects from world space to view space.
+var g_viewMatrix;
+// The projection matrix projects objects from view space to the screen.
+var g_projMatrix;
+
+// Animation varibles
+
+// Boolean flag that signals whether animation is on.
+var g_animate;
+// Current angle
+var g_animateAngle;
+
+/**
+ * Creates our client area by looking for <div>s with an id that starts with
+ * "o3d".
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D, loads the effect, and draws the cube.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initialize global variables and libraries.
+ g_o3dElement = clientElements[0];
+ g_o3d = g_o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = g_o3dElement.client;
+
+ // Create a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Reset animation variables.
+ g_animate = false;
+ g_animateAngle = 0;
+
+ // Create and load the effect.
+ var effect = g_pack.createObject('Effect');
+ o3djs.effect.loadEffect(effect, 'shaders/vertex-color.shader');
+
+ // Create a material for it.
+ var myMaterial = g_pack.createObject('Material');
+
+ // Set the material's drawList
+ myMaterial.drawList = g_viewInfo.performanceDrawList;
+
+ // Apply our effect to this material. The effect tells the 3D hardware
+ // which shader to use.
+ myMaterial.effect = effect;
+
+ // Create the parameters the effect needs on the material.
+ effect.createUniformParameters(myMaterial);
+
+ // Create a cube.
+ var cube = o3djs.primitives.createRainbowCube(g_pack, myMaterial, 1);
+
+ setDefaultCameraValues();
+ setCamera();
+
+ // Generate the projection and viewProjection matrices based
+ // on the plugin size by calling Resize().
+ resize();
+
+ // Attach the cube to the root of the transform graph.
+ var root = g_client.root;
+ root.addShape(cube);
+
+
+ // Set our render callback for animation.
+ // This sets a function to be executed every time a frame is rendered.
+ g_client.setRenderCallback(onrender);
+}
+
+// Generates the projection matrix based on the size of the g_o3d plugin
+// and calculates the view-projection matrix.
+function resize() {
+ var newWidth = g_client.width;
+ var newHeight = g_client.height;
+
+ if (newWidth != g_o3dWidth || newHeight != g_o3dHeight) {
+ g_o3dWidth = newWidth;
+ g_o3dHeight = newHeight;
+
+ // Create our projection matrix, with a vertical field of view of 45 degrees
+ // a near clipping plane of 0.1 and far clipping plane of 100.
+ g_projMatrix = g_math.matrix4.perspective(
+ g_math.degToRad(45),
+ g_o3dWidth / g_o3dHeight,
+ 0.1,
+ 100);
+
+ // update our view projection matrix
+ setViewProjection();
+ }
+}
+
+// Sets the view and projection matrices.
+function setViewProjection() {
+ g_viewInfo.drawContext.view = g_viewMatrix;
+ g_viewInfo.drawContext.projection = g_projMatrix;
+}
+
+// Sets the default camera values and updates the view matrix
+function setDefaultCameraValues() {
+ var myForm = document.defaultForm;
+
+ // default eye position = (2, 2, 4)
+ myForm.eyeX.value = '2.0';
+ myForm.eyeY.value = '1.0';
+ myForm.eyeZ.value = '4.0';
+
+ // default target position = (0, 0, 0)
+ // (ie centre of the cube)
+ myForm.targetX.value = '0.0';
+ myForm.targetY.value = '0.0';
+ myForm.targetZ.value = '0.0';
+
+ // default up vector = (0, 1, 0)
+ // (this tells the renderer which direction is 'up')
+ // in this case, we define the positive y-axis to be 'up'.
+ myForm.upX.value = '0.0';
+ myForm.upY.value = '1.0';
+ myForm.upZ.value = '0.0';
+
+ // update the view matrix
+ setCamera();
+}
+
+// Updates the view matrix using the current camera parameters
+function setCamera() {
+ // Create our view matrix using the target, eye, and up vectors in the form
+ // and using the lookAt(..)
+ // helper function to create the matrix.
+ var myForm = document.defaultForm;
+
+ // Eye-position, this is where our camera is at.
+ var eye = [parseFloat(myForm.eyeX.value),
+ parseFloat(myForm.eyeY.value),
+ parseFloat(myForm.eyeZ.value)];
+
+ // Target, this is where our camera is pointed at.
+ var target = [parseFloat(myForm.targetX.value),
+ parseFloat(myForm.targetY.value),
+ parseFloat(myForm.targetZ.value)];
+
+ // Up-vector, this tells the camera which direction is 'up'.
+ // We define the positive y-direction to be up in this example.
+ var up = [parseFloat(myForm.upX.value),
+ parseFloat(myForm.upY.value),
+ parseFloat(myForm.upZ.value)];
+
+ g_viewMatrix = g_math.matrix4.lookAt(eye, target, up);
+
+ // if we already have our projection matrix,
+ // update the view projection matrix.
+ if (g_projMatrix)
+ setViewProjection();
+}
+
+// Validates a field in the form to make sure it contains a float.
+function validateFloat(field) {
+ var floatValue = parseFloat(field.value);
+ if (isNaN(floatValue))
+ field.value = '0.0';
+ else
+ field.value = floatValue;
+}
+
+// Toggles animation
+function toggleAnimate() {
+ // toggle the animate flag.
+ g_animate = !g_animate;
+
+ if (g_animate) {
+ // turn on animation
+ document.defaultForm.btnAnimate.value = 'Stop animation';
+ } else {
+ // turn off animation
+ document.defaultForm.btnAnimate.value = 'Animate';
+ }
+}
+
+// Animates the camera.
+// This function executes on each frame.
+// It was set using g_client.setRenderCallback(..)
+// in initStep2().
+function onrender(renderEvent) {
+ resize();
+ if (g_animate) {
+ // Update the angle frame rate independently.
+ g_animateAngle += 2.0 * renderEvent.elapsedTime;
+
+ var myForm = document.defaultForm;
+ // Set radius to 1.5
+ var radius = 1.5;
+ // rotate around the y-axis relative to the target
+ myForm.eyeX.value = parseFloat(myForm.targetX.value) +
+ Math.sin(g_animateAngle) * radius;
+ myForm.eyeZ.value = parseFloat(myForm.targetZ.value) +
+ Math.cos(g_animateAngle) * radius;
+
+ setCamera();
+ }
+}
+
+/**
+ * Removes any callbacks so they don't get called after the page has unloaded.
+ */
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+
+</script>
+</head>
+<body>
+<table width="100%" style="height:100%;"><tr><td valign="middle" align="center">
+<table width="100%" style="height:100%;"><tr><td>
+<h1>Cameras and events</h1>
+<p>
+This tutorial shows how we generate a simple cube mesh
+and view it using a dynamically generated camera which can be animated.
+</p>
+<p>
+Press Animate to animate the camera and use the textboxes to manually
+set the position of the camera.
+</p>
+<!-- Centre everything in the div -->
+ <table id="container" width="98%" style="height:50%;"><tr><td height="100%">
+ <!-- Start of g_o3d plugin -->
+ <div id="o3d" style="width: 100%; height: 100%;"></div>
+ <!-- End of g_o3d plugin -->
+ </td></tr></table>
+ <!-- Format input fields nicely in a table -->
+ <form action="#" method="get" name="defaultForm">
+ <table style="margin-left:auto; margin-right:auto"
+ summary="This table contains camera controls.">
+ <thead style="font-weight:bold; text-align:center">
+ <tr>
+ <th></th>
+ <th>x</th>
+ <th>y</th>
+ <th>z</th>
+ <th></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>Eye</td>
+ <td><input name="eyeX"
+ type="text"
+ onblur="validateFloat(this)"/></td>
+ <td><input name="eyeY"
+ type="text"
+ onblur="validateFloat(this)"/></td>
+ <td><input name="eyeZ"
+ type="text"
+ onblur="validateFloat(this)"/></td>
+ <td rowspan="2">
+ <input name="btnSet"
+ type="button"
+ value="Set camera"
+ onClick="setCamera()"/>
+ </td>
+ </tr>
+ <tr>
+ <td>Target</td>
+ <td><input name="targetX"
+ type="text"
+ onblur="validateFloat(this)"/></td>
+ <td><input name="targetY"
+ type="text"
+ onblur="validateFloat(this)"/></td>
+ <td><input name="targetZ"
+ type="text"
+ onblur="validateFloat(this)"/></td>
+ </tr>
+ <tr>
+ <td>Up vector</td>
+ <td><input name="upX"
+ type="text"
+ onblur="validateFloat(this)"/></td>
+ <td><input name="upY"
+ type="text"
+ onblur="validateFloat(this)"/></td>
+ <td><input name="upZ"
+ type="text"
+ onblur="validateFloat(this)"/></td>
+ <td>
+ <input name="btnDefault"
+ type="button"
+ value="Restore default camera"
+ onClick="setDefaultCameraValues()"/>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="5" style="text-align:center">
+ <input name="btnAnimate"
+ type="button"
+ value="Animate"
+ onClick="toggleAnimate()"/>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </form>
+</td></tr></table>
+</td></tr></table>
+</body>
+</html>
diff --git a/o3d/samples/debugging.html b/o3d/samples/debugging.html
new file mode 100644
index 0000000..bc96bff
--- /dev/null
+++ b/o3d/samples/debugging.html
@@ -0,0 +1,246 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+This sample shows examples of using the debug.js utilities.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Debugging.
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.pack');
+o3djs.require('o3djs.debug');
+// Events
+// init() once the page has finished loading.
+// unload() when the page is unloaded.
+window.onload = init;
+window.onunload = unload;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_viewInfo;
+var g_pack;
+var g_transforms = [];
+var g_o3dWidth; // width of our client area.
+var g_o3dHeight; // height of our client area.
+var g_finished = false; // for selenium testing.
+var g_clock = 0;
+var g_lastClock = 0;
+var g_tempAxesOn = false;
+var g_tempLinesOn = false;
+var g_timeMult = 1;
+var g_debugHelper;
+var g_debugLineGroup;
+var g_startEndLine;
+var g_tempLineGroup;
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = o3dElement.client;
+
+ // Get the width and height of our client area. We will need this to create
+ // a projection matrix.
+ g_o3dWidth = o3dElement.clientWidth;
+ g_o3dHeight = o3dElement.clientHeight;
+
+ // Creates a pack to manage our resources/assets.
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Set the projection matrix, with a vertical field of view of 45 degrees
+ // a near clipping plane of 0.1 and far clipping plane of 10000.
+ g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(45),
+ g_o3dWidth / g_o3dHeight,
+ 0.1,
+ 10000);
+
+ // Create 11 transforms for the bones and parent them into a chain.
+ for (var ii = 0; ii <= 10; ++ii) {
+ var transform = g_pack.createObject('Transform');
+ g_transforms[ii] = transform;
+ if (ii > 0) {
+ transform.translate(0, 20, 0);
+ }
+ transform.parent = ii == 0 ? g_client.root : g_transforms[ii - 1];
+ }
+
+ // create a debug helper.
+ g_debugHelper = o3djs.debug.createDebugHelper(g_client.createPack(),
+ g_viewInfo);
+
+ // create a debug line group
+ g_debugLineGroup = g_debugHelper.createDebugLineGroup(g_client.root);
+
+ // draw a few lines.
+ g_debugLineGroup.addLine([-20, 0, -20], [-20, 0, +20], [0, 1, 1, 1]);
+ g_debugLineGroup.addLine([-20, 0, +20], [+20, 0, +20], [0, 1, 1, 1]);
+ g_debugLineGroup.addLine([+20, 0, +20], [+20, 0, -20], [0, 1, 1, 1]);
+ g_debugLineGroup.addLine([+20, 0, -20], [-20, 0, -20], [0, 1, 1, 1]);
+
+ // create a line for updating.
+ g_startEndLine = g_debugLineGroup.addLine();
+
+ // Add axes to all the transforms.
+ g_debugHelper.addAxes(g_client.root);
+
+ // Add a cube to a transform.
+ g_debugHelper.addCube(g_transforms[3]);
+ g_debugHelper.setCubeScale(g_transforms[3], 10);
+ g_debugHelper.setCubeColor(g_transforms[3], [1, 1, 0, 1]); // yellow.
+
+ // Add a sphere to a transform.
+ g_debugHelper.addSphere(g_transforms[7]);
+ g_debugHelper.setSphereScale(g_transforms[7], 20);
+ g_debugHelper.setSphereColor(g_transforms[7], [1, 0, 1, 1]); // magenta.
+
+ // Setup an onrender callback for animation.
+ g_client.setRenderCallback(onrender);
+
+ g_finished = true; // for selenium testing.
+}
+
+// spin the camera.
+function onrender(render_event) {
+ // Get the number of seconds since the last render.
+ var elapsedTime = render_event.elapsedTime;
+ g_clock += elapsedTime * g_timeMult;
+
+ var x = Math.sin(g_clock * 0.3) * 400;
+ var z = Math.cos(g_clock * 0.3) * 400;
+ var y = Math.sin(g_clock * 0.7) * 50 + 100;
+
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [x, y, z], // eye
+ [0, 100, 0], // target
+ [0, 1, 0]); // up
+
+ // Make our bone chain bend.
+ var rotation = Math.PI / g_transforms.length * Math.sin(g_clock * 1);
+ for (var ii = 1; ii < g_transforms.length; ++ii) {
+ var transform = g_transforms[ii];
+ transform.identity();
+ transform.translate(0, 20, 0);
+ transform.rotateX(rotation);
+ }
+
+ // change the color of an axis each frame.
+ if ((g_clock * 2) % 2 < 1) {
+ g_debugHelper.setAxisColor(g_transforms[4], [1, 1, 1, 1]);
+ } else {
+ g_debugHelper.clearAxisColor(g_transforms[4]);
+ }
+
+ var start = g_math.matrix4.getTranslation(g_transforms[0].worldMatrix);
+ var end = g_math.matrix4.getTranslation(g_transforms[10].worldMatrix);
+ g_startEndLine.setEndPoints(start, end);
+
+ // Add/Remove some axes.
+ var tempAxesOn = (g_clock % 2) < 1;
+ if (tempAxesOn != g_tempAxesOn) {
+ g_tempAxesOn = tempAxesOn;
+ if (tempAxesOn) {
+ g_debugHelper.addAxes(g_transforms[7]);
+ } else {
+ g_debugHelper.removeAxes(g_transforms[7]);
+ }
+ }
+
+ // Create or delete a line group.
+ var tempLinesOn = (g_clock * 1.1 % 2) < 1;
+ if (tempLinesOn != g_tempLinesOn) {
+ g_tempLinesOn = tempLinesOn;
+ if (tempLinesOn) {
+ g_tempLineGroup = g_debugHelper.createDebugLineGroup(g_transforms[10]);
+ g_tempLineGroup.setColor([1, 0.7, 0.7, 1]);
+ g_tempLineGroup.addLine([-20, 0, -20], [-20, 0, +20]);
+ g_tempLineGroup.addLine([-20, 0, +20], [+20, 0, +20]);
+ g_tempLineGroup.addLine([+20, 0, +20], [+20, 0, -20]);
+ g_tempLineGroup.addLine([+20, 0, -20], [-20, 0, -20]);
+ } else {
+ g_tempLineGroup.destroy();
+ }
+ }
+
+ g_lastClock = g_clock;
+}
+
+/**
+ * Removes any callbacks so they don't get called after the page has unloaded.
+ */
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+</script>
+</head>
+<body>
+<h1>Debugging.</h1>
+<p>This sample shows examples of using the debug.js utilities.
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 800px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/samples/displayfps.html b/o3d/samples/displayfps.html
new file mode 100644
index 0000000..fb96f89
--- /dev/null
+++ b/o3d/samples/displayfps.html
@@ -0,0 +1,219 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Compute and Display FPS.
+</title>
+<style type="text/css">
+ html, body {
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ border: none;
+ }
+</style>
+<!-- Our javascript code -->
+<script type="text/javascript" src="o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.effect');
+o3djs.require('o3djs.fps');
+
+// Events
+// Run the init() function once the page has finished loading.
+// unload() when the page is unloaded.
+window.onload = init;
+window.onunload = unload;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_pack;
+var g_viewInfo;
+var g_o3dWidth = -1;
+var g_o3dHeight = -1;
+var g_o3dElement;
+var g_clock = 0;
+var g_timeMult = 1;
+var g_finished = false; // for selenium testing
+var g_eye;
+var g_fpsManager;
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D, loads the effect, and draws the cube.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initialize global variables and libraries.
+ g_o3dElement = clientElements[0];
+ g_o3d = g_o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = g_o3dElement.client;
+
+ // Create a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Create an FPS manager.
+ g_fpsManager = o3djs.fps.createFPSManager(g_pack,
+ g_client.width,
+ g_client.height,
+ g_client.renderGraphRoot);
+
+ // Create a material.
+ var myMaterial = g_pack.createObject('Material');
+
+ // Set the material's drawList for opaque objects.
+ myMaterial.drawList = g_viewInfo.performanceDrawList;
+
+ // Create and load the effect.
+ var effect = g_pack.createObject('Effect');
+ o3djs.effect.loadEffect(effect, 'shaders/vertex-color.shader');
+
+ // Apply our effect to this material and create the params the effect needs
+ // on the material.
+ myMaterial.effect = effect;
+ effect.createUniformParameters(myMaterial);
+
+ // Draw a cube using the effect we have loaded.
+ var myShape = o3djs.primitives.createRainbowCube(g_pack,
+ myMaterial,
+ 0.5);
+
+ // Attach the cube to the root of the transform graph.
+ var root = g_client.root;
+ root.addShape(myShape);
+
+ // Set the projection and viewProjection matrices based
+ // on the o3d plugin size by calling resize().
+ resize();
+
+ // Set our render callback for animation.
+ // This sets a function to be executed every time a frame is rendered.
+ g_client.setRenderCallback(onrender);
+
+ g_finished = true; // for selenium testing.
+}
+
+// Sets the projection matrix based on the size of the plugin.
+function resize() {
+ var newWidth = g_client.width;
+ var newHeight = g_client.height;
+
+ if (newWidth != g_o3dWidth || newHeight != g_o3dHeight) {
+ g_o3dWidth = newWidth;
+ g_o3dHeight = newHeight;
+
+ // Set the projection matrix, with a vertical field of view of 45 degrees
+ // a near clipping plane of 0.1 and far clipping plane of 100.
+ g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(45),
+ g_o3dWidth / g_o3dHeight,
+ 0.1,
+ 100);
+
+ g_fpsManager.resize(g_o3dWidth, g_o3dHeight);
+ }
+}
+
+// Animates the cube.
+// This function executes on each frame.
+// @param {!o3d.RenderEvent} renderEvent Info about rendering.
+function onrender(renderEvent) {
+ // Get the number of seconds since the last render.
+ var elapsedTime = renderEvent.elapsedTime;
+
+ g_fpsManager.update(renderEvent);
+
+ // Rotate frame rate independently.
+ g_clock += 2.0 * elapsedTime * g_timeMult;
+
+ // Eye-position, the position of the camera.
+ var eye = [
+ 0.0 + Math.sin(g_clock) * 1.5,
+ 1.0,
+ 0.0 + Math.cos(g_clock) * 1.5
+ ];
+
+ var target = [0, 0, 0];
+ var up = [0, 1, 0];
+
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(eye, target, up);
+
+ resize();
+}
+
+/**
+ * Removes any callbacks so they don't get called after the page has unloaded.
+ */
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+
+</script>
+</head>
+<body>
+<table width="100%" style="height:100%;">
+ <tr><td>
+<h1>Show FPS</h1>
+<p>
+This example shows computing and displaying FPS (Frames Per Second)
+</p></td></tr>
+<tr><td height="100%">
+<div id=o3d" style="width: 100%; height: 100%;"></div>
+</td></tr></table>
+</body>
+</html>
diff --git a/o3d/samples/error-texture.html b/o3d/samples/error-texture.html
new file mode 100644
index 0000000..e71d742
--- /dev/null
+++ b/o3d/samples/error-texture.html
@@ -0,0 +1,263 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+This sample attempts to show what the error texture is, how to set it and
+how turning it off will generate helpful error information.
+-->
+<html>
+<head>
+<title>Error Texture</title>
+<script type="text/javascript" src="o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.effect');
+
+// Events
+// Run the init() once the page has finished loading.
+// and unload() when the page is unloaded.
+window.onload = init;
+window.onunload = unload;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_pack;
+var g_viewInfo;
+var g_texture;
+var g_errorMsgElement;
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D, loads the effect, and sets up some quads.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initialize global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = o3dElement.client;
+
+ g_errorMsgElement =
+ document.getElementById('errorMsg');
+
+ // Turn of the error callback that o3djs.base.init setup.
+ g_client.clearErrorCallback();
+
+ // Let us render on demand.
+ g_client.renderMode = g_o3d.Client.RENDERMODE_ON_DEMAND;
+
+ // Create a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ var clientWidth = g_client.width;
+ var clientHeight = g_client.height;
+ g_viewInfo.drawContext.projection = g_math.matrix4.orthographic(
+ -clientWidth * 0.5,
+ clientWidth * 0.5,
+ -clientHeight * 0.5,
+ clientHeight * 0.5,
+ 0.001,
+ 1000);
+
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [0, 500, 0], // eye
+ [0, 0, 0], // target
+ [0, 0, -1]); // up
+
+ // Create and load the effect.
+ var effect = g_pack.createObject('Effect');
+ o3djs.effect.loadEffect(effect, 'shaders/texture-only.shader');
+
+ // Create a Material for the effect.
+ var myMaterial = g_pack.createObject('Material');
+
+ // Set the material's drawList
+ myMaterial.drawList = g_viewInfo.zOrderedDrawList;
+
+ // Apply our effect to this material.
+ myMaterial.effect = effect;
+
+ // Creates a quad using the effect.
+ var shape = o3djs.primitives.createPlane(g_pack,
+ myMaterial,
+ 1,
+ 1,
+ 1,
+ 1);
+
+ var pixels = [];
+ for (var y = 0; y < 32; ++y) {
+ for (var x = 0; x < 32; ++x) {
+ var offset = (y * 32 + x) * 3; // rgb
+ var u = x / 32 * Math.PI * 0.5;
+ var v = y / 32 * Math.PI * 0.5;
+ pixels[offset + 0] = 0; // red
+ pixels[offset + 1] = Math.floor(y / 8) % 2; // green
+ pixels[offset + 2] = Math.floor(x / 8) % 2; // blue
+ }
+ }
+ var texture = g_pack.createTexture2D(32, 32, g_o3d.Texture.XRGB8, 1, false);
+ texture.set(0, pixels);
+
+ // display the quad 4 times with situations
+ // by overriding the sampler on each instance.
+ for (var s = 0; s < 4; ++s) {
+ // create a transform for an instance
+ var transform = g_pack.createObject('Transform');
+ transform.translate((s - 1.5) * 140, 0, 0);
+ transform.scale(128, 1, 128),
+ transform.parent = g_client.root;
+ transform.addShape(shape);
+
+ // case 0: Correct Texture.
+ // case 1: ParamSampler and Sampler but no Texture
+ // case 2: ParamSampler but no Sampler,
+ // case 3: No ParamSampler.
+ if (s <= 2) {
+ // Create a ParamSampler on the transform with the same name as in
+ // the effect so this param will be used instead of the one on the
+ // material.
+ var samplerParam = transform.createParam('texSampler0', 'ParamSampler');
+
+ if (s <= 1) {
+ var sampler = g_pack.createObject('Sampler');
+ sampler.name = "s2d";
+ samplerParam.value = sampler;
+ sampler.addressModeU = g_o3d.Sampler.CLAMP;
+ sampler.addressModeV = g_o3d.Sampler.CLAMP;
+ if (s == 0) {
+ sampler.texture = texture;
+ }
+ }
+ }
+ }
+
+ g_client.setPostRenderCallback(onRender);
+
+ // Render once now that things are setup.
+ render();
+}
+
+function setToUserTexture() {
+ var pixels = [];
+ for (var y = 0; y < 32; ++y) {
+ for (var x = 0; x < 32; ++x) {
+ var offset = (y * 32 + x) * 3; // rgb
+ var u = x / 32 * Math.PI * 0.5;
+ var v = y / 32 * Math.PI * 0.5;
+ pixels[offset + 0] = Math.cos(u); // red
+ pixels[offset + 1] = Math.sin(v); // green
+ pixels[offset + 2] = Math.sin(u); // blue
+ }
+ }
+ var texture = g_pack.createTexture2D(32, 32, g_o3d.Texture.XRGB8, 1, false);
+ texture.set(0, pixels);
+
+ g_client.setErrorTexture(texture);
+ // Render once now that things are setup.
+ render();
+}
+
+function setToNoTexture() {
+ g_client.setErrorTexture(null);
+ render();
+
+}
+
+function hide0() {
+ var child = g_client.root.children[1];
+ child.visible = !child.visible;
+ render();
+}
+
+function hide1() {
+ var child = g_client.root.children[2];
+ child.visible = !child.visible;
+ render();
+}
+
+function reportError(msg) {
+ g_errorMsgElement.innerHTML = g_client.lastError;
+ g_client.clearLastError();
+ g_client.clearErrorCallback();
+}
+
+function render() {
+ // Render once now that things are setup.
+ g_client.setErrorCallback(reportError);
+ g_client.render();
+}
+
+function onRender() {
+}
+
+/**
+ * Removes any callbacks so they don't get called after the page has unloaded.
+ */
+function unload() {
+ g_client.cleanup();
+}
+</script>
+</head>
+<body>
+<h1>Error Texture.</h1>
+<br/>
+Demonstrates how missing textures are handled.
+<div>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 800px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+</div>
+<br/>
+<input type="button" value="User Texture" onClick="setToUserTexture()"/>
+<input type="button" value="No Texture" onClick="setToNoTexture()"/>
+<input type="button" value="hide 0" onClick="hide0()"/>
+<input type="button" value="hide 1" onClick="hide1()"/>
+<table><tr><td>Error: </td><td id="errorMsg">-</td></tr></table>
+</html>
diff --git a/o3d/samples/fullscreen.html b/o3d/samples/fullscreen.html
new file mode 100644
index 0000000..198eadb
--- /dev/null
+++ b/o3d/samples/fullscreen.html
@@ -0,0 +1,538 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+HUD 2D Overlay with fullscreen toggle.
+
+This example modifies the HUD sample to add fullscreen functionality.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+HUD 2D Overlay with fullscreen toggle.
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.effect');
+o3djs.require('o3djs.loader');
+o3djs.require('o3djs.event');
+
+// Events
+// init() once the page has finished loading.
+// unload() when the page is unloaded.
+window.onload = init;
+window.onunload= unload;
+
+// global variables
+var g_o3dElement;
+var g_o3d;
+var g_math;
+var g_client;
+var g_3dRoot;
+var g_hudRoot;
+var g_viewInfo;
+var g_hudViewInfo;
+var g_pack;
+var g_clock = 0;
+var g_timeMult = 1;
+var g_finished = false; // for selenium testing
+var g_cameraRadius = 35;
+var g_cameraSpeed = 0.3;
+var g_gaugeWidth = 145;
+var g_gaugeHeight = 16;
+var g_planeShape;
+var g_groundShape;
+var g_cubeShape;
+var g_materialUrls = [
+ 'shaders/texture-colormult.shader', // 0
+ 'shaders/phong-with-colormult.shader' // 1
+];
+var g_materials = [];
+var g_textures = [];
+var g_textureUrls = [
+ 'assets/purple-flower.png', // 0
+ 'assets/orange-flower.png', // 1
+ 'assets/egg.png', // 2
+ 'assets/gaugeback.png', // 3
+ 'assets/gauge.png', // 4
+ 'assets/iconback.png', // 5
+ 'assets/radar.png', // 6
+ 'assets/one-pixel-white.tga', // 7
+ 'assets/fullscreen.png' // 8
+];
+var g_radar;
+var g_radarNeedle;
+var g_gaugeBack;
+var g_fullscreen;
+var g_gauges = [];
+var g_gaugeFrames = [];
+var g_iconBacks = [];
+var g_icons = [];
+var g_selectedIndex = 0;
+var g_randSeed = 0;
+
+/**
+ * Returns a deterministic pseudorandom number bewteen 0 and 1
+ * @return {number} a random number between 0 and 1
+ */
+function pseudoRandom() {
+ var range = Math.pow(2, 32);
+
+ return (g_randSeed = (134775813 * g_randSeed + 1) % range) / range;
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and creates one shape.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ g_o3dElement = clientElements[0];
+ g_o3d = g_o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = g_o3dElement.client;
+
+ // Creates a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create 2 root transforms, one for the 3d parts, one for the 2d parts.
+ // This is not strictly neccassary but it is helpful for organization.
+ g_3dRoot = g_pack.createObject('Transform');
+ g_hudRoot = g_pack.createObject('Transform');
+
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_3dRoot,
+ g_client.renderGraphRoot);
+
+ // Create a second view for the hud. There are other ways to do this but
+ // this is the easiest.
+ g_hudViewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_hudRoot,
+ g_client.renderGraphRoot);
+
+ // Make sure the hud gets drawn after the 3d stuff
+ g_hudViewInfo.root.priority = g_viewInfo.root.priority + 1;
+
+ // Turn off clearing the color for the hud since that would erase the 3d
+ // parts but leave clearing the depth and stencil so the HUD is unaffected
+ // by anything done by the 3d parts.
+ g_hudViewInfo.clearBuffer.clearColorFlag = false;
+
+ // Set culling to none so we can flip images using rotation or negative scale.
+ g_hudViewInfo.zOrderedState.getStateParam('CullMode').value =
+ g_o3d.State.CULL_NONE;
+ g_hudViewInfo.zOrderedState.getStateParam('ZWriteEnable').value = false;
+
+ // Create an orthographic matrix for 2d stuff in the HUD.
+ // We assume the area is 800 pixels by 600 pixels and therefore we can
+ // position things using a 0-799, 0-599 coordinate system. If we change the
+ // size of the client area everything will get scaled to fix but we don't
+ // have to change any of our code. See 2d.html
+ g_hudViewInfo.drawContext.projection = g_math.matrix4.orthographic(
+ 0 + 0.5,
+ 800 + 0.5,
+ 600 + 0.5,
+ 0 + 0.5,
+ 0.001,
+ 1000);
+
+ g_hudViewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [0, 0, 1], // eye
+ [0, 0, 0], // target
+ [0, 1, 0]); // up
+
+ g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(30), // 30 degree fov.
+ g_client.width / g_client.height,
+ 0.1, // Near plane.
+ 5000); // Far plane.
+
+ for (var ii = 0; ii < g_materialUrls.length; ++ii) {
+ var effect = g_pack.createObject('Effect');
+ o3djs.effect.loadEffect(effect, g_materialUrls[ii]);
+
+ // Create a Material for the effect.
+ var material = g_pack.createObject('Material');
+
+ // Apply our effect to this material.
+ material.effect = effect;
+
+ // Create the params the effect needs on the material.
+ effect.createUniformParameters(material);
+
+ // Set the default params. We'll override these with params on transforms.
+ material.getParam('colorMult').value = [1, 1, 1, 1];
+
+ g_materials[ii] = material;
+ }
+
+ // Set the materials' drawLists
+ g_materials[0].drawList = g_hudViewInfo.zOrderedDrawList;
+ g_materials[1].drawList = g_viewInfo.performanceDrawList;
+
+ g_materials[1].getParam('lightWorldPos').value = [500, 1000, 0];
+ g_materials[1].getParam('lightIntensity').value = [1, 1, 1, 1];
+ g_materials[1].getParam('ambientIntensity').value = [0.1, 0.1, 0.1, 1];
+ g_materials[1].getParam('ambient').value = [1, 1, 1, 1];
+ g_materials[1].getParam('diffuse').value = [1, 1, 1, 1];
+ g_materials[1].getParam('specular').value = [0.5, 0.5, 0.5, 1];
+ g_materials[1].getParam('shininess').value = 20;
+
+ // Create a 2d plane for images. createPlane makes an XZ plane by default
+ // so we pass in matrix to rotate it to an XY plane. We could do
+ // all our manipluations in XZ but most people seem to like XY for 2D.
+ g_planeShape = o3djs.primitives.createPlane(
+ g_pack,
+ g_materials[0],
+ 1,
+ 1,
+ 1,
+ 1,
+ [[1, 0, 0, 0],
+ [0, 0, 1, 0],
+ [0,-1, 0, 0],
+ [0, 0, 0, 1]]);
+
+ // Create a ground plane
+ g_groundShape = o3djs.primitives.createPlane(
+ g_pack,
+ g_materials[1],
+ 30,
+ 30,
+ 10,
+ 10);
+
+ // Create a cube with its origin at the bottom center.
+ g_cubeShape = o3djs.primitives.createCube(
+ g_pack,
+ g_materials[1],
+ 1,
+ [[0.9, 0, 0, 0],
+ [0, 1, 0, 0],
+ [0, 0, 0.9, 0],
+ [0, 0.5, 0, 1]]);
+
+ // Load all the textures.
+ var loader = o3djs.loader.createLoader(initStep3);
+ for (var ii = 0; ii < g_textureUrls.length; ++ii) {
+ loadTexture(loader, g_textureUrls[ii], ii);
+ }
+ loader.finish();
+}
+
+/**
+ * Loads a texture and saves it in the g_textures array.
+ * @param {Object} loader The loader to load with.
+ * @param {stinrg} url of texture to load
+ * @param {number} index Index to put texture in g_textures
+ */
+function loadTexture(loader, url, index) {
+ loader.loadTexture(g_pack, url, function(texture, exception) {
+ if (exception) {
+ alert(exception);
+ } else {
+ g_textures[index] = texture;
+ }
+ });
+}
+
+/**
+ * Finds a good, large display mode that the current screen supports.
+ */
+function getFullscreenModeId() {
+ var displayModes = g_client.getDisplayModes();
+ var bestMode;
+ for (var index in displayModes) {
+ var mode = displayModes[index];
+ if (!bestMode ||
+ (mode.width > bestMode.width && mode.height > bestMode.height)) {
+ bestMode = mode;
+ }
+ }
+ if (bestMode) {
+ return bestMode.id;
+ } else {
+ return 0; // getDisplayModes isn't implemented on all platforms yet
+ }
+}
+
+function handleResizeEvent(event) {
+ g_fullscreen.transform.visible = !event.fullscreen;
+}
+
+/**
+ * Now that the textures are loaded continue.
+ */
+function initStep3() {
+ // Setup the hud images.
+ g_radar = new Image(g_textures[6], true);
+ g_radar.transform.translate(3, 1, -2);
+
+ g_radarNeedle = new Image(g_textures[7], false);
+ g_radarNeedle.scaleTransform.translate(0, 0.5, 0);
+
+ g_gaugeBack = new Image(g_textures[3], true);
+ g_gaugeBack.transform.translate(201, 17, -2);
+
+ // This image is 150x60, and we keep it 10 pixels from the edge.
+ g_fullscreen = new Image(g_textures[8], true);
+ g_fullscreen.transform.translate(10, 600 - 60 - 10, -2);
+ o3djs.event.addEventListener(g_o3dElement, 'resize', handleResizeEvent);
+ g_client.setFullscreenClickRegion(10, 600 - 60 - 10, 150, 60,
+ getFullscreenModeId());
+
+ for (var ii = 0; ii < 3; ++ii) {
+ g_gaugeFrames[ii] = new Image(g_textures[4], true);
+ g_gaugeFrames[ii].transform.translate(220, 39 + ii * 21, -2);
+
+ g_gauges[ii] = new Image(g_textures[7], true);
+ g_gauges[ii].setColor((ii == 0) ? 1 : 0,
+ (ii == 1) ? 1 : 0,
+ (ii == 2) ? 1 : 0,
+ 1);
+
+ g_iconBacks[ii] = new Image(g_textures[5], true);
+ g_iconBacks[ii].transform.translate(634, 17 + ii * 140, -2);
+
+ // Make the icons' origin their center so we can easily rotate/scale them.
+ g_icons[ii] = new Image(g_textures[ii], false);
+ }
+
+ resetIcons();
+
+ // make the ground plane.
+ var transform = g_pack.createObject('Transform');
+ transform.addShape(g_groundShape);
+ transform.parent = g_3dRoot;
+ transform.createParam('colorMult', 'ParamFloat4').value =
+ [166 / 255, 124 / 255, 82 / 255, 1];
+
+ // Make a random city with 25 blocks.
+ for (var bz = -2; bz <= 2; ++bz) {
+ for (var bx = -2; bx <= 2; ++bx) {
+ for (var xx = 0; xx < 4; ++xx) {
+ createBuilding(bx * 5 + 1 + xx - 1.5, bz * 5 + 1 - 1.5);
+ createBuilding(bx * 5 + 1 + xx - 1.5, bz * 5 + 4 - 1.5);
+ }
+ for (var zz = 1; zz < 3; ++zz) {
+ createBuilding(bx * 5 + 1 - 1.5, bz * 5 + 1 + zz - 1.5);
+ createBuilding(bx * 5 + 4 - 1.5, bz * 5 + 1 + zz - 1.5);
+ }
+ }
+ }
+
+ // Setup an onrender callback for animation.
+ g_client.setRenderCallback(onrender);
+
+ g_finished = true; // for selenium testing.
+}
+
+/**
+ * Creates a building.
+ * @param {number} x X coordinate to create building at
+ * @param {number} z Y coordinate to create building at
+ */
+function createBuilding(x, z) {
+ var transform = g_pack.createObject('Transform');
+ transform.addShape(g_cubeShape);
+ transform.parent = g_3dRoot;
+ transform.translate(x, 0, z);
+ transform.scale(1, pseudoRandom() * 3 + 1, 1);
+ transform.createParam('colorMult', 'ParamFloat4').value = [
+ pseudoRandom() * 0.6 + 0.4,
+ pseudoRandom() * 0.6 + 0.4,
+ pseudoRandom() * 0.6 + 0.4,
+ 1];
+}
+
+/**
+ * Resets the orientation of the icons.
+ */
+function resetIcons() {
+ for (var ii = 0; ii < g_icons.length; ++ii) {
+ g_icons[ii].transform.identity();
+ g_icons[ii].transform.translate(634 + 6 + 64, 17 + ii * 140 + 5 + 64, -1);
+ g_icons[ii].transform.scale(0.8, 0.8, 0);
+ }
+}
+
+/**
+ * Called every frame.
+ * @param {!o3d.RenderEvent} renderEvent Rendering Information.
+ */
+function onrender(renderEvent) {
+ var elapsedTime = renderEvent.elapsedTime;
+ g_clock += elapsedTime * g_timeMult;
+
+ g_selectedIndex = Math.floor(g_clock / 3) % 3;
+
+ // Fly the camera around the city.
+ var eye = [
+ Math.sin(g_clock * g_cameraSpeed) * g_cameraRadius,
+ 10,
+ Math.cos(g_clock * g_cameraSpeed) * g_cameraRadius];
+
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ eye,
+ [0, 0, 0], // target
+ [0, 1, 0]); // up
+
+ // Rotate/Scale the selected icon.
+ var icon = g_icons[g_selectedIndex];
+ icon.transform.identity();
+ icon.transform.translate(
+ 634 + 6 + 64, 17 + g_selectedIndex * 140 + 5 + 64, -1);
+ icon.transform.rotateZ(g_clock * -1);
+ var scale = Math.sin(g_clock * 15) * 0.1 + 0.7;
+ icon.transform.scale(scale, scale, 1);
+
+ // Adjust the gauges
+ for (var ii = 0; ii < 3; ++ii) {
+ var gauge = g_gauges[ii];
+ gauge.transform.identity();
+ gauge.transform.translate(220 + 1, 39 + ii * 21 + 1, -1);
+ switch (ii) {
+ case 0:
+ gauge.transform.scale((Math.sin(g_clock) * 0.5 + 0.5) * g_gaugeWidth,
+ g_gaugeHeight,
+ 1);
+ break;
+ case 1:
+ gauge.transform.scale((Math.cos(g_clock) * 0.5 + 0.5) * g_gaugeWidth,
+ g_gaugeHeight,
+ 1);
+ break;
+ case 2:
+ gauge.transform.scale(
+ (Math.cos(g_clock * 3.2) * 0.2 + 0.6) * g_gaugeWidth,
+ g_gaugeHeight,
+ 1);
+ break;
+ }
+ }
+
+ // Rotate the radar
+ g_radarNeedle.transform.identity();
+ g_radarNeedle.transform.translate(93, 89, 0);
+ g_radarNeedle.transform.rotateZ(g_clock * 3);
+ g_radarNeedle.transform.scale(1, 80, 1);
+}
+
+/**
+ * Creates an Image object which is a transform and a child scaleTransform
+ * scaled to match the texture
+ *
+ * @constructor
+ * @param {!o3d.Texture} texture The texture
+ * @param {boolean} opt_topLeft If true the origin of the image will be its
+ * topleft corner, the default is the center of the image.
+ */
+function Image(texture, opt_topLeft) {
+ // create a transform for positioning
+ this.transform = g_pack.createObject('Transform');
+ this.transform.parent = g_hudRoot;
+
+ // create a transform for scaling to the size of the image just so
+ // we don't have to manage that manually in the transform above.
+ this.scaleTransform = g_pack.createObject('Transform');
+ this.scaleTransform.parent = this.transform;
+
+ // setup the sampler for the texture
+ this.sampler = g_pack.createObject('Sampler');
+ this.sampler.addressModeU = g_o3d.Sampler.CLAMP;
+ this.sampler.addressModeV = g_o3d.Sampler.CLAMP;
+ this.paramSampler = this.scaleTransform.createParam('texSampler0',
+ 'ParamSampler');
+ this.paramSampler.value = this.sampler;
+
+ // Setup our UV offsets and color multiplier
+ this.paramColorMult = this.scaleTransform.createParam('colorMult',
+ 'ParamFloat4');
+
+ this.setColor(1, 1, 1, 1);
+
+ this.sampler.texture = texture;
+ this.scaleTransform.addShape(g_planeShape);
+ if (opt_topLeft) {
+ this.scaleTransform.translate(texture.width / 2, texture.height / 2, 0);
+ }
+ this.scaleTransform.scale(texture.width, -texture.height, 1);
+}
+
+/**
+ * Sets the color multiplier for the image.
+ * @param {number} r Red component.
+ * @param {number} g Green component.
+ * @param {number} b Blue component.
+ * @param {number} a Alpha component.
+ */
+Image.prototype.setColor = function(r, g, b, a) {
+ this.paramColorMult.set(r, g, b, a);
+};
+
+/**
+ * Remove any callbacks so they don't get called after the page has unloaded.
+ */
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+</script>
+</head>
+<body>
+<h1>HUD 2D Overlay</h1>
+HUD = Heads Up Display.
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 800px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/samples/gadgets/readme.txt b/o3d/samples/gadgets/readme.txt
new file mode 100644
index 0000000..2ee0f51
--- /dev/null
+++ b/o3d/samples/gadgets/readme.txt
@@ -0,0 +1,11 @@
+This directory contains Google gadgets samples using the O3D API.
+
+The gadgets can be added to an iGoogle homepage by using the
+"Add feed or gadget" link from the iGoogle gadgets directory:
+http://www.google.com/ig/directory.
+
+More information about creating O3D gadgets can be found in the
+O3D Developer's Guide under "How to Add O3D to a Gadget".
+
+
+
diff --git a/o3d/samples/gadgets/scatter-chart.xml b/o3d/samples/gadgets/scatter-chart.xml
new file mode 100644
index 0000000..b22c7c4
--- /dev/null
+++ b/o3d/samples/gadgets/scatter-chart.xml
@@ -0,0 +1,434 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Module>
+ <ModulePrefs title="3D Scatter Chart"
+ height="340"
+ author="Google O3D Team"
+ author_email="o3d-discuss@googlegroups.com"
+ />
+
+<Content type="html">
+<![CDATA[
+
+<!-- Include vizualizations - Load the Google AJAX API -->
+<script type="text/javascript" src="http://www.google.com/jsapi"></script>
+
+<!-- The path to the utility libraries. -->
+<script type="text/javascript" src="http://o3d.googlecode.com/svn/trunk/samples/o3djs/base.js"></script>
+
+<script type="text/javascript">
+
+// for gadgets we need to specify the base path of our included utility files.
+o3djs.basePath="http://o3d.googlecode.com/svn/trunk/samples/";
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.quaternions');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.effect');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.arcball');
+o3djs.require('o3djs.event');
+
+// Load the Visualization API.
+google.load('visualization', '1', {'packages':['piechart']});
+
+// The initClient() function runs when the page has finished loading.
+_IG_RegisterOnloadHandler(initClient);
+
+// global variables
+var g_o3dElement;
+var g_client;
+var g_o3d;
+var g_math;
+var g_quaternions;
+var g_pack;
+var g_viewInfo;
+var g_modelRoot;
+var g_eyeView;
+var g_cubeShape;
+var g_material;
+var g_aball;
+var g_thisRot;
+var g_lastRot;
+var g_zoomFactor;
+var g_dragging;
+var g_dataView;
+var g_dataSpreadsheet;
+var g_finished = false; // for selenium testing.
+
+/**
+ * Creates the client area.
+ */
+function initClient() {
+ o3djs.util.makeClients(main);
+}
+
+/**
+ * Initializes global variables, positions camera, draws the 3D chart.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function main(clientElements) {
+ // Init global variables.
+ initGlobals(clientElements);
+
+ // Set up the view and projection transformations.
+ initContext();
+
+ // Setup rendering on demand only.
+ g_client.renderMode = g_o3d.Client.RENDERMODE_ON_DEMAND;
+
+ // Add the 3D chart model to the transform hierarchy.
+ create3dChartModel();
+
+ // Query the spreadsheet for the chart data.
+ queryChartDataSource();
+
+ // Start rendering.
+ g_client.render();
+
+ // Execute keyPressed(..) when we detect a keypress on the window or
+ // on the o3d object.
+ window.document.onkeypress = keyPressed;
+ g_o3dElement.onkeypress = keyPressed;
+
+ o3djs.event.addEventListener(g_o3dElement, 'mousedown', startDragging);
+ o3djs.event.addEventListener(g_o3dElement, 'mousemove', drag);
+ o3djs.event.addEventListener(g_o3dElement, 'mouseup', stopDragging);
+ o3djs.event.addEventListener(g_o3dElement, 'wheel', scrollMe);
+
+ g_finished = true; // for selenium testing.
+}
+
+/**
+ * Initializes global variables and libraries.
+ */
+function initGlobals(clientElements) {
+ // init o3d globals.
+ g_o3dElement = clientElements[0];
+ g_client = g_o3dElement.client;
+ g_o3d = g_o3dElement.o3d;
+ g_math = o3djs.math;
+ g_quaternions = o3djs.quaternions;
+
+ // Create an arcball.
+ g_aball = o3djs.arcball.create(g_o3dElement.clientWidth,
+ g_o3dElement.clientHeight);
+
+ // Create a pack to manage the objects created.
+ g_pack = g_client.createPack();
+
+ // Create a transform node to act as the 'root' of the model.
+ // Attach it to the root of the transform graph.
+ g_modelRoot = g_pack.createObject('Transform');
+ g_modelRoot.parent = g_client.root;
+
+ // Create the render graph for the view.
+ var clearColor = [.98, .98, .98, 1];
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot,
+ clearColor);
+
+ // Create a material for the objects rendered.
+ g_material = createPhongMaterial([1, 1, 1, 1]);
+
+ // Create a cube shape to simulate the scatter points.
+ g_cubeShape = o3djs.primitives.createCube(
+ g_pack,
+ g_material,
+ 1);
+
+ // set the chart data spreadsheet.
+ g_dataSpreadsheet = 'http://spreadsheets.google.com/tq?key=pjdnPsZuqE92yNqA4iqT9Ig&range=A4:C533&gid=0';
+}
+
+/**
+ * Initialize the original view of the model.
+ */
+function initContext() {
+ g_eyeView = [-35, 60, 140];
+ g_zoomFactor = 1.03;
+ g_dragging = false;
+ g_modelRoot.identity();
+ g_lastRot = g_math.matrix4.identity();
+ g_thisRot = g_math.matrix4.identity();
+
+ // Set up a perspective transformation for the projection.
+ g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(40), // 30 degree frustum.
+ g_o3dElement.clientWidth / g_o3dElement.clientHeight, // Aspect ratio.
+ 1, // Near plane.
+ 10000); // Far plane.
+
+ // Set up our view transformation to look towards the axes origin.
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ g_eyeView, // eye
+ [0, 0, 0], // target
+ [0, 1, 0]); // up
+}
+
+/**
+ * Reset to the original view of the model.
+ */
+function resetView() {
+ initContext();
+ g_client.render();
+}
+
+/**
+ * Creates a phong material based on the given single color.
+ * @param {Array} baseColor An array with 4 entries, the R,G,B, and A components
+ * of a color.
+ * @return {!o3d.Material} A phong material whose overall pigment is baseColor.
+ */
+function createPhongMaterial(baseColor) {
+ // Create a new, empty Material object.
+ var material = g_pack.createObject('Material');
+
+ var lightPosition = [1000, 2000, 3000];
+ o3djs.effect.attachStandardShader(
+ g_pack, material, lightPosition, 'phong');
+
+ material.drawList = g_viewInfo.performanceDrawList;
+
+ // Assign parameters to the phong material.
+ material.getParam('emissive').value = [0, 0, 0, 1];
+ material.getParam('ambient').value =
+ [.1 * baseColor[0], .1 * baseColor[1], .1 * baseColor[2], 1];
+ material.getParam('diffuse').value =
+ [.9 * baseColor[0], .9 * baseColor[1], .9 * baseColor[2], 1];
+ material.getParam('specular').value = [.5, .5, .5, 1];
+ material.getParam('shininess').value = 50;
+ material.getParam('lightColor').value = [1, 1, 1, 1];
+
+ return material;
+}
+
+/**
+ * Initializes and queries the spreadsheet for the chart data.
+ * The query is asynchronous and the response is handled by a callback.
+ */
+function queryChartDataSource() {
+ // Get chart data from the spreadsheet.
+ var query = new google.visualization.Query(g_dataSpreadsheet);
+
+ // Send the query with a callback function.
+ query.send(handleQueryResponse);
+}
+
+/**
+ * Handler for the spreadsheet query.
+ * Called when the query response is returned.
+ */
+function handleQueryResponse(response) {
+ if (response.isError()) {
+ alert('Error in query: ' + response.getMessage() + ' ' + response.getDetailedMessage());
+ return;
+ }
+
+ // Get the DataTable.
+ var dataTable = response.getDataTable();
+
+ // Create a DataView.
+ g_dataView = new google.visualization.DataView(dataTable);
+
+ // Create the scatter chart.
+ createScatterObject();
+
+ // Start rendering.
+ g_client.render();
+}
+
+
+/**
+ * Create a 3D Scatter object by plotting each data point on a 3D model.
+ */
+function createScatterObject() {
+ // size of a scatter point (represented as a small cube).
+ var pointSize = 0.5;
+
+ for (var i = 0; i < g_dataView.getNumberOfRows(); i += 1) {
+ var varX = g_dataView.getValue(i,0);
+ var varY = g_dataView.getValue(i,2);
+ var varZ = g_dataView.getValue(i,1);
+
+ // create the transform for the cube.
+ var transform = g_pack.createObject('Transform');
+ transform.parent = g_modelRoot;
+ transform.addShape(g_cubeShape);
+ // translate and scale the point correctly relative to origin
+ transform.translate(varX, varY, varZ);
+ transform.scale(pointSize, pointSize, pointSize);
+ transform.createParam('diffuse', 'ParamFloat4').value = [1, 0, 0, 1];
+ }
+}
+
+/**
+ * Creates a 3D chart model.
+ */
+function create3dChartModel() {
+ // create the x,y,z axes - use the cylinder primitive.
+ var cylinder_length = 90;
+ var cylinder_radius = 0.15;
+ var cylinder_subdivisions = 6;
+
+ var cylinder = o3djs.primitives.createCylinder(
+ g_pack,
+ g_material,
+ cylinder_radius, // Radius.
+ cylinder_length, // Depth.
+ cylinder_subdivisions, // Number of subdivisions.
+ 1);
+
+ var cylinder_x = g_pack.createObject('Transform');
+ cylinder_x.parent = g_modelRoot;
+ cylinder_x.addShape(cylinder);
+ cylinder_x.createParam('diffuse', 'ParamFloat4').value = [1, 0.2, 0.2, 1];
+ cylinder_x.rotateZ(Math.PI / 2);
+
+ var cylinder_y = g_pack.createObject('Transform');
+ cylinder_y.parent = g_modelRoot;
+ cylinder_y.addShape(cylinder);
+ cylinder_y.createParam('diffuse', 'ParamFloat4').value = [0.2, 1, 0.2, 1];
+ cylinder_y.rotateX(Math.PI / 2);
+
+ var cylinder_z = g_pack.createObject('Transform');
+ cylinder_z.parent = g_modelRoot;
+ cylinder_z.addShape(cylinder);
+ cylinder_z.createParam('diffuse', 'ParamFloat4').value = [0.2, 0.2, 1, 1];
+}
+
+/**
+ * Callback for the keypress event.
+ * Rotates the 3D model along the x, y or z-axes based on key pressed.
+ * Zooms in and out by moving the viewpoint.
+ * @param {event} event keyPress event passed to us by javascript.
+ */
+function keyPressed(event) {
+ var keyChar = String.fromCharCode(o3djs.event.getEventKeyChar(event));
+ keyChar = keyChar.toLowerCase();
+
+ var delta = 0.03; // rotation delta.
+ // Create an array associating the keystroke to an axis about which to rotate.
+ // Then dereference that array to get the axis.
+
+ switch(keyChar) {
+ case 'a':
+ g_modelRoot.localMatrix =
+ g_math.mul(g_modelRoot.localMatrix,
+ g_math.matrix4.rotationY(-delta));
+ break;
+ case 'd':
+ g_modelRoot.localMatrix =
+ g_math.mul(g_modelRoot.localMatrix,
+ g_math.matrix4.rotationY(delta));
+ break;
+ case 'w':
+ g_modelRoot.localMatrix =
+ g_math.mul(g_modelRoot.localMatrix,
+ g_math.matrix4.rotationX(-delta));
+ break;
+ case 's':
+ g_modelRoot.localMatrix =
+ g_math.mul(g_modelRoot.localMatrix,
+ g_math.matrix4.rotationX(delta));
+ break;
+ case 'l':
+ g_modelRoot.localMatrix =
+ g_math.mul(g_modelRoot.localMatrix,
+ g_math.matrix4.rotationZ(-delta));
+ break;
+ case 'k':
+ g_modelRoot.localMatrix =
+ g_math.mul(g_modelRoot.localMatrix,
+ g_math.matrix4.rotationZ(delta));
+ break;
+ case 'i':
+ ZoomInOut(g_zoomFactor);
+ break;
+ case 'o':
+ ZoomInOut(1.0 / g_zoomFactor);
+ break;
+ }
+
+ o3djs.event.cancel(event);
+
+ g_client.render();
+}
+
+/**
+ * Zooms the model in / out by changing the viewpoint.
+ * @param {number} zoom zooming factor.
+ */
+function ZoomInOut(zoom) {
+ for (i = 0; i < g_eyeView.length; i += 1) {
+ g_eyeView[i] = g_eyeView[i] / zoom;
+ }
+
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ g_eyeView, // eye
+ [0, 0, 0], // target
+ [0, 1, 0]); // up
+}
+
+/**
+ * Start mouse dragging.
+ * @param {event} e event.
+ */
+function startDragging(e) {
+ g_lastRot = g_thisRot;
+ g_aball.click([e.x, e.y]);
+ g_dragging = true;
+}
+
+/**
+ * Use the arcball to rotate the model.
+ * Computes the rotation matrix.
+ * @param {event} e event.
+ */
+function drag(e) {
+ if (g_dragging) {
+ var rotationQuat = g_aball.drag([e.x, e.y]);
+ var rot_mat = g_quaternions.quaternionToRotation(rotationQuat);
+ g_thisRot = g_math.mul(g_lastRot, rot_mat);
+ var m = g_modelRoot.localMatrix;
+ g_math.matrix4.setUpper3x3(m, g_thisRot);
+ g_modelRoot.localMatrix = m;
+
+ g_client.render();
+ }
+}
+
+/**
+ * Stop dragging.
+ * @param {event} e event.
+ */
+function stopDragging(e) {
+ g_dragging = false;
+}
+
+/**
+ * Using the mouse wheel zoom in and out of the model.
+ * @param {event} e event.
+ */
+function scrollMe(e) {
+ var zoom = (e.deltaY < 0) ? 1 / g_zoomFactor : g_zoomFactor;
+ ZoomInOut(zoom);
+ g_client.render();
+}
+
+</script>
+
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 320px; height: 320px;"></div>
+<!-- End of O3D plugin -->
+
+<form name="default_form" action="#" method="get" >
+<div style="font-size:10">
+Rotate: (W, S), (A, D), (K, L) &nbsp &nbsp &nbsp Zoom: (I, O)&nbsp &nbsp &nbsp
+<input type="button" value="Reset View" onclick="resetView()" style="font-size:10"/>
+</div>
+</form>
+]]>
+</Content>
+</Module>
diff --git a/o3d/samples/generate-texture.html b/o3d/samples/generate-texture.html
new file mode 100644
index 0000000..b4ff612
--- /dev/null
+++ b/o3d/samples/generate-texture.html
@@ -0,0 +1,286 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+How to generate a texture.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Generate Texture.
+</title>
+<script type="text/javascript" src="o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.effect');
+
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_pack;
+var g_viewInfo;
+var g_finished = false; // for selenium testing
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ // Comment out the line below to run the sample in the browser
+ // JavaScript engine. This may be helpful for debugging.
+ o3djs.util.setMainEngine(o3djs.util.Engine.V8);
+
+ o3djs.util.makeClients(initStep2, 'FloatingPointTextures');
+}
+
+/**
+ * Initializes O3D, loads an effect, creates some textures
+ * and quads to display them.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+
+ // Set window.g_client as well. Otherwise when the sample runs in
+ // V8, selenium won't be able to find this variable (it can only see
+ // the browser environment).
+ window.g_client = g_client = o3dElement.client;
+
+ // Create a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ var clientWidth = g_client.width;
+ var clientHeight = g_client.height;
+ g_viewInfo.drawContext.projection = g_math.matrix4.orthographic(
+ -clientWidth * 0.5,
+ clientWidth * 0.5,
+ -clientHeight * 0.5,
+ clientHeight * 0.5,
+ 0.001,
+ 1000);
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [0, 500, 0], // eye
+ [0, 0, 0], // target
+ [0, 0, -1]); // up
+
+ // Create and load the effects.
+ var effectNames = [
+ 'texture-only.shader',
+ 'luminance_alpha_texture.shader'];
+
+ var effectInfos = {
+ texture_only: {name: 'texture-only.shader'},
+ one_channel_texture: {name: 'one-channel-texture.shader'}};
+ for (var key in effectInfos) {
+ var info = effectInfos[key];
+ var effect = g_pack.createObject('Effect');
+ o3djs.effect.loadEffect(effect, 'shaders/' + info.name);
+
+ // Create a Material for the effect.
+ var material = g_pack.createObject('Material');
+
+ // Set the material's drawList for transparent objects.
+ material.drawList = g_viewInfo.zOrderedDrawList;
+
+ // Apply the effect to this material.
+ material.effect = effect;
+
+ // Create the params that effect requires on the material.
+ effect.createUniformParameters(material);
+
+ // Create a quad.
+ var shape = o3djs.primitives.createPlane(g_pack,
+ material,
+ 1,
+ 1,
+ 1,
+ 1);
+ info.shape = shape;
+ }
+
+ // display our shape 5 times with 5 different textures
+ // by overriding the sampler on each instance.
+ for (var s = 0; s < 5; ++s) {
+ // create a transform for an instance
+ var transform = g_pack.createObject('Transform');
+ var x = s % 3;
+ var z = Math.floor(s / 3);
+ transform.translate((x - 1) * 140, 0, (z - 0.5) * 140);
+ transform.scale(128, 1, 128);
+ transform.parent = g_client.root;
+
+ // Create a ParamSampler on the transform with the same name as in
+ // the effect so this param will be used instead of the one on the material.
+ var samplerParam = transform.createParam('texSampler0', 'ParamSampler');
+
+ var sampler = g_pack.createObject('Sampler');
+ samplerParam.value = sampler;
+ sampler.addressModeU = g_o3d.Sampler.CLAMP;
+ sampler.addressModeV = g_o3d.Sampler.CLAMP;
+
+ // Create a texture.
+ {
+ var pixels = [];
+ var format;
+
+ switch (s) {
+ case 0: { // XRGB8
+ transform.addShape(effectInfos.texture_only.shape);
+ format = g_o3d.Texture.XRGB8;
+ for (var y = 0; y < 32; ++y) {
+ for (var x = 0; x < 32; ++x) {
+ var offset = (y * 32 + x) * 3; // rgb
+ var u = x / 32 * Math.PI * 0.5;
+ var v = y / 32 * Math.PI * 0.5;
+ pixels[offset + 0] = Math.cos(u); // red
+ pixels[offset + 1] = Math.sin(v); // green
+ pixels[offset + 2] = Math.sin(u); // blue
+ }
+ }
+ break;
+ }
+ case 1: { // ARGB8
+ transform.addShape(effectInfos.texture_only.shape);
+ format = g_o3d.Texture.ARGB8;
+ for (var y = 0; y < 32; ++y) {
+ for (var x = 0; x < 32; ++x) {
+ var offset = (y * 32 + x) * 4; // rgba
+ var u = x / 32 * Math.PI * 0.5;
+ var v = y / 32 * Math.PI * 0.5;
+ pixels[offset + 0] = Math.floor(y / 4) % 2; // red
+ pixels[offset + 1] = Math.sin(v); // green
+ pixels[offset + 2] = Math.floor(x / 4) % 2; // blue
+ pixels[offset + 3] = Math.abs(Math.sin(v * 4)); // alpha
+ }
+ }
+ break;
+ }
+ case 2: { // ABGR16F
+ transform.addShape(effectInfos.texture_only.shape);
+ format = g_o3d.Texture.ABGR16F;
+ for (var y = 0; y < 32; ++y) {
+ for (var x = 0; x < 32; ++x) {
+ var offset = (y * 32 + x) * 4; // rgba
+ var u = x / 32 * Math.PI * 0.5;
+ var v = y / 32 * Math.PI * 0.5;
+ pixels[offset + 0] = Math.cos(v); // red
+ pixels[offset + 1] = Math.sin(u); // green
+ pixels[offset + 2] = Math.sin(v); // blue
+ pixels[offset + 3] = Math.abs(Math.sin(u * 8)); // alpha
+ }
+ }
+ break;
+ }
+ case 3: { // ABGR32F
+ transform.addShape(effectInfos.texture_only.shape);
+ format = g_o3d.Texture.ABGR32F;
+ for (var y = 0; y < 32; ++y) {
+ for (var x = 0; x < 32; ++x) {
+ var offset = (y * 32 + x) * 4; // rgba
+ var u = x / 32 * Math.PI * 0.5;
+ var v = y / 32 * Math.PI * 0.5;
+ pixels[offset + 0] = Math.cos(v); // red
+ pixels[offset + 1] = Math.sin(u); // green
+ pixels[offset + 2] = Math.sin(v); // blue
+ pixels[offset + 3] = Math.abs(Math.sin(u * 8)); // alpha
+ }
+ }
+ break;
+ }
+ case 4: { // R32F
+ /**
+ * NOTE: GL and D3D do NOT share compatible 1 channel texture formats.
+ *
+ * In D3D the sampler will return
+ *
+ * R = channel 0
+ * G = const 1
+ * B = const 1
+ * A = const 1
+ *
+ * In GL the sampler will return
+ *
+ * R = channel 0
+ * G = channel 0
+ * B = channel 0
+ * A = channel 0
+ *
+ * What that means is only R works across platforms. G, B and A are
+ * undefined and if you use them you'll get the wrong results.
+ */
+ transform.addShape(effectInfos.one_channel_texture.shape);
+ format = g_o3d.Texture.R32F;
+ for (var y = 0; y < 32; ++y) {
+ for (var x = 0; x < 32; ++x) {
+ var offset = (y * 32 + x); // r
+ var u = x / 32 * Math.PI * 0.5;
+ var v = y / 32 * Math.PI * 0.5;
+ pixels[offset + 0] = Math.cos(v * 16); // red
+ }
+ }
+ break;
+ }
+ }
+ var texture = g_pack.createTexture2D(32, 32, format, 1, false);
+ texture.set(0, pixels);
+ sampler.texture = texture;
+ }
+ }
+ window.g_finished = true; // for selenium testing.
+}
+
+</script>
+</head>
+<body onload="init()">
+<h1>Generate Texture</h1>
+Shows how to create textures in Javascript.
+<br/>
+
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 600px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/samples/hellocube-colors.html b/o3d/samples/hellocube-colors.html
new file mode 100644
index 0000000..6e49825
--- /dev/null
+++ b/o3d/samples/hellocube-colors.html
@@ -0,0 +1,352 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+This sample creates an O3D area with a spinning cube. The user
+can change the color of the cube by clicking on one of the buttons that appear
+at the bottom of the page. This sample demonstrates how to use O3D Params
+to change the values of uniform parameters used by shaders.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Hello Square Colors: Getting started with O3D, take 2.
+</title>
+<script type="text/javascript" src="o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+
+// Events
+// Run the init() function once the page has finished loading.
+// Run the uninit() function when the page has is unloaded.
+window.onload = init;
+window.onunload = uninit;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_pack;
+var g_client;
+var g_clock = 0;
+var g_timeMult = 1;
+var g_finished = false; // for selenium testing
+
+var g_cubeTransform;
+var g_cubeColorParam;
+
+/**
+ * Changes the color of the cube.
+ * @param red red component of the color
+ * @param green green component of the color
+ * @param blue blue component of the color
+ */
+function changeColor(red, green, blue) {
+ // Set the rgb color values and alpha = 1
+ g_cubeColorParam.value = [red, green, blue, 1];
+}
+
+/**
+ * Creates an O3D shape representing a cube. The shape consists of
+ * a single primitive with eight vertices and 12 triangles (two for each face
+ * of the cube).
+ * @param {o3d.Material} material the material used by the primitive.
+ * @return {o3d.Shape} The Shape object created.
+ */
+function createCube(material) {
+ // Create a Shape object for the mesh.
+ var cubeShape = g_pack.createObject('Shape');
+
+ // Create the Primitive that will contain the geometry data for
+ // the cube.
+ var cubePrimitive = g_pack.createObject('Primitive');
+
+ // Create a StreamBank to hold the streams of vertex data.
+ var streamBank = g_pack.createObject('StreamBank');
+
+ // Assign the material that was passed in to the primitive.
+ cubePrimitive.material = material;
+
+ // Assign the Primitive to the Shape.
+ cubePrimitive.owner = cubeShape;
+
+ // Assign the StreamBank to the Primitive.
+ cubePrimitive.streamBank = streamBank;
+
+ // The cube is made of 12 triangles. There's eight vertices in total which
+ // are shared between the face triangles.
+ cubePrimitive.primitiveType = g_o3d.Primitive.TRIANGLELIST;
+ cubePrimitive.numberPrimitives = 12; // 12 triangles
+ cubePrimitive.numberVertices = 8; // 8 vertices in total
+
+ // Generate the draw element for the cube primitive.
+ cubePrimitive.createDrawElement(g_pack, null);
+
+ // Create a javascript array that stores the X, Y and Z coordinates of each
+ // of the 8 corners of the cube.
+ var positionArray = [
+ -0.5, -0.5, 0.5, // vertex 0
+ 0.5, -0.5, 0.5, // vertex 1
+ -0.5, 0.5, 0.5, // vertex 2
+ 0.5, 0.5, 0.5, // vertex 3
+ -0.5, 0.5, -0.5, // vertex 4
+ 0.5, 0.5, -0.5, // vertex 5
+ -0.5, -0.5, -0.5, // vertex 6
+ 0.5, -0.5, -0.5 // vertex 7
+ ];
+
+ // The following array defines how vertices are to be put together to form
+ // the triangles that make up the cube's faces. In the index array, every
+ // three elements define a triangle. So for example vertices 0, 1 and 2
+ // make up the first triangle, vertices 2, 1 and 3 the second one, etc.
+ var indicesArray = [
+ 0, 1, 2, // face 1
+ 2, 1, 3,
+ 2, 3, 4, // face 2
+ 4, 3, 5,
+ 4, 5, 6, // face 3
+ 6, 5, 7,
+ 6, 7, 0, // face 4
+ 0, 7, 1,
+ 1, 7, 3, // face 5
+ 3, 7, 5,
+ 6, 0, 4, // face 6
+ 4, 0, 2
+ ];
+
+ // Create buffers containing the vertex data.
+ var positionsBuffer = g_pack.createObject('VertexBuffer');
+ var positionsField = positionsBuffer.createField('FloatField', 3);
+ positionsBuffer.set(positionArray);
+
+ var indexBuffer = g_pack.createObject('IndexBuffer');
+ indexBuffer.set(indicesArray);
+
+ // Associate the positions Buffer with the StreamBank.
+ streamBank.setVertexStream(
+ g_o3d.Stream.POSITION, // semantic: This stream stores vertex positions
+ 0, // semantic index: First (and only) position stream
+ positionsField, // field: the field this stream uses.
+ 0); // start_index: How many elements to skip in the
+ // field.
+
+ // Associate the triangle indices Buffer with the primitive.
+ cubePrimitive.indexBuffer = indexBuffer;
+
+ return cubeShape;
+}
+
+/**
+ * This method gets called every time O3D renders a frame. Here's where
+ * we update the cube's transform to make it spin.
+ * @param {o3d.RenderEvent} renderEvent The render event object that gives
+ * us the elapsed time since the last time a frame was rendered.
+ */
+function renderCallback(renderEvent) {
+ g_clock += renderEvent.elapsedTime * g_timeMult;
+ // Rotate the cube around the Y axis.
+ g_cubeTransform.identity();
+ g_cubeTransform.rotateY(2.0 * g_clock);
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D, creates the cube and sets up the transform and
+ * render graphs.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_client = o3dElement.client;
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+
+ // Create a pack to manage the objects created.
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ var viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Set up a perspective projection.
+ viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(30), // 30 degree fov.
+ g_client.width / g_client.height,
+ 1, // Near plane.
+ 5000); // Far plane.
+
+ // Set up our view transformation to look towards the world origin where the
+ // cube is located.
+ viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [0, 1, 5], // eye
+ [0, 0, 0], // target
+ [0, 1, 0]); // up
+
+ // Create an Effect object and initialize it using the shaders from the
+ // text area.
+ var cubeEffect = g_pack.createObject('Effect');
+ var shaderString = document.getElementById('effect').value;
+ cubeEffect.loadFromFXString(shaderString);
+
+ // Create a Material for the mesh.
+ var cubeMaterial = g_pack.createObject('Material');
+
+ // Set the material's drawList.
+ cubeMaterial.drawList = viewInfo.performanceDrawList;
+
+ // Apply our effect to this material. The effect tells the 3D hardware
+ // which shaders to use.
+ cubeMaterial.effect = cubeEffect;
+
+ // Create an O3D Param on the material for every uniform used by the
+ // shader.
+ cubeEffect.createUniformParameters(cubeMaterial);
+
+ // Get the color parameter from the material and set its value to red.
+ g_cubeColorParam = cubeMaterial.getParam('color');
+ g_cubeColorParam.value = [1, 0, 0, 1];
+
+ // Create the Shape for the cube mesh and assign its material.
+ var cubeShape = createCube(cubeMaterial);
+
+ // Create a new transform and parent the Shape under it.
+ g_cubeTransform = g_pack.createObject('Transform');
+ g_cubeTransform.addShape(cubeShape);
+
+ // Parent the cube's transform to the client root.
+ g_cubeTransform.parent = g_client.root;
+
+ // Set our render callback for animation.
+ // This sets a function to be executed every time a frame is rendered.
+ g_client.setRenderCallback(renderCallback);
+
+ g_finished = true; // for selenium testing.
+}
+
+/**
+ * Removes any callbacks so they don't get called after the page has unloaded.
+ */
+function uninit() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+
+</script>
+</head>
+<body>
+<h1>Hello Square: Colors</h1>
+This example shows how to use parameters to the color output of a shader.
+<br/>
+
+
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 600px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+
+<form name="default_form" action="#" method="get">
+ <p>
+ Change color:
+ <input type="button" value="Red" onclick="changeColor(1,0,0);" />
+ <input type="button" value="Green" onclick="changeColor(0,1,0);" />
+ <input type="button" value="Blue" onclick="changeColor(0,0,1);" />
+ <input type="button" value="Yellow" onclick="changeColor(1,1,0);" />
+ <input type="button" value="Cyan" onclick="changeColor(0,1,1);" />
+ <input type="button" value="Purple" onclick="changeColor(1,0,1);" />
+ <input type="button" value="White" onclick="changeColor(1,1,1);" />
+ </p>
+</form>
+
+<!-- Don't render the textarea -->
+<div style="display:none">
+<!-- Start of effect -->
+<textarea id="effect">
+ // World View Projection matrix that will transform the input vertices
+ // to screen space.
+ float4x4 worldViewProjection : WorldViewProjection;
+
+ // This specifies the color of the triangles. A Param with the same name will
+ // be created on the material using the shader.
+ float4 color;
+
+ // input parameters for our vertex shader
+ struct VertexShaderInput {
+ float4 position : POSITION;
+ };
+
+ // input parameters for our pixel shader
+ struct PixelShaderInput {
+ float4 position : POSITION;
+ };
+
+ /**
+ * The vertex shader simply transforms the input vertices to screen space.
+ */
+ PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ PixelShaderInput output;
+
+ // Multiply the vertex positions by the worldViewProjection matrix to
+ // transform them to screen space.
+ output.position = mul(input.position, worldViewProjection);
+ return output;
+ }
+
+ /**
+ * Our pixel shader returns the value of color parameter.
+ */
+ float4 pixelShaderFunction(PixelShaderInput input): COLOR {
+ return color;
+ }
+
+ // Here we tell our effect file *which* functions are
+ // our vertex and pixel shaders.
+
+ // #o3d VertexShaderEntryPoint vertexShaderFunction
+ // #o3d PixelShaderEntryPoint pixelShaderFunction
+ // #o3d MatrixLoadOrder RowMajor
+</textarea>
+<!-- End of effect -->
+</div>
+</body>
+</html>
diff --git a/o3d/samples/hellocube-textures.html b/o3d/samples/hellocube-textures.html
new file mode 100644
index 0000000..16522b2
--- /dev/null
+++ b/o3d/samples/hellocube-textures.html
@@ -0,0 +1,445 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+This sample creates an O3D area with a textured cube in the middle. The
+user can specify the URL where the texture image will be picked from.
+This sample is a simple demonstration of texture usage in O3D.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Hello Square Textures: Getting started with O3D, take 3.
+</title>
+<script type="text/javascript" src="o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.io');
+
+// Events
+// Run the init() function once the page has finished loading.
+// Run the uninit() function when the page is unloaded.
+window.onload = init;
+window.onunload = uninit;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_pack;
+var g_client;
+var g_cubeTransform;
+var g_sampler;
+var g_clock = 0;
+var g_timeMult = 1;
+var g_finished = false; // for selenium testing
+var g_textureLoadDenied = false; // also for selenium testing
+
+/**
+ * Creates an O3D shape representing a cube.
+ * @param {o3d.Material} material the material used by the primitive.
+ * @return {o3d.Shape} The Shape object created.
+ */
+function createCube(material) {
+ // Create a Shape object for the mesh.
+ var cubeShape = g_pack.createObject('Shape');
+
+ // Create the Primitive that will contain the geometry data for
+ // the cube.
+ var cubePrimitive = g_pack.createObject('Primitive');
+
+ // Create a StreamBank to hold the streams of vertex data.
+ var streamBank = g_pack.createObject('StreamBank');
+
+ // Assign the material that was passed in to the primitive.
+ cubePrimitive.material = material;
+
+ // Assign the Primitive to the Shape.
+ cubePrimitive.owner = cubeShape;
+
+ // Assign the StreamBank to the Primitive.
+ cubePrimitive.streamBank = streamBank;
+
+ // The cube is made of 12 triangles (6 faces x 2 triangles per face)
+ cubePrimitive.primitiveType = g_o3d.Primitive.TRIANGLELIST;
+ cubePrimitive.numberPrimitives = 12; // 12 triangles
+
+ // Vertices used by each triangle must specify both a position and texture
+ // coordinates. We cannot share vertices between adjacent cube faces since
+ // while their positions are the same, their texture coordinates are
+ // different. We therefore create 24 vertices, 4 for each of the cube's
+ // six faces.
+ cubePrimitive.numberVertices = 24;
+
+ // Generate the draw element for the cube primitive.
+ cubePrimitive.createDrawElement(g_pack, null);
+
+ // Create a javascript array that stores the X, Y and Z coordinates of each
+ // of the 24 vertices used by the cube.
+ var positionArray = [
+ -0.5, -0.5, 0.5,
+ 0.5, -0.5, 0.5,
+ 0.5, 0.5, 0.5,
+ -0.5, 0.5, 0.5,
+ -0.5, 0.5, 0.5,
+ 0.5, 0.5, 0.5,
+ 0.5, 0.5, -0.5,
+ -0.5, 0.5, -0.5,
+ -0.5, 0.5, -0.5,
+ 0.5, 0.5, -0.5,
+ 0.5, -0.5, -0.5,
+ -0.5, -0.5, -0.5,
+ -0.5, -0.5, -0.5,
+ 0.5, -0.5, -0.5,
+ 0.5, -0.5, 0.5,
+ -0.5, -0.5, 0.5,
+ 0.5, -0.5, 0.5,
+ 0.5, -0.5, -0.5,
+ 0.5, 0.5, -0.5,
+ 0.5, 0.5, 0.5,
+ -0.5, -0.5, -0.5,
+ -0.5, -0.5, 0.5,
+ -0.5, 0.5, 0.5,
+ -0.5, 0.5, -0.5
+ ];
+
+ // The following array stores the texture coordinates (u, v) for each vertex.
+ // These coordinates are used by the shader when displaying the texture image
+ // on the mesh triangles.
+ var texCoordsArray = [
+ 0, 0,
+ 1, 0,
+ 1, 1,
+ 0, 1,
+ 0, 0,
+ 1, 0,
+ 1, 1,
+ 0, 1,
+ 1, 1,
+ 0, 1,
+ 0, 0,
+ 1, 0,
+ 0, 0,
+ 1, 0,
+ 1, 1,
+ 0, 1,
+ 0, 0,
+ 1, 0,
+ 1, 1,
+ 0, 1,
+ 0, 0,
+ 1, 0,
+ 1, 1,
+ 0, 1
+ ];
+
+ // The following array defines how vertices are to be put together to form
+ // the triangles that make up the cube's faces. In the index array, every
+ // three elements define a triangle. So for example vertices 0, 1 and 2
+ // make up the first triangle, vertices 0, 2 and 3 the second one, etc.
+ var indicesArray = [
+ 0, 1, 2,
+ 0, 2, 3,
+ 4, 5, 6,
+ 4, 6, 7,
+ 8, 9, 10,
+ 8, 10, 11,
+ 12, 13, 14,
+ 12, 14, 15,
+ 16, 17, 18,
+ 16, 18, 19,
+ 20, 21, 22,
+ 20, 22, 23
+ ];
+
+ // Create buffers containing the vertex data.
+ var positionsBuffer = g_pack.createObject('VertexBuffer');
+ var positionsField = positionsBuffer.createField('FloatField', 3);
+ positionsBuffer.set(positionArray);
+
+ var texCoordsBuffer = g_pack.createObject('VertexBuffer');
+ var texCoordsField = texCoordsBuffer.createField('FloatField', 2);
+ texCoordsBuffer.set(texCoordsArray);
+
+ var indexBuffer = g_pack.createObject('IndexBuffer');
+ indexBuffer.set(indicesArray);
+
+ // Associate the positions buffer with the StreamBank.
+ streamBank.setVertexStream(
+ g_o3d.Stream.POSITION, // semantic: This stream stores vertex positions
+ 0, // semantic index: First (and only) position stream
+ positionsField, // field: the field this stream uses.
+ 0); // start_index: How many elements to skip in the
+ // field.
+
+ // Associate the texture coordinates buffer with the primitive.
+ streamBank.setVertexStream(
+ g_o3d.Stream.TEXCOORD, // semantic
+ 0, // semantic index
+ texCoordsField, // field
+ 0); // start_index
+
+ // Associate the triangle indices Buffer with the primitive.
+ cubePrimitive.indexBuffer = indexBuffer;
+
+ return cubeShape;
+}
+
+/**
+ * This method gets called every time O3D renders a frame. Here's where
+ * we update the cube's transform to make it spin.
+ * @param {o3d.RenderEvent} renderEvent The render event object that gives
+ * us the elapsed time since the last time a frame was rendered.
+ */
+function renderCallback(renderEvent) {
+ g_clock += renderEvent.elapsedTime * g_timeMult;
+ // Rotate the cube around the Y axis.
+ g_cubeTransform.identity();
+ g_cubeTransform.rotateY(2.0 * g_clock);
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D, creates the quad and sets up the transform and
+ * render graphs.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Set the texture URL.
+ var path = window.location.href;
+ var index = path.lastIndexOf('/');
+ path = path.substring(0, index+1) + 'assets/texture_b3.jpg';
+ var url = document.getElementById("url").value = path;
+
+ // Initialize global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_client = o3dElement.client;
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+
+ // Create a pack to manage the objects created.
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ var viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Set up a perspective projection.
+ viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(30), // 30 degree fov.
+ g_client.width / g_client.height,
+ 1, // Near plane.
+ 5000); // Far plane.
+
+ // Set up our view transformation to look towards the world origin where the
+ // cube is located.
+ viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [0, 1, 5], // eye
+ [0, 0, 0], // target
+ [0, 1, 0]); // up
+
+ // Create an Effect object and initialize it using the shaders from the
+ // text area.
+ var cubeEffect = g_pack.createObject('Effect');
+ var shaderString = document.getElementById('effect').value;
+ cubeEffect.loadFromFXString(shaderString);
+
+ // Create a Material for the mesh.
+ var cubeMaterial = g_pack.createObject('Material');
+
+ // Set the material's drawList.
+ cubeMaterial.drawList = viewInfo.performanceDrawList;
+
+ // Apply our effect to this material. The effect tells the 3D hardware
+ // which shaders to use.
+ cubeMaterial.effect = cubeEffect;
+
+ // Create an O3D Param on the material for every uniform used by the
+ // shader.
+ cubeEffect.createUniformParameters(cubeMaterial);
+
+ // Get the material's sampler parameter so that we can set the texture value
+ // to it.
+ var samplerParam = cubeMaterial.getParam('texSampler0');
+
+ // Create a Sampler object and set the min filtering to ANISOTROPIC. This
+ // will improve the quality of the rendered texture when viewed at an angle.
+ g_sampler = g_pack.createObject('Sampler');
+ g_sampler.minFilter = g_o3d.Sampler.ANISOTROPIC;
+ g_sampler.maxAnisotropy = 4;
+ samplerParam.value = g_sampler;
+
+ // Create the Shape for the cube mesh and assign its material.
+ var cubeShape = createCube(cubeMaterial);
+
+ // Create a new transform and parent the Shape under it.
+ g_cubeTransform = g_pack.createObject('Transform');
+ g_cubeTransform.addShape(cubeShape);
+
+ // Note that we don't parent the transform until the texture is
+ // succesfully loaded because we don't want the system
+ // to try the draw the shape without its required texture.
+
+ // Set our render callback for animation.
+ // This sets a function to be executed every time a frame is rendered.
+ g_client.setRenderCallback(renderCallback);
+
+ // Set the initial texture.
+ changeTexture();
+}
+
+/**
+ * Fetches the bitmap pointed to by the URL supplied by the user, creates
+ * an O3D Texture object with it updates the Sampler used by the material
+ * to point to the newly created texture.
+ */
+function changeTexture() {
+ var textureUrl = document.getElementById('url').value;
+ o3djs.io.loadTexture(g_pack, textureUrl, function(texture, exception) {
+ // Remove the currently used texture from the pack so that when it's not
+ // referenced anymore, it can get destroyed.
+ if (g_sampler.texture)
+ g_pack.removeObject(g_sampler.texture);
+
+ // Because the loading is asynchronous an exception is not thrown but is
+ // instead passed on failure.
+ if (exception) {
+ g_sampler.texture = null;
+
+ g_textureLoadDenied = true; // for selenium testing.
+ } else {
+ // Set the texture on the sampler object to the newly created texture
+ // object returned by the request.
+ g_sampler.texture = texture;
+
+ // We can now safely add the cube transform to the root of the
+ // scenegraph since it now has a valid texture. If the transform
+ // is already parented under the root, the call will have no effect.
+ g_cubeTransform.parent = g_client.root;
+
+ g_finished = true; // for selenium testing.
+ }
+ });
+}
+
+/**
+ * Removes any callbacks so they don't get called after the page has unloaded.
+ */
+function uninit() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+
+</script>
+</head>
+<body>
+<h1>Hello Cube: Textures</h1>
+This example shows how texture map a cube using an image fetched from a URL.
+<br/>
+
+
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 600px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+<br />
+Image URL: <input type="text" id="url" size="100">
+<input type="button" id="updateButton" onclick="changeTexture();" value="Update Texture"><BR>
+
+<!-- Don't render the textarea -->
+<div style="display:none">
+<!-- Start of effect -->
+<textarea id="effect">
+ // World View Projection matrix that will transform the input vertices
+ // to screen space.
+ float4x4 worldViewProjection : WorldViewProjection;
+
+ // The texture sampler is used to access the texture bitmap in the fragment
+ // shader.
+ sampler texSampler0;
+
+ // input parameters for our vertex shader
+ struct VertexShaderInput {
+ float4 position : POSITION;
+ float2 tex : TEXCOORD0; // Texture coordinates
+ };
+
+ // input parameters for our pixel shader
+ struct PixelShaderInput {
+ float4 position : POSITION;
+ float2 tex : TEXCOORD0; // Texture coordinates
+ };
+
+ /**
+ * The vertex shader simply transforms the input vertices to screen space.
+ */
+ PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ PixelShaderInput output;
+
+ // Multiply the vertex positions by the worldViewProjection matrix to
+ // transform them to screen space.
+ output.position = mul(input.position, worldViewProjection);
+
+ output.tex = input.tex;
+ return output;
+ }
+
+ /**
+ * Given the texture coordinates, our pixel shader grabs the corresponding
+ * color from the texture.
+ */
+ float4 pixelShaderFunction(PixelShaderInput input): COLOR {
+ return tex2D(texSampler0, input.tex);
+ }
+
+ // Here we tell our effect file *which* functions are
+ // our vertex and pixel shaders.
+
+ // #o3d VertexShaderEntryPoint vertexShaderFunction
+ // #o3d PixelShaderEntryPoint pixelShaderFunction
+ // #o3d MatrixLoadOrder RowMajor
+</textarea>
+<!-- End of effect -->
+</div>
+</body>
+</html>
diff --git a/o3d/samples/hellocube.html b/o3d/samples/hellocube.html
new file mode 100644
index 0000000..4ad4eb4
--- /dev/null
+++ b/o3d/samples/hellocube.html
@@ -0,0 +1,312 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+This sample shows how to place an O3D area in a page and draw simple
+3D shape in it.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Hello Cube: Getting started with O3D
+</title>
+<script type="text/javascript" src="o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+
+// Events
+// Run the init() function once the page has finished loading.
+// Run the uninit() function when the page has is unloaded.
+window.onload = init;
+window.onunload = uninit;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_pack;
+var g_clock = 0;
+var g_timeMult = 1;
+var g_cubeTransform;
+var g_finished = false; // for selenium testing
+
+/**
+ * Creates an O3D shape representing a cube. The shape consists of
+ * a single primitive with eight vertices and 12 triangles (two for each face
+ * of the cube).
+ * @param {o3d.Material} material the material used by the primitive.
+ * @return {o3d.Shape} The Shape object created.
+ */
+function createCube(material) {
+ // Create a Shape object for the mesh.
+ var cubeShape = g_pack.createObject('Shape');
+
+ // Create the Primitive that will contain the geometry data for
+ // the cube.
+ var cubePrimitive = g_pack.createObject('Primitive');
+
+ // Create a StreamBank to hold the streams of vertex data.
+ var streamBank = g_pack.createObject('StreamBank');
+
+ // Assign the material that was passed in to the primitive.
+ cubePrimitive.material = material;
+
+ // Assign the Primitive to the Shape.
+ cubePrimitive.owner = cubeShape;
+
+ // Assign the StreamBank to the Primitive.
+ cubePrimitive.streamBank = streamBank;
+
+ // The cube is made of 12 triangles. There's eight vertices in total which
+ // are shared between the face triangles.
+ cubePrimitive.primitiveType = g_o3d.Primitive.TRIANGLELIST;
+ cubePrimitive.numberPrimitives = 12; // 12 triangles
+ cubePrimitive.numberVertices = 8; // 8 vertices in total
+
+ // Generate the draw element for the cube primitive.
+ cubePrimitive.createDrawElement(g_pack, null);
+
+ // Create a javascript array that stores the X, Y and Z coordinates of each
+ // of the 8 corners of the cube.
+ var positionArray = [
+ -0.5, -0.5, 0.5, // vertex 0
+ 0.5, -0.5, 0.5, // vertex 1
+ -0.5, 0.5, 0.5, // vertex 2
+ 0.5, 0.5, 0.5, // vertex 3
+ -0.5, 0.5, -0.5, // vertex 4
+ 0.5, 0.5, -0.5, // vertex 5
+ -0.5, -0.5, -0.5, // vertex 6
+ 0.5, -0.5, -0.5 // vertex 7
+ ];
+
+ // The following array defines how vertices are to be put together to form
+ // the triangles that make up the cube's faces. In the index array, every
+ // three elements define a triangle. So for example vertices 0, 1 and 2
+ // make up the first triangle, vertices 2, 1 and 3 the second one, etc.
+ var indicesArray = [
+ 0, 1, 2, // face 1
+ 2, 1, 3,
+ 2, 3, 4, // face 2
+ 4, 3, 5,
+ 4, 5, 6, // face 3
+ 6, 5, 7,
+ 6, 7, 0, // face 4
+ 0, 7, 1,
+ 1, 7, 3, // face 5
+ 3, 7, 5,
+ 6, 0, 4, // face 6
+ 4, 0, 2
+ ];
+
+ // Create buffers containing the vertex data.
+ var positionsBuffer = g_pack.createObject('VertexBuffer');
+ var positionsField = positionsBuffer.createField('FloatField', 3);
+ positionsBuffer.set(positionArray);
+
+ var indexBuffer = g_pack.createObject('IndexBuffer');
+ indexBuffer.set(indicesArray);
+
+ // Associate the positions Buffer with the StreamBank.
+ streamBank.setVertexStream(
+ g_o3d.Stream.POSITION, // semantic: This stream stores vertex positions
+ 0, // semantic index: First (and only) position stream
+ positionsField, // field: the field this stream uses.
+ 0); // start_index: How many elements to skip in the
+ // field.
+
+ // Associate the triangle indices Buffer with the primitive.
+ cubePrimitive.indexBuffer = indexBuffer;
+
+ return cubeShape;
+}
+
+/**
+ * This method gets called every time O3D renders a frame. Here's where
+ * we update the cube's transform to make it spin.
+ * @param {o3d.RenderEvent} renderEvent The render event object that gives
+ * us the elapsed time since the last time a frame was rendered.
+ */
+function renderCallback(renderEvent) {
+ g_clock += renderEvent.elapsedTime * g_timeMult;
+ // Rotate the cube around the Y axis.
+ g_cubeTransform.identity();
+ g_cubeTransform.rotateY(2.0 * g_clock);
+}
+
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D, creates the cube and sets up the transform and
+ * render graphs.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_client = o3dElement.client;
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+
+ // Create a pack to manage the objects created.
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ var viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Set up a perspective projection.
+ viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(30), // 30 degree fov.
+ g_client.width / g_client.height,
+ 1, // Near plane.
+ 5000); // Far plane.
+
+ // Set up our view transformation to look towards the world origin where the
+ // cube is located.
+ viewInfo.drawContext.view = g_math.matrix4.lookAt([0, 1, 5], // eye
+ [0, 0, 0], // target
+ [0, 1, 0]); // up
+
+ // Create an Effect object and initialize it using the shaders from the
+ // text area.
+ var redEffect = g_pack.createObject('Effect');
+ var shaderString = document.getElementById('effect').value;
+ redEffect.loadFromFXString(shaderString);
+
+ // Create a Material for the mesh.
+ var redMaterial = g_pack.createObject('Material');
+
+ // Set the material's drawList.
+ redMaterial.drawList = viewInfo.performanceDrawList;
+
+ // Apply our effect to this material. The effect tells the 3D hardware
+ // which shaders to use.
+ redMaterial.effect = redEffect;
+
+ // Create the Shape for the cube mesh and assign its material.
+ var cubeShape = createCube(redMaterial);
+
+ // Create a new transform and parent the Shape under it.
+ g_cubeTransform = g_pack.createObject('Transform');
+ g_cubeTransform.addShape(cubeShape);
+
+ // Parent the cube's transform to the client root.
+ g_cubeTransform.parent = g_client.root;
+
+ // Set our render callback for animation.
+ // This sets a function to be executed every time a frame is rendered.
+ g_client.setRenderCallback(renderCallback);
+
+ g_finished = true; // for selenium testing.
+}
+
+/**
+ * Removes any callbacks so they don't get called after the page has unloaded.
+ */
+function uninit() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+
+</script>
+</head>
+<body>
+<h1>Hello Cube</h1>
+This example shows how to display a spinning red cube in O3D.
+<br/>
+
+
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 600px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+
+<!-- Don't render the textarea -->
+<div style="display:none">
+<!-- Start of effect -->
+<textarea id="effect">
+ // World View Projection matrix that will transform the input vertices
+ // to screen space.
+ float4x4 worldViewProjection : WorldViewProjection;
+
+ // input parameters for our vertex shader
+ struct VertexShaderInput {
+ float4 position : POSITION;
+ };
+
+ // input parameters for our pixel shader
+ struct PixelShaderInput {
+ float4 position : POSITION;
+ };
+
+ /**
+ * The vertex shader simply transforms the input vertices to screen space.
+ */
+ PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ PixelShaderInput output;
+
+ // Multiply the vertex positions by the worldViewProjection matrix to
+ // transform them to screen space.
+ output.position = mul(input.position, worldViewProjection);
+ return output;
+ }
+
+ /**
+ * This pixel shader just returns the color red.
+ */
+ float4 pixelShaderFunction(PixelShaderInput input): COLOR {
+ return float4(1, 0, 0, 1); // Red.
+ }
+
+ // Here we tell our effect file *which* functions are
+ // our vertex and pixel shaders.
+
+ // #o3d VertexShaderEntryPoint vertexShaderFunction
+ // #o3d PixelShaderEntryPoint pixelShaderFunction
+ // #o3d MatrixLoadOrder RowMajor
+</textarea>
+<!-- End of effect -->
+</div>
+</body>
+</html>
diff --git a/o3d/samples/helloworld.html b/o3d/samples/helloworld.html
new file mode 100644
index 0000000..f5ed748
--- /dev/null
+++ b/o3d/samples/helloworld.html
@@ -0,0 +1,160 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+O3D Tutorial A1
+
+In this tutorial, we load and display a scene in O3D.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Tutorial A1: Loading a scene
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.camera');
+o3djs.require('o3djs.pack');
+o3djs.require('o3djs.scene');
+
+// Events
+// init() once the page has finished loading.
+window.onload = init;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_viewInfo;
+var g_pack;
+var g_finished = false; // for selenium testing
+
+/**
+ * Loads a scene into the transform graph.
+ * @param {!o3d.Pack} pack Pack to load scene into.
+ * @param {string} fileName filename of the scene.
+ * @param {!o3d.Transform} parent parent node in the transform graph to
+ * which to load the scene into.
+ */
+function loadScene(pack, fileName, parent) {
+ // Get our full path to the scene
+ var scenePath = o3djs.util.getCurrentURI() + fileName;
+
+ // Load the file given the full path, and call the callback function
+ // when its done loading.
+ o3djs.scene.loadScene(g_client, pack, parent, scenePath, callback);
+
+ /**
+ * Our callback is called once the scene has been loaded into memory
+ * from the web or locally.
+ * @param {!o3d.Pack} pack The pack that was passed in above.
+ * @param {!o3d.Transform} parent The parent that was passed in above.
+ * @param {*} exception null if loading succeeded.
+ */
+ function callback(pack, parent, exception) {
+ if (exception) {
+ alert('Could not load: ' + fileName + '\n' + exception);
+ return;
+ }
+ // Get a CameraInfo (an object with a view and projection matrix)
+ // using our javascript library function
+ var cameraInfo = o3djs.camera.getViewAndProjectionFromCameras(
+ parent,
+ g_client.width,
+ g_client.height);
+
+ // Copy the view and projection to the draw context.
+ g_viewInfo.drawContext.view = cameraInfo.view;
+ g_viewInfo.drawContext.projection = cameraInfo.projection;
+
+ // Generate draw elements and setup material draw lists.
+ o3djs.pack.preparePack(pack, g_viewInfo);
+
+ g_finished = true; // for selenium testing.
+ }
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and loads the scene into the transform graph.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = o3dElement.client;
+
+ // Creates a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Creates a transform to put our data on.
+ var myDataRoot = g_pack.createObject('Transform');
+
+ // Connects our root to the client root.
+ myDataRoot.parent = g_client.root;
+
+ // Load the scene into the transform graph as a child myDataRoot
+ loadScene(g_pack, 'assets/teapot.o3dtgz', myDataRoot);
+}
+</script>
+</head>
+<body>
+<h1>Loading a scene.</h1>
+This tutorial shows how we load and display a scene in O3D.
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 600px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/samples/home-configurators/assets/empty.txt b/o3d/samples/home-configurators/assets/empty.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/o3d/samples/home-configurators/assets/empty.txt
diff --git a/o3d/samples/home-configurators/cb_images/cb_item_thumbnails.jpg b/o3d/samples/home-configurators/cb_images/cb_item_thumbnails.jpg
new file mode 100644
index 0000000..65a5473
--- /dev/null
+++ b/o3d/samples/home-configurators/cb_images/cb_item_thumbnails.jpg
Binary files differ
diff --git a/o3d/samples/home-configurators/cb_images/toolselector.gif b/o3d/samples/home-configurators/cb_images/toolselector.gif
new file mode 100644
index 0000000..f4d1722
--- /dev/null
+++ b/o3d/samples/home-configurators/cb_images/toolselector.gif
Binary files differ
diff --git a/o3d/samples/home-configurators/cb_images/unbranded_bg.png b/o3d/samples/home-configurators/cb_images/unbranded_bg.png
new file mode 100644
index 0000000..94e2747
--- /dev/null
+++ b/o3d/samples/home-configurators/cb_images/unbranded_bg.png
Binary files differ
diff --git a/o3d/samples/home-configurators/cbassets/empty.txt b/o3d/samples/home-configurators/cbassets/empty.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/o3d/samples/home-configurators/cbassets/empty.txt
diff --git a/o3d/samples/home-configurators/craftsmanassets/craftsman_item_thumbnails.jpg b/o3d/samples/home-configurators/craftsmanassets/craftsman_item_thumbnails.jpg
new file mode 100644
index 0000000..212a01c
--- /dev/null
+++ b/o3d/samples/home-configurators/craftsmanassets/craftsman_item_thumbnails.jpg
Binary files differ
diff --git a/o3d/samples/home-configurators/deletetool.js b/o3d/samples/home-configurators/deletetool.js
new file mode 100644
index 0000000..616303bb
--- /dev/null
+++ b/o3d/samples/home-configurators/deletetool.js
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * A tool to move components around in the scene. Will constrain movement to
+ * the ground plane.
+ *
+ * Requires the picking Client 3d Utilities API.
+ */
+function DeleteTool(context, root) {
+ this.context = context;
+ this.root = root;
+ this.transformInfo = o3djs.picking.createTransformInfo(root, null);
+ this.selectedObject = null;
+}
+
+DeleteTool.prototype.getPickedObject = function(e) {
+ var worldRay = o3djs.picking.clientPositionToWorldRay(e.x,
+ e.y,
+ this.context,
+ g_client.width,
+ g_client.height);
+ this.transformInfo.update();
+ var pickInfo = this.transformInfo.pick(worldRay);
+ var picked_object_root = null;
+ if (pickInfo) {
+ pickedObject = pickInfo.shapeInfo.parent;
+ while (pickedObject.parent != null &&
+ pickedObject.parent.transform != this.root) {
+ pickedObject = pickedObject.parent;
+ }
+ picked_object_root = pickedObject.transform;
+ }
+ return picked_object_root;
+};
+
+DeleteTool.prototype.handleMouseDown = function(e) {
+ this.selectedObject = this.getPickedObject(e);
+};
+
+DeleteTool.prototype.handleMouseUp = function(e) {
+ if (this.selectedObject != null &&
+ this.selectedObject == this.getPickedObject(e)) {
+ // Delete the object if the user clicked down and up on the same object.
+ this.selectedObject.parent = null;
+ }
+ this.selectedObject = null;
+};
+
+DeleteTool.prototype.handleMouseMove = function(e) {
+};
+
+DeleteTool.prototype.handleKeyDown = function(e) {
+ return false;
+};
+
+DeleteTool.prototype.handleKeyUp = function(e) {
+ return false;
+};
+
diff --git a/o3d/samples/home-configurators/homedesigner.html b/o3d/samples/home-configurators/homedesigner.html
new file mode 100644
index 0000000..ce7e2bf
--- /dev/null
+++ b/o3d/samples/home-configurators/homedesigner.html
@@ -0,0 +1,171 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<html>
+<title>
+ O3D Home Designer Demo
+</title>
+<style type="text/css">
+BODY {
+ background-image: url(cb_images/unbranded_bg.png);
+ background-position: top center;
+ background-repeat: no-repeat;
+ background-color: white;
+}
+
+#intro {
+ font-family: sans-serif;
+ color: 666666;
+ font-size: 12px;
+ padding: 12px 8px 0px 8px;
+}
+
+#toolpanel {
+ width: 400px;
+ height: 36px;
+ padding-top: 1px;
+}
+
+#toolselector {
+ width: 38px;
+ height: 38px;
+ background-image: url(cb_images/toolselector.gif);
+ position: relative;
+ left: 9px;
+ border: 0px;
+}
+
+.catalogItem {
+ -moz-user-select:none;
+ cursor: pointer;
+ width: 62px;
+ height: 62px;
+ border: 1px solid silver;
+ margin: 1px;
+ float: left;
+ background-image: url(cb_images/cb_item_thumbnails.jpg);
+}
+
+#itemlist {
+ padding-left: 8px;
+
+}
+
+</style>
+ <script type="text/javascript" src="../o3djs/base.js"></script>
+ <script type="text/javascript" src="viewer.js"></script>
+ <script type="text/javascript" src="rotatetool.js"></script>
+ <script type="text/javascript" src="movetool.js"></script>
+ <script type="text/javascript" src="orbittool.js"></script>
+ <script type="text/javascript" src="pantool.js"></script>
+ <script type="text/javascript" src="zoomtool.js"></script>
+ <script type="text/javascript" src="deletetool.js"></script>
+
+<script type="text/javascript">
+/**
+ * A structure to store our list of catalog items that can be dragged in.
+ * If the url doesn't start with 'http:' then we assume it's a local
+ * file to be loaded from the /assets subdirectory.
+ */
+g_items = [
+ { url: 'Agra_Rug.o3dtgz', cost: 100, title: 'Agra Rug' },
+ { url: 'Asimi_Rug.o3dtgz', cost: 100, title: 'Asimi Rug' },
+ { url: 'Camden_Chair.o3dtgz', cost: 100, title: 'Camden Chair' },
+ { url: 'Elements_Bookshelf.o3dtgz', cost: 100, title: 'Elements Bookshelf' },
+ { url: 'Ferrara_Rug.o3dtgz', cost: 100, title: 'Ferrara Rug' },
+ { url: 'Lounge_Chair.o3dtgz', cost: 100, title: 'Lounge Chair' },
+ { url: 'Lounge_Chaise.o3dtgz', cost: 100, title: 'Lounge Chaise' },
+ { url: 'Lounge_Sofa.o3dtgz', cost: 100, title: 'Lounge Sofa' },
+ { url: 'Lounge_Storage_Ottoman.o3dtgz', cost: 100,
+ title: 'Lounge_Storage_Ottoman' },
+ { url: 'Madison_Dining_Table.o3dtgz', cost: 100, title: 'Madison Dining Table' },
+ { url: 'Miles_Side_Chair.o3dtgz', cost: 100, title: 'Miles Side Chair' },
+ { url: 'Pullman_Bar_Stool.o3dtgz', cost: 100, title: 'Pullman Bar Stool' },
+ { url: 'Puzzle_TV_Stand.o3dtgz', cost: 100, title: 'Puzzle TV Stand' },
+ { url: 'Stow_Leather_Ottoman.o3dtgz', cost: 100, title: 'Stow Leather Ottoman' },
+ { url: 'Tivoli_Dining_Table.o3dtgz', cost: 100, title: 'Tivoli Dining Table' },
+ { url: 'Tivoli_Miles_Dining_Set.o3dtgz', cost: 100,
+ title: 'Tivoli_Miles_Dining_Set' },
+ { url: 'Troy_Chair.o3dtgz', cost: 100, title: 'Troy Chair' },
+ { url: 'Troy_Ottoman.o3dtgz', cost: 100, title: 'Troy Ottoman' },
+ { url: 'Troy_Sofa.o3dtgz', cost: 100, title: 'Troy Sofa' },
+ { url: 'Troy_Storage_Ottoman.o3dtgz', cost: 100, title: 'Troy Storage Ottoman' },
+ { url: 'Troy_Twin_Sleeper.o3dtgz', cost: 100, title: 'Troy Twin Sleeper' }
+];
+
+var g_assetPath = '/cbassets/';
+var g_buildingAsset = '/cbassets/House_Roofless.o3dtgz';
+
+</script>
+<body onload="init();" onresize="resize();">
+
+<div style="position:absolute;top:104px;left:0px;text-align:center;width:100%">
+
+
+<table style="width:900px;" align="center"><tr>
+ <td width="100%" valign="top">
+
+ <table id="container" width="676" height="545" border="0"><tr><td>
+ <div id="o3d" style="width:100%; height:100%"></div>
+ </td></tr></table>
+ <div id="toolpanel"><div id="toolselector"></div></div>
+ </td>
+ <td valign="top" style="padding-right:8px">
+ <div id="contentpanel" style="width:222px;height:365px">
+ <div id="intro">
+ <span style="font-size: 18px">Most Popular Items</span>
+ <p>Drag and drop objects into the
+ <br>rooms on the left.
+ </div>
+ <div id="itemlist">
+ </div>
+
+
+ </div><br>
+ </td>
+</tr></table>
+</div>
+
+<!-- hide the old UI -->
+<div style="display:none">
+ <input type="text" id="url" size="100">
+ <input type="button" onclick="doload();" value="load"><BR>
+ <div id="output">
+ </div>
+ <div style="display:none">
+
+ </div>
+</div>
+
+</body>
+</html>
+
+
diff --git a/o3d/samples/home-configurators/movetool.js b/o3d/samples/home-configurators/movetool.js
new file mode 100644
index 0000000..d0db043
--- /dev/null
+++ b/o3d/samples/home-configurators/movetool.js
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * A tool to move components around in the scene. Will constrain movement to
+ * the ground plane.
+ *
+ * Requires the picking Client 3d Utilities API.
+ */
+function MoveTool(context, root) {
+ this.lastMove = null;
+ this.context = context;
+ this.root = root;
+ this.transformInfo = o3djs.picking.createTransformInfo(root, null);
+ this.movePlane = [0.0, 0.0, 1.0];
+ this.downPoint = null;
+ this.selectedObject = null;
+}
+
+/*
+ * transform_node : The shape's root node that should be moving.
+ * (the same node as would be found by our picking code)
+ */
+MoveTool.prototype.initializeWithShape = function(transform_node) {
+ this.transformInfo.update();
+ translation = g_math.matrix4.getTranslation(
+ transform_node.getUpdatedWorldMatrix());
+ this.downPoint = translation;
+ this.selectedObject = transform_node;
+ this.lastMove = null;
+};
+
+MoveTool.prototype.handleMouseDown = function(e) {
+ var worldRay = o3djs.picking.clientPositionToWorldRay(e.x,
+ e.y,
+ this.context,
+ g_client.width,
+ g_client.height);
+ this.transformInfo.update();
+ this.selectedObject = null;
+ var pickInfo = this.transformInfo.pick(worldRay);
+ if (pickInfo) {
+ pickedObject = pickInfo.shapeInfo.parent;
+ while (pickedObject.parent != null &&
+ pickedObject.parent.transform != this.root) {
+ pickedObject = pickedObject.parent;
+ }
+ this.selectedObject = pickedObject.transform;
+ }
+ if (this.selectedObject) {
+ this.downPoint = pickInfo.worldIntersectionPosition.slice(0, 3);
+ this.lastMove = null;
+ }
+};
+
+MoveTool.prototype.handleMouseUp = function(e) {
+ this.downPoint = null;
+};
+
+MoveTool.prototype.handleMouseMove = function(e) {
+ if (this.downPoint == null)
+ return;
+
+ if (this.selectedObject === null)
+ return;
+
+ var offset;
+
+ if (e.x !== undefined) {
+ // An O3D event
+ offset = { x: e.x, y: e.y };
+ } else {
+ // A JS event
+ e = e ? e : window.event;
+ elem = e.target ? e.target : e.srcElement;
+
+ // Check if the cursor is in the O3D area.
+ if (elem !== g_o3dElement) {
+ return;
+ }
+ offset = getRelativeCoordinates(e, elem);
+ }
+
+ var worldRay = o3djs.picking.clientPositionToWorldRay(offset.x,
+ offset.y,
+ this.context,
+ g_client.width,
+ g_client.height);
+ // Compute the offset from the start point.
+ var ray_dir = g_math.subVector(worldRay.far, worldRay.near);
+ ray_dir = g_math.normalize(ray_dir);
+ var ray_dot_plane = g_math.dot(ray_dir, this.movePlane);
+ if (Math.abs(ray_dot_plane) < 0.001) {
+ return [0.0, 0.0, 0.0];
+ }
+ var plane_distance =
+ g_math.dot(this.downPoint, this.movePlane) -
+ g_math.dot(worldRay.near, this.movePlane);
+ t = plane_distance / ray_dot_plane;
+ move_position = g_math.addVector(
+ g_math.mulVectorScalar(ray_dir, t), worldRay.near);
+ var offset = g_math.subVector(move_position, this.downPoint);
+ translation = offset;
+ if (this.lastMove != null) {
+ translation = g_math.subVector(offset, this.lastMove);
+ }
+ this.lastMove = offset;
+
+ if (this.selectedObject) {
+ matrix = this.selectedObject.localMatrix;
+ transformation = g_math.matrix4.translation(translation);
+ matrix = g_math.matrix4.mul(matrix, transformation);
+ this.selectedObject.localMatrix = matrix;
+ }
+};
+
+MoveTool.prototype.handleKeyDown = function(key) {
+ return false;
+};
+
+MoveTool.prototype.handleKeyUp = function(key) {
+ // TODO: We should really have a selection instead of this
+ // dual-use of the tool's last selected object.
+ if (key == DELETE || key == BACKSPACE) {
+ if (this.downPoint == null && this.selectedObject != null) {
+ this.selectedObject.setParent(null);
+ this.selectedObject = null;
+ return true;
+ }
+ }
+ return false;
+};
diff --git a/o3d/samples/home-configurators/orbittool.js b/o3d/samples/home-configurators/orbittool.js
new file mode 100644
index 0000000..0f37242
--- /dev/null
+++ b/o3d/samples/home-configurators/orbittool.js
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * The Orbit tool allows the user to look around the model by click-dragging.
+ * The shift key enters a pan mode instead of orbiting.
+ */
+function OrbitTool(camera) {
+ this.camera = camera;
+ this.lastOffset = null;
+ this.mouseLeftDown = false;
+ this.mouseMiddleDown = false;
+}
+
+OrbitTool.prototype.handleMouseDown = function(e) {
+ this.lastOffset = { x: e.x, y: e.y };
+ if (e.button == g_o3d.Event.BUTTON_LEFT) {
+ this.mouseLeftDown = true;
+ } else if (e.button == g_o3d.Event.BUTTON_MIDDLE) {
+ this.mouseMiddleDown = true;
+ }
+};
+
+OrbitTool.prototype.handleMouseMove = function(e) {
+ if (e.x !== undefined && (this.mouseLeftDown || this.mouseMiddleDown)) {
+ var offset = { x: e.x, y: e.y };
+
+ dY = (offset.y - this.lastOffset.y);
+ dX = (offset.x - this.lastOffset.x);
+ this.lastOffset = offset;
+ panInsteadOfOrbit = (this.mouseLeftDown && this.mouseMiddleDown) ||
+ e.shiftKey;
+ if (panInsteadOfOrbit) {
+ dX *= -1;
+ this.camera.target.x -= (dY * Math.cos(this.camera.eye.rotZ) +
+ dX * Math.sin(this.camera.eye.rotZ)) /
+ (700 / this.camera.eye.distanceFromTarget);
+ this.camera.target.y += (-dY * Math.sin(this.camera.eye.rotZ) +
+ dX * Math.cos(this.camera.eye.rotZ)) /
+ (700 / this.camera.eye.distanceFromTarget);
+ } else {
+ this.camera.eye.rotZ -= dX / 300;
+ this.camera.eye.rotH -= dY / 300;
+ this.camera.eye.rotH = peg(this.camera.eye.rotH, 0.1, Math.PI - 0.1);
+ document.getElementById('output').innerHTML = this.camera.eye.rotH;
+ }
+ this.camera.update();
+ }
+};
+
+OrbitTool.prototype.handleMouseUp = function(e) {
+ if (e.button == g_o3d.Event.BUTTON_LEFT) {
+ this.mouseLeftDown = false;
+ } else if (e.button == g_o3d.Event.BUTTON_MIDDLE) {
+ this.mouseMiddleDown = false;
+ }
+};
+
+OrbitTool.prototype.handleKeyDown = function(e) {
+ return false;
+};
+
+OrbitTool.prototype.handleKeyUp = function(e) {
+ return false;
+};
diff --git a/o3d/samples/home-configurators/pantool.js b/o3d/samples/home-configurators/pantool.js
new file mode 100644
index 0000000..5caaa59
--- /dev/null
+++ b/o3d/samples/home-configurators/pantool.js
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * The Pan tool allows the user to pan around the view by clicking and dragging.
+ */
+function PanTool(camera) {
+ this.camera = camera;
+ this.dragging = false;
+ this.lastOffset = null;
+}
+
+PanTool.prototype.handleMouseDown = function(e) {
+ var offset = { x: e.x, y: e.y };
+ this.lastOffset = offset;
+ this.dragging = true;
+};
+
+PanTool.prototype.handleMouseMove = function(e) {
+ if (this.dragging && e.x !== undefined) {
+ var offset = { x: e.x, y: e.y };
+
+ dY = (offset.y - this.lastOffset.y);
+ dX = -(offset.x - this.lastOffset.x);
+ this.lastOffset = offset;
+
+ this.camera.target.x -= (dY * Math.cos(this.camera.eye.rotZ) +
+ dX * Math.sin(this.camera.eye.rotZ)) /
+ (700 / this.camera.eye.distanceFromTarget);
+ this.camera.target.y += (-dY * Math.sin(this.camera.eye.rotZ) +
+ dX * Math.cos(this.camera.eye.rotZ)) /
+ (700 / this.camera.eye.distanceFromTarget);
+
+ this.camera.update();
+ }
+};
+
+PanTool.prototype.handleMouseUp = function(e) {
+ this.dragging = false;
+};
+
+PanTool.prototype.handleKeyDown = function(e) {
+ return false;
+};
+
+PanTool.prototype.handleKeyUp = function(e) {
+ return false;
+};
diff --git a/o3d/samples/home-configurators/rotatetool.js b/o3d/samples/home-configurators/rotatetool.js
new file mode 100644
index 0000000..9bf852b
--- /dev/null
+++ b/o3d/samples/home-configurators/rotatetool.js
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * A tool to move components around in the scene. Will constrain movement to
+ * the ground plane.
+ *
+ * Requires the picking Client 3d Utilities API.
+ */
+function RotateTool(context, root) {
+ this.downPoint = null;
+ this.lastRotation = 0;
+ this.context = context;
+ this.selectedObject = null;
+ this.root = root;
+ this.transformInfo = o3djs.picking.createTransformInfo(root, null);
+ this.rotateAxis = [0.0, 0.0, 1.0];
+ this.rotateCenter = null;
+}
+
+RotateTool.prototype.handleMouseDown = function(e) {
+ this.downPoint = { x: e.x, y: e.y };
+ var window_width = g_client.width;
+ var window_height = g_client.height;
+ var worldRay = o3djs.picking.clientPositionToWorldRay(this.downPoint.x,
+ this.downPoint.y,
+ this.context,
+ window_width,
+ window_height);
+ this.transformInfo.update();
+ var pickInfo = this.transformInfo.pick(worldRay);
+ if (pickInfo) {
+ pickedObject = pickInfo.shapeInfo.parent;
+ while (pickedObject.parent != null &&
+ pickedObject.parent.transform != this.root) {
+ dump(pickedObject.transform.name + '\n');
+ pickedObject = pickedObject.parent;
+ }
+ this.selectedObject = pickedObject.transform;
+ } else {
+ this.selectedObject = null;
+ }
+ if (this.selectedObject) {
+ dump(this.selectedObject.name + '\n');
+ this.lastRotation = 0.0;
+ } else {
+ this.downPoint = null;
+ }
+};
+
+RotateTool.prototype.handleMouseUp = function(e) {
+ this.downPoint = null;
+ this.selectedObject = null;
+};
+
+RotateTool.prototype.handleMouseMove = function(e) {
+ if (this.downPoint == null)
+ return;
+
+ if (e.x === undefined)
+ return;
+
+ offset = this.downPoint.x - e.x;
+ radian_rotation = -10.0 * Math.round(offset / 10.0) * (Math.PI / 180.0);
+
+ relative_rotation = radian_rotation - this.lastRotation;
+ this.lastRotation = radian_rotation;
+
+ if (relative_rotation != 0 && this.selectedObject) {
+ matrix = this.selectedObject.localMatrix;
+ transformation = g_math.matrix4.axisRotation(this.rotateAxis,
+ relative_rotation);
+ matrix = g_math.matrix4.mul(transformation, matrix);
+ this.selectedObject.localMatrix = matrix;
+ }
+};
+
+RotateTool.prototype.handleKeyDown = function(e) {
+ return false;
+};
+
+RotateTool.prototype.handleKeyUp = function(e) {
+ return false;
+};
diff --git a/o3d/samples/home-configurators/searsassets/sears_bg.png b/o3d/samples/home-configurators/searsassets/sears_bg.png
new file mode 100644
index 0000000..7842ddf
--- /dev/null
+++ b/o3d/samples/home-configurators/searsassets/sears_bg.png
Binary files differ
diff --git a/o3d/samples/home-configurators/searsassets/sears_item_thumbnails.jpg b/o3d/samples/home-configurators/searsassets/sears_item_thumbnails.jpg
new file mode 100644
index 0000000..428e8fa
--- /dev/null
+++ b/o3d/samples/home-configurators/searsassets/sears_item_thumbnails.jpg
Binary files differ
diff --git a/o3d/samples/home-configurators/viewer.js b/o3d/samples/home-configurators/viewer.js
new file mode 100644
index 0000000..69c4fb7
--- /dev/null
+++ b/o3d/samples/home-configurators/viewer.js
@@ -0,0 +1,578 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * This file contains definitions for the common functions used by all the home
+ * configurator pages.
+ */
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.arcball');
+o3djs.require('o3djs.dump');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.shape');
+o3djs.require('o3djs.effect');
+o3djs.require('o3djs.material');
+o3djs.require('o3djs.pack');
+o3djs.require('o3djs.picking');
+o3djs.require('o3djs.scene');
+
+var g_root;
+var g_o3d;
+var g_math;
+var g_client;
+var g_thisRot;
+var g_lastRot;
+var g_pack = null;
+var g_mainPack;
+var g_viewInfo;
+var g_lightPosParam;
+var g_currentTool = null;
+var g_floorplanRoot = null;
+var g_placedModesRoot = null;
+var TOOL_ORBIT = 3;
+var TOOL_MOVE = 1;
+var TOOL_ZOOM_EXTENTS = 6;
+var g_urlToInsert;
+var g_isDraggingItem = false;
+var g_o3dElement = null;
+var g_lastMouseButtonState = 0;
+
+/**
+ * Retrieve the absolute position of an element on the screen.
+ */
+function getAbsolutePosition(element) {
+ var r = { x: element.offsetLeft, y: element.offsetTop };
+ if (element.offsetParent) {
+ var tmp = getAbsolutePosition(element.offsetParent);
+ r.x += tmp.x;
+ r.y += tmp.y;
+ }
+ return r;
+}
+
+/**
+ * Retrieve the coordinates of the given event relative to the center
+ * of the widget.
+ *
+ * @param event
+ * A mouse-related DOM event.
+ * @param reference
+ * A DOM element whose position we want to transform the mouse coordinates to.
+ * @return
+ * An object containing keys 'x' and 'y'.
+ */
+function getRelativeCoordinates(event, reference) {
+ var x, y;
+ event = event || window.event;
+ var el = event.target || event.srcElement;
+ if (!window.opera && typeof event.offsetX != 'undefined') {
+ // Use offset coordinates and find common offsetParent
+ var pos = { x: event.offsetX, y: event.offsetY };
+ // Send the coordinates upwards through the offsetParent chain.
+ var e = el;
+ while (e) {
+ e.mouseX = pos.x;
+ e.mouseY = pos.y;
+ pos.x += e.offsetLeft;
+ pos.y += e.offsetTop;
+ e = e.offsetParent;
+ }
+ // Look for the coordinates starting from the reference element.
+ var e = reference;
+ var offset = { x: 0, y: 0 }
+ while (e) {
+ if (typeof e.mouseX != 'undefined') {
+ x = e.mouseX - offset.x;
+ y = e.mouseY - offset.y;
+ break;
+ }
+ offset.x += e.offsetLeft;
+ offset.y += e.offsetTop;
+ e = e.offsetParent;
+ }
+ // Reset stored coordinates
+ e = el;
+ while (e) {
+ delete e.mouseX;
+ delete e.mouseY;
+ e = e.offsetParent;
+ }
+ }
+ else {
+ // Use absolute coordinates
+ var pos = getAbsolutePosition(reference);
+ x = event.pageX - pos.x;
+ y = event.pageY - pos.y;
+ }
+
+ return { x: x, y: y };
+}
+
+
+// The target camera has its z and y flipped because that's the way Scott
+// Lininger thinks.
+function TargetCamera() {
+ this.eye = {
+ rotZ: -Math.PI / 3,
+ rotH: Math.PI / 3,
+ distanceFromTarget: 700 };
+ this.target = { x: 0, y: 0, z: 0 };
+}
+
+TargetCamera.prototype.update = function() {
+ var target = [this.target.x, this.target.y, this.target.z];
+
+ this.eye.x = this.target.x + Math.cos(this.eye.rotZ) *
+ this.eye.distanceFromTarget * Math.sin(this.eye.rotH);
+ this.eye.y = this.target.y + Math.sin(this.eye.rotZ) *
+ this.eye.distanceFromTarget * Math.sin(this.eye.rotH);
+ this.eye.z = this.target.z + Math.cos(this.eye.rotH) *
+ this.eye.distanceFromTarget;
+
+ var eye = [this.eye.x, this.eye.y, this.eye.z];
+ var up = [0, 0, 1];
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(eye, target, up);
+ g_lightPosParam.value = eye;
+};
+
+var g_camera = new TargetCamera();
+
+function peg(value, lower, upper) {
+ if (value < lower) {
+ return lower;
+ } else if (value > upper) {
+ return upper;
+ } else {
+ return value;
+ }
+}
+
+/**
+ * Keyboard constants.
+ */
+var BACKSPACE = 8;
+var TAB = 9;
+var ENTER = 13;
+var SHIFT = 16;
+var CTRL = 17;
+var ALT = 18;
+var ESCAPE = 27;
+var PAGEUP = 33;
+var PAGEDOWN = 34;
+var END = 35;
+var HOME = 36;
+var LEFT = 37;
+var UP = 38;
+var RIGHT = 39;
+var DOWN = 40;
+var DELETE = 46;
+var SPACE = 32;
+
+/**
+ * Create some global key capturing. Keys that are pressed will be stored in
+ * this global array.
+ */
+g_keyIsDown = [];
+
+document.onkeydown = function(e) {
+ var keycode;
+ if (window.event) {
+ keycode = window.event.keyCode;
+ } else if (e) {
+ keycode = e.which;
+ }
+ g_keyIsDown[keycode] = true;
+ if (g_currentTool != null) {
+ g_currentTool.handleKeyDown(keycode);
+ }
+};
+
+document.onkeyup = function(e) {
+ var keycode;
+ if (window.event) {
+ keycode = window.event.keyCode;
+ } else if (e) {
+ keycode = e.which;
+ }
+ g_keyIsDown[keycode] = false;
+ if (g_currentTool != null) {
+ g_currentTool.handleKeyUp(keycode);
+ }
+};
+
+document.onmouseup = function(e) {
+ if (g_currentTool != null) {
+ g_currentTool.handleMouseUp(e);
+ } else {
+ cancelInsertDrag();
+ }
+};
+
+// NOTE: mouseDown, mouseMove and mouseUp are mouse event handlers for events
+// taking place inside the o3d area. They typically pass the events down
+// to the currently selected tool (e.g. Orbit, Move, etc). Tool and item
+// selection mouse events are registered seperately on their respective DOM
+// elements.
+
+// This function handles the mousedown events that happen inside the o3d
+// area. If a tool is currently selected (e.g. Orbit, Move, etc.) the event
+// is forwarded over to it. If the middle mouse button is pressed then we
+// temporarily switch over to the orbit tool to emulate the SketchUp behavior.
+function mouseDown(e) {
+ // If the middle mouse button is used, then switch into the orbit tool,
+ // Sketchup-style.
+ if (e.button == g_o3d.Event.BUTTON_MIDDLE) {
+ g_lastTool = g_currentTool;
+ g_lastSelectorLeft = $('toolselector').style.left;
+ selectTool(null, TOOL_ORBIT);
+ }
+
+ if (g_currentTool != null) {
+ g_currentTool.handleMouseDown(e);
+ }
+}
+
+// This function handles mouse move events inside the o3d area. It simply
+// forwards them down to the currently selected tool.
+function mouseMove(e) {
+ if (g_currentTool != null) {
+ g_currentTool.handleMouseMove(e);
+ }
+}
+
+// This function handles mouse up events that take place in the o3d area.
+// If the middle mouse button is lifted then we switch out of the temporary
+// orbit tool mode.
+function mouseUp(e) {
+ // If the middle mouse button was used, then switch out of the orbit tool
+ // and reset to their last tool.
+ if (e.button == g_o3d.Event.BUTTON_MIDDLE) {
+ $('toolselector').style.left = g_lastSelectorLeft;
+ g_currentTool = g_lastTool
+ }
+ if (g_currentTool != null) {
+ g_currentTool.handleMouseUp(e);
+ }
+}
+
+function scrollMe(e) {
+ e = e ? e : window.event;
+ var raw = e.detail ? e.detail : -e.wheelDelta;
+ if (raw < 0) {
+ g_camera.eye.distanceFromTarget *= 11 / 12;
+
+ } else {
+ g_camera.eye.distanceFromTarget *= (1 + 1 / 12);
+ }
+ g_camera.update();
+}
+
+function $(name) {
+ return document.getElementById(name);
+}
+
+
+// An array of tool objects that will get populated when our base model loads.
+var g_tools = [];
+
+function selectTool(e, opt_toolNumber) {
+ var ICON_WIDTH = 32;
+ var toolNumber = opt_toolNumber;
+
+ if (toolNumber == undefined) {
+ // Where you click determines your tool. But since our underlying toolbar
+ // graphic isn't perfectly uniform, perform some acrobatics to get the best
+ // toolNumber match.
+ var pt = getRelativeCoordinates(e, $('toolpanel'));
+ if (pt.x < 120) {
+ toolNumber = Math.floor((pt.x - 8) / ICON_WIDTH)
+ } else {
+ toolNumber = Math.floor((pt.x - 26) / ICON_WIDTH)
+ }
+ toolNumber = peg(toolNumber, 0, 9);
+ }
+
+ // Now place the selector graphic over the tool we clicked.
+ if (toolNumber < 3) {
+ $('toolselector').style.left = toolNumber * ICON_WIDTH + 8;
+ } else {
+ $('toolselector').style.left = toolNumber * ICON_WIDTH + 26;
+ }
+
+ // Finally, activate the appropriate tool.
+ // TODO: Replace this hacky zoom extents tool detection with
+ // tools that have an onActivate callback.
+ if (toolNumber == TOOL_ZOOM_EXTENTS) {
+ // Reset our camera. (Zooming to extents would be better, obviously ;)
+ g_camera.eye.distanceFromTarget = 900;
+ g_camera.target = { x: -100, y: 0, z: 0 };
+ g_camera.update();
+
+ // Then reset to the orbit tool, after pausing for a bit so the user
+ // sees the zoom extents button depress.
+ setTimeout(function() { selectTool(null, TOOL_ORBIT)}, 200);
+ } else {
+ g_currentTool = g_tools[toolNumber];
+ }
+
+}
+
+function loadFile(context, path) {
+ function callback(pack, start_move_tool_root, exception) {
+ if (exception) {
+ alert('Could not load: ' + path + '\n' + exception);
+ } else {
+ // Generate draw elements and setup material draw lists.
+ o3djs.pack.preparePack(g_pack, g_viewInfo);
+
+ // Manually connect all the materials' lightWorldPos params to the context
+ var materials = g_pack.getObjectsByClassName('o3d.Material');
+ for (var m = 0; m < materials.length; ++m) {
+ var material = materials[m];
+ var param = material.getParam('lightWorldPos');
+ if (param) {
+ param.bind(g_lightPosParam);
+ }
+ }
+
+ /*
+ o3djs.dump.dump('---dumping context---\n');
+ o3djs.dump.dumpParamObject(context);
+
+ o3djs.dump.dump('---dumping root---\n');
+ o3djs.dump.dumpTransformTree(g_client.root);
+
+ o3djs.dump.dump('---dumping render root---\n');
+ o3djs.dump.dumpRenderNodeTree(g_client.renderGraphRoot);
+
+ o3djs.dump.dump('---dump g_pack shapes---\n');
+ var shapes = g_pack.getObjectsByClassName('o3d.Shape');
+ for (var t = 0; t < shapes.length; t++) {
+ o3djs.dump.dumpShape(shapes[t]);
+ }
+
+ o3djs.dump.dump('---dump g_pack materials---\n');
+ var materials = g_pack.getObjectsByClassName('o3d.Material');
+ for (var t = 0; t < materials.length; t++) {
+ o3djs.dump.dump (
+ ' ' + t + ' : ' + materials[t].className +
+ ' : "' + materials[t].name + '"\n');
+ o3djs.dump.dumpParams(materials[t], ' ');
+ }
+
+ o3djs.dump.dump('---dump g_pack textures---\n');
+ var textures = g_pack.getObjectsByClassName('o3d.Texture');
+ for (var t = 0; t < textures.length; t++) {
+ o3djs.dump.dumpTexture(textures[t]);
+ }
+
+ o3djs.dump.dump('---dump g_pack effects---\n');
+ var effects = g_pack.getObjectsByClassName('o3d.Effect');
+ for (var t = 0; t < effects.length; t++) {
+ o3djs.dump.dump (' ' + t + ' : ' + effects[t].className +
+ ' : "' + effects[t].name + '"\n');
+ o3djs.dump.dumpParams(effects[t], ' ');
+ }
+ */
+ }
+ if (start_move_tool_root != g_floorplanRoot) {
+ selectTool(null, TOOL_MOVE);
+ g_tools[TOOL_MOVE].initializeWithShape(start_move_tool_root);
+ }
+ g_camera.update();
+ }
+
+ g_pack = g_client.createPack();
+ g_pack.name = 'load pack';
+
+ new_object_root = null;
+ if (g_floorplanRoot == null) {
+ // Assign as the floorplan
+ g_floorplanRoot = g_pack.createObject('o3d.Transform');
+ g_floorplanRoot.name = 'floorplan';
+ g_floorplanRoot.parent = g_client.root;
+
+ g_placedModelsRoot = g_pack.createObject('o3d.Transform');
+ g_placedModelsRoot.name = 'placedModels';
+ g_placedModelsRoot.parent = g_floorplanRoot;
+
+ // Put the object we're loading on the floorplan.
+ new_object_root = g_floorplanRoot;
+
+ // Create our set of tools that can be activated.
+ // Note: Currently only the Delete, Move, Rotate, Orbit, Pan and Zoom
+ // tools are implemented. The last four icons in the toolbar are unused.
+ g_tools = [
+ new DeleteTool(g_viewInfo.drawContext, g_placedModelsRoot),
+ new MoveTool(g_viewInfo.drawContext, g_placedModelsRoot),
+ new RotateTool(g_viewInfo.drawContext, g_placedModelsRoot),
+ new OrbitTool(g_camera),
+ new PanTool(g_camera),
+ new ZoomTool(g_camera),
+ null,
+ null,
+ null,
+ null
+ ]
+
+ selectTool(null, TOOL_ORBIT);
+ } else {
+ // Create a new transform for the loaded file
+ new_object_root = g_pack.createObject('o3d.Transform');
+ new_object_root.name = 'loaded object';
+ new_object_root.parent = g_placedModelsRoot;
+
+ }
+
+ if (path != null) {
+ o3djs.scene.loadScene(g_client, g_pack, new_object_root, path, callback);
+ }
+
+ return new_object_root;
+}
+
+function setClientSize() {
+ // Create a perspective projection matrix
+ g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ 3.14 * 45 / 180, g_client.width / g_client.height, 0.1, 5000);
+}
+
+function resize() {
+ setClientSize();
+}
+
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+function initStep2(clientElements) {
+ g_o3dElement = clientElements[0];
+
+ var path = window.location.href;
+ var index = path.lastIndexOf('/');
+ path = path.substring(0, index + 1) + g_buildingAsset;
+ var url = document.getElementById('url').value = path;
+
+ g_o3d = g_o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = g_o3dElement.client;
+
+ g_mainPack = g_client.createPack();
+ g_mainPack.name = 'simple viewer pack';
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_mainPack,
+ g_client.root,
+ g_client.renderGraphRoot,
+ [1, 1, 1, 1]);
+
+ g_lastRot = g_math.identity(3);
+ g_thisRot = g_math.identity(3);
+
+ var root = g_client.root;
+
+ var target = [0, 0, 0];
+ var eye = [0, 0, 5];
+ var up = [0, 1, 0];
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(eye, target, up);
+
+ setClientSize();
+
+ var paramObject = g_mainPack.createObject('ParamObject');
+ // Set the light at the same position as the camera to create a headlight
+ // that illuminates the object straight on.
+ g_lightPosParam = paramObject.createParam('lightWorldPos', 'ParamFloat3');
+ g_lightPosParam.value = eye;
+
+ doload();
+
+ o3djs.event.addEventListener(g_o3dElement, 'mousedown', mouseDown);
+ o3djs.event.addEventListener(g_o3dElement, 'mousemove', mouseMove);
+ o3djs.event.addEventListener(g_o3dElement, 'mouseup', mouseUp);
+
+ g_o3dElement.addEventListener('mouseover', dragOver, false);
+ // for Firefox
+ g_o3dElement.addEventListener('DOMMouseScroll', scrollMe, false);
+ // for Safari
+ g_o3dElement.onmousewheel = scrollMe;
+
+ document.getElementById('toolpanel').onmousedown = selectTool;
+
+ // Create our catalog list from the global list of items (g_items).
+ html = [];
+ for (var i = 0; i < g_items.length; i++) {
+ html.push('<div class="catalogItem" onmousedown="startInsertDrag(\'',
+ g_items[i].url, '\')" style="background-position:-', (i * 62) ,
+ 'px 0px" title="', g_items[i].title, '"></div>');
+ }
+ $('itemlist').innerHTML = html.join('');
+
+ // Register a mouse-move event listener to the entire window so that we can
+ // catch the click-and-drag events that originate from the list of items
+ // and end up in the o3d element.
+ document.addEventListener('mousemove', mouseMove, false);
+}
+
+function dragOver(e) {
+ if (g_urlToInsert != null) {
+ doload(g_urlToInsert);
+ }
+ g_urlToInsert = null;
+}
+
+function doload(opt_url) {
+ var url;
+ if (opt_url != null) {
+ url = opt_url
+ } else if ($('url')) {
+ url = $('url').value;
+ }
+ g_root = loadFile(g_viewInfo.drawContext, url);
+}
+
+function startInsertDrag(url) {
+ // If no absolute web path was passed, assume it's a local file
+ // coming from the assets directory.
+ if (url.indexOf('http') != 0) {
+ var path = window.location.href;
+ var index = path.lastIndexOf('/');
+ g_urlToInsert = path.substring(0, index + 1) + g_assetPath + url;
+ } else {
+ g_urlToInsert = url;
+ }
+}
+
+function cancelInsertDrag() {
+ g_urlToInsert = null;
+}
+
diff --git a/o3d/samples/home-configurators/zoomtool.js b/o3d/samples/home-configurators/zoomtool.js
new file mode 100644
index 0000000..117044e
--- /dev/null
+++ b/o3d/samples/home-configurators/zoomtool.js
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * The Zoom tool allows the user to Zoom around the view by clicking and
+ * dragging.
+ */
+function ZoomTool(camera) {
+ this.camera = camera;
+ this.dragging = false;
+ this.lastOffsetY = null;
+}
+
+ZoomTool.prototype.handleMouseDown = function(e) {
+ this.lastOffsetY = e.y;
+ this.dragging = true;
+};
+
+ZoomTool.prototype.handleMouseMove = function(e) {
+ if (this.dragging && e.x !== undefined) {
+ dY = (e.y - this.lastOffsetY);
+ this.lastOffsetY = e.y;
+
+ var zoomLevels = Math.abs(dY / 6);
+ var zoomChange;
+ if (dY < 0) {
+ zoomChange = Math.pow(11 / 12, zoomLevels);
+ } else {
+ zoomChange = Math.pow((1 + 1 / 12), zoomLevels);
+ }
+ this.camera.eye.distanceFromTarget *= zoomChange;
+
+ this.camera.update();
+ }
+};
+
+ZoomTool.prototype.handleMouseUp = function(e) {
+ this.dragging = false;
+};
+
+ZoomTool.prototype.handleKeyDown = function(e) {
+ return false;
+};
+
+ZoomTool.prototype.handleKeyUp = function(e) {
+ return false;
+};
diff --git a/o3d/samples/hud-2d-overlay.html b/o3d/samples/hud-2d-overlay.html
new file mode 100644
index 0000000..49c114b
--- /dev/null
+++ b/o3d/samples/hud-2d-overlay.html
@@ -0,0 +1,503 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+HUD 2D Overlay.
+
+This example shows implementing a HUD or 2d Overlay
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+HUD 2D Overlay.
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.effect');
+o3djs.require('o3djs.loader');
+
+// Events
+// init() once the page has finished loading.
+// unload() when the page is unloaded.
+window.onload = init;
+window.onunload= unload;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_3dRoot;
+var g_hudRoot;
+var g_viewInfo;
+var g_hudViewInfo;
+var g_pack;
+var g_clock = 0;
+var g_timeMult = 1;
+var g_finished = false; // for selenium testing
+var g_cameraRadius = 35;
+var g_cameraSpeed = 0.3;
+var g_gaugeWidth = 145;
+var g_gaugeHeight = 16;
+var g_planeShape;
+var g_groundShape;
+var g_cubeShape;
+var g_materialUrls = [
+ 'shaders/texture-colormult.shader', // 0
+ 'shaders/phong-with-colormult.shader' // 1
+];
+var g_materials = [];
+var g_textures = [];
+var g_textureUrls = [
+ 'assets/purple-flower.png', // 0
+ 'assets/orange-flower.png', // 1
+ 'assets/egg.png', // 2
+ 'assets/gaugeback.png', // 3
+ 'assets/gauge.png', // 4
+ 'assets/iconback.png', // 5
+ 'assets/radar.png', // 6
+ 'assets/one-pixel-white.tga' // 7
+];
+var g_radar;
+var g_radarNeedle;
+var g_gaugeBack;
+var g_gauges = [];
+var g_gaugeFrames = [];
+var g_iconBacks = [];
+var g_icons = [];
+var g_selectedIndex = 0;
+var g_randSeed = 0;
+
+/**
+ * Returns a deterministic pseudorandom number bewteen 0 and 1
+ * @return {number} a random number between 0 and 1
+ */
+function pseudoRandom() {
+ var range = Math.pow(2, 32);
+
+ return (g_randSeed = (134775813 * g_randSeed + 1) % range) / range;
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and creates one shape.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = o3dElement.client;
+
+ // Creates a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create 2 root transforms, one for the 3d parts, one for the 2d parts.
+ // This is not strictly neccassary but it is helpful for organization.
+ g_3dRoot = g_pack.createObject('Transform');
+ g_hudRoot = g_pack.createObject('Transform');
+
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_3dRoot,
+ g_client.renderGraphRoot);
+
+ // Create a second view for the hud. There are other ways to do this but
+ // this is the easiest.
+ g_hudViewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_hudRoot,
+ g_client.renderGraphRoot);
+
+ // Make sure the hud gets drawn after the 3d stuff
+ g_hudViewInfo.root.priority = g_viewInfo.root.priority + 1;
+
+ // Turn off clearing the color for the hud since that would erase the 3d
+ // parts but leave clearing the depth and stencil so the HUD is unaffected
+ // by anything done by the 3d parts.
+ g_hudViewInfo.clearBuffer.clearColorFlag = false;
+
+ // Set culling to none so we can flip images using rotation or negative scale.
+ g_hudViewInfo.zOrderedState.getStateParam('CullMode').value =
+ g_o3d.State.CULL_NONE;
+ g_hudViewInfo.zOrderedState.getStateParam('ZWriteEnable').value = false;
+
+ // Create an orthographic matrix for 2d stuff in the HUD.
+ // We assume the area is 800 pixels by 600 pixels and therefore we can
+ // position things using a 0-799, 0-599 coordinate system. If we change the
+ // size of the client area everything will get scaled to fix but we don't
+ // have to change any of our code. See 2d.html
+ g_hudViewInfo.drawContext.projection = g_math.matrix4.orthographic(
+ 0 + 0.5,
+ 800 + 0.5,
+ 600 + 0.5,
+ 0 + 0.5,
+ 0.001,
+ 1000);
+
+ g_hudViewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [0, 0, 1], // eye
+ [0, 0, 0], // target
+ [0, 1, 0]); // up
+
+ g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(30), // 30 degree fov.
+ g_client.width / g_client.height,
+ 0.1, // Near plane.
+ 5000); // Far plane.
+
+ for (var ii = 0; ii < g_materialUrls.length; ++ii) {
+ var effect = g_pack.createObject('Effect');
+ o3djs.effect.loadEffect(effect, g_materialUrls[ii]);
+
+ // Create a Material for the effect.
+ var material = g_pack.createObject('Material');
+
+ // Apply our effect to this material.
+ material.effect = effect;
+
+ // Create the params the effect needs on the material.
+ effect.createUniformParameters(material);
+
+ // Set the default params. We'll override these with params on transforms.
+ material.getParam('colorMult').value = [1, 1, 1, 1];
+
+ g_materials[ii] = material;
+ }
+
+ // Set the materials' drawLists
+ g_materials[0].drawList = g_hudViewInfo.zOrderedDrawList;
+ g_materials[1].drawList = g_viewInfo.performanceDrawList;
+
+ g_materials[1].getParam('lightWorldPos').value = [500, 1000, 0];
+ g_materials[1].getParam('lightIntensity').value = [1, 1, 1, 1];
+ g_materials[1].getParam('ambientIntensity').value = [0.1, 0.1, 0.1, 1];
+ g_materials[1].getParam('ambient').value = [1, 1, 1, 1];
+ g_materials[1].getParam('diffuse').value = [1, 1, 1, 1];
+ g_materials[1].getParam('specular').value = [0.5, 0.5, 0.5, 1];
+ g_materials[1].getParam('shininess').value = 20;
+
+ // Create a 2d plane for images. createPlane makes an XZ plane by default
+ // so we pass in matrix to rotate it to an XY plane. We could do
+ // all our manipluations in XZ but most people seem to like XY for 2D.
+ g_planeShape = o3djs.primitives.createPlane(
+ g_pack,
+ g_materials[0],
+ 1,
+ 1,
+ 1,
+ 1,
+ [[1, 0, 0, 0],
+ [0, 0, 1, 0],
+ [0,-1, 0, 0],
+ [0, 0, 0, 1]]);
+
+ // Create a ground plane
+ g_groundShape = o3djs.primitives.createPlane(
+ g_pack,
+ g_materials[1],
+ 30,
+ 30,
+ 10,
+ 10);
+
+ // Create a cube with its origin at the bottom center.
+ g_cubeShape = o3djs.primitives.createCube(
+ g_pack,
+ g_materials[1],
+ 1,
+ [[0.9, 0, 0, 0],
+ [0, 1, 0, 0],
+ [0, 0, 0.9, 0],
+ [0, 0.5, 0, 1]]);
+
+ // Load all the textures.
+ var loader = o3djs.loader.createLoader(initStep3);
+ for (var ii = 0; ii < g_textureUrls.length; ++ii) {
+ loadTexture(loader, g_textureUrls[ii], ii);
+ }
+ loader.finish();
+}
+
+/**
+ * Loads a texture and saves it in the g_textures array.
+ * @param {Object} loader The loader to load with.
+ * @param {stinrg} url of texture to load
+ * @param {number} index Index to put texture in g_textures
+ */
+function loadTexture(loader, url, index) {
+ loader.loadTexture(g_pack, url, function(texture, exception) {
+ if (exception) {
+ alert(exception);
+ } else {
+ g_textures[index] = texture;
+ }
+ });
+}
+
+/**
+ * Now that the textures are loaded continue.
+ */
+function initStep3() {
+ // Setup the hud images.
+ g_radar = new Image(g_textures[6], true);
+ g_radar.transform.translate(3, 1, -2);
+
+ g_radarNeedle = new Image(g_textures[7], false);
+ g_radarNeedle.scaleTransform.translate(0, 0.5, 0);
+
+ g_gaugeBack = new Image(g_textures[3], true);
+ g_gaugeBack.transform.translate(201, 17, -2);
+
+ for (var ii = 0; ii < 3; ++ii) {
+ g_gaugeFrames[ii] = new Image(g_textures[4], true);
+ g_gaugeFrames[ii].transform.translate(220, 39 + ii * 21, -2);
+
+ g_gauges[ii] = new Image(g_textures[7], true);
+ g_gauges[ii].setColor((ii == 0) ? 1 : 0,
+ (ii == 1) ? 1 : 0,
+ (ii == 2) ? 1 : 0,
+ 1);
+
+ g_iconBacks[ii] = new Image(g_textures[5], true);
+ g_iconBacks[ii].transform.translate(634, 17 + ii * 140, -2);
+
+ // Make the icons' origin their center so we can easily rotate/scale them.
+ g_icons[ii] = new Image(g_textures[ii], false);
+ }
+
+ resetIcons();
+
+ // make the ground plane.
+ var transform = g_pack.createObject('Transform');
+ transform.addShape(g_groundShape);
+ transform.parent = g_3dRoot;
+ transform.createParam('colorMult', 'ParamFloat4').value =
+ [166 / 255, 124 / 255, 82 / 255, 1];
+
+ // Make a random city with 25 blocks.
+ for (var bz = -2; bz <= 2; ++bz) {
+ for (var bx = -2; bx <= 2; ++bx) {
+ for (var xx = 0; xx < 4; ++xx) {
+ createBuilding(bx * 5 + 1 + xx - 1.5, bz * 5 + 1 - 1.5);
+ createBuilding(bx * 5 + 1 + xx - 1.5, bz * 5 + 4 - 1.5);
+ }
+ for (var zz = 1; zz < 3; ++zz) {
+ createBuilding(bx * 5 + 1 - 1.5, bz * 5 + 1 + zz - 1.5);
+ createBuilding(bx * 5 + 4 - 1.5, bz * 5 + 1 + zz - 1.5);
+ }
+ }
+ }
+
+ // Setup an onrender callback for animation.
+ g_client.setRenderCallback(onrender);
+
+ g_finished = true; // for selenium testing.
+}
+
+/**
+ * Creates a building.
+ * @param {number} x X coordinate to create building at
+ * @param {number} z Y coordinate to create building at
+ */
+function createBuilding(x, z) {
+ var transform = g_pack.createObject('Transform');
+ transform.addShape(g_cubeShape);
+ transform.parent = g_3dRoot;
+ transform.translate(x, 0, z);
+ transform.scale(1, pseudoRandom() * 3 + 1, 1);
+ transform.createParam('colorMult', 'ParamFloat4').value = [
+ pseudoRandom() * 0.6 + 0.4,
+ pseudoRandom() * 0.6 + 0.4,
+ pseudoRandom() * 0.6 + 0.4,
+ 1];
+}
+
+/**
+ * Resets the orientation of the icons.
+ */
+function resetIcons() {
+ for (var ii = 0; ii < g_icons.length; ++ii) {
+ g_icons[ii].transform.identity();
+ g_icons[ii].transform.translate(634 + 6 + 64, 17 + ii * 140 + 5 + 64, -1);
+ g_icons[ii].transform.scale(0.8, 0.8, 0);
+ }
+}
+
+/**
+ * Called every frame.
+ * @param {!o3d.RenderEvent} renderEvent Rendering Information.
+ */
+function onrender(renderEvent) {
+ var elapsedTime = renderEvent.elapsedTime;
+ g_clock += elapsedTime * g_timeMult;
+
+ g_selectedIndex = Math.floor(g_clock / 3) % 3;
+
+ // Fly the camera around the city.
+ var eye = [
+ Math.sin(g_clock * g_cameraSpeed) * g_cameraRadius,
+ 10,
+ Math.cos(g_clock * g_cameraSpeed) * g_cameraRadius];
+
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ eye,
+ [0, 0, 0], // target
+ [0, 1, 0]); // up
+
+ // Rotate/Scale the selected icon.
+ var icon = g_icons[g_selectedIndex];
+ icon.transform.identity();
+ icon.transform.translate(
+ 634 + 6 + 64, 17 + g_selectedIndex * 140 + 5 + 64, -1);
+ icon.transform.rotateZ(g_clock * -1);
+ var scale = Math.sin(g_clock * 15) * 0.1 + 0.7;
+ icon.transform.scale(scale, scale, 1);
+
+ // Adjust the gauges
+ for (var ii = 0; ii < 3; ++ii) {
+ var gauge = g_gauges[ii];
+ gauge.transform.identity();
+ gauge.transform.translate(220 + 1, 39 + ii * 21 + 1, -1);
+ switch (ii) {
+ case 0:
+ gauge.transform.scale((Math.sin(g_clock) * 0.5 + 0.5) * g_gaugeWidth,
+ g_gaugeHeight,
+ 1);
+ break;
+ case 1:
+ gauge.transform.scale((Math.cos(g_clock) * 0.5 + 0.5) * g_gaugeWidth,
+ g_gaugeHeight,
+ 1);
+ break;
+ case 2:
+ gauge.transform.scale(
+ (Math.cos(g_clock * 3.2) * 0.2 + 0.6) * g_gaugeWidth,
+ g_gaugeHeight,
+ 1);
+ break;
+ }
+ }
+
+ // Rotate the radar
+ g_radarNeedle.transform.identity();
+ g_radarNeedle.transform.translate(93, 89, 0);
+ g_radarNeedle.transform.rotateZ(g_clock * 3);
+ g_radarNeedle.transform.scale(1, 80, 1);
+}
+
+/**
+ * Creates an Image object which is a transform and a child scaleTransform
+ * scaled to match the texture
+ *
+ * @constructor
+ * @param {!o3d.Texture} texture The texture
+ * @param {boolean} opt_topLeft If true the origin of the image will be its
+ * topleft corner, the default is the center of the image.
+ */
+function Image(texture, opt_topLeft) {
+ // create a transform for positioning
+ this.transform = g_pack.createObject('Transform');
+ this.transform.parent = g_hudRoot;
+
+ // create a transform for scaling to the size of the image just so
+ // we don't have to manage that manually in the transform above.
+ this.scaleTransform = g_pack.createObject('Transform');
+ this.scaleTransform.parent = this.transform;
+
+ // setup the sampler for the texture
+ this.sampler = g_pack.createObject('Sampler');
+ this.sampler.addressModeU = g_o3d.Sampler.CLAMP;
+ this.sampler.addressModeV = g_o3d.Sampler.CLAMP;
+ this.paramSampler = this.scaleTransform.createParam('texSampler0',
+ 'ParamSampler');
+ this.paramSampler.value = this.sampler;
+
+ // Setup our UV offsets and color multiplier
+ this.paramColorMult = this.scaleTransform.createParam('colorMult',
+ 'ParamFloat4');
+
+ this.setColor(1, 1, 1, 1);
+
+ this.sampler.texture = texture;
+ this.scaleTransform.addShape(g_planeShape);
+ if (opt_topLeft) {
+ this.scaleTransform.translate(texture.width / 2, texture.height / 2, 0);
+ }
+ this.scaleTransform.scale(texture.width, -texture.height, 1);
+}
+
+/**
+ * Sets the color multiplier for the image.
+ * @param {number} r Red component.
+ * @param {number} g Green component.
+ * @param {number} b Blue component.
+ * @param {number} a Alpha component.
+ */
+Image.prototype.setColor = function(r, g, b, a) {
+ this.paramColorMult.set(r, g, b, a);
+};
+
+/**
+ * Remove any callbacks so they don't get called after the page has unloaded.
+ */
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+</script>
+</head>
+<body>
+<h1>HUD 2D Overlay</h1>
+HUD = Heads Up Display.
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 800px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/samples/iframeit.html b/o3d/samples/iframeit.html
new file mode 100644
index 0000000..85bc776
--- /dev/null
+++ b/o3d/samples/iframeit.html
@@ -0,0 +1,133 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<style type="text/css" media="screen">
+ body {
+ background: #FDFCE9;
+ }
+</style>
+
+<script type="text/javascript" charset="utf-8">
+var errors = [];
+window.onerror = onError;
+addEvent(window, 'load', load);
+
+// Set focus to this iframe
+this.focus();
+code = parent.codeToRun;
+
+try {
+ document.write(code);
+} catch(e) {
+ alert(e.message);
+}
+
+
+function addEvent(a,b,c,d) {
+ if (a.addEventListener) {
+ a.addEventListener(b,c,d?true:false);
+ } else if(a.attachEvent) {
+ a.attachEvent('on'+b,c);
+ } else{
+ a['on'+b]=c;
+ }
+}
+
+function changeOverlayZIndex() {
+ // This is a little hack to fix the overlay covering up <object> tags.
+ // TODO: clean this up. Did this because it was fast hack
+ var lastZ = parent.iframeZindex;
+ var newZ;
+ if (typeof lastZ != 'undefined') {
+ newZ = parseInt(lastZ) - 1;
+
+ } else {
+ newZ = 4999;
+ }
+
+ parent.iframeZindex = newZ;
+
+ var overlay = parent.document.getElementById('overlay');
+ overlay.style.zIndex = newZ;
+}
+
+function load() {
+ var first = document.getElementsByTagName('body')[0].childNodes[0];
+ for (var i=0; i < errors.length; i++) {
+ document.body.insertBefore(errors[i], first);
+ }
+
+ changeOverlayZIndex();
+}
+
+function Stack() { try { throw Error() } catch(ex) { return ex.stack } }
+
+function onError(msg, href, lineNo) {
+ var errorDiv = document.createElement('div');
+ errorDiv.style.textAlign = 'center';
+ errorDiv.style.fontWeight = 'bold';
+ errorDiv.style.fontSize = '92%';
+ errorDiv.style.width = '100%';
+ errorDiv.style.padding = '0.5em 0';
+ var html = [];
+
+ var lastSlash = href.lastIndexOf("/");
+ var fileName = lastSlash == -1 ? href : href.substr(lastSlash+1);
+
+ html.push(
+ '<span style="display: inline-block;background: #ffd363 url(interactive_sampler_assets/images/tl.gif) top left no-repeat;">',
+ '<span style="background: url(interactive_sampler_assets/images/bl.gif) bottom left no-repeat;">',
+ '<span style="background: url(interactive_sampler_assets/images/tr.gif) top right no-repeat;">',
+ '<span style="background: url(interactive_sampler_assets/images/br.gif) bottom right no-repeat;">',
+ '<span style="background: transparent;padding: 0.2em 2em;color: #ce0c0c">',
+ 'Error: ' + msg,
+ '</span>',
+ '</span>',
+ '</span>',
+ '</span>',
+ '</span>'
+ );
+ errorDiv.innerHTML = html.join('\n');
+
+ // In some firefox's, the body tag isn't loaded so we can't print the error.
+ // We have to wait until the body tag loads, then we print.
+ // In other firefox's, the body loads before errors are fired, so we can add
+ // immediately
+ var first = document.getElementsByTagName('body')[0];
+ if (first && first.childNodes) {
+ first = first.childNodes[0];
+ document.body.insertBefore(errorDiv, first);
+ } else {
+ errors.push(errorDiv);
+ }
+};
+
+</script>
diff --git a/o3d/samples/instance-override.html b/o3d/samples/instance-override.html
new file mode 100644
index 0000000..10e53a4
--- /dev/null
+++ b/o3d/samples/instance-override.html
@@ -0,0 +1,199 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+O3D Instancing Override example.
+
+Display a single instanced object but change some shader parameters for each
+instance by putting them on the transform.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Instancing with Overrides.
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.effect');
+
+// Events
+// init() once the page has finished loading.
+// unload() when the page is unloaded.
+window.onload = init;
+window.onunload = unload;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_viewInfo;
+var g_pack;
+var g_clock = 0;
+var g_timeMult = 1;
+var g_finished = false; // for selenium testing.
+
+function createInstances(pack, shape) {
+ // make a grid of transforms and put a teapot instance on each one
+ for (var x = 0; x < 10; x++) {
+ for (var y = 0; y < 10; y++) {
+ for (var z = 0; z < 10; z++) {
+ var transform = pack.createObject('Transform');
+ transform.parent = g_client.root;
+ transform.addShape(shape);
+ transform.translate((x - 4.5) * 100,
+ (y - 4.5) * 100,
+ (z - 4.5) * 100);
+ transform.createParam('colorMult', 'ParamFloat4').value = [
+ x * 0.1,
+ y * 0.1,
+ z * 0.1,
+ 1.0];
+ }
+ }
+ }
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and creates a sphere.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = o3dElement.client;
+
+ // Creates a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Create our projection matrix, with a vertical field of view of 45 degrees
+ // a near clipping plane of 0.1 and far clipping plane of 10000.
+ g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(45),
+ g_client.width / g_client.height,
+ 0.1,
+ 10000);
+
+ // Create and load the effect.
+ var effect = g_pack.createObject('Effect');
+ o3djs.effect.loadEffect(effect, 'shaders/phong-with-colormult.shader');
+
+ // Create a Material for the effect.
+ var material = g_pack.createObject('Material');
+
+ // Apply our effect to this material.
+ material.effect = effect;
+
+ // Set the material's drawList.
+ material.drawList = g_viewInfo.performanceDrawList;
+
+ // Create the params the effect needs on the material.
+ effect.createUniformParameters(material);
+
+ // Set the material parameters.
+ material.getParam('lightWorldPos').value = [1000, 1000, 0];
+ material.getParam('lightIntensity').value = [1, 1, 1, 1];
+ material.getParam('ambientIntensity').value = [0.1, 0.1, 0.1, 1];
+ material.getParam('ambient').value = [1, 1, 1, 1];
+ material.getParam('diffuse').value = [1, 1, 1, 1];
+ material.getParam('specular').value = [0.5, 0.5, 0.5, 1];
+ material.getParam('shininess').value = 50;
+
+ // Create a sphere.
+ var shape = o3djs.primitives.createSphere(g_pack, material, 25, 10, 12);
+
+ createInstances(g_pack, shape);
+
+ // Setup an onrender callback for animation.
+ g_client.setRenderCallback(onrender);
+
+ g_finished = true; // for selenium testing.
+}
+
+// spin the camera.
+function onrender(render_event) {
+ // Get the number of seconds since the last render.
+ var elapsedTime = render_event.elapsedTime;
+ g_clock += elapsedTime * g_timeMult;
+
+ var x = Math.sin(g_clock * 0.3) * 1000;
+ var z = Math.cos(g_clock * 0.3) * 1000;
+ var y = Math.sin(g_clock * 0.7) * 500;
+
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [x, y, z], // eye
+ [0, 0, 0], // target
+ [0, 1, 0]); // up
+}
+
+/**
+ * Removes any callbacks so they don't get called after the page has unloaded.
+ */
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+</script>
+</head>
+<body>
+<h1>Instancing with overrides</h1>
+1000 Instances of the same sphere.
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 800px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/samples/instancing.html b/o3d/samples/instancing.html
new file mode 100644
index 0000000..344a385
--- /dev/null
+++ b/o3d/samples/instancing.html
@@ -0,0 +1,208 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+O3D Instancing example.
+
+Load the teapot, display it a bunch of times.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Instancing.
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.pack');
+o3djs.require('o3djs.scene');
+
+// Events
+// init() once the page has finished loading.
+// unload() when the page is unloaded.
+window.onload = init;
+window.onunload = unload;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_viewInfo;
+var g_pack;
+var g_clock = 0;
+var g_timeMult = 1;
+var g_finished = false; // for selenium testing
+
+/**
+ * Loads a scene into the transform graph.
+ * @param {!o3d.Pack} pack Pack to load scene into.
+ * @param {string} fileName filename of the scene.
+ * @param {!o3d.Transform} parent parent node in the transform graph to
+ * which to load the scene into.
+ */
+function loadScene(pack, fileName, parent) {
+ // Get our full path to the scene
+ var scenePath = o3djs.util.getCurrentURI() + fileName;
+
+ // Load the file given the full path, and call the callback function
+ // when its done loading.
+ o3djs.scene.loadScene(g_client, pack, parent, scenePath, callback);
+
+ /**
+ * Our callback is called once the scene has been loaded into memory
+ * from the web or locally.
+ * @param {!o3d.Pack} pack The pack that was passed in above.
+ * @param {!o3d.Transform} parent The parent that was passed in above.
+ * @param {*} exception null if loading succeeded.
+ */
+ function callback(pack, parent, exception) {
+ if (exception) {
+ alert('Could not load: ' + fileName + '\n' + exception);
+ return;
+ }
+ // Generate draw elements and setup material draw lists.
+ o3djs.pack.preparePack(pack, g_viewInfo);
+
+ // Find the "shape" for the teapot.
+ // I'm going to assume there is only one since I know the contents
+ // of the file.
+ var shape = pack.getObjectsByClassName('o3d.Shape')[0];
+
+ // now make a grid of transforms and put a teapot instance on each one
+ for (var x = 0; x < 10; x++) {
+ for (var y = 0; y < 10; y++) {
+ for (var z = 0; z < 10; z++) {
+ var transform = pack.createObject('Transform');
+ transform.parent = g_client.root;
+ transform.addShape(shape);
+ var angles = [
+ (x - 4.5) * 0.2,
+ (y - 4.5) * 0.2,
+ (z - 4.5) * 0.2];
+ transform.translate((x - 4.5) * 100,
+ (y - 4.5) * 100,
+ (z - 4.5) * 100);
+ transform.rotateZYX(angles);
+ }
+ }
+ }
+
+ g_finished = true; // for selenium testing.
+ }
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and loads the scene into the transform graph.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = o3dElement.client;
+
+ // Creates a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Create our projection matrix, with a vertical field of view of 45 degrees
+ // a near clipping plane of 0.1 and far clipping plane of 10000.
+ g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(45),
+ g_client.width / g_client.height,
+ 0.1,
+ 10000);
+
+ // Creates a transform to put our data on.
+ var my_data_root = g_pack.createObject('Transform');
+
+ // Load the scene into the transform graph as a child my_data_root
+ loadScene(g_pack, 'assets/teapot.o3dtgz', my_data_root);
+
+ // Setup an onrender callback for animation.
+ g_client.setRenderCallback(onrender);
+}
+
+// spin the camera.
+function onrender(render_event) {
+ // Get the number of seconds since the last render.
+ var elapsedTime = render_event.elapsedTime;
+ g_clock += elapsedTime * g_timeMult;
+
+ var x = Math.sin(g_clock * 0.41) * 400;
+ var z = Math.cos(g_clock * 0.31) * 1000;
+ var y = Math.sin(g_clock * 0.61) * 200;
+
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt([x, y, z], // eye
+ [0, 0, 0], // target
+ [0, 1, 0]); // up
+}
+
+/**
+ * Removes any callbacks so they don't get called after the page has unloaded.
+ */
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+</script>
+</head>
+<body>
+<h1>Instancing</h1>
+1000 Teapots
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 800px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/samples/interactive_logic.js b/o3d/samples/interactive_logic.js
new file mode 100644
index 0000000..e2f9eed
--- /dev/null
+++ b/o3d/samples/interactive_logic.js
@@ -0,0 +1,576 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file is the master javascript file for the interactive
+ * editor/runner for all the simple samples.
+ *
+ */
+// For browserFun
+// TODO:
+// Make it so that you can make the code editing window wider...
+// Make it so that you can click links to the documentation of each object
+// Add Ping Pong
+// Fix the spacing in the editor (when u hit tab)
+// Try to see if for the javascript part you can eval line by line so that you
+// can output the line number
+
+var fileTypes = {
+ 'js' : 'javascript',
+ 'html' : 'html',
+ 'php' : 'php'
+};
+
+function InteractiveSample() {
+ this.categories = [];
+ this.codeTitles = [];
+ this.adjustCodeBoxAmount = 50;
+ this.selectCode;
+ this.codeDiv;
+ this.codeLIs = [];
+ this.currentCode = new Object();
+ this.curI = '';
+ this.cleanWindowObj;
+
+ // Assume we're offline until we know that file reading doesn't work
+ this.online = false;
+}
+
+function sortByCategory(a, b) {
+ return a.category > b.category;
+}
+
+function sortByName(a, b) {
+ return a.sampleName > b.sampleName;
+}
+
+function nameToHashName(name) {
+ var hashName = name.toLowerCase();
+ hashName = hashName.replace(/ /g, '_');
+ return hashName;
+}
+
+InteractiveSample.prototype.init = function(codeDiv) {
+ this.codeDiv = codeDiv;
+ this.createCategories();
+ this.addShowHideClicks();
+
+ // TODO: Sort samples
+ // codeArray.sort(sortByCategory);
+ //
+ // for (var i=0; i < codeArray.length; i++) {
+ // codeArray[i].samples.sort(sortByName);
+ // }
+
+ // Gotta set this for the iFrame to know it and change it
+ // This is specific to O3D because a transparent overlay that is
+ // under the <object> tag will actually hide the <object> tag
+ // unless we muck with the zIndex after the <object> loads
+ window.iframeZindex = '4999';
+
+ // This is so that we can restore the first level of the window object
+ // after we run a sample... so samples can get run a lot and not clutter
+ // the window object.
+ this.cleanWindowObj = singleLevelKeyCopy(window);
+};
+
+InteractiveSample.prototype.createCategories = function() {
+ // codeArray is from interactive_samples.js
+ this.selectCode = _gel('selectCode');
+
+ for (var i = 0; i < codeArray.length; i++) {
+ var container = _cel('span');
+ container.className = 'category';
+
+ var catName = _cel('span');
+
+ var img = _cel('img');
+ img.className = 'collapse';
+ img.src = 'interactive_sampler_assets/images/cleardot.gif';
+ // addEvent(img, 'click', this.toggleExpand(img), false);
+
+ catName.appendChild(img);
+ catName.innerHTML += codeArray[i].category;
+
+ container.appendChild(catName);
+
+ var ul = _cel('ul');
+ ul.className = 'categoryItems';
+
+ container.appendChild(ul);
+
+ for (var j = 0; j < codeArray[i].samples.length; j++) {
+ var item = codeArray[i].samples[j];
+ var li = _cel('li');
+
+ li.innerHTML = item.sampleName;
+
+ this.codeTitles.push(li);
+ var files = []
+ for (var index = 0; index < codeArray[i].samples[j].files.length;
+ ++index) {
+ var dirName = '';
+ var baseName = codeArray[i].samples[j].files[index];
+ var slashIndex = baseName.lastIndexOf('/');
+ if (slashIndex >= 0) {
+ dirName = baseName.substring(0, slashIndex + 1);
+ baseName = baseName.substring(slashIndex + 1);
+ }
+ files[index] = dirName + 'sampler_' + baseName;
+ }
+ //var files = codeArray[i].samples[j].files;
+ addEvent(li, 'click', this.showSample(this, item, files, li));
+
+ if (i == 0 && j == 0) {
+ this.showSample(this, item, files, li, true)();
+ }
+
+ if (window.location.hash.length > 0) {
+ var hashName = nameToHashName(item.sampleName);
+ if (window.location.hash.substring(1) == hashName) {
+ this.showSample(this, item, files, li)();
+ }
+ }
+
+ this.codeLIs.push(li);
+ ul.appendChild(li);
+ }
+
+ this.selectCode.appendChild(container);
+ this.categories.push(container);
+ }
+};
+
+InteractiveSample.prototype.toggleShowHide = function(category,
+ interactiveSample) {
+ return function() {
+ var li = category.nextSibling;
+ var el = category.childNodes[0];
+ if (el.className == 'expand')
+ el.className = 'collapse';
+ else
+ el.className = 'expand';
+
+ if (li.style.display == 'none') {
+ li.style.display = 'block';
+ } else {
+ li.style.display = 'none';
+ }
+ };
+};
+
+InteractiveSample.prototype.addShowHideClicks = function() {
+ for (var i = 0; i < this.categories.length; i++) {
+ categoryName = this.categories[i].childNodes[0];
+ addEvent(categoryName, 'click', this.toggleShowHide(categoryName, this));
+ }
+};
+
+InteractiveSample.prototype.loadLocally = function(relativeUrl, filename,
+ fileType,
+ opt_changeCodeMirror) {
+ // readFile is in utils.js
+ var data = readFile(relativeUrl);
+ if (data == null) {
+ this.online = true;
+ return false;
+ }
+ if (fileType == 'javascript') {
+ data = this.replaceV8Call(data);
+ }
+ if (opt_changeCodeMirror == true) {
+ this.changeCodeMirror(data, fileType);
+ }
+ is_instance.currentCode[filename] = {
+ code : data
+ };
+ console.log(relativeUrl + ': loaded locally.');
+
+ return true;
+};
+
+InteractiveSample.prototype.loadRemotely = function(relativeUrl, filename,
+ fileType,
+ opt_changeCodeMirror) {
+ is_instance = this;
+ downloadUrl(relativeUrl, function(data, status) {
+ if (fileType == 'javascript') {
+ data = this.replaceV8Call(data);
+ }
+ if (opt_changeCodeMirror == true) {
+ is_instance.changeCodeMirror(data, fileType);
+ }
+ is_instance.currentCode[filename] = {
+ code : data
+ };
+ });
+};
+
+InteractiveSample.prototype.loadCode = function(filename,
+ opt_changeCodeMirror) {
+ // If the code is in the currentCode buffer, then grab it there
+ // otherwise, load it via XHR
+ // If opt_changeCodeMirror is specified, load it into the window
+
+ // Get filetype
+ var filenameSplit = filename.split('.');
+ var extension = filenameSplit[filenameSplit.length - 1];
+ var fileType = fileTypes[extension.toLowerCase()];
+ var inBuffer = (this.currentCode[filename] &&
+ this.currentCode[filename].code) ? true : false;
+ if (inBuffer && opt_changeCodeMirror == true) {
+ this.changeCodeMirror(this.currentCode[filename].code, fileType);
+ } else {
+ var relativeUrl = filename;
+ is_instance = this;
+
+ if (!this.online) {
+ this.loadLocally(relativeUrl, filename, fileType, opt_changeCodeMirror);
+ }
+
+ if (this.online) {
+ this.loadRemotely(relativeUrl, filename, fileType, opt_changeCodeMirror);
+ }
+ }
+};
+
+// TODO: can is_instance just be set as is_instance = this above return
+// function()
+InteractiveSample.prototype.showSample = function(is_instance, codeItem,
+ files, thisLI, def) {
+ return function() {
+ is_instance.refreshInlinePlugin();
+
+ var codeDiv = is_instance.codeDiv;
+ var codeLIs = is_instance.codeLIs;
+ for (var i = 0; i < codeLIs.length; i++) {
+ codeLIs[i].className = '';
+ }
+
+ // turn off the "Run Here" button if this sample should be run in its own
+ // window.
+ var runHere = document.getElementById('runHere');
+ runHere.style.visibility = codeItem.ownWindow ? 'hidden' : 'visible';
+
+ // For linking purposes
+ if (!def) {
+ window.location.hash = nameToHashName(thisLI.innerHTML);
+ }
+
+ // Make code selected designate this as selected
+ thisLI.className = 'selected';
+
+ is_instance.currentCode = new Object();
+
+
+ // add file names at top
+ var tab_bar = _gel('tab_bar');
+ tab_bar.innerHTML = '';
+
+ // prototype syntax
+ files.each(function(file, index) {
+
+ var tabClass = 'lb';
+ if (index == 0) {
+ tabClass = 'db';
+ is_instance.loadCode(file, true);
+ } else {
+ is_instance.loadCode(file, false);
+ }
+
+
+ var containerDiv = _cel('div');
+ containerDiv.className = 'roundedcornr_box';
+ addEvent(containerDiv, 'click', is_instance.changeTab(file, is_instance));
+
+ var html = '<div class="' + tabClass + '_top" ><div></div></div>';
+ html += '<div class="' + tabClass + '_roundedcornr_content" >';
+ html += file;
+ html += '</div>';
+
+ containerDiv.innerHTML = html;
+
+ tab_bar.appendChild(containerDiv);
+ });
+
+ // is_instance.loadCode(files[0], textArea);
+ is_instance.curI = files[0];
+ };
+};
+
+InteractiveSample.prototype.changeTab = function(i, is_instance) {
+ return function() {
+ var siblings = this.parentNode.childNodes;
+ is_instance.currentCode[is_instance.curI].code = is_instance.getCode();
+
+ // Swap the colors of the tabs
+ for (var z = 0; z < siblings.length; z++) {
+ if (siblings[z].childNodes[1].innerHTML == i) {
+ siblings[z].childNodes[0].className = 'db_top';
+ siblings[z].childNodes[1].className = 'db_roundedcornr_content';
+ } else {
+ siblings[z].childNodes[0].className = 'lb_top';
+ siblings[z].childNodes[1].className = 'lb_roundedcornr_content';
+ }
+ }
+
+ is_instance.loadCode(i, true);
+ is_instance.curI = i;
+ };
+};
+
+InteractiveSample.prototype.increaseCodeBoxHeight = function() {
+ var curHeight = this.textArea.style.height;
+ curHeight = curHeight.substr(0, curHeight.indexOf('px'));
+ var newHeight = parseInt(curHeight) + this.adjustCodeBoxAmount;
+ newHeight += 'px';
+ this.textArea.style.height = newHeight;
+};
+
+InteractiveSample.prototype.decreaseCodeBoxHeight = function() {
+ var curHeight = this.textArea.style.height;
+ curHeight = curHeight.substr(0, curHeight.indexOf('px'));
+ var newHeight = parseInt(curHeight) - this.adjustCodeBoxAmount;
+ newHeight += 'px';
+ this.textArea.style.height = newHeight;
+};
+
+InteractiveSample.prototype.replaceV8Call = function(code) {
+ return code.replace(
+ /(o3djs.util.setMainEngine[^;\n]+)[;\n]/,
+ "\n // Can't use V8 in the interactive sampler yet.\n //$1");
+}
+
+InteractiveSample.prototype.prepareAllCodeRun = function() {
+ // TODO: Change this so it doesn't rely on the first file being HTML
+ // TODO: Change this to use REGEX to replace
+ this.deleteOldWindowStuff();
+
+ this.refreshInlinePlugin();
+ this.currentCode[this.curI].code = this.getCode();
+
+ var html = '';
+ for (var i in this.currentCode) {
+ if (i.indexOf('.html') != -1)
+ html = this.currentCode[i].code;
+ }
+ var pattern = /<script\s*(type\s*=\s*"[^"]*"\s*)?src\s*=\s*"/g;
+ var result;
+ while ((result = pattern.exec(html)) != null) {
+ var scriptSrc = pattern.lastIndex;
+ var scriptSrcEnd = html.indexOf('"', scriptSrc);
+ var script = html.substring(scriptSrc, scriptSrcEnd);
+ var endScriptLoc = html.indexOf('</script>', scriptSrcEnd) + 9;
+ var found = false;
+ for (var z in this.currentCode) {
+ if (z == script) {
+ var data = this.replaceV8Call(this.currentCode[z].code);
+ script = '<script type="text/javascript" charset="utf-8">' + data +
+ '</script>';
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ html = html.substring(0, result.index) + script +
+ html.substring(endScriptLoc);
+ }
+ }
+
+ // console.log(html);
+ window.codeToRun = html;
+
+ // console.log(html.slice(scriptLoc, endScriptLoc));
+ // console.log(html);
+};
+
+InteractiveSample.prototype.refreshInlinePlugin = function() {
+ var o3dobject = _gel('o3d');
+ o3dparent = o3dobject.parentNode;
+ o3dparent.removeChild(o3dobject);
+
+ var nO3D = _cel('div');
+ nO3D.id = 'o3d';
+ nO3D.style.width = '300px';
+ nO3D.style.height = '300px';
+ nO3D.style.backgroundColor = 'gray';
+
+ o3dparent.appendChild(nO3D);
+};
+
+InteractiveSample.prototype.runJS = function() {
+ // TODO don't assume that we run javascript in any order. Make it so that
+ // it checks the HTML for which order JS goes in
+ this.refreshInlinePlugin();
+
+ // console.log(this.currentCode[0].code.split('\n'));
+ window.is.startJS();
+};
+
+InteractiveSample.prototype.deleteOldWindowStuff = function() {
+ for (var i in window) {
+ if (typeof this.cleanWindowObj[i] == 'boolean' &&
+ this.cleanWindowObj[i] == true) {
+ } else {
+ window[i] = null; // Delete doesn't remove properties created with var.
+ delete window[i]; // But just in case we can, we do.
+ }
+ }
+};
+
+InteractiveSample.prototype.startJS = function() {
+ this.deleteOldWindowStuff();
+ var topHolder = _gel('HTMLforInlineJavascript');
+ topHolder.innerHTML = "";
+ var scriptHolder = document.createElement('div');
+ topHolder.appendChild(scriptHolder);
+ var htmlHolder = document.createElement('div');
+ topHolder.appendChild(htmlHolder);
+
+ // NOTE: These are ordered such that no file depends on anything after it.
+ var forced_includes = [
+ "o3djs/base.js",
+ "o3djs/error.js",
+ "o3djs/dump.js",
+ "o3djs/math.js",
+ "o3djs/serialization.js",
+ "o3djs/element.js",
+ "o3djs/shape.js",
+ "o3djs/rendergraph.js",
+ "o3djs/test.js",
+ "o3djs/particles.js",
+ "o3djs/primitives.js",
+ "o3djs/quaternions.js",
+ "o3djs/arcball.js",
+ "o3djs/event.js",
+ "o3djs/debug.js",
+ "o3djs/picking.js",
+ "o3djs/io.js",
+ "o3djs/util.js",
+ "o3djs/effect.js",
+ "o3djs/material.js",
+ "o3djs/camera.js",
+ "o3djs/canvas.js",
+ "o3djs/pack.js",
+ "o3djs/scene.js",
+ "o3djs/loader.js",
+ "o3djs/simple.js",
+ "o3djs/fps.js" // If fps isn't last on this list, update the check below.
+ ];
+
+ for (var i = 0; i < forced_includes.length; ++i) {
+ var scr = document.createElement('script');
+ scr.type = "text/javascript";
+ scr.src = forced_includes[i];
+ scriptHolder.appendChild(scr);
+ }
+
+ this.currentCode[this.curI].code = this.getCode();
+
+ var sampleJS;
+ var sampleHTML;
+ for (var i in this.currentCode) {
+ if (i.indexOf('.html') != -1) {
+ // Change the name and id of the object tag before storing the HTML,
+ // otherwise it will override the inline object we actually want the demo
+ // to be run in.
+ sampleHTML = this.currentCode[i].code;
+ sampleHTML = sampleHTML.replace('id="o3d"', 'id="notouchy"');
+ sampleHTML = sampleHTML.replace('id=\'o3d\'', 'id="notouchy"');
+ sampleHTML = sampleHTML.replace('name="o3d"', 'name="notouchy"');
+ sampleHTML = sampleHTML.replace('name=\'o3d\'', 'name="notouchy"');
+ }
+ if (i.indexOf('.js') != -1) {
+ sampleJS = this.currentCode[i].code;
+ }
+ }
+
+ var intervalId;
+ var callback = function() {
+ // fps is the last on the forced_includes list.
+ if (o3djs != undefined && o3djs.fps) {
+ clearInterval(intervalId);
+ intervalId = null;
+ // TODO Is there any chance that the onload could get called before
+ // we're done loading the script tag? If so, we'll need to add another
+ // stage to the callback.
+ htmlHolder.innerHTML = sampleHTML;
+ var scr = document.createElement('script');
+ scr.type = "text/javascript";
+ scr.text = sampleJS;
+ scriptHolder.appendChild(scr);
+ try {
+ if (typeof window.onload == 'function') {
+ window.onload()
+ } else if (typeof window.init == 'function') {
+ window.init();
+ } else if (typeof window.initClient == 'function') {
+ window.initClient();
+ } else if (typeof window.createClients == 'function') {
+ window.createClients();
+ }
+ } catch(e) {
+ alert(e.message);
+ }
+
+ _gel('o3d').focus();
+ }
+ }
+ intervalId = setInterval(callback, 10);
+};
+
+InteractiveSample.prototype.changeCodeMirror = function(content, lang) {
+ if (lang == 'javascript') {
+ window.jsEditor.setCode(content);
+ window.jsEditor.frame.style.display = 'inline';
+ window.htmlEditor.frame.style.display = 'none';
+ } else if (lang == 'html') {
+ window.htmlEditor.setCode(content);
+ window.htmlEditor.frame.style.display = 'inline';
+ window.jsEditor.frame.style.display = 'none';
+ }
+};
+
+InteractiveSample.prototype.getCode = function() {
+ if (window.htmlEditor.frame.style.display != 'none') {
+ return window.htmlEditor.getCode();
+ } else if (window.jsEditor.frame.style.display != 'none') {
+ return window.jsEditor.getCode();
+ }
+};
+
+// Todo have the window automatically size to the size of the window
+
+InteractiveSample.prototype.increaseWidth = function() {
+ var container = document.getElementById('container');
+ var curWidth = container.style.maxWidth = '1800px';
+};
diff --git a/o3d/samples/interactive_sampler_assets/images/bl.gif b/o3d/samples/interactive_sampler_assets/images/bl.gif
new file mode 100644
index 0000000..244627d
--- /dev/null
+++ b/o3d/samples/interactive_sampler_assets/images/bl.gif
Binary files differ
diff --git a/o3d/samples/interactive_sampler_assets/images/br.gif b/o3d/samples/interactive_sampler_assets/images/br.gif
new file mode 100644
index 0000000..a63a18e
--- /dev/null
+++ b/o3d/samples/interactive_sampler_assets/images/br.gif
Binary files differ
diff --git a/o3d/samples/interactive_sampler_assets/images/cleardot.gif b/o3d/samples/interactive_sampler_assets/images/cleardot.gif
new file mode 100644
index 0000000..1d11fa9
--- /dev/null
+++ b/o3d/samples/interactive_sampler_assets/images/cleardot.gif
Binary files differ
diff --git a/o3d/samples/interactive_sampler_assets/images/corner.png b/o3d/samples/interactive_sampler_assets/images/corner.png
new file mode 100644
index 0000000..51aa458
--- /dev/null
+++ b/o3d/samples/interactive_sampler_assets/images/corner.png
Binary files differ
diff --git a/o3d/samples/interactive_sampler_assets/images/db_tl.png b/o3d/samples/interactive_sampler_assets/images/db_tl.png
new file mode 100644
index 0000000..381883f
--- /dev/null
+++ b/o3d/samples/interactive_sampler_assets/images/db_tl.png
Binary files differ
diff --git a/o3d/samples/interactive_sampler_assets/images/db_tr.png b/o3d/samples/interactive_sampler_assets/images/db_tr.png
new file mode 100644
index 0000000..230205f
--- /dev/null
+++ b/o3d/samples/interactive_sampler_assets/images/db_tr.png
Binary files differ
diff --git a/o3d/samples/interactive_sampler_assets/images/google-small.png b/o3d/samples/interactive_sampler_assets/images/google-small.png
new file mode 100644
index 0000000..e62b8b8
--- /dev/null
+++ b/o3d/samples/interactive_sampler_assets/images/google-small.png
Binary files differ
diff --git a/o3d/samples/interactive_sampler_assets/images/lb_tl.png b/o3d/samples/interactive_sampler_assets/images/lb_tl.png
new file mode 100644
index 0000000..5de27abc
--- /dev/null
+++ b/o3d/samples/interactive_sampler_assets/images/lb_tl.png
Binary files differ
diff --git a/o3d/samples/interactive_sampler_assets/images/lb_tr.png b/o3d/samples/interactive_sampler_assets/images/lb_tr.png
new file mode 100644
index 0000000..d29e333
--- /dev/null
+++ b/o3d/samples/interactive_sampler_assets/images/lb_tr.png
Binary files differ
diff --git a/o3d/samples/interactive_sampler_assets/images/sprites.gif b/o3d/samples/interactive_sampler_assets/images/sprites.gif
new file mode 100644
index 0000000..8132129
--- /dev/null
+++ b/o3d/samples/interactive_sampler_assets/images/sprites.gif
Binary files differ
diff --git a/o3d/samples/interactive_sampler_assets/images/sprites08132008.png b/o3d/samples/interactive_sampler_assets/images/sprites08132008.png
new file mode 100644
index 0000000..64f70f2
--- /dev/null
+++ b/o3d/samples/interactive_sampler_assets/images/sprites08132008.png
Binary files differ
diff --git a/o3d/samples/interactive_sampler_assets/images/sprites2.jpg b/o3d/samples/interactive_sampler_assets/images/sprites2.jpg
new file mode 100644
index 0000000..5cf26f3
--- /dev/null
+++ b/o3d/samples/interactive_sampler_assets/images/sprites2.jpg
Binary files differ
diff --git a/o3d/samples/interactive_sampler_assets/images/tl.gif b/o3d/samples/interactive_sampler_assets/images/tl.gif
new file mode 100644
index 0000000..5eb8006
--- /dev/null
+++ b/o3d/samples/interactive_sampler_assets/images/tl.gif
Binary files differ
diff --git a/o3d/samples/interactive_sampler_assets/images/tr.gif b/o3d/samples/interactive_sampler_assets/images/tr.gif
new file mode 100644
index 0000000..f88810f
--- /dev/null
+++ b/o3d/samples/interactive_sampler_assets/images/tr.gif
Binary files differ
diff --git a/o3d/samples/interactive_sampler_assets/styles.css b/o3d/samples/interactive_sampler_assets/styles.css
new file mode 100644
index 0000000..18d6645
--- /dev/null
+++ b/o3d/samples/interactive_sampler_assets/styles.css
@@ -0,0 +1,282 @@
+body {
+ font-family: Arial, sans-serif;
+}
+
+#container {
+ width: 100%;
+ margin:0pt auto;
+ max-width:1024px;
+}
+
+tr {
+ vertical-align: top;
+}
+
+div {
+/* border: 1px solid #999;*/
+}
+
+#header {
+ height: 50px;
+}
+
+/*#footer {
+ width: 100%;
+
+}*/
+
+#selectSample {
+ width: 30%;
+ display: inline;
+}
+
+#code {
+ width: 70%;
+ display: inline;
+}
+
+.codepress {
+ width:100%;
+ height:550px;
+/* overflow-x: hidden;*/
+}
+
+#codeContainer {
+ width:80%;
+ height:500px;
+ padding-left: 9px;
+ padding-right: 9px;
+
+}
+
+#selectContainer {
+ width:20%;
+ height:100%;
+ padding-bottom: 3px;
+}
+
+#selectCode {
+ width:100%;
+ height: auto;
+ overflow: auto;
+ overflow-x: hidden;
+ margin-bottom: 3px;
+ margin-top: 20px;
+}
+
+.category {
+ font-size: 85%;
+ font-family: Arial, sans-serif;
+ font-weight: bold;
+ display: block;
+}
+
+.categoryItems {
+ margin-top: 0px;
+ margin-bottom: 0px;
+ list-style-type: none;
+ font-size: 80%;
+ font-family: Arial, sans-serif;
+ padding-left: 0px;
+}
+
+#outputContainer {
+ /* width: 400px;*/
+ /* height: 400px;*/
+ /* width: 70%;*/
+}
+
+#consoleContainer {
+ height: 300px;
+ width: 30%;
+ border: 1px solid #999;
+}
+
+iframe {
+/* overflow-x: hidden;*/
+}
+
+.contentTable {
+ width: 100%;
+ height: 100%;
+}
+
+#header {
+ margin: 0px;
+ display: inline;
+}
+
+.bluehr {
+ background-color:transparent;
+ border:0pt none;
+ float:left;
+ font-size:1.5em;
+ font-weight:bold;
+ line-height:1.3em;
+ margin:0pt;
+ padding:0pt 0pt 0pt 0.3em;
+}
+
+h2 {
+ border-bottom:1px solid #CCCCCC;
+ font-size:1.3em;
+ margin-bottom:0pt;
+ font-family:Helvetica,Arial,sans-serif;
+}
+
+.bluediv {
+ background-color:#E5ECF9;
+ border-top:1px solid #3366CC;
+ font-size:1em;
+ margin:0pt;
+ padding:0.1em 0pt;
+ white-space:nowrap;
+ width:100%;
+}
+
+li.selected {
+ padding:0.2em 0pt 0.2em 15px;
+ background-color:#E5ECF9;
+ color:#000000;
+ position:relative;
+ text-decoration:none;
+ z-index:2;
+ list-style-image:none;
+ list-style-position:outside;
+ list-style-type:none;
+ margin:0pt;
+ vertical-align:middle;
+ line-height:100%;
+ cursor: default;
+}
+
+li {
+ padding-left: 15px;
+ cursor: pointer;
+ text-decoration: underline;
+ color: blue;
+}
+
+img.expand, img.collapse {
+ border:medium none;
+ height:inherit;
+ margin-bottom: 4px;
+ margin-right: 5px;
+ padding:0pt;
+ position:relative;
+ width:9px;
+ background-image:url(interactive_sampler_assets/images/sprites.gif);
+ vertical-align:middle;
+ cursor: pointer;
+}
+
+img.collapse {
+ background-position: -28px -310px; /* The empty Plus */
+}
+
+img.collapse:hover {
+ background-position:-28px -508px; /* The filled Plus */
+}
+
+img.expand {
+ background-position: -28px -246px; /* The empty Plus */
+}
+
+img.expand:hover {
+ background-position:-28px -444px; /* The filled Plus */
+}
+
+/*.separator {
+ height: 100%;
+ background:#E5ECF9 none repeat scroll 0% 0%;
+ border-color:-moz-use-text-color #FFFFFF;
+ border-style:1px solid;
+ border-width:medium 2px;
+ cursor:pointer;
+ left:-5px;
+ overflow:hidden;
+/* position:absolute;*/
+/* top:0pt;
+ margin-top: 63px;
+ width:4px;
+}*/
+
+#gc-footer-img {
+ background-image:url(interactive_sampler_assets/images/sprites.gif);
+ background-position:-28px -28px;
+ display:inline;
+ float:left;
+ height:53px;
+ margin:12px 0pt;
+ width:143px;
+}
+
+#gc-footer .text {
+ margin:0pt 0pt 0pt 170px;
+ padding:30px 0pt;
+ text-align:center;
+}
+
+#gc-footer {
+ color:#666666;
+ margin:0pt;
+ width:100%;
+}
+
+td.footer {
+ padding-top: 60px;
+}
+
+#googleLogoTD {
+ text-align:right;
+ vertical-align:middle;
+ padding-right: 7px;
+}
+
+#tab_bar {
+ margin-top:20px;
+ cursor: pointer;
+}
+
+#button_bar {
+ text-align: right;
+}
+
+.roundedcornr_box {
+ float:left;
+ margin: 0px 4px;
+}
+
+.db_top div {
+ background: url(interactive_sampler_assets/images/db_tl.png) no-repeat top left;
+}
+.db_top {
+ background: #92c1f0 url(interactive_sampler_assets/images/db_tr.png) no-repeat top right;
+}
+
+.lb_top div {
+ background: url(interactive_sampler_assets/images/lb_tl.png) no-repeat top left;
+}
+
+.lb_top {
+ background: #D0E4F6 url(interactive_sampler_assets/images/lb_tr.png) no-repeat top right;
+}
+
+.db_top div, .db_top,
+.lb_top div, .lb_top {
+ height: 5px;
+ font-size: 1px;
+}
+.lb_roundedcornr_content,
+.db_roundedcornr_content {
+ padding: 0px 10px;
+ font-size: 80%;
+}
+
+.lb_roundedcornr_content {
+ background-color: #D0E4F6;
+}
+
+.db_roundedcornr_content {
+ background-color: #92c1f0;
+}
diff --git a/o3d/samples/interactive_sampler_assets/styles_ie.css b/o3d/samples/interactive_sampler_assets/styles_ie.css
new file mode 100644
index 0000000..9fe30ab
--- /dev/null
+++ b/o3d/samples/interactive_sampler_assets/styles_ie.css
@@ -0,0 +1,301 @@
+body {
+ font-family: Arial, sans-serif;
+}
+
+#container {
+ width: 100%;
+ margin:0pt auto;
+ max-width:1024px;
+ width:expression(
+ document.body.clientWidth > 1024 ?
+ "1024px":
+ "auto" );
+}
+
+tr {
+ vertical-align: top;
+}
+
+div {
+/* border: 1px solid #999;*/
+}
+
+#header {
+ height: 50px;
+}
+
+/*#footer {
+ width: 100%;
+
+}*/
+
+#selectSample {
+ width: 30%;
+ display: inline;
+}
+
+#code {
+ width: 70%;
+ display: inline;
+}
+
+.codepress {
+ width:100%;
+ height:550px;
+/* overflow-x: hidden;*/
+}
+
+#codeContainer {
+ width:80%;
+ height:500px;
+ padding-left: 9px;
+ padding-right: 9px;
+
+}
+
+#selectContainer {
+ width:20%;
+ height:100%;
+ padding-bottom: 3px;
+}
+
+#selectCode {
+ width:100%;
+ height: auto;
+ overflow: auto;
+ overflow-x: hidden;
+ margin-bottom: 3px;
+ margin-top: 20px;
+}
+
+.category {
+ font-size: 85%;
+ font-family: Arial, sans-serif;
+ font-weight: bold;
+ display: block;
+}
+
+.categoryItems {
+ margin-top: 0px;
+ margin-bottom: 0px;
+ list-style-type: none;
+ font-size: 80%;
+ font-family: Arial, sans-serif;
+ padding-left: 0px;
+}
+
+#outputContainer {
+ /* width: 400px;*/
+ /* height: 400px;*/
+ /* width: 70%;*/
+}
+
+#consoleContainer {
+ height: 300px;
+ width: 30%;
+ border: 1px solid #999;
+}
+
+iframe {
+/* overflow-x: hidden;*/
+}
+
+.contentTable {
+ width: 100%;
+ height: 100%;
+}
+
+#header {
+ margin: 0px;
+ display: inline;
+}
+
+.bluehr {
+ background-color:transparent;
+ border:0pt none;
+ float:left;
+ font-size:1.5em;
+ font-weight:bold;
+ line-height:1.3em;
+ margin:0pt;
+ padding:0pt 0pt 0pt 0.3em;
+}
+
+h2 {
+ border-bottom:1px solid #CCCCCC;
+ font-size:1.3em;
+ margin-bottom:0pt;
+ font-family:Helvetica,Arial,sans-serif;
+}
+
+.bluediv {
+ background-color:#E5ECF9;
+ border-top:1px solid #3366CC;
+ font-size:1em;
+ margin:0pt;
+ padding:0.1em 0pt;
+ white-space:nowrap;
+ width:100%;
+}
+
+li.selected {
+ padding:0.2em 0pt 0.2em 15px;
+ background-color:#E5ECF9;
+ color:#000000;
+ position:relative;
+ text-decoration:none;
+ z-index:2;
+ list-style-image:none;
+ list-style-position:outside;
+ list-style-type:none;
+ margin:0pt;
+ vertical-align:middle;
+ line-height:100%;
+ cursor: default;
+}
+
+li {
+ padding-left: 15px;
+ cursor: pointer;
+ text-decoration: underline;
+ color: blue;
+}
+
+img.expand, img.collapse {
+ border:medium none;
+ height:inherit;
+ margin-bottom: 4px;
+ margin-right: 5px;
+ padding:0pt;
+ position:relative;
+ width:9px;
+ background-image:url(interactive_sampler_assets/images/sprites.gif);
+ vertical-align:middle;
+ cursor: pointer;
+}
+
+img.collapse {
+ background-position: -28px -310px; /* The empty Plus */
+}
+
+img.collapse:hover {
+ background-position:-28px -508px; /* The filled Plus */
+}
+
+img.expand {
+ background-position: -28px -246px; /* The empty Plus */
+}
+
+img.expand:hover {
+ background-position:-28px -444px; /* The filled Plus */
+}
+
+/*.separator {
+ height: 100%;
+ background:#E5ECF9 none repeat scroll 0% 0%;
+ border-color:-moz-use-text-color #FFFFFF;
+ border-style:1px solid;
+ border-width:medium 2px;
+ cursor:pointer;
+ left:-5px;
+ overflow:hidden;
+/* position:absolute;*/
+/* top:0pt;
+ margin-top: 63px;
+ width:4px;
+}*/
+
+#gc-footer-img {
+ background-image:url(interactive_sampler_assets/images/sprites.gif);
+ background-position:-28px -28px;
+ display:inline;
+ float:left;
+ height:53px;
+ margin:12px 0pt;
+ width:143px;
+}
+
+#gc-footer .text {
+ margin:0pt 0pt 0pt 170px;
+ padding:30px 0pt;
+ text-align:center;
+}
+
+#gc-footer {
+ color:#666666;
+ margin:0pt;
+ width:100%;
+}
+
+td.footer {
+ padding-top: 60px;
+}
+
+#lbinner {
+ width:expression(
+ document.body.clientWidth > 1100 ?
+ "1100px":
+ "auto" );
+}
+
+#lightbox {
+ position: absolute;
+}
+
+#overlay {
+ width: 1000%;
+}
+
+#googleLogoTD {
+ text-align:right;
+ vertical-align:middle;
+}
+
+#tab_bar {
+ margin-top:20px;
+ margin-bottom: -2px;
+ cursor: pointer;
+}
+
+#button_bar {
+ text-align: right;
+}
+
+.roundedcornr_box {
+ float:left;
+ margin: 0px 4px;
+}
+
+.db_top div {
+ background: url(interactive_sampler_assets/images/db_tl.png) no-repeat top left;
+}
+.db_top {
+ background: #92c1f0 url(interactive_sampler_assets/images/db_tr.png) no-repeat top right;
+}
+
+.lb_top div {
+ background: url(interactive_sampler_assets/images/lb_tl.png) no-repeat top left;
+}
+
+.lb_top {
+ background: #D0E4F6 url(interactive_sampler_assets/images/lb_tr.png) no-repeat top right;
+}
+
+.db_top div, .db_top,
+.lb_top div, .lb_top {
+ height: 5px;
+ font-size: 1px;
+}
+.lb_roundedcornr_content,
+.db_roundedcornr_content {
+ padding: 0px 10px;
+ font-size: 80%;
+}
+
+.lb_roundedcornr_content {
+ background-color: #D0E4F6;
+}
+
+.db_roundedcornr_content {
+ background-color: #92c1f0;
+}
diff --git a/o3d/samples/interactive_sampler_assets/utils.js b/o3d/samples/interactive_sampler_assets/utils.js
new file mode 100644
index 0000000..8132df1
--- /dev/null
+++ b/o3d/samples/interactive_sampler_assets/utils.js
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains utility functions for the interactive
+ * sample runner.
+ *
+ */
+
+var ie;
+var opera;
+var safari;
+var gecko;
+
+
+if (typeof console == 'undefined') {
+ var console = {
+ log : function() {}
+ };
+}
+
+function readFile(relativePathToFile) {
+ var curPath = location.href;
+ var fileLoc = curPath.indexOf('file:///');
+ var lastSlash = curPath.lastIndexOf('/');
+ curPath = curPath.substring(0, lastSlash + 1);
+ if (fileLoc != -1) {
+ curPath = curPath.substring(fileLoc + 8);
+ }
+ var filepath = curPath + relativePathToFile;
+ filepath = filepath.replace(/\//g, '\\');
+ filepath = filepath.replace(/%20/g, ' ');
+
+ try {
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ } catch (e) {
+ return null;
+ }
+ var file = Components.classes["@mozilla.org/file/local;1"].createInstance(
+ Components.interfaces.nsILocalFile);
+ file.initWithPath( filepath );
+ if ( file.exists() == false ) {
+ alert("File does not exist");
+ }
+ var is = Components.classes["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Components.interfaces.nsIFileInputStream);
+ is.init( file,0x01, 00004, null);
+ var sis = Components.classes["@mozilla.org/scriptableinputstream;1"].
+ createInstance(Components.interfaces.nsIScriptableInputStream);
+ sis.init(is);
+ var output = sis.read( sis.available() );
+ return output;
+}
+
+/**
+* Returns an XMLHttp instance to use for asynchronous
+* downloading. This method will never throw an exception, but will
+* return NULL if the browser does not support XmlHttp for any reason.
+* @return {XMLHttpRequest|Null}
+*/
+function createXmlHttpRequest() {
+ try {
+ if (typeof ActiveXObject != 'undefined') {
+ return new ActiveXObject('Microsoft.XMLHTTP');
+ } else if (window["XMLHttpRequest"]) {
+ return new XMLHttpRequest();
+ }
+ } catch (e) {
+ changeStatus(e);
+ }
+ return null;
+};
+
+/**
+* This functions wraps XMLHttpRequest open/send function.
+* It lets you specify a URL and will call the callback if
+* it gets a status code of 200.
+* @param {String} url The URL to retrieve
+* @param {Function} callback The function to call once retrieved.
+*/
+function downloadUrl(url, callback) {
+ var status = -1;
+ var request = createXmlHttpRequest();
+ if (!request) {
+ return false;
+ }
+
+ request.onreadystatechange = function() {
+ if (request.readyState == 4) {
+ try {
+ status = request.status;
+ } catch (e) {
+ // Usually indicates request timed out in FF.
+ }
+ if (status == 200) {
+ callback(request.responseText, request.status);
+ request.onreadystatechange = function() {};
+ }
+ }
+ };
+
+ request.open('GET', url, true);
+ try {
+ request.send(null);
+ } catch (e) {
+ changeStatus(e);
+ }
+}
+
+
+function deepObjCopy(dupeObj) {
+ var retObj = new Object();
+ if (typeof(dupeObj) == 'object') {
+ if (typeof(dupeObj.length) != 'undefined')
+ var retObj = new Array();
+ for (var objInd in dupeObj) {
+ if (typeof(dupeObj[objInd]) == 'object') {
+ retObj[objInd] = deepObjCopy(dupeObj[objInd]);
+ } else if (typeof(dupeObj[objInd]) == 'string') {
+ retObj[objInd] = dupeObj[objInd];
+ } else if (typeof(dupeObj[objInd]) == 'number') {
+ retObj[objInd] = dupeObj[objInd];
+ } else if (typeof(dupeObj[objInd]) == 'boolean') {
+ ((dupeObj[objInd] == true) ?
+ retObj[objInd] = true : retObj[objInd] = false);
+ }
+ }
+ }
+ return retObj;
+}
+
+function singleLevelKeyCopy(dupeObj) {
+ var retObj = new Object();
+ for (var objInd in dupeObj) {
+ retObj[objInd] = true;
+ }
+ return retObj;
+}
+
+function browserFun() {
+ // Browser fun.
+ if (window.ActiveXObject) {
+ ie = this[window.XMLHttpRequest ? 'ie7' : 'ie6'] = true;
+ } else if (window.opera) {
+ opera = true;
+ } else if (document.childNodes && !document.all && !navigator.taintEnabled) {
+ safari = true;
+ } else if (document.getBoxObjectFor != null) {
+ gecko = true;
+ }
+}
+
+function _cel(name) {
+ return document.createElement(name);
+}
+
+function _gel(name) {
+ return document.getElementById(name);
+}
+
+function addEvent(a,b,c,d) {
+ if (a.addEventListener) {
+ a.addEventListener(b,c,d?true:false);
+ } else if(a.attachEvent) {
+ a.attachEvent('on'+b,c);
+ } else{
+ a['on'+b]=c;
+ }
+}
+
+browserFun();
diff --git a/o3d/samples/interactive_samples.js b/o3d/samples/interactive_samples.js
new file mode 100644
index 0000000..d5e818d
--- /dev/null
+++ b/o3d/samples/interactive_samples.js
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+var codeArray = [
+ {
+ 'category': 'Basic',
+ 'samples': [
+ {'files':
+ ['simple.html', 'simple.js'],
+ 'sampleName': 'Simple'},
+ {'files':
+ ['helloworld.html', 'helloworld.js'],
+ 'sampleName': 'Hello World'},
+ {'files':
+ ['customcamera.html', 'customcamera.js'],
+ 'sampleName': 'Custom Camera',
+ 'ownWindow': true},
+ {'files':
+ ['simpletexture.html', 'simpletexture.js'],
+ 'sampleName': 'Simple Texture'},
+ {'files':
+ ['hellocube.html', 'hellocube.js'],
+ 'sampleName': 'Simple Cube'},
+ {'files':
+ ['hellocube-colors.html', 'hellocube-colors.js'],
+ 'sampleName': 'Simple Cube With Colors',
+ 'ownWindow': true},
+ {'files':
+ ['hellocube-textures.html', 'hellocube-textures.js'],
+ 'sampleName': 'Simple Cube With Textures'}
+ ]
+ },
+ {
+ 'category': 'Advanced',
+ 'samples': [
+ {'files':
+ ['2d.html', '2d.js'],
+ 'sampleName': '2d'},
+ {'files':
+ ['culling.html', 'culling.js'],
+ 'sampleName': 'Culling',
+ 'ownWindow': true},
+ {'files':
+ ['displayfps.html', 'displayfps.js'],
+ 'sampleName': 'Display FPS',
+ 'ownWindow': true},
+ {'files':
+ ['hud-2d-overlay.html', 'hud-2d-overlay.js'],
+ 'sampleName': 'HUD 2D Overlay'},
+ {'files':
+ ['instancing.html', 'instancing.js'],
+ 'sampleName': 'Instancing'},
+ {'files':
+ ['instance-override.html', 'instance-override.js'],
+ 'sampleName': 'Instance Override'},
+ {'files':
+ ['juggler.html', 'juggler.js'],
+ 'sampleName': 'Juggler'},
+ {'files':
+ ['julia.html', 'julia.js'],
+ 'sampleName': 'Julia'},
+ {'files':
+ ['multiple-clients.html', 'multiple-clients.js'],
+ 'sampleName': 'Multiple Clients',
+ 'ownWindow': true},
+ {'files':
+ ['multiple-views.html', 'multiple-views.js'],
+ 'sampleName': 'Multiple Views'},
+ {'files':
+ ['phongshading.html', 'phongshading.js'],
+ 'sampleName': 'Phong Shading'},
+ {'files':
+ ['picking.html', 'picking.js'],
+ 'sampleName': 'Picking'},
+ {'files':
+ ['render-mode.html', 'render-mode.js'],
+ 'sampleName': 'Render Mode',
+ 'ownWindow': true},
+ {'files':
+ ['render-targets.html', 'render-targets.js'],
+ 'sampleName': 'Render Targets'},
+ {'files':
+ ['convolution.html', 'convolution.js'],
+ 'sampleName': 'Convolution Shader'},
+ {'files':
+ ['sobel.html', 'sobel.js'],
+ 'sampleName': 'Sobel Edge Detection'},
+ {'files':
+ ['rotatemodel.html', 'rotatemodel.js'],
+ 'sampleName': 'Rotate Model'},
+ {'files':
+ ['scatter-chart.html', 'scatter-chart.js'],
+ 'sampleName': 'Scatter Chart',
+ 'ownWindow': true},
+ {'files':
+ ['shader-test.html', 'shader-test.js'],
+ 'sampleName': 'Shader Test',
+ 'ownWindow': true},
+ {'files':
+ ['stencil_example.html', 'stencil_example.js'],
+ 'sampleName': 'Stencil Example'},
+ {'files':
+ ['error-texture.html', 'error-texture.js'],
+ 'sampleName': 'Error Texture',
+ 'ownWindow': true},
+ {'files':
+ ['texturesamplers.html', 'texturesamplers.js'],
+ 'sampleName': 'Texture Samplers'},
+ {'files':
+ ['generate-texture.html', 'generate-texture.js'],
+ 'sampleName': 'Generating A Texture'},
+ {'files':
+ ['procedural-texture.html', 'procedural-texture.js'],
+ 'sampleName': 'Procedural Texture'},
+ {'files':
+ ['zsorting.html', 'zsorting.js'],
+ 'sampleName': 'Z-Sorting'},
+ {'files':
+ ['canvas.html', 'canvas.js'],
+ 'sampleName': 'Canvas' },
+ {'files':
+ ['canvas-texturedraw.html', 'canvas-texturedraw.js'],
+ 'sampleName': 'Canvas: Drawing With Bitmaps',
+ 'ownWindow': true},
+ {'files':
+ ['vertex-shader.html', 'vertex-shader.js'],
+ 'sampleName': 'Vertex Shader' },
+ {'files':
+ ['primitives.html', 'primitives.js'],
+ 'sampleName': 'Primitives',
+ 'ownWindow': true },
+ {'files':
+ ['tutorial-primitive.html', 'tutorial-primitive.js'],
+ 'sampleName': 'Tutorial Primitive',
+ 'ownWindow': true },
+ {'files':
+ ['skinning.html', 'skinning.js'],
+ 'sampleName': 'Skinning' },
+ {'files':
+ ['animation.html', 'animation.js'],
+ 'sampleName': 'Animation' },
+ {'files':
+ ['animated-scene.html', 'animated-scene.js'],
+ 'sampleName': 'Animated Scene',
+ 'ownWindow': true },
+ {'files':
+ ['particles.html', 'particles.js'],
+ 'sampleName': 'Particles' },
+ {'files':
+ ['vertex-shader-animation.html', 'vertex-shader-animation.js'],
+ 'sampleName': 'Vertex Shader Animation' }
+ ]
+ }
+];
+
diff --git a/o3d/samples/io/README.txt b/o3d/samples/io/README.txt
new file mode 100644
index 0000000..e6ede03
--- /dev/null
+++ b/o3d/samples/io/README.txt
@@ -0,0 +1,29 @@
+Steps to Create a level:
+
+1. Install the SketchUp Level Creation tools
+2. Start SU 6
+3. Draw your "world path" using the line tool
+
+- Using the line tool, draw a line from the drawing origin along the solid green axis line (that's your x axis) It can be as long or as short as you want.
+- From there, you can draw lines continuously from the end of your first line segment. Each line is a distinct plane in the world path.
+- Only draw horizontal lines along the Sketchup drawing's ground plane. No slopes or vertical changes in altitude.
+- Your avatar will NOT walk on these lines. They are only used to define the path that the world will run along.
+
+4. Draw your platforms
+- Each horizontal line must be exactly parallel and exactly "above" or "below" one of your "world path" line segments.
+- Good way to do this is to CTRL+MOVE your base lines up or down, and then chop them up from there.
+- Each horizontal line is a platform that your avatar can walk on.
+- Each vertical line is an "obstacle" that your avatar can't walk through.
+
+5. Drag in your actors
+- The installer created a directory under components > Prince IO (or it will, once we're done with it. -SEL)
+- Drag these suckers in. Note that you're going to want to put them on line segments, typically.
+
+6. Draw random geometry only in components and groups.
+
+7. Test everything in SU.
+- Press the "play" button in the Prince IO level designer. You can play the game.
+- Note: Set your field of view to 45 degrees to have approximately the same camera view as in O3D.
+
+8. Press the "export" button and save your myLevelName.js and myLevelName.kmz into your prince IO /levels directory.
+(this will also automatically generate the autoincludes.js file.)
diff --git a/o3d/samples/io/actors/actor.js b/o3d/samples/io/actors/actor.js
new file mode 100644
index 0000000..a0e1e50
--- /dev/null
+++ b/o3d/samples/io/actors/actor.js
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file defines the Actor class.
+ */
+
+/**
+ * Pulls in all names attributes of a source object and applies them to a
+ * target object. Handy for pulling in a bunch of name:value pairs that were
+ * passed all at once.
+ */
+function Actor() {
+ // Create some defaults for our "required" attributes.
+ this.x = 0;
+ this.y = 0;
+ this.z = 0;
+ this.width = 20;
+ this.height = 20;
+ this.mapX = 0;
+ this.platformID = 0;
+ this.rotZ = 0;
+ this.frameName = '';
+}
+
+Actor.prototype.absorbNamedValues = function(initObj) {
+ for (var key in initObj) {
+ this[key] = initObj[key];
+ }
+};
+
+/**
+ * Note that for collision detection, all actors are envisioned as 2d rectangles
+ * inside a single plane. These rectangles have a width and a height, and their
+ * "origin" is on the bottom center of the rectangle. So if you create a new
+ * actor, be sure to give it a reasonable width and height.
+ */
+Actor.prototype.collidesWith = function(otherActor) {
+ thisLeft = this.mapX - this.width/2;
+ thisRight = this.mapX + this.width/2;
+ thisTop = this.z + this.height;
+ thisBottom = this.z;
+
+ otherLeft = otherActor.mapX - otherActor.width/2;
+ otherRight = otherActor.mapX + otherActor.width/2;
+ otherTop = otherActor.z + otherActor.height;
+ otherBottom = otherActor.z;
+
+ // First see if we're not overlapping in any dimension.
+ if (thisRight < otherLeft ||
+ thisLeft > otherRight ||
+ thisBottom > otherTop ||
+ thisTop < otherBottom) {
+ return false;
+ }
+
+ // Next check for overlap along X.
+ if (thisRight >= otherLeft &&
+ thisLeft <= otherRight) {
+ // then we're still in the running...
+ } else {
+ return false;
+ }
+
+ // Next check for overlap along Y.
+ if (thisBottom <= otherTop &&
+ thisTop >= otherBottom) {
+ return true;
+ }
+
+ // We're not overlapping in Y, so bomb.
+ return false;
+};
+
+Actor.prototype.isHitBySword = function() {
+ var isHit = false;
+ if (avatar.animation == "Hero_Sword" && avatar.frame > 3) {
+ avatar.width += 80;
+ avatar.height += avatar.frame * 5;
+ if (this.collidesWith(avatar)) {
+ var isHit = true;
+ }
+ avatar.width -= 80;
+ avatar.height -= avatar.frame * 5;
+ }
+ return isHit;
+}
+
+Actor.prototype.isHitByArrow = function() {
+ var isHit = false;
+ // TODO: Make this allow multiple arrows, perform better, etc.
+ if (top.arrowActor != undefined) {
+ // If we don't have the same "parentPlatform, meaning we're in different
+ // world path space, then we don't collide.
+ if (world.platforms[top.arrowActor.platformID].parentID !=
+ world.platforms[this.platformID].parentID) {
+ return false;
+ }
+ if (this.collidesWith(top.arrowActor)) {
+ var isHit = true;
+ }
+ }
+ return isHit;
+}
+
+Actor.prototype.moveMapX = function(change) {
+ var platformAngle = world.platforms[this.platformID].rotZ;
+ this.mapX += change;
+ this.x += change * Math.cos(platformAngle);
+ this.y += change * Math.sin(platformAngle);
+};
+
+
+
+
diff --git a/o3d/samples/io/actors/arrow.js b/o3d/samples/io/actors/arrow.js
new file mode 100644
index 0000000..0103b5c
--- /dev/null
+++ b/o3d/samples/io/actors/arrow.js
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file defines the Arrow class.
+ */
+
+/**
+ * An arrow that Io can shoot.
+ */
+function Arrow(initObj) {
+ this.absorbNamedValues(initObj);
+
+ // Hide myself by moving way off the screen.
+ this.z = -10000;
+ this.mapX = -10000;
+ this.width = 40;
+ this.height = 1;
+ this.velX = 0;
+
+ // Figure out which arrow number I am.
+ this.arrowNumber = parseInt(initObj.name.substr(initObj.name.length-1,1));
+
+}
+Arrow.prototype = new Actor;
+
+Arrow.prototype.onTick = function(timeElapsed) {
+ if (this.mapX < avatar.mapX-500 || this.mapX > avatar.mapX+500 &&
+ this.z > -10000) {
+ // then hide ourselves way off screen.
+ this.mapX = -10000;
+ this.z = -10000;
+ updateActor(this);
+ } else {
+ // Here's how I move. moveMapX is a handy function that updates both
+ // my "virtual 2d world" mapX, as well as my literal x + y values
+ this.moveMapX(this.velX * timeElapsed)
+ updateActor(this);
+ }
+}
+
+Arrow.prototype.shoot = function() {
+ this.x = avatar.x;
+ this.y = avatar.y;
+ this.z = avatar.z + 33; // Approximate height of IO's crossbow.
+ this.platformID = avatar.platformID;
+ this.parentPlatformID = avatar.parentPlatformID;
+ this.rotZ = world.platforms[this.platformID].rotZ;
+ this.mapX = avatar.mapX;
+
+ if (Math.abs(avatar.rotZ - this.rotZ) < Math.PI/2) {
+ this.velX = 50 * 20;
+ } else {
+ this.velX = -50 * 20;
+ this.rotZ -= Math.PI;
+ }
+ soundPlayer.play('sound/arrow.mp3', 100);
+ updateActor(this);
+}
+
+// TODO: This should be a generic concept, not an arrow-specifc
+// thing.
+Arrow.prototype.hide = function() {
+ this.mapX = -10000;
+}
diff --git a/o3d/samples/io/actors/avatar.js b/o3d/samples/io/actors/avatar.js
new file mode 100644
index 0000000..aac4f45e
--- /dev/null
+++ b/o3d/samples/io/actors/avatar.js
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Our avatar object. Hooray!
+ */
+function Avatar(initObj) {
+ this.absorbNamedValues(initObj);
+
+ this.animation = "Hero_Stand"
+ this.velX = 0;
+ this.velY = 0;
+ this.velZ = 0;
+ this.targetRotZ = 0; // Where we'd like to be facing.
+ this.targetVelX = 0; // Speed we'd like to be going.
+ this.swordRate = .5;
+
+ this.width = 30;
+ this.height = 50;
+
+ this.frame = 1;
+ this.framesSinceShot = 0; // If > 0, tack a "#1" onto our instance name.
+ this.isJumping = false;
+
+ // We always start on platform 0 to fix a placement bug.
+ this.platformID = 0;
+ this.x = 0;
+ this.y = 0;
+ this.mapX = 0;
+ this.z = 200;
+ this.parentPlatformID = 0;
+
+}
+
+Avatar.prototype = new Actor;
+
+Avatar.prototype.onTick = function(timeElapsed) {
+ updateActor(this);
+}
diff --git a/o3d/samples/io/actors/coin.js b/o3d/samples/io/actors/coin.js
new file mode 100644
index 0000000..1124d5e
--- /dev/null
+++ b/o3d/samples/io/actors/coin.js
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file defines the Coin class.
+ */
+
+/**
+ * A Coin to be picked up
+ */
+function Coin(initObj) {
+ this.absorbNamedValues(initObj);
+ this.hasBeenPickedUp = false;
+ this.width = 14;
+ this.height = 14;
+ this.isHidden = false;
+}
+Coin.prototype = new Actor;
+
+Coin.prototype.onTick = function(timeElapsed) {
+ if (this.isHidden == true) {
+ return;
+ } else if (this.hasBeenPickedUp == true) {
+ this.z = (this.z*6 + eyeZ+40)/7;
+ this.x = (this.x*3 + eyeX)/4;
+ this.y = (this.y*3 + eyeY)/4;
+ if (Math.abs(this.z - eyeZ+40) < 1) {
+ this.isHidden = true;
+ this.z = -10000;
+ }
+ } else {
+ if (this.collidesWith(avatar)) {
+ soundPlayer.play('sound/coin_3.mp3', 100, 0, true);
+ this.hasBeenPickedUp = true;
+ }
+ }
+ this.rotZ += .4 * 20 * timeElapsed;
+ updateActor(this);
+}
diff --git a/o3d/samples/io/actors/horizontalpad.js b/o3d/samples/io/actors/horizontalpad.js
new file mode 100644
index 0000000..b2e3f85
--- /dev/null
+++ b/o3d/samples/io/actors/horizontalpad.js
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file defines the HorizontalPad class.
+ */
+
+/**
+ * A Horizontal Floater.
+ */
+function HorizontalPad(initObj) {
+ this.absorbNamedValues(initObj);
+ this.maxSpeed = 4 * 20;
+ this.moveAmount = 4 * 20;
+ this.width = 42;
+ this.height = 1;
+}
+HorizontalPad.prototype = new Actor;
+
+HorizontalPad.prototype.onTick = function(timeElapsed) {
+ // When you attach me to a platform, I make it so nobody
+ // can stand on it... only me.
+ world.platforms[this.platformID].isNotSolid = true;
+
+ // I move based off of the platform that I'm on. So get that now.
+ var myPlatform = world.platforms[this.platformID];
+
+ // I bounce back and forth between the left and right points on my platform.
+ if (this.mapX < myPlatform.left.mapX) {
+ this.moveAmount += 1;
+ } else if (this.mapX > myPlatform.right.mapX) {
+ this.moveAmount -= 1;
+ } else if (this.moveAmount >= 0) {
+ this.moveAmount = this.maxSpeed;
+ } else {
+ this.moveAmount = -this.maxSpeed;
+ }
+
+ // I match my rotation to that of my platform.
+ this.rotZ = myPlatform.rotZ;
+
+ // Here's how I move. moveMapX is a handy function that updates both
+ // my "virtual 2d world" mapX, as well as my literal x + y values
+ this.moveMapX(this.moveAmount * timeElapsed)
+
+ // If I collide with the avatar, then move the avatar along with me,
+ // and set his "override" groundZ to be my own Z.
+ if (this.collidesWith(avatar)) {
+ if (avatar.velZ <= 0 && avatar.z >= this.z - 25) {
+ avatar.moveMapX(this.moveAmount * timeElapsed);
+ avatar.groundZ = this.z;
+ }
+ }
+
+ updateActor(this);
+}
diff --git a/o3d/samples/io/actors/mover.js b/o3d/samples/io/actors/mover.js
new file mode 100644
index 0000000..8a36eed
--- /dev/null
+++ b/o3d/samples/io/actors/mover.js
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file defines the Mover class.
+ */
+
+/**
+ * A Horizontal Floater.
+ */
+function Mover(initObj) {
+ this.absorbNamedValues(initObj);
+}
+Mover.prototype = new Actor;
+
+Mover.prototype.onTick = function(timeElapsed) {
+ this.z += 1 * 20 * timeElapsed;
+ updateActor(this);
+}
diff --git a/o3d/samples/io/actors/spikem.js b/o3d/samples/io/actors/spikem.js
new file mode 100644
index 0000000..e056c98
--- /dev/null
+++ b/o3d/samples/io/actors/spikem.js
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file defines the Spikem class.
+ */
+
+/**
+ * A Horizontal Floater.
+ */
+function Spikem(initObj) {
+ this.absorbNamedValues(initObj);
+ this.width = 24;
+ this.height = 55;
+ this.velX = -200;
+ this.velZ = 0;
+ this.isDead = false;
+ this.isHit = false;
+ this.pauseFrames = 0;
+ this.frameName = "spinning"
+}
+Spikem.prototype = new Actor;
+
+Spikem.prototype.onTick = function(timeElapsed) {
+ if (this.isDead == true) {
+ return;
+ }
+
+ if (this.isHit == true) {
+ if (this.z < -1000) {
+ isDead = true;
+ } else {
+ this.velX = this.velX*.9;
+ // Here's how I move. moveMapX is a handy function that updates both
+ // my "virtual 2d world" mapX, as well as my literal x + y values
+ this.z -= 20;
+ this.moveMapX(this.velX * timeElapsed)
+ }
+ } else {
+ // I move based off of the platform that I'm on. So get that now.
+ var myPlatform = world.platforms[this.platformID];
+
+ // I stay on my platform
+ if (this.mapX - this.width/2 < myPlatform.left.mapX
+ && this.velX < 0) {
+ this.velX *= -1;
+ } else if (this.mapX + this.width/2 > myPlatform.right.mapX
+ && this.velX > 0) {
+ this.velX *= -1;
+ }
+
+ this.rotZ += this.velX / 60 * timeElapsed;
+
+ if (Math.abs(this.velX) < .1) {
+ if (Math.random() > .5) {
+ this.velX = -400;
+ } else {
+ this.velX = 400;
+ }
+ this.pauseFrames = Math.random() * 0.5 + 1;
+ }
+
+ if (this.pauseFrames > 0) {
+ this.frameName = "spinning"
+ this.pauseFrames -= timeElapsed;
+ } else {
+ this.frameName = "charging"
+ this.velX = this.velX*.9;
+ // Here's how I move. moveMapX is a handy function that updates both
+ // my "virtual 2d world" mapX, as well as my literal x + y values
+ this.moveMapX(this.velX * timeElapsed)
+ }
+
+ if (this.isHitBySword()) {
+ this.isHit = true;
+ soundPlayer.play('sound/_SMASH.mp3', 100);
+ this.velX = 0;
+ } else if (this.isHitByArrow()) {
+ top.arrowActor.hide();
+ this.isHit = true;
+ soundPlayer.play('sound/_SMASH.mp3', 100);
+ this.velX = top.arrowActor.velX;
+ } else if (this.collidesWith(avatar)) {
+ // Play an event sound @100% volume, 0 repeats
+ soundPlayer.play('sound/ah.mp3', 100);
+
+ this.velZ = 0;
+ if (avatar.velX < 1) {
+ avatar.velX = this.velX * 7;
+ } else {
+ avatar.velX = avatar.velX * -7;
+ }
+ }
+ }
+
+ updateActor(this);
+}
diff --git a/o3d/samples/io/actors/verticalpad.js b/o3d/samples/io/actors/verticalpad.js
new file mode 100644
index 0000000..fd90425
--- /dev/null
+++ b/o3d/samples/io/actors/verticalpad.js
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file defines the VerticalPad class.
+ */
+
+/**
+ * A Vertical Pad you can run on. These things oscillate between their starting
+ * position and a z of 350"
+ */
+function VerticalPad(initObj) {
+ this.absorbNamedValues(initObj);
+ this.maxSpeed = 4 * 20;
+ this.moveAmount = 4 * 20;
+ this.bottomZ = this.z;
+ this.topZ = 350;
+ this.width = 42;
+ this.height = 10;
+}
+VerticalPad.prototype = new Actor;
+
+VerticalPad.prototype.onTick = function(timeElapsed) {
+ // I bounce back and forth between the left and right points on my platform.
+ if (this.z < this.bottomZ) {
+ this.moveAmount += 1;
+ } else if (this.z > this.topZ) {
+ this.moveAmount -= 1;
+ } else if (this.moveAmount >= 0) {
+ this.moveAmount = this.maxSpeed;
+ } else {
+ this.moveAmount = -this.maxSpeed;
+ }
+
+ // I match my rotation to that of my platform.
+ var myPlatform = world.platforms[this.platformID];
+ this.rotZ = myPlatform.rotZ;
+
+
+ this.z += this.moveAmount * timeElapsed;
+
+ // If I collide with the avatar, then move the avatar along with me,
+ // and set his "override" groundZ to be my own Z.
+ if (this.collidesWith(avatar)) {
+ if (avatar.velZ <= 0 && avatar.z >= this.z - 45) {
+ avatar.z = this.z
+ if (this.moveAmount < 0) {
+ avatar.z += this.moveAmount * timeElapsed;
+ }
+ avatar.groundZ = avatar.z;
+ }
+ }
+
+ updateActor(this);
+}
diff --git a/o3d/samples/io/autoincludes.js b/o3d/samples/io/autoincludes.js
new file mode 100644
index 0000000..be2163f
--- /dev/null
+++ b/o3d/samples/io/autoincludes.js
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * This file was auto-generated by a SketchUp ruby script. Include this in your
+ * game and everything else will load right in.
+ *
+ * WARNING: This file will be overwritten when you publish a new level from
+ * SketchUp, so if you wan to manually modify it, you've been warned. :)
+ */
+document.write('<script type="text/javascript" src="actors/actor.js"></script>');
+document.write('<script type="text/javascript" src="actors/arrow.js"></script>');
+document.write('<script type="text/javascript" src="actors/avatar.js"></script>');
+document.write('<script type="text/javascript" src="actors/coin.js"></script>');
+document.write('<script type="text/javascript" src="actors/horizontalpad.js"></script>');
+document.write('<script type="text/javascript" src="actors/mover.js"></script>');
+document.write('<script type="text/javascript" src="actors/spikem.js"></script>');
+document.write('<script type="text/javascript" src="actors/verticalpad.js"></script>');
+
+document.write('<script type="text/javascript" src="levels/all_actors.js"></script>');
+document.write('<script type="text/javascript" src="levels/map1.js"></script>');
diff --git a/o3d/samples/io/cutscenes.js b/o3d/samples/io/cutscenes.js
new file mode 100644
index 0000000..376a7845
--- /dev/null
+++ b/o3d/samples/io/cutscenes.js
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file isn't important to the actual game. It just runs
+ * pretty animations outside the o3d window.
+ */
+
+/**
+ * "Opens" the cover. This thing calls itself again and again until it's done.
+ * @param {number} lastPercent Number from 0 to 100 representing how "done"
+ */
+function animateCover(lastPercent) {
+ // Move 10% more toward a 100% animation
+ lastPercent = ifEmpty(lastPercent,0);
+ lastPercent = lastPercent + 10;
+
+ // Play a nice sound.
+ if (lastPercent == 10) {
+ soundPlayer.play('sound/page.mp3', 100);
+ $('innercover-div').style.width = $('book-left').offsetWidth;
+ $('book-left').style.width = $('book-left').offsetWidth;
+ }
+
+ // Transform the cover graphic from "closed" to "open." If we're more than
+ // 50% along the way, then this cover gets hidden.
+ var cover = $('cover')
+ var innerCover = $('innercover')
+
+ var factor = Math.abs(Math.cos(lastPercent/50*Math.PI/2));
+ if (lastPercent < 50){
+ cover.style.width = factor * 1000;
+ cover.style.height = 600;
+ } else {
+ cover.visibility = 'hidden';
+ cover.style.width = 1;
+ innerCover.style.width = factor * 1000;
+ innerCover.style.height = 600;
+ }
+
+ var shadow = $('cover-shadow');
+ if (lastPercent <= 70) {
+ factor = Math.abs(Math.cos(lastPercent/70*Math.PI/2));
+ shadow.style.width = factor * 1000;
+ shadow.style.height = 600;
+ shadow.style.opacity = (70 - lastPercent)/70;
+ }
+
+ if (lastPercent < 100) {
+ setTimeout('animateCover(' + lastPercent + ')',100);
+ } else {
+ shadow.style.visibility = 'hidden';
+ }
+}
+
+
+/**
+ * "Opens" a page. This thing calls itself again and again until it's done.
+ * @param {string} id HTML element ID of the page image that we're animating.
+ * @param {number} lastPercent Number from 0 to 100 representing how "done"
+ */
+function animatePage(id, lastPercent) {
+ // Transform the page graphic from "closed" to "open." If we're more than
+ // 50% along the way, then this cover gets hidden.
+ var page = $(id)
+ page.style.zIndex = 200;
+ lastPercent = ifEmpty(lastPercent,0);
+
+ // if we've already animated this page... don't do it again.
+ if (parseInt(page.style.width) < 900 && lastPercent == 0) {
+ return;
+ }
+
+ // Move 10% more toward a 100% animation
+ lastPercent = lastPercent + 10;
+
+ // Play a nice sound.
+ if (lastPercent == 10) {
+ soundPlayer.play('sound/page.mp3', 100);
+ }
+
+ var factor = Math.abs(Math.cos(lastPercent/50*Math.PI/2));
+ if (lastPercent < 50) {
+ page.style.width = factor * 951;
+ page.style.height = 549;
+ } else {
+ page.visibility = 'hidden';
+ page.style.width = 1;
+ }
+
+ var shadow = $('cover-shadow')
+ var coverDiv = $('cover-div')
+
+ if (lastPercent <= 70){
+ coverDiv.style.zIndex = 99;
+ factor = Math.abs(Math.cos(lastPercent/70*Math.PI/2));
+ shadow.style.width = factor * 1000;
+ shadow.style.height = 600;
+ shadow.style.opacity = (70 - lastPercent)/70;
+ shadow.style.visibility = 'visible';
+ }
+
+ // Fade in a bit for the start of the animated shadow.
+ if (lastPercent <= 20) {
+ shadow.style.opacity = (lastPercent/30);
+ }
+
+ if (lastPercent < 100) {
+ setTimeout('animatePage("' + id + '",' + lastPercent + ')',100);
+ } else {
+ shadow.style.visibility = 'hidden';
+ if (id == "page2") {
+ // We'll fire the animation of page 3 after 2 seconds.
+ setTimeout('animatePage("page3")',2000);
+ } else if (id == "page3") {
+ loadLevel(0);
+ }
+ }
+}
diff --git a/o3d/samples/io/dynamic_lights.js b/o3d/samples/io/dynamic_lights.js
new file mode 100644
index 0000000..9a5b1f9
--- /dev/null
+++ b/o3d/samples/io/dynamic_lights.js
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview Manage all the dynamic lights in a level.
+ */
+
+
+var s_light_array = new Array();
+var s_number_of_lights_in_scene = 0;
+
+var s_number_of_lights_in_program = 0;
+var s_light_parameter_array = new Array();
+
+// Call this before any UpdateLightsInProgram.
+function InitializeLightParameters(context, number_of_lights) {
+ s_number_of_lights_in_program = number_of_lights;
+ for (var l = 0; l < number_of_lights; ++l) {
+ s_light_parameter_array[l] = {};
+ s_light_parameter_array[l].location = context.createParam(
+ 'light' + l + '_location', 'ParamFloat3');
+ s_light_parameter_array[l].location.value = [0, 0, 0];
+
+ // For the light, the final color parameter is the attentuation.
+ s_light_parameter_array[l].color = context.createParam(
+ 'light' + l + '_color', 'ParamFloat4');
+ s_light_parameter_array[l].color.value = [0, 0, 0, 1];
+ }
+}
+
+// Returns the index of the new light for future manipulation.
+// World location and color should be arrays.
+// These will be assigned to Param objects.
+function AddLight(world_location, color) {
+ var light_index = s_number_of_lights_in_scene++;
+ s_light_array[light_index] = {};
+ s_light_array[light_index].location = world_location;
+ s_light_array[light_index].color = color;
+ return light_index;
+}
+
+// Sets the shader parameters to render using the closest lights
+// to the 'focus' (whatever's passed in).
+// |focus_location| must provide .x, .y, and .z for determining the
+// distance to each light.
+function UpdateLightsInProgram(focus_location) {
+ // TODO: Modulate the lights' color / add behaviors for the lights.
+ var lights_to_render = new Array();
+ for (var i = 0; i < s_number_of_lights_in_program; ++i) {
+ lights_to_render[i] = {};
+ lights_to_render[i].distance = 10000000000000.0; // Arbitrary large number.
+ lights_to_render[i].index = -1;
+ }
+ var number_of_lights = s_number_of_lights_in_scene;
+ for (var l = 0; l < number_of_lights; ++l) {
+ var x_diff = s_light_array[l].location.x - focus_location.x;
+ var y_diff = s_light_array[l].location.y - focus_location.y;
+ // TODO: This should be focus_location.z, but the avatar's location that is
+ // passed in is offset from the world coordinates. For our relatively flat
+ // levels, this works more predictably.
+ var z_diff = s_light_array[l].location.z - 0;
+ var distance_to_focus = x_diff * x_diff + y_diff * y_diff + z_diff * z_diff;
+ // This may change if we assign to an entry early in the list.
+ var index_to_assign = l;
+ for (var i = 0; i < s_number_of_lights_in_program; ++i) {
+ if (distance_to_focus < lights_to_render[i].distance) {
+ // Swap the index into this entry. We will percolate the
+ // light up.
+ var replaced_index = lights_to_render[i].index;
+ lights_to_render[i].index = index_to_assign;
+ index_to_assign = replaced_index;
+
+ var replaced_distance = lights_to_render[i].distance;
+ lights_to_render[i].distance = distance_to_focus;
+ distance_to_focus = replaced_distance;
+ }
+ if (index_to_assign < 0) {
+ break;
+ }
+ }
+ }
+
+ // Finally, assign to the actual shader parameters.
+ for (var i = 0; i < s_number_of_lights_in_program; ++i) {
+ var light_index = lights_to_render[i].index;
+ if (light_index >= 0) {
+ s_light_parameter_array[i].location.value =
+ s_light_array[light_index].location;
+ s_light_parameter_array[i].color.value =
+ s_light_array[light_index].color;
+ }
+ }
+}
+
diff --git a/o3d/samples/io/editor.html b/o3d/samples/io/editor.html
new file mode 100644
index 0000000..a212e58
--- /dev/null
+++ b/o3d/samples/io/editor.html
@@ -0,0 +1,126 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<html>
+<head>
+ <title>Level Editor - Don't open this is a browser.
+ It's only good inside a SketchUp WebDialog</title>
+ <script type="text/javascript" src="init.js"></script>
+ <script type="text/javascript" src="autoincludes.js"></script>
+ <script type="text/javascript" src="gamelogic.js"></script>
+ <script type="text/javascript" src="sound/soundplayer.js"></script>
+
+<script>
+function parseWorld() {
+ url = 'skp:parse_world';
+ window.location.href = url;
+}
+
+function play() {
+ $('play-button').disabled = false;
+ $('pause-button').disabled = true;
+
+ url = 'skp:do_play@';
+ window.location.href = url;
+
+}
+
+function pause() {
+
+ $('play-button').disabled = false;
+ $('pause-button').disabled = true;
+ clearTimeout(timerID)
+}
+
+function save() {
+ url = 'skp:do_save@';
+ window.location.href = url;
+}
+
+function startNew() {
+ name = prompt("What do you want to call your new level?\n" +
+ "(Choose wisely... you can't rename yet.)","My First Level");
+ url = 'skp:do_new@';
+ url += 'name=' + name;
+ window.location.href = url;
+ $('export-button').disabled = false;
+ $('play-button').disabled = false;
+}
+
+function setWorldFromName(name) {
+ output.innerHTML += '<script type="text/javascript" src="autoincludes.js"><' + '/script>';
+ for (i=0;i<levels.length; i++) {
+ //alert('looking at ' + levels[i].colladaFile);
+ if (levels[i].colladaFile == name + '.o3dtgz') {
+ world = levels[i];
+ avatar = levels[i].actors[0];
+ nextFrame();
+ $('pause-button').disabled = false;
+ $('play-button').disabled = true;
+ break;
+ }
+ }
+
+}
+
+</script>
+</head>
+<style>
+A { font-family: sans-serif; text-decoration: none; }
+A:link { color: yellow; }
+A:visited { color: yellow; }
+A:active { color: yellow; }
+A:hover { color: yellow; }
+BODY { font-family: sans-serif; }
+INPUT { width: 100%; text-align: left; padding-left: 6px; margin-top: 3px;}
+.head { background-color: black; color: gold; font-weight: bold; padding: 6px; }
+.levellink {
+ color: blue;
+ text-decoration: underline;
+ cursor: pointer;
+
+}
+</style>
+<body style="background-color:threedface;" onload="init()">
+ <div class="head">Select an action...</div>
+ <input type="button" onclick="startNew()" value="Start a new level from a template">
+ <input id="export-button" type="button" onclick="save()" disabled="true" value="Save this file and export to O3D">
+ <input id="play-button" type="button" disabled="true" onclick="play();" value="Play the level inside SketchUp">
+ <input id="pause-button" type="button" disabled="true" onclick="pause();" value="Pause playback">
+
+ <br><div class="head">Or select an existing level to load...</div>
+
+ <span id="content">List...</span>
+
+ <span id="output"></span>
+</body>
+</html>
+
diff --git a/o3d/samples/io/gamelogic.js b/o3d/samples/io/gamelogic.js
new file mode 100644
index 0000000..65f5ff6
--- /dev/null
+++ b/o3d/samples/io/gamelogic.js
@@ -0,0 +1,578 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file sets up the game state JS structures, sets up
+ * keyboard handlers, and contains the main game loop.
+ */
+
+ /**
+ * Global constants that drive the game engine.
+ */
+var FRAMES_PER_SECOND = 20; // Change for a faster or slower game.
+var FRAME_DELAY = 1000 / FRAMES_PER_SECOND; // Milliseconds between frames.
+var GRAVITY = -2000; // Inches per second per second.
+var JUMP_VELOCITY = 650;
+var RUN_VELOCITY = 200;
+var WORLD_ANGLE = 0;
+var CAMERA_SOFTNESS = 15;
+
+
+/**
+ * These are some global pointers that we'll use for to point at our world
+ * object (aka "current map" or "current level"), and our happy avatar (aka
+ * the protagonist, Prince Io.)
+ */
+var world;
+var avatar;
+
+/**
+ * Keyboard constants.
+ */
+var BACKSPACE = 8;
+var TAB = 9;
+var ENTER = 13;
+var SHIFT = 16;
+var CTRL = 17;
+var ALT = 18;
+var ESCAPE = 27;
+var PAGEUP = 33;
+var PAGEDOWN = 34;
+var END = 35;
+var HOME = 36;
+var LEFT = 37;
+var UP = 38;
+var RIGHT = 39;
+var DOWN = 40;
+var DELETE = 46;
+var SPACE = 32;
+
+/**
+ * These variables are special SketchUp constants that aren't really constant.
+ * They are updated by SketchUp every frame to indicate what's around the
+ * avatar.
+ * TODO: ADDENDUM! These vars used to be "special constants,"
+ * but now they're leftovers of a time when SU was the main engine, and the
+ * game logic below should be cleaned up so we're not using all caps on these.
+ */
+var GROUNDZ; // Absolute Z of the obstacle beneath the avatar.
+var OBSTACLE_LEFT; // Distance to nearest obstacle to the left.
+var OBSTACLE_RIGHT; // Distance to nearest obstacle on the right.
+
+/**
+ * Camera position stuff.
+ * TODO: These are redundant vs. the camera.eye.x stuff in
+ * init.js. Clean that outa thar!
+ */
+var eyeX = 0;
+var eyeY = -1000;
+var eyeZ = 1000;
+var targetX = 0;
+var targetY = 0;
+var targetZ = 0;
+
+/**
+ * If the user presses the swing button while we're showing a swing, then we'll
+ * mark the fact that we need to immediately swing again once the current
+ * animation is done.
+ */
+var swingAgain = false;
+
+/**
+ * Way of determining that the user just pressed then unpressed certain keys.
+ */
+var upWasJustDown = false;
+var ctrlWasJustUp = false;
+
+/**
+ * Identifier for our global game timer. This is only used when SketchUp is the
+ * main CG engine.
+ */
+var timerID;
+
+/**
+ * Code to keep track of the current key state. Since these events can fire at
+ * any time but we only want to apply changes to our game state at the start of
+ * each game loop, we do this trick so that the nextFrame() function can poll
+ * the keyIsDown data structure to determine what's happened since the last
+ * frame.
+ */
+var keyIsDown = {};
+
+function keyMessage(e, state) {
+ keyIsDown[o3djs.event.getEventKeyChar(e ? e : window.event)] = state;
+}
+
+document.onkeydown = function(e) { keyMessage(e, true); };
+
+document.onkeyup = function(e) { keyMessage(e, false); };
+
+
+/**
+ * Main Game Loop.... Here's where we actually run the game.
+ * @param {number} elapsedTime Number of seconds since we were last called.
+ */
+function nextFrame(elapsedTime) {
+ // First, let's look at our avatar. If he's jumping, then figure out which
+ // platform (if any) he's currently over.
+ if (avatar.isJumping) {
+ var lowestZFound = -5000;
+ for (var i = 0; i < world.platforms.length; i++) {
+ platform = world.platforms[i];
+ if (platform.parentID == avatar.parentPlatformID ||
+ i == avatar.parentPlatformID) {
+ if (avatar.z > platform.z &&
+ avatar.mapX > platform.left.mapX &&
+ avatar.mapX < platform.right.mapX &&
+ platform.z > lowestZFound && platform.isNotSolid != true) {
+ lowestZFound = world.platforms[i].z
+ avatar.platformID = i;
+ }
+ }
+ }
+ }
+
+ // Create a myPlatform variable for convenience.
+ var myPlatform = world.platforms[avatar.platformID]
+
+ // Platforms without a parentID aren't really platforms. The actually define
+ // the "world path" that meanders through our level. Therefore, if the only
+ // platform that Io is over is one of these, then our current GROUNDZ (aka
+ // the nearest walkable altitude),
+ if (exists(myPlatform.parentID)) {
+ GROUNDZ = myPlatform.z
+ avatar.parentPlatformID = myPlatform.parentID;
+ } else {
+ GROUNDZ = -5000;
+ avatar.parentPlatformID = avatar.platformID;
+ }
+
+ // The avatar's mapX is really important. It defines his position in the
+ // imaginary 2d platformer that we weave through a 3D world. When he's at
+ // mapX of 0, he's at the far left of the level (think super mario.) As
+ // he proceeds through the level, his mapX will get bigger. Meanwhile, his
+ // actual x, y, and z (aka altitude) values will be kept track of as well.
+ // Anyway, every platform that Io can stand on has a "left" and a "right"
+ // to them. Let's check to see if we've walked off of it...
+ if (avatar.mapX < myPlatform.left.mapX) {
+ // Whoa! We've walked off the left of our platform! If there's an adjacent
+ // platform to step onto, then do that. Otherwise, it's fallin' time.
+ if (exists(myPlatform.left.adjacentID)) {
+ // Do some math to ensure that our actual x and y values are corrected
+ // so Io stays perfectly in our "world path" plane.
+ oldPlatform = myPlatform;
+ overshot = avatar.mapX - myPlatform.left.mapX;
+ avatar.platformID = myPlatform.left.adjacentID;
+ myPlatform = world.platforms[avatar.platformID];
+ avatar.x -= Math.cos(oldPlatform.rotZ) * overshot;
+ avatar.y -= Math.sin(oldPlatform.rotZ) * overshot;
+ avatar.x += Math.cos(myPlatform.rotZ) * overshot;
+ avatar.y += Math.sin(myPlatform.rotZ) * overshot;
+ } else {
+ GROUNDZ = -5000;
+ }
+ } else if (avatar.mapX > myPlatform.right.mapX) {
+ // Whoa! We've walked off the right of our platform!
+ if (exists(myPlatform.right.adjacentID)) {
+ oldPlatform = myPlatform;
+ overshot = avatar.mapX - myPlatform.right.mapX;
+ avatar.platformID = myPlatform.right.adjacentID;
+ myPlatform = world.platforms[avatar.platformID];
+ avatar.x -= Math.cos(oldPlatform.rotZ) * overshot;
+ avatar.y -= Math.sin(oldPlatform.rotZ) * overshot;
+ avatar.x += Math.cos(myPlatform.rotZ) * overshot;
+ avatar.y += Math.sin(myPlatform.rotZ) * overshot;
+ } else {
+ GROUNDZ = -5000;
+ }
+ }
+
+ // Now it's time to figure out if there's a "obstacle" nearby. Each platform
+ // can potentially have a left.obstacleHeight and/or a right.obstableHeight.
+ // We'll store how close any of them are in these OBSTABLE_LEFT type
+ // "constants."
+ OBSTACLE_RIGHT = 9000
+ OBSTACLE_RIGHT_HEIGHT = 0
+ OBSTACLE_LEFT = 9000
+ OBSTACLE_LEFT_HEIGHT = 0
+
+ if (myPlatform.right.obstacleHeight > 0) {
+ OBSTACLE_RIGHT = myPlatform.right.mapX - avatar.mapX;
+ OBSTACLE_RIGHT_HEIGHT = myPlatform.right.obstacleHeight - avatar.velZ + 20;
+ }
+ if (myPlatform.left.obstacleHeight > 0) {
+ OBSTACLE_LEFT = avatar.mapX - myPlatform.left.mapX;
+ OBSTACLE_LEFT_HEIGHT = myPlatform.left.obstacleHeight - avatar.velZ + 20;
+ }
+
+ // So... now that we've figured all of that stuff out, let's store how
+ // far *above* the current platform Io is.
+ avatar.relativeZ = avatar.z - myPlatform.z;
+
+ // This special variable, avatar.groundZ, can be set by moving
+ // platforms and other actors to override our actual groundZ. This allows
+ // the avatar to "float" in space (or really, to stand on stuff that's
+ // not a static platform but a moving one.) So if we find that variable,
+ // it trumps the calculated GROUNDZ. However, we *always* clear that
+ // variable out after using it. It's up to the Actor who is moving Io to
+ // keep this updated inside their onTick event. (See horizontalpad.js for
+ // an example of this.)
+ if (exists(avatar.groundZ)) {
+ GROUNDZ = avatar.groundZ;
+ avatar.groundZ = undefined;
+ }
+
+ // Gah. Another one of our "special constants" that used to be updated
+ // by SketchUp but now is done via JS data structures.
+ // TODO: Clean 'tup.
+ WORLD_ANGLE = myPlatform.rotZ;
+
+ // Check for keyboard stuff.
+ handleInput();
+
+ // Handle falling and gravity.
+ avatar.z += avatar.velZ * elapsedTime;
+ if (avatar.z - GROUNDZ > 1) {
+ avatar.isJumping = true;
+ } else {
+ if (avatar.isJumping == true) {
+ soundPlayer.play('sound/step2.mp3', 100);
+ }
+ avatar.isJumping = false;
+ }
+ if (avatar.isJumping == true) {
+ avatar.velZ += GRAVITY * elapsedTime;
+ }
+ if (avatar.z < GROUNDZ) {
+ avatar.velZ = 0;
+ avatar.z = GROUNDZ + .1;
+ avatar.isJumping = false;
+ avatar.animation = 'Hero_Run';
+
+ }
+ if (avatar.z < -1000) {
+ avatar.x = 0;
+ avatar.y = 0;
+ avatar.z = 200;
+ avatar.mapX = 0;
+ avatar.platformID = 0;
+ avatar.parentPlatformID = 0;
+ }
+
+ // Handle collision with things to the left and right of our avatar.
+ // From here is where we start to decide which animation cycle we're on.
+ // Now you can see how we're using avatar.relativeZ along with our
+ // OBSTACLE_FOO "special constants" to determine not only if we've hit an
+ // obstacle, but whether we've jumped high enough to get over it.
+ if (OBSTACLE_LEFT < 25 && keyIsDown[LEFT] &&
+ OBSTACLE_LEFT_HEIGHT > avatar.relativeZ) {
+ avatar.targetVelX = 0;
+ avatar.animation = 'Hero_Stand';
+ avatar.frame = 1;
+ }
+ if (OBSTACLE_RIGHT < 25 && keyIsDown[RIGHT] &&
+ OBSTACLE_RIGHT_HEIGHT > avatar.relativeZ) {
+ avatar.targetVelX = 0;
+ avatar.animation = 'Hero_Stand';
+ avatar.frame = 1;
+ }
+
+ // If we're jumping, then use the jumping animation cycle.
+ if (avatar.isJumping == true) {
+ avatar.animation = 'Hero_Jump';
+ if (avatar.velZ > 0) {
+ avatar.frame += elapsedTime * 20;
+ if (avatar.frame > 3) {
+ avatar.frame = 3;
+ }
+ } else {
+ if (avatar.z > GROUNDZ + 20) {
+ avatar.frame = 4;
+ } else {
+ avatar.frame = 5;
+ }
+ }
+ }
+
+ // Figure out which frame to display inside our current cycle.
+ didSwordStrike = '0';
+ if (avatar.animation == 'Hero_Run') {
+ avatar.frame += elapsedTime * 20;
+ if (avatar.frame > 10) {
+ avatar.frame = 1;
+ }
+ if (Math.floor(avatar.frame % 5) == 0) {
+ var soundToPlay = 'sound/step'
+ soundToPlay += (Math.floor(Math.random() * 3) + 1);
+ soundToPlay += '.mp3'
+ soundPlayer.play(soundToPlay, 100);
+ }
+ } else if (avatar.animation == 'Hero_Sword') {
+ avatar.targetVelX = 0;
+ avatar.framesSinceShot = 0;
+ avatar.frame += avatar.swordRate * 20 * elapsedTime;
+ if (avatar.frame < 1) {
+ avatar.animation = 'Hero_Stand'
+ avatar.frame = 1;
+ } else if (avatar.frame > 7) {
+ avatar.swordRate = -.5;
+ avatar.frame = 6.2;
+ didSwordStrike = '1';
+ } else if (avatar.frame == 3 && avatar.swordRate < 0) {
+ if (swingAgain == true) {
+ avatar.swordRate = 1;
+ swingAgain = false;
+ } else {
+ avatar.swordRate = -.5;
+ }
+ }
+
+ if (avatar.frame == 3 && avatar.swordRate > 0) {
+ soundPlayer.play('sound/_MISS.mp3', 100);
+ }
+ }
+
+ // Update our position.
+ avatar.velX = (avatar.velX + avatar.targetVelX + avatar.targetVelX) / 3;
+ if (Math.abs(avatar.velX) < .2) {
+ avatar.velX = 0;
+ }
+ avatar.mapX += avatar.velX * elapsedTime;
+ avatar.x += avatar.velX * Math.cos(WORLD_ANGLE) * elapsedTime;
+ avatar.y += avatar.velX * Math.sin(WORLD_ANGLE) * elapsedTime;
+ avatar.rotZ = (avatar.rotZ + avatar.targetRotZ + avatar.targetRotZ) / 3;
+
+ // Calculate our final frameName string to pass to SketchUp (or o3d).
+ frameName = avatar.animation;
+ if (avatar.frame < 10) {
+ frameName += '0';
+ }
+ frameName += Math.floor(avatar.frame);
+ if (avatar.framesSinceShot > 0) {
+ frameName += 'Shoot';
+ avatar.framesSinceShot--;
+ }
+ if (avatar.framesSinceShot == 3) {
+ // TODO: Make this work with multiple arrows, perform
+ // better, and be smart enough to handle level reloads.
+ if (top.arrowActor == undefined) {
+ for (var actorID = 0; actorID < world.actors.length; actorID++) {
+ arrow = world.actors[actorID];
+ if (arrow.name.indexOf('Arrow') == 0) {
+ top.arrowActor = arrow;
+ top.arrowActor.shoot();
+ }
+ }
+ } else {
+ top.arrowActor.shoot();
+ }
+ }
+ avatar.frameName = frameName;
+
+ // Here's where we send our state down to SketchUp or O3D.
+ // Always do our avatar last, since he's the one likely to have
+ // been modified by other things in the environment. (Our avatar
+ // is guaranteed by the level exporter to be in world.actors[0])
+ for (i = world.actors.length - 1; i >= 0; i--) {
+ world.actors[i].onTick(elapsedTime);
+ }
+
+ // Now move the camera in a pleasing motion to float over Io's
+ // right shoulder. (Well, it's his right shoulder when he's
+ // facing "right" toward the end of the level.)
+ newEyeX = avatar.x + 350 * Math.sin(WORLD_ANGLE);
+ newEyeY = avatar.y - 350 * Math.cos(WORLD_ANGLE);
+ newEyeZ = avatar.z + 150;
+ eyeX = (newEyeX + eyeX * CAMERA_SOFTNESS) / (CAMERA_SOFTNESS + 1);
+ eyeY = (newEyeY + eyeY * CAMERA_SOFTNESS) / (CAMERA_SOFTNESS + 1);
+ eyeZ = (newEyeZ + eyeZ * CAMERA_SOFTNESS) / (CAMERA_SOFTNESS + 1);
+ targetX = (avatar.x + targetX) / 2;
+ targetY = (avatar.y + targetY) / 2;
+ targetZ = (avatar.z + 200 + targetZ * 3) / 5;
+
+ // Here's where we send our camera down to SketchUp or O3D.
+ updateCamera(targetX, targetY, targetZ, eyeX, eyeY, eyeZ)
+
+ // If we're running inside SketchUp, clear out our old timer and set a new
+ // one.
+ if (isSU) {
+ clearTimeout(timerID);
+ timerID = self.setTimeout('nextFrame()', FRAME_DELAY);
+ }
+}
+
+
+/**
+ * Sends our state to our render engine. (See knightgame.rb for the Sketchup
+ * ruby handler.)
+ * @param {Object} actor An instance of an Actor object.
+ */
+function updateActor(actor) {
+ var frameName = actor.frameName;
+ var x = actor.x;
+ var y = actor.y;
+ var z = actor.z;
+ var rotZ = actor.rotZ;
+
+ // Sending stuff to SU vs. to O3D.
+ if (isSU == true) {
+ url = 'skp:push_frame_js@';
+ url += 'name=' + actor.name;
+ url += '&frame=' + frameName;
+ url += '&x=' + x;
+ url += '&y=' + y;
+ url += '&z=' + z;
+ url += '&rotz=' + rotZ;
+ window.location.href = url;
+ } else {
+ // For performance reasons, we want to cache node pointers directly onto
+ // our Actor object, so we don't have to look it all up every time.
+ if (actor.nodesHaveBeenCached != true) {
+ actor.hasBeenCached = true;
+ nodes = client.getObjects(actor.colladaID, 'o3d.Transform');
+ actor.node = nodes[0];
+
+ var instance = actor.node.children[0];
+ actor.children = instance.children;
+ actor.nodesHaveBeenCached = true;
+ }
+
+ var m = math.matrix4.rotationZYX([0, rotZ, 0]);
+ math.matrix4.setTranslation(m, [x, z, -y]);
+ actor.node.localMatrix = m;
+
+ if (!isEmpty(frameName)) {
+ for (var c = 0; c < actor.children.length; c++) {
+ var child = actor.children[c];
+ child_name = child.name;
+ child.visible = (child_name == frameName ||
+ child_name == frameName + '1' ||
+ child_name == frameName + '_1');
+ }
+ }
+ }
+}
+
+/**
+ * Sends our state down to SketchUp or o3d. (See knightgame.rb for the handler.)
+ * @param {number} targetX Where we want the camera to point at.
+ * @param {number} targetY Where we want the camera to point at.
+ * @param {number} targetZ Where we want the camera to point at.
+ * @param {number} eyeX Where we want the camera to point from.
+ * @param {number} eyeY Where we want the camera to point from.
+ * @param {number} eyeZ Where we want the camera to point from.
+ */
+function updateCamera(targetX, targetY, targetZ, eyeX, eyeY, eyeZ) {
+ if (isSU == true) {
+ url = 'skp:push_camera@';
+ url += 'targetX=' + targetX;
+ url += '&targetY=' + targetY;
+ url += '&targetZ=' + targetZ;
+ url += '&eyeX=' + eyeX;
+ url += '&eyeY=' + eyeY;
+ url += '&eyeZ=' + eyeZ;
+ window.location.href = url;
+ } else {
+ var target = [targetX, targetZ, -targetY];
+ var eye = [eyeX, eyeZ, -eyeY];
+ var up = [0, 1, 0];
+ var view_matrix = math.matrix4.lookAt(eye, target, up);
+
+ drawContext.view = view_matrix;
+
+ // !lacz modified!
+ camera_eye_param.value = eye;
+ camera_target_param.value = target;
+ }
+}
+
+/**
+ * Checks for keyboard stuff.
+ */
+function handleInput() {
+ if (keyIsDown[SPACE]) {
+ avatar.framesSinceShot = 5;
+ }
+
+ if (keyIsDown[DOWN]) {
+ if (avatar.animation == 'Hero_Sword') {
+ if (ctrlWasJustUp == true) {
+ swingAgain = true;
+ }
+ } else {
+ avatar.animation = 'Hero_Sword';
+ avatar.frame = 1;
+ avatar.swordRate = 1;
+ ctrlWasJustUp = false;
+ }
+ } else {
+ ctrlWasJustUp = true;
+ }
+
+ if (keyIsDown[UP] && avatar.isJumping != true && upWasJustDown == false) {
+ avatar.velZ = JUMP_VELOCITY;
+ avatar.isJumping = true;
+ upWasJustDown = true;
+ }
+ if (keyIsDown[UP]) {
+ upWasJustDown = true;
+ } else {
+ upWasJustDown = false;
+ }
+
+ if (keyIsDown[RIGHT] || keyIsDown[LEFT]) {
+ if (avatar.animation == 'Hero_Stand' && avatar.frame == 1) {
+ avatar.frame = 2;
+ } else if (avatar.animation == 'Hero_Stand' && avatar.frame == 2) {
+ avatar.animation = 'Hero_Run';
+ avatar.frame = 1;
+ }
+
+ } else {
+ if (avatar.animation == 'Hero_Run') {
+ avatar.animation = 'Hero_Stand';
+ avatar.frame = 2;
+ } else if (avatar.animation == 'Hero_Stand' && avatar.frame == 2) {
+ avatar.frame = 1;
+ }
+ }
+
+ if (keyIsDown[RIGHT]) {
+ avatar.targetVelX = RUN_VELOCITY;
+ avatar.targetRotZ = WORLD_ANGLE;
+ } else if (keyIsDown[LEFT]) {
+ avatar.targetVelX = -RUN_VELOCITY;
+ avatar.targetRotZ = -(Math.PI) + WORLD_ANGLE;
+ } else {
+ avatar.targetVelX = 0;
+ }
+}
diff --git a/o3d/samples/io/init.js b/o3d/samples/io/init.js
new file mode 100644
index 0000000..e6c145f
--- /dev/null
+++ b/o3d/samples/io/init.js
@@ -0,0 +1,395 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file creates all of the global o3d stuff that
+ * establishes the plugin window, sets its size, creates necessary light
+ * and camera parameters, and stuff like that.
+ */
+o3djs.require('o3djs.util');
+o3djs.require('O3D.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.pack');
+o3djs.require('o3djs.scene');
+
+// Create some globals that store our main pointers to o3d objects.
+var o3d;
+var math;
+var math;
+var client;
+var pack;
+var globalParams;
+var g_viewInfo;
+var drawContext;
+var blackSampler;
+var g_loadInfo;
+
+// This variable keeps track of whether a collada file is correctly loaded.
+var isLoaded = false;
+
+// This variable, surprisingly, contains the time since the last frame
+// (in seconds)
+var timeSinceLastFrame = 0;
+var frameCount = 0;
+
+// This is only a handy data structure for storing camera information. It's not
+// actually a core O3D object.
+var camera = {
+ eye: [0, 0, 500],
+ target: [0, 0, 0]
+};
+
+/**
+ * This is a top-level switch that tells us whether we're running inside a
+ * SketchUp web dialog. (If not, then we're in O3D.)
+ * TODO : Re-enable the SketchUp web-dialog functionality, but through
+ * a check different than simply testing if we are running in IE.
+ */
+//var isSU = (navigator.appVersion.indexOf('MSIE') != -1) ? true : false;
+var isSU = false;
+
+// Some parameters to pass to the shaders as the camera moves around.
+var camera_eye_param;
+var camera_target_param;
+
+// Keep track of the lights in the level.
+var light_array;
+var max_number_of_lights = 4; // Determined from the GPU effect.
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Gets called at the page's onLoad event. This function sets up our plugin and
+ * level selection UI.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Walk across all levels loaded by includes.js and show a list for the user
+ // to choose from.
+ $('content').innerHTML = 'Choose a level...<br>';
+ for (var i = 0; i < levels.length; i++) {
+ var level = levels[i];
+ var str = '<span class="levellink" onclick="loadLevel(' + i + ')">' +
+ level.name + '</span><br>'
+ $('content').innerHTML += str;
+ }
+
+ soundPlayer.play('sound/music.mp3', 100, 999);
+
+ // If we're NOT running inside SketchUp, then we need to set up our o3d
+ // window.
+ if (!isSU) {
+ var o3dElement = clientElements[0];
+ o3dElement.id = 'o3dObj';
+ o3d = o3dElement.o3d;
+ math = o3djs.math;
+ client = o3dElement.client;
+ pack = client.createPack();
+ var blackTexture = pack.createTexture2D(1, 1, o3d.Texture.XRGB8, 1, false);
+ blackTexture.set(0, [0, 0, 0]);
+ blackSampler = pack.createObject('Sampler');
+ blackSampler.texture = blackTexture;
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ pack,
+ client.root,
+ client.renderGraphRoot);
+
+ drawContext = g_viewInfo.drawContext;
+
+ var target = [0, 0, 0];
+ var eye = [0, 0, 5];
+ var up = [0, 1, 0];
+ var view_matrix = math.matrix4.lookAt(eye, target, up);
+
+ drawContext.view = view_matrix;
+
+ globalParams = pack.createObject('ParamObject');
+
+ // Initialize all the effect's required parameters.
+ var ambient_light_color_param = globalParams.createParam(
+ 'ambientLightColor',
+ 'ParamFloat4');
+ ambient_light_color_param.value = [0.27, 0.2, 0.2, 1];
+ var sunlight_direction_param = globalParams.createParam('sunlightDirection',
+ 'ParamFloat3');
+
+ // 20 degrees off.
+ sunlight_direction_param.value = [-0.34202, 0.93969262, 0.0];
+ var sunlight_color_param = globalParams.createParam('sunlightColor',
+ 'ParamFloat4');
+
+ sunlight_color_param.value = [0.55, 0.6, 0.7, 1.0];
+ // global parameter, this will change as the character moves.
+ camera_eye_param = globalParams.createParam('cameraEye', 'ParamFloat3');
+ camera_eye_param.value = eye;
+ camera_target_param = globalParams.createParam('cameraTarget',
+ 'ParamFloat3');
+ camera_target_param.value = target;
+
+ InitializeLightParameters(globalParams, max_number_of_lights);
+
+ var fog_color_param = globalParams.createParam('fog_color', 'ParamFloat4');
+ fog_color_param.value = [0.5, 0.5, 0.5, 1.0];
+
+ // Create a perspective projection matrix.
+ var o3d_width = 845;
+ var o3d_height = 549;
+
+ var proj_matrix = math.matrix4.perspective(
+ 45 * Math.PI / 180, o3d_width / o3d_height, 0.1, 5000);
+
+ drawContext.projection = proj_matrix;
+
+ o3dElement.onkeydown = document.onkeydown;
+ o3dElement.onkeyup = document.onkeyup;
+
+ o3djs.event.startKeyboardEventSynthesis(o3dElement);
+ }
+}
+
+/**
+ * Remove callbacks on the o3d client.
+ */
+function uninit() {
+ if (client) {
+ client.cleanup();
+ }
+}
+
+/**
+ * Helper function to shorten document.getElementById()
+ * @param {string} value The elementID to find.
+ * @return {string} The document element with our ID.
+ */
+function $(id) {
+ return document.getElementById(id);
+}
+
+/**
+ * Helper function to tell us if a value is set to something useful.
+ * @param {Object} value The thing to check out.
+ * @return {boolean} Whether the thing is empty.
+ * TODO: 'Tis silly to have both an isEmpty and exists function
+ * when they're probably doing the same thing.
+ */
+function isEmpty(value) {
+ return (value == undefined) || (value == '');
+}
+
+/**
+ * Returns the 2nd value if the first is rmpty.
+ * @param {Object} val1 The thing to check for emptyness.
+ * @param {Object} val2 The thing to maybe pass back.
+ * @return {Object} One of the above.
+ */
+function ifEmpty(val1, val2) {
+ if (val1 == undefined || val1 == '') {
+ return val2;
+ }
+ return val1;
+}
+
+/**
+ * Helper function to tell us if a value exists.
+ * @param {Object} item The thing to check out.
+ * @return {boolean} Whether the thing exists.
+ */
+function exists(item) {
+ if (item == undefined || item == null) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+/**
+ * Takes an index number into our global levels[] array, and it loads up that
+ * level.
+ * @param {number} levelID The number of the level to load in.
+ */
+function loadLevel(levelID) {
+ if (isLoaded) {
+ return;
+ }
+ world = levels[levelID];
+
+ // If we're running in 'SketchUp mode' inside the level editor, tell SU to
+ // do the loading, then bail out of here.
+ if (isSU) {
+ clearTimeout(timerID)
+ url = 'skp:do_open@';
+ url += 'level_name=' + world.colladaFile;
+ url += '&friendly_name=' + world.name;
+ window.location.href = url;
+ $('export-button').disabled = false;
+ $('play-button').disabled = false;
+ $('pause-button').disabled = true;
+ isLoaded = true;
+ return;
+ }
+
+ var path = window.location.href;
+ var index = path.lastIndexOf('/');
+ path = path.substring(0, index + 1) + 'levels/' + world.colladaFile;
+ $('o3d').style.width = '845px';
+ $('o3d').style.height = '549px';
+
+ // Create a callback function to prepare the transform graph once its loaded.
+ function callback(exception) {
+ if (exception) {
+ alert('Could not load: ' + path + '\n' + exception);
+ } else {
+ var current_light = 0;
+ var my_effect = pack.createObject('Effect');
+ my_effect.name = 'global_effect';
+ var effect_string = $('global_effect').value;
+ my_effect.loadFromFXString(effect_string);
+
+ var xform_nodes = client.root.getTransformsInTree();
+ for (var i = 0; i < xform_nodes.length; ++i) {
+ var shape_name = xform_nodes[i].name;
+ if (shape_name.indexOf('Light') == 0) {
+ var world_transform = xform_nodes[i].getUpdatedWorldMatrix();
+ var world_loc = world_transform[3];
+ var world_location = world_loc;
+ var color = [1.0, 0.7, 0.10, 150.0];
+
+ var light_index = AddLight(world_location, color);
+ xform_nodes[i].parent = null;
+ ++current_light;
+ }
+ }
+
+ {
+ var materials = pack.getObjectsByClassName('o3d.Material');
+ for (var m = 0; m < materials.length; ++m) {
+ var material = materials[m];
+ if (!material.getParam('diffuseSampler')) {
+ diffuseParam = material.createParam('diffuseSampler',
+ 'ParamSampler');
+ diffuseParam.value = blackSampler;
+ }
+ material.effect = my_effect;
+ my_effect.createUniformParameters(material);
+ // go through each param on the material
+ var params = material.params;
+ for (var p = 0; p < params.length; ++p) {
+ var dst_param = params[p];
+ // see if there is one of the same name on the paramObject we're
+ // using for global parameters.
+ var src_param = globalParams.getParam(dst_param.name);
+ if (src_param) {
+ // see if they are compatible
+ if (src_param.isAClassName(dst_param.className)) {
+ // bind them
+ dst_param.bind(src_param);
+ }
+ }
+ }
+ }
+ }
+
+ // Remove all line primitives as we don't really want them to render.
+ {
+ var primitives = pack.getObjectsByClassName('o3d.Primitive');
+ for (var p = 0; p < primitives.length; ++p) {
+ var primitive = primitives[p];
+ if (primitive.primitiveType == o3d.Primitive.LINELIST) {
+ primitive.owner = null;
+ pack.removeObject(primitive);
+ }
+ }
+ }
+
+ o3djs.pack.preparePack(pack, g_viewInfo);
+ avatar = world.actors[0];
+ isLoaded = true;
+ client.setRenderCallback(onRender);
+ $('container').style.visibility = 'visible';
+ }
+ }
+
+ // Create a new transform node to attach our world to.
+ var world_parent = pack.createObject('Transform');
+ world_parent.parent = client.root;
+
+ function loadSceneCallback(pack, parent, exception) {
+ g_loadInfo = null;
+ callback(exception);
+ }
+ g_loadInfo = o3djs.scene.loadScene(client, pack, world_parent, path,
+ loadSceneCallback);
+}
+
+/**
+ * This is our little callback handler that o3d calls after each frame.
+ * @param {Object} renderEvent An event object.
+ */
+function onRender(renderEvent) {
+ if (g_loadInfo) {
+ $('fps').innerHTML = 'Loading: ' +
+ g_loadInfo.getKnownProgressInfoSoFar().percent + '%';
+ }
+ if (isLoaded == false) {
+ return;
+ }
+
+ var elapsedTime = Math.min(renderEvent.elapsedTime, 1 / 20);
+
+ nextFrame(elapsedTime);
+ UpdateLightsInProgram(avatar);
+
+ // Every 20 frames, update our frame rate display
+ frameCount++;
+ if (frameCount % 20 == 0) {
+ $('fps').innerHTML = Math.floor(1.0 / elapsedTime + 0.5);
+ }
+}
+
+/**
+ * This cancels scrolling and keeps focus on a hidden element so spacebar
+ * doesn't page down.
+ */
+function cancelScroll() {
+ $('focusHolder').focus();
+ var pageWidth = document.body.clientWidth;
+ var pageHeight = document.body.clientHeight;
+ document.body.scrollTop = 0;
+ document.body.scrollLeft = 0;
+}
diff --git a/o3d/samples/io/io.html b/o3d/samples/io/io.html
new file mode 100644
index 0000000..55c0db3
--- /dev/null
+++ b/o3d/samples/io/io.html
@@ -0,0 +1,173 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<html>
+ <title>The Journies of Prince IO: An O3D Adventure</title>
+ <link href="ui/io.css" rel="stylesheet" type="text/css"/>
+ <script type="text/javascript" src="../o3djs/base.js"></script>
+ <script type="text/javascript" src="dynamic_lights.js"></script>
+ <script type="text/javascript" src="init.js"></script>
+ <script type="text/javascript" src="autoincludes.js"></script>
+ <script type="text/javascript" src="gamelogic.js"></script>
+ <script type="text/javascript" src="cutscenes.js"></script>
+ <script type="text/javascript" src="sound/soundplayer.js"></script>
+</head>
+
+<body onload="init();" onscroll="cancelScroll()" onunload="uninit();">
+
+<table id="table-main"><tr><td valign=center>
+
+<table id="table-middle" cellspacing="0" cellpadding="0" border="0">
+ <tr>
+ <td id="book-left" width="50%"><div id="innercover-div"><img id="innercover" src="ui/book_innercover.jpg"></div></td>
+ <td id="book-center" width="1000"><div id="cover-div"><img id="cover-shadow" src="ui/covershadow.png"><img onclick="animateCover()" id="cover" src="ui/book_cover.jpg"></div><img src="ui/book_captop.jpg" width="988" height="39"><br><div id="cover-seam"></div><div id="content">
+
+ Choose a level...
+
+ </div><div id="container" name="container" >
+ <div id="o3d" style="width: 1px; height: 1px;"></div>
+ </div><img id="page1" src="ui/book_page1.jpg" onclick="animatePage('page1')"><img id="page2" src="ui/book_page2.jpg" onclick="animatePage('page2')"><img id="page3" src="ui/book_page3.jpg" onclick="animatePage('page3')"><img src="ui/book_pageblank.jpg" width="951" height="549"><img src="ui/book_capright.jpg" width="37" height="549"><br><img src="ui/book_capbottom.jpg" width="988" height="90"></td>
+ <td id="book-right" width="50%">&nbsp;</td>
+ </tr>
+</table>
+
+</td></tr></table>
+
+<form>
+<input id="focusHolder" style="position:absolute;top:-100px;">
+</form>
+
+<div id="footer"><div id="output"></div><div id="fps"></div><img src="ui/logo.gif" alt="Google"></div>
+<div id="fx" style="visibility:hidden">
+<textarea id="global_effect">
+struct a2v {
+ float4 position : POSITION;
+ float3 normal : NORMAL;
+ float2 texCoord : TEXCOORD0;
+};
+
+struct v2f {
+ float4 position : POSITION;
+ float3 worldPosition : TEXCOORD0;
+ float2 texCoord : TEXCOORD1;
+ float3 n : TEXCOORD2;
+ float3 l : TEXCOORD3;
+};
+
+float4x4 worldViewProj : WorldViewProjection;
+float4x4 world : World;
+float4x4 worldIT : WorldInverseTranspose;
+
+uniform float4 ambientLightColor;
+uniform float3 sunlightDirection;
+uniform float4 sunlightColor;
+uniform float3 cameraEye;
+uniform float3 cameraTarget;
+
+uniform float3 light0_location;
+uniform float3 light1_location;
+uniform float3 light2_location;
+uniform float3 light3_location;
+uniform float3 light4_location;
+
+uniform float4 light0_color;
+uniform float4 light1_color;
+uniform float4 light2_color;
+uniform float4 light3_color;
+uniform float4 light4_color;
+uniform float4 fog_color;
+
+// The texture from a sketchup6 file
+// A diffuseTexture and a diffuse color (when there isn't a texture)
+uniform float4 diffuse; // The color, unless texture
+sampler2D diffuseSampler;
+
+v2f vsMain(a2v IN) {
+ v2f OUT;
+ OUT.position = mul(IN.position, worldViewProj);
+ OUT.worldPosition = mul(IN.position, world).xyz;
+ OUT.texCoord = IN.texCoord;
+
+ OUT.n = mul(float4(IN.normal,0), worldIT).xyz;
+ OUT.l = IN.normal;
+ return OUT;
+}
+
+float4 fsMain(v2f IN): COLOR {
+ float4 textureColor = tex2D(diffuseSampler, IN.texCoord);
+ float3 normalDirection = normalize(IN.n);
+ float3 viewDirection = normalize(cameraEye - IN.worldPosition.xyz);
+ // Only diffuse light until we can get better than face normals.
+ float4 litWorld = lit(dot(normalDirection.xyz, sunlightDirection), 0, 0);
+ float4 total_color = ambientLightColor + litWorld.yyyy * sunlightColor;
+
+ float3 light_direction = light0_location - IN.worldPosition.xyz;
+ float attenuation = light0_color.a / length(light_direction);
+ light_direction = normalize(light_direction);
+ float4 litLight = lit(dot(normalDirection.xyz, light_direction), 0, 0);
+ litLight.y *= clamp(attenuation * attenuation, 0, 1);
+ total_color.rgb += litLight.yyy * light0_color.rgb;
+
+ light_direction = light1_location - IN.worldPosition.xyz;
+ attenuation = light1_color.a / length(light_direction);
+ light_direction = normalize(light_direction);
+ litLight = lit(dot(normalDirection.xyz, light_direction), 0, 0);
+ litLight.y *= clamp(attenuation * attenuation, 0, 1);
+ total_color.rgb += litLight.yyy * light1_color.rgb;
+
+ light_direction = light2_location - IN.worldPosition.xyz;
+ attenuation = light2_color.a / length(light_direction);
+ light_direction = normalize(light_direction);
+ litLight = lit(dot(normalDirection.xyz, light_direction), 0, 0);
+ litLight.y *= clamp(attenuation * attenuation, 0, 1);
+ total_color.rgb += litLight.yyy * light2_color.rgb;
+
+ light_direction = light3_location - IN.worldPosition.xyz;
+ attenuation = light3_color.a / length(light_direction);
+ light_direction = normalize(light_direction);
+ litLight = lit(dot(normalDirection.xyz, light_direction), 0, 0);
+ litLight.y *= clamp(attenuation * attenuation, 0, 1);
+ total_color.rgb += litLight.yyy * light3_color.rgb;
+
+ textureColor.rgb += diffuse.rgb;
+ total_color *= textureColor;
+
+ // Fog
+ float fog = saturate(pow(2.718, -.005 * (length(cameraEye - IN.worldPosition.xyz) - 300)));
+ return lerp(fog_color, total_color, fog);
+}
+
+// #o3d VertexShaderEntryPoint vsMain
+// #o3d PixelShaderEntryPoint fsMain
+// #o3d MatrixLoadOrder RowMajor
+</textarea>
+</div>
+</body>
diff --git a/o3d/samples/io/levels/all_actors.js b/o3d/samples/io/levels/all_actors.js
new file mode 100644
index 0000000..e630949
--- /dev/null
+++ b/o3d/samples/io/levels/all_actors.js
@@ -0,0 +1,383 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @fileoverview This file defines one level that our little game can run
+ * inside of. This file was auto-generated by io.rb from a SketchUp model.
+ */
+if (levels == undefined) {
+ var levels = [];
+}
+
+levels.push({
+ name: 'All Actors',
+ colladaFile: 'all_actors.o3dtgz',
+
+ platforms: [
+ {'left': {'mapX': 0},
+ 'z': 0, 'rotZ': 0.0,
+ 'right': {'adjacentID': 1, 'mapX': 1151}},
+ {'left': {'adjacentID': 0, 'mapX': 1151},
+ 'z': 0, 'rotZ': 0.432710088213842,
+ 'right': {'adjacentID': 2, 'mapX': 2588}},
+ {'left': {'adjacentID': 1, 'mapX': 2588},
+ 'z': 0, 'rotZ': 0.0,
+ 'right': {'adjacentID': 3, 'mapX': 3159}},
+ {'left': {'adjacentID': 2, 'mapX': 3159},
+ 'z': 0, 'rotZ': 5.85194141379659,
+ 'right': {'adjacentID': 4, 'mapX': 3474}},
+ {'left': {'adjacentID': 3, 'mapX': 3474},
+ 'z': 0, 'rotZ': 5.27977245408739,
+ 'right': {'adjacentID': 5, 'mapX': 4178}},
+ {'left': {'adjacentID': 4, 'mapX': 4178},
+ 'z': 0, 'rotZ': 4.97548502987221,
+ 'right': {'adjacentID': 6, 'mapX': 4798}},
+ {'left': {'adjacentID': 5, 'mapX': 4798},
+ 'z': 0, 'rotZ': 4.71238898038469,
+ 'right': {'mapX': 6726}},
+ {'left': {'mapX': 3960},
+ 'z': 397, 'parentID': 4, 'rotZ': 5.27977245408739,
+ 'right': {'mapX': 4051}},
+ {'left': {'mapX': 1308},
+ 'z': 45, 'parentID': 1, 'rotZ': 0.432710088213843,
+ 'right': {'obstacleHeight': 15, 'mapX': 1563}},
+ {'left': {'mapX': 312},
+ 'z': 172, 'parentID': 0, 'rotZ': 0.0,
+ 'right': {'mapX': 524}},
+ {'left': {'mapX': 2915},
+ 'z': 123, 'parentID': 2, 'rotZ': 0.0,
+ 'right': {'obstacleHeight': 64, 'mapX': 3066}},
+ {'left': {'obstacleHeight': 72, 'mapX': 525},
+ 'z': 99, 'parentID': 0, 'rotZ': 0.0,
+ 'right': {'mapX': 545}},
+ {'left': {'mapX': 2117},
+ 'z': 74, 'parentID': 1, 'rotZ': 0.432710088213842,
+ 'right': {'obstacleHeight': 76, 'mapX': 2441}},
+ {'left': {'mapX': 514},
+ 'z': 365, 'parentID': 0, 'rotZ': 0.0,
+ 'right': {'mapX': 576}},
+ {'left': {'mapX': 1377},
+ 'z': 115, 'parentID': 1, 'rotZ': 0.432710088213839,
+ 'right': {'mapX': 1433}},
+ {'left': {'mapX': 1828},
+ 'z': 309, 'parentID': 1, 'rotZ': 0.432710088213842,
+ 'right': {'mapX': 1897}},
+ {'left': {'obstacleHeight': 37, 'mapX': 6233},
+ 'z': 166, 'parentID': 6, 'rotZ': 4.71238898038469,
+ 'right': {'mapX': 6327}},
+ {'left': {'mapX': 714},
+ 'z': 138, 'parentID': 0, 'rotZ': 0.0,
+ 'right': {'mapX': 869}},
+ {'left': {'mapX': 1505},
+ 'z': 233, 'parentID': 1, 'rotZ': 0.432710088213845,
+ 'right': {'mapX': 1563}},
+ {'left': {'obstacleHeight': 43, 'mapX': 4338},
+ 'z': 222, 'parentID': 5, 'rotZ': 4.97548502987221,
+ 'right': {'mapX': 4502}},
+ {'left': {'mapX': 593},
+ 'z': 287, 'parentID': 0, 'rotZ': 0.0,
+ 'right': {'obstacleHeight': 24, 'mapX': 717}},
+ {'left': {'mapX': 3377},
+ 'z': 326, 'parentID': 3, 'rotZ': 5.85194141379659,
+ 'right': {'obstacleHeight': 144, 'mapX': 4466}},
+ {'left': {'mapX': 3704},
+ 'z': 194, 'parentID': 4, 'rotZ': 5.27977245408739,
+ 'right': {'mapX': 3862}},
+ {'left': {'mapX': 4087},
+ 'z': 397, 'parentID': 4, 'rotZ': 5.27977245408739,
+ 'right': {'mapX': 4161}},
+ {'left': {'mapX': 2699},
+ 'z': 351, 'parentID': 2, 'rotZ': 0.0,
+ 'right': {'mapX': 2705}},
+ {'left': {'obstacleHeight': 22, 'mapX': 545},
+ 'z': 76, 'parentID': 0, 'rotZ': 0.0,
+ 'right': {'obstacleHeight': 22, 'mapX': 636}},
+ {'left': {'mapX': 1678},
+ 'z': 309, 'parentID': 1, 'rotZ': 0.432710088213842,
+ 'right': {'mapX': 1767}},
+ {'left': {'mapX': 4208},
+ 'z': 373, 'parentID': 5, 'rotZ': 4.97548502987219,
+ 'right': {'mapX': 4232}},
+ {'left': {'mapX': 2441},
+ 'z': 150, 'parentID': 1, 'rotZ': 0.432710088213842,
+ 'right': {'adjacentID': 29, 'mapX': 2587}},
+ {'left': {'adjacentID': 28, 'mapX': 2587},
+ 'z': 150, 'parentID': 2, 'rotZ': 0.0,
+ 'right': {'obstacleHeight': 200, 'mapX': 2705}},
+ {'left': {'mapX': 4208},
+ 'z': 252, 'parentID': 5, 'rotZ': 4.97548502987219,
+ 'right': {'obstacleHeight': 30, 'mapX': 4238}},
+ {'left': {'obstacleHeight': 107, 'mapX': -24},
+ 'z': 76, 'parentID': 0, 'rotZ': 0.0,
+ 'right': {'obstacleHeight': 42, 'mapX': 265}},
+ {'left': {'mapX': 4239},
+ 'z': 282, 'parentID': 5, 'rotZ': 4.9754850298722,
+ 'right': {'mapX': 4301}},
+ {'left': {'obstacleHeight': 38, 'mapX': 1433},
+ 'z': 76, 'parentID': 1, 'rotZ': 0.432710088213846,
+ 'right': {'obstacleHeight': 34, 'mapX': 1477}},
+ {'left': {'obstacleHeight': 17, 'mapX': 4302},
+ 'z': 265, 'parentID': 5, 'rotZ': 4.97548502987222,
+ 'right': {'mapX': 4338}},
+ {'left': {'mapX': 1505},
+ 'z': 148, 'parentID': 1, 'rotZ': 0.432710088213845,
+ 'right': {'mapX': 1563}},
+ {'left': {'obstacleHeight': 30, 'mapX': 2441},
+ 'z': 43, 'parentID': 1, 'rotZ': 0.432710088213842,
+ 'right': {'mapX': 2587}},
+ {'left': {'mapX': 1478},
+ 'z': 111, 'parentID': 1, 'rotZ': 0.432710088213837,
+ 'right': {'obstacleHeight': 37, 'mapX': 1504}},
+ {'left': {'mapX': 1927},
+ 'z': 290, 'parentID': 1, 'rotZ': 0.432710088213842,
+ 'right': {'mapX': 2193}},
+ {'left': {'mapX': 2706},
+ 'z': 351, 'parentID': 2, 'rotZ': 0.0,
+ 'right': {'mapX': 2738}},
+ {'left': {'obstacleHeight': 37, 'mapX': 6136},
+ 'z': 203, 'parentID': 6, 'rotZ': 4.71238898038469,
+ 'right': {'mapX': 6232}},
+ {'left': {'mapX': 4843},
+ 'z': 240, 'parentID': 6, 'rotZ': 4.71238898038469,
+ 'right': {'mapX': 6135}},
+ {'left': {'mapX': 3921},
+ 'z': 222, 'parentID': 4, 'rotZ': 5.27977245408739,
+ 'right': {'adjacentID': 43, 'mapX': 4177}},
+ {'left': {'adjacentID': 42, 'mapX': 4177},
+ 'z': 222, 'parentID': 5, 'rotZ': 4.97548502987222,
+ 'right': {'obstacleHeight': 30, 'mapX': 4207}},
+ {'left': {'mapX': 1478},
+ 'z': 196, 'parentID': 1, 'rotZ': 0.432710088213837,
+ 'right': {'mapX': 1504}},
+ {'left': {'obstacleHeight': 61, 'mapX': 870},
+ 'z': 76, 'parentID': 0, 'rotZ': 0.0,
+ 'right': {'adjacentID': 46, 'mapX': 1151}},
+ {'left': {'adjacentID': 45, 'mapX': 1151},
+ 'z': 76, 'parentID': 1, 'rotZ': 0.432710088213843,
+ 'right': {'obstacleHeight': 38, 'mapX': 1377}},
+ {'left': {'mapX': 4665},
+ 'z': 222, 'parentID': 5, 'rotZ': 4.97548502987221,
+ 'right': {'mapX': 4739}},
+ {'left': {'obstacleHeight': 72, 'mapX': 1563},
+ 'z': 76, 'parentID': 1, 'rotZ': 0.432710088213843,
+ 'right': {'mapX': 2018}},
+ {'left': {'mapX': 4769},
+ 'z': 222, 'parentID': 5, 'rotZ': 4.97548502987221,
+ 'right': {'adjacentID': 50, 'mapX': 4798}},
+ {'left': {'adjacentID': 49, 'mapX': 4798},
+ 'z': 222, 'parentID': 6, 'rotZ': 4.71238898038469,
+ 'right': {'obstacleHeight': 18, 'mapX': 4843}},
+ {'left': {'mapX': 312},
+ 'z': 234, 'parentID': 0, 'rotZ': 0.0,
+ 'right': {'mapX': 362}},
+ {'left': {'mapX': 265},
+ 'z': 119, 'parentID': 0, 'rotZ': 0.0,
+ 'right': {'obstacleHeight': 53, 'mapX': 312}},
+ {'left': {'mapX': 2642},
+ 'z': 326, 'parentID': 2, 'rotZ': 0.0,
+ 'right': {'obstacleHeight': 25, 'mapX': 2699}},
+ {'left': {'obstacleHeight': 37, 'mapX': 6327},
+ 'z': 129, 'parentID': 6, 'rotZ': 4.71238898038469,
+ 'right': {'mapX': 6596}},
+ {'left': {'obstacleHeight': 96, 'mapX': 6596},
+ 'z': 32, 'parentID': 6, 'rotZ': 4.71238898038469,
+ 'right': {'mapX': 6726}},
+ {'left': {'mapX': 1352},
+ 'z': 155, 'parentID': 1, 'rotZ': 0.432710088213843,
+ 'right': {'mapX': 1388}},
+ {'left': {'mapX': 4551},
+ 'z': 222, 'parentID': 5, 'rotZ': 4.97548502987221,
+ 'right': {'mapX': 4595}},
+ {'left': {'mapX': 636},
+ 'z': 98, 'parentID': 0, 'rotZ': 0.0,
+ 'right': {'obstacleHeight': 39, 'mapX': 714}},
+ {'left': {'obstacleHeight': 227, 'mapX': 2738},
+ 'z': 123, 'parentID': 2, 'rotZ': 0.0,
+ 'right': {'mapX': 2762}},
+ {'left': {'mapX': 2642},
+ 'z': 247, 'parentID': 2, 'rotZ': 0.0,
+ 'right': {'obstacleHeight': 78, 'mapX': 2701}},
+ {'left': {'mapX': 4401},
+ 'z': 394, 'parentID': 3, 'rotZ': 5.85194141379659,
+ 'right': {'obstacleHeight': 161, 'mapX': 4463}},
+ {'left': {'mapX': 1561},
+ 'z': 309, 'parentID': 1, 'rotZ': 0.432710088213842,
+ 'right': {'mapX': 1623}},
+ {'left': {'mapX': 3248},
+ 'z': 222, 'parentID': 3, 'rotZ': 5.85194141379659,
+ 'right': {'adjacentID': 64, 'mapX': 3473}},
+ {'left': {'adjacentID': 63, 'mapX': 3473},
+ 'z': 222, 'parentID': 4, 'rotZ': 5.2797724540874,
+ 'right': {'mapX': 3607}},
+ {'left': {'mapX': 400},
+ 'z': 270, 'parentID': 0, 'rotZ': 0.0,
+ 'right': {'mapX': 524}},
+ {'left': {'mapX': 3066},
+ 'z': 188, 'parentID': 2, 'rotZ': 0.0,
+ 'right': {'adjacentID': 67, 'mapX': 3159}},
+ {'left': {'adjacentID': 66, 'mapX': 3159},
+ 'z': 188, 'parentID': 3, 'rotZ': 5.85194141379659,
+ 'right': {'obstacleHeight': 33, 'mapX': 3248}}],
+ actors: [
+ new Avatar({ name: 'Avatar1', colladaID: 'Avatar1',
+ x: 23, y: 0, z: 77, mapX: 24, rotZ: 0.0628318530722379,
+
+ platformID: 31}),
+ new Spikem({ name: 'Spikem1', colladaID: 'Spikem1',
+ x: 186, y: -1, z: 76, mapX: 186, rotZ: 0.0,
+ platformID: 31}),
+ new Spikem({ name: 'Spikem2', colladaID: 'Spikem2',
+ x: 763, y: -2, z: 144, mapX: 762, rotZ: 0.0,
+ platformID: 17}),
+ new Spikem({ name: 'Spikem3', colladaID: 'Spikem3',
+ x: 437, y: -1, z: 178, mapX: 436, rotZ: 0.0,
+ platformID: 9}),
+ new Spikem({ name: 'Spikem4', colladaID: 'Spikem4',
+ x: 1819, y: 311, z: 80, mapX: 1886, rotZ: 0.0,
+ platformID: 48}),
+ new Spikem({ name: 'Spikem5', colladaID: 'Spikem5',
+ x: 2861, y: 602, z: 130, mapX: 2992, rotZ: 0.0,
+ platformID: 10}),
+ new Spikem({ name: 'Spikem6', colladaID: 'Spikem6',
+ x: 3830, y: -631, z: 228, mapX: 4704, rotZ: 0.0,
+ platformID: 47}),
+ new Spikem({ name: 'Spikem7', colladaID: 'Spikem7',
+ x: 3854, y: -1682, z: 246, mapX: 5757, rotZ: 0.0,
+ platformID: 41}),
+ new Spikem({ name: 'Spikem8', colladaID: 'Spikem8',
+ x: 3796, y: -507, z: 228, mapX: 4575, rotZ: 0.0,
+ platformID: 57}),
+ new Spikem({ name: 'Spikem9', colladaID: 'Spikem9',
+ x: 3765, y: -390, z: 228, mapX: 4454, rotZ: 0.0,
+ platformID: 19}),
+ new Spikem({ name: 'Spikem10', colladaID: 'Spikem10',
+ x: 3848, y: -2623, z: 22, mapX: 6699, rotZ: 0.0,
+ platformID: 55}),
+ new Arrow({ name: 'Arrow1', colladaID: 'Arrow1',
+ x: 40, y: 3, z: 99, mapX: 40, rotZ: 0.0,
+ platformID: 31}),
+ new Arrow({ name: 'Arrow2', colladaID: 'Arrow2',
+ x: 37, y: 0, z: 76, mapX: 37, rotZ: 0.0,
+ platformID: 31}),
+ new Arrow({ name: 'Arrow3', colladaID: 'Arrow3',
+ x: 40, y: 3, z: 86, mapX: 40, rotZ: 0.0,
+ platformID: 31}),
+ new HorizontalPad({ name: 'HorizontalPad1', colladaID: 'HorizontalPad1',
+ x: 1692, y: 247, z: 312, mapX: 1745, rotZ: -0.411897703470668,
+ platformID: 26}),
+ new HorizontalPad({ name: 'HorizontalPad2', colladaID: 'HorizontalPad2',
+ x: 1926, y: 355, z: 292, mapX: 2003, rotZ: -0.411897703470668,
+ platformID: 38}),
+ new Coin({ name: 'Coin1', colladaID: 'Coin1',
+ x: 342, y: -2, z: 247, mapX: 341, rotZ: 0.0,
+ platformID: 51}),
+ new Coin({ name: 'Coin2', colladaID: 'Coin2',
+ x: 424, y: -2, z: 299, mapX: 423, rotZ: 0.0,
+ platformID: 65}),
+ new Coin({ name: 'Coin3', colladaID: 'Coin3',
+ x: 461, y: -2, z: 303, mapX: 461, rotZ: 0.0,
+ platformID: 65}),
+ new Coin({ name: 'Coin4', colladaID: 'Coin4',
+ x: 496, y: -2, z: 303, mapX: 495, rotZ: 0.0,
+ platformID: 65}),
+ new Coin({ name: 'Coin5', colladaID: 'Coin5',
+ x: 1426, y: 121, z: 83, mapX: 1450, rotZ: 0.0,
+ platformID: 33}),
+ new Coin({ name: 'Coin6', colladaID: 'Coin6',
+ x: 1659, y: 232, z: 344, mapX: 1709, rotZ: 0.0,
+ platformID: 26}),
+ new Coin({ name: 'Coin7', colladaID: 'Coin7',
+ x: 1697, y: 254, z: 344, mapX: 1753, rotZ: 0.0,
+ platformID: 26}),
+ new Coin({ name: 'Coin8', colladaID: 'Coin8',
+ x: 1971, y: 382, z: 304, mapX: 2055, rotZ: 0.0,
+ platformID: 38}),
+ new Coin({ name: 'Coin9', colladaID: 'Coin9',
+ x: 2053, y: 419, z: 304, mapX: 2145, rotZ: 0.0,
+ platformID: 38}),
+ new Coin({ name: 'Coin10', colladaID: 'Coin10',
+ x: 2015, y: 402, z: 305, mapX: 2103, rotZ: 0.0,
+ platformID: 38}),
+ new Coin({ name: 'Coin11', colladaID: 'Coin11',
+ x: 2184, y: 475, z: 229, mapX: 2287, rotZ: 0.0,
+ platformID: 38}),
+ new Coin({ name: 'Coin12', colladaID: 'Coin12',
+ x: 2184, y: 475, z: 256, mapX: 2287, rotZ: 0.0,
+ platformID: 38}),
+ new Coin({ name: 'Coin13', colladaID: 'Coin13',
+ x: 2184, y: 475, z: 307, mapX: 2287, rotZ: 0.0,
+ platformID: 38}),
+ new Coin({ name: 'Coin14', colladaID: 'Coin14',
+ x: 2184, y: 475, z: 282, mapX: 2287, rotZ: 0.0,
+ platformID: 38}),
+ new Coin({ name: 'Coin15', colladaID: 'Coin15',
+ x: 546, y: -2, z: 422, mapX: 545, rotZ: 0.0,
+ platformID: 13}),
+ new Coin({ name: 'Coin16', colladaID: 'Coin16',
+ x: 2590, y: 607, z: 371, mapX: 2721, rotZ: 0.0,
+ platformID: 39}),
+ new Coin({ name: 'Coin17', colladaID: 'Coin17',
+ x: 2737, y: 606, z: 246, mapX: 2961, rotZ: 0.0,
+ platformID: 10}),
+ new Coin({ name: 'Coin18', colladaID: 'Coin18',
+ x: 2737, y: 606, z: 273, mapX: 2868, rotZ: 0.0,
+ platformID: 39}),
+ new Coin({ name: 'Coin19', colladaID: 'Coin19',
+ x: 3599, y: 21, z: 433, mapX: 4005, rotZ: 0.0,
+ platformID: 7}),
+ new Coin({ name: 'Coin20', colladaID: 'Coin20',
+ x: 3665, y: -78, z: 429, mapX: 4125, rotZ: 0.0,
+ platformID: 23}),
+ new Coin({ name: 'Coin21', colladaID: 'Coin21',
+ x: 4156, y: 77, z: 423, mapX: 4402, rotZ: 0.0,
+ platformID: 61}),
+ new Coin({ name: 'Coin22', colladaID: 'Coin22',
+ x: 4182, y: 71, z: 418, mapX: 4428, rotZ: 0.0,
+ platformID: 61}),
+ new Coin({ name: 'Coin23', colladaID: 'Coin23',
+ x: 4182, y: 71, z: 447, mapX: 4428, rotZ: 0.0,
+ platformID: 61}),
+ new Coin({ name: 'Coin24', colladaID: 'Coin24',
+ x: 4156, y: 77, z: 452, mapX: 4402, rotZ: 0.0,
+ platformID: 61}),
+ new Coin({ name: 'Coin25', colladaID: 'Coin25',
+ x: 4156, y: 77, z: 482, mapX: 4402, rotZ: 0.0,
+ platformID: 61}),
+ new Coin({ name: 'Coin26', colladaID: 'Coin26',
+ x: 4182, y: 71, z: 477, mapX: 4428, rotZ: 0.0,
+ platformID: 61}),
+ new VerticalPad({ name: 'VerticalPad1', colladaID: 'VerticalPad1',
+ x: 2663, y: 603, z: 221, mapX: 2794, rotZ: 0.0,
+ platformID: 60}),
+ new VerticalPad({ name: 'VerticalPad2', colladaID: 'VerticalPad2',
+ x: 2799, y: 603, z: 152, mapX: 2930, rotZ: 0.0,
+ platformID: 10})]
+});
diff --git a/o3d/samples/io/levels/all_actors.skp b/o3d/samples/io/levels/all_actors.skp
new file mode 100644
index 0000000..99e97ee
--- /dev/null
+++ b/o3d/samples/io/levels/all_actors.skp
Binary files differ
diff --git a/o3d/samples/io/levels/map1.js b/o3d/samples/io/levels/map1.js
new file mode 100644
index 0000000..2bd7eee
--- /dev/null
+++ b/o3d/samples/io/levels/map1.js
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @fileoverview This file defines one level that our little game can run
+ * inside of. This file was auto-generated by io.rb from a SketchUp model.
+ */
+if (levels == undefined) {
+ var levels = [];
+}
+
+levels.push({
+ name: 'Chapter One',
+ colladaFile: 'map1.o3dtgz',
+
+ platforms: [
+ {'left': {'mapX': 0},
+ 'z': 0.0, 'rotZ': 0.0,
+ 'right': {'adjacentID': 1, 'mapX': 99.9999999999998}},
+ {'left': {'adjacentID': 0, 'mapX': 99.9999999999998},
+ 'z': 0.0, 'rotZ': 0.785398163397443,
+ 'right': {'adjacentID': 2, 'mapX': 399.999999999999}},
+ {'left': {'adjacentID': 1, 'mapX': 399.999999999999},
+ 'z': 0.0, 'rotZ': 1.5707963267949,
+ 'right': {'adjacentID': 3, 'mapX': 561.617965644037}},
+ {'left': {'adjacentID': 2, 'mapX': 561.617965644037},
+ 'z': 0.0, 'rotZ': 0.0,
+ 'right': {'adjacentID': 4, 'mapX': 812.03592686014}},
+ {'left': {'adjacentID': 3, 'mapX': 812.03592686014},
+ 'z': 0.0, 'rotZ': 5.49970875351729,
+ 'right': {'adjacentID': 5, 'mapX': 922.215597985646}},
+ {'left': {'adjacentID': 4, 'mapX': 922.215597985646},
+ 'z': 0.0, 'rotZ': 4.716232199855,
+ 'right': {'adjacentID': 6, 'mapX': 1105.95801639536}},
+ {'left': {'adjacentID': 5, 'mapX': 1105.95801639536},
+ 'z': 0.0, 'rotZ': 4.04298946008257,
+ 'right': {'adjacentID': 7, 'mapX': 1208.29233370876}},
+ {'left': {'adjacentID': 6, 'mapX': 1208.29233370876},
+ 'z': 0.0, 'rotZ': 4.71238898038469,
+ 'right': {'adjacentID': 8, 'mapX': 1367.79233370876}},
+ {'left': {'adjacentID': 7, 'mapX': 1367.79233370876},
+ 'z': 0.0, 'rotZ': 0.0,
+ 'right': {'mapX': 1748.54233370876}},
+ {'left': {'mapX': 672.461706167729},
+ 'z': 133.25, 'parentID': 3, 'rotZ': 0.0,
+ 'right': {'mapX': 725.904505439235}},
+ {'left': {'mapX': 70.3557338817432},
+ 'z': 58.5, 'parentID': 0, 'rotZ': 0.0,
+ 'right': {'adjacentID': 11, 'mapX': 99.9999999999998}},
+ {'left': {'adjacentID': 10, 'mapX': 99.9999999999998},
+ 'z': 58.5, 'parentID': 1, 'rotZ': 0.785398163397443,
+ 'right': {'mapX': 177.235601196938}},
+ {'left': {'obstacleHeight': 32.0, 'mapX': 832.589734479398},
+ 'z': 65.5, 'parentID': 4, 'rotZ': 5.49970875351729,
+ 'right': {'adjacentID': 13, 'mapX': 922.215597985645}},
+ {'left': {'adjacentID': 12, 'mapX': 922.215597985645},
+ 'z': 65.5, 'parentID': 5, 'rotZ': 4.716232199855,
+ 'right': {'mapX': 1025.68972725654}},
+ {'left': {'mapX': 1176.12562519989},
+ 'z': 133.25, 'parentID': 6, 'rotZ': 4.04298946008257,
+ 'right': {'adjacentID': 15, 'mapX': 1208.29233370876}},
+ {'left': {'adjacentID': 14, 'mapX': 1208.29233370876},
+ 'z': 133.25, 'parentID': 7, 'rotZ': 4.71238898038469,
+ 'right': {'adjacentID': 16, 'mapX': 1367.79233370876}},
+ {'left': {'adjacentID': 15, 'mapX': 1367.79233370876},
+ 'z': 133.25, 'parentID': 8, 'rotZ': 0.0,
+ 'right': {'mapX': 1748.54233370876}},
+ {'left': {'obstacleHeight': 13.75, 'mapX': -13.0628855147159},
+ 'z': 69.5, 'parentID': 0, 'rotZ': 0.0,
+ 'right': {'mapX': 31.2499999999998}},
+ {'left': {'mapX': 812.03592686014},
+ 'z': 47.0, 'parentID': 4, 'rotZ': 5.49970875351729,
+ 'right': {'mapX': 922.215597985646}},
+ {'left': {'mapX': 177.235601196938},
+ 'z': 75.5, 'parentID': 1, 'rotZ': 0.785398163397443,
+ 'right': {'obstacleHeight': 28.25, 'mapX': 254.471202393877}},
+ {'left': {'mapX': 757.192725889269},
+ 'z': 97.5, 'parentID': 3, 'rotZ': 0.0,
+ 'right': {'adjacentID': 21, 'mapX': 812.03592686014}},
+ {'left': {'adjacentID': 20, 'mapX': 812.03592686014},
+ 'z': 97.5, 'parentID': 4, 'rotZ': 5.49970875351729,
+ 'right': {'mapX': 832.589734479398}},
+ {'left': {'mapX': 254.471202393877},
+ 'z': 103.75, 'parentID': 1, 'rotZ': 0.785398163397442,
+ 'right': {'mapX': 331.706803590815}},
+ {'left': {'mapX': 1091.9536790532},
+ 'z': 60.5, 'parentID': 5, 'rotZ': 4.71623219985501,
+ 'right': {'adjacentID': 24, 'mapX': 1105.95801639536}},
+ {'left': {'adjacentID': 23, 'mapX': 1105.95801639536},
+ 'z': 60.5, 'parentID': 6, 'rotZ': 4.04298946008257,
+ 'right': {'obstacleHeight': 31.5, 'mapX': 1143.95891669101}},
+ {'left': {'mapX': 399.999999999999},
+ 'z': 133.25, 'parentID': 2, 'rotZ': 1.5707963267949,
+ 'right': {'adjacentID': 26, 'mapX': 561.617965644037}},
+ {'left': {'adjacentID': 25, 'mapX': 561.617965644037},
+ 'z': 133.25, 'parentID': 3, 'rotZ': 0.0,
+ 'right': {'mapX': 621.461706167729}},
+ {'left': {'obstacleHeight': 74.25, 'mapX': 621.461706167729},
+ 'z': 59.0, 'parentID': 3, 'rotZ': 0.0,
+ 'right': {'obstacleHeight': 74.25, 'mapX': 672.461706167729}},
+ {'left': {'mapX': 1143.95891669101},
+ 'z': 92.0, 'parentID': 6, 'rotZ': 4.04298946008257,
+ 'right': {'mapX': 1176.12562519989}},
+ {'left': {'obstacleHeight': 21.25, 'mapX': 331.706803590815},
+ 'z': 82.5, 'parentID': 1, 'rotZ': 0.785398163397442,
+ 'right': {'mapX': 384.382894323691}}],
+ actors: [
+ new Avatar({
+ name: 'Avatar1', colladaID: 'Avatar1',
+ x: 8.25548871083476, y: -10.107499431384, z: 70.0761619683742,
+ mapX: 8.25548871083476, rotZ: 0.0628318530722379, platformID: 17}),
+ new HorizontalPad({
+ name: 'HorizontalPad1', colladaID: 'HorizontalPad1',
+ x: 69.9999999999999, y: 0.0, z: 0.0,
+ mapX: 69.9999999999999, rotZ: 0.0, platformID: 0}),
+ new Arrow({
+ name: 'Arrow1', colladaID: 'Arrow1',
+ x: 24.8151490181853, y: -1.89670669278537, z: 68.75,
+ mapX: 24.8151490181853, rotZ: 0.0, platformID: 17}),
+ new Arrow({
+ name: 'Arrow2', colladaID: 'Arrow2',
+ x: 24.8151490181853, y: -1.89670669278537, z: 79.25,
+ mapX: 24.8151490181853, rotZ: 0.0, platformID: 17}),
+ new Arrow({
+ name: 'Arrow3', colladaID: 'Arrow3',
+ x: 24.8151490181853, y: -1.89670669278537, z: 92,
+ mapX: 24.8151490181853, rotZ: 0.0, platformID: 17})]
+});
diff --git a/o3d/samples/io/levels/map1.skp b/o3d/samples/io/levels/map1.skp
new file mode 100644
index 0000000..8014334
--- /dev/null
+++ b/o3d/samples/io/levels/map1.skp
Binary files differ
diff --git a/o3d/samples/io/levels/starter_level.skp b/o3d/samples/io/levels/starter_level.skp
new file mode 100644
index 0000000..31869f0
--- /dev/null
+++ b/o3d/samples/io/levels/starter_level.skp
Binary files differ
diff --git a/o3d/samples/io/sound/_MISS.mp3 b/o3d/samples/io/sound/_MISS.mp3
new file mode 100644
index 0000000..7acfc80
--- /dev/null
+++ b/o3d/samples/io/sound/_MISS.mp3
Binary files differ
diff --git a/o3d/samples/io/sound/_PUNCH.mp3 b/o3d/samples/io/sound/_PUNCH.mp3
new file mode 100644
index 0000000..abb7277
--- /dev/null
+++ b/o3d/samples/io/sound/_PUNCH.mp3
Binary files differ
diff --git a/o3d/samples/io/sound/_SMASH.mp3 b/o3d/samples/io/sound/_SMASH.mp3
new file mode 100644
index 0000000..c655a11
--- /dev/null
+++ b/o3d/samples/io/sound/_SMASH.mp3
Binary files differ
diff --git a/o3d/samples/io/sound/_woosh.mp3 b/o3d/samples/io/sound/_woosh.mp3
new file mode 100644
index 0000000..5687be2
--- /dev/null
+++ b/o3d/samples/io/sound/_woosh.mp3
Binary files differ
diff --git a/o3d/samples/io/sound/ah.mp3 b/o3d/samples/io/sound/ah.mp3
new file mode 100644
index 0000000..ff40207
--- /dev/null
+++ b/o3d/samples/io/sound/ah.mp3
Binary files differ
diff --git a/o3d/samples/io/sound/arrow.mp3 b/o3d/samples/io/sound/arrow.mp3
new file mode 100644
index 0000000..e106d668
--- /dev/null
+++ b/o3d/samples/io/sound/arrow.mp3
Binary files differ
diff --git a/o3d/samples/io/sound/coin_3.mp3 b/o3d/samples/io/sound/coin_3.mp3
new file mode 100644
index 0000000..d8f3ff5
--- /dev/null
+++ b/o3d/samples/io/sound/coin_3.mp3
Binary files differ
diff --git a/o3d/samples/io/sound/music.mp3 b/o3d/samples/io/sound/music.mp3
new file mode 100644
index 0000000..653d191
--- /dev/null
+++ b/o3d/samples/io/sound/music.mp3
Binary files differ
diff --git a/o3d/samples/io/sound/page.mp3 b/o3d/samples/io/sound/page.mp3
new file mode 100644
index 0000000..f2c9a08
--- /dev/null
+++ b/o3d/samples/io/sound/page.mp3
Binary files differ
diff --git a/o3d/samples/io/sound/soundplayer.js b/o3d/samples/io/sound/soundplayer.js
new file mode 100644
index 0000000..95c0470
--- /dev/null
+++ b/o3d/samples/io/sound/soundplayer.js
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file defines a SoundPlayer object that can be used to
+ * play .mp3 files via a tiny soundplayer.swf file.
+ *
+ * Here's some example javascript for how you'd use this thing:
+ * var soundPlayer = new SoundPlayer();
+ * soundPlayer.play('songToPlayOnce.mp3')
+ * soundPlayer.play('soundToLoopForeverAt50PercentVolume.mp3',50,999)
+ * soundPlayer.play('sfxToPlayAsEvent.mp3',100,0,true)
+ * soundPlayer.setVolume('soundToTurnOff.mp3',0);
+ * soundPlayer.setGlobalVolume(20);
+ *
+ */
+
+// Write a DIV to the screen that we can later use to embed our flash movie.
+ var html = '<div style="position:absolute; top:-1000px; left:-1000px; z-index: 0;width: 200px; height: 200px; ">'
+ html = '<OBJECT style="position:absolute; top:-1000px; left:-1000px;" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" '
+ html += 'codebase="http://download.macromedia.com/pub/shockwave/cabs/flash'
+ html += '/swflash.cab#version=6,0,0,0" WIDTH="200" HEIGHT="200" '
+ html += 'id="soundPlayer" ALIGN=""> '
+ html += '<PARAM NAME=movie VALUE="sound/soundplayer.swf"> '
+ html += '<PARAM NAME=quality VALUE=high> '
+ html += '<PARAM NAME=allowScriptAccess VALUE=always> '
+ html += '<EMBED src="sound/soundplayer.swf" quality=high WIDTH="200" HEIGHT="200" '
+ html += 'NAME="soundPlayer" swLiveConnect=true ALIGN="" '
+ html += 'TYPE="application/x-shockwave-flash" allowScriptAccess="always" '
+ html += 'PLUGINSPAGE="http://www.macromedia.com/go/getflashplayer">'
+ html += '</EMBED></OBJECT></div>';
+ document.write(html);
+
+/**
+ * Constructor for our SoundPlayer object.
+ */
+function SoundPlayer() {
+ // TODO: Make this more generic so it loads at the author's
+ // request rather than at include time.
+}
+
+/**
+ * This is a helper function to simplify code throughout. Returns a default
+ * value if the first is empty.
+ * @param {Object} val1 Value to check for emptiness
+ * @param {Object} val2 Value to return if va1 is empty
+ * @return {Object} One of the above.
+ */
+SoundPlayer.prototype.defaultIfEmpty = function(val1,val2) {
+ if (val1 == undefined) {
+ return val2;
+ } else {
+ return val1;
+ }
+}
+
+/**
+ * This is the way to send action calls to the flash plugin. In this case,
+ * a string containing our action script that we want to execute. (Note that
+ * flash doesn't have a true "eval" function, so this isn't a security hole.)
+ * @param {string} callStr The command to have flash execute.
+ */
+SoundPlayer.prototype.makeActionScriptCall = function(callStr) {
+ try {
+ window.document['soundPlayer'].SetVariable('actionScriptCall', callStr);
+ } catch (ex) {
+ // Catch the exception so that any problems with the sound player don't
+ // derail the whle game.
+ }
+}
+
+// TODO: Need to clean up these function comments to follow
+// jsdoc standard. Also need to clean up for readability.
+
+/**
+ * SoundPlayer.play() : Call this guy to play a sound or music file.
+ *
+ * fileURL : a url to any mp3 you want to play (required)
+ * volumeLevel : a number from 0 (silent) to 100 (optional, defaults to 100)
+ * loopCount : How many times to loop the sound (optional, defaults to 0)
+ * isNewTrack : Boolean. If true, then a new "track" will be created,
+ * allowing you to layer as many sounds as you like
+ */
+SoundPlayer.prototype.play = function(fileURL,volumeLevel,loopCount,isNewTrack) {
+ volumeLevel = this.defaultIfEmpty(volumeLevel,100);
+ loopCount = this.defaultIfEmpty(loopCount,0);
+ isNewTrack = this.defaultIfEmpty(isNewTrack,false);
+ var actionScriptCall = 'play('+fileURL+',' + volumeLevel + ',' +
+ loopCount + ',' + isNewTrack + ')';
+ this.makeActionScriptCall(actionScriptCall);
+}
+
+/**
+ * SoundPlayer.setVolume() : Call this guy to change the volume on a sound.
+ *
+ * fileURL : a url to any mp3 or wav file you want to change the volume on
+ * volumeLevel : a number from 0 (silent) to 100 (loud)
+ */
+SoundPlayer.prototype.setVolume = function(fileURL,volumeLevel,fadeTime) {
+ var actionScriptCall = 'setVolume('+fileURL+',' + volumeLevel + ')';
+ this.makeActionScriptCall(actionScriptCall);
+}
+
+/**
+ * SoundPlayer.setGlobalVolume() : Call this guy to change the global
+ * volume on a sound. The global volume "multiplies" each sound's volume,
+ * so if your sound is playing at 100% and your global is at 50%, then
+ * your sounds will be half as loud.
+ *
+ * volumeLevel : a number from 0 (silent) to 100 (loud)
+ */
+SoundPlayer.prototype.setGlobalVolume = function(volumeLevel) {
+ var actionScriptCall = 'setGlobalVolume(' + volumeLevel + ')';
+ this.makeActionScriptCall(actionScriptCall);
+}
+
+// Create a global variable for this application to use.
+var soundPlayer = new SoundPlayer();
diff --git a/o3d/samples/io/sound/soundplayer.swf b/o3d/samples/io/sound/soundplayer.swf
new file mode 100644
index 0000000..4a1f0e0
--- /dev/null
+++ b/o3d/samples/io/sound/soundplayer.swf
Binary files differ
diff --git a/o3d/samples/io/sound/step1.mp3 b/o3d/samples/io/sound/step1.mp3
new file mode 100644
index 0000000..8419591
--- /dev/null
+++ b/o3d/samples/io/sound/step1.mp3
Binary files differ
diff --git a/o3d/samples/io/sound/step2.mp3 b/o3d/samples/io/sound/step2.mp3
new file mode 100644
index 0000000..02895f0
--- /dev/null
+++ b/o3d/samples/io/sound/step2.mp3
Binary files differ
diff --git a/o3d/samples/io/sound/step3.mp3 b/o3d/samples/io/sound/step3.mp3
new file mode 100644
index 0000000..8d7cf61
--- /dev/null
+++ b/o3d/samples/io/sound/step3.mp3
Binary files differ
diff --git a/o3d/samples/io/sound/ug.mp3 b/o3d/samples/io/sound/ug.mp3
new file mode 100644
index 0000000..7297175
--- /dev/null
+++ b/o3d/samples/io/sound/ug.mp3
Binary files differ
diff --git a/o3d/samples/io/ui/Thumbs.db b/o3d/samples/io/ui/Thumbs.db
new file mode 100644
index 0000000..1bb8f7a
--- /dev/null
+++ b/o3d/samples/io/ui/Thumbs.db
Binary files differ
diff --git a/o3d/samples/io/ui/bgtile.jpg b/o3d/samples/io/ui/bgtile.jpg
new file mode 100644
index 0000000..d6278f1
--- /dev/null
+++ b/o3d/samples/io/ui/bgtile.jpg
Binary files differ
diff --git a/o3d/samples/io/ui/book_capbottom.jpg b/o3d/samples/io/ui/book_capbottom.jpg
new file mode 100644
index 0000000..ef07486
--- /dev/null
+++ b/o3d/samples/io/ui/book_capbottom.jpg
Binary files differ
diff --git a/o3d/samples/io/ui/book_capleft.jpg b/o3d/samples/io/ui/book_capleft.jpg
new file mode 100644
index 0000000..eae6cd9
--- /dev/null
+++ b/o3d/samples/io/ui/book_capleft.jpg
Binary files differ
diff --git a/o3d/samples/io/ui/book_capright.jpg b/o3d/samples/io/ui/book_capright.jpg
new file mode 100644
index 0000000..0d7bfad
--- /dev/null
+++ b/o3d/samples/io/ui/book_capright.jpg
Binary files differ
diff --git a/o3d/samples/io/ui/book_captop.jpg b/o3d/samples/io/ui/book_captop.jpg
new file mode 100644
index 0000000..a1e2705
--- /dev/null
+++ b/o3d/samples/io/ui/book_captop.jpg
Binary files differ
diff --git a/o3d/samples/io/ui/book_cover.jpg b/o3d/samples/io/ui/book_cover.jpg
new file mode 100644
index 0000000..eaf42a0
--- /dev/null
+++ b/o3d/samples/io/ui/book_cover.jpg
Binary files differ
diff --git a/o3d/samples/io/ui/book_innercover.jpg b/o3d/samples/io/ui/book_innercover.jpg
new file mode 100644
index 0000000..7eca9e8
--- /dev/null
+++ b/o3d/samples/io/ui/book_innercover.jpg
Binary files differ
diff --git a/o3d/samples/io/ui/book_page1.jpg b/o3d/samples/io/ui/book_page1.jpg
new file mode 100644
index 0000000..3b62f53e
--- /dev/null
+++ b/o3d/samples/io/ui/book_page1.jpg
Binary files differ
diff --git a/o3d/samples/io/ui/book_page2.jpg b/o3d/samples/io/ui/book_page2.jpg
new file mode 100644
index 0000000..a0bfa1f
--- /dev/null
+++ b/o3d/samples/io/ui/book_page2.jpg
Binary files differ
diff --git a/o3d/samples/io/ui/book_page3.jpg b/o3d/samples/io/ui/book_page3.jpg
new file mode 100644
index 0000000..740e8b9
--- /dev/null
+++ b/o3d/samples/io/ui/book_page3.jpg
Binary files differ
diff --git a/o3d/samples/io/ui/book_pageblank.jpg b/o3d/samples/io/ui/book_pageblank.jpg
new file mode 100644
index 0000000..d266853
--- /dev/null
+++ b/o3d/samples/io/ui/book_pageblank.jpg
Binary files differ
diff --git a/o3d/samples/io/ui/covershadow.png b/o3d/samples/io/ui/covershadow.png
new file mode 100644
index 0000000..2409a78
--- /dev/null
+++ b/o3d/samples/io/ui/covershadow.png
Binary files differ
diff --git a/o3d/samples/io/ui/io.css b/o3d/samples/io/ui/io.css
new file mode 100644
index 0000000..03918ad
--- /dev/null
+++ b/o3d/samples/io/ui/io.css
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+BODY {
+ overflow: hidden;
+ background-image: url(../ui/bgtile.jpg);
+ padding: 0px;
+ margin: 0px;
+ color: white;
+}
+#content {
+ visibility: hidden; /* for now */
+ position: absolute;
+ color: black;
+ text-align: center;
+ padding-top: 50px;
+ font-size: 25px;
+ width: 951px;
+ height: 500px;
+}
+#container {
+ position: absolute;
+ text-align: center;
+ width: 882px;
+ height: 549px;
+ margin-left: 37px;
+}
+#footer {
+ position: absolute;
+ bottom: 0px;
+ height: 70px;
+ width: 100%;
+ text-align: center;
+}
+#whole-page {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ line-height: 100%;
+ border: 1px solid red;
+}
+#table-main {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+}
+#table-middle {
+ padding: 0px;
+ margin: 0px;
+ border: 0px;
+}
+#book-left {
+ background-image: url(../ui/scrollwork.gif);
+ background-repeat: repeat-x;
+ background-position: right 40px;
+ text-align: right;
+ vertical-align: top;
+}
+#book-center {
+ width: 988px;
+ height: 662px;
+}
+#book-right {
+ background-image: url(../ui/scrollwork.gif);
+ background-repeat: repeat-x;
+ background-position: left 40px;
+}
+#book-capleft {
+ position: absolute;
+}
+#book-captop {
+ position: relative;
+}
+#cover-div {
+ position: absolute;
+ z-index: 100;
+ padding-top: 15px;
+ margin: 0px;
+}
+#cover-seam {
+ position: absolute;
+ z-index: 1000;
+ border-left: 1px solid #222222;
+ height: 575px;
+}
+#cover {
+ width: 975px;
+ height: 600px;
+ position: absolute;
+ z-index: 100;
+ cursor: pointer;
+ border: 0px;
+}
+#page1 {
+ position: absolute;
+ z-index: 90;
+ cursor: pointer;
+ border: 0px;
+}
+#page2 {
+ position: absolute;
+ z-index: 80;
+ cursor: pointer;
+}
+#page3 {
+ position: absolute;
+ z-index: 70;
+ cursor: pointer;
+}
+#cover-shadow {
+ position: absolute;
+ z-index: 99;
+}
+#innercover-div {
+ padding-top: 15px;
+ width: 20px;
+ height: 600px;
+ overflow: hidden;
+ text-align: right;
+ float: right;
+}
+
+#innercover {
+ width: 0px;
+ height: 600px;
+ float: right;
+}
+
+.levellink {
+ cursor: pointer;
+ text-decoration: underline;
+}
+
+
+#fps {
+ font-family: sans-serif;
+ text-align: left;
+ padding-top: 15px;
+ width: 250px;
+ float: left;
+ padding-left: 25px;
+ color: #B87333;
+}
+#output {
+ font-family: sans-serif;
+ text-align: right;
+ padding-top: 15px;
+ width: 250px;
+ float: right;
+ padding-right: 25px;
+ color: #222222;
+}
diff --git a/o3d/samples/io/ui/logo.gif b/o3d/samples/io/ui/logo.gif
new file mode 100644
index 0000000..7c9bae3
--- /dev/null
+++ b/o3d/samples/io/ui/logo.gif
Binary files differ
diff --git a/o3d/samples/io/ui/scrollwork.gif b/o3d/samples/io/ui/scrollwork.gif
new file mode 100644
index 0000000..55e2476
--- /dev/null
+++ b/o3d/samples/io/ui/scrollwork.gif
Binary files differ
diff --git a/o3d/samples/juggler.html b/o3d/samples/juggler.html
new file mode 100644
index 0000000..3dc01f1
--- /dev/null
+++ b/o3d/samples/juggler.html
@@ -0,0 +1,431 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+O3D Juggler
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html style="width: 100%; height: 100%;">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+ Juggler Shader
+</title>
+
+<script type="text/javascript" src="o3djs/base.js"></script>
+
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+
+// Events
+// Run the init() function once the page has finished loading.
+// unload() when the page is unloaded.
+window.onload = init;
+window.onunload = unload;
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_o3dElement;
+var g_viewInfo;
+var g_pack;
+var g_o3dWidth = -1;
+var g_o3dHeight = -1;
+var g_transform;
+var g_clock = 0.0;
+var g_timeMult = 1; // amount to multiply elapsed time by.
+ // Used to make the animation run faster or slower.
+var g_finished = false; // for selenium testing
+var g_thetaParam;
+var g_numParam;
+var g_numBalls; // Must be either 3, 5, 7, or 9 for now.
+var g_speedScale; // Used to make higher numbers of balls animate faster.
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D, loads the effect, and creates the square.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initialize global variables and libraries.
+ g_o3dElement = clientElements[0];
+ g_o3d = g_o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = g_o3dElement.client;
+
+ // Create a g_pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot,
+ [0, 0, 0, 1]);
+
+ var effect = g_pack.createObject('Effect');
+ effect.loadFromFXString(document.getElementById('shader').value);
+
+ // Create a Material for the effect.
+ var myMaterial = g_pack.createObject('Material');
+
+ // Apply our effect to this material.
+ myMaterial.effect = effect;
+
+ // Set the material's drawList for opaque objects.
+ myMaterial.drawList = g_viewInfo.performanceDrawList;
+
+ // Create the params the effect needs on the material.
+ effect.createUniformParameters(myMaterial);
+
+ // Create a square.
+ var myShape = o3djs.primitives.createPlane(g_pack, myMaterial,
+ 1, 1, 1, 1);
+
+ // Set up the individual parameters in our effect file.
+ g_thetaParam = myMaterial.getParam('theta');
+ g_thetaParam.value = 0;
+ g_numParam = myMaterial.getParam('num');
+ updateNum();
+
+ // Set the position of the camera.
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [0, 1, 0], //eye
+ [0, 0, 0], //target
+ [0, 0, -1]); //up
+
+ // Generate the projection matrix based
+ // on the g_o3d plugin size by calling resize().
+ resize();
+
+ // Now attach the square to the root of the transform graph.
+ g_client.root.addShape(myShape);
+
+ toggleRenderCallback();
+
+ g_finished = true; // for selenium testing.
+}
+
+function updateNum() {
+ var group = document.the_form.radio_group;
+ for (var i = 0; i < group.length; ++i) {
+ if (group[i].checked) {
+ setNumBalls(parseInt(group[i].value));
+ }
+ }
+}
+
+function toggleRenderCallback() {
+ var box = document.the_form.check_box;
+ if (box.checked) {
+ g_client.setRenderCallback(onrender);
+ } else {
+ g_client.clearRenderCallback();
+ }
+}
+
+function setNumBalls(num) {
+ g_numBalls = num;
+ g_numParam.value = g_numBalls;
+ g_speedScale = Math.sqrt(g_numBalls) * 5;
+}
+
+function onrender(render_event) {
+ g_clock += render_event.elapsedTime * g_timeMult;
+ g_thetaParam.value = g_clock * g_speedScale;
+
+ // If we don't check the size of the client area every frame we don't get a
+ // chance to adjust the perspective matrix fast enough to keep up with the
+ // browser resizing us, so onrender must call resize.
+ resize();
+}
+
+
+// Generates the projection matrix based on the size of the g_o3d plugin
+// and calculates the view-projection matrix.
+function resize() {
+ var newWidth = g_client.width;
+ var newHeight = g_client.height;
+
+ if (newWidth != g_o3dWidth || newHeight != g_o3dHeight) {
+ g_o3dWidth = newWidth;
+ g_o3dHeight = newHeight;
+
+ // Determine what the size of the rendered square within the client should
+ // be in pixels.
+ var side = g_o3dWidth < g_o3dHeight ?
+ g_o3dWidth : g_o3dHeight;
+
+ // Convert to the region of world space that must be enclosed by the
+ // orthographic projection.
+ var worldSize = g_math.divVectorScalar([g_o3dWidth, g_o3dHeight], side);
+
+ // Find a projection matrix to transform from world space to screen space.
+ g_viewInfo.drawContext.projection = o3djs.math.matrix4.orthographic(
+ -0.5 * worldSize[0], 0.5 * worldSize[0],
+ -0.5 * worldSize[1], 0.5 * worldSize[1],
+ 0.5, 1.5);
+ }
+}
+
+/**
+ * Removes any callbacks so they don't get called after the page has unloaded.
+ */
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+</script>
+</head>
+<body style="width: 100%; height: 100%;">
+<table style="width: 100%; height: 100%;">
+ <tr>
+ <td>
+ <h1>Juggler</h1>
+ <p>
+ This sample displays a juggling pattern computed entirely in a shader.
+ <form name="the_form">
+ <input type="radio" name="radio_group" value="3"
+ onclick=updateNum()>3 Balls
+ <input type="radio" name="radio_group" value="5" checked
+ onclick=updateNum()>5 Balls
+ <input type="radio" name="radio_group" value="7"
+ onclick=updateNum()>7 Balls
+ <input type="radio" name="radio_group" value="9"
+ onclick=updateNum()>9 Balls
+ <input type="checkbox" name="check_box" checked
+ onclick=toggleRenderCallback()>Animate
+ </form>
+ </p>
+ <table id="container" style="width: 100%; height: 80%;">
+ <tr>
+ <td height="100%">
+ <!-- Start of g_o3d plugin -->
+ <div id="o3d" style="width: 100%; height: 100%;"></div>
+ <!-- End of g_o3d plugin -->
+ </td>
+ </tr>
+ </table>
+ <!-- a simple way to get a multiline string -->
+ <textarea id="shader" name="shader" cols="80" rows="20"
+ style="display: none;">
+// The 4x4 world view projection matrix.
+float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+
+float theta;
+float num;
+
+// input parameters for our vertex shader
+struct VertexShaderInput {
+ float4 position : POSITION;
+ float2 texCoord : TEXCOORD0;
+};
+
+// input parameters for our pixel shader
+// also the output parameters for our vertex shader
+struct PixelShaderInput {
+ float4 position : POSITION;
+ float2 texCoord : TEXCOORD0;
+ float4 color : COLOR;
+};
+
+/**
+ * vertexShaderMain - our vertex shader for the juggling texture
+ */
+PixelShaderInput vertexShaderMain(VertexShaderInput input) {
+ PixelShaderInput output;
+
+ output.position = mul(input.position, worldViewProjection);
+ output.texCoord = 4.0 * (input.texCoord - float2(0.5, 0.5));
+ output.color = float4(1, 1, 1, 1);
+
+ return output;
+}
+
+float length_2(in float2 v) {
+ return dot(v, v);
+}
+
+// Draw the balls in a single arc.
+float drawBallsInArc(in float pi,
+ in float4 offset,
+ in float2 source_hand,
+ in float2 dest_hand,
+ in float height_factor,
+ in float baseline,
+ in float ball_radius_2,
+ in float hand_throw_offset,
+ in float2 Z,
+ in float threshold) {
+ // Map theta from its current range of [0, 2 * num * pi) onto [0, (num - 1))
+ // by scaling, adding offset, and modding, then map that to [0, 1) by scaling.
+ // The first mapping tells us where in the repeating cycle we are, and the
+ // second mapping simplifies the calculation of the parabola.
+
+ // The vector offset is used to distinguish between balls in the same arc, but
+ // out of phase. At the beginning of this function, all the operations are
+ // vectorized to save instructions; we get to calculate 4 ball positions for
+ // the price of 1.
+
+ // The reason for the (num - 1) in the expression below is that with num
+ // balls, each ball spends (num - 1) beats in the air, then one in the hand.
+ // So (num - 1) is the length of time a parabola takes.
+
+ float4 time = fmod(theta / pi + offset, (num - 1)) / (num - 1);
+ float dx = dest_hand.x - source_hand.x;
+ float4 x = time * dx + source_hand.x - hand_throw_offset;
+ float4 y = time * (1 - time);
+ y = y * height_factor + baseline;
+ float4 ZX = Z.x;
+ float4 ZY = Z.y;
+ float4 len_2 = (ZX - x) * (ZX - x) + (ZY - y) * (ZY - y);
+ float4 temp = clamp((len_2 - ball_radius_2) / threshold, 0, 1);
+
+ // One minus the product of all entries in temp.
+ temp.xy = temp.xy * temp.zw;
+ return 1 - temp.x * temp.y;
+}
+
+float drawAirborneBalls(in float pi,
+ in float4 offset,
+ in float2 right_hand,
+ in float2 left_hand,
+ in float height_factor,
+ in float baseline,
+ in float ball_radius_2,
+ in float hand_swing_radius,
+ in float2 Z,
+ in float threshold) {
+ // The expression below is one minus a product. The first factor in the
+ // product is responsible for balls moving from right to left, and the second
+ // factor is responsible for balls moving from left to right.
+ return 1 -
+ (1 - drawBallsInArc(pi, offset, right_hand, left_hand, height_factor,
+ baseline, ball_radius_2, hand_swing_radius, Z, threshold)) *
+ (1 - drawBallsInArc(pi, offset + 1, left_hand, right_hand, height_factor,
+ baseline, ball_radius_2, -hand_swing_radius, Z, threshold));
+}
+
+/**
+ * pixelShaderMain - pixel shader
+ */
+
+float4 pixelShaderMain(PixelShaderInput input) : COLOR {
+ const float pi = 3.14159265;
+ const float baseline = -1.2;
+ const float2 right_hand = float2(0.8, baseline);
+ const float2 left_hand = float2(-0.8, baseline);
+ const float hand_swing_radius = 0.3;
+ const float hand_radius_2 = 0.15 * 0.15;
+ const float ball_radius_2 = 0.1 * 0.1;
+
+ const float4 ball_color = float4(1, 1, 1, 1);
+ const float4 right_hand_color = float4(1, 0, 0, 1);
+ const float4 left_hand_color = float4(0, 0, 1, 1);
+ const float4 background_color = float4(0, 0, 0, 0);
+
+ const float threshold = 0.002; // Used in clamp for antialiasing.
+
+ float height_factor = num;
+
+ float2 Z = input.texCoord;
+
+ // Coerce to the range [0, 2 * Pi * num].
+ float2 r_h = hand_swing_radius * float2(-cos(theta), sin(theta)) + right_hand;
+ float2 l_h = hand_swing_radius * float2(-cos(theta), -sin(theta)) + left_hand;
+
+ // Initialize color of pixel to background_color. Background color has an
+ // alpha of 0. Color of objects each have alpha 1, so multiplying by
+ // (1-alpha) before adding the color ensures that nothing gets overdrawn.
+ // It's kind of like a rudimentary z-buffer.
+ float4 result = background_color;
+
+ // Draw the hands.
+ result += (1 - result.a) * (
+ clamp((hand_radius_2 - length_2(Z - r_h)) / threshold, 0, 1) *
+ (Z.y < r_h.y) * right_hand_color +
+ clamp((hand_radius_2 - length_2(Z - l_h)) / threshold, 0, 1) *
+ (Z.y < l_h.y) * left_hand_color);
+
+ // Draw the ball in the hand. There is always a ball in exactly one hand, and
+ // which hand that is alternates.
+ float2 hand;
+ if (fmod(floor(theta / pi), 2) > 0.5) {
+ hand = r_h;
+ } else {
+ hand = l_h;
+ }
+ result += (1 - result.a) * ball_color *
+ clamp((ball_radius_2 - length_2(Z - hand)) / threshold, 0, 1);
+
+ float4 offset = float4(0, 2, 4, 6);
+
+ // For each of up-to-4 pairs of balls you want to add, increment offsets by
+ // (8, 8, 8, 8) and call drawAirborneBalls again.
+
+ // Draw airborne balls.
+ result += (1 - result.a) * drawAirborneBalls(pi,
+ offset,
+ right_hand,
+ left_hand,
+ height_factor,
+ baseline,
+ ball_radius_2,
+ hand_swing_radius,
+ Z,
+ threshold);
+
+ return result;
+}
+
+// Here we tell our effect file *which* functions are
+// our vertex and pixel shaders.
+
+// #o3d VertexShaderEntryPoint vertexShaderMain
+// #o3d PixelShaderEntryPoint pixelShaderMain
+// #o3d MatrixLoadOrder RowMajor
+ </textarea>
+ </td>
+ </tr>
+</table>
+</body>
+</html>
diff --git a/o3d/samples/julia.html b/o3d/samples/julia.html
new file mode 100644
index 0000000..6162db4
--- /dev/null
+++ b/o3d/samples/julia.html
@@ -0,0 +1,295 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+O3D Julia Set
+
+This sample draws an animated julia set in real time using
+the pixel shader for the computation.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html style="width: 100%; height: 100%;">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Julia Set Pixel Shader
+</title>
+
+<script type="text/javascript" src="o3djs/base.js"></script>
+
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+
+// Events
+// Run the init() function once the page has finished loading.
+// unload() when the page is unloaded.
+window.onload = init;
+window.onunload = unload;
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_o3dElement;
+var g_viewInfo;
+var g_pack;
+var g_o3dWidth = -1;
+var g_o3dHeight = -1;
+var g_clock = 0.0;
+var g_timeMult = 1;
+var g_finished = false; // for selenium testing
+var g_seedParam;
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes o3d, loads the effect, and creates the square.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initialize global variables and libraries.
+ g_o3dElement = clientElements[0];
+ g_o3d = g_o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = g_o3dElement.client;
+
+ // Create a g_pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot,
+ [0, 0, 0, 1]);
+
+ // Load shader code from DOM and use it to build the effect.
+ var effect = g_pack.createObject('Effect');
+ effect.loadFromFXString(document.getElementById('shader').value);
+
+ // Create a Material for the effect.
+ var myMaterial = g_pack.createObject('Material');
+
+ // Apply our effect to this material.
+ myMaterial.effect = effect;
+
+ // Set the material's drawList for opaque objects.
+ myMaterial.drawList = g_viewInfo.performanceDrawList;
+
+ // create the parameters the effect needs to the material.
+ effect.createUniformParameters(myMaterial);
+
+ // Create a square.
+ var myShape = o3djs.primitives.createPlane(g_pack, myMaterial, 1, 1, 1, 1);
+
+ // Initialize effect parameters to something reasonable
+ g_seedParam = myMaterial.getParam('seed');
+ g_seedParam.value = [0.2, 0.5];
+
+ // Put the camera somewhere where it has a good view of that square.
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [0, 1, 0], //eye
+ [0, 0, 0], //target
+ [0, 0, -1]); //up
+
+ // Generate the projection matrix based
+ // on the g_o3d plugin size by calling resize().
+ resize();
+
+ // Now attach the square to the root of the transform graph.
+ g_client.root.addShape(myShape);
+
+ g_client.setRenderCallback(onrender);
+
+ g_finished = true; // for selenium testing.
+}
+
+
+/**
+ * Render callback. Walks the seed of the Julia set through
+ * a parametric path in the complex plane that stays
+ * in the neighborhood of the Mandelbrot set.
+ */
+function onrender(render_event) {
+ g_clock += render_event.elapsedTime * g_timeMult;
+
+ var t = 0.1 * g_clock;
+ var x = 0.6 * Math.cos(3.0 * t) - 0.3;
+ var y = (0.5 * x + 1.7)*(0.2 * Math.sin(7 * t));
+
+ g_seedParam.value = [x, y];
+
+ resize();
+}
+
+
+/**
+ * Generates the projection matrix based on the size of the o3d plugin and
+ * calculates the view-projection matrix.
+ */
+function resize() {
+ var newWidth = g_client.width;
+ var newHeight = g_client.height;
+
+ if (newWidth != g_o3dWidth || newHeight != g_o3dHeight) {
+ g_o3dWidth = newWidth;
+ g_o3dHeight = newHeight;
+
+ // Determine what the size of the rendered square within the client should
+ // be in pixels.
+ var side = g_o3dWidth < g_o3dHeight ?
+ g_o3dWidth : g_o3dHeight;
+
+ // Convert to the region of world space that must be enclosed by the
+ // orthographic projection.
+ var worldSize = g_math.divVectorScalar([g_o3dWidth, g_o3dHeight], side);
+
+ // Find a projection matrix to transform from world space to screen space.
+ g_viewInfo.drawContext.projection = g_math.matrix4.orthographic(
+ -0.5 * worldSize[0], 0.5 * worldSize[0],
+ -0.5 * worldSize[1], 0.5 * worldSize[1],
+ 0.5, 1.5);
+ }
+}
+
+
+/**
+ * Removes any callbacks so they don't get called after the page has unloaded.
+ */
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+
+
+</script>
+</head>
+<body style="width: 100%; height: 100%;">
+<table style="width: 100%; height: 100%;">
+ <tr>
+ <td>
+ <h1>Julia Set</h1>
+ <p>
+ This sample draws an animated julia set in real time using
+ the pixel shader for the computation.
+ </p>
+ <table id="container" style="width: 100%; height: 80%;">
+ <tr>
+ <td height="100%">
+ <!-- Start of g_o3d plugin -->
+ <div id="o3d" style="width: 100%; height: 100%;"></div>
+ <!-- End of g_o3d plugin -->
+ </td>
+ </tr>
+ </table>
+ <!-- a simple way to get a multiline string -->
+ <textarea id="shader" name="shader" cols="80" rows="20"
+ style="display: none;">
+// The 4x4 world view projection matrix.
+float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+
+// The seed for the julia set (c in the expression z(n+1) = z(n)^2+c).
+float2 seed;
+
+// input parameters for our vertex shader
+struct VertexShaderInput {
+ float4 position : POSITION;
+ float2 texCoord : TEXCOORD0;
+};
+
+// input parameters for our pixel shader
+// also the output parameters for our vertex shader
+struct PixelShaderInput {
+ float4 position : POSITION;
+ float2 texCoord : TEXCOORD0;
+};
+
+/**
+ * vertexShaderMain - Multiplies position by world-view-projection matrix, and
+ * passes on texture coordinates scaled to put the origin in the center of the
+ * quad and reveal a nicely sized portion of the plane to show the julia set.
+ */
+PixelShaderInput vertexShaderMain(VertexShaderInput input) {
+ PixelShaderInput output;
+
+ output.position = mul(input.position, worldViewProjection);
+ output.texCoord = 4.0 * (input.texCoord - float2(0.5, 0.5));
+
+ return output;
+}
+
+
+/**
+ * pixelShaderMain - Calculates the color of the pixel by iterating on the
+ * formula z = z*z + seed. After some number of iterations, the magnitude of z
+ * determines the color.
+ */
+float4 pixelShaderMain(PixelShaderInput input) : COLOR {
+ float2 Z = input.texCoord;
+
+ // Number of iterations hardcoded here. The more iterations, the crisper the
+ // image.
+ for(int i = 0; i < 10; ++i) {
+ Z = float2(Z.x * Z.x - Z.y * Z.y, 2.0 * Z.x * Z.y) + seed;
+
+ // Some graphics cards and some software renderers don't appreciate large
+ // floating point values, so we clamp to prevent Z from getting that big.
+ if (i > 7) {
+ Z = clamp(Z, -25, 25);
+ }
+ }
+
+ return (1 - length(Z)) * float4(0.5, 1, 2, 1);
+}
+
+// Here we tell our effect file *which* functions are
+// our vertex and pixel shaders.
+
+// #o3d VertexShaderEntryPoint vertexShaderMain
+// #o3d PixelShaderEntryPoint pixelShaderMain
+// #o3d MatrixLoadOrder RowMajor
+ </textarea>
+ </td>
+ </tr>
+</table>
+</body>
+</html>
+
+
diff --git a/o3d/samples/multiple-clients.html b/o3d/samples/multiple-clients.html
new file mode 100644
index 0000000..331adc0
--- /dev/null
+++ b/o3d/samples/multiple-clients.html
@@ -0,0 +1,243 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+This sample demonstrates running more than 1 instance of the plugin at
+the same time.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Multiple Clients
+</title>
+<!-- Our javascript code -->
+<script type="text/javascript" src="o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.io');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+
+// Events
+// Run the init() function once the page has finished loading.
+// uninit() when the page is unloaded.
+window.onload = init;
+window.onunload = uninit;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_clients = [];
+var g_packs = [];
+var g_viewInfos = [];
+var g_num_clients;
+var g_setupDone = false;
+
+var g_animate_angle = [];
+var g_eye;
+
+var NUM_ACROSS = 10;
+var NUM_DOWN = 5;
+
+/**
+ * Initializes O3D, loads the effect, and draws the cube.
+ */
+function init() {
+ // Create a bunch of clients.
+ var ii = 0;
+ var tbodyElem = document.getElementById('clients');
+ for (var row = 0; row < NUM_DOWN; row++) {
+ var trElem = document.createElement('tr');
+ for (var column = 0; column < NUM_ACROSS; column++) {
+ var tdElem = document.createElement('td');
+ var objElem = o3djs.util.createClient(tdElem);
+ objElem.style.width = '60px';
+ objElem.style.height = '60px';
+ objElem.id = 'o3d' + ii;
+ objElem.name = 'o3d' + ii;
+ trElem.appendChild(tdElem);
+ ii++;
+ }
+ tbodyElem.appendChild(trElem);
+ }
+ g_num_clients = ii;
+
+ var chromeWorkaround = o3djs.base.IsChrome10();
+ // wait for the browser to settle down.
+ var clearId = window.setInterval(function() {
+ var element = document.o3d0
+ if (!element.o3d) {
+ if (chromeWorkaround) {
+ if (element.style.width != '60px') {
+ element.style.width = '60px';
+ } else {
+ element.style.width = '1px';
+ }
+ }
+ return;
+ }
+ if (chromeWorkaround && element.style.width != '60px') {
+ // The plugin has loaded but it may not be the right size yet.
+ element.style.width = '60px';
+ return;
+ }
+ window.clearInterval(clearId);
+ setup();
+ }, 10);
+}
+
+function setup() {
+ // Lookup all the clients.
+ for (var ii = 0; ii < g_num_clients; ++ii) {
+ g_clients[ii] = document.getElementById('o3d' + ii).client;
+ }
+
+ // Get constants and math functions.
+ g_o3d = document.o3d0.o3d;
+ g_math = o3djs.math;
+
+ // Init our sample helper libraries. We need to call this manually since
+ // we did not use o3djs.base.makeClients.
+ o3djs.base.init(document.o3d0);
+
+ var effectSource =
+ o3djs.io.loadTextFileSynchronous('shaders/vertex-color.shader');
+
+ // For each client make a cube, context, effect,...
+ for (ii = 0; ii < g_num_clients; ii++) {
+ // Create a pack to manage our resources/assets
+ g_packs[ii] = g_clients[ii].createPack();
+
+ // Create the render graph for a view.
+ g_viewInfos[ii] = o3djs.rendergraph.createBasicView(
+ g_packs[ii],
+ g_clients[ii].root,
+ g_clients[ii].renderGraphRoot);
+
+ g_animate_angle[ii] = 0;
+
+ // Create our projection matrix, with a vertical field of view of 45 degrees
+ // a near clipping plane of 0.1 and far clipping plane of 100.
+ g_viewInfos[ii].drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(45),
+ 128 / 128,
+ 0.1,
+ 100);
+
+ // Create and load the effect.
+ var effect = g_packs[ii].createObject('Effect');
+ effect.loadFromFXString(effectSource);
+
+ // Create a material for it.
+ var material = g_packs[ii].createObject('Material');
+
+ // Set the material's drawList
+ material.drawList = g_viewInfos[ii].performanceDrawList;
+
+ // Apply our effect to this material.
+ material.effect = effect;
+ effect.createUniformParameters(material);
+
+ // Create a cube using the effect we have loaded.
+ createCube(g_clients[ii].root, g_packs[ii], material);
+ }
+
+ // Set our render callback for animation.
+ // This sets a function to be executed every time a frame is rendered.
+ g_clients[0].setRenderCallback(onrender);
+ g_setupDone = true;
+}
+
+/**
+ * Creates a cube with the given effect.
+ *
+ * @param {!o3d.Transform} parent Transform to parent shape to.
+ * @param {!o3d.Pack} pack Pack to use.
+ * @param {!o3d.Material} material Material to use.
+ */
+function createCube(parent, pack, material) {
+ // First, create a 'shape' node to store our sphere.
+ var myShape = o3djs.primitives.createRainbowCube(pack, material, 0.5);
+
+ // Attach the cube to the root of the transform graph.
+ parent.addShape(myShape);
+}
+
+// Animates the cubes.
+// This function executes on each frame.
+// It was set using document.o3d.setEventCallback(..) in setup().
+function onrender(render_event) {
+ var elapsedTime = render_event.elapsedTime;
+ for (var ii = 0; ii < g_num_clients; ii++) {
+ // Rotate frame rate independently.
+ g_animate_angle[ii] += (0.1 + 0.1 * ii) * elapsedTime;
+
+ // Eye-position, this is where our camera is at.
+ var eye = [
+ 0.0 + Math.sin(g_animate_angle[ii]) * 1.0,
+ 1.0,
+ 0.0 + Math.cos(g_animate_angle[ii]) * 1.0
+ ];
+
+ // Target, this is where our camera is pointed at.
+ var target = [0, 0, 0];
+
+ // Up-vector, this tells the camera which direction is 'up'.
+ // We define the positive y-direction to be up in this example.
+ var up = [0, 1, 0];
+
+ g_viewInfos[ii].drawContext.view = g_math.matrix4.lookAt(eye, target, up);
+ }
+}
+
+/**
+ * Removes any callbacks so they don't get called after the page has unloaded.
+ */
+function uninit() {
+ if (g_clients[0]) {
+ g_clients[0].cleanup();
+ }
+}
+
+</script>
+</head>
+<body>
+<h1>Multiple Clients</h1>
+<table>
+ <tbody id="clients">
+ </tbody>
+</table>
+</body>
+</html>
diff --git a/o3d/samples/multiple-views.html b/o3d/samples/multiple-views.html
new file mode 100644
index 0000000..b5694c9
--- /dev/null
+++ b/o3d/samples/multiple-views.html
@@ -0,0 +1,220 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+Example of multiple views into the same scene.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Multiple Views
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.camera');
+o3djs.require('o3djs.pack');
+o3djs.require('o3djs.scene');
+
+// Events
+// init() once the page has finished loading.
+window.onload = init;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_viewInfos = [];
+var g_pack;
+var g_finished = false; // for selenium testing
+
+/**
+ * Loads a scene into the transform graph.
+ * @param {!o3d.Pack} pack Pack to load scene into.
+ * @param {string} fileName filename of the scene.
+ * @param {!o3d.Transform} parent parent node in the transform graph to
+ * which to load the scene into.
+ */
+function loadScene(pack, fileName, parent) {
+ // Get our full path to the scene
+ var scenePath = o3djs.util.getCurrentURI() + fileName;
+
+ // Load the file given the full path, and call the callback function
+ // when its done loading.
+ o3djs.scene.loadScene(g_client, pack, parent, scenePath, callback);
+
+ /**
+ * Our callback is called once the scene has been loaded into memory
+ * from the web or locally.
+ * @param {!o3d.Pack} pack The pack that was passed in above.
+ * @param {!o3d.Transform} parent The parent that was passed in above.
+ * @param {*} exception null if loading succeeded.
+ */
+ function callback(pack, parent, exception) {
+ if (exception) {
+ alert('Could not load: ' + fileName + '\n' + exception);
+ return;
+ }
+ // Get a cameraInfo (an object with a view and projection matrix)
+ // using our javascript library function
+ var cameraInfo = o3djs.camera.getViewAndProjectionFromCameras(
+ parent,
+ g_client.width / 2, // because this context is only used 1/2 the area
+ g_client.height);
+
+ // Copy the view and projection to the draw context.
+ g_viewInfos[0].drawContext.view = cameraInfo.view;
+ g_viewInfos[0].drawContext.projection = cameraInfo.projection;
+
+ // Generate draw elements and setup material draw lists.
+ o3djs.pack.preparePack(pack, g_viewInfos[0]);
+
+ g_finished = true; // for selenium testing.
+ }
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and loads the scene into the transform graph.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = o3dElement.client;
+
+ // Creates a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view for the left half of screen.
+ g_viewInfos[0] = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot,
+ [0.7, 0.2, 0.2, 1],
+ 0,
+ [0, 0, 0.5, 1]); // left half of screen.
+
+ // Setup 3 areas. Each area needs a viewport, a tree traveral, 2 drawpasses
+ // and although we could clear the screen once let's do it per viewport just
+ // as an example. The original area is already setup except it needs a
+ // viewport setting.
+
+ // make the 2 right viewports
+ for (var yy = 0; yy < 2; yy++) {
+ // Set the view extents to the right half, top or bottom.
+ var viewExtents = [0.5, yy * 0.5, 0.5, 0.5];
+
+ // Give each view a different background color.
+ var viewBackgroundColor = [0.5, 0.2 + 0.5 * yy, 0.7 - 0.5 * yy, 1.0];
+
+ // Make this view get processes after the first view.
+ var viewPriority = yy + 1;
+
+ // Create an extra view. We call createExtraView because we want to
+ // share DrawLists with the first view so that all 3 views draw the same
+ // things.
+ var viewInfo = o3djs.rendergraph.createExtraView(
+ g_viewInfos[0],
+ viewExtents,
+ viewBackgroundColor,
+ viewPriority);
+
+ // Save it off for later.
+ g_viewInfos[g_viewInfos.length] = viewInfo;
+
+ // Get the DrawContext for this area and set the view and
+ // projection matrices.
+ var drawContext = viewInfo.drawContext;
+
+ if (yy == 0) {
+ drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(45),
+ (g_client.width * 0.5) / (g_client.height * 0.5),
+ 0.1,
+ 100);
+ drawContext.view = g_math.matrix4.lookAt(
+ [-10, 3, -15], // eye
+ [0, 2, 0], // target
+ [0, 1, 0]); // up
+ } else {
+ // lets make this one orthographic
+ var aspect = g_client.width / g_client.height;
+ drawContext.projection = g_math.matrix4.orthographic(
+ -10,
+ 10,
+ -10 / aspect,
+ 10 / aspect,
+ 0,
+ 100);
+ // look directly from the front
+ drawContext.view = g_math.matrix4.lookAt(
+ [0, 5, 30], // eye
+ [0, 5, 0], // target
+ [0, 1, 0]); // up
+ }
+ }
+
+ // Creates a transform to put our data on.
+ var my_data_root = g_pack.createObject('Transform');
+
+ // Connects our root to the client's root.
+ my_data_root.parent = g_client.root;
+
+ // Load the scene into the transform graph as a child my_data_root
+ loadScene(g_pack, 'assets/yard.o3dtgz', my_data_root);
+}
+</script>
+</head>
+<body>
+<h1>Multiple Views</h1>
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 800px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/samples/o3djs/arcball.js b/o3d/samples/o3djs/arcball.js
new file mode 100644
index 0000000..0f511ab
--- /dev/null
+++ b/o3d/samples/o3djs/arcball.js
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// A shout out to Terrance J. Grant at tatewake.com for his tutorial on arcball
+// implementations.
+
+/**
+ * @fileoverview This file contains functions for implementing an arcball
+ * calculation. It puts them in the "arcball" module on the o3djs object.
+ *
+ * Note: This library is only a sample. It is not meant to be some official
+ * library. It is provided only as example code.
+ *
+ */
+
+o3djs.provide('o3djs.arcball');
+
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.quaternions');
+
+/**
+ * A Module for arcball manipulation.
+ *
+ * This is useful for rotating a model with the mouse.
+ *
+ * @namespace
+ */
+o3djs.arcball = o3djs.arcball || {};
+
+/**
+ * Creates a new arcball.
+ * @param {number} areaWidth width of area arcball should cover.
+ * @param {number} areaHeight height of area arcball should cover.
+ * @return {!o3djs.arcball.ArcBall} The created arcball.
+ * @see o3djs.arcball
+ */
+o3djs.arcball.create = function(areaWidth, areaHeight) {
+ return new o3djs.arcball.ArcBall(areaWidth, areaHeight);
+};
+
+/**
+ * A class that implements an arcball.
+ * @constructor
+ * @param {number} areaWidth width of area arcball should cover.
+ * @param {number} areaHeight height of area arcball should cover.
+ * @see o3djs.arcball
+ */
+o3djs.arcball.ArcBall = function(areaWidth, areaHeight) {
+ this.startVector = [0, 0, 0];
+ this.endVector = [0, 0, 0];
+ this.areaWidth = areaWidth;
+ this.areaHeight = areaHeight;
+};
+
+
+/**
+ * Sets the size of the arcball.
+ * @param {number} areaWidth width of area arcball should cover.
+ * @param {number} areaHeight height of area arcball should cover.
+ */
+o3djs.arcball.ArcBall.prototype.setAreaSize = function(areaWidth, areaHeight) {
+ this.areaWidth = areaWidth;
+ this.areaHeight = areaHeight;
+};
+
+/**
+ * Converts a 2d point to a point on the sphere of radius 1 sphere.
+ * @param {!o3djs.math.Vector2} newPoint A point in 2d.
+ * @return {!o3djs.math.Vector3} A point on the sphere of radius 1.
+ */
+o3djs.arcball.ArcBall.prototype.mapToSphere = function(newPoint) {
+ // Copy parameter into temp
+ var tempPoint = o3djs.math.copyVector(newPoint);
+
+ // Scale to -1.0 <-> 1.0
+ tempPoint[0] = tempPoint[0] / this.areaWidth * 2.0 - 1.0;
+ tempPoint[1] = 1.0 - tempPoint[1] / this.areaHeight * 2.0;
+
+ // Compute square of length from center
+ var lengthSquared = o3djs.math.lengthSquared(tempPoint);
+
+ // If the point is mapped outside of the sphere... (length > radius squared)
+ if (lengthSquared > 1.0) {
+ return o3djs.math.normalize(tempPoint).concat(0);
+ } else {
+ // Otherwise it's on the inside.
+ return tempPoint.concat(Math.sqrt(1.0 - lengthSquared));
+ }
+};
+
+/**
+ * Records the starting point on the sphere.
+ * @param {!o3djs.math.Vector2} newPoint point in 2d.
+ */
+o3djs.arcball.ArcBall.prototype.click = function(newPoint) {
+ this.startVector = this.mapToSphere(newPoint);
+};
+
+/**
+ * Computes the rotation of the sphere based on the initial point clicked as
+ * set through Arcball.click and the current point passed in as newPoint
+ * @param {!o3djs.math.Vector2} newPoint point in 2d.
+ * @return {!o3djs.quaternions.Quaternion} A quaternion representing the new
+ * orientation.
+ */
+o3djs.arcball.ArcBall.prototype.drag = function(newPoint) {
+ this.endVector = this.mapToSphere(newPoint);
+
+ return o3djs.math.cross(this.startVector, this.endVector).concat(
+ o3djs.math.dot(this.startVector, this.endVector));
+};
diff --git a/o3d/samples/o3djs/base.js b/o3d/samples/o3djs/base.js
new file mode 100644
index 0000000..a1cd85f
--- /dev/null
+++ b/o3d/samples/o3djs/base.js
@@ -0,0 +1,779 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview Base for all o3d sample utilties.
+ * For more information about o3d see
+ * http://code.google.com/p/o3d.
+ *
+ *
+ * The main point of this module is to provide a central place to
+ * have an init function to register an o3d namespace object because many other
+ * modules need access to it.
+ */
+
+/**
+ * A namespace for all the o3djs utility libraries.
+ * @namespace
+ */
+var o3djs = o3djs || {};
+
+/**
+ * Define this because the Google internal JSCompiler needs goog.typedef below.
+ */
+var goog = goog || {};
+
+/**
+ * A macro for defining composite types.
+ *
+ * By assigning goog.typedef to a name, this tells Google internal JSCompiler
+ * that this is not the name of a class, but rather it's the name of a composite
+ * type.
+ *
+ * For example,
+ * /** @type {Array|NodeList} / goog.ArrayLike = goog.typedef;
+ * will tell JSCompiler to replace all appearances of goog.ArrayLike in type
+ * definitions with the union of Array and NodeList.
+ *
+ * Does nothing in uncompiled code.
+ */
+goog.typedef = true;
+
+/**
+ * Reference to the global context. In most cases this will be 'window'.
+ */
+o3djs.global = this;
+
+/**
+ * Flag used to force a function to run in the browser when it is called
+ * from V8.
+ * @type {boolean}
+ */
+o3djs.BROWSER_ONLY = true;
+
+/**
+ * Array of namespaces that have been provided.
+ * @private
+ * @type {!Array.<string>}
+ */
+o3djs.provided_ = [];
+
+/**
+ * Creates object stubs for a namespace. When present in a file,
+ * o3djs.provide also indicates that the file defines the indicated
+ * object.
+ * @param {string} name name of the object that this file defines.
+ */
+o3djs.provide = function(name) {
+ // Ensure that the same namespace isn't provided twice.
+ if (o3djs.getObjectByName(name) &&
+ !o3djs.implicitNamespaces_[name]) {
+ throw 'Namespace "' + name + '" already declared.';
+ }
+
+ var namespace = name;
+ while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) {
+ o3djs.implicitNamespaces_[namespace] = true;
+ }
+
+ o3djs.exportPath_(name);
+ o3djs.provided_.push(name);
+};
+
+
+/**
+ * Namespaces implicitly defined by o3djs.provide. For example,
+ * o3djs.provide('o3djs.events.Event') implicitly declares
+ * that 'o3djs' and 'o3djs.events' must be namespaces.
+ *
+ * @type {Object}
+ * @private
+ */
+o3djs.implicitNamespaces_ = {};
+
+/**
+ * Builds an object structure for the provided namespace path,
+ * ensuring that names that already exist are not overwritten. For
+ * example:
+ * "a.b.c" -> a = {};a.b={};a.b.c={};
+ * Used by o3djs.provide and o3djs.exportSymbol.
+ * @param {string} name name of the object that this file defines.
+ * @param {Object} opt_object the object to expose at the end of the path.
+ * @param {Object} opt_objectToExportTo The object to add the path to; default
+ * is |o3djs.global|.
+ * @private
+ */
+o3djs.exportPath_ = function(name, opt_object, opt_objectToExportTo) {
+ var parts = name.split('.');
+ var cur = opt_objectToExportTo || o3djs.global;
+ var part;
+
+ // Internet Explorer exhibits strange behavior when throwing errors from
+ // methods externed in this manner. See the testExportSymbolExceptions in
+ // base_test.html for an example.
+ if (!(parts[0] in cur) && cur.execScript) {
+ cur.execScript('var ' + parts[0]);
+ }
+
+ // Parentheses added to eliminate strict JS warning in Firefox.
+ while (parts.length && (part = parts.shift())) {
+ if (!parts.length && o3djs.isDef(opt_object)) {
+ // last part and we have an object; use it.
+ cur[part] = opt_object;
+ } else if (cur[part]) {
+ cur = cur[part];
+ } else {
+ cur = cur[part] = {};
+ }
+ }
+};
+
+
+/**
+ * Returns an object based on its fully qualified external name. If you are
+ * using a compilation pass that renames property names beware that using this
+ * function will not find renamed properties.
+ *
+ * @param {string} name The fully qualified name.
+ * @param {Object} opt_obj The object within which to look; default is
+ * |o3djs.global|.
+ * @return {Object} The object or, if not found, null.
+ */
+o3djs.getObjectByName = function(name, opt_obj) {
+ var parts = name.split('.');
+ var cur = opt_obj || o3djs.global;
+ for (var pp = 0; pp < parts.length; ++pp) {
+ var part = parts[pp];
+ if (cur[part]) {
+ cur = cur[part];
+ } else {
+ return null;
+ }
+ }
+ return cur;
+};
+
+
+/**
+ * Implements a system for the dynamic resolution of dependencies.
+ * @param {string} rule Rule to include, in the form o3djs.package.part.
+ */
+o3djs.require = function(rule) {
+ // if the object already exists we do not need do do anything
+ if (o3djs.getObjectByName(rule)) {
+ return;
+ }
+ var path = o3djs.getPathFromRule_(rule);
+ if (path) {
+ o3djs.included_[path] = true;
+ o3djs.writeScripts_();
+ } else {
+ throw new Error('o3djs.require could not find: ' + rule);
+ }
+};
+
+
+/**
+ * Path for included scripts.
+ * @type {string}
+ */
+o3djs.basePath = '';
+
+
+/**
+ * Object used to keep track of urls that have already been added. This
+ * record allows the prevention of circular dependencies.
+ * @type {Object}
+ * @private
+ */
+o3djs.included_ = {};
+
+
+/**
+ * This object is used to keep track of dependencies and other data that is
+ * used for loading scripts.
+ * @private
+ * @type {Object}
+ */
+o3djs.dependencies_ = {
+ visited: {}, // used when resolving dependencies to prevent us from
+ // visiting the file twice.
+ written: {} // used to keep track of script files we have written.
+};
+
+
+/**
+ * Tries to detect the base path of the o3djs-base.js script that
+ * bootstraps the o3djs libraries.
+ * @private
+ */
+o3djs.findBasePath_ = function() {
+ var doc = o3djs.global.document;
+ if (typeof doc == 'undefined') {
+ return;
+ }
+ if (o3djs.global.BASE_PATH) {
+ o3djs.basePath = o3djs.global.BASE_PATH;
+ return;
+ } else {
+ // HACKHACK to hide compiler warnings :(
+ o3djs.global.BASE_PATH = null;
+ }
+ var scripts = doc.getElementsByTagName('script');
+ for (var script, i = 0; script = scripts[i]; i++) {
+ var src = script.src;
+ var l = src.length;
+ if (src.substr(l - 13) == 'o3djs/base.js') {
+ o3djs.basePath = src.substr(0, l - 13);
+ return;
+ }
+ }
+};
+
+
+/**
+ * Writes a script tag if, and only if, that script hasn't already been added
+ * to the document. (Must be called at execution time.)
+ * @param {string} src Script source.
+ * @private
+ */
+o3djs.writeScriptTag_ = function(src) {
+ var doc = o3djs.global.document;
+ if (typeof doc != 'undefined' &&
+ !o3djs.dependencies_.written[src]) {
+ o3djs.dependencies_.written[src] = true;
+ doc.write('<script type="text/javascript" src="' +
+ src + '"></' + 'script>');
+ }
+};
+
+
+/**
+ * Resolves dependencies based on the dependencies added using addDependency
+ * and calls writeScriptTag_ in the correct order.
+ * @private
+ */
+o3djs.writeScripts_ = function() {
+ // the scripts we need to write this time.
+ var scripts = [];
+ var seenScript = {};
+ var deps = o3djs.dependencies_;
+
+ function visitNode(path) {
+ if (path in deps.written) {
+ return;
+ }
+
+ // we have already visited this one. We can get here if we have cyclic
+ // dependencies.
+ if (path in deps.visited) {
+ if (!(path in seenScript)) {
+ seenScript[path] = true;
+ scripts.push(path);
+ }
+ return;
+ }
+
+ deps.visited[path] = true;
+
+ if (!(path in seenScript)) {
+ seenScript[path] = true;
+ scripts.push(path);
+ }
+ }
+
+ for (var path in o3djs.included_) {
+ if (!deps.written[path]) {
+ visitNode(path);
+ }
+ }
+
+ for (var i = 0; i < scripts.length; i++) {
+ if (scripts[i]) {
+ o3djs.writeScriptTag_(o3djs.basePath + scripts[i]);
+ } else {
+ throw Error('Undefined script input');
+ }
+ }
+};
+
+
+/**
+ * Looks at the dependency rules and tries to determine the script file that
+ * fulfills a particular rule.
+ * @param {string} rule In the form o3djs.namespace.Class or
+ * project.script.
+ * @return {string?} Url corresponding to the rule, or null.
+ * @private
+ */
+o3djs.getPathFromRule_ = function(rule) {
+ var parts = rule.split('.');
+ return parts.join('/') + '.js';
+};
+
+o3djs.findBasePath_();
+
+/**
+ * Returns true if the specified value is not |undefined|.
+ * WARNING: Do not use this to test if an object has a property. Use the in
+ * operator instead.
+ * @param {*} val Variable to test.
+ * @return {boolean} Whether variable is defined.
+ */
+o3djs.isDef = function(val) {
+ return typeof val != 'undefined';
+};
+
+
+/**
+ * Exposes an unobfuscated global namespace path for the given object.
+ * Note that fields of the exported object *will* be obfuscated,
+ * unless they are exported in turn via this function or
+ * o3djs.exportProperty.
+ *
+ * <p>Also handy for making public items that are defined in anonymous
+ * closures.
+ *
+ * ex. o3djs.exportSymbol('Foo', Foo);
+ *
+ * ex. o3djs.exportSymbol('public.path.Foo.staticFunction',
+ * Foo.staticFunction);
+ * public.path.Foo.staticFunction();
+ *
+ * ex. o3djs.exportSymbol('public.path.Foo.prototype.myMethod',
+ * Foo.prototype.myMethod);
+ * new public.path.Foo().myMethod();
+ *
+ * @param {string} publicPath Unobfuscated name to export.
+ * @param {Object} object Object the name should point to.
+ * @param {Object} opt_objectToExportTo The object to add the path to; default
+ * is |o3djs.global|.
+ */
+o3djs.exportSymbol = function(publicPath, object, opt_objectToExportTo) {
+ o3djs.exportPath_(publicPath, object, opt_objectToExportTo);
+};
+
+/**
+ * This string contains JavaScript code to initialize a new V8 instance.
+ * @private
+ * @type {string}
+ */
+o3djs.v8Initializer_ = '';
+
+/**
+ * This array contains references to objects that v8 needs to bind to when
+ * it initializes.
+ * @private
+ * @type {!Array.<Object>}
+ */
+o3djs.v8InitializerArgs_ = [];
+
+/**
+ * Converts any JavaScript value to a string representation that when evaluated
+ * will result in an equal value.
+ * @param {*} value Any value.
+ * @return {string} A string representation for the value.
+ * @private
+ */
+o3djs.valueToString_ = function(value) {
+ switch (typeof(value)) {
+ case 'undefined':
+ return 'undefined';
+ case 'string':
+ var escaped = escape(value);
+ if (escaped === value) {
+ return '"' + value + '"';
+ } else {
+ return 'unescape("' + escaped + '")';
+ }
+ case 'object':
+ if (value === null) {
+ return 'null';
+ } else {
+ // TODO: all the other builtin JavaScript objects like Date,
+ // Number, Boolean, etc.
+ if (value instanceof RegExp) {
+ var result =
+ 'new RegExp(' + o3djs.valueToString_(value.source) + ', "';
+ if (value.global) {
+ result += 'g';
+ }
+ if (value.ignoreCase) {
+ result += 'i';
+ }
+ if (value.multiline) {
+ result += 'm';
+ }
+ result += '")';
+ return result;
+ } else if (o3djs.base.isArray(value)) {
+ var valueAsArray = /** @type {!Array} */ (value);
+ var result = '[';
+ var separator = '';
+ for (var i = 0; i < valueAsArray.length; ++i) {
+ result += separator + o3djs.valueToString_(valueAsArray[i]);
+ separator = ',';
+ }
+ result += ']\n';
+ return result;
+ } else {
+ var valueAsObject = /** @type {!Object} */ (value);
+ var result = '{\n';
+ var separator = '';
+ for (var propertyName in valueAsObject) {
+ result += separator + '"' + propertyName + '": ' +
+ o3djs.valueToString_(valueAsObject[propertyName]);
+ separator = ',';
+ }
+ result += '}\n';
+ return result;
+ }
+ }
+ default:
+ return value.toString()
+ }
+};
+
+/**
+ * Given an object holding a namespace and the name of that namespace,
+ * generates a string that when evaluated will populate the namespace.
+ * @param {!Object} namespaceObject An object holding a namespace.
+ * @param {string} namespaceName The name of the namespace.
+ * @param {!Array.<Object>} opt_args An array of objects that will be used
+ * together with the initializer string to populate a namespace. The args
+ * may be referenced from initializer code as args_[i] where i is the index
+ * in the array.
+ * @return {string} A string that will populate the namespace.
+ * @private
+ */
+o3djs.namespaceInitializer_ = function(namespaceObject,
+ namespaceName,
+ opt_args) {
+ var result = namespaceName + ' = {};\n';
+ for (var propertyName in namespaceObject) {
+ var propertyNamespaceName = namespaceName + '.' + propertyName;
+ var propertyValue = namespaceObject[propertyName];
+ if (typeof(propertyValue) === 'object' && propertyValue !== null &&
+ !o3djs.base.isArray(propertyValue) &&
+ !(propertyValue instanceof RegExp)) {
+ result += o3djs.namespaceInitializer_(propertyValue,
+ propertyNamespaceName);
+ } else {
+ var valueAsString = o3djs.valueToString_(propertyValue);
+
+ // If this is a browser only function then bind to the browser version
+ // of the function rather than create a new function in V8.
+ if (typeof(propertyValue) == 'function' &&
+ valueAsString.indexOf('o3djs.BROWSER_ONLY') != -1) {
+ valueAsString = 'args_[' + opt_args.length + ']';
+ opt_args.push(propertyValue);
+ }
+ result += propertyNamespaceName + ' = ' + valueAsString + ';\n';
+
+ if (typeof(propertyValue) === 'function' && propertyValue.prototype) {
+ result += o3djs.namespaceInitializer_(
+ propertyValue.prototype,
+ propertyNamespaceName + '.prototype');
+ }
+ }
+ }
+ return result;
+};
+
+o3djs.provide('o3djs.base');
+
+/**
+ * The base module for o3djs.
+ * @namespace
+ */
+o3djs.base = o3djs.base || {};
+
+/**
+ * The a Javascript copy of the o3d namespace object. (holds constants, enums,
+ * etc...)
+ * @type {o3d.o3d}
+ */
+o3djs.base.o3d = null;
+
+/**
+ * Snapshots the current state of all provided namespaces. This state will be
+ * used to initialize future V8 instances. It is automatically
+ * called by o3djs.util.makeClients.
+ */
+o3djs.base.snapshotProvidedNamespaces = function() {
+ // Snapshot the V8 initializer string from the current state of browser
+ // JavaScript the first time this is called.
+ o3djs.v8Initializer_ = 'function(args_) {\n';
+ o3djs.v8InitializerArgs_ = [];
+ for (var i = 0; i < o3djs.provided_.length; ++i) {
+ var object = o3djs.getObjectByName(o3djs.provided_[i]);
+ o3djs.v8Initializer_ += o3djs.namespaceInitializer_(
+ /** @type {!Object} */ (object),
+ o3djs.provided_[i],
+ o3djs.v8InitializerArgs_);
+ }
+
+ o3djs.v8Initializer_ += '}\n';
+};
+
+/**
+ * Initializes the o3djs.sample library in a v8 instance. This should be called
+ * for every V8 instance that uses the sample library. It is automatically
+ * called by o3djs.util.makeClients.
+ * @param {!o3d.plugin} clientObject O3D.Plugin Object.
+ */
+o3djs.base.initV8 = function(clientObject) {
+ var v8Init = function(initializer, args) {
+ // Set up the o3djs namespace.
+ var o3djsBrowser = o3djs;
+ o3djs = {};
+ o3djs.browser = o3djsBrowser;
+ o3djs.global = (function() { return this; })();
+
+ o3djs.require = function(rule) {}
+ o3djs.provide = function(rule) {}
+
+ // Evaluate the initializer string with the arguments containing bindings
+ // to browser side objects.
+ eval('(' + initializer + ')')(args);
+
+ // Make sure this points to the o3d namespace for this particular
+ // instance of the plugin.
+ o3djs.base.o3d = plugin.o3d;
+ };
+
+ clientObject.eval(v8Init.toString())(o3djs.v8Initializer_,
+ o3djs.v8InitializerArgs_);
+};
+
+/**
+ * Initializes the o3djs.sample library.
+ * Basically all it does is record the o3djs.namespace object which is used by
+ * other functions to look up o3d constants.
+ *
+ * @param {!Element} clientObject O3D.Plugin Object.
+ */
+o3djs.base.init = function(clientObject) {
+ function recursivelyCopyProperties(object) {
+ var copy = {};
+ var hasProperties = false;
+ for (var key in object) {
+ var property = object[key];
+ if (typeof property == 'object' || typeof property == 'function') {
+ property = recursivelyCopyProperties(property);
+ }
+ if (typeof property != 'undefined') {
+ copy[key] = property;
+ hasProperties = true;
+ }
+ }
+ return hasProperties ? copy : undefined;
+ }
+ try {
+ o3djs.base.o3d = recursivelyCopyProperties(clientObject.o3d);
+ } catch (e) {
+ // Firefox 2 raises an exception when trying to enumerate a NPObject
+ o3djs.base.o3d = clientObject.o3d;
+ }
+ // Because of a bug in chrome, it is not possible for the browser to enumerate
+ // the properties of an NPObject.
+ // Chrome bug: http://code.google.com/p/chromium/issues/detail?id=5743
+ o3djs.base.o3d = o3djs.base.o3d || clientObject.o3d;
+};
+
+/**
+ * Determine whether a value is an array. Do not use instanceof because that
+ * will not work for V8 arrays (the browser thinks they are Objects).
+ * @param {*} value A value.
+ * @return {boolean} Whether the value is an array.
+ */
+o3djs.base.isArray = function(value) {
+ var valueAsObject = /** @type {!Object} */ (value);
+ return typeof(value) === 'object' && value !== null &&
+ 'length' in valueAsObject && 'splice' in valueAsObject;
+};
+
+/**
+ * Check if the o3djs library has been initialized.
+ * @return {boolean} true if ready, false if not.
+ */
+o3djs.base.ready = function() {
+ return o3djs.base.o3d != null;
+};
+
+/**
+ * A stub for later optionally converting obfuscated names
+ * @private
+ * @param {string} name Name to un-obfuscate.
+ * @return {string} un-obfuscated name.
+ */
+o3djs.base.maybeDeobfuscateFunctionName_ = function(name) {
+ return name;
+};
+
+/**
+ * Parses an error stack from an exception
+ * @param {!Exception} excp The exception to get a stack trace from.
+ * @return {!Array.<string>} An array of strings of the stack trace.
+ */
+o3djs.base.parseErrorStack = function(excp) {
+ var stack = [];
+ var name;
+ var line;
+
+ if (!excp || !excp.stack) {
+ return stack;
+ }
+
+ var stacklist = excp.stack.split('\n');
+
+ for (var i = 0; i < stacklist.length - 1; i++) {
+ var framedata = stacklist[i];
+
+ name = framedata.match(/^([a-zA-Z0-9_$]*)/)[1];
+ if (name) {
+ name = o3djs.base.maybeDeobfuscateFunctionName_(name);
+ } else {
+ name = 'anonymous';
+ }
+
+ var result = framedata.match(/(.*:[0-9]+)$/);
+ line = result && result[1];
+
+ if (!line) {
+ line = '(unknown)';
+ }
+
+ stack[stack.length] = name + ' : ' + line
+ }
+
+ // remove top level anonymous functions to match IE
+ var omitRegexp = /^anonymous :/;
+ while (stack.length && omitRegexp.exec(stack[stack.length - 1])) {
+ stack.length = stack.length - 1;
+ }
+
+ return stack;
+};
+
+/**
+ * Gets a function name from a function object.
+ * @param {!function(...): *} aFunction The function object to try to get a
+ * name from.
+ * @return {string} function name or 'anonymous' if not found.
+ */
+o3djs.base.getFunctionName = function(aFunction) {
+ var regexpResult = aFunction.toString().match(/function(\s*)(\w*)/);
+ if (regexpResult && regexpResult.length >= 2 && regexpResult[2]) {
+ return o3djs.base.maybeDeobfuscateFunctionName_(regexpResult[2]);
+ }
+ return 'anonymous';
+};
+
+/**
+ * Pretty prints an exception's stack, if it has one.
+ * @param {Array.<string>} stack An array of errors.
+ * @return {string} The pretty stack.
+ */
+o3djs.base.formatErrorStack = function(stack) {
+ var result = '';
+ for (var i = 0; i < stack.length; i++) {
+ result += '> ' + stack[i] + '\n';
+ }
+ return result;
+};
+
+/**
+ * Gets a stack trace as a string.
+ * @param {number} stripCount The number of entries to strip from the top of the
+ * stack. Example: Pass in 1 to remove yourself from the stack trace.
+ * @return {string} The stack trace.
+ */
+o3djs.base.getStackTrace = function(stripCount) {
+ var result = '';
+
+ if (typeof(arguments.caller) != 'undefined') { // IE, not ECMA
+ for (var a = arguments.caller; a != null; a = a.caller) {
+ result += '> ' + o3djs.base.getFunctionName(a.callee) + '\n';
+ if (a.caller == a) {
+ result += '*';
+ break;
+ }
+ }
+ } else { // Mozilla, not ECMA
+ // fake an exception so we can get Mozilla's error stack
+ var testExcp;
+ try {
+ eval('var var;');
+ } catch (testExcp) {
+ var stack = o3djs.base.parseErrorStack(testExcp);
+ result += o3djs.base.formatErrorStack(stack.slice(3 + stripCount,
+ stack.length));
+ }
+ }
+
+ return result;
+};
+
+/**
+ * Sets the error handler on a client to a handler that displays an alert on the
+ * first error.
+ * @param {!o3d.Client} client The client object of the plugin.
+ */
+o3djs.base.setErrorHandler = function(client) {
+ client.setErrorCallback(
+ function(msg) {
+ // Clear the error callback. Otherwise if the callback is happening
+ // during rendering it's possible the user will not be able to
+ // get out of an infinite loop of alerts.
+ client.clearErrorCallback();
+ alert('ERROR: ' + msg + '\n' + o3djs.base.getStackTrace(1));
+ });
+};
+
+/**
+ * Returns true if the user's browser is Microsoft IE.
+ * @return {boolean} true if the user's browser is Microsoft IE.
+ */
+o3djs.base.IsMSIE = function() {
+ var ua = navigator.userAgent.toLowerCase();
+ var msie = /msie/.test(ua) && !/opera/.test(ua);
+ return msie;
+};
+/**
+ * Returns true if the user's browser is Chrome 1.0, that requires a workaround
+ * to create the plugin.
+ * @return {boolean} true if the user's browser is Chrome 1.0.
+ */
+o3djs.base.IsChrome10 = function() {
+ return navigator.userAgent.indexOf('Chrome/1.0') >= 0;
+};
diff --git a/o3d/samples/o3djs/camera.js b/o3d/samples/o3djs/camera.js
new file mode 100644
index 0000000..9acf95e
--- /dev/null
+++ b/o3d/samples/o3djs/camera.js
@@ -0,0 +1,364 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains various camera utility functions for
+ * o3d. It puts them in the "camera" module on the o3djs object.
+ *
+ * Note: This library is only a sample. It is not meant to be some official
+ * library. It is provided only as example code.
+ *
+ */
+
+o3djs.provide('o3djs.camera');
+
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+
+/**
+ * A Module for camera utilites.
+ * @namespace
+ */
+o3djs.camera = o3djs.camera || {};
+
+/**
+ * Class to hold Camera information.
+ * @constructor
+ * @param {!o3djs.math.Matrix4} view The 4-by-4 view matrix.
+ * @param {number} zNear near z plane.
+ * @param {number} zFar far z plane.
+ * @param {!o3djs.math.Vector3} opt_eye The eye position.
+ * @param {!o3djs.math.Vector3} opt_target The target position.
+ * @param {!o3djs.math.Vector3} opt_up The up vector.
+ */
+o3djs.camera.CameraInfo = function(view,
+ zNear,
+ zFar,
+ opt_eye,
+ opt_target,
+ opt_up) {
+ /**
+ * View Matrix.
+ * @type {!o3djs.math.Matrix4}
+ */
+ this.view = view;
+
+ /**
+ * Projection Matrix.
+ * @type {!o3djs.math.Matrix4}
+ */
+ this.projection = o3djs.math.matrix4.identity();
+
+ /**
+ * Projection is orthographic.
+ * @type {boolean}
+ */
+ this.orthographic = false;
+
+ /**
+ * Near z plane.
+ * @type {number}
+ */
+ this.zNear = zNear;
+
+ /**
+ * Far z plane.
+ * @type {number}
+ */
+ this.zFar = zFar;
+
+ /**
+ * Field of view in radians.
+ * @type {number}
+ */
+ this.fieldOfViewRadians = o3djs.math.degToRad(30);
+
+ /**
+ * Eye position.
+ * @type {(!o3djs.math.Vector3|undefined)}
+ */
+ this.eye = opt_eye;
+
+ /**
+ * Target position.
+ * @type {(!o3djs.math.Vector3|undefined)}
+ */
+ this.target = opt_target;
+
+ /**
+ * Up Vector.
+ * @type {(!o3djs.math.Vector3|undefined)}
+ */
+ this.up = opt_up;
+
+ /**
+ * horizontal magnification for an orthographic view.
+ * @type {(number|undefined)}
+ */
+ this.magX = undefined;
+
+ /**
+ * vertical magnification for an orthographic view.
+ * @type {(number|undefined)}
+ */
+ this.magY = undefined;
+};
+
+/**
+ * Sets the CameraInfo to an orthographic camera.
+ * @param {number} magX horizontal magnification.
+ * @param {number} magY vertical magnification.
+ */
+o3djs.camera.CameraInfo.prototype.setAsOrthographic = function(
+ magX, magY) {
+ this.orthographic = true
+ this.magX = magX;
+ this.magY = magY;
+};
+
+/**
+ * Sets the CameraInfo to an orthographic camera.
+ * @param {number} fieldOfView Field of view in radians.
+ */
+o3djs.camera.CameraInfo.prototype.setAsPerspective = function(
+ fieldOfView) {
+ this.orthographic = false;
+ this.fieldOfViewRadians = fieldOfView;
+};
+
+/**
+ * Computes a projection matrix for this CameraInfo using the areaWidth
+ * and areaHeight passed in.
+ *
+ * @param {number} areaWidth width of client area.
+ * @param {number} areaHeight heigh of client area.
+ * @return {!o3djs.math.Matrix4} The computed projection matrix.
+ */
+o3djs.camera.CameraInfo.prototype.computeProjection = function(
+ areaWidth,
+ areaHeight) {
+ if (this.orthographic) {
+ // TODO: figure out if there is a way to make this take the areaWidth
+ // and areaHeight into account. As it is, magX and magY from the
+ // collada file are relative to the aspect ratio of Maya's render
+ // settings which are not available here.
+ // var magX = areaWidth * 0.5 / this.magX;
+ // var magY = areaHeight * 0.5 / this.magY;
+ var magX = /** @type {number} */ (this.magX);
+ var magY = /** @type {number} */ (this.magY);
+ this.projection = o3djs.math.matrix4.orthographic(
+ -magX, magX, -magY, magY, this.zNear, this.zFar);
+ } else {
+ this.projection = o3djs.math.matrix4.perspective(
+ this.fieldOfViewRadians, // field of view.
+ areaWidth / areaHeight, // Aspect ratio.
+ this.zNear, // Near plane.
+ this.zFar); // Far plane.
+ }
+ return this.projection;
+};
+
+/**
+ * Searches for all nodes with a "o3d.tags" ParamString
+ * that contains the word "camera" assuming comma separated
+ * words.
+ * @param {!o3d.Transform} treeRoot Root of tree to search for cameras.
+ * @return {!Array.<!o3d.Transform>} Array of camera transforms.
+ */
+o3djs.camera.findCameras = function(treeRoot) {
+ return o3djs.util.getTransformsInTreeByTags(treeRoot, 'camera');
+};
+
+/**
+ * Creates a object with view and projection matrices using paramters found on
+ * the camera 'o3d.projection_near_z', 'o3d.projection_far_z', and
+ * 'o3d.perspective_fov_y' as well as the areaWidth and areaHeight passed
+ * in.
+ * @param {!o3d.Transform} camera Transform with camera information on it.
+ * @param {number} areaWidth width of client area.
+ * @param {number} areaHeight height of client area.
+ * @return {!o3djs.camera.CameraInfo} A CameraInfo object.
+ */
+o3djs.camera.getViewAndProjectionFromCamera = function(camera,
+ areaWidth,
+ areaHeight) {
+ var fieldOfView = 30;
+ var zNear = 1;
+ var zFar = 5000;
+ var eye = undefined;
+ var target = undefined;
+ var up = undefined;
+ var view;
+ var math = o3djs.math;
+ var cameraInfo;
+
+ // Check if any LookAt elements were found for the camera and use their
+ // values to compute a view matrix.
+ var eyeParam = camera.getParam('collada.eyePosition');
+ var targetParam = camera.getParam('collada.targetPosition');
+ var upParam = camera.getParam('collada.upVector');
+ if (eyeParam != null && targetParam != null && upParam != null) {
+ eye = eyeParam.value;
+ target = targetParam.value;
+ up = upParam.value;
+ view = math.matrix4.lookAt(eye, target, up);
+ } else {
+ // Set it to the orientation of the camera.
+ view = math.inverse(camera.getUpdatedWorldMatrix());
+ }
+
+ var projectionType = camera.getParam('collada.projectionType');
+ if (projectionType) {
+ zNear = camera.getParam('collada.projectionNearZ').value;
+ zFar = camera.getParam('collada.projectionFarZ').value;
+
+ if (projectionType.value == 'orthographic') {
+ var magX = camera.getParam('collada.projectionMagX').value;
+ var magY = camera.getParam('collada.projectionMagY').value;
+
+ cameraInfo = new o3djs.camera.CameraInfo(view, zNear, zFar);
+ cameraInfo.setAsOrthographic(magX, magY);
+ } else if (projectionType.value == 'perspective') {
+ fieldOfView = camera.getParam('collada.perspectiveFovY').value;
+ }
+ }
+
+ if (!cameraInfo) {
+ cameraInfo = new o3djs.camera.CameraInfo(view, zNear, zFar,
+ eye, target, up);
+ cameraInfo.setAsPerspective(math.degToRad(fieldOfView));
+ }
+
+ cameraInfo.computeProjection(areaWidth, areaHeight);
+ return cameraInfo;
+};
+
+/**
+ * Get CameraInfo that represents a view of the bounding box that encompasses
+ * a tree of transforms.
+ * @param {!o3d.Transform} treeRoot Root of sub tree to get extents from.
+ * @param {number} clientWidth width of client area.
+ * @param {number} clientHeight height of client area.
+ * @return {!o3djs.camera.CameraInfo} A CameraInfo object.
+ */
+o3djs.camera.getCameraFitToScene = function(treeRoot,
+ clientWidth,
+ clientHeight) {
+ var math = o3djs.math;
+ var box = o3djs.util.getBoundingBoxOfTree(treeRoot);
+ var target = math.lerpVector(box.minExtent, box.maxExtent, 0.5);
+ var boxDimensions = math.subVector(box.maxExtent, box.minExtent);
+ var diag = o3djs.math.distance(box.minExtent, box.maxExtent);
+ var eye = math.addVector(target, [boxDimensions[0] * 0.3,
+ boxDimensions[1] * 0.7,
+ diag * 1.5]);
+ var nearPlane = diag / 1000;
+ var farPlane = diag * 10;
+
+ var up = [0, 1, 0];
+ var cameraInfo = new o3djs.camera.CameraInfo(
+ math.matrix4.lookAt(eye, target, up),
+ nearPlane,
+ farPlane);
+
+ cameraInfo.setAsPerspective(math.degToRad(45));
+ cameraInfo.computeProjection(clientWidth, clientHeight);
+ return cameraInfo;
+};
+
+/**
+ * Calls findCameras and takes the first camera. Then calls
+ * o3djs.camera.getViewAndProjectionFromCamera. If no camera is found it
+ * sets up some defaults.
+ * @param {!o3d.Transform} treeRoot Root of tree to search for cameras.
+ * @param {number} areaWidth Width of client area.
+ * @param {number} areaHeight Height of client area.
+ * @return {!o3djs.camera.CameraInfo} A CameraInfo object.
+ */
+o3djs.camera.getViewAndProjectionFromCameras = function(treeRoot,
+ areaWidth,
+ areaHeight) {
+ var cameras = o3djs.camera.findCameras(treeRoot);
+
+ if (cameras.length > 0) {
+ return o3djs.camera.getViewAndProjectionFromCamera(cameras[0],
+ areaWidth,
+ areaHeight);
+ } else {
+ // There was no camera in the file so make up a hopefully resonable default.
+ return o3djs.camera.getCameraFitToScene(treeRoot,
+ areaWidth,
+ areaHeight);
+ }
+};
+
+/**
+ * Calls findCameras and creates an array of CameraInfos for each camera found.
+ * @param {!o3d.Transform} treeRoot Root of tree to search for cameras.
+ * @param {number} areaWidth Width of client area.
+ * @param {number} areaHeight Height of client area.
+ * @return {!Array.<!o3djs.camera.CameraInfo>} A CameraInfo object.
+ */
+o3djs.camera.getCameraInfos = function(treeRoot, areaWidth, areaHeight) {
+ var cameras = o3djs.camera.findCameras(treeRoot);
+ var cameraInfos = [];
+
+ for (var cc = 0; cc < cameras.length; ++cc) {
+ cameraInfos.push(o3djs.camera.getViewAndProjectionFromCamera(
+ cameras[cc], areaWidth, areaHeight));
+ }
+ return cameraInfos;
+};
+
+/**
+ * Sets the view and projection of a DrawContext to view the bounding box
+ * that encompasses the tree of transforms passed.
+ *
+ * This function is here to help debug a program by providing an easy way to
+ * attempt to get your content in front of the camera.
+ *
+ * @param {!o3d.Transform} treeRoot Root of sub tree to get extents from.
+ * @param {number} clientWidth width of client area.
+ * @param {number} clientHeight height of client area.
+ * @param {!o3d.DrawContext} drawContext DrawContext to set view and
+ * projection on.
+ */
+o3djs.camera.fitContextToScene = function(treeRoot,
+ clientWidth,
+ clientHeight,
+ drawContext) {
+ var cameraInfo = o3djs.camera.getCameraFitToScene(treeRoot,
+ clientWidth,
+ clientHeight);
+ drawContext.view = cameraInfo.view;
+ drawContext.projection = cameraInfo.projection;
+};
diff --git a/o3d/samples/o3djs/canvas.js b/o3d/samples/o3djs/canvas.js
new file mode 100644
index 0000000..693de1d
--- /dev/null
+++ b/o3d/samples/o3djs/canvas.js
@@ -0,0 +1,387 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains a basic utility library that simplifies the
+ * creation of simple 2D Canvas surfaces for the purposes of drawing 2D elements
+ * in O3D.
+ *
+ * Example
+ *
+ * <pre>
+ * &lt;html&gt;&lt;body&gt;
+ * &lt;script type="text/javascript" src="o3djs/all.js"&gt;
+ * &lt;/script&gt;
+ * &lt;script&gt;
+ * window.onload = init;
+ *
+ * function init() {
+ * o3djs.base.makeClients(initStep2);
+ * }
+ *
+ * function initStep2(clientElements) {
+ * var clientElement = clientElements[0];
+ * var client = clientElement.client;
+ * var pack = client.createPack();
+ * var viewInfo = o3djs.rendergraph.createBasicView(
+ * pack,
+ * client.root,
+ * client.renderGraphRoot);
+ *
+ * // Create an instance of the canvas utility library.
+ * var canvasLib = o3djs.canvas.create(
+ * pack, client.root, g_viewInfo);
+ *
+ * // Create a 700x500 rectangle at (x,y,z) = (4, 10, 0)
+ * var canvasQuad = canvasLib.createXYQuad(4, 10, 0, 700, 500, false);
+ *
+ * // Draw into the canvas.
+ * canvasQuad.canvas.clear([1, 0, 0, 1]);
+ * canvasQuad.canvas.drawText('Hello', 0, 10, canvasPaint);
+ * ...
+ * ...
+ *
+ * // Update the o3d texture associated with the canvas.
+ * canvasQuad.updateTexture();
+ * }
+ * &lt;/script&gt;
+ * &lt;div id="o3d" style="width: 600px; height: 600px"&gt;&lt;/div&gt;
+ * &lt;/body&gt;&lt;/html&gt;
+ * </pre>
+ *
+ */
+
+o3djs.provide('o3djs.canvas');
+
+o3djs.require('o3djs.effect');
+o3djs.require('o3djs.primitives');
+
+/**
+ * A Module for using a 2d canvas.
+ * @namespace
+ */
+o3djs.canvas = o3djs.canvas || {};
+
+/**
+ * Creates an o3djs.canvas library object through which CanvasQuad objects
+ * can be created.
+ * @param {!o3d.Pack} pack to manage objects created by this library.
+ * @param {!o3d.Transform} root Default root for visual objects.
+ * @param {!o3djs.rendergraph.ViewInfo} viewInfo A ViewInfo object as
+ * created by o3djs.createView which contains draw lists that the created
+ * quads will be placed into.
+ * @return {!o3djs.canvas.CanvasInfo} A CanvasInfo object containing
+ * references to all the common O3D elements used by this instance
+ * of the library.
+ */
+o3djs.canvas.create = function(pack, root, viewInfo) {
+ return new o3djs.canvas.CanvasInfo(pack, root, viewInfo);
+};
+
+/**
+ * The shader code used by the canvas quads. It only does two things:
+ * 1. Transforms the shape to screen space via the worldViewProjection matrix.
+ * 2. Performs a texture lookup to display the contents of the texture
+ * bound to texSampler0.
+ * @type {string}
+ */
+o3djs.canvas.FX_STRING =
+ 'float4x4 worldViewProjection : WORLDVIEWPROJECTION;\n' +
+ 'sampler texSampler0;\n' +
+ 'struct VertexShaderInput {\n' +
+ ' float4 position : POSITION;\n' +
+ ' float2 texcoord : TEXCOORD0;\n' +
+ '};\n'+
+ 'struct PixelShaderInput {\n' +
+ ' float4 position : POSITION;\n' +
+ ' float2 texcoord : TEXCOORD0;\n' +
+ '};\n' +
+ 'PixelShaderInput vertexShaderFunction(VertexShaderInput input) {\n' +
+ ' PixelShaderInput output;\n' +
+ ' output.position = mul(input.position, worldViewProjection);\n' +
+ ' output.texcoord = input.texcoord;\n' +
+ ' return output;\n' +
+ '}\n' +
+ 'float4 pixelShaderFunction(PixelShaderInput input): COLOR {\n' +
+ ' return tex2D(texSampler0, input.texcoord);\n' +
+ '}\n' +
+ '// #o3d VertexShaderEntryPoint vertexShaderFunction\n' +
+ '// #o3d PixelShaderEntryPoint pixelShaderFunction\n' +
+ '// #o3d MatrixLoadOrder RowMajor\n';
+
+/**
+ * The CanvasInfo object creates and keeps references to the O3D objects
+ * that are shared between all CanvasQuad objects created through it.
+ * @constructor
+ * @param {!o3d.Pack} pack Pack to manage CanvasInfo objects.
+ * @param {!o3d.Transform} root Default root for visual objects.
+ * @param {!o3djs.rendergraph.ViewInfo} viewInfo A ViewInfo object as
+ * created by o3djs.createView which contains draw lists that the
+ * created quads will be placed into.
+ */
+o3djs.canvas.CanvasInfo = function(pack, root, viewInfo) {
+ /**
+ * The pack being used to manage objects created by this CanvasInfo.
+ * @type {!o3d.Pack}
+ */
+ this.pack = pack;
+
+ /**
+ * The ViewInfo this CanvasInfo uses for rendering.
+ * @type {!o3djs.rendergraph.ViewInfo}
+ */
+ this.viewInfo = viewInfo;
+
+ /**
+ * The default root for objects created by this CanvasInfo.
+ * @type {!o3d.Transform}
+ */
+ this.root = root;
+
+ // Create the Effect object shared by all CanvasQuad instances.
+ this.effect = this.pack.createObject('Effect');
+ this.effect.loadFromFXString(o3djs.canvas.FX_STRING);
+
+ // Create two materials: One used for canvases with transparent content
+ // and one for opaque canvases.
+ this.transparentMaterial = this.pack.createObject('Material');
+ this.opaqueMaterial = this.pack.createObject('Material');
+
+ this.transparentMaterial.effect = this.effect;
+ this.opaqueMaterial.effect = this.effect;
+
+ this.transparentMaterial.drawList = viewInfo.zOrderedDrawList;
+ this.opaqueMaterial.drawList = viewInfo.performanceDrawList;
+
+ // Create a state object to handle the transparency blending mode
+ // for transparent canvas quads.
+ // The canvas bitmap already multiplies the color values by alpha. In order
+ // to avoid a black halo around text drawn on a transparent background we
+ // need to set the blending mode as follows.
+ this.transparentState = this.pack.createObject('State');
+ this.transparentState.getStateParam('AlphaBlendEnable').value = true;
+ this.transparentState.getStateParam('SourceBlendFunction').value =
+ o3djs.base.o3d.State.BLENDFUNC_ONE;
+ this.transparentState.getStateParam('DestinationBlendFunction').value =
+ o3djs.base.o3d.State.BLENDFUNC_INVERSE_SOURCE_ALPHA;
+
+ this.transparentMaterial.state = this.transparentState;
+
+ // Create 2d plane shapes. createPlane makes an XZ plane by default
+ // so we pass in matrix to rotate it to an XY plane. We could do
+ // all our manipulations in XZ but most people seem to like XY for 2D.
+
+ /**
+ * A shape for transparent quads.
+ * @type {!o3d.Shape}
+ */
+ this.transparentQuadShape = o3djs.primitives.createPlane(
+ this.pack,
+ this.transparentMaterial,
+ 1,
+ 1,
+ 1,
+ 1,
+ [[1, 0, 0, 0],
+ [0, 0, 1, 0],
+ [0, -1, 0, 0],
+ [0, 0, 0, 1]]);
+
+ /**
+ * A shape for opaque quads.
+ * @type {!o3d.Shape}
+ */
+ this.opaqueQuadShape = o3djs.primitives.createPlane(
+ this.pack,
+ this.opaqueMaterial,
+ 1,
+ 1,
+ 1,
+ 1,
+ [[1, 0, 0, 0],
+ [0, 0, 1, 0],
+ [0, -1, 0, 0],
+ [0, 0, 0, 1]]);
+};
+/**
+ * The CanvasQuad object encapsulates a Transform, a rectangle Shape,
+ * an effect that applies a texture to render the quad, and a matching Canvas
+ * object that can render into the texture. The dimensions of the texture and
+ * the canvas object match those of the quad in order to get pixel-accurate
+ * results with the appropriate orthographic projection.
+ * The resulting rectangle Shape is positioned at the origin. It can be moved
+ * around by setting the localMatrix on the Transform object referenced to by
+ * the canvasQuad.transform property.
+ * The Canvas associated with the returned CanvasQuad object can be retrieved
+ * from the object's 'canvas' property. After issuing any draw commands on the
+ * Canvas, you need to call the updateTexture() method on the CanvasQuad to
+ * update the contents of the quad surface.
+ * @constructor
+ * @param {!o3djs.canvas.CanvasInfo} canvasInfo The CanvasInfo object
+ * instance creating this CanvasQuad.
+ * @param {number} width The width of the quad.
+ * @param {number} height The height of the quad.
+ * @param {boolean} transparent Set to true if the canvas will
+ * be transparent so that the appropriate blending modes are set.
+ * @param {!o3d.Transform} opt_parent parent transform to parent
+ * the newly created quad under. If no parent transform is provided then
+ * the quad gets parented under the CanvasInfo's root.
+ */
+o3djs.canvas.CanvasQuad = function(canvasInfo,
+ width,
+ height,
+ transparent,
+ opt_parent) {
+ this.canvasInfo = canvasInfo;
+ var parentTransform = opt_parent || canvasInfo.root;
+
+ // create a transform for positioning
+
+ /**
+ * A transform for this quad.
+ * @type {!o3d.Transform}
+ */
+ this.transform = canvasInfo.pack.createObject('Transform');
+ this.transform.parent = parentTransform;
+
+ // create a transform for scaling to the size of the image just so
+ // we don't have to manage that manually in the transform above.
+
+ /**
+ * A scale transform for this quad.
+ * You can change the scale the quad without effecting its positon using
+ * this transform.
+ * @type {!o3d.Transform}
+ */
+ this.scaleTransform = canvasInfo.pack.createObject('Transform');
+ this.scaleTransform.parent = this.transform;
+
+ // create the texture the canvas will draw on.
+ this.texture = canvasInfo.pack.createTexture2D(
+ width,
+ height,
+ o3djs.base.o3d.Texture.ARGB8,
+ 1, // mipmap levels
+ false);
+
+ // Create a Canvas object to go with the quad.
+
+ /**
+ * The Canvas object used to draw on this quad.
+ * @type {!o3d.Canvas}
+ */
+ this.canvas = canvasInfo.pack.createObject('Canvas');
+ this.canvas.setSize(width, height);
+
+ // setup the sampler for the texture
+ this.sampler = canvasInfo.pack.createObject('Sampler');
+ this.sampler.addressModeU = o3djs.base.o3d.Sampler.CLAMP;
+ this.sampler.addressModeV = o3djs.base.o3d.Sampler.CLAMP;
+ this.paramSampler = this.scaleTransform.createParam('texSampler0',
+ 'ParamSampler');
+ this.paramSampler.value = this.sampler;
+
+ this.sampler.texture = this.texture;
+ if (transparent) {
+ this.scaleTransform.addShape(canvasInfo.transparentQuadShape);
+ } else {
+ this.scaleTransform.addShape(canvasInfo.opaqueQuadShape);
+ }
+ this.scaleTransform.translate(width / 2, height / 2, 0);
+ this.scaleTransform.scale(width, -height, 1);
+};
+
+/**
+ * Copies the current contents of the Canvas object to the texture associated
+ * with the quad. This method should be called after any new draw calls have
+ * been issued to the CanvasQuad's Canvas object.
+ */
+o3djs.canvas.CanvasQuad.prototype.updateTexture = function() {
+ this.canvas.copyToTexture(this.texture);
+};
+
+/**
+ * Creates a CanvasQuad object on the XY plane at the specified position.
+ * @param {number} topX The x coordinate of the top left corner of the quad.
+ * @param {number} topY The y coordinate of the top left corner of the quad.
+ * @param {number} z The z coordinate of the quad. z values are negative
+ * numbers, the smaller the number the further back the quad will be.
+ * @param {number} width The width of the quad.
+ * @param {number} height The height of the quad.
+ * @param {boolean} transparent Set to true if the canvas bitmap uses
+ * transparency so that the appropriate blending modes are set.
+ * @param {!o3d.Transform} opt_parent parent transform to parent the newly
+ * created quad under. If no parent transform is provided then the quad
+ * gets parented under the CanvasInfo's root.
+ * @return {!o3djs.canvas.CanvasQuad} The newly created CanvasQuad object.
+ */
+o3djs.canvas.CanvasInfo.prototype.createXYQuad = function(topX,
+ topY,
+ z,
+ width,
+ height,
+ transparent,
+ opt_parent) {
+ var canvasQuad = new o3djs.canvas.CanvasQuad(this,
+ width,
+ height,
+ transparent,
+ opt_parent);
+
+ canvasQuad.transform.translate(topX, topY, z);
+ return canvasQuad;
+};
+
+/**
+ * Creates a CanvasQuad object of the given size. The resulting rectangle Shape
+ * is centered at the origin. It can be moved around by setting the
+ * localMatrix on the Transform object referenced to by the canvasQuad.transform
+ * property.
+ * @param {number} width The width of the quad.
+ * @param {number} height The height of the quad.
+ * @param {boolean} transparent Set to true if the canvas bitmap uses
+ * transparency so that the appropriate blending modes are set.
+ * @param {!o3d.Transform} opt_parent parent transform to parent the newly
+ * created quad under. If no parent transform is provided then the quad
+ * gets parented under the CanvasInfo's root.
+ * @return {!o3djs.canvas.CanvasQuad} The newly created CanvasQuad object.
+ */
+o3djs.canvas.CanvasInfo.prototype.createQuad = function(width,
+ height,
+ transparent,
+ opt_parent) {
+ return new o3djs.canvas.CanvasQuad(this,
+ width,
+ height,
+ transparent,
+ opt_parent);
+};
diff --git a/o3d/samples/o3djs/debug.js b/o3d/samples/o3djs/debug.js
new file mode 100644
index 0000000..cc34b7a
--- /dev/null
+++ b/o3d/samples/o3djs/debug.js
@@ -0,0 +1,1257 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains various functions to help debug for o3d
+ * applications.
+ *
+ * Note: This library is only a sample. It is not meant to be some official
+ * library. It is provided only as example code.
+ *
+ */
+
+o3djs.provide('o3djs.debug');
+
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.primitives');
+
+var O3D_DEBUG_PREFIX = 'o3dDebug_';
+var O3D_DEBUG_PREFIX_LENGTH = O3D_DEBUG_PREFIX.length;
+var O3D_DEBUG_COLOR_PARAM_NAME = O3D_DEBUG_PREFIX + 'Color';
+var O3D_DEBUG_VECTOR_SCALE_PARAM_NAME =
+ O3D_DEBUG_PREFIX + 'VectorScale';
+var O3D_DEBUG_AXIS_SHAPE_NAME = O3D_DEBUG_PREFIX + 'AxisShape';
+var O3D_DEBUG_LINE_SHAPE_NAME = O3D_DEBUG_PREFIX + 'LineShape';
+var O3D_DEBUG_SPHERE_SHAPE_NAME = O3D_DEBUG_PREFIX + 'SphereShape';
+var O3D_DEBUG_CUBE_SHAPE_NAME = O3D_DEBUG_PREFIX + 'CubeShape';
+
+var O3D_DEBUG_AXIS_INFO_ = [
+ {offset: [1, 0, 0], color: [1, 0, 0, 1]},
+ {offset: [0, 1, 0], color: [0, 1, 0, 1]},
+ {offset: [0, 0, 1], color: [0, 0, 1, 1]}];
+
+/**
+ * Checks whether or not a transform is a debug transform.
+ * @param {!o3d.Transform} transform Transform to check.
+ * @return {boolean} true if this transform is a debug transform.
+ */
+o3djs.debug.isDebugTransform = function(transform) {
+ var name = transform.name;
+ var isDT =
+ name.length >= O3D_DEBUG_PREFIX_LENGTH &&
+ name.substr(0, O3D_DEBUG_PREFIX_LENGTH) == O3D_DEBUG_PREFIX;
+ return isDT;
+};
+
+/**
+ * Gets the debug transform.
+ * @private
+ * @param {!o3d.Transform} transform Transform to get debug Transform
+ * from.
+ * @param {string} name Name of debug transform to get.
+ * @return {o3d.Transform} Debug Transform or null if not found.
+ */
+o3djs.debug.getDebugTransform_ = function(transform, name) {
+ if (transform.name == name) {
+ return transform;
+ } else {
+ var children = transform.children;
+ for (var cc = 0; cc < children.length; ++cc) {
+ if (children[cc].name == name) {
+ return children[cc];
+ }
+ }
+ }
+ return null;
+};
+
+/**
+ * Creates shaders that output the constant color from a parameter.
+ * @private
+ * @param {string} colorParamName Name of color parameter to use.
+ * @return {string} Shader string.
+ */
+o3djs.debug.createColorShaders_ = function(colorParamName) {
+ var shaders =
+ 'uniform float4 ' + colorParamName + ';\n' +
+ 'uniform float4x4 worldViewProjection: WORLDVIEWPROJECTION;\n' +
+ 'struct VertexShaderInput {\n' +
+ ' float4 position : POSITION;\n' +
+ '};\n' +
+ 'struct PixelShaderInput {\n' +
+ ' float4 position : POSITION;\n' +
+ '};\n' +
+ 'PixelShaderInput vertexShaderFunction(VertexShaderInput input) {\n' +
+ ' PixelShaderInput output;\n' +
+ ' output.position = mul(input.position, worldViewProjection);\n' +
+ ' return output;\n' +
+ '}\n' +
+ 'float4 pixelShaderFunction(PixelShaderInput input) : COLOR {\n' +
+ ' return ' + colorParamName + ';\n' +
+ '}\n' +
+ '// #o3d VertexShaderEntryPoint vertexShaderFunction\n' +
+ '// #o3d PixelShaderEntryPoint pixelShaderFunction\n' +
+ '// #o3d MatrixLoadOrder RowMajor\n';
+ return shaders;
+};
+
+/**
+ * Creates shaders that output the constant color from a parameter and scale
+ * the vertices in object space.
+ * @private
+ * @param {string} colorParamName Name of color parameter to use.
+ * @param {string} scaleParamName Name of scale parameter to use.
+ * @return {string} Shader string.
+ */
+o3djs.debug.createScaleShaders_ = function(colorParamName, scaleParamName) {
+ var shaders =
+ 'uniform float4 ' + colorParamName + ';\n' +
+ 'uniform float3 ' + scaleParamName + ';\n' +
+ 'uniform float4x4 worldViewProjection: WORLDVIEWPROJECTION;\n' +
+ 'struct VertexShaderInput {\n' +
+ ' float4 position : POSITION;\n' +
+ '};\n' +
+ 'struct PixelShaderInput {\n' +
+ ' float4 position : POSITION;\n' +
+ '};\n' +
+ 'PixelShaderInput vertexShaderFunction(VertexShaderInput input) {\n' +
+ ' PixelShaderInput output;\n' +
+ ' float4 position = float4(\n' +
+ ' input.position.x * ' + scaleParamName + '.x,\n' +
+ ' input.position.y * ' + scaleParamName + '.y,\n' +
+ ' input.position.z * ' + scaleParamName + '.z,\n' +
+ ' 1);\n' +
+ ' output.position = mul(position, worldViewProjection);\n' +
+ ' return output;\n' +
+ '}\n' +
+ 'float4 pixelShaderFunction(PixelShaderInput input) : COLOR {\n' +
+ ' return ' + colorParamName + ';\n' +
+ '}\n' +
+ '// #o3d VertexShaderEntryPoint vertexShaderFunction\n' +
+ '// #o3d PixelShaderEntryPoint pixelShaderFunction\n' +
+ '// #o3d MatrixLoadOrder RowMajor\n';
+ return shaders;
+};
+
+
+/**
+ * Defines a namespace for o3djs.debug.
+ * @namespace
+ */
+o3djs.debug = o3djs.debug || {};
+
+/**
+ * Creates a line list shape and primitive given a material, vertex array and
+ * index array.
+ *
+ * @param {!o3d.Pack} pack Pack to create objects in.
+ * @param {!o3d.Material} material to use.
+ * @param {!Array.<!o3djs.math.Vector3>} vertices array of numbers in the
+ * format positionX, positionY, positionZ.
+ * @param {!Array.<number>} indices array of vertex indices, 2 per line.
+ * @return {!o3d.Shape} The created shape.
+ */
+o3djs.debug.createLineShape = function(pack,
+ material,
+ vertices,
+ indices) {
+ // create a shape and primitive for the vertices.
+ var shape = pack.createObject('Shape');
+ var primitive = pack.createObject('Primitive');
+ var streamBank = pack.createObject('StreamBank');
+ primitive.owner = shape;
+ primitive.streamBank = streamBank;
+
+ // Apply the material
+ primitive.material = material;
+
+ primitive.numberPrimitives = indices.length / 2;
+ primitive.primitiveType = o3djs.base.o3d.Primitive.LINELIST;
+ primitive.numberVertices = vertices.length / 3;
+ primitive.createDrawElement(pack, null);
+
+ var vertexBuffer = pack.createObject('VertexBuffer');
+ var positionField = vertexBuffer.createField('FloatField', 3);
+ vertexBuffer.set(vertices);
+
+ // Attach our buffers to our primitive.
+ streamBank.setVertexStream(o3djs.base.o3d.Stream.POSITION,
+ 0,
+ positionField,
+ 0);
+
+ var indexBuffer = pack.createObject('IndexBuffer');
+ indexBuffer.set(indices);
+ primitive.indexBuffer = indexBuffer;
+ return shape;
+};
+
+/**
+ * VertexInfo. Used to store vertices and indices.
+ * @constructor
+ * @param {!Array.<!o3djs.math.Vector3>} opt_vertices array of numbers in the
+ * format positionX, positionY, positionZ.
+ * @param {!Array.<number>} opt_indices array of indices in pairs.
+ */
+o3djs.debug.VertexInfo = function(opt_vertices, opt_indices) {
+ this.vertices = opt_vertices || [];
+ this.indices = opt_indices || [];
+};
+
+/**
+ * Creates a new VertexInfo.
+ * @param {!Array.<!o3djs.math.Vector3>} opt_vertices array of numbers in the
+ * format positionX, positionY, positionZ.
+ * @param {!Array.<number>} opt_indices array of indices, 2 per line.
+ * @return {!o3djs.debug.VertexInfo} The new VertexInfo.
+ */
+o3djs.debug.createVertexInfo = function(opt_vertices, opt_indices) {
+ return new o3djs.debug.VertexInfo(opt_vertices, opt_indices);
+};
+
+/**
+ * Enum for vertex component offsets.
+ *
+ * To get/set a vertex component you can do something like this.
+ *
+ * vertInfo.vertices[vertInfo.vertexIndex(index) + vertInfo.Offset.X] *= 2;
+ *
+ * @enum {number}
+ */
+o3djs.debug.VertexInfo.prototype.Offset = {
+ X: 0,
+ Y: 1,
+ Z: 2
+};
+
+/**
+ * Adds a vertex.
+ * @param {number} positionX The x position of the vertex.
+ * @param {number} positionY The y position of the vertex.
+ * @param {number} positionZ The z position of the vertex.
+ */
+o3djs.debug.VertexInfo.prototype.addVertex = function(
+ positionX, positionY, positionZ) {
+ this.vertices.push(positionX, positionY, positionZ);
+};
+
+/**
+ * Adds a line.
+ * @param {number} index1 The index of the first vertex of the triangle.
+ * @param {number} index2 The index of the second vertex of the triangle.
+ */
+o3djs.debug.VertexInfo.prototype.addLine = function(
+ index1, index2) {
+ this.indices.push(index1, index2);
+};
+
+/**
+ * Creates a shape from a VertexInfo
+ * @param {!o3d.Pack} pack Pack to create objects in.
+ * @param {!o3d.Material} material to use.
+ * @return {!o3d.Shape} The created shape.
+ */
+o3djs.debug.VertexInfo.prototype.createShape = function(
+ pack,
+ material) {
+ return o3djs.debug.createLineShape(pack,
+ material,
+ this.vertices,
+ this.indices);
+};
+
+/**
+ * Reorients the vertex positions of this vertexInfo by the
+ * given matrix. In other words it multiplies each vertex by the
+ * given matrix.
+ * @param {!o3djs.math.Matrix4} matrix Matrix to multiply by.
+ */
+o3djs.debug.VertexInfo.prototype.reorient = function(matrix) {
+ var math = o3djs.math;
+ // Assume if it has a length it's not a Matrix4
+ if (matrix.length) {
+ matrix = math.matrix4.copy(matrix);
+ }
+ var numVerts = this.numVertices();
+ for (var v = 0; v < numVerts; ++v) {
+ var index = this.vertexIndex(v);
+ var position = [this.vertices[index + this.Offset.X],
+ this.vertices[index + this.Offset.Y],
+ this.vertices[index + this.Offset.Z],
+ 1];
+ position = math.mulVectorMatrix(position, matrix);
+ this.vertices[index + this.Offset.X] = position[0];
+ this.vertices[index + this.Offset.Y] = position[1];
+ this.vertices[index + this.Offset.Z] = position[2];
+ }
+};
+
+/**
+ * Creates the vertices and indices for a cube of lines. The
+ * cube will be created around the origin. (-size / 2, size / 2)
+ * The created cube has a position stream only and can therefore only be used
+ * with shaders that support those a position stream.
+ *
+ * @param {number} size Width, height and depth of the cube.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply all
+ * the vertices.
+ * @return {!o3djs.debug.VertexInfo} The created cube vertices.
+ */
+o3djs.debug.createLineCubeVertices = function(size, opt_matrix) {
+ var k = size / 2;
+
+ var vertices = [
+ -k, -k, -k,
+ +k, -k, -k,
+ -k, +k, -k,
+ +k, +k, -k,
+ -k, -k, +k,
+ +k, -k, +k,
+ -k, +k, +k,
+ +k, +k, +k
+ ];
+
+ var indices = [
+ 0, 1, 1, 3, 3, 2, 2, 0,
+ 4, 5, 5, 7, 7, 6, 6, 4,
+ 0, 4, 1, 5, 2, 6, 3, 7
+ ];
+
+ var vertexInfo = o3djs.debug.createVertexInfo(vertices, indices);
+ if (opt_matrix) {
+ vertexInfo.reorient(opt_matrix);
+ }
+ return vertexInfo;
+};
+
+/**
+ * Creates a cube of lines.
+ * @param {!o3d.Pack} pack Pack to create sphere elements in.
+ * @param {!o3d.Material} material to use.
+ * @param {number} size Width, height and depth of the cube.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply all
+ * the vertices.
+ * @return {!o3d.Shape} The created cube.
+ */
+o3djs.debug.createLineCube = function(pack,
+ material,
+ size,
+ opt_matrix) {
+ var vertexInfo = o3djs.debug.createLineCubeVertices(size, opt_matrix);
+ return vertexInfo.createShape(pack, material);
+};
+
+/**
+ * Creates sphere vertices.
+ * The created sphere has a position stream only and can therefore only be
+ * used with shaders that support those a position stream.
+ *
+ * @param {number} radius radius of the sphere.
+ * @param {number} subdivisionsAxis number of steps around the sphere.
+ * @param {number} subdivisionsHeight number of vertically on the sphere.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply all
+ * the vertices.
+ * @return {!o3djs.debug.VertexInfo} The created sphere vertices.
+ */
+o3djs.debug.createLineSphereVertices = function(radius,
+ subdivisionsAxis,
+ subdivisionsHeight,
+ opt_matrix) {
+ if (subdivisionsAxis <= 0 || subdivisionsHeight <= 0) {
+ throw RangeError('subdivisionAxis and subdivisionHeight must be > 0');
+ }
+
+ // We are going to generate our sphere by iterating through its
+ // spherical coordinates and generating 1 quad for each quad on a
+ // ring of the sphere.
+
+ var vertexInfo = o3djs.debug.createVertexInfo();
+
+ // Generate the individual vertices in our vertex buffer.
+ for (var y = 0; y <= subdivisionsHeight; y++) {
+ for (var x = 0; x <= subdivisionsAxis; x++) {
+ // Generate a vertex based on its spherical coordinates
+ var u = x / subdivisionsAxis
+ var v = y / subdivisionsHeight;
+ var theta = 2 * Math.PI * u;
+ var phi = Math.PI * v;
+ var sinTheta = Math.sin(theta);
+ var cosTheta = Math.cos(theta);
+ var sinPhi = Math.sin(phi);
+ var cosPhi = Math.cos(phi);
+ var ux = cosTheta * sinPhi;
+ var uy = cosPhi;
+ var uz = sinTheta * sinPhi;
+ vertexInfo.addVertex(radius * ux, radius * uy, radius * uz);
+ }
+ }
+ var numVertsAround = subdivisionsAxis + 1;
+
+ for (var x = 0; x < subdivisionsAxis; x++) {
+ for (var y = 0; y < subdivisionsHeight; y++) {
+ // Make 2 lines per quad.
+ vertexInfo.addLine(
+ (y + 0) * numVertsAround + x,
+ (y + 0) * numVertsAround + x + 1);
+ vertexInfo.addLine(
+ (y + 0) * numVertsAround + x,
+ (y + 1) * numVertsAround + x);
+ }
+ }
+
+ if (opt_matrix) {
+ vertexInfo.reorient(opt_matrix);
+ }
+ return vertexInfo;
+};
+
+/**
+ * Creates a sphere.
+ * The created sphere has position, normal, uv and vertex color streams only
+ * and can therefore only be used with shaders that support those 4
+ * streams.
+ *
+ * @param {!o3d.Pack} pack Pack to create sphere elements in.
+ * @param {!o3d.Material} material to use.
+ * @param {number} radius radius of the sphere.
+ * @param {number} subdivisionsAxis number of steps around the sphere.
+ * @param {number} subdivisionsHeight number of vertically on the sphere.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply all
+ * the vertices.
+ * @return {!o3d.Shape} The created sphere.
+ *
+ * @see o3d.Pack
+ * @see o3d.Shape
+ */
+o3djs.debug.createLineSphere = function(pack,
+ material,
+ radius,
+ subdivisionsAxis,
+ subdivisionsHeight,
+ opt_matrix) {
+ var vertexInfo = o3djs.debug.createLineSphereVertices(
+ radius,
+ subdivisionsAxis,
+ subdivisionsHeight,
+ opt_matrix);
+
+ return vertexInfo.createShape(pack, material);
+};
+
+/**
+ * An object to manage a single debug line.
+ * @constructor
+ * @param {!o3djs.debug.DebugLineGroup} debugLineGroup DebugLineGroup
+ * this line belongs too.
+ */
+o3djs.debug.DebugLine = function(debugLineGroup) {
+ this.debugLineGroup_ = debugLineGroup;
+ var pack = debugLineGroup.getPack();
+ this.transform_ = pack.createObject('Transform');
+ this.transform_.name = O3D_DEBUG_LINE_SHAPE_NAME;
+ this.transform_.addShape(debugLineGroup.getLineShape());
+ this.start_ = [0, 0, 0];
+ this.end_ = [0, 0, 0];
+ this.colorParam_ = this.transform_.createParam(
+ O3D_DEBUG_COLOR_PARAM_NAME, 'ParamFloat4');
+ this.colorParam_.value = debugLineGroup.getColor();
+};
+
+/**
+ * Destroys this line object cleaning up the resources used by it.
+ */
+o3djs.debug.DebugLine.prototype.destroy = function() {
+ this.debugLineGroup_.getPack().removeObject(this.transform_);
+};
+
+/**
+ * Returns a unique Id for the line.
+ * @return {number} the Id of the line.
+ */
+o3djs.debug.DebugLine.prototype.getId = function() {
+ return this.transform_.clientId;
+};
+
+/**
+ * Updates the line with the current start and end settings.
+ * @private
+ */
+o3djs.debug.DebugLine.prototype.update_ = function() {
+ var math = o3djs.math;
+ var vector = math.subVector(this.end_, this.start_);
+ var direction = math.normalize(vector);
+ var dot = math.dot(direction, [0, 1, 0]);
+ var perp1;
+ var perp2;
+ if (dot > 0.99) {
+ perp2 = math.cross([1, 0, 0], direction);
+ perp1 = math.cross(perp2, direction);
+ } else {
+ perp1 = math.cross([0, 1, 0], direction);
+ perp2 = math.cross(perp1, direction);
+ }
+ this.transform_.localMatrix =
+ [perp2.concat(0),
+ direction.concat(0),
+ perp1.concat(0),
+ this.start_.concat(1)];
+ this.transform_.scale(1, math.length(vector), 1);
+};
+
+/**
+ * Sets the end points of the DebugLine.
+ * @param {!o3djs.math.Vector3} start Start point for line.
+ * @param {!o3djs.math.Vector3} end End point for line.
+ */
+o3djs.debug.DebugLine.prototype.setEndPoints = function(start, end) {
+ this.start_ = start;
+ this.end_ = end;
+ this.update_();
+};
+
+/**
+ * Sets the start point of the DebugLine.
+ * @param {!o3djs.math.Vector3} start Start point for line.
+ */
+ o3djs.debug.DebugLine.prototype.setStart = function(start) {
+ this.start_ = start;
+ this.update_();
+};
+
+/**
+ * Sets the end point of the DebugLine.
+ * @param {!o3djs.math.Vector3} end End point for line.
+ */
+o3djs.debug.DebugLine.prototype.setEnd = function(end) {
+ this.end_ = end;
+ this.update_();
+};
+
+/**
+ * Sets the color of the DebugLine.
+ * @param {!o3djs.math.Vector4} color The color of the debug line.
+ */
+o3djs.debug.DebugLine.prototype.setColor = function(color) {
+ this.colorParam_.value = color;
+};
+
+/**
+ * Sets the visibility of the DebugLine.
+ * @param {boolean} visible True = visible.
+ */
+o3djs.debug.DebugLine.prototype.setVisible = function(visible) {
+ this.transform_.parent = visible ? this.debugLineGroup_.getRoot() : null;
+};
+
+/**
+ * Removes this line.
+ */
+o3djs.debug.DebugLine.prototype.remove = function() {
+ this.transform_.parent = null;
+ this.debugLineGroup_.remove(this);
+};
+
+/**
+ * An object to manage debug lines.
+ * @constructor
+ * @param {!o3djs.debug.DebugHelper} debugHelper The DebugHelper
+ * associated with this object.
+ * @param {!o3d.Transform} root Transform to put debug lines under.
+ */
+o3djs.debug.DebugLineGroup = function(debugHelper, root) {
+ this.currentColor_ = [1, 1, 1, 1];
+ this.lineTransforms_ = { };
+ this.freeLineTransforms_ = { };
+ this.debugHelper_ = debugHelper;
+ this.root_ = root;
+};
+
+/**
+ * Gets the root transform for this line group.
+ * @return {!o3d.Transform} The root transform for this line group.
+ */
+o3djs.debug.DebugLineGroup.prototype.getRoot = function() {
+ return this.root_;
+};
+
+/**
+ * Gets the pack for this line group.
+ * @return {!o3d.Pack} The pack for this line group.
+ */
+o3djs.debug.DebugLineGroup.prototype.getPack = function() {
+ return this.debugHelper_.getPack();
+};
+
+/**
+ * Gets the shape for lines.
+ * @return {!o3d.Shape} The shape for lines.
+ */
+o3djs.debug.DebugLineGroup.prototype.getLineShape = function() {
+ return this.debugHelper_.getLineShape();
+};
+
+/**
+ * Gets the current color for this line group.
+ * @return {!o3djs.math.Vector4} The current color.
+ */
+o3djs.debug.DebugLineGroup.prototype.getColor = function() {
+ return this.currentColor_;
+};
+
+/**
+ * Sets the current color for this line group. All lines added after
+ * setting this will be this color by default.
+ * @param {!o3djs.math.Vector4} color The color for this line group.
+ */
+o3djs.debug.DebugLineGroup.prototype.setColor = function(color) {
+ this.currentColor_ = color;
+};
+
+/**
+ * Gets a debug line. If none exist creates a new one.
+ * @private
+ * @return {!o3djs.debug.DebugLine} The DebugLine.
+ */
+o3djs.debug.DebugLineGroup.prototype.getLine_ = function() {
+ for (var id in this.freeLineTransforms_) {
+ var line = this.freeLineTransforms_[id];
+ delete this.freeLineTransforms_[id];
+ return line;
+ }
+ return new o3djs.debug.DebugLine(this);
+};
+
+/**
+ * Adds a debug line.
+ * @param {!o3djs.math.Vector3} opt_start Start position for line.
+ * @param {!o3djs.math.Vector3} opt_end End position for line.
+ * @param {!o3djs.math.Vector4} opt_color Color for line.
+ * @return {!o3djs.debug.debugLine} The DebugLine.
+ */
+o3djs.debug.DebugLineGroup.prototype.addLine = function(opt_start,
+ opt_end,
+ opt_color) {
+ var line = this.getLine_();
+ line.setEndPoints(opt_start || [0, 0, 0], opt_end || [0, 0, 0]);
+ line.setColor(opt_color || this.currentColor_);
+ line.setVisible(true);
+ this.lineTransforms_[line.getId()] = line;
+ return line;
+};
+
+/**
+ * Clears all the lines in this group.
+ */
+o3djs.debug.DebugLineGroup.prototype.clear = function() {
+ for (var id in this.lineTransforms_) {
+ var line = this.lineTransforms_[id];
+ line.setVisible(false);
+ this.freeLineTransforms_[id] = line;
+ }
+ this.lineTransforms_ = { };
+};
+
+/**
+ * Destroys a DeubgLineGroup, freeing all its lines and resources.
+ */
+o3djs.debug.DebugLineGroup.prototype.destroy = function() {
+ this.clear();
+ for (var id in this.freeLineTransforms_) {
+ this.freeLineTransforms_[id].destroy();
+ }
+ this.freeLineTransforms_ = { };
+};
+
+/**
+ * Removes a line.
+ * @param {!o3djs.debug.DebugLine} line Line to remove.
+ */
+o3djs.debug.DebugLineGroup.prototype.remove = function(line) {
+ var id = line.getId();
+ delete this.lineTransforms_[id];
+ this.freeLineTransforms_[id] = line;
+};
+
+/**
+ * A Debug object to help with debugging o3d apps.
+ *
+ * A debug helper object provides functions to help debug your o3d
+ * application and manages the resources needed to do that for you. For
+ * example it can add axes, spheres and boxes to your transforms as well as
+ * draw lines in 3d space given 2 points.
+ *
+ * @constructor
+ * @param {!o3d.Pack} pack Pack for this debug object to use to manage
+ * its resources.
+ * @param {!o3djs.rendergraph.viewInfo} viewInfo ViewInfo for debug
+ * visuals.
+ */
+o3djs.debug.DebugHelper = function(pack, viewInfo) {
+ this.pack_ = pack;
+ this.viewInfo_ = viewInfo;
+ this.axisPrimitives_ = [];
+ this.axisShape_ = pack.createObject('Shape');
+ this.axisShape_.name = O3D_DEBUG_AXIS_SHAPE_NAME;
+ this.lineShape_ = pack.createObject('Shape');
+ this.lineShape_.name = O3D_DEBUG_LINE_SHAPE_NAME;
+
+ // Setup shape, material, primitive for axes.
+ {
+ // create a simple material for the axis.
+ var effect = pack.createObject('Effect');
+ var shaders = o3djs.debug.createScaleShaders_(
+ O3D_DEBUG_COLOR_PARAM_NAME,
+ O3D_DEBUG_VECTOR_SCALE_PARAM_NAME);
+ effect.loadFromFXString(shaders);
+ var material = pack.createObject('Material');
+ material.effect = effect;
+ material.drawList = viewInfo.performanceDrawList;
+ effect.createUniformParameters(material);
+
+ // Set the default color to white.
+ material.getParam(O3D_DEBUG_COLOR_PARAM_NAME).value = [1, 1, 1, 1];
+
+ // Set the default scale.
+ material.getParam(O3D_DEBUG_VECTOR_SCALE_PARAM_NAME).value =
+ [1, 1, 1];
+
+ // Create the axis shape.
+ for (var ii = 0; ii < O3D_DEBUG_AXIS_INFO_.length; ++ii) {
+ var info = O3D_DEBUG_AXIS_INFO_[ii];
+ var cubeShape = o3djs.primitives.createCube(pack,
+ material,
+ 1,
+ [[1, 0, 0, 0],
+ [0, 1, 0, 0],
+ [0, 0, 1, 0],
+ [info.offset[0] * 0.5,
+ info.offset[1] * 0.5,
+ info.offset[2] * 0.5,
+ 1]]);
+ var cube = cubeShape.elements[0];
+ cube.owner = this.axisShape_;
+ pack.removeObject(cubeShape);
+ cube.createParam(O3D_DEBUG_COLOR_PARAM_NAME, 'ParamFloat4').value =
+ info.color;
+ cube.createParam(O3D_DEBUG_VECTOR_SCALE_PARAM_NAME, 'ParamFloat3');
+ this.axisPrimitives_[ii] = cube;
+ }
+
+ this.axisMaterial_ = material;
+ this.setAxisScale(10, 1);
+ }
+
+ // Setup shape, material, primitive for debug lines.
+ {
+ // create a simple material for the debug lines.
+ var effect = pack.createObject('Effect');
+ var shaders = o3djs.debug.createColorShaders_(O3D_DEBUG_COLOR_PARAM_NAME);
+ effect.loadFromFXString(shaders);
+ var material = pack.createObject('Material');
+ material.effect = effect;
+ material.drawList = viewInfo.performanceDrawList;
+ effect.createUniformParameters(material);
+
+ // Set the default color to white.
+ material.getParam(O3D_DEBUG_COLOR_PARAM_NAME).value = [1, 1, 1, 1];
+
+ // Create the debug line shape.
+ var vertices = [0, 0, 0, 0, 1, 0];
+ var streamBank = pack.createObject('StreamBank');
+ var primitive = pack.createObject('Primitive');
+ var shape = pack.createObject('Shape');
+ var vertexBuffer = pack.createObject('VertexBuffer');
+ var positionField = vertexBuffer.createField('FloatField', 3);
+ vertexBuffer.set(vertices);
+ primitive.owner = shape;
+ primitive.createDrawElement(pack, null);
+ primitive.streamBank = streamBank;
+ primitive.material = material;
+ primitive.numberVertices = 2;
+ primitive.numberPrimitives = 1;
+ primitive.primitiveType = o3djs.base.o3d.Primitive.LINELIST;
+ streamBank.setVertexStream(o3djs.base.o3d.Stream.POSITION,
+ 0,
+ positionField,
+ 0);
+ this.lineShape_ = shape;
+ this.lineShape_.name = O3D_DEBUG_LINE_SHAPE_NAME;
+ this.lineMaterial_ = material;
+ }
+
+ {
+ this.sphereShape_ = o3djs.debug.createLineSphere(pack,
+ this.axisMaterial_,
+ 0.5, 8, 8);
+ this.sphereShape_.name = O3D_DEBUG_SPHERE_SHAPE_NAME;
+ var primitive = this.sphereShape_.elements[0];
+ this.sphereScaleParam_ = primitive.createParam(
+ O3D_DEBUG_VECTOR_SCALE_PARAM_NAME,
+ 'ParamFloat3').value = [1, 1, 1];
+ }
+
+ {
+ this.cubeShape_ = o3djs.debug.createLineCube(pack,
+ this.axisMaterial_,
+ 1);
+ this.cubeShape_.name = O3D_DEBUG_CUBE_SHAPE_NAME;
+ var primitive = this.cubeShape_.elements[0];
+ this.cubeScaleParam_ = primitive.createParam(
+ O3D_DEBUG_VECTOR_SCALE_PARAM_NAME,
+ 'ParamFloat3').value = [1, 1, 1];
+ }
+};
+
+
+/**
+ * Gets the pack for this DebugHelper.
+ * @return {!o3d.Pack} The pack for this DebugHelper.
+ */
+o3djs.debug.DebugHelper.prototype.getPack = function() {
+ return this.pack_;
+};
+
+/**
+ * Gets the line shape.
+ * @return {!o3d.Shape} The shape for debug lines.
+ */
+o3djs.debug.DebugHelper.prototype.getLineShape = function() {
+ return this.lineShape_;
+};
+
+/**
+ * Sets the length and width of the axis lines.
+ * @param {number} length Length of an axis in the direction of the axis.
+ * @param {number} width Width of the axis or its thickness.
+ */
+o3djs.debug.DebugHelper.prototype.setAxisScale = function(length,
+ width) {
+ for (var ii = 0; ii < O3D_DEBUG_AXIS_INFO_.length; ++ii) {
+ var info = O3D_DEBUG_AXIS_INFO_[ii];
+ this.axisPrimitives_[ii].getParam(
+ O3D_DEBUG_VECTOR_SCALE_PARAM_NAME).value = [
+ info.offset[0] ? length : width,
+ info.offset[1] ? length : width,
+ info.offset[2] ? length : width];
+ }
+};
+
+/**
+ * Creates a debug shape at a world position.
+ * @private
+ * @param {!o3djs.math.Vector3} position Position at which to create shape.
+ * @param {!o3d.Shape} shape Shape to add to transform.
+ * @return {!o3d.Transform} transform for shape.
+ */
+o3djs.debug.DebugHelper.prototype.createShape_ = function(position, shape) {
+
+ var debugTransform = this.getPack().createObject('Transform');
+ debugTransform.name = shape.name;
+ debugTransform.addShape(shape);
+ debugTransform.parent = this.viewInfo_.treeRoot;
+ debugTransform.translate(position);
+ return debugTransform;
+};
+
+/**
+ * Adds an debug shape to a transform.
+ * @private
+ * @param {!o3d.Transform} transform Transform to add shape to.
+ * @param {!o3d.Shape} shape Shape to add to transform.
+ */
+o3djs.debug.DebugHelper.prototype.addShape_ = function(transform,
+ shape) {
+
+ var debugTransform = o3djs.debug.getDebugTransform_(transform, shape.name);
+ if (!debugTransform) {
+ var debugTransform = this.getPack().createObject('Transform');
+ debugTransform.name = shape.name;
+ debugTransform.addShape(shape);
+ debugTransform.parent = transform;
+ }
+};
+
+/**
+ * Removes a debug shape from a transform
+ * @private
+ * @param {!o3d.Transform} transform Transform to remove shape from.
+ * @param {!o3d.Shape} shape Shape to remove from transform.
+ */
+o3djs.debug.DebugHelper.prototype.removeShape_ = function(transform,
+ shape) {
+ var name = shape.name;
+ var debugTransform = o3djs.debug.getDebugTransform_(transform, shape.name);
+ if (debugTransform) {
+ debugTransform.parent = null;
+ this.getPack().removeObject(debugTransform);
+ }
+};
+
+/**
+ * Adds a debug shape to all transform a tree.
+ * @private
+ * @param {!o3d.Transform} treeRoot root of tree to add shape to.
+ * @param {!o3d.Shape} shape Shape to add to transforms.
+ */
+o3djs.debug.DebugHelper.prototype.addShapes_ = function(treeRoot,
+ shape) {
+ this.addShape_(treeRoot, shape);
+ var children = treeRoot.children;
+ for (var cc = 0; cc < children.length; ++cc) {
+ var child = children[cc];
+ if (!o3djs.debug.isDebugTransform(child)) {
+ this.addShapes_(child, shape);
+ }
+ }
+};
+
+/**
+ * Removes a debug shape from all transforms in a tree.
+ * @private
+ * @param {!o3d.Transform} treeRoot root of tree to remove axes from.
+ * @param {!o3d.Shape} shape Shape to remove from transforms.
+ */
+o3djs.debug.DebugHelper.prototype.removeShapes_ = function(treeRoot,
+ shape) {
+ this.removeShape_(treeRoot, shape);
+ var children = treeRoot.children;
+ for (var cc = 0; cc < children.length; ++cc) {
+ var child = children[cc];
+ if (!o3djs.debug.isDebugTransform(child)) {
+ this.removeShapes_(child, shape);
+ }
+ }
+};
+
+/**
+ * Sets a param value on a debug transform. If the param does not exist it
+ * will be created.
+ * @private
+ * @param {!o3d.Transform} transform Transform on which debug transform
+ * exists.
+ * @param {string} name Name of debug transform.
+ * @param {string} paramName Name of param to set.
+ * @param {string} paramType type of param to set.
+ * @param {*} paramValue value to set param.
+ */
+o3djs.debug.DebugHelper.prototype.addSetDebugTransformParam_ = function(
+ transform,
+ name,
+ paramName,
+ paramType,
+ paramValue) {
+ var debugTransform = o3djs.debug.getDebugTransform_(transform, name);
+ if (debugTransform) {
+ var param = debugTransform.getParam(paramName);
+ if (!param) {
+ param = debugTransform.createParam(paramName, paramType);
+ }
+ param.value = paramValue;
+ }
+};
+
+/**
+ * Adds an axis to a transform.
+ * @param {!o3d.Transform} transform Transform to add axis to.
+ */
+o3djs.debug.DebugHelper.prototype.addAxis = function(transform) {
+ this.addShape_(transform, this.axisShape_);
+};
+
+/**
+ * Removes an axis from a transform
+ * @param {!o3d.Transform} transform Transform to remove axis from.
+ */
+o3djs.debug.DebugHelper.prototype.removeAxis = function(transform) {
+ this.removeShape_(transform, this.axisShape_);
+};
+
+/**
+ * Adds axes to all transform in a tree.
+ * @param {!o3d.Transform} treeRoot root of tree to add axes to.
+ */
+o3djs.debug.DebugHelper.prototype.addAxes = function(treeRoot) {
+ this.addShapes_(treeRoot, this.axisShape_);
+};
+
+/**
+ * Removes axes from all transforms in a tree.
+ * @param {!o3d.Transform} treeRoot root of tree to remove axes from.
+ */
+o3djs.debug.DebugHelper.prototype.removeAxes = function(treeRoot) {
+ this.removeShapes_(treeRoot, this.axisShape_);
+};
+
+/**
+ * Set axis color.
+ * @param {!o3d.Transform} transform Transform on which to change axis
+ * color.
+ * @param {!o3djs.math.Vector4} color 4 number array in RGBA format.
+ */
+o3djs.debug.DebugHelper.prototype.setAxisColor = function(transform,
+ color) {
+ this.addSetDebugTransformParam_(transform,
+ O3D_DEBUG_AXIS_SHAPE_NAME,
+ O3D_DEBUG_COLOR_PARAM_NAME,
+ 'ParamFloat4',
+ color);
+};
+
+/**
+ * Removes the color from an axis.
+ * @param {!o3d.Transform} transform Transform on which to remove color.
+ */
+o3djs.debug.DebugHelper.prototype.clearAxisColor = function(transform) {
+ var debugTransform = o3djs.debug.getDebugTransform_(
+ transform,
+ O3D_DEBUG_AXIS_SHAPE_NAME);
+ if (debugTransform) {
+ var colorParam = debugTransform.getParam(O3D_DEBUG_COLOR_PARAM_NAME);
+ if (colorParam) {
+ debugTransform.removeParam(colorParam);
+ }
+ }
+};
+
+/**
+ * Creates a sphere in world space.
+ * @param {!o3djs.math.Vector3} position Position at which to create sphere.
+ * @param {!o3djs.math.Vector4} opt_color RGBA color for sphere.
+ * @param {number} opt_scale of sphere.
+ * @return {!o3d.Transform} transform for sphere.
+ */
+o3djs.debug.DebugHelper.prototype.createSphere = function(position,
+ opt_color,
+ opt_scale) {
+ var transform = this.createShape_(position, this.sphereShape_);
+ if (opt_color) {
+ this.setSphereColor(transform, opt_color);
+ }
+ if (opt_scale) {
+ this.setSphereScale(transform, opt_scale);
+ }
+ return transform;
+};
+
+/**
+ * Adds a sphere to a transform.
+ * @param {!o3d.Transform} transform Transform to add sphere to.
+ * @param {!o3djs.math.Vector4} opt_color RGBA color for sphere.
+ * @param {number} opt_scale of sphere.
+ */
+o3djs.debug.DebugHelper.prototype.addSphere = function(transform,
+ opt_color,
+ opt_scale) {
+ this.addShape_(transform, this.sphereShape_);
+ if (opt_color) {
+ this.setSphereColor(transform, opt_color);
+ }
+ if (opt_scale) {
+ this.setSphereScale(transform, opt_scale);
+ }
+};
+
+/**
+ * Removes a sphere from a transform
+ * @param {!o3d.Transform} transform Transform to remove sphere from.
+ */
+o3djs.debug.DebugHelper.prototype.removeSphere = function(transform) {
+ this.removeShape_(transform, this.sphereShape_);
+};
+
+/**
+ * Adds spheres to all transform a tree.
+ * @param {!o3d.Transform} treeRoot root of tree to add spheres to.
+ */
+o3djs.debug.DebugHelper.prototype.addSpheres = function(treeRoot) {
+ this.addShapes_(treeRoot, this.sphereShape_);
+};
+
+/**
+ * Removes spheres from all transforms in a tree.
+ * @param {!o3d.Transform} treeRoot root of tree to remove spheres from.
+ */
+o3djs.debug.DebugHelper.prototype.removeSpheres = function(treeRoot) {
+ this.removeShapes_(treeRoot, this.sphereShape_);
+};
+
+/**
+ * Set sphere color.
+ * @param {!o3d.Transform} transform Transform on which to change sphere
+ * color.
+ * @param {!o3djs.math.Vector4} color 4 number array in RGBA format.
+ */
+o3djs.debug.DebugHelper.prototype.setSphereColor = function(transform,
+ color) {
+ this.addSetDebugTransformParam_(transform,
+ O3D_DEBUG_SPHERE_SHAPE_NAME,
+ O3D_DEBUG_COLOR_PARAM_NAME,
+ 'ParamFloat4',
+ color);
+};
+
+/**
+ * Sets the scale of a debug sphere.
+ * @param {!o3d.Transform} transform Transform on which to change sphere
+ * scale.
+ * @param {number} scale Scale to make the sphere.
+ */
+o3djs.debug.DebugHelper.prototype.setSphereScale = function(transform,
+ scale) {
+ this.addSetDebugTransformParam_(transform,
+ O3D_DEBUG_SPHERE_SHAPE_NAME,
+ O3D_DEBUG_VECTOR_SCALE_PARAM_NAME,
+ 'ParamFloat3',
+ [scale, scale, scale]);
+};
+
+/**
+ * Creates a cube at a world position..
+ * @param {!o3djs.math.Vector3} position Position at which to create sphere.
+ * @param {!o3djs.math.Vector4} opt_color RGBA color for cube.
+ * @param {number} opt_scale of cube.
+ * @return {!o3d.Transform} transform for cube.
+ */
+o3djs.debug.DebugHelper.prototype.createCube = function(position,
+ opt_color,
+ opt_scale) {
+ var transform = this.createShape_(position, this.cubeShape_);
+ if (opt_color) {
+ this.setCubeColor(transform, opt_color);
+ }
+ if (opt_scale) {
+ this.setCubeScale(transform, opt_scale);
+ }
+ return transform;
+};
+
+/**
+ * Adds a cube to a transform.
+ * @param {!o3d.Transform} transform Transform to add cube to.
+ * @param {!o3djs.math.Vector4} opt_color RGBA color for cube.
+ * @param {number} opt_scale of cube.
+ */
+o3djs.debug.DebugHelper.prototype.addCube = function(transform,
+ opt_color,
+ opt_scale) {
+ this.addShape_(transform, this.cubeShape_);
+ if (opt_color) {
+ this.setCubeColor(transform, opt_color);
+ }
+ if (opt_scale) {
+ this.setCubeScale(transform, opt_scale);
+ }
+};
+
+/**
+ * Removes a cube from a transform
+ * @param {!o3d.Transform} transform Transform to remove cube from.
+ */
+o3djs.debug.DebugHelper.prototype.removeCube = function(transform) {
+ this.removeShape_(transform, this.cubeShape_);
+};
+
+/**
+ * Adds cubes to all transform in a tree.
+ * @param {!o3d.Transform} treeRoot root of tree to add cubes to.
+ */
+o3djs.debug.DebugHelper.prototype.addCubes = function(treeRoot) {
+ this.addShapes_(treeRoot, this.cubeShape_);
+};
+
+/**
+ * Removes cubes from all transforms in a tree.
+ * @param {!o3d.Transform} treeRoot root of tree to remove cubes from.
+ */
+o3djs.debug.DebugHelper.prototype.removeCubes = function(treeRoot) {
+ this.removeShapes_(treeRoot, this.cubeShape_);
+};
+
+/**
+ * Set cube color.
+ * @param {!o3d.Transform} transform Transform on which to change cube
+ * color.
+ * @param {!o3djs.math.Vector3} color 4 number array in RGBA format.
+ */
+o3djs.debug.DebugHelper.prototype.setCubeColor = function(transform,
+ color) {
+ this.addSetDebugTransformParam_(transform,
+ O3D_DEBUG_CUBE_SHAPE_NAME,
+ O3D_DEBUG_COLOR_PARAM_NAME,
+ 'ParamFloat4',
+ color);
+};
+
+/**
+ * Sets the scale of a cubes.
+ * @param {!o3d.Transform} transform Transform on which to change cube
+ * scale.
+ * @param {number} scale Scale to make the cube.
+ */
+o3djs.debug.DebugHelper.prototype.setCubeScale = function(transform,
+ scale) {
+ this.addSetDebugTransformParam_(transform,
+ O3D_DEBUG_CUBE_SHAPE_NAME,
+ O3D_DEBUG_VECTOR_SCALE_PARAM_NAME,
+ 'ParamFloat3',
+ [scale, scale, scale]);
+};
+
+/**
+ * Creates a debug line group. A Debug line group's purpose is the let you
+ * quickly delete a set of lines.
+ * @param {!o3d.Transform} root Root transform to use for lines.
+ * @return {!o3djs.debug.DebugLineGroup} The debug line group.
+ */
+o3djs.debug.DebugHelper.prototype.createDebugLineGroup =
+ function(root) {
+ return new o3djs.debug.DebugLineGroup(this, root);
+};
+
+/**
+ * Creates a debug helper object.
+ *
+ * A debug helper object provides functions to help debug your o3d
+ * application and manages the resources needed to do that for you. For
+ * example it can add axes, spheres and boxes to your transforms as well as
+ * draw lines in 3d space given 2 points.
+ *
+ * @param {!o3d.Pack} pack Pack for DebugHelper to manage its resources
+ * with.
+ * @param {!o3djs.rendergraph.viewInfo} viewInfo ViewInfo for debug
+ * visuals.
+ * @return {!o3djs.debug.DebugHelper} the DebugHelper object.
+ */
+o3djs.debug.createDebugHelper = function(pack, viewInfo) {
+ return new o3djs.debug.DebugHelper(pack, viewInfo);
+};
+
diff --git a/o3d/samples/o3djs/dump.js b/o3d/samples/o3djs/dump.js
new file mode 100644
index 0000000..5989c52e
--- /dev/null
+++ b/o3d/samples/o3djs/dump.js
@@ -0,0 +1,597 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains various dumping functions for o3d. It
+ * puts them in the "dump" module on the o3djs object.
+ *
+ * Note: This library is only a sample. It is not meant to be some official
+ * library. It is provided only as example code.
+ *
+ */
+
+o3djs.provide('o3djs.dump');
+
+/**
+ * A Module for dumping information about o3d objects.
+ * @namespace
+ */
+o3djs.dump = o3djs.dump || {};
+
+/**
+ * Dump the 3 elements of an array of numbers.
+ * @private
+ * @param {string} label Label to put in front of dump.
+ * @param {!Array.<number>} object Array.
+ * @param {string} opt_prefix Optional prefix for indenting.
+ */
+o3djs.dump.dumpXYZ_ = function(label, object, opt_prefix) {
+ opt_prefix = opt_prefix || '';
+ o3djs.dump.dump(opt_prefix + label + ' : ' + object[0] + ', ' +
+ object[1] + ', ' + object[2] + '\n');
+};
+
+/**
+ * Dump the 4 elements of an array of numbers.
+ * @private
+ * @param {string} label Label to put in front of dump.
+ * @param {!Array.<number>} object Array.
+ * @param {string} opt_prefix Optional prefix for indenting.
+ */
+o3djs.dump.dumpXYZW_ = function(label, object, opt_prefix) {
+ opt_prefix = opt_prefix || '';
+ o3djs.dump.dump(opt_prefix + label + ' : ' +
+ object[0] + ', ' +
+ object[1] + ', ' +
+ object[2] + ', ' +
+ object[3] + '\n');
+};
+
+/**
+ * Get the name of a function.
+ * @private
+ * @param {!function(...): *} theFunction Function.
+ * @return {string} The function name.
+ */
+o3djs.dump.getFunctionName_ = function(theFunction) {
+ if (theFunction.name) {
+ return theFunction.name;
+ }
+
+ // try to parse the function name from the definition
+ var definition = theFunction.toString();
+ var name = definition.substring(definition.indexOf('function') + 8,
+ definition.indexOf('('));
+ if (name) {
+ return name;
+ }
+
+ // sometimes there won't be a function name
+ // like for dynamic functions
+ return '*anonymous*';
+};
+
+/**
+ * Get the signature of a function.
+ * @private
+ * @param {!function(...): *} theFunction Function.
+ * @return {string} The function signature.
+ */
+o3djs.dump.getSignature_ = function(theFunction) {
+ var signature = o3djs.dump.getFunctionName_(theFunction);
+ signature += '(';
+ for (var x = 0; x < theFunction.arguments.length; x++) {
+ // trim long arguments
+ var nextArgument = theFunction.arguments[x];
+ if (nextArgument.length > 30) {
+ nextArgument = nextArgument.substring(0, 30) + '...';
+ }
+
+ // apend the next argument to the signature
+ signature += "'" + nextArgument + "'";
+
+ // comma separator
+ if (x < theFunction.arguments.length - 1) {
+ signature += ', ';
+ }
+ }
+ signature += ')';
+ return signature;
+};
+
+/**
+ * Prints a value the console or log or wherever it thinks is appropriate
+ * for debugging.
+ * @param {string} string String to print.
+ */
+o3djs.dump.dump = function(string) {
+ o3djs.BROWSER_ONLY = true;
+ if (window.dump) {
+ window.dump(string);
+ } else if (window.console && window.console.log) {
+ window.console.log(string);
+ }
+};
+
+/**
+ * Gets the value of a matrix as a string.
+ * @param {!o3djs.math.Matrix4} matrix Matrix4 to get value of.
+ * @param {string} opt_prefix Optional prefix for indenting.
+ * @return {string} Value of param.
+ */
+o3djs.dump.getMatrixAsString = function(matrix, opt_prefix) {
+ opt_prefix = opt_prefix || '';
+ var result = opt_prefix + '[';
+ for (var i = 0; 1; ++i){
+ var mi = matrix[i];
+ result += '[';
+ for (var j = 0; 1; ++j) {
+ result += mi[j];
+ if (j < mi.length - 1) {
+ result += ', ';
+ } else {
+ result += ']';
+ break;
+ }
+ }
+ if (i < matrix.length - 1) {
+ result += '\n';
+ result += opt_prefix;
+ } else {
+ break;
+ }
+ }
+ result += ']';
+ return result;
+};
+
+/**
+ * Dumps a point3
+ * @param {string} label Label to put in front of dump.
+ * @param {!o3d.Point3} point3 Point3 to dump.
+ * @param {string} opt_prefix optional prefix for indenting.
+ */
+o3djs.dump.dumpPoint3 = function(label, point3, opt_prefix) {
+ opt_prefix = opt_prefix || '';
+ o3djs.dump.dumpXYZ_(label, point3, opt_prefix);
+};
+
+/**
+ * Dumps a float3
+ * @param {string} label Label to put in front of dump.
+ * @param {!o3d.Float3} float3 Float3 to dump.
+ * @param {string} opt_prefix optional prefix for indenting.
+ */
+o3djs.dump.dumpFloat3 = function(label, float3, opt_prefix) {
+ opt_prefix = opt_prefix || '';
+ o3djs.dump.dumpXYZ_(label, float3, opt_prefix);
+};
+
+/**
+ * Dumps a vector3
+ * @param {string} label Label to put in front of dump.
+ * @param {!o3d.Vector3} vector3 Vector3 to dump.
+ * @param {string} opt_prefix optional prefix for indenting.
+ */
+o3djs.dump.dumpVector3 = function(label, vector3, opt_prefix) {
+ opt_prefix = opt_prefix || '';
+ o3djs.dump.dumpXYZ_(label, vector3, opt_prefix);
+};
+
+/**
+ * Dumps a float4
+ * @param {string} label Label to put in front of dump.
+ * @param {!o3d.Float4} float4 Float4 to dump.
+ * @param {string} opt_prefix optional prefix for indenting.
+ */
+o3djs.dump.dumpFloat4 = function(label, float4, opt_prefix) {
+ opt_prefix = opt_prefix || '';
+ o3djs.dump.dumpXYZW_(label, float4, opt_prefix);
+};
+
+/**
+ * Dumps a vector4
+ * @param {string} label Label to put in front of dump.
+ * @param {!Array.<number>} vector4 vector to dump.
+ * @param {string} opt_prefix optional prefix for indenting.
+ */
+o3djs.dump.dumpVector4 = function(label, vector4, opt_prefix) {
+ opt_prefix = opt_prefix || '';
+ o3djs.dump.dumpXYZW_(label, vector4, opt_prefix);
+};
+
+/**
+ * Dumps a matrix
+ * @param {string} label Label to put in front of dump.
+ * @param {!o3djs.math.Matrix4} matrix Matrix to dump.
+ * @param {string} opt_prefix optional prefix for indenting.
+ */
+o3djs.dump.dumpMatrix = function(label, matrix, opt_prefix) {
+ opt_prefix = opt_prefix || '';
+ o3djs.dump.dump(
+ opt_prefix + label + ' :\n' +
+ o3djs.dump.getMatrixAsString(matrix, opt_prefix + ' ') +
+ '\n');
+};
+
+/**
+ * Dump a bounding box.
+ * @param {string} label Label to put in front of dump.
+ * @param {!o3d.BoundingBox} boundingBox BoundingBox to dump.
+ * @param {string} opt_prefix optional prefix for indenting.
+ */
+o3djs.dump.dumpBoundingBox = function(label,
+ boundingBox,
+ opt_prefix) {
+ opt_prefix = opt_prefix || '';
+ o3djs.dump.dump(opt_prefix + label + ' :\n');
+ o3djs.dump.dumpPoint3('min : ',
+ boundingBox.minExtent,
+ opt_prefix + ' ');
+ o3djs.dump.dumpPoint3('max : ',
+ boundingBox.maxExtent,
+ opt_prefix + ' ');
+};
+
+/**
+ * Gets the value of a parameter as a string.
+ * @param {!o3d.Param} param Parameter to get value of.
+ * @param {string} opt_prefix Optional prefix for indenting.
+ * @return {string} Value of param.
+ */
+o3djs.dump.getParamValueAsString = function(param, opt_prefix) {
+ opt_prefix = opt_prefix || '';
+ var value = '*unknown*';
+
+ if (param.isAClassName('o3d.ParamFloat')) {
+ value = param.value.toString();
+ } else if (param.isAClassName('o3d.ParamFloat2')) {
+ value = '[' + param.value[0] + ', ' +
+ param.value[1] + ']';
+ } else if (param.isAClassName('o3d.ParamFloat3')) {
+ value = '[' + param.value[0] + ', ' +
+ param.value[1] + ', ' +
+ param.value[2] + ']';
+ } else if (param.isAClassName('o3d.ParamFloat4')) {
+ value = '[' + param.value[0] + ', ' +
+ param.value[1] + ', ' +
+ param.value[2] + ', ' +
+ param.value[3] + ']';
+ } else if (param.isAClassName('o3d.ParamInteger')) {
+ value = param.value.toString();
+ } else if (param.isAClassName('o3d.ParamBoolean')) {
+ value = param.value.toString();
+ } else if (param.isAClassName('o3d.ParamMatrix4')) {
+ value = '\n' + o3djs.dump.getMatrixAsString(param.value,
+ opt_prefix + ' ');
+ } else if (param.isAClassName('o3d.ParamString')) {
+ value = param.value;
+ } else if (param.isAClassName('o3d.ParamTexture')) {
+ value = param.value;
+ value = 'texture : "' + (value ? value.name : 'NULL') + '"';
+ } else if (param.isAClassName('o3d.ParamSampler')) {
+ value = param.value;
+ value = 'sampler : "' + (value ? value.name : 'NULL') + '"';
+ } else if (param.isAClassName('o3d.ParamMaterial')) {
+ value = param.value;
+ value = 'material : "' + (value ? value.name : 'NULL') + '"';
+ } else if (param.isAClassName('o3d.ParamEffect')) {
+ value = param.value;
+ value = 'effect : "' + (value ? value.name : 'NULL') + '"';
+ } else if (param.isAClassName('o3d.ParamState')) {
+ value = param.value;
+ value = 'state : "' + (value ? value.name : 'NULL') + '"';
+ } else if (param.isAClassName('o3d.ParamTransform')) {
+ value = param.value;
+ value = 'transform : "' + (value ? value.name : 'NULL') + '"';
+ } else if (param.isAClassName('o3d.ParamDrawList')) {
+ value = param.value;
+ value = 'drawlist : "' + (value ? value.name : 'NULL') + '"';
+ } else if (param.isAClassName('o3d.ParamDrawContext')) {
+ value = param.value;
+ value = 'drawcontext : "' + (value ? value.name : 'NULL') + '"';
+ }
+
+ return value;
+};
+
+/**
+ * Dumps an single parameter
+ * @param {!o3d.Param} param Param to dump.
+ * @param {string} opt_prefix Optional prefix for indenting.
+ */
+o3djs.dump.dumpParam = function(param, opt_prefix) {
+ opt_prefix = opt_prefix || '';
+ o3djs.dump.dump (
+ opt_prefix + param.className + ' : "' + param.name + '" : ' +
+ o3djs.dump.getParamValueAsString(param, opt_prefix) + '\n');
+};
+
+/**
+ * Given a ParamObject dumps all the Params on it.
+ * @param {!o3d.ParamObject} param_object ParamObject to dump Params of.
+ * @param {string} opt_prefix Optional prefix for indenting.
+ */
+o3djs.dump.dumpParams = function(param_object, opt_prefix) {
+ opt_prefix = opt_prefix || '';
+ // print params
+ var params = param_object.params;
+ for (var p = 0; p < params.length; p++) {
+ o3djs.dump.dumpParam(params[p], opt_prefix);
+ }
+};
+
+/**
+ * Given a ParamObject dumps it and all the Params on it.
+ * @param {!o3d.ParamObject} param_object ParamObject to dump.
+ * @param {string} opt_prefix Optional prefix for indenting.
+ */
+o3djs.dump.dumpParamObject = function(param_object, opt_prefix) {
+ opt_prefix = opt_prefix || '';
+ o3djs.dump.dump (
+ opt_prefix + param_object.className + ' : "' +
+ param_object.name + '"\n');
+ o3djs.dump.dumpParams(param_object, opt_prefix + ' ');
+};
+
+/**
+ * Given a Stream dumps it and all the Params on it.
+ * @param {!o3d.Stream} stream Stream to dump.
+ * @param {string} opt_prefix Optional prefix for indenting.
+ */
+o3djs.dump.dumpStream = function(stream, opt_prefix) {
+ opt_prefix = opt_prefix || '';
+ o3djs.dump.dump(
+ opt_prefix + 'semantic: ' + stream.semantic +
+ ', index: ' + stream.semanticIndex +
+ ', dataType: ' + stream.dataType +
+ ', field: ' + stream.field.name + '\n');
+};
+
+/**
+ * Given a element dumps its name, all the Params and DrawElements on
+ * it.
+ * @param {!o3d.Element} element Element to dump.
+ * @param {string} opt_prefix Optional prefix for indenting.
+ */
+o3djs.dump.dumpElement = function(element, opt_prefix) {
+ opt_prefix = opt_prefix || '';
+
+ o3djs.dump.dump (opt_prefix + '------------ Element --------------\n');
+
+ // get type
+ o3djs.dump.dump (
+ opt_prefix + 'Element: "' + element.name + '"\n');
+
+ // print params
+ o3djs.dump.dump (opt_prefix + ' --Params--\n');
+ o3djs.dump.dumpParams (element, opt_prefix + ' ');
+
+ // print elements.
+ o3djs.dump.dump (opt_prefix + ' --DrawElements--\n');
+ var drawElements = element.drawElements;
+ for (var g = 0; g < drawElements.length; g++) {
+ var drawElement = drawElements[g]
+ o3djs.dump.dumpParamObject(drawElement, opt_prefix + ' ');
+ }
+
+ if (element.isAClassName('o3d.Primitive')) {
+ o3djs.dump.dump (
+ opt_prefix + ' primitive type: ' + element.primitiveType + '\n');
+ o3djs.dump.dump (
+ opt_prefix + ' number vertices: ' + element.numberVertices + '\n');
+ o3djs.dump.dump (
+ opt_prefix + ' number primitives: ' + element.numberPrimitives +
+ '\n');
+ var streamBank = element.streamBank;
+ if (streamBank) {
+ var streams = streamBank.vertexStreams;
+ for (var ss = 0; ss < streams.length; ss++) {
+ var stream = streams[ss];
+ o3djs.dump.dump(opt_prefix + ' stream ' + ss + ': ');
+ o3djs.dump.dumpStream(stream);
+ }
+ }
+ }
+};
+
+/**
+ * Given a shape dumps its name, all the Params and Primitves on
+ * it.
+ * @param {!o3d.Shape} shape Shape to dump.
+ * @param {string} opt_prefix Optional prefix for indenting.
+ */
+o3djs.dump.dumpShape = function(shape, opt_prefix) {
+ opt_prefix = opt_prefix || '';
+
+ o3djs.dump.dump (opt_prefix + '------------ Shape --------------\n');
+
+ // get type
+ o3djs.dump.dump (
+ opt_prefix + 'Shape: "' + shape.name + '"\n');
+
+ // print params
+ o3djs.dump.dump (opt_prefix + ' --Params--\n');
+ o3djs.dump.dumpParams (shape, opt_prefix + ' ');
+
+ // print elements.
+ o3djs.dump.dump (opt_prefix + ' --Elements--\n');
+ var elements = shape.elements;
+ for (var p = 0; p < elements.length; p++) {
+ var element = elements[p];
+ o3djs.dump.dumpElement(element, opt_prefix + ' ');
+ }
+};
+
+/**
+ * Given a texture dumps its name and other info.
+ * it.
+ * @param {!o3d.Texture} texture Texture to dump.
+ * @param {string} opt_prefix Optional prefix for indenting.
+ */
+o3djs.dump.dumpTexture = function(texture, opt_prefix) {
+ opt_prefix = opt_prefix || '';
+ var uri = '';
+ var param = texture.getParam('uri');
+ if (param) {
+ uri = param.value;
+ }
+ o3djs.dump.dump (
+ opt_prefix + texture.className +
+ ' : "' + texture.name +
+ '" uri : "' + uri +
+ '" width: ' + texture.width +
+ ' height: ' + texture.height +
+ ' alphaIsOne: ' + texture.alphaIsOne +
+ '\n');
+};
+
+/**
+ * Given a transform dumps its name and all the Params and Shapes on it.
+ * @param {!o3d.Transform} transform Transform to dump.
+ * @param {string} opt_prefix Optional prefix for indenting.
+ */
+o3djs.dump.dumpTransform = function(transform, opt_prefix) {
+ opt_prefix = opt_prefix || '';
+
+ o3djs.dump.dump (opt_prefix + '----------- Transform -------------\n');
+
+ // get type
+ o3djs.dump.dump (
+ opt_prefix + 'Transform: ' + transform.name + '"\n');
+
+ // print params
+ o3djs.dump.dump (opt_prefix + ' --Local Matrix--\n');
+ o3djs.dump.dump (
+ o3djs.dump.getMatrixAsString(transform.localMatrix,
+ opt_prefix + ' ') + '\n');
+
+ // print params
+ o3djs.dump.dump (opt_prefix + ' --Params--\n');
+ o3djs.dump.dumpParams (transform, opt_prefix + ' ');
+
+ // print shapes.
+ o3djs.dump.dump (opt_prefix + ' --Shapes--\n');
+ var shapes = transform.shapes;
+ for (var s = 0; s < shapes.length; s++) {
+ var shape = shapes[s];
+ o3djs.dump.dumpNamedObjectName(shape, opt_prefix + ' ');
+ }
+};
+
+/**
+ * Dumps an entire transform graph tree.
+ * @param {!o3d.Transform} transform Transform to start dumping from.
+ * @param {string} opt_prefix Optional prefix for indenting.
+ */
+o3djs.dump.dumpTransformTree = function(transform, opt_prefix) {
+ opt_prefix = opt_prefix || '';
+
+ o3djs.dump.dumpTransform(transform, opt_prefix);
+
+ var child_prefix = opt_prefix + ' ';
+ var children = transform.children;
+ for (var c = 0; c < children.length; c++) {
+ o3djs.dump.dumpTransformTree(children[c], child_prefix);
+ }
+};
+
+/**
+ * Dumps a list of Transforms.
+ * @param {!Array.<!o3d.Transform>} transform_list Array of Transforms to dump.
+ */
+o3djs.dump.dumpTransformList = function(transform_list) {
+ o3djs.dump.dump (transform_list.length + ' transforms in list!!!\n');
+ for (var i = 0; i < transform_list.length; i++) {
+ o3djs.dump.dumpTransform(transform_list[i]);
+ }
+};
+
+/**
+ * Dumps the name and class of a NamedObject.
+ * @param {!o3d.NamedObject} namedObject to use.
+ * @param {string} opt_prefix Optional prefix for indenting.
+ */
+o3djs.dump.dumpNamedObjectName = function(namedObject, opt_prefix) {
+ opt_prefix = opt_prefix || '';
+ o3djs.dump.dump (
+ opt_prefix + namedObject.className + ' : "' + namedObject.name +
+ '"\n');
+};
+
+/**
+ * Dumps a RenderNode and all its paramaters.
+ * @param {!o3d.RenderNode} render_node RenderNode to use.
+ * @param {string} opt_prefix Optional prefix for indenting.
+ */
+o3djs.dump.dumpRenderNode = function(render_node, opt_prefix) {
+ opt_prefix = opt_prefix || '';
+
+ o3djs.dump.dump (opt_prefix + '----------- Render Node -----------\n');
+ // get type
+ o3djs.dump.dumpNamedObjectName(render_node, opt_prefix);
+
+ // print params
+ o3djs.dump.dump (opt_prefix + ' --Params--\n');
+ o3djs.dump.dumpParams(render_node, opt_prefix + ' ');
+};
+
+/**
+ * Dumps an entire RenderGraph tree.
+ * @param {!o3d.RenderNode} render_node RenderNode to start dumping from.
+ * @param {string} opt_prefix Optional prefix for indenting.
+ */
+o3djs.dump.dumpRenderNodeTree = function(render_node, opt_prefix) {
+ opt_prefix = opt_prefix || '';
+
+ o3djs.dump.dumpRenderNode(render_node, opt_prefix);
+
+ var child_prefix = opt_prefix + ' ';
+ var children = render_node.children;
+ for (var c = 0; c < children.length; c++) {
+ o3djs.dump.dumpRenderNodeTree(children[c], child_prefix);
+ }
+};
+
+/**
+ * Dumps a javascript stack track.
+ */
+o3djs.dump.dumpStackTrace = function() {
+ o3djs.dump.dump('Stack trace:\n');
+ var nextCaller = arguments.callee.caller;
+ while (nextCaller) {
+ o3djs.dump.dump(o3djs.dump.getSignature_(nextCaller) + '\n');
+ nextCaller = nextCaller.caller;
+ }
+ o3djs.dump.dump('\n\n');
+};
+
diff --git a/o3d/samples/o3djs/effect.js b/o3d/samples/o3djs/effect.js
new file mode 100644
index 0000000..469d4e4
--- /dev/null
+++ b/o3d/samples/o3djs/effect.js
@@ -0,0 +1,737 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains various functions related to effects.
+ * It puts them in the "effect" module on the o3djs object.
+ *
+ * Note: This library is only a sample. It is not meant to be some official
+ * library. It is provided only as example code.
+ *
+ */
+
+o3djs.provide('o3djs.effect');
+
+o3djs.require('o3djs.io');
+
+/**
+ * A Module for dealing with effects.
+ * @namespace
+ */
+o3djs.effect = o3djs.effect || {};
+
+/**
+ * The name of the parameter on a material if it's a collada standard
+ * material.
+ *
+ * NOTE: This parameter is just a string attached to a material. It has no
+ * meaning to the plugin, it is passed from the conditioner to the
+ * javascript libraries so that they can build collada like effects.
+ *
+ * @type {string}
+ */
+o3djs.effect.COLLADA_LIGHTING_TYPE_PARAM_NAME = 'collada.lightingType';
+
+/**
+ * The collada standard lighting types.
+ * @type {!Object}
+ */
+o3djs.effect.COLLADA_LIGHTING_TYPES = {phong: 1,
+ lambert: 1,
+ blinn: 1,
+ constant: 1};
+
+/**
+ * The FCollada standard materials sampler parameter name prefixes.
+ * @type {!Array.<string>}
+ */
+o3djs.effect.COLLADA_SAMPLER_PARAMETER_PREFIXES = ['emissive',
+ 'ambient',
+ 'diffuse',
+ 'specular',
+ 'bump'];
+
+/**
+ * Check if lighting type is a collada lighting type.
+ * @param {string} lightingType Lighting type to check.
+ * @return {boolean} true if it's a collada lighting type.
+ */
+o3djs.effect.isColladaLightingType = function(lightingType) {
+ return o3djs.effect.COLLADA_LIGHTING_TYPES[lightingType.toLowerCase()] ==
+ 1;
+};
+
+/**
+ * Returns the collada lighting type of a collada standard material.
+ * @param {!o3d.Material} material Material to get lighting type from.
+ * @return {string} The lighting type or "" if it's not a collada standard
+ * material.
+ */
+o3djs.effect.getColladaLightingType = function(material) {
+ var lightingTypeParam = material.getParam(
+ o3djs.effect.COLLADA_LIGHTING_TYPE_PARAM_NAME);
+ if (lightingTypeParam) {
+ var lightingType = lightingTypeParam.value.toLowerCase();
+ if (o3djs.effect.isColladaLightingType(lightingType)) {
+ return lightingType;
+ }
+ }
+ return '';
+};
+
+/**
+ * Get the number of TEXCOORD streams needed by this material.
+ * @param {!o3d.Material} material The material MUST be a standard
+ * collada material.
+ * @return {number} The number oc TEXCOORD streams needed.
+ */
+o3djs.effect.getNumTexCoordStreamsNeeded = function(material) {
+ var lightingType = o3djs.effect.getColladaLightingType(material);
+ if (!o3djs.effect.isColladaLightingType(lightingType)) {
+ throw 'not a collada standard material';
+ }
+ var colladaSamplers = o3djs.effect.COLLADA_SAMPLER_PARAMETER_PREFIXES;
+ var numTexCoordStreamsNeeded = 0
+ for (var cc = 0; cc < colladaSamplers.length; ++cc) {
+ var samplerPrefix = colladaSamplers[cc];
+ var samplerParam = material.getParam(samplerPrefix + 'Sampler');
+ if (samplerParam) {
+ ++numTexCoordStreamsNeeded;
+ }
+ }
+ return numTexCoordStreamsNeeded;
+};
+
+/**
+ * Loads shader source from an external file and creates shaders for an effect.
+ * @param {!o3d.Effect} effect The effect to create the shaders in.
+ * @param {string} url The url of the shader source.
+ */
+o3djs.effect.loadEffect = function(effect, url) {
+ var fxString = o3djs.io.loadTextFileSynchronous(url);
+ effect.loadFromFXString(fxString);
+};
+
+/**
+ * Creates an effect from a file.
+ * If the effect already exists in the pack that effect will be returned.
+ * @param {!o3d.Pack} pack Pack to create effect in.
+ * @param {string} url Url for effect file.
+ * @return {!o3d.Effect} The effect.
+ */
+o3djs.effect.createEffectFromFile = function(pack, url) {
+ var effect = pack.getObjects(url, 'o3d.Effect')[0];
+ if (!effect) {
+ effect = pack.createObject('Effect');
+ o3djs.effect.loadEffect(effect, url);
+ effect.name = url;
+ }
+ return effect;
+};
+
+/**
+ * Builds a shader string for a given standard COLLADA material type.
+ *
+ * @param {!o3d.Material} material Material for which to build the shader.
+ * @param {string} effectType Type of effect to create ('phong', 'lambert',
+ * 'constant').
+ * @return {{description: string, shader: string}} A description and the shader
+ * string.
+ */
+o3djs.effect.buildStandardShaderString = function(material,
+ effectType) {
+ var bumpSampler = material.getParam('bumpSampler');
+ var bumpUVInterpolant;
+
+ /**
+ * Extracts the texture type from a texture param.
+ * @param {!o3d.ParamTexture} textureParam The texture parameter to
+ * inspect.
+ * @return {string} The texture type (1D, 2D, 3D or CUBE).
+ */
+ var getTextureType = function(textureParam) {
+ var texture = textureParam.value;
+ if (!texture) return '2D'; // No texture value, have to make a guess.
+ switch (texture.className) {
+ case 'o3d.Texture1D' : return '1D';
+ case 'o3d.Texture2D' : return '2D';
+ case 'o3d.Texture3D' : return '3D';
+ case 'o3d.TextureCUBE' : return 'CUBE';
+ default : return '2D';
+ }
+ }
+
+ /**
+ * Extracts the sampler type from a sampler param. It does it by inspecting
+ * the texture associated with the sampler.
+ * @param {!o3d.ParamTexture} samplerParam The texture parameter to
+ * inspect.
+ * @return {string} The texture type (1D, 2D, 3D or CUBE).
+ */
+ var getSamplerType = function(samplerParam) {
+ var sampler = samplerParam.value;
+ if (!sampler) return '2D';
+ var textureParam = sampler.getParam('Texture');
+ if (textureParam)
+ return getTextureType(textureParam);
+ else
+ return '2D';
+ };
+
+ /**
+ * Builds uniform variables common to all standard lighting types.
+ * @return {string} The effect code for the common shader uniforms.
+ */
+ var buildCommonUniforms = function() {
+ return 'uniform float4x4 worldViewProjection : WORLDVIEWPROJECTION;\n' +
+ 'uniform float3 lightWorldPos;\n' +
+ 'uniform float4 lightColor;\n'
+ };
+
+ /**
+ * Builds uniform variables common to lambert, phong and blinn lighting types.
+ * @return {string} The effect code for the common shader uniforms.
+ */
+ var buildLightingUniforms = function() {
+ return 'uniform float4x4 world : WORLD;\n' +
+ 'uniform float4x4 viewInverse : VIEWINVERSE;\n' +
+ 'uniform float4x4 worldInverseTranspose : WORLDINVERSETRANSPOSE;\n';
+ };
+
+ /**
+ * Builds uniform parameters for a given color input. If the material
+ * has a sampler parameter, a sampler uniform is created, otherwise a
+ * float4 color value is created.
+ * @param {!o3d.Material} material The material to inspect.
+ * @param {!Array.<string>} descriptions Array to add descriptions too.
+ * @param {string} name The name of the parameter to look for. Usually
+ * emissive, ambient, diffuse or specular.
+ * @param {boolean} opt_addColorParam Whether to add a color param if no
+ * sampler exists. Default = true.
+ * @return {string} The effect code for the uniform parameter.
+ */
+ var buildColorParam = function(material, descriptions, name,
+ opt_addColorParam) {
+ if (opt_addColorParam === undefined) {
+ opt_addColorParam = true;
+ }
+ var samplerParam = material.getParam(name + 'Sampler');
+ if (samplerParam) {
+ var type = getSamplerType(samplerParam);
+ descriptions.push(name + type + 'Texture');
+ return 'sampler' + type + ' ' + name + 'Sampler;\n'
+ } else if (opt_addColorParam) {
+ descriptions.push(name + 'Color');
+ return 'uniform float4 ' + name + ';\n';
+ } else {
+ return '';
+ }
+ };
+
+ /**
+ * Builds the effect code to retrieve a given color input. If the material
+ * has a sampler parameter of that name, a texture lookup is done. Otherwise
+ * it's a no-op, since the value is retrieved directly from the color uniform
+ * of that name.
+ * @param {!o3d.Material} material The material to inspect.
+ * @param {string} name The name of the parameter to look for. Usually
+ * emissive, ambient, diffuse or specular.
+ * @return {string} The effect code for the uniform parameter retrieval.
+ */
+ var getColorParam = function(material, name) {
+ var samplerParam = material.getParam(name + 'Sampler');
+ if (samplerParam) {
+ var type = getSamplerType(samplerParam);
+ return ' float4 ' + name + ' = tex' + type +
+ '(' + name + 'Sampler, input.' + name + 'UV);\n'
+ } else {
+ return '';
+ }
+ };
+
+ /**
+ * Builds the vertex and fragment shader entry point in the format that
+ * o3d can parse.
+ * @return {string} The effect code for the entry points.
+ */
+ var buildEntryPoints = function() {
+ return ' // #o3d VertexShaderEntryPoint vertexShaderFunction\n' +
+ ' // #o3d PixelShaderEntryPoint pixelShaderFunction\n' +
+ ' // #o3d MatrixLoadOrder RowMajor\n';
+ };
+
+ /**
+ * Builds vertex and fragment shader string for the Constant lighting type.
+ * @param {!o3d.Material} material The material for which to build
+ * shaders.
+ * @param {!Array.<string>} descriptions Array to add descriptions too.
+ * @return {string} The effect code for the shader, ready to be parsed.
+ */
+ var buildConstantShaderString = function(material, descriptions) {
+ descriptions.push('constant');
+ return buildCommonUniforms() +
+ buildColorParam(material, descriptions, 'emissive') +
+ buildVertexDecls(material, false, false) +
+ 'OutVertex vertexShaderFunction(InVertex input) {\n' +
+ ' OutVertex output;\n' +
+ positionVertexShaderCode() +
+ buildUVPassthroughs(material) +
+ ' return output;\n' +
+ '}\n' +
+ 'float4 pixelShaderFunction(OutVertex input) : COLOR {\n' +
+ getColorParam(material, 'emissive') +
+ ' return emissive;\n' +
+ '}\n' +
+ '\n' +
+ buildEntryPoints();
+ };
+
+ /**
+ * Builds vertex and fragment shader string for the Lambert lighting type.
+ * @param {!o3d.Material} material The material for which to build
+ * shaders.
+ * @param {!Array.<string>} descriptions Array to add descriptions too.
+ * @return {string} The effect code for the shader, ready to be parsed.
+ */
+ var buildLambertShaderString = function(material, descriptions) {
+ descriptions.push('lambert');
+ return buildCommonUniforms() +
+ buildLightingUniforms() +
+ buildColorParam(material, descriptions, 'emissive') +
+ buildColorParam(material, descriptions, 'ambient') +
+ buildColorParam(material, descriptions, 'diffuse') +
+ buildColorParam(material, descriptions, 'bump', false) +
+ buildVertexDecls(material, true, false) +
+ 'OutVertex vertexShaderFunction(InVertex input) {\n' +
+ ' OutVertex output;\n' +
+ buildUVPassthroughs(material) +
+ positionVertexShaderCode() +
+ normalVertexShaderCode() +
+ surfaceToLightVertexShaderCode() +
+ bumpVertexShaderCode() +
+ ' return output;\n' +
+ '}\n' +
+ 'float4 pixelShaderFunction(OutVertex input) : COLOR\n' +
+ '{\n' +
+ getColorParam(material, 'emissive') +
+ getColorParam(material, 'ambient') +
+ getColorParam(material, 'diffuse') +
+ getNormalShaderCode() +
+ ' float3 surfaceToLight = normalize(input.surfaceToLight);\n' +
+ ' float4 litR = lit(dot(normal, surfaceToLight), 0, 0);\n' +
+ ' return float4((emissive +\n' +
+ ' lightColor * (ambient * diffuse + diffuse * litR.y)).rgb,' +
+ ' diffuse.a);\n' +
+ '}\n' +
+ '\n' +
+ buildEntryPoints();
+
+ };
+
+ /**
+ * Builds vertex and fragment shader string for the Blinn lighting type.
+ * @param {!o3d.Material} material The material for which to build
+ * shaders.
+ * @param {!Array.<string>} descriptions Array to add descriptions too.
+ * @return {string} The effect code for the shader, ready to be parsed.
+ * TODO: This is actually just a copy of the Phong code.
+ * Change to Blinn.
+ */
+ var buildBlinnShaderString = function(material, descriptions) {
+ descriptions.push('blinn');
+ return buildCommonUniforms() +
+ buildLightingUniforms() +
+ buildColorParam(material, descriptions, 'emissive') +
+ buildColorParam(material, descriptions, 'ambient') +
+ buildColorParam(material, descriptions, 'diffuse') +
+ buildColorParam(material, descriptions, 'specular') +
+ buildColorParam(material, descriptions, 'bump', false) +
+ 'uniform float shininess;\n' +
+ 'uniform float specularFactor;\n' +
+ buildVertexDecls(material, true, true) +
+ 'OutVertex vertexShaderFunction(InVertex input) {\n' +
+ ' OutVertex output;\n' +
+ buildUVPassthroughs(material) +
+ positionVertexShaderCode() +
+ normalVertexShaderCode() +
+ surfaceToLightVertexShaderCode() +
+ surfaceToViewVertexShaderCode() +
+ bumpVertexShaderCode() +
+ ' return output;\n' +
+ '}\n' +
+ 'float4 pixelShaderFunction(OutVertex input) : COLOR\n' +
+ '{\n' +
+ getColorParam(material, 'emissive') +
+ getColorParam(material, 'ambient') +
+ getColorParam(material, 'diffuse') +
+ getColorParam(material, 'specular') +
+ getNormalShaderCode() +
+ ' float3 surfaceToLight = normalize(input.surfaceToLight);\n' +
+ ' float3 surfaceToView = normalize(input.surfaceToView);\n' +
+ ' float3 halfVector = normalize(surfaceToLight + surfaceToView);\n' +
+ ' float4 litR = lit(dot(normal, surfaceToLight), \n' +
+ ' dot(normal, halfVector), shininess);\n' +
+ ' return float4((emissive +\n' +
+ ' lightColor * (ambient * diffuse + diffuse * litR.y +\n' +
+ ' + specular * litR.z * specularFactor)).rgb,' +
+ ' diffuse.a);\n' +
+ '}\n' +
+ '\n' +
+ buildEntryPoints();
+ };
+
+ /**
+ * Builds vertex and fragment shader string for the Phong lighting type.
+ * @param {!o3d.Material} material The material for which to build
+ * shaders.
+ * @param {!Array.<string>} descriptions Array to add descriptions too.
+ * @return {string} The effect code for the shader, ready to be parsed.
+ */
+ var buildPhongShaderString = function(material, descriptions) {
+ descriptions.push('phong');
+ return buildCommonUniforms() +
+ buildLightingUniforms() +
+ buildColorParam(material, descriptions, 'emissive') +
+ buildColorParam(material, descriptions, 'ambient') +
+ buildColorParam(material, descriptions, 'diffuse') +
+ buildColorParam(material, descriptions, 'specular') +
+ buildColorParam(material, descriptions, 'bump', false) +
+ 'uniform float shininess;\n' +
+ 'uniform float specularFactor;\n' +
+ buildVertexDecls(material, true, true) +
+ 'OutVertex vertexShaderFunction(InVertex input) {\n' +
+ ' OutVertex output;\n' +
+ buildUVPassthroughs(material) +
+ positionVertexShaderCode() +
+ normalVertexShaderCode() +
+ surfaceToLightVertexShaderCode() +
+ surfaceToViewVertexShaderCode() +
+ bumpVertexShaderCode() +
+ ' return output;\n' +
+ '}\n' +
+ 'float4 pixelShaderFunction(OutVertex input) : COLOR\n' +
+ '{\n' +
+ getColorParam(material, 'emissive') +
+ getColorParam(material, 'ambient') +
+ getColorParam(material, 'diffuse') +
+ getColorParam(material, 'specular') +
+ getNormalShaderCode() +
+ ' float3 surfaceToLight = normalize(input.surfaceToLight);\n' +
+ ' float3 surfaceToView = normalize(input.surfaceToView);\n' +
+ ' float3 halfVector = normalize(surfaceToLight + surfaceToView);\n' +
+ ' float4 litR = lit(dot(normal, surfaceToLight), \n' +
+ ' dot(normal, halfVector), shininess);\n' +
+ ' return float4((emissive +\n' +
+ ' lightColor * (ambient * diffuse + diffuse * litR.y +\n' +
+ ' + specular * litR.z * specularFactor)).rgb,' +
+ ' diffuse.a);\n' +
+ '}\n' +
+ '\n' +
+ buildEntryPoints();
+ };
+
+ // An integer value which keeps track of the next available interpolant.
+ var interpolant;
+
+ /**
+ * Builds the texture coordinate declaration for a given color input
+ * (usually emissive, anmbient, diffuse or specular). If the color
+ * input does not have a sampler, no TEXCOORD declaration is built.
+ * @param {!o3d.Material} material The material to inspect.
+ * @param {string} name The name of the color input.
+ * @return {string} The code for the texture coordinate declaration.
+ */
+ var buildTexCoord = function(material, name) {
+ if (material.getParam(name + 'Sampler')) {
+ return ' float2 ' + name + 'UV : TEXCOORD' + interpolant++ + ';\n';
+ } else {
+ return '';
+ }
+ };
+
+ /**
+ * Builds all the texture coordinate declarations for a vertex attribute
+ * declaration.
+ * @param {!o3d.Material} material The material to inspect.
+ * @return {string} The code for the texture coordinate declarations.
+ */
+ var buildTexCoords = function(material) {
+ interpolant = 0;
+ return buildTexCoord(material, 'emissive') +
+ buildTexCoord(material, 'ambient') +
+ buildTexCoord(material, 'diffuse') +
+ buildTexCoord(material, 'specular');
+ };
+
+ /**
+ * Builds the texture coordinate passthrough statement for a given
+ * color input (usually emissive, ambient, diffuse or specular). These
+ * assigments are used in the vertex shader to pass the texcoords to be
+ * interpolated to the rasterizer. If the color input does not have
+ * a sampler, no code is generated.
+ * @param {!o3d.Material} material The material to inspect.
+ * @param {string} name The name of the color input.
+ * @return {string} The code for the texture coordinate passthrough statement.
+ */
+ var buildUVPassthrough = function(material, name) {
+ if (material.getParam(name + 'Sampler')) {
+ return ' output.' + name + 'UV = input.' + name + 'UV;\n';
+ } else {
+ return '';
+ }
+ };
+
+ /**
+ * Builds all the texture coordinate passthrough statements for the
+ * vertex shader.
+ * @param {!o3d.Material} material The material to inspect.
+ * @return {string} The code for the texture coordinate passthrough
+ * statements.
+ */
+ var buildUVPassthroughs = function(material) {
+ return buildUVPassthrough(material, 'emissive') +
+ buildUVPassthrough(material, 'ambient') +
+ buildUVPassthrough(material, 'diffuse') +
+ buildUVPassthrough(material, 'specular') +
+ buildUVPassthrough(material, 'bump');
+ };
+
+ /**
+ * Builds bump input coords if needed.
+ * @return {string} The code for bump input coords.
+ */
+ var buildBumpInputCoords = function() {
+ return bumpSampler ?
+ (' float3 tangent : TANGENT;\n' +
+ ' float3 binormal : BINORMAL;\n' +
+ ' float2 bumpUV : TEXCOORD' + interpolant++ + ';\n') : '';
+ };
+
+ /**
+ * Builds bump output coords if needed.
+ * @return {string} The code for bump input coords.
+ */
+ var buildBumpOutputCoords = function() {
+ return bumpSampler ?
+ (' float3 tangent : TEXCOORD' + interpolant++ + ';\n' +
+ ' float3 binormal : TEXCOORD' + interpolant++ + ';\n' +
+ ' float2 bumpUV : TEXCOORD' + interpolant++ + ';\n') : '';
+ };
+
+ /**
+ * Builds the position code for the vertex shader.
+ * @return {string} The code for the vertex shader.
+ */
+ var positionVertexShaderCode = function() {
+ return ' output.position = mul(input.position, worldViewProjection);\n';
+ };
+
+ /**
+ * Builds the normal code for the vertex shader.
+ * @return {string} The code for the vertex shader.
+ */
+ var normalVertexShaderCode = function() {
+ return ' output.normal = mul(float4(input.normal, 0),\n' +
+ ' worldInverseTranspose).xyz;\n';
+ };
+
+ /**
+ * Builds the surface to light code for the vertex shader.
+ * @return {string} The code for the vertex shader.
+ */
+ var surfaceToLightVertexShaderCode = function() {
+ return ' output.surfaceToLight = lightWorldPos - \n' +
+ ' mul(input.position, world).xyz;\n';
+ };
+
+ /**
+ * Builds the surface to view code for the vertex shader.
+ * @return {string} The code for the vertex shader.
+ */
+ var surfaceToViewVertexShaderCode = function() {
+ return ' output.surfaceToView = (viewInverse[3] - mul(input.position,\n' +
+ ' world)).xyz;\n';
+ };
+
+ /**
+ * Builds the normal map part of the vertex shader.
+ * @return {string} The code for normal mapping in the vertex shader.
+ */
+ var bumpVertexShaderCode = function() {
+ return bumpSampler ?
+ (' output.binormal = ' +
+ 'mul(float4(input.binormal, 0), worldInverseTranspose).xyz;\n' +
+ ' output.tangent = ' +
+ 'mul(float4(input.tangent, 0), worldInverseTranspose).xyz;\n') : '';
+ };
+
+ /**
+ * Builds the normal calculation of the pixel shader.
+ * @return {string} The code for normal computation in the pixel shader.
+ */
+ var getNormalShaderCode = function() {
+ return bumpSampler ?
+ ('float3x3 tangentToWorld = float3x3(input.tangent,\n' +
+ ' input.binormal,\n' +
+ ' input.normal);\n' +
+ 'float3 tangentNormal = tex2D(bumpSampler, input.bumpUV.xy).xyz -\n' +
+ ' float3(0.5, 0.5, 0.5);\n' +
+ 'float3 normal = mul(tangentNormal, tangentToWorld);\n' +
+ 'normal = normalize(normal);\n') :
+ ' float3 normal = normalize(input.normal);\n';
+ };
+
+ /**
+ * Builds the vertex declarations for a given material.
+ * @param {!o3d.Material} material The material to inspect.
+ * @param {boolean} diffuse Whether to include stuff for diffuse calculations.
+ * @param {boolean} specular Whether to include stuff for diffuse
+ * calculations.
+ * @return {string} The code for the vertex declarations.
+ */
+ var buildVertexDecls = function(material, diffuse, specular) {
+ var str = 'struct InVertex {\n' +
+ ' float4 position : POSITION;\n' +
+ ' float3 normal : NORMAL;\n' +
+ buildTexCoords(material) +
+ buildBumpInputCoords() +
+ '};\n' +
+ 'struct OutVertex {\n' +
+ ' float4 position : POSITION;\n' +
+ buildTexCoords(material) +
+ buildBumpOutputCoords();
+ if (diffuse || specular) {
+ str += ' float3 normal : TEXCOORD' + interpolant++ + ';\n' +
+ ' float3 surfaceToLight: TEXCOORD' + interpolant++ + ';\n';
+ }
+ if (specular) {
+ str += ' float3 surfaceToView : TEXCOORD' + interpolant++ + ';\n';
+ }
+ str += '};\n'
+ return str;
+ };
+
+ // Create a shader string of the appropriate type, based on the
+ // effectType.
+ var str;
+ var descriptions = [];
+ if (effectType == 'phong') {
+ str = buildPhongShaderString(material, descriptions);
+ } else if (effectType == 'lambert') {
+ str = buildLambertShaderString(material, descriptions);
+ } else if (effectType == 'blinn') {
+ str = buildBlinnShaderString(material, descriptions);
+ } else if (effectType == 'constant') {
+ str = buildConstantShaderString(material, descriptions);
+ } else {
+ throw ('unknown effect type "' + effectType + '"');
+ }
+
+ return {description: descriptions.join('_'), shader: str};
+};
+
+/**
+ * Gets or builds a shader for given standard COLLADA material type.
+ *
+ * Looks at the material passed in and assigns it an Effect that matches its
+ * Params. If a suitable Effect already exists in pack it will use that Effect.
+ *
+ * @param {!o3d.Pack} pack Pack in which to create the new Effect.
+ * @param {!o3d.Material} material Material for which to build the shader.
+ * @param {string} effectType Type of effect to create ('phong', 'lambert',
+ * 'constant').
+ * @return {o3d.Effect} The created effect.
+ */
+o3djs.effect.getStandardShader = function(pack,
+ material,
+ effectType) {
+ var record = o3djs.effect.buildStandardShaderString(material,
+ effectType);
+ var effects = pack.getObjectsByClassName('o3d.Effect');
+ for (var ii = 0; ii < effects.length; ++ii) {
+ if (effects[ii].name == record.description &&
+ effects[ii].source == record.shader) {
+ return effects[ii];
+ }
+ }
+ var effect = pack.createObject('Effect');
+ if (effect) {
+ effect.name = record.description;
+ if (effect.loadFromFXString(record.shader)) {
+ return effect;
+ }
+ pack.removeObject(effect);
+ }
+ return null;
+};
+
+/**
+ * Attaches a shader for a given standard COLLADA material type to the material.
+ *
+ * Looks at the material passed in and assigns it an Effect that matches its
+ * Params. If a suitable Effect already exists in pack it will use that Effect.
+ *
+ * @param {!o3d.Pack} pack Pack in which to create the new Effect.
+ * @param {!o3d.Material} material Material for which to build the shader.
+ * @param {!o3djs.math.Vector3} lightPos Position of the default light.
+ * @param {string} effectType Type of effect to create ('phong', 'lambert',
+ * 'constant').
+ * @return {boolean} True on success.
+ */
+o3djs.effect.attachStandardShader = function(pack,
+ material,
+ lightPos,
+ effectType) {
+ var effect = o3djs.effect.getStandardShader(pack,
+ material,
+ effectType);
+ if (effect) {
+ material.effect = effect;
+ effect.createUniformParameters(material);
+
+ // Set a couple of the default parameters in the hopes that this will
+ // help the user get something on the screen. We check to make sure they
+ // are not connected to something otherwise we'll get an error.
+ var param = material.getParam('lightWorldPos');
+ if (!param.inputConnection) {
+ param.value = lightPos;
+ }
+ var param = material.getParam('lightColor');
+ if (!param.inputConnection) {
+ param.value = [1, 1, 1, 1];
+ }
+ return true;
+ } else {
+ return false;
+ }
+};
+
diff --git a/o3d/samples/o3djs/element.js b/o3d/samples/o3djs/element.js
new file mode 100644
index 0000000..aedcacb
--- /dev/null
+++ b/o3d/samples/o3djs/element.js
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains various functions for helping setup
+ * elements for o3d
+ */
+
+o3djs.provide('o3djs.element');
+
+o3djs.require('o3djs.math');
+
+/**
+ * A Module for element functions.
+ * @namespace
+ */
+o3djs.element = o3djs.element || {};
+
+/**
+ * Sets the bounding box and z sort point of an element.
+ * @param {!o3d.Element} element Element to set bounding box and z sort point
+ * on.
+ */
+o3djs.element.setBoundingBoxAndZSortPoint = function(element) {
+ var boundingBox = element.getBoundingBox(0);
+ var minExtent = boundingBox.minExtent;
+ var maxExtent = boundingBox.maxExtent;
+ element.boundingBox = boundingBox;
+ element.cull = true;
+ element.zSortPoint = o3djs.math.divVectorScalar(
+ o3djs.math.addVector(minExtent, maxExtent), 2);
+};
+
+/**
+ * Adds missing texture coordinate streams to a primitive.
+ *
+ * This is very application specific but if it's a primitive
+ * and if it uses a collada material the material builder
+ * assumes 1 TEXCOORD stream per texture. In other words if you have
+ * both a specular texture AND a diffuse texture the builder assumes
+ * you have 2 TEXCOORD streams. This assumption is often false.
+ *
+ * To work around this we check how many streams the material
+ * expects and if there are not enough UVs streams we duplicate the
+ * last TEXCOORD stream until there are, making a BIG assumption that
+ * that will work.
+ *
+ * The problem is maybe you have 4 textures and each of them share
+ * texture coordinates. There is information in the collada file about
+ * what stream to connect each texture to.
+ *
+ * @param {!o3d.Element} element Element to add streams to.
+ */
+o3djs.element.addMissingTexCoordStreams = function(element) {
+ // TODO: We should store that info. The conditioner should either
+ // make streams that way or pass on the info so we can do it here.
+ if (element.isAClassName('o3d.Primitive')) {
+ var material = /** @type {!o3d.Material} */ (element.material);
+ var streamBank = element.streamBank;
+ var lightingType = o3djs.effect.getColladaLightingType(material);
+ if (lightingType) {
+ var numTexCoordStreamsNeeded =
+ o3djs.effect.getNumTexCoordStreamsNeeded(material);
+ // Count the number of TEXCOORD streams the streamBank has.
+ var streams = streamBank.vertexStreams;
+ var lastTexCoordStream = null;
+ var numTexCoordStreams = 0;
+ for (var ii = 0; ii < streams.length; ++ii) {
+ var stream = streams[ii];
+ if (stream.semantic == o3djs.base.o3d.Stream.TEXCOORD) {
+ lastTexCoordStream = stream;
+ ++numTexCoordStreams;
+ }
+ }
+ // Add any missing TEXCOORD streams. It might be more efficient for
+ // the GPU to create an effect that doesn't need the extra streams
+ // but this is a more generic solution because it means we can reuse
+ // the same effect.
+ for (var ii = numTexCoordStreams;
+ ii < numTexCoordStreamsNeeded;
+ ++ii) {
+ streamBank.setVertexStream(
+ lastTexCoordStream.semantic,
+ lastTexCoordStream.semanticIndex + ii - numTexCoordStreams + 1,
+ lastTexCoordStream.field,
+ lastTexCoordStream.startIndex);
+ }
+ }
+ }
+};
+
+/**
+ * Copies an element and streambank or buffers so the two will share
+ * streambanks, vertex and index buffers.
+ * @param {!o3d.Pack} pack Pack to manage created objects.
+ * @param {!o3d.Element} sourceElement The element to copy.
+ * @return {!o3d.Element} the new copy of sourceElement.
+ */
+o3djs.element.duplicateElement = function(pack, sourceElement) {
+ var newElement = pack.createObject(sourceElement.className);
+ newElement.copyParams(sourceElement);
+ // TODO: If we get the chance to parameterize the primitive's settings
+ // we can delete this code since copyParams will handle it.
+ // For now it only handles primitives by doing it manually.
+ if (sourceElement.isAClassName('o3d.Primitive')) {
+ newElement.indexBuffer = sourceElement.indexBuffer;
+ newElement.startIndex = sourceElement.startIndex;
+ newElement.primitiveType = sourceElement.primitiveType;
+ newElement.numberVertices = sourceElement.numberVertices;
+ newElement.numberPrimitives = sourceElement.numberPrimitives;
+ }
+ return newElement;
+};
+
+
diff --git a/o3d/samples/o3djs/error.js b/o3d/samples/o3djs/error.js
new file mode 100644
index 0000000..8fa16ad
--- /dev/null
+++ b/o3d/samples/o3djs/error.js
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains various error handing functions for o3d.
+ *
+ */
+
+o3djs.provide('o3djs.error');
+
+/**
+ * A Module with various error handing functions.
+ *
+ * This module is for helping to manage the client's error callback.
+ * Because you can not read the current callback on the client we wrap it with
+ * these utilities which track the last callback added so as long as you use
+ * o3djs.error.setErrorHandler(client, callback) instead of
+ * client.setErrorCallback you'll be able to get and restore the error callback
+ * when you need to.
+ * @namespace
+ */
+o3djs.error = o3djs.error || {};
+
+/**
+ * A map of error callbacks by client.
+ * @private
+ * @type {!Array.<(function(string): void|null)>}
+ */
+o3djs.error.callbacks_ = [];
+
+/**
+ * Sets the error handler on a client to a handler that manages the client's
+ * error callback.
+ * displays an alert on the first error.
+ * @param {!o3d.Client} client The client object of the plugin.
+ * @param {(function(string): void|null)} callback The callack to use, null to
+ * clear.
+ * @return {(function(string): void|null)} the previous error callback for this
+ * client.
+ */
+o3djs.error.setErrorHandler = function(client, callback) {
+ var clientId = client.clientId;
+ var old_callback = o3djs.error.callbacks_[clientId];
+ o3djs.error.callbacks_[clientId] = callback;
+ if (callback) {
+ client.setErrorCallback(callback);
+ } else {
+ client.clearErrorCallback();
+ }
+ return old_callback;
+};
+
+/**
+ * Sets a default error handler on the client.
+ * The default error handler displays an alert on the first error encountered.
+ * @param {!o3d.Client} client The client object of the plugin.
+ */
+o3djs.error.setDefaultErrorHandler = function(client) {
+ o3djs.error.setErrorHandler(
+ client,
+ function(msg) {
+ // Clear the error callback. Otherwise if the callback is happening
+ // during rendering it's possible the user will not be able to
+ // get out of an infinite loop of alerts.
+ o3djs.error.setErrorHandler(client, null);
+ alert('ERROR: ' + msg);
+ });
+};
+
+/**
+ * Creates an ErrorCollector.
+ * @param {!o3d.Client} client The client object of the plugin.
+ * @return {!o3djs.error.ErrorCollector} The created error collector.
+ */
+o3djs.error.createErrorCollector = function(client) {
+ return new o3djs.error.ErrorCollector(client);
+};
+
+/**
+ * An ErrorCollector takes over the client error callback and continues
+ * to collect errors until ErrorCollector.finish() is called.
+ * @constructor
+ * @param {!o3d.Client} client The client object of the plugin.
+ */
+o3djs.error.ErrorCollector = function(client) {
+ var that = this;
+ this.client_ = client;
+ /**
+ * The collected errors.
+ * @type {!Array.<string>}
+ */
+ this.errors = [];
+ this.oldCallback_ = o3djs.error.setErrorHandler(client, function(msg) {
+ that.errors.push(msg);
+ });
+};
+
+/**
+ * Stops the ErrorCollector from collecting errors and restores the previous
+ * error callback.
+ */
+o3djs.error.ErrorCollector.prototype.finish = function() {
+ o3djs.error.setErrorHandler(this.client_, this.oldCallback_);
+};
diff --git a/o3d/samples/o3djs/event.js b/o3d/samples/o3djs/event.js
new file mode 100644
index 0000000..2497bc6
--- /dev/null
+++ b/o3d/samples/o3djs/event.js
@@ -0,0 +1,360 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains various event related functions for
+ * o3d. It puts them in the 'event' module on the o3djs object.
+ *
+ * TODO Add selenium tests.
+ *
+ *
+ */
+o3djs.provide('o3djs.event');
+
+/**
+ * A Module for handling events related to o3d and various browsers.
+ * @namespace
+ */
+o3djs.event = o3djs.event || {};
+
+/**
+ * @param {string} inStr base string.
+ * @param {string} extraStr string to append.
+ * @return {string} inStr + ' ' + extraStr, or just inStr if extraStr is ''.
+ */
+o3djs.event.appendWithSpace = function(inStr, extraStr) {
+ return (inStr.length == 0) ? extraStr : inStr + ' ' + extraStr;
+};
+
+/**
+ * @param {boolean} state whether to append or not.
+ * @param {string} inStr base string.
+ * @param {string} extraStr string to append.
+ * @return {string} inStr + ' ' + extraStr, or just inStr if state is false.
+ */
+o3djs.event.appendWithSpaceIf = function(state, inStr, extraStr) {
+ return (state) ? o3djs.event.appendWithSpace(inStr, extraStr) : inStr;
+};
+
+
+/**
+ * Builds a DOM-level 3 modifier string for a KeyboardEvent - see
+ * http://www.w3.org/TR/DOM-Level-3-Events/events.html
+ * #Events-KeyboardEvents-Interfaces.
+ * @param {boolean} control whether the control key is down.
+ * @param {boolean} alt whether the alt/option key is down.
+ * @param {boolean} shift whether the shift key is down.
+ * @param {boolean} meta whether the meta/command key is down.
+ * @return {string} space delimited list of keys that are down.
+ */
+o3djs.event.getModifierString = function(control, alt, shift, meta) {
+ var modStr = o3djs.event.appendWithSpaceIf(control, '', 'Control');
+ modStr = o3djs.event.appendWithSpaceIf(alt, modStr, 'Alt');
+ modStr = o3djs.event.appendWithSpaceIf(shift, modStr, 'Shift');
+ return o3djs.event.appendWithSpaceIf(meta, modStr, 'Meta');
+};
+
+
+/**
+ * Pad a string with leading zeroes if needed until it is the length desired.
+ * @param {string} str The input string, probably representing a number.
+ * @param {number} to_length The desired minimum length of string with padding.
+ * @return {string} A string padded with leading zeroes as needed to be the
+ * length desired.
+ */
+o3djs.event.padWithLeadingZeroes = function(str, to_length) {
+ while (str.length < to_length)
+ str = '0' + str;
+ return str;
+};
+
+/**
+ * Creates a keyIdentifer string for a given keystroke as specified in the w3c
+ * spec on http://www.w3.org/TR/DOM-Level-3-Events/events.html.
+ * @param {number} charCode numeric unicode code point as reported by the OS.
+ * @param {number} keyCode numeric keyCode as reported by the OS, currently
+ * unused but will probably be necessary in the future.
+ * @return {string} eg 'Left' or 'U+0040'.
+ */
+o3djs.event.getKeyIdentifier = function(charCode, keyCode) {
+ if (!charCode) {
+ // TODO: This works for webkit for keydown and keyup, for basic
+ // alphanumeric keys, at least. Likely it needs lots of work to handle
+ // accented characters, various keyboards, etc., as does the rest of our
+ // keyboard event code.
+ charCode = keyCode;
+ }
+ switch (charCode) {
+ case 3: case 13: return 'Enter'; // spec merges these.
+ case 37: return 'Left';
+ case 39: return 'Right';
+ case 38: return 'Up';
+ case 40: return 'Down';
+ }
+ charCode = (charCode >= 97 && charCode <= 122) ? charCode - 32 : charCode;
+ var keyStr = charCode.toString(16).toUpperCase();
+ return 'U+' + o3djs.event.padWithLeadingZeroes(keyStr, 4);
+};
+
+
+/** Takes a keyIdentifier string and remaps it to an ASCII/Unicode value
+ * suitable for javascript event handling.
+ * @param {string} keyIdent a keyIdentifier string as generated above.
+ * @return {number} the numeric Unicode code point represented.
+ */
+o3djs.event.keyIdentifierToChar = function(keyIdent) {
+ if (keyIdent) {
+ switch (keyIdent) {
+ case 'Enter': return 13;
+ case 'Left': return 37;
+ case 'Right': return 39;
+ case 'Up': return 38;
+ case 'Down': return 40;
+ }
+ if (keyIdent.indexOf('U+') == 0)
+ return parseInt(keyIdent.substr(2).toUpperCase(), 16);
+ }
+ return 0;
+};
+
+/**
+ * Extracts the key char in number form from the event, in a cross-browser
+ * manner.
+ * @param {!Event} event .
+ * @return {number} unicode code point for the key.
+ */
+o3djs.event.getEventKeyChar = function(event) {
+ if (!event) {
+ event = window.event;
+ }
+ var charCode = 0;
+ if (event.keyIdentifier)
+ charCode = o3djs.event.keyIdentifierToChar(event.keyIdentifier);
+ if (!charCode)
+ charCode = (window.event) ? window.event.keyCode : event.charCode;
+ if (!charCode)
+ charCode = event.keyCode;
+ return charCode;
+};
+
+
+/**
+ * Cancel an event we've handled so it stops propagating upwards.
+ * The cancelBubble is for IE, stopPropagation is for all other browsers.
+ * preventDefault ensures that the default action is also canceled.
+ * @param {!Event} event - the event to cancel.
+ */
+o3djs.event.cancel = function(event) {
+ if (!event)
+ event = window.event;
+ event.cancelBubble = true;
+ if (event.stopPropagation)
+ event.stopPropagation();
+ if (event.preventDefault)
+ event.preventDefault();
+};
+
+/**
+ * Convenience function to setup synthesizing and dispatching of keyboard events
+ * whenever the focussed plug-in calls Javascript to report a keyboard action.
+ * @param {!Element} pluginObject the <object> where the o3d plugin lives,
+ * which the caller probably obtained by calling getElementById.
+ */
+o3djs.event.startKeyboardEventSynthesis = function(pluginObject) {
+ var handler = function(event) {
+ o3djs.event.onKey(event, pluginObject);
+ };
+
+ o3djs.event.addEventListener(pluginObject, 'keypress', handler);
+ o3djs.event.addEventListener(pluginObject, 'keydown', handler);
+ o3djs.event.addEventListener(pluginObject, 'keyup', handler);
+};
+
+/**
+ * Dispatches a DOM-level 3 KeyboardEvent when called back by the plugin.
+ * see http://www.w3.org/TR/DOM-Level-3-Events/events.html
+ * #Events-KeyboardEvents-Interfaces
+ * see http://developer.mozilla.org/en/DOM/event.initKeyEvent
+ * @param {!Element} event an O3D event object.
+ * @param {!o3d.Plugin} pluginObject the plugin object on the page.
+ */
+o3djs.event.onKey = function(event, pluginObject) {
+ var k_evt = o3djs.event.createKeyEvent(event.type, event.charCode,
+ event.keyCode, event.ctrlKey, event.altKey, event.shiftKey,
+ event.metaKey);
+ if (k_evt) {
+ if (pluginObject.parentNode.dispatchEvent) {
+ // Using the pluginObject itself fails for non-capturing event listeners
+ // on keypress events on Firefox only, as far as I've been able to
+ // determine. I have no idea why.
+ pluginObject.parentNode.dispatchEvent(k_evt);
+ } else if (pluginObject.fireEvent) {
+ pluginObject.fireEvent('on' + event.type, k_evt);
+ }
+ }
+};
+
+/**
+ * Creates a DOM-level 3 KeyboardEvent.
+ * see http://www.w3.org/TR/DOM-Level-3-Events/events.html
+ * #Events-KeyboardEvents-Interfaces.
+ * see http://developer.mozilla.org/en/DOM/event.initKeyEvent
+ * @param {string} eventName one of 'keypress', 'keydown' or 'keyup'.
+ * @param {number} charCode the character code for the key.
+ * @param {number} keyCode the key code for the key.
+ * @param {boolean} control whether the control key is down.
+ * @param {boolean} alt whether the alt/option key is down.
+ * @param {boolean} shift whether the shift key is down.
+ * @param {boolean} meta whether the meta/command key is down.
+ */
+o3djs.event.createKeyEvent = function(eventName, charCode, keyCode,
+ control, alt, shift, meta) {
+ var k_evt;
+ var keyIdentifier = o3djs.event.getKeyIdentifier(charCode, keyCode);
+ if (document.createEvent) {
+ k_evt = document.createEvent('KeyboardEvent');
+ if (k_evt.initKeyboardEvent) { // WebKit.
+ k_evt.initKeyboardEvent(eventName, true, true, window,
+ keyIdentifier, 0,
+ control, alt, shift, meta);
+ // TODO: These actually fail to do anything in Chrome; those are
+ // read-only fields, and it's not setting them in initKeyboardEvent.
+ k_evt.charCode = charCode;
+ if (eventName == 'keypress')
+ k_evt.keyCode = charCode;
+ else
+ k_evt.keyCode = keyCode;
+ } else if (k_evt.initKeyEvent) { // FF.
+ k_evt.initKeyEvent(eventName, true, true, window,
+ control, alt, shift, meta, keyCode, charCode);
+ k_evt.keyIdentifier = keyIdentifier;
+ }
+ } else if (document.createEventObject) {
+ k_evt = document.createEventObject();
+ k_evt.ctrlKey = control;
+ k_evt.altKey = alt;
+ k_evt.shiftKey = shift;
+ k_evt.metaKey = meta;
+ k_evt.keyCode = charCode; // Emulate IE charcode-in-the-keycode onkeypress.
+ k_evt.keyIdentifier = keyIdentifier;
+ }
+ k_evt.synthetic = true;
+ return k_evt;
+};
+
+/*
+ * Function to create a closure that will call each event handler in an array
+ * whenever it gets called, passing its single argument through to the
+ * sub-handlers. The sub-handlers may either be functions or EventListeners.
+ * This is generally expected to be used only through
+ * o3djs.event.addEventListener.
+ * @param {!Array.<*>} listenerSet an array of handlers.
+ * @return {!function(*): void} a closure to be used to multiplex out
+ * event-handling.
+ */
+o3djs.event.createEventHandler = function(listenerSet) {
+ return function(event) {
+ var length = listenerSet.length;
+ for (var index = 0; index < length; ++index) {
+ var handler = listenerSet[index];
+ if (typeof(handler.handleEvent) == 'function') {
+ handler.handleEvent(event);
+ } else {
+ handler(event);
+ }
+ }
+ }
+};
+
+/**
+ * Convenience function to manage event listeners on the o3d plugin object,
+ * intended as a drop-in replacement for the DOM addEventListener [with slightly
+ * different arguments, but the same effect].
+ * @param {!Element} pluginObject the <object> where the o3d plugin lives,
+ * which the caller probably obtained by calling getElementById.
+ * @param {string} type the event type on which to trigger, e.g. 'mousedown',
+ * 'mousemove', etc.
+ * @param {!Object} handler either a function or an EventListener object.
+ */
+o3djs.event.addEventListener = function(pluginObject, type, handler) {
+ if (!handler || typeof(type) != 'string' ||
+ (typeof(handler) != 'function' &&
+ typeof(handler.handleEvent) != 'function')) {
+ throw new Error('Invalid argument.');
+ }
+ pluginObject.o3d_eventRegistry = pluginObject.o3d_eventRegistry || [];
+ var registry = pluginObject.o3d_eventRegistry;
+ var listenerSet = registry[type];
+ if (!listenerSet || listenerSet.length == 0) {
+ listenerSet = registry[type] = [];
+ pluginObject.client.setEventCallback(type,
+ o3djs.event.createEventHandler(listenerSet));
+ } else {
+ for (var index in listenerSet) {
+ if (listenerSet[index] == handler) {
+ return; // We're idempotent.
+ }
+ }
+ }
+ listenerSet.push(handler);
+};
+
+
+/**
+ * Convenience function to manage event listeners on the o3d plugin object,
+ * intended as a drop-in replacement for the DOM removeEventListener [with
+ * slightly different arguments, but the same effect].
+ * @param {!Element} pluginObject the <object> where the o3d plugin lives,
+ * which the caller probably obtained by calling getElementById.
+ * @param {string} type the event type on which the handler to be removed was to
+ * trigger, e.g. 'mousedown', 'mousemove', etc.
+ * @param {!Object} handler either a function or an EventListener object.
+ */
+o3djs.event.removeEventListener = function(pluginObject, type, handler) {
+ var registry = pluginObject.o3d_eventRegistry;
+ if (!registry) {
+ return;
+ }
+ var listenerSet = registry[type];
+ if (!listenerSet) {
+ return;
+ }
+ for (var index in listenerSet) {
+ if (listenerSet[index] == handler) {
+ if (listenerSet.length == 1) {
+ pluginObject.client.clearEventCallback(type);
+ }
+ listenerSet.splice(index, 1);
+ break;
+ }
+ }
+};
diff --git a/o3d/samples/o3djs/fps.js b/o3d/samples/o3djs/fps.js
new file mode 100644
index 0000000..6bed43f
--- /dev/null
+++ b/o3d/samples/o3djs/fps.js
@@ -0,0 +1,469 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains a class for displaying frames per second.
+ */
+
+o3djs.provide('o3djs.fps');
+
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.canvas');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.primitives');
+
+/**
+ * A Module with a fps class for helping to easily display frames per second.
+ * @namespace
+ */
+o3djs.fps = o3djs.fps || {};
+
+/**
+ * Number of frames to average over for computing FPS.
+ * @type {number}
+ */
+o3djs.fps.NUM_FRAMES_TO_AVERAGE = 16;
+
+/**
+ * Colors used for each second of the performance bar.
+ * @type {!Array.<!o3djs.math.Vector4>}
+ */
+o3djs.fps.PERF_BAR_COLORS = [
+ [0, 0, 1, 1],
+ [0, 1, 0, 1],
+ [1, 1, 0, 1],
+ [1, 0.5, 0, 1],
+ [1, 0, 0, 1]];
+
+/**
+ * The shader code used by the pref quads.
+ * @type {string}
+ */
+o3djs.fps.CONST_COLOR_EFFECT =
+ 'float4x4 worldViewProjection : WorldViewProjection;\n' +
+ 'float4 color;\n' +
+ 'struct a2v {\n' +
+ ' float4 pos : POSITION;\n' +
+ '};\n'+
+ 'struct v2f {\n' +
+ ' float4 pos : POSITION;\n' +
+ '};\n' +
+ 'v2f vsMain(a2v IN) {\n' +
+ ' v2f OUT;\n' +
+ ' OUT.pos = mul(IN.pos, worldViewProjection);\n' +
+ ' return OUT;\n' +
+ '}\n' +
+ 'float4 psMain(v2f IN): COLOR {\n' +
+ ' return color;\n' +
+ '}\n' +
+ '// #o3d VertexShaderEntryPoint vsMain\n' +
+ '// #o3d PixelShaderEntryPoint psMain\n' +
+ '// #o3d MatrixLoadOrder RowMajor\n';
+
+/**
+ * Creates an object for displaying frames per second.
+ *
+ * You can use it like this.
+ * <pre>
+ * &lt;html&gt;&lt;body&gt;
+ * &lt;script type="text/javascript" src="o3djs/base.js"&gt;
+ * &lt;/script&gt;
+ * &lt;script type="text/javascript"&gt;
+ * o3djs.require('o3djs.util');
+ * o3djs.require('o3djs.rendergraph');
+ * o3djs.require('o3djs.fps');
+ * window.onload = init;
+ * window.onunload = uninit;
+ *
+ * var g_client;
+ * var g_fpsManager;
+ *
+ * function init() {
+ * o3djs.base.makeClients(initStep2);
+ * }
+ *
+ * function initStep2(clientElements) {
+ * var clientElement = clientElements[0];
+ * var g_client = clientElement.client;
+ * var pack = g_client.createPack();
+ * var viewInfo = o3djs.rendergraph.createBasicView(
+ * pack,
+ * g_client.root,
+ * g_client.renderGraphRoot);
+ * g_fpsManager = o3djs.fps.createFPSManager(pack,
+ * g_client.width,
+ * g_client.height,
+ * g_client.renderGraphRoot);
+ * g_client.setRenderCallback(onRender);
+ * }
+ *
+ * function onrender(renderEvent) {
+ * g_fpsManager.update(renderEvent);
+ * }
+ *
+ * function uninit() {
+ * if (g_client) {
+ * g_client.cleanup();
+ * }
+ * }
+ * &lt;/script&gt;
+ * &lt;div id="o3d" style="width: 600px; height: 600px"&gt;&lt;/div&gt;
+ * &lt;/body&gt;&lt;/html&gt;
+ * </pre>
+ *
+ *
+ * @constructor
+ * @param {!o3d.Pack} pack Pack to create objects in.
+ * @param {number} clientWidth width of client area.
+ * @param {number} clientHeight Height of client area.
+ * @param {!o3d.RenderNode} opt_parent RenderNode to use as parent for
+ * ViewInfo that will be used to render the FPS with.
+ */
+o3djs.fps.createFPSManager = function(pack,
+ clientWidth,
+ clientHeight,
+ opt_parent) {
+ return new o3djs.fps.FPSManager(pack, clientWidth, clientHeight, opt_parent);
+};
+
+/**
+ * A class for displaying frames per second.
+ * @constructor
+ * @param {!o3d.Pack} pack Pack to create objects in.
+ * @param {number} clientWidth width of client area.
+ * @param {number} clientHeight Height of client area.
+ * @param {!o3d.RenderNode} opt_parent RenderNode to use as parent for
+ * ViewInfo that will be used to render the FPS with.
+ */
+o3djs.fps.FPSManager = function(pack, clientWidth, clientHeight, opt_parent) {
+ // total time spent for last N frames.
+ this.totalTime_ = 0.0;
+
+ // total active time for last N frames.
+ this.totalActiveTime_ = 0.0;
+
+ // elapsed time for last N frames.
+ this.timeTable_ = [];
+
+ // active time for last N frames.
+ this.activeTimeTable_ = [];
+
+ // where to record next elapsed time.
+ this.timeTableCursor_ = 0;
+
+ // Initialize the FPS elapsed time history table.
+ for (var tt = 0; tt < o3djs.fps.NUM_FRAMES_TO_AVERAGE; ++tt) {
+ this.timeTable_[tt] = 0.0;
+ this.activeTimeTable_[tt] = 0.0;
+ }
+
+ // The root transform for this sub graph.
+ this.root_ = pack.createObject('Transform');
+
+ /**
+ * The ViewInfo to display FPS.
+ * @type {!o3djs.rendergraph.ViewInfo}
+ */
+ this.viewInfo = o3djs.rendergraph.createBasicView(pack,
+ this.root_,
+ opt_parent);
+ this.viewInfo.root.priority = 100000;
+ this.viewInfo.clearBuffer.clearColorFlag = false;
+
+ this.viewInfo.zOrderedState.getStateParam('CullMode').value =
+ o3djs.base.o3d.State.CULL_NONE;
+
+ this.viewInfo.drawContext.view = o3djs.math.matrix4.lookAt(
+ [0, 0, 1], // eye
+ [0, 0, 0], // target
+ [0, 1, 0]); // up
+
+ // create a view just for the FPS. That way it's indepdendent of other views.
+ this.canvasLib_ = o3djs.canvas.create(pack,
+ this.root_,
+ this.viewInfo);
+
+ this.paint_ = pack.createObject('CanvasPaint');
+
+ /**
+ * The quad used to display the FPS.
+ *
+ */
+ this.fpsQuad = this.canvasLib_.createXYQuad(0, 0, -1, 64, 32, true);
+
+ // create a unit plane with a const color effect we can use to draw
+ // rectangles.
+ this.colorEffect_ = pack.createObject('Effect');
+ this.colorEffect_.loadFromFXString(o3djs.fps.CONST_COLOR_EFFECT);
+ this.colorMaterial_ = pack.createObject('Material');
+ this.colorMaterial_.effect = this.colorEffect_;
+ this.colorMaterial_.drawList = this.viewInfo.zOrderedDrawList;
+ this.colorEffect_.createUniformParameters(this.colorMaterial_);
+ this.colorMaterial_.getParam('color').value = [1, 1, 1, 1];
+ this.colorQuadShape_ = o3djs.primitives.createPlane(
+ pack,
+ this.colorMaterial_,
+ 1,
+ 1,
+ 1,
+ 1,
+ [[1, 0, 0, 0],
+ [0, 0, 1, 0],
+ [0, -1, 0, 0],
+ [0.5, 0.5, 0, 1]]);
+
+ var barXOffset = 10;
+ var barYOffset = 2;
+ var barWidth = clientWidth - barXOffset * 2;
+ var barHeight = 7;
+ this.numPerfBars_ = o3djs.fps.PERF_BAR_COLORS.length - 1;
+ this.perfBarRoot_ = pack.createObject('Transform');
+ this.perfBarRoot_.parent = this.root_;
+ this.perfBarBack_ = new o3djs.fps.ColorRect(
+ pack, this.colorQuadShape_, this.perfBarRoot_,
+ barXOffset, barYOffset, -3, barWidth, barHeight,
+ [0, 0, 0, 1]);
+ this.perfMarker_ = [];
+ for (var ii = 0; ii < this.numPerfBars_; ++ii) {
+ this.perfMarker_[ii] = new o3djs.fps.ColorRect(
+ pack, this.colorQuadShape_, this.perfBarRoot_,
+ barXOffset + barWidth / (this.numPerfBars_ + 1) * (ii + 1),
+ barYOffset - 1, -1,
+ 1, barHeight + 2,
+ [1, 1, 1, 1]);
+ }
+ this.perfBar_ = new o3djs.fps.ColorRect(
+ pack, this.colorQuadShape_, this.perfBarRoot_,
+ barXOffset + 1, barYOffset + 1, -2, 1, barHeight - 2,
+ [1, 1, 0, 1]);
+ this.perfBarWidth_ = barWidth - 2;
+ this.perfBarHeight_ = barHeight - 2;
+ this.perfBarXOffset_ = barXOffset;
+ this.perfBarYOffset_ = barYOffset;
+
+ // set the size and position.
+ this.resize(clientWidth, clientHeight);
+ this.setPosition(10, 10);
+};
+
+/**
+ * Sets the position of the FPS display
+ *
+ * The position is in pixels assuming the size of the client matches the size
+ * last set either on creation or with FPSManager.resize.
+ *
+ * @param {number} x The x position.
+ * @param {number} y The y position.
+ */
+o3djs.fps.FPSManager.prototype.setPosition = function(x, y) {
+ this.fpsQuad.transform.identity();
+ this.fpsQuad.transform.translate(x, y, -1);
+};
+
+/**
+ * Sets the visiblity of the fps display.
+ * @param {boolean} visible true = visible.
+ */
+o3djs.fps.FPSManager.prototype.setVisible = function(visible) {
+ this.viewInfo.root.active = visible;
+};
+
+/**
+ * Sets the visibility of the performance bar.
+ * @param {boolean} visible true = visible.
+ */
+o3djs.fps.FPSManager.prototype.setPerfVisible = function(visible) {
+ this.perfBarRoot_.visible = visible;
+};
+
+/**
+ * Resizes the area for the FPS display.
+ * Note: you must call this if your client area changes size.
+ * @param {number} clientWidth width of client area.
+ * @param {number} clientHeight height of client area.
+ */
+o3djs.fps.FPSManager.prototype.resize = function(clientWidth, clientHeight) {
+ this.viewInfo.drawContext.projection = o3djs.math.matrix4.orthographic(
+ 0 + 0.5,
+ clientWidth + 0.5,
+ clientHeight + 0.5,
+ 0 + 0.5,
+ 0.001,
+ 1000);
+
+ var barWidth = clientWidth - this.perfBarXOffset_ * 2;
+ this.perfBarBack_.setSize(barWidth, this.perfBarHeight_);
+ for (var ii = 0; ii < this.numPerfBars_; ++ii) {
+ this.perfMarker_[ii].setPosition(
+ this.perfBarXOffset_ + barWidth / (this.numPerfBars_ + 1) * (ii + 1),
+ this.perfBarYOffset_ - 1);
+ }
+ this.perfBarWidth_ = barWidth - 2;
+};
+
+/**
+ * Updates the fps display.
+ * You must call this every frame to update the FPS display.
+ *
+ * <pre>
+ * ...
+ * client.setRenderCallback(onRender);
+ * ...
+ * function onRender(renderEvent) {
+ * myFpsManager.update(renderEvent);
+ * }
+ * </pre>
+ *
+ * @param {!o3d.RenderEvent} renderEvent The object passed into the render
+ * callback.
+ */
+o3djs.fps.FPSManager.prototype.update = function(renderEvent) {
+ var elapsedTime = renderEvent.elapsedTime;
+ var activeTime = renderEvent.activeTime;
+ // Keep the total time and total active time for the last N frames.
+ this.totalTime_ += elapsedTime - this.timeTable_[this.timeTableCursor_];
+ this.totalActiveTime_ +=
+ activeTime - this.activeTimeTable_[this.timeTableCursor_];
+
+ // Save off the elapsed time for this frame so we can subtract it later.
+ this.timeTable_[this.timeTableCursor_] = elapsedTime;
+ this.activeTimeTable_[this.timeTableCursor_] = activeTime;
+
+ // Wrap the place to store the next time sample.
+ ++this.timeTableCursor_;
+ if (this.timeTableCursor_ == o3djs.fps.NUM_FRAMES_TO_AVERAGE) {
+ this.timeTableCursor_ = 0;
+ }
+
+ // Print the average frame rate for the last N frames as well as the
+ // instantaneous frame rate.
+ var fps = '' +
+ Math.floor((1.0 / (this.totalTime_ /
+ o3djs.fps.NUM_FRAMES_TO_AVERAGE)) + 0.5) +
+ ' : ' + Math.floor(1.0 / elapsedTime + 0.5);
+
+ var canvas = this.fpsQuad.canvas;
+ canvas.clear([0, 0, 0, 0]);
+
+ var paint = this.paint_;
+
+ canvas.saveMatrix();
+ paint.setOutline(3, [0, 0, 0, 1]);
+ paint.textAlign = o3djs.base.o3d.CanvasPaint.LEFT;
+ paint.textSize = 12;
+ paint.textTypeface = 'Arial';
+ paint.color = [1, 1, 0, 1];
+ canvas.drawText(fps, 2, 16, paint);
+ canvas.restoreMatrix();
+
+ this.fpsQuad.updateTexture();
+
+ var frames = this.totalActiveTime_ / o3djs.fps.NUM_FRAMES_TO_AVERAGE /
+ (1 / 60.0);
+ var colorIndex = Math.min(frames, o3djs.fps.PERF_BAR_COLORS.length - 1);
+ colorIndex = Math.floor(Math.max(colorIndex, 0));
+
+ if (!isNaN(colorIndex)) {
+ this.perfBar_.setColor(o3djs.fps.PERF_BAR_COLORS[colorIndex]);
+ this.perfBar_.setSize(frames * this.perfBarWidth_ / this.numPerfBars_,
+ this.perfBarHeight_);
+ }
+};
+
+/**
+ * A Class the manages a color rect.
+ * @constructor
+ * @param {!o3d.Pack} pack Pack to create things in.
+ * @param {!o3d.Shape} shape Shape to use for rectangle.
+ * @param {!o3d.Transform} parent Transform to parent rect under.
+ * @param {number} x initial x position.
+ * @param {number} y initial y position.
+ * @param {number} z initial z position.
+ * @param {number} width initial width.
+ * @param {number} height initial height.
+ * @param {!o3djs.math.Vector4} color initial color.
+ */
+o3djs.fps.ColorRect = function(pack, shape, parent,
+ x, y, z, width, height, color) {
+ this.transform_ = pack.createObject('Transform');
+ this.colorParam_ = this.transform_.createParam('color', 'ParamFloat4');
+ this.transform_.addShape(shape);
+ this.transform_.parent = parent;
+ this.width_ = 0;
+ this.height_ = 0;
+ this.x_ = 0;
+ this.y_ = 0;
+ this.z_ = z;
+ this.setPosition(x, y);
+ this.setSize(width, height);
+ this.setColor(color);
+};
+
+/**
+ * Updates the transform of this ColorRect
+ * @private
+ */
+o3djs.fps.ColorRect.prototype.updateTransform_ = function() {
+ this.transform_.identity();
+ this.transform_.translate(this.x_, this.y_, this.z_);
+ this.transform_.scale(this.width_, this.height_, 1);
+};
+
+/**
+ * Sets the position of this ColorRect.
+ * @param {number} x x position.
+ * @param {number} y y position.
+ */
+o3djs.fps.ColorRect.prototype.setPosition = function(x, y) {
+ this.x_ = x;
+ this.y_ = y;
+ this.updateTransform_();
+};
+
+/**
+ * Sets the size of this ColorRect
+ * @param {number} width width.
+ * @param {number} height height.
+ */
+o3djs.fps.ColorRect.prototype.setSize = function(width, height) {
+ this.width_ = width;
+ this.height_ = height;
+ this.updateTransform_();
+};
+
+/**
+ * Sets the color of this ColorRect.
+ * @param {!o3djs.math.Vector4} color initial color.
+ */
+o3djs.fps.ColorRect.prototype.setColor = function(color) {
+ this.colorParam_.value = color;
+};
diff --git a/o3d/samples/o3djs/io.js b/o3d/samples/o3djs/io.js
new file mode 100644
index 0000000..2bd1a3a
--- /dev/null
+++ b/o3d/samples/o3djs/io.js
@@ -0,0 +1,577 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains various functions and class for io.
+ */
+
+o3djs.provide('o3djs.io');
+
+/**
+ * A Module with various io functions and classes.
+ * @namespace
+ */
+o3djs.io = o3djs.io || {};
+
+/**
+ * Creates a LoadInfo object.
+ * @param {(!o3d.ArchiveRequest|!o3d.FileRequest|!XMLHttpRequest)} opt_request
+ * The request to watch.
+ * @param {boolean} opt_hasStatus true if opt_request is a
+ * o3d.ArchiveRequest vs for example an o3d.FileRequest or an
+ * XMLHttpRequest.
+ * @return {!o3djs.io.LoadInfo} The new LoadInfo.
+ * @see o3djs.io.LoadInfo
+ */
+o3djs.io.createLoadInfo = function(opt_request, opt_hasStatus) {
+ return new o3djs.io.LoadInfo(opt_request, opt_hasStatus);
+};
+
+/**
+ * A class to help with progress reporting for most loading utilities.
+ *
+ * Example:
+ * <pre>
+ * var g_loadInfo = null;
+ * g_id = window.setInterval(statusUpdate, 500);
+ * g_loadInfo = o3djs.scene.loadScene(client, pack, parent,
+ * 'http://google.com/somescene.o3dtgz',
+ * callback);
+ *
+ * function callback(pack, parent, exception) {
+ * g_loadInfo = null;
+ * window.clearInterval(g_id);
+ * if (!exception) {
+ * // do something with scene just loaded
+ * }
+ * }
+ *
+ * function statusUpdate() {
+ * if (g_loadInfo) {
+ * var progress = g_loadInfo.getKnownProgressInfoSoFar();
+ * document.getElementById('loadstatus').innerHTML = progress.percent;
+ * }
+ * }
+ * </pre>
+ *
+ * @constructor
+ * @param {(!o3d.ArchiveRequest|!o3d.FileRequest|!XMLHttpRequest)} opt_request
+ * The request to watch.
+ * @param {boolean} opt_hasStatus true if opt_request is a
+ * o3d.ArchiveRequest vs for example an o3d.FileRequest or an
+ * XMLHttpRequest.
+ * @see o3djs.scene.loadScene
+ * @see o3djs.io.loadArchive
+ * @see o3djs.io.loadTexture
+ * @see o3djs.loader.Loader
+ */
+o3djs.io.LoadInfo = function(opt_request, opt_hasStatus) {
+ this.request_ = opt_request;
+ this.hasStatus_ = opt_hasStatus;
+ this.streamLength_ = 0; // because the request may have been freed.
+ this.children_ = [];
+};
+
+/**
+ * Adds another LoadInfo as a child of this LoadInfo so they can be
+ * managed as a group.
+ * @param {!o3djs.io.LoadInfo} loadInfo The child LoadInfo.
+ */
+o3djs.io.LoadInfo.prototype.addChild = function(loadInfo) {
+ this.children_.push(loadInfo);
+};
+
+/**
+ * Marks this LoadInfo as finished.
+ */
+o3djs.io.LoadInfo.prototype.finish = function() {
+ if (this.request_) {
+ if (this.hasStatus_) {
+ this.streamLength_ = this.request_.streamLength;
+ }
+ this.request_ = null;
+ }
+};
+
+/**
+ * Gets the total bytes that will be streamed known so far.
+ * If you are only streaming 1 file then this will be the info for that file but
+ * if you have queued up many files using an o3djs.loader.Loader only a couple of
+ * files are streamed at a time meaning that the size is not known for files
+ * that have yet started to download.
+ *
+ * If you are downloading many files for your application and you want to
+ * provide a progress status you have about 4 options
+ *
+ * 1) Use LoadInfo.getTotalBytesDownloaded() /
+ * LoadInfo.getTotalKnownBytesToStreamSoFar() and just be aware the bar will
+ * grown and then shrink as new files start to download and their lengths
+ * become known.
+ *
+ * 2) Use LoadInfo.getTotalRequestsDownloaded() /
+ * LoadInfo.getTotalKnownRequestsToStreamSoFar() and be aware the granularity
+ * is not all that great since it only reports fully downloaded files. If you
+ * are downloading a bunch of small files this might be ok.
+ *
+ * 3) Put all your files in one archive. Then there will be only one file and
+ * method 1 will work well.
+ *
+ * 4) Figure out the total size in bytes of the files you will download and put
+ * that number in your application, then use LoadInfo.getTotalBytesDownloaded()
+ * / MY_APPS_TOTAL_BYTES_TO_DOWNLOAD.
+ *
+ * @return {number} The total number of currently known bytes to be streamed.
+ */
+o3djs.io.LoadInfo.prototype.getTotalKnownBytesToStreamSoFar = function() {
+ if (!this.streamLength_ && this.request_ && this.hasStatus_) {
+ this.streamLength_ = this.request_.streamLength;
+ }
+ var total = this.streamLength_;
+ for (var cc = 0; cc < this.children_.length; ++cc) {
+ total += this.children_[cc].getTotalKnownBytesToStreamSoFar();
+ }
+ return total;
+};
+
+/**
+ * Gets the total bytes downloaded so far.
+ * @return {number} The total number of currently known bytes to be streamed.
+ */
+o3djs.io.LoadInfo.prototype.getTotalBytesDownloaded = function() {
+ var total = (this.request_ && this.hasStatus_) ?
+ this.request_.bytesReceived : this.streamLength_;
+ for (var cc = 0; cc < this.children_.length; ++cc) {
+ total += this.children_[cc].getTotalBytesDownloaded();
+ }
+ return total;
+};
+
+/**
+ * Gets the total streams that will be download known so far.
+ * We can't know all the streams since you could use an o3djs.loader.Loader
+ * object, request some streams, then call this function, then request some
+ * more.
+ *
+ * See LoadInfo.getTotalKnownBytesToStreamSoFar for details.
+ * @return {number} The total number of requests currently known to be streamed.
+ * @see o3djs.io.LoadInfo.getTotalKnownBytesToStreamSoFar
+ */
+o3djs.io.LoadInfo.prototype.getTotalKnownRequestsToStreamSoFar = function() {
+ var total = 1;
+ for (var cc = 0; cc < this.children_.length; ++cc) {
+ total += this.children_[cc].getTotalKnownRequestToStreamSoFar();
+ }
+ return total;
+};
+
+/**
+ * Gets the total requests downloaded so far.
+ * @return {number} The total requests downloaded so far.
+ */
+o3djs.io.LoadInfo.prototype.getTotalRequestsDownloaded = function() {
+ var total = this.request_ ? 0 : 1;
+ for (var cc = 0; cc < this.children_.length; ++cc) {
+ total += this.children_[cc].getTotalRequestsDownloaded();
+ }
+ return total;
+};
+
+/**
+ * Gets progress info.
+ * This is commonly formatted version of the information available from a
+ * LoadInfo.
+ *
+ * See LoadInfo.getTotalKnownBytesToStreamSoFar for details.
+ * @return {{percent: number, downloaded: string, totalBytes: string,
+ * base: number, suffix: string}} progress info.
+ * @see o3djs.io.LoadInfo.getTotalKnownBytesToStreamSoFar
+ */
+o3djs.io.LoadInfo.prototype.getKnownProgressInfoSoFar = function() {
+ var percent = 0;
+ var bytesToDownload = this.getTotalKnownBytesToStreamSoFar();
+ var bytesDownloaded = this.getTotalBytesDownloaded();
+ if (bytesToDownload > 0) {
+ percent = Math.floor(bytesDownloaded / bytesToDownload * 100);
+ }
+
+ var base = (bytesToDownload < 1024 * 1024) ? 1024 : (1024 * 1024);
+
+ return {
+ percent: percent,
+ downloaded: (bytesDownloaded / base).toFixed(2),
+ totalBytes: (bytesToDownload / base).toFixed(2),
+ base: base,
+ suffix: (base == 1024 ? 'kb' : 'mb')}
+
+};
+
+/**
+ * Loads text from an external file. This function is synchronous.
+ * @param {string} url The url of the external file.
+ * @return {string} the loaded text if the request is synchronous.
+ */
+o3djs.io.loadTextFileSynchronous = function(url) {
+ o3djs.BROWSER_ONLY = true;
+
+ var error = 'loadTextFileSynchronous failed to load url "' + url + '"';
+ var request;
+ if (!o3djs.base.IsMSIE() && window.XMLHttpRequest) {
+ request = new XMLHttpRequest();
+ if (request.overrideMimeType) {
+ request.overrideMimeType('text/plain');
+ }
+ } else if (window.ActiveXObject) {
+ request = new ActiveXObject('MSXML2.XMLHTTP.3.0');
+ } else {
+ throw 'XMLHttpRequest is disabled';
+ }
+ request.open('GET', url, false);
+ request.send(null);
+ if (request.readyState != 4) {
+ throw error;
+ }
+ return request.responseText;
+};
+
+/**
+ * Loads text from an external file. This function is asynchronous.
+ * @param {string} url The url of the external file.
+ * @param {function(string, *): void} callback A callback passed the loaded
+ * string and an exception which will be null on success.
+ * @return {!o3djs.io.LoadInfo} A LoadInfo to track progress.
+ */
+o3djs.io.loadTextFile = function(url, callback) {
+ o3djs.BROWSER_ONLY = true;
+
+ var error = 'loadTextFile failed to load url "' + url + '"';
+ var request;
+ if (!o3djs.base.IsMSIE() && window.XMLHttpRequest) {
+ request = new XMLHttpRequest();
+ if (request.overrideMimeType) {
+ request.overrideMimeType('text/plain');
+ }
+ } else if (window.ActiveXObject) {
+ request = new ActiveXObject('MSXML2.XMLHTTP.3.0');
+ } else {
+ throw 'XMLHttpRequest is disabled';
+ }
+ var loadInfo = o3djs.io.createLoadInfo(request, false);
+ request.open('GET', url, true);
+ var finish = function() {
+ if (request.readyState == 4) {
+ var text = '';
+ // HTTP reports success with a 200 status. The file protocol reports
+ // success with zero. HTTP does not use zero as a status code (they
+ // start at 100).
+ // https://developer.mozilla.org/En/Using_XMLHttpRequest
+ var success = request.status == 200 || request.status == 0;
+ if (success) {
+ text = request.responseText;
+ }
+ loadInfo.finish();
+ callback(text, success ? null : 'could not load: ' + url);
+ }
+ };
+ request.onreadystatechange = finish;
+ request.send(null);
+ return loadInfo;
+};
+
+/**
+ * A ArchiveInfo object loads and manages an archive of files.
+ * There are methods for locating a file by uri and for freeing
+ * the archive.
+ *
+ * You can only read archives that have as their first file a file named
+ * 'aaaaaaaa.o3d' the contents of the which is 'o3d'. This is to prevent O3D
+ * from being used to read arbitrary tar gz files.
+ *
+ * Example:
+ * <pre>
+ * var loadInfo = o3djs.io.loadArchive(pack,
+ * 'http://google.com/files.o3dtgz',
+ * callback);
+ *
+ * function callback(archiveInfo, exception) {
+ * if (!exception) {
+ * pack.createTextureFromRawData(
+ * archiveInfo.getFileByURI('logo.jpg'), true);
+ * pack.createTextureFromRawData(
+ * archiveInfo.getFileByURI('wood/oak.png'), true);
+ * pack.createTextureFromRawData(
+ * archiveInfo.getFileByURI('wood/maple.dds'), true);
+ * archiveInfo.destroy();
+ * } else {
+ * alert(exception);
+ * }
+ * }
+ * </pre>
+ *
+ * @constructor
+ * @param {!o3d.Pack} pack Pack to create request in.
+ * @param {string} url The url of the archive file.
+ * @param {!function(!o3djs.io.ArchiveInfo, *): void} onFinished A
+ * callback that is called when the archive is finished loading and passed
+ * the ArchiveInfo and an exception which is null on success.
+ */
+o3djs.io.ArchiveInfo = function(pack, url, onFinished) {
+ var that = this;
+
+ /**
+ * The list of files in the archive by uri.
+ * @type {!Object}
+ */
+ this.files = {};
+
+ /**
+ * The pack used to create the archive request.
+ * @type {!o3d.Pack}
+ */
+ this.pack = pack;
+
+ /**
+ * True if this archive has not be destroyed.
+ * @type {boolean}
+ */
+ this.destroyed = false;
+
+ this.request_ = null;
+
+ /**
+ * Records each RawData file as it comes in.
+ * @private
+ * @param {!o3d.RawData} rawData RawData from archive request.
+ */
+ function addFile(rawData) {
+ that.files[rawData.uri] = rawData;
+ }
+
+ /**
+ * The LoadInfo to track loading progress.
+ * @type {!o3djs.io.LoadInfo}
+ */
+ this.loadInfo = o3djs.io.loadArchiveAdvanced(
+ pack,
+ url,
+ addFile,
+ function(request, exception) {
+ that.request_ = request;
+ onFinished(that, exception);
+ });
+};
+
+/**
+ * Releases all the RAW data associated with this archive. It does not release
+ * any objects created from that RAW data.
+ */
+o3djs.io.ArchiveInfo.prototype.destroy = function() {
+ if (!this.destroyed) {
+ this.pack.removeObject(this.request_);
+ this.destroyed = true;
+ this.files = {};
+ }
+};
+
+/**
+ * Gets files by regular expression or wildcards from the archive.
+ * @param {(string|!RegExp)} uri of file to get. Can have wildcards '*' and '?'.
+ * @param {boolean} opt_caseInsensitive Only valid if it's a wildcard string.
+ * @return {!Array.<!o3d.RawData>} An array of the matching RawDatas for
+ * the files matching or undefined if it doesn't exist.
+ */
+o3djs.io.ArchiveInfo.prototype.getFiles = function(uri,
+ opt_caseInsensitive) {
+ if (!(uri instanceof RegExp)) {
+ uri = uri.replace(/(\W)/g, '\\$&');
+ uri = uri.replace(/\\\*/g, '.*');
+ uri = uri.replace(/\\\?/g, '.');
+ uri = new RegExp(uri, opt_caseInsensitive ? 'i' : '');
+ }
+ var files = [];
+ for (var key in this.files) {
+ if (uri.test(key)) {
+ files.push(this.files[key]);
+ }
+ }
+
+ return files;
+};
+
+/**
+ * Gets a file by URI from the archive.
+ * @param {string} uri of file to get.
+ * @param {boolean} opt_caseInsensitive True to be case insensitive. Default
+ * false.
+ * @return {(o3d.RawData|undefined)} The RawData for the file or undefined if
+ * it doesn't exist.
+ */
+o3djs.io.ArchiveInfo.prototype.getFileByURI = function(
+ uri,
+ opt_caseInsensitive) {
+ if (opt_caseInsensitive) {
+ uri = uri.toLowerCase();
+ for (var key in this.files) {
+ if (key.toLowerCase() == uri) {
+ return this.files[key];
+ }
+ }
+ return undefined;
+ } else {
+ return this.files[uri];
+ }
+};
+
+/**
+ * Loads an archive file.
+ * When the entire archive is ready the onFinished callback will be called
+ * with an ArchiveInfo for accessing the archive.
+ *
+ * @param {!o3d.Pack} pack Pack to create request in.
+ * @param {string} url The url of the archive file.
+ * @param {!function(!o3djs.io.ArchiveInfo, *): void} onFinished A
+ * callback that is called when the archive is successfully loaded and an
+ * Exception object which is null on success.
+ * @return {!o3djs.io.LoadInfo} The a LoadInfo for tracking progress.
+ * @see o3djs.io.ArchiveInfo
+ */
+o3djs.io.loadArchive = function(pack,
+ url,
+ onFinished) {
+ var archiveInfo = new o3djs.io.ArchiveInfo(pack, url, onFinished);
+ return archiveInfo.loadInfo;
+};
+
+/**
+ * Loads an archive file. This function is asynchronous. This is a low level
+ * version of o3djs.io.loadArchive which can be used for things like
+ * progressive loading.
+ *
+ * @param {!o3d.Pack} pack Pack to create request in.
+ * @param {string} url The url of the archive file.
+ * @param {!function(!o3d.RawData): void} onFileAvailable A callback, taking a
+ * single argument 'data'. As each file is loaded from the archive, this
+ * function is called with the file's data.
+ * @param {!function(!o3d.ArchiveRequest, *): void} onFinished
+ * A callback that is called when the archive is successfully loaded. It is
+ * passed the ArchiveRquest and null on success or a javascript exception on
+ * failure.
+ * @return {!o3djs.io.LoadInfo} A LoadInfo for tracking progress.
+ */
+o3djs.io.loadArchiveAdvanced = function(pack,
+ url,
+ onFileAvailable,
+ onFinished) {
+ var error = 'loadArchive failed to load url "' + url + '"';
+ var request = pack.createArchiveRequest();
+ var loadInfo = o3djs.io.createLoadInfo(request, true);
+ request.open('GET', url);
+ request.onfileavailable = function() {
+ onFileAvailable(/** @type {!o3d.RawData} */ (request.data));
+ };
+ /**
+ * @ignore
+ */
+ request.onreadystatechange = function() {
+ if (request.done) {
+ loadInfo.finish();
+ var success = request.success;
+ var exception = null;
+ if (!success) {
+ exception = request.error;
+ if (!exception) {
+ exception = 'unknown error loading archive';
+ }
+ }
+ onFinished(request, exception);
+ }
+ };
+ request.send();
+ return loadInfo;
+};
+
+/**
+ * Loads a texture.
+ *
+ * Textures are loaded asynchronously.
+ *
+ * Example:
+ * <pre>
+ * var loadInfo = o3djs.io.loadTexture(pack,
+ * 'http://google.com/someimage.jpg',
+ * callback);
+ *
+ * function callback(texture, exception) {
+ * if (!exception) {
+ * g_mySampler.texture = texture;
+ * } else {
+ * alert(exception);
+ * }
+ * }
+ * </pre>
+ *
+ *
+ * @param {!o3d.Pack} pack Pack to load texture into.
+ * @param {string} url URL of texture to load.
+ * @param {!function(o3d.Texture, *): void} callback Callback when
+ * texture is loaded. It will be passed the texture and an exception on
+ * error or null on success.
+ * @return {!o3djs.io.LoadInfo} A LoadInfo to track progress.
+ * @see o3djs.io.createLoader
+ */
+o3djs.io.loadTexture = function(pack, url, callback) {
+ // TODO: change this to get use RawData and Bitmap
+ var request = pack.createFileRequest('TEXTURE');
+ var loadInfo = o3djs.io.createLoadInfo(
+ /** @type {!o3d.FileRequest} */ (request),
+ false);
+ request.open('GET', url, true);
+ /**
+ * @ignore
+ */
+ request.onreadystatechange = function() {
+ if (request.done) {
+ var texture = request.texture;
+ var success = request.success;
+ var exception = request.error;
+ loadInfo.finish();
+ pack.removeObject(request);
+ if (!success && !exception) {
+ exception = 'unknown error loading texture';
+ }
+ callback(texture, success ? null : exception);
+ }
+ };
+ request.send();
+ return loadInfo;
+};
+
+
diff --git a/o3d/samples/o3djs/js_list.scons b/o3d/samples/o3djs/js_list.scons
new file mode 100644
index 0000000..5a0da33
--- /dev/null
+++ b/o3d/samples/o3djs/js_list.scons
@@ -0,0 +1,61 @@
+# Copyright 2009, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+O3D_JS_SOURCES = [
+ 'arcball.js',
+ 'base.js',
+ 'camera.js',
+ 'canvas.js',
+ 'debug.js',
+ 'dump.js',
+ 'effect.js',
+ 'element.js',
+ 'error.js',
+ 'event.js',
+ 'fps.js',
+ 'io.js',
+ 'loader.js',
+ 'material.js',
+ 'math.js',
+ 'pack.js',
+ 'particles.js',
+ 'picking.js',
+ 'primitives.js',
+ 'quaternions.js',
+ 'rendergraph.js',
+ 'scene.js',
+ 'serialization.js',
+ 'shape.js',
+ 'simple.js',
+ 'test.js',
+ 'util.js',
+]
+
+Export('O3D_JS_SOURCES');
+
diff --git a/o3d/samples/o3djs/loader.js b/o3d/samples/o3djs/loader.js
new file mode 100644
index 0000000..abfd1d9
--- /dev/null
+++ b/o3d/samples/o3djs/loader.js
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains a loader class for helping to load
+ * muliple assets in an asynchronous manner.
+ */
+
+o3djs.provide('o3djs.loader');
+
+o3djs.require('o3djs.io');
+o3djs.require('o3djs.scene');
+
+/**
+ * A Module with a loader class for helping to load muliple assets in an
+ * asynchronous manner.
+ * @namespace
+ */
+o3djs.loader = o3djs.loader || {};
+
+/**
+ * A simple Loader class to call some callback when everything has loaded.
+ * @constructor
+ * @param {!function(): void} onFinished Function to call when final item has
+ * loaded.
+ */
+o3djs.loader.Loader = function(onFinished) {
+ this.count_ = 1;
+ this.onFinished_ = onFinished;
+
+ /**
+ * The LoadInfo for this loader you can use to track progress.
+ * @type {!o3djs.io.LoadInfo}
+ */
+ this.loadInfo = o3djs.io.createLoadInfo();
+};
+
+/**
+ * Creates a Loader for helping to load a bunch of items asychronously.
+ *
+ * The way you use this is as follows.
+ *
+ * <pre>
+ * var loader = o3djs.loader.createLoader(myFinishedCallback);
+ * loader.loadTexture(pack, texture1Url, callbackForTexture);
+ * loader.loadTexture(pack, texture2Url, callbackForTexture);
+ * loader.loadTexture(pack, texture3Url, callbackForTexture);
+ * loader.finish();
+ * </pre>
+ *
+ * The loader guarantees that myFinishedCallback will be called after
+ * all the items have been loaded.
+ *
+* @param {!function(): void} onFinished Function to call when final item has
+* loaded.
+* @return {!o3djs.loader.Loader} A Loader Object.
+ */
+o3djs.loader.createLoader = function(onFinished) {
+ return new o3djs.loader.Loader(onFinished);
+};
+
+/**
+ * Loads a texture.
+ * @param {!o3d.Pack} pack Pack to load texture into.
+ * @param {string} url URL of texture to load.
+ * @param {!function(o3d.Texture, *): void} opt_onTextureLoaded
+ * optional callback when texture is loaded. It will be passed the texture
+ * and an exception which is null on success.
+ */
+o3djs.loader.Loader.prototype.loadTexture = function(pack,
+ url,
+ opt_onTextureLoaded) {
+ var that = this; // so the function below can see "this".
+ ++this.count_;
+ var loadInfo = o3djs.io.loadTexture(pack, url, function(texture, exception) {
+ if (opt_onTextureLoaded) {
+ opt_onTextureLoaded(texture, exception);
+ }
+ that.countDown_();
+ });
+ this.loadInfo.addChild(loadInfo);
+};
+
+/**
+ * Loads a 3d scene.
+ * @param {!o3d.Client} client An O3D client object.
+ * @param {!o3d.Pack} pack Pack to load texture into.
+ * @param {!o3d.Transform} parent Transform to parent scene under.
+ * @param {string} url URL of scene to load.
+ * @param {!function(!o3d.Pack, !o3d.Transform, *): void}
+ * opt_onSceneLoaded optional callback when scene is loaded. It will be
+ * passed the pack and parent and an exception which is null on success.
+ */
+o3djs.loader.Loader.prototype.loadScene = function(client,
+ pack,
+ parent,
+ url,
+ opt_onSceneLoaded) {
+ var that = this; // so the function below can see "this".
+ ++this.count_;
+ var loadInfo = o3djs.scene.loadScene(
+ client, pack, parent, url, function(pack, parent, exception) {
+ if (opt_onSceneLoaded) {
+ opt_onSceneLoaded(pack, parent, exception);
+ }
+ that.countDown_();
+ });
+ this.loadInfo.addChild(loadInfo);
+};
+
+/**
+ * Loads a text file.
+ * @param {string} url URL of scene to load.
+ * @param {!function(string, *): void} onTextLoaded Function to call when
+ * the file is loaded. It will be passed the contents of the file as a
+ * string and an exception which is null on success.
+ */
+o3djs.loader.Loader.prototype.loadTextFile = function(url, onTextLoaded) {
+ var that = this; // so the function below can see "this".
+ ++this.count_;
+ var loadInfo = o3djs.io.loadTextFile(url, function(string, exception) {
+ onTextLoaded(string, exception);
+ that.countDown_();
+ });
+ this.loadInfo.addChild(loadInfo);
+};
+
+/**
+ * Creates a loader that is tracked by this loader so that when the new loader
+ * is finished it will be reported to this loader.
+ * @param {!function(): void} onFinished Function to be called when everything
+ * loaded with this loader has finished.
+ * @return {!o3djs.loader.Loader} The new Loader.
+ */
+o3djs.loader.Loader.prototype.createLoader = function(onFinished) {
+ var that = this;
+ ++this.count_;
+ var loader = o3djs.loader.createLoader(function() {
+ onFinished();
+ that.countDown_();
+ });
+ this.loadInfo.addChild(loader.loadInfo);
+ return loader;
+};
+
+/**
+ * Counts down the internal count and if it gets to zero calls the callback.
+ * @private
+ */
+o3djs.loader.Loader.prototype.countDown_ = function() {
+ --this.count_;
+ if (this.count_ === 0) {
+ this.onFinished_();
+ }
+};
+
+/**
+ * Finishes the loading process.
+ * Actually this just calls countDown_ to account for the count starting at 1.
+ */
+o3djs.loader.Loader.prototype.finish = function() {
+ this.countDown_();
+};
+
+
diff --git a/o3d/samples/o3djs/material.js b/o3d/samples/o3djs/material.js
new file mode 100644
index 0000000..8e0f00a
--- /dev/null
+++ b/o3d/samples/o3djs/material.js
@@ -0,0 +1,461 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains various functions for helping setup
+ * materials for o3d. It puts them in the "material" module on the
+ * o3djs object.
+ *
+ * Note: This library is only a sample. It is not meant to be some official
+ * library. It is provided only as example code.
+ *
+ */
+
+o3djs.provide('o3djs.material');
+
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.effect');
+
+/**
+ * A Module for materials.
+ * @namespace
+ */
+o3djs.material = o3djs.material || {};
+
+/**
+ * Checks a material's params by name to see if it possibly has non 1.0 alpha.
+ * Given a name, checks for a ParamTexture called 'nameTexture' and if that
+ * fails, checks for a ParamFloat4 'name'.
+ * @private
+ * @param {!o3d.Material} material Materal to check.
+ * @param {string} name name of color params to check.
+ * @return {{found: boolean, nonOneAlpha: boolean}} found is true if one of
+ * the params was found, nonOneAlpha is true if that param had non 1.0
+ * alpha.
+ */
+o3djs.material.hasNonOneAlpha_ = function(material, name) {
+ var found = false;
+ var nonOneAlpha = false;
+ var texture = null;
+ var samplerParam = material.getParam(name + 'Sampler');
+ if (samplerParam && samplerParam.isAClassName('o3d.ParamSampler')) {
+ found = true;
+ var sampler = samplerParam.value;
+ if (sampler) {
+ texture = sampler.texture;
+ }
+ } else {
+ var textureParam = material.getParam(name + 'Texture');
+ if (textureParam && textureParam.isAClassName('o3d.ParamTexture')) {
+ found = true;
+ texture = textureParam.value;
+ }
+ }
+
+ if (texture && !texture.alphaIsOne) {
+ nonOneAlpha = true;
+ }
+
+ if (!found) {
+ var colorParam = material.getParam(name);
+ if (colorParam && colorParam.isAClassName('o3d.ParamFloat4')) {
+ found = true;
+ // TODO: this check does not work. We need to check for the
+ // <transparency> and <transparent> elements or something.
+ // if (colorParam.value[3] < 1) {
+ // nonOneAlpha = true;
+ // }
+ }
+ }
+ return {found: found, nonOneAlpha: nonOneAlpha};
+};
+
+/**
+ * Prepares a material by setting their drawList and possibly creating
+ * an standard effect if one does not already exist.
+ *
+ * This function is very specific to our sample importer. It expects that if
+ * no Effect exists on a material that certain extra Params have been created
+ * on the Material to give us instructions on what to Effects to create.
+ *
+ * @param {!o3d.Pack} pack Pack to manage created objects.
+ * @param {!o3djs.rendergraph.ViewInfo} viewInfo as returned from
+ * o3djs.rendergraph.createView.
+ * @param {!o3d.Material} material to prepare.
+ * @param {string} opt_effectType type of effect to create ('phong',
+ * 'lambert', 'constant').
+ *
+ * @see o3djs.material.attachStandardEffect
+ */
+o3djs.material.prepareMaterial = function(pack,
+ viewInfo,
+ material,
+ opt_effectType) {
+ // Assume we want the performance list
+ var drawList = viewInfo.performanceDrawList;
+ // First check if we have a tag telling us that it is or is not
+ // transparent
+ if (!material.drawList) {
+ var param = material.getParam('collada.transparent');
+ if (param && param.className == 'o3d.ParamBoolean') {
+ material.drawList = param.value ? viewInfo.zOrderedDrawList :
+ viewInfo.performanceDrawList;
+ }
+ }
+ // If the material has no effect, try to build shaders for it.
+ if (!material.effect) {
+ // If the user didn't pass an effect type in see if one was stored there
+ // by our importer.
+ if (!opt_effectType) {
+ // Retrieve the lightingType parameter from the material, if any.
+ var lightingType = o3djs.effect.getColladaLightingType(material);
+ if (lightingType) {
+ opt_effectType = lightingType;
+ }
+ }
+ if (opt_effectType) {
+ o3djs.material.attachStandardEffect(pack,
+ material,
+ viewInfo,
+ opt_effectType);
+ // For collada common profile stuff guess what drawList to use. Note: We
+ // can only do this for collada common profile stuff because we supply
+ // the shaders and therefore now the inputs and how they are used.
+ // For other shaders you've got to do this stuff yourself. On top of
+ // that this is a total guess. Just because a texture has no alpha
+ // it does not follow that you don't want it in the zOrderedDrawList.
+ // That is application specific. Here we are just making a guess and
+ // hoping that it covers most cases.
+ if (material.drawList == null) {
+ // Check the common profile params.
+ var result = o3djs.material.hasNonOneAlpha_(material, 'diffuse');
+ if (!result.found) {
+ result = o3djs.material.hasNonOneAlpha_(material, 'emissive');
+ }
+ if (result.nonOneAlpha) {
+ drawList = viewInfo.zOrderedDrawList;
+ }
+ }
+ }
+ }
+ if (!material.drawList) {
+ material.drawList = drawList;
+ }
+};
+
+/**
+ * Prepares all the materials in the given pack by setting their drawList and
+ * if they don't have an Effect, creating one for them.
+ *
+ * This function is very specific to our sample importer. It expects that if
+ * no Effect exists on a material that certain extra Params have been created
+ * on the Material to give us instructions on what to Effects to create.
+ *
+ * @param {!o3d.Pack} pack Pack to prepare.
+ * @param {!o3djs.rendergraph.ViewInfo} viewInfo as returned from
+ * o3djs.rendergraph.createView.
+ * @param {!o3d.Pack} opt_effectPack Pack to create effects in. If this
+ * is not specifed the pack to prepare above will be used.
+ *
+ * @see o3djs.material.prepareMaterial
+ */
+o3djs.material.prepareMaterials = function(pack,
+ viewInfo,
+ opt_effectPack) {
+ var materials = pack.getObjectsByClassName('o3d.Material');
+ for (var mm = 0; mm < materials.length; mm++) {
+ o3djs.material.prepareMaterial(opt_effectPack || pack,
+ viewInfo,
+ materials[mm]);
+ }
+};
+
+/**
+ * Builds a standard effect for a given material. The position of the
+ * default light is set to the view position. If the material already has
+ * an effect, none is created.
+ * @param {!o3d.Pack} pack Pack to manage created objects.
+ * @param {!o3d.Material} material The material for which to create an
+ * effect.
+ * @param {!o3djs.rendergraph.ViewInfo} viewInfo as returned from
+ * o3djs.rendergraph.createView.
+ * @param {string} effectType Type of effect to create ('phong', 'lambert',
+ * 'constant').
+ *
+ * @see o3djs.effect.attachStandardShader
+ */
+o3djs.material.attachStandardEffect = function(pack,
+ material,
+ viewInfo,
+ effectType) {
+ if (!material.effect) {
+ var lightPos =
+ o3djs.math.matrix4.getTranslation(
+ o3djs.math.inverse(viewInfo.drawContext.view));
+ if (!o3djs.effect.attachStandardShader(pack,
+ material,
+ lightPos,
+ effectType)) {
+ throw 'Could not attach a standard effect';
+ }
+ }
+};
+
+
+/**
+ * Prepares all the materials in the given pack by setting their
+ * drawList.
+ * @param {!o3d.Pack} pack Pack to manage created objects.
+ * @param {!o3d.DrawList} drawList DrawList to assign to materials.
+ */
+o3djs.material.setDrawListOnMaterials = function(pack, drawList) {
+ var materials = pack.getObjectsByClassName('o3d.Material');
+ for (var mm = 0; mm < materials.length; mm++) {
+ var material = materials[mm];
+ // TODO: look at flags on the material left by the importer
+ // to decide which draw list to use.
+ material.drawList = drawList;
+ }
+};
+
+/**
+ * This function creates a basic material for when you just want to get
+ * something on the screen quickly without having to manually setup shaders.
+ * You can call this function something like.
+ *
+ * <pre>
+ * &lt;html&gt;&lt;body&gt;
+ * &lt;script type="text/javascript" src="o3djs/all.js"&gt;
+ * &lt;/script&gt;
+ * &lt;script&gt;
+ * window.onload = init;
+ *
+ * function init() {
+ * o3djs.base.makeClients(initStep2);
+ * }
+ *
+ * function initStep2(clientElements) {
+ * var clientElement = clientElements[0];
+ * var client = clientElement.client;
+ * var pack = client.createPack();
+ * var viewInfo = o3djs.rendergraph.createBasicView(
+ * pack,
+ * client.root,
+ * client.renderGraphRoot);
+ * var material = o3djs.material.createBasicMaterial(
+ * pack,
+ * viewInfo,
+ * [1, 0, 0, 1]); // red
+ * var shape = o3djs.primitives.createCube(pack, material, 10);
+ * var transform = pack.createObject('Transform');
+ * transform.parent = client.root;
+ * transform.addShape(shape);
+ * o3djs.camera.fitContextToScene(client.root,
+ * client.width,
+ * client.height,
+ * viewInfo.drawContext);
+ * }
+ * &lt;/script&gt;
+ * &lt;div id="o3d" style="width: 600px; height: 600px"&gt;&lt;/div&gt;
+ * &lt;/body&gt;&lt;/html&gt;
+ * </pre>
+ *
+ * @param {!o3d.Pack} pack Pack to manage created objects.
+ * @param {!o3djs.rendergraph.ViewInfo} viewInfo as returned from
+ * o3djs.rendergraph.createBasicView.
+ * @param {(!o3djs.math.Vector4|!o3d.Texture)} colorOrTexture Either a color in
+ * the format [r, g, b, a] or an O3D texture.
+ * @param {boolean} opt_transparent Whether or not the material is transparent.
+ * Defaults to false.
+ * @return {!o3d.Material} The created material.
+ */
+o3djs.material.createBasicMaterial = function(pack,
+ viewInfo,
+ colorOrTexture,
+ opt_transparent) {
+ var material = pack.createObject('Material');
+ material.drawList = opt_transparent ? viewInfo.zOrderedDrawList :
+ viewInfo.performanceDrawList;
+
+ // If it has a length assume it's a color, otherwise assume it's a texture.
+ if (colorOrTexture.length) {
+ material.createParam('diffuse', 'ParamFloat4').value = colorOrTexture;
+ } else {
+ var paramSampler = material.createParam('diffuseSampler', 'ParamSampler');
+ var sampler = pack.createObject('Sampler');
+ paramSampler.value = sampler;
+ sampler.texture = colorOrTexture;
+ }
+
+ // Create some suitable defaults for the material to save the user having
+ // to know all this stuff right off the bat.
+ material.createParam('emissive', 'ParamFloat4').value = [0, 0, 0, 1];
+ material.createParam('ambient', 'ParamFloat4').value = [0, 0, 0, 1];
+ material.createParam('specular', 'ParamFloat4').value = [1, 1, 1, 1];
+ material.createParam('shininess', 'ParamFloat').value = 50;
+ material.createParam('specularFactor', 'ParamFloat').value = 1;
+ material.createParam('lightColor', 'ParamFloat4').value = [1, 1, 1, 1];
+ var lightPositionParam = material.createParam('lightWorldPos',
+ 'ParamFloat3');
+
+ o3djs.material.attachStandardEffect(pack, material, viewInfo, 'phong');
+
+ // We have to set the light position after calling attachStandardEffect
+ // because attachStandardEffect sets it based on the view.
+ lightPositionParam.value = [1000, 2000, 3000];
+
+ return material;
+};
+
+/**
+ * Creates a material for an effect loaded from a file.
+ * If the effect has already been loaded in the pack it will be reused.
+ * @param {!o3d.Pack} pack Pack to create effect in.
+ * @param {string} url Url for effect file.
+ * @param {!o3d.DrawList} drawList DrawList to assign effect to.
+ * @return {!o3d.Material} The material.
+ */
+o3djs.material.createMaterialFromFile = function(pack, url, drawList) {
+ var effect = o3djs.effect.createEffectFromFile(pack, url);
+
+ var material = pack.createObject('Material');
+ material.effect = effect;
+ material.drawList = drawList;
+ effect.createUniformParameters(material);
+
+ return material;
+};
+
+/**
+ * Binds params to all materials in a pack by name.
+ * @param {!o3d.Material} material Material to bind params on.
+ * @param {!Object} params A object where each property is the name of a param
+ * and its value is that param.
+ */
+o3djs.material.bindParamsOnMaterial = function(material, params) {
+ for (var paramName in params) {
+ var sourceParam = params[paramName];
+ var param = material.getParam(paramName);
+ if (param && sourceParam.isAClassName(param.className)) {
+ param.bind(sourceParam);
+ }
+ }
+};
+
+/**
+ * Binds params to all materials in a pack by name.
+ * @param {!o3d.Pack} pack Pack with materials to bind.
+ * @param {!Object} params A object where each property is the name of a param
+ * and its value is that param.
+ */
+o3djs.material.bindParams = function(pack, params) {
+ var materials = pack.getObjectsByClassName('o3d.Material');
+ for (var mm = 0; mm < materials.length; ++mm) {
+ o3djs.material.bindParamsOnMaterial(materials[mm], params);
+ }
+};
+
+/**
+ * Creates params from a param spec.
+ * @param {!o3d.Pack} pack Pack to create params in.
+ * @param {!Object} paramSpec An object where each property is the name of a
+ * param and its value is the type of param.
+ * @return {!Object} params A object where each property is the name of a param
+ * and its value is that param.
+ */
+o3djs.material.createParams = function(pack, paramSpec) {
+ var paramObject = pack.createObject('ParamObject');
+ var params = { };
+ for (var paramName in paramSpec) {
+ params[paramName] = paramObject.createParam(paramName,
+ paramSpec[paramName]);
+ }
+ return params;
+};
+
+/**
+ * Creates the global params need by the shaders built in effect.js
+ *
+ * The params currently created are 'lightColor' which is a ParamFloat4 and
+ * 'lightWorldPos' which is a ParamFloat3. You can set their values like this
+ *
+ * <pre>
+ * var params = o3djs.material.createStandardParams(pack);
+ * param.lightColor.value = [1, 0, 0, 1]; // red
+ * param.lightWorldPos.value = [1000, 2000, 3000]; // set light position.
+ * </pre>
+ *
+ * Note: This function just creates the params. It does not connect them to
+ * anything. See o3djs.material.createAndBindStandardParams,
+ * o3djs.material.createParams and o3djs.material.bindParams
+ *
+ * @see o3djs.material.createAndBindStandardParams
+ * @see o3djs.material.createParams
+ * @see o3djs.material.bindParams
+ *
+ * @param {!o3d.Pack} pack Pack to create params in.
+ * @return {!Object} params A object where each property is the name of a param
+ * and its value is that param.
+ */
+o3djs.material.createStandardParams = function(pack) {
+ var paramSpec = {
+ 'lightColor': 'ParamFloat4',
+ 'lightWorldPos': 'ParamFloat3'};
+ return o3djs.material.createParams(pack, paramSpec);
+};
+
+/**
+ * Creates the global params need by the shaders built in effect.js then binds
+ * all the matching params on materials in pack to these global params.
+ *
+ * The params currently created are 'lightColor' which is a ParamFloat4 and
+ * 'lightWorldPos' which is a ParamFloat3. You can set their values like this
+ *
+ * <pre>
+ * var params = o3djs.material.createAndBindStandardParams(pack);
+ * param.lightColor.value = [1, 0, 0, 1]; // red
+ * param.lightWorldPos.value = [1000, 2000, 3000]; // set light position.
+ * </pre>
+ *
+ * @see o3djs.material.createParams
+ * @see o3djs.material.bindParams
+ *
+ * @param {!o3d.Pack} pack Pack to create params in.
+ * @return {!Object} params A object where each property is the name of a param
+ * and its value is that param.
+ */
+o3djs.material.createAndBindStandardParams = function(pack) {
+ var params = o3djs.material.createStandardParams(pack);
+ o3djs.material.bindParams(pack, params);
+ return params;
+};
+
diff --git a/o3d/samples/o3djs/math.js b/o3d/samples/o3djs/math.js
new file mode 100644
index 0000000..94a9f534
--- /dev/null
+++ b/o3d/samples/o3djs/math.js
@@ -0,0 +1,2493 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains matrix/vector math functions.
+ * It adds them to the "math" module on the o3djs object.
+ *
+ * o3djs.math supports a row-major and a column-major mode. In both
+ * modes, vectors are stored as arrays of numbers, and matrices are stored as
+ * arrays of arrays of numbers.
+ *
+ * In row-major mode:
+ *
+ * - Rows of a matrix are sub-arrays.
+ * - Individual entries of a matrix M get accessed in M[row][column] fashion.
+ * - Tuples of coordinates are interpreted as row-vectors.
+ * - A vector v gets transformed by a matrix M by multiplying in the order v*M.
+ *
+ * In column-major mode:
+ *
+ * - Columns of a matrix are sub-arrays.
+ * - Individual entries of a matrix M get accessed in M[column][row] fashion.
+ * - Tuples of coordinates are interpreted as column-vectors.
+ * - A matrix M transforms a vector v by multiplying in the order M*v.
+ *
+ * When a function in o3djs.math requires separate row-major and
+ * column-major versions, a function with the same name gets added to each of
+ * the namespaces o3djs.math.rowMajor and o3djs.math.columnMajor. The
+ * function installRowMajorFunctions() or the function
+ * installColumnMajorFunctions() should get called during initialization to
+ * establish the mode. installRowMajorFunctions() works by iterating through
+ * the o3djs.math.rowMajor namespace and for each function foo, setting
+ * o3djs.math.foo equal to o3djs.math.rowMajor.foo.
+ * installRowMajorFunctions() works the same way, iterating over the columnMajor
+ * namespace. At the end of this file, we call installRowMajorFunctions().
+ *
+ * Switching modes changes two things. It changes how a matrix is encoded as an
+ * array, and it changes how the entries of a matrix get interpreted. Because
+ * those two things change together, the matrix representing a given
+ * transformation of space is the same JavaScript object in either mode.
+ * One consequence of this is that very few functions require separate row-major
+ * and column-major versions. Typically, a function requires separate versions
+ * only if it makes matrix multiplication order explicit, like
+ * mulMatrixMatrix(), mulMatrixVector(), or mulVectorMatrix(). Functions which
+ * create a new matrix, like scaling(), rotationZYX(), and translation() return
+ * the same JavaScript object in either mode, and functions which implicitly
+ * multiply like scale(), rotateZYX() and translate() modify the matrix in the
+ * same way in either mode.
+ *
+ * The convention choice made for math functions in this library is independent
+ * of the convention choice for how matrices get loaded into shaders. That
+ * convention is determined on a per-shader basis.
+ *
+ * Other utilities in o3djs should avoid making calls to functions that make
+ * multiplication order explicit. Instead they should appeal to functions like:
+ *
+ * o3djs.math.matrix4.transformPoint
+ * o3djs.math.matrix4.transformDirection
+ * o3djs.math.matrix4.transformNormal
+ * o3djs.math.matrix4.transformVector4
+ * o3djs.math.matrix4.composition
+ * o3djs.math.matrix4.compose
+ *
+ * These functions multiply matrices implicitly and internally choose the
+ * multiplication order to get the right result. That way, utilities which use
+ * o3djs.math work in either major mode. Note that this does not necessarily
+ * mean all sample code will work even if a line is added which switches major
+ * modes, but it does mean that calls to o3djs still do what they are supposed
+ * to.
+ *
+ */
+
+o3djs.provide('o3djs.math');
+
+/**
+ * A module for math for o3djs.math.
+ * @namespace
+ */
+o3djs.math = o3djs.math || {};
+
+/**
+ * Functions which deal with 4-by-4 transformation matrices are kept in their
+ * own namespsace.
+ */
+o3djs.math.matrix4 = o3djs.math.matrix4 || {};
+
+/**
+ * Functions that are specifically row major are kept in their own namespace.
+ */
+o3djs.math.rowMajor = o3djs.math.rowMajor || {};
+
+/**
+ * Functions that are specifically column major are kept in their own namespace.
+ */
+o3djs.math.columnMajor = o3djs.math.columnMajor || {};
+
+/**
+ * Functions that do error checking are stored in their own namespace.
+ */
+o3djs.math.errorCheck = o3djs.math.errorCheck || {};
+
+/**
+ * Functions that do no error checking and have a separate version that does in
+ * o3djs.math.errorCheck are stored in their own namespace.
+ */
+o3djs.math.errorCheckFree = o3djs.math.errorCheckFree || {};
+
+/**
+ * An Array of 2 floats
+ * @type {(!Array.<number>|!o3d.Float2)}
+ */
+o3djs.math.Vector2 = goog.typedef;
+
+/**
+ * An Array of 3 floats
+ * @type {(!Array.<number>|!o3d.Float3)}
+ */
+o3djs.math.Vector3 = goog.typedef;
+
+/**
+ * An Array of 4 floats
+ * @type {(!Array.<number>|!o3d.Float4)}
+ */
+o3djs.math.Vector4 = goog.typedef;
+
+/**
+ * An Array of floats.
+ * @type {!Array.<number>}
+ */
+o3djs.math.Vector = goog.typedef;
+
+/**
+ * A 1x1 Matrix of floats
+ * @type {!Array.<!Array.<number>>}
+ */
+o3djs.math.Matrix1 = goog.typedef;
+
+/**
+ * A 2x2 Matrix of floats
+ * @type {!Array.<!Array.<number>>}
+ */
+o3djs.math.Matrix2 = goog.typedef;
+
+/**
+ * A 3x3 Matrix of floats
+ * @type {!Array.<!Array.<number>>}
+ */
+o3djs.math.Matrix3 = goog.typedef;
+
+/**
+ * A 4x4 Matrix of floats
+ * @type {(!Array.<!Array.<number>>|!o3d.Matrix4)}
+ */
+o3djs.math.Matrix4 = goog.typedef;
+
+/**
+ * A arbitrary size Matrix of floats
+ * @type {(!Array.<!Array.<number>>|!o3d.Matrix4)}
+ */
+o3djs.math.Matrix = goog.typedef;
+
+/**
+ * Converts degrees to radians.
+ * @param {number} degrees A value in degrees.
+ * @return {number} the value in radians.
+ */
+o3djs.math.degToRad = function(degrees) {
+ return degrees * Math.PI / 180;
+};
+
+/**
+ * Converts radians to degrees.
+ * @param {number} radians A value in radians.
+ * @return {number} the value in degrees.
+ */
+o3djs.math.radToDeg = function(radians) {
+ return radians * 180 / Math.PI;
+};
+
+/**
+ * Performs linear interpolation on two scalars.
+ * Given scalars a and b and interpolation coefficient t, returns
+ * (1 - t) * a + t * b.
+ * @param {number} a Operand scalar.
+ * @param {number} b Operand scalar.
+ * @param {number} t Interpolation coefficient.
+ * @return {number} The weighted sum of a and b.
+ */
+o3djs.math.lerpScalar = function(a, b, t) {
+ return (1 - t) * a + t * b;
+};
+
+/**
+ * Adds two vectors; assumes a and b have the same dimension.
+ * @param {!o3djs.math.Vector} a Operand vector.
+ * @param {!o3djs.math.Vector} b Operand vector.
+ * @return {!o3djs.math.Vector} The sum of a and b.
+ */
+o3djs.math.addVector = function(a, b) {
+ var r = [];
+ var aLength = a.length;
+ for (var i = 0; i < aLength; ++i)
+ r[i] = a[i] + b[i];
+ return r;
+};
+
+/**
+ * Subtracts two vectors.
+ * @param {!o3djs.math.Vector} a Operand vector.
+ * @param {!o3djs.math.Vector} b Operand vector.
+ * @return {!o3djs.math.Vector} The difference of a and b.
+ */
+o3djs.math.subVector = function(a, b) {
+ var r = [];
+ var aLength = a.length;
+ for (var i = 0; i < aLength; ++i)
+ r[i] = a[i] - b[i];
+ return r;
+};
+
+/**
+ * Performs linear interpolation on two vectors.
+ * Given vectors a and b and interpolation coefficient t, returns
+ * (1 - t) * a + t * b.
+ * @param {!o3djs.math.Vector} a Operand vector.
+ * @param {!o3djs.math.Vector} b Operand vector.
+ * @param {number} t Interpolation coefficient.
+ * @return {!o3djs.math.Vector} The weighted sum of a and b.
+ */
+o3djs.math.lerpVector = function(a, b, t) {
+ var r = [];
+ var aLength = a.length;
+ for (var i = 0; i < aLength; ++i)
+ r[i] = (1 - t) * a[i] + t * b[i];
+ return r;
+};
+
+/**
+ * Clamps a value between 0 and range using a modulo.
+ * @param {number} v Value to clamp mod.
+ * @param {number} range Range to clamp to.
+ * @param {number} opt_rangeStart start of range. Default = 0.
+ * @return {number} Clamp modded value.
+ */
+o3djs.math.modClamp = function(v, range, opt_rangeStart) {
+ var start = opt_rangeStart || 0;
+ if (range < 0.00001) {
+ return start;
+ }
+ v -= start;
+ if (v < 0) {
+ v -= Math.floor(v / range) * range;
+ } else {
+ v = v % range;
+ }
+ return v + start;
+};
+
+/**
+ * Lerps in a circle.
+ * Does a lerp between a and b but inside range so for example if
+ * range is 100, a is 95 and b is 5 lerping will go in the positive direction.
+ * @param {number} a Start value.
+ * @param {number} b Target value.
+ * @param {number} t Amount to lerp (0 to 1).
+ * @param {number} range Range of circle.
+ * @return {number} lerped result.
+ */
+o3djs.math.lerpCircular = function(a, b, t, range) {
+ a = o3djs.math.modClamp(a, range);
+ b = o3djs.math.modClamp(b, range);
+ var delta = b - a;
+ if (Math.abs(delta) > range * 0.5) {
+ if (delta > 0) {
+ b -= range;
+ } else {
+ b += range;
+ }
+ }
+ return o3djs.math.modClamp(o3djs.math.lerpScalar(a, b, t), range);
+};
+
+/**
+ * Lerps radians.
+ * @param {number} a Start value.
+ * @param {number} b Target value.
+ * @param {number} t Amount to lerp (0 to 1).
+ * @return {number} lerped result.
+ */
+o3djs.math.lerpRadian = function(a, b, t) {
+ return o3djs.math.lerpCircular(a, b, t, Math.PI * 2);
+};
+
+/**
+ * Divides a vector by a scalar.
+ * @param {!o3djs.math.Vector} v The vector.
+ * @param {number} k The scalar.
+ * @return {!o3djs.math.Vector} v The vector v divided by k.
+ */
+o3djs.math.divVectorScalar = function(v, k) {
+ var r = [];
+ var vLength = v.length;
+ for (var i = 0; i < vLength; ++i)
+ r[i] = v[i] / k;
+ return r;
+};
+
+/**
+ * Computes the dot product of two vectors; assumes that a and b have
+ * the same dimension.
+ * @param {!o3djs.math.Vector} a Operand vector.
+ * @param {!o3djs.math.Vector} b Operand vector.
+ * @return {number} The dot product of a and b.
+ */
+o3djs.math.dot = function(a, b) {
+ var r = 0.0;
+ var aLength = a.length;
+ for (var i = 0; i < aLength; ++i)
+ r += a[i] * b[i];
+ return r;
+};
+
+/**
+ * Computes the cross product of two vectors; assumes both vectors have
+ * three entries.
+ * @param {!o3djs.math.Vector} a Operand vector.
+ * @param {!o3djs.math.Vector} b Operand vector.
+ * @return {!o3djs.math.Vector} The vector a cross b.
+ */
+o3djs.math.cross = function(a, b) {
+ return [a[1] * b[2] - a[2] * b[1],
+ a[2] * b[0] - a[0] * b[2],
+ a[0] * b[1] - a[1] * b[0]];
+};
+
+/**
+ * Computes the Euclidean length of a vector, i.e. the square root of the
+ * sum of the squares of the entries.
+ * @param {!o3djs.math.Vector} a The vector.
+ * @return {number} The length of a.
+ */
+o3djs.math.length = function(a) {
+ var r = 0.0;
+ var aLength = a.length;
+ for (var i = 0; i < aLength; ++i)
+ r += a[i] * a[i];
+ return Math.sqrt(r);
+};
+
+/**
+ * Computes the square of the Euclidean length of a vector, i.e. the sum
+ * of the squares of the entries.
+ * @param {!o3djs.math.Vector} a The vector.
+ * @return {number} The square of the length of a.
+ */
+o3djs.math.lengthSquared = function(a) {
+ var r = 0.0;
+ var aLength = a.length;
+ for (var i = 0; i < aLength; ++i)
+ r += a[i] * a[i];
+ return r;
+};
+
+/**
+ * Computes the Euclidean distance between two vectors.
+ * @param {!o3djs.math.Vector} a A vector.
+ * @param {!o3djs.math.Vector} b A vector.
+ * @return {number} The distance between a and b.
+ */
+o3djs.math.distance = function(a, b) {
+ var r = 0.0;
+ var aLength = a.length;
+ for (var i = 0; i < aLength; ++i) {
+ var t = a[i] - b[i];
+ r += t * t;
+ }
+ return Math.sqrt(r);
+};
+
+/**
+ * Computes the square of the Euclidean distance between two vectors.
+ * @param {!o3djs.math.Vector} a A vector.
+ * @param {!o3djs.math.Vector} b A vector.
+ * @return {number} The distance between a and b.
+ */
+o3djs.math.distanceSquared = function(a, b) {
+ var r = 0.0;
+ var aLength = a.length;
+ for (var i = 0; i < aLength; ++i) {
+ var t = a[i] - b[i];
+ r += t * t;
+ }
+ return r;
+};
+
+/**
+ * Divides a vector by its Euclidean length and returns the quotient.
+ * @param {!o3djs.math.Vector} a The vector.
+ * @return {!o3djs.math.Vector} The normalized vector.
+ */
+o3djs.math.normalize = function(a) {
+ var r = [];
+ var n = 0.0;
+ var aLength = a.length;
+ for (var i = 0; i < aLength; ++i)
+ n += a[i] * a[i];
+ n = Math.sqrt(n);
+ for (var i = 0; i < aLength; ++i)
+ r[i] = a[i] / n;
+ return r;
+};
+
+/**
+ * Adds two matrices; assumes a and b are the same size.
+ * @param {!o3djs.math.Matrix} a Operand matrix.
+ * @param {!o3djs.math.Matrix} b Operand matrix.
+ * @return {!o3djs.math.Matrix} The sum of a and b.
+ */
+o3djs.math.addMatrix = function(a, b) {
+ var r = [];
+ var aLength = a.length;
+ var a0Length = a[0].length;
+ for (var i = 0; i < aLength; ++i) {
+ var row = [];
+ var ai = a[i];
+ var bi = b[i];
+ for (var j = 0; j < a0Length; ++j)
+ row[j] = ai[j] + bi[j];
+ r[i] = row;
+ }
+ return r;
+};
+
+/**
+ * Subtracts two matrices; assumes a and b are the same size.
+ * @param {!o3djs.math.Matrix} a Operand matrix.
+ * @param {!o3djs.math.Matrix} b Operand matrix.
+ * @return {!o3djs.math.Matrix} The sum of a and b.
+ */
+o3djs.math.subMatrix = function(a, b) {
+ var r = [];
+ var aLength = a.length;
+ var a0Length = a[0].length;
+ for (var i = 0; i < aLength; ++i) {
+ var row = [];
+ var ai = a[i];
+ var bi = b[i];
+ for (var j = 0; j < a0Length; ++j)
+ row[j] = ai[j] - bi[j];
+ r[i] = row;
+ }
+ return r;
+};
+
+/**
+ * Performs linear interpolation on two matrices.
+ * Given matrices a and b and interpolation coefficient t, returns
+ * (1 - t) * a + t * b.
+ * @param {!o3djs.math.Matrix} a Operand matrix.
+ * @param {!o3djs.math.Matrix} b Operand matrix.
+ * @param {number} t Interpolation coefficient.
+ * @return {!o3djs.math.Matrix} The weighted of a and b.
+ */
+o3djs.math.lerpMatrix = function(a, b, t) {
+ var r = [];
+ var aLength = a.length;
+ var a0Length = a[0].length;
+ for (var i = 0; i < aLength; ++i) {
+ var row = [];
+ var ai = a[i];
+ var bi = b[i];
+ for (var j = 0; j < a0Length; ++j)
+ row[j] = (1 - t) * ai[j] + t * bi[j];
+ r[i] = row;
+ }
+ return r;
+};
+
+/**
+ * Divides a matrix by a scalar.
+ * @param {!o3djs.math.Matrix} m The matrix.
+ * @param {number} k The scalar.
+ * @return {!o3djs.math.Matrix} The matrix m divided by k.
+ */
+o3djs.math.divMatrixScalar = function(m, k) {
+ var r = [];
+ var mLength = m.length;
+ var m0Length = m[0].length;
+ for (var i = 0; i < mLength; ++i) {
+ r[i] = [];
+ for (var j = 0; j < m0Length; ++j)
+ r[i][j] = m[i][j] / k;
+ }
+ return r;
+};
+
+/**
+ * Negates a scalar.
+ * @param {number} a The scalar.
+ * @return {number} -a.
+ */
+o3djs.math.negativeScalar = function(a) {
+ return -a;
+};
+
+/**
+ * Negates a vector.
+ * @param {!o3djs.math.Vector} v The vector.
+ * @return {!o3djs.math.Vector} -v.
+ */
+o3djs.math.negativeVector = function(v) {
+ var r = [];
+ var vLength = v.length;
+ for (var i = 0; i < vLength; ++i) {
+ r[i] = -v[i];
+ }
+ return r;
+};
+
+/**
+ * Negates a matrix.
+ * @param {!o3djs.math.Matrix} m The matrix.
+ * @return {!o3djs.math.Matrix} -m.
+ */
+o3djs.math.negativeMatrix = function(m) {
+ var r = [];
+ var mLength = m.length;
+ var m0Length = m[0].length;
+ for (var i = 0; i < mLength; ++i) {
+ r[i] = [];
+ for (var j = 0; j < m0Length; ++j)
+ r[i][j] = -m[i][j];
+ }
+ return r;
+};
+
+/**
+ * Copies a scalar.
+ * @param {number} a The scalar.
+ * @return {number} a.
+ */
+o3djs.math.copyScalar = function(a) {
+ return a;
+};
+
+/**
+ * Copies a vector.
+ * @param {!o3djs.math.Vector} v The vector.
+ * @return {!o3djs.math.Vector} A copy of v.
+ */
+o3djs.math.copyVector = function(v) {
+ var r = [];
+ for (var i = 0; i < v.length; i++)
+ r[i] = v[i];
+ return r;
+};
+
+/**
+ * Copies a matrix.
+ * @param {!o3djs.math.Matrix} m The matrix.
+ * @return {!o3djs.math.Matrix} A copy of m.
+ */
+o3djs.math.copyMatrix = function(m) {
+ var r = [];
+ var mLength = m.length;
+ for (var i = 0; i < mLength; ++i) {
+ r[i] = [];
+ for (var j = 0; j < m[i].length; j++) {
+ r[i][j] = m[i][j];
+ }
+ }
+ return r;
+};
+
+/**
+ * Multiplies two scalars.
+ * @param {number} a Operand scalar.
+ * @param {number} b Operand scalar.
+ * @return {number} The product of a and b.
+ */
+o3djs.math.mulScalarScalar = function(a, b) {
+ return a * b;
+};
+
+/**
+ * Multiplies a scalar by a vector.
+ * @param {number} k The scalar.
+ * @param {!o3djs.math.Vector} v The vector.
+ * @return {!o3djs.math.Vector} The product of k and v.
+ */
+o3djs.math.mulScalarVector = function(k, v) {
+ var r = [];
+ var vLength = v.length;
+ for (var i = 0; i < vLength; ++i) {
+ r[i] = k * v[i];
+ }
+ return r;
+};
+
+/**
+ * Multiplies a vector by a scalar.
+ * @param {!o3djs.math.Vector} v The vector.
+ * @param {number} k The scalar.
+ * @return {!o3djs.math.Vector} The product of k and v.
+ */
+o3djs.math.mulVectorScalar = function(v, k) {
+ return o3djs.math.mulScalarVector(k, v);
+};
+
+/**
+ * Multiplies a scalar by a matrix.
+ * @param {number} k The scalar.
+ * @param {!o3djs.math.Matrix} m The matrix.
+ * @return {!o3djs.math.Matrix} The product of m and k.
+ */
+o3djs.math.mulScalarMatrix = function(k, m) {
+ var r = [];
+ var mLength = m.length;
+ var m0Length = m[0].length;
+ for (var i = 0; i < mLength; ++i) {
+ r[i] = [];
+ for (var j = 0; j < m0Length; ++j)
+ r[i][j] = k * m[i][j];
+ }
+ return r;
+};
+
+/**
+ * Multiplies a matrix by a scalar.
+ * @param {!o3djs.math.Matrix} m The matrix.
+ * @param {number} k The scalar.
+ * @return {!o3djs.math.Matrix} The product of m and k.
+ */
+o3djs.math.mulMatrixScalar = function(m, k) {
+ return o3djs.math.mulScalarMatrix(k, m);
+};
+
+/**
+ * Multiplies a vector by another vector (component-wise); assumes a and
+ * b have the same length.
+ * @param {!o3djs.math.Vector} a Operand vector.
+ * @param {!o3djs.math.Vector} b Operand vector.
+ * @return {!o3djs.math.Vector} The vector of products of entries of a and
+ * b.
+ */
+o3djs.math.mulVectorVector = function(a, b) {
+ var r = [];
+ var aLength = a.length;
+ for (var i = 0; i < aLength; ++i)
+ r[i] = a[i] * b[i];
+ return r;
+};
+
+/**
+ * Divides a vector by another vector (component-wise); assumes a and
+ * b have the same length.
+ * @param {!o3djs.math.Vector} a Operand vector.
+ * @param {!o3djs.math.Vector} b Operand vector.
+ * @return {!o3djs.math.Vector} The vector of quotients of entries of a and
+ * b.
+ */
+o3djs.math.divVectorVector = function(a, b) {
+ var r = [];
+ var aLength = a.length;
+ for (var i = 0; i < aLength; ++i)
+ r[i] = a[i] * b[i];
+ return r;
+};
+
+/**
+ * Multiplies a vector by a matrix; treats the vector as a row vector; assumes
+ * matrix entries are accessed in [row][column] fashion.
+ * @param {!o3djs.math.Vector} v The vector.
+ * @param {!o3djs.math.Matrix} m The matrix.
+ * @return {!o3djs.math.Vector} The product of v and m as a row vector.
+ */
+o3djs.math.rowMajor.mulVectorMatrix = function(v, m) {
+ var r = [];
+ var m0Length = m[0].length;
+ var vLength = v.length;
+ for (var i = 0; i < m0Length; ++i) {
+ r[i] = 0.0;
+ for (var j = 0; j < vLength; ++j)
+ r[i] += v[j] * m[j][i];
+ }
+ return r;
+};
+
+/**
+ * Multiplies a vector by a matrix; treats the vector as a row vector; assumes
+ * matrix entries are accessed in [column][row] fashion.
+ * @param {!o3djs.math.Vector} v The vector.
+ * @param {!o3djs.math.Matrix} m The matrix.
+ * @return {!o3djs.math.Vector} The product of v and m as a row vector.
+ */
+o3djs.math.columnMajor.mulVectorMatrix = function(v, m) {
+ var r = [];
+ var mLength = m.length;
+ var vLength = v.length;
+ for (var i = 0; i < mLength; ++i) {
+ r[i] = 0.0;
+ var column = m[i];
+ for (var j = 0; j < vLength; ++j)
+ r[i] += v[j] * column[j];
+ }
+ return r;
+};
+
+/**
+ * Multiplies a vector by a matrix; treats the vector as a row vector.
+ * @param {!o3djs.math.Matrix} m The matrix.
+ * @param {!o3djs.math.Vector} v The vector.
+ * @return {!o3djs.math.Vector} The product of m and v as a row vector.
+ */
+o3djs.math.mulVectorMatrix = null;
+
+/**
+ * Multiplies a matrix by a vector; treats the vector as a column vector.
+ * assumes matrix entries are accessed in [row][column] fashion.
+ * @param {!o3djs.math.Matrix} m The matrix.
+ * @param {!o3djs.math.Vector} v The vector.
+ * @return {!o3djs.math.Vector} The product of m and v as a column vector.
+ */
+o3djs.math.rowMajor.mulMatrixVector = function(m, v) {
+ var r = [];
+ var mLength = m.length;
+ var m0Length = m[0].length;
+ for (var i = 0; i < mLength; ++i) {
+ r[i] = 0.0;
+ var row = m[i];
+ for (var j = 0; j < m0Length; ++j)
+ r[i] += row[j] * v[j];
+ }
+ return r;
+};
+
+/**
+ * Multiplies a matrix by a vector; treats the vector as a column vector;
+ * assumes matrix entries are accessed in [column][row] fashion.
+ * @param {!o3djs.math.Matrix} m The matrix.
+ * @param {!o3djs.math.Vector} v The vector.
+ * @return {!o3djs.math.Vector} The product of m and v as a column vector.
+ */
+o3djs.math.columnMajor.mulMatrixVector = function(m, v) {
+ var r = [];
+ var m0Length = m[0].length;
+ var vLength = v.length;
+ for (var i = 0; i < m0Length; ++i) {
+ r[i] = 0.0;
+ for (var j = 0; j < vLength; ++j)
+ r[i] += v[j] * m[j][i];
+ }
+ return r;
+};
+
+/**
+ * Multiplies a matrix by a vector; treats the vector as a column vector.
+ * @param {!o3djs.math.Matrix} m The matrix.
+ * @param {!o3djs.math.Vector} v The vector.
+ * @return {!o3djs.math.Vector} The product of m and v as a column vector.
+ */
+o3djs.math.mulMatrixVector = null;
+
+/**
+ * Multiplies two 2-by-2 matrices; assumes that the given matrices are 2-by-2;
+ * assumes matrix entries are accessed in [row][column] fashion.
+ * @param {!o3djs.math.Matrix2} a The matrix on the left.
+ * @param {!o3djs.math.Matrix2} b The matrix on the right.
+ * @return {!o3djs.math.Matrix2} The matrix product of a and b.
+ */
+o3djs.math.rowMajor.mulMatrixMatrix2 = function(a, b) {
+ var a0 = a[0];
+ var a1 = a[1];
+ var b0 = b[0];
+ var b1 = b[1];
+ var a00 = a0[0];
+ var a01 = a0[1];
+ var a10 = a1[0];
+ var a11 = a1[1];
+ var b00 = b0[0];
+ var b01 = b0[1];
+ var b10 = b1[0];
+ var b11 = b1[1];
+ return [[a00 * b00 + a01 * b10, a00 * b01 + a01 * b11],
+ [a10 * b00 + a11 * b10, a10 * b01 + a11 * b11]];
+};
+
+/**
+ * Multiplies two 2-by-2 matrices; assumes that the given matrices are 2-by-2;
+ * assumes matrix entries are accessed in [column][row] fashion.
+ * @param {!o3djs.math.Matrix2} a The matrix on the left.
+ * @param {!o3djs.math.Matrix2} b The matrix on the right.
+ * @return {!o3djs.math.Matrix2} The matrix product of a and b.
+ */
+o3djs.math.columnMajor.mulMatrixMatrix2 = function(a, b) {
+ var a0 = a[0];
+ var a1 = a[1];
+ var b0 = b[0];
+ var b1 = b[1];
+ var a00 = a0[0];
+ var a01 = a0[1];
+ var a10 = a1[0];
+ var a11 = a1[1];
+ var b00 = b0[0];
+ var b01 = b0[1];
+ var b10 = b1[0];
+ var b11 = b1[1];
+ return [[a00 * b00 + a10 * b01, a01 * b00 + a11 * b01],
+ [a00 * b10 + a10 * b11, a01 * b10 + a11 * b11]];
+};
+
+/**
+ * Multiplies two 2-by-2 matrices.
+ * @param {!o3djs.math.Matrix2} a The matrix on the left.
+ * @param {!o3djs.math.Matrix2} b The matrix on the right.
+ * @return {!o3djs.math.Matrix2} The matrix product of a and b.
+ */
+o3djs.math.mulMatrixMatrix2 = null;
+
+
+/**
+ * Multiplies two 3-by-3 matrices; assumes that the given matrices are 3-by-3;
+ * assumes matrix entries are accessed in [row][column] fashion.
+ * @param {!o3djs.math.Matrix3} a The matrix on the left.
+ * @param {!o3djs.math.Matrix3} b The matrix on the right.
+ * @return {!o3djs.math.Matrix3} The matrix product of a and b.
+ */
+o3djs.math.rowMajor.mulMatrixMatrix3 = function(a, b) {
+ var a0 = a[0];
+ var a1 = a[1];
+ var a2 = a[2];
+ var b0 = b[0];
+ var b1 = b[1];
+ var b2 = b[2];
+ var a00 = a0[0];
+ var a01 = a0[1];
+ var a02 = a0[2];
+ var a10 = a1[0];
+ var a11 = a1[1];
+ var a12 = a1[2];
+ var a20 = a2[0];
+ var a21 = a2[1];
+ var a22 = a2[2];
+ var b00 = b0[0];
+ var b01 = b0[1];
+ var b02 = b0[2];
+ var b10 = b1[0];
+ var b11 = b1[1];
+ var b12 = b1[2];
+ var b20 = b2[0];
+ var b21 = b2[1];
+ var b22 = b2[2];
+ return [[a00 * b00 + a01 * b10 + a02 * b20,
+ a00 * b01 + a01 * b11 + a02 * b21,
+ a00 * b02 + a01 * b12 + a02 * b22],
+ [a10 * b00 + a11 * b10 + a12 * b20,
+ a10 * b01 + a11 * b11 + a12 * b21,
+ a10 * b02 + a11 * b12 + a12 * b22],
+ [a20 * b00 + a21 * b10 + a22 * b20,
+ a20 * b01 + a21 * b11 + a22 * b21,
+ a20 * b02 + a21 * b12 + a22 * b22]];
+};
+
+/**
+ * Multiplies two 3-by-3 matrices; assumes that the given matrices are 3-by-3;
+ * assumes matrix entries are accessed in [column][row] fashion.
+ * @param {!o3djs.math.Matrix3} a The matrix on the left.
+ * @param {!o3djs.math.Matrix3} b The matrix on the right.
+ * @return {!o3djs.math.Matrix3} The matrix product of a and b.
+ */
+o3djs.math.columnMajor.mulMatrixMatrix3 = function(a, b) {
+ var a0 = a[0];
+ var a1 = a[1];
+ var a2 = a[2];
+ var b0 = b[0];
+ var b1 = b[1];
+ var b2 = b[2];
+ var a00 = a0[0];
+ var a01 = a0[1];
+ var a02 = a0[2];
+ var a10 = a1[0];
+ var a11 = a1[1];
+ var a12 = a1[2];
+ var a20 = a2[0];
+ var a21 = a2[1];
+ var a22 = a2[2];
+ var b00 = b0[0];
+ var b01 = b0[1];
+ var b02 = b0[2];
+ var b10 = b1[0];
+ var b11 = b1[1];
+ var b12 = b1[2];
+ var b20 = b2[0];
+ var b21 = b2[1];
+ var b22 = b2[2];
+ return [[a00 * b00 + a10 * b01 + a20 * b02,
+ a01 * b00 + a11 * b01 + a21 * b02,
+ a02 * b00 + a12 * b01 + a22 * b02],
+ [a00 * b10 + a10 * b11 + a20 * b12,
+ a01 * b10 + a11 * b11 + a21 * b12,
+ a02 * b10 + a12 * b11 + a22 * b12],
+ [a00 * b20 + a10 * b21 + a20 * b22,
+ a01 * b20 + a11 * b21 + a21 * b22,
+ a02 * b20 + a12 * b21 + a22 * b22]];
+};
+
+/**
+ * Multiplies two 3-by-3 matrices; assumes that the given matrices are 3-by-3.
+ * @param {!o3djs.math.Matrix3} a The matrix on the left.
+ * @param {!o3djs.math.Matrix3} b The matrix on the right.
+ * @return {!o3djs.math.Matrix3} The matrix product of a and b.
+ */
+o3djs.math.mulMatrixMatrix3 = null;
+
+/**
+ * Multiplies two 4-by-4 matrices; assumes that the given matrices are 4-by-4;
+ * assumes matrix entries are accessed in [row][column] fashion.
+ * @param {!o3djs.math.Matrix4} a The matrix on the left.
+ * @param {!o3djs.math.Matrix4} b The matrix on the right.
+ * @return {!o3djs.math.Matrix4} The matrix product of a and b.
+ */
+o3djs.math.rowMajor.mulMatrixMatrix4 = function(a, b) {
+ var a0 = a[0];
+ var a1 = a[1];
+ var a2 = a[2];
+ var a3 = a[3];
+ var b0 = b[0];
+ var b1 = b[1];
+ var b2 = b[2];
+ var b3 = b[3];
+ var a00 = a0[0];
+ var a01 = a0[1];
+ var a02 = a0[2];
+ var a03 = a0[3];
+ var a10 = a1[0];
+ var a11 = a1[1];
+ var a12 = a1[2];
+ var a13 = a1[3];
+ var a20 = a2[0];
+ var a21 = a2[1];
+ var a22 = a2[2];
+ var a23 = a2[3];
+ var a30 = a3[0];
+ var a31 = a3[1];
+ var a32 = a3[2];
+ var a33 = a3[3];
+ var b00 = b0[0];
+ var b01 = b0[1];
+ var b02 = b0[2];
+ var b03 = b0[3];
+ var b10 = b1[0];
+ var b11 = b1[1];
+ var b12 = b1[2];
+ var b13 = b1[3];
+ var b20 = b2[0];
+ var b21 = b2[1];
+ var b22 = b2[2];
+ var b23 = b2[3];
+ var b30 = b3[0];
+ var b31 = b3[1];
+ var b32 = b3[2];
+ var b33 = b3[3];
+ return [[a00 * b00 + a01 * b10 + a02 * b20 + a03 * b30,
+ a00 * b01 + a01 * b11 + a02 * b21 + a03 * b31,
+ a00 * b02 + a01 * b12 + a02 * b22 + a03 * b32,
+ a00 * b03 + a01 * b13 + a02 * b23 + a03 * b33],
+ [a10 * b00 + a11 * b10 + a12 * b20 + a13 * b30,
+ a10 * b01 + a11 * b11 + a12 * b21 + a13 * b31,
+ a10 * b02 + a11 * b12 + a12 * b22 + a13 * b32,
+ a10 * b03 + a11 * b13 + a12 * b23 + a13 * b33],
+ [a20 * b00 + a21 * b10 + a22 * b20 + a23 * b30,
+ a20 * b01 + a21 * b11 + a22 * b21 + a23 * b31,
+ a20 * b02 + a21 * b12 + a22 * b22 + a23 * b32,
+ a20 * b03 + a21 * b13 + a22 * b23 + a23 * b33],
+ [a30 * b00 + a31 * b10 + a32 * b20 + a33 * b30,
+ a30 * b01 + a31 * b11 + a32 * b21 + a33 * b31,
+ a30 * b02 + a31 * b12 + a32 * b22 + a33 * b32,
+ a30 * b03 + a31 * b13 + a32 * b23 + a33 * b33]];
+};
+
+/**
+ * Multiplies two 4-by-4 matrices; assumes that the given matrices are 4-by-4;
+ * assumes matrix entries are accessed in [column][row] fashion.
+ * @param {!o3djs.math.Matrix4} a The matrix on the left.
+ * @param {!o3djs.math.Matrix4} b The matrix on the right.
+ * @return {!o3djs.math.Matrix4} The matrix product of a and b.
+ */
+o3djs.math.columnMajor.mulMatrixMatrix4 = function(a, b) {
+ var a0 = a[0];
+ var a1 = a[1];
+ var a2 = a[2];
+ var a3 = a[3];
+ var b0 = b[0];
+ var b1 = b[1];
+ var b2 = b[2];
+ var b3 = b[3];
+ var a00 = a0[0];
+ var a01 = a0[1];
+ var a02 = a0[2];
+ var a03 = a0[3];
+ var a10 = a1[0];
+ var a11 = a1[1];
+ var a12 = a1[2];
+ var a13 = a1[3];
+ var a20 = a2[0];
+ var a21 = a2[1];
+ var a22 = a2[2];
+ var a23 = a2[3];
+ var a30 = a3[0];
+ var a31 = a3[1];
+ var a32 = a3[2];
+ var a33 = a3[3];
+ var b00 = b0[0];
+ var b01 = b0[1];
+ var b02 = b0[2];
+ var b03 = b0[3];
+ var b10 = b1[0];
+ var b11 = b1[1];
+ var b12 = b1[2];
+ var b13 = b1[3];
+ var b20 = b2[0];
+ var b21 = b2[1];
+ var b22 = b2[2];
+ var b23 = b2[3];
+ var b30 = b3[0];
+ var b31 = b3[1];
+ var b32 = b3[2];
+ var b33 = b3[3];
+ return [[a00 * b00 + a10 * b01 + a20 * b02 + a30 * b03,
+ a01 * b00 + a11 * b01 + a21 * b02 + a31 * b03,
+ a02 * b00 + a12 * b01 + a22 * b02 + a32 * b03,
+ a03 * b00 + a13 * b01 + a23 * b02 + a33 * b03],
+ [a00 * b10 + a10 * b11 + a20 * b12 + a30 * b13,
+ a01 * b10 + a11 * b11 + a21 * b12 + a31 * b13,
+ a02 * b10 + a12 * b11 + a22 * b12 + a32 * b13,
+ a03 * b10 + a13 * b11 + a23 * b12 + a33 * b13],
+ [a00 * b20 + a10 * b21 + a20 * b22 + a30 * b23,
+ a01 * b20 + a11 * b21 + a21 * b22 + a31 * b23,
+ a02 * b20 + a12 * b21 + a22 * b22 + a32 * b23,
+ a03 * b20 + a13 * b21 + a23 * b22 + a33 * b23],
+ [a00 * b30 + a10 * b31 + a20 * b32 + a30 * b33,
+ a01 * b30 + a11 * b31 + a21 * b32 + a31 * b33,
+ a02 * b30 + a12 * b31 + a22 * b32 + a32 * b33,
+ a03 * b30 + a13 * b31 + a23 * b32 + a33 * b33]];
+};
+
+/**
+ * Multiplies two 4-by-4 matrices; assumes that the given matrices are 4-by-4.
+ * @param {!o3djs.math.Matrix4} a The matrix on the left.
+ * @param {!o3djs.math.Matrix4} b The matrix on the right.
+ * @return {!o3djs.math.Matrix4} The matrix product of a and b.
+ */
+o3djs.math.mulMatrixMatrix4 = null;
+
+/**
+ * Multiplies two matrices; assumes that the sizes of the matrices are
+ * appropriately compatible; assumes matrix entries are accessed in
+ * [row][column] fashion.
+ * @param {!o3djs.math.Matrix} a The matrix on the left.
+ * @param {!o3djs.math.Matrix} b The matrix on the right.
+ * @return {!o3djs.math.Matrix} The matrix product of a and b.
+ */
+o3djs.math.rowMajor.mulMatrixMatrix = function(a, b) {
+ var r = [];
+ var aRows = a.length;
+ var bColumns = b[0].length;
+ var bRows = b.length;
+ for (var i = 0; i < aRows; ++i) {
+ var v = []; // v becomes a row of the answer.
+ var ai = a[i]; // ith row of a.
+ for (var j = 0; j < bColumns; ++j) {
+ v[j] = 0.0;
+ for (var k = 0; k < bRows; ++k)
+ v[j] += ai[k] * b[k][j]; // kth row, jth column.
+ }
+ r[i] = v;
+ }
+ return r;
+};
+
+/**
+ * Multiplies two matrices; assumes that the sizes of the matrices are
+ * appropriately compatible; assumes matrix entries are accessed in
+ * [row][column] fashion.
+ * @param {!o3djs.math.Matrix} a The matrix on the left.
+ * @param {!o3djs.math.Matrix} b The matrix on the right.
+ * @return {!o3djs.math.Matrix} The matrix product of a and b.
+ */
+o3djs.math.columnMajor.mulMatrixMatrix = function(a, b) {
+ var r = [];
+ var bColumns = b.length;
+ var aRows = a[0].length;
+ var aColumns = a.length;
+ for (var i = 0; i < bColumns; ++i) {
+ var v = []; // v becomes a column of the answer.
+ var bi = b[i]; // ith column of b.
+ for (var j = 0; j < aRows; ++j) {
+ v[j] = 0.0;
+ for (var k = 0; k < aColumns; ++k)
+ v[j] += bi[k] * a[k][j]; // kth column, jth row.
+ }
+ r[i] = v;
+ }
+ return r;
+};
+
+/**
+ * Multiplies two matrices; assumes that the sizes of the matrices are
+ * appropriately compatible.
+ * @param {!o3djs.math.Matrix} a The matrix on the left.
+ * @param {!o3djs.math.Matrix} b The matrix on the right.
+ * @return {!o3djs.math.Matrix} The matrix product of a and b.
+ */
+o3djs.math.mulMatrixMatrix = null;
+
+/**
+ * Gets the jth column of the given matrix m; assumes matrix entries are
+ * accessed in [row][column] fashion.
+ * @param {!o3djs.math.Matrix} m The matrix.
+ * @param {number} j The index of the desired column.
+ * @return {!o3djs.math.Vector} The jth column of m as a vector.
+ */
+o3djs.math.rowMajor.column = function(m, j) {
+ var r = [];
+ var mLength = m.length;
+ for (var i = 0; i < mLength; ++i) {
+ r[i] = m[i][j];
+ }
+ return r;
+};
+
+/**
+ * Gets the jth column of the given matrix m; assumes matrix entries are
+ * accessed in [column][row] fashion.
+ * @param {!o3djs.math.Matrix} m The matrix.
+ * @param {number} j The index of the desired column.
+ * @return {!o3djs.math.Vector} The jth column of m as a vector.
+ */
+o3djs.math.columnMajor.column = function(m, j) {
+ return m[j].slice();
+};
+
+/**
+ * Gets the jth column of the given matrix m.
+ * @param {!o3djs.math.Matrix} m The matrix.
+ * @param {number} j The index of the desired column.
+ * @return {!o3djs.math.Vector} The jth column of m as a vector.
+ */
+o3djs.math.column = null;
+
+/**
+ * Gets the ith row of the given matrix m; assumes matrix entries are
+ * accessed in [row][column] fashion.
+ * @param {!o3djs.math.Matrix} m The matrix.
+ * @param {number} i The index of the desired row.
+ * @return {!o3djs.math.Vector} The ith row of m.
+ */
+o3djs.math.rowMajor.row = function(m, i) {
+ return m[i].slice();
+};
+
+/**
+ * Gets the ith row of the given matrix m; assumes matrix entries are
+ * accessed in [column][row] fashion.
+ * @param {!o3djs.math.Matrix} m The matrix.
+ * @param {number} i The index of the desired row.
+ * @return {!o3djs.math.Vector} The ith row of m.
+ */
+o3djs.math.columnMajor.row = function(m, i) {
+ var r = [];
+ var mLength = m.length;
+ for (var j = 0; j < mLength; ++j) {
+ r[j] = m[j][i];
+ }
+ return r;
+};
+
+/**
+ * Gets the ith row of the given matrix m.
+ * @param {!o3djs.math.Matrix} m The matrix.
+ * @param {number} i The index of the desired row.
+ * @return {!o3djs.math.Vector} The ith row of m.
+ */
+o3djs.math.row = null;
+
+/**
+ * Creates an n-by-n identity matrix.
+ * @param {number} n The dimension of the identity matrix required.
+ * @return {!o3djs.math.Matrix} An n-by-n identity matrix.
+ */
+o3djs.math.identity = function(n) {
+ var r = [];
+ for (var j = 0; j < n; ++j) {
+ r[j] = [];
+ for (var i = 0; i < n; ++i)
+ r[j][i] = (i == j) ? 1 : 0;
+ }
+ return r;
+};
+
+/**
+ * Takes the transpose of a matrix.
+ * @param {!o3djs.math.Matrix} m The matrix.
+ * @return {!o3djs.math.Matrix} The transpose of m.
+ */
+o3djs.math.transpose = function(m) {
+ var r = [];
+ var m0Length = m[0].length;
+ var mLength = m.length;
+ for (var j = 0; j < m0Length; ++j) {
+ r[j] = [];
+ for (var i = 0; i < mLength; ++i)
+ r[j][i] = m[i][j];
+ }
+ return r;
+};
+
+/**
+ * Computes the trace (sum of the diagonal entries) of a square matrix;
+ * assumes m is square.
+ * @param {!o3djs.math.Matrix} m The matrix.
+ * @return {number} The trace of m.
+ */
+o3djs.math.trace = function(m) {
+ var r = 0.0;
+ var mLength = m.length;
+ for (var i = 0; i < mLength; ++i)
+ r += m[i][i];
+ return r;
+};
+
+/**
+ * Computes the determinant of a 1-by-1 matrix.
+ * @param {!o3djs.math.Matrix1} m The matrix.
+ * @return {number} The determinant of m.
+ */
+o3djs.math.det1 = function(m) {
+ return m[0][0];
+};
+
+/**
+ * Computes the determinant of a 2-by-2 matrix.
+ * @param {!o3djs.math.Matrix2} m The matrix.
+ * @return {number} The determinant of m.
+ */
+o3djs.math.det2 = function(m) {
+ return m[0][0] * m[1][1] - m[0][1] * m[1][0];
+};
+
+/**
+ * Computes the determinant of a 3-by-3 matrix.
+ * @param {!o3djs.math.Matrix3} m The matrix.
+ * @return {number} The determinant of m.
+ */
+o3djs.math.det3 = function(m) {
+ return m[2][2] * (m[0][0] * m[1][1] - m[0][1] * m[1][0]) -
+ m[2][1] * (m[0][0] * m[1][2] - m[0][2] * m[1][0]) +
+ m[2][0] * (m[0][1] * m[1][2] - m[0][2] * m[1][1]);
+};
+
+/**
+ * Computes the determinant of a 4-by-4 matrix.
+ * @param {!o3djs.math.Matrix4} m The matrix.
+ * @return {number} The determinant of m.
+ */
+o3djs.math.det4 = function(m) {
+ var t01 = m[0][0] * m[1][1] - m[0][1] * m[1][0];
+ var t02 = m[0][0] * m[1][2] - m[0][2] * m[1][0];
+ var t03 = m[0][0] * m[1][3] - m[0][3] * m[1][0];
+ var t12 = m[0][1] * m[1][2] - m[0][2] * m[1][1];
+ var t13 = m[0][1] * m[1][3] - m[0][3] * m[1][1];
+ var t23 = m[0][2] * m[1][3] - m[0][3] * m[1][2];
+ return m[3][3] * (m[2][2] * t01 - m[2][1] * t02 + m[2][0] * t12) -
+ m[3][2] * (m[2][3] * t01 - m[2][1] * t03 + m[2][0] * t13) +
+ m[3][1] * (m[2][3] * t02 - m[2][2] * t03 + m[2][0] * t23) -
+ m[3][0] * (m[2][3] * t12 - m[2][2] * t13 + m[2][1] * t23);
+};
+
+/**
+ * Computes the inverse of a 1-by-1 matrix.
+ * @param {!o3djs.math.Matrix1} m The matrix.
+ * @return {!o3djs.math.Matrix1} The inverse of m.
+ */
+o3djs.math.inverse1 = function(m) {
+ return [[1.0 / m[0][0]]];
+};
+
+/**
+ * Computes the inverse of a 2-by-2 matrix.
+ * @param {!o3djs.math.Matrix2} m The matrix.
+ * @return {!o3djs.math.Matrix2} The inverse of m.
+ */
+o3djs.math.inverse2 = function(m) {
+ var d = 1.0 / (m[0][0] * m[1][1] - m[0][1] * m[1][0]);
+ return [[d * m[1][1], -d * m[0][1]], [-d * m[1][0], d * m[0][0]]];
+};
+
+/**
+ * Computes the inverse of a 3-by-3 matrix.
+ * @param {!o3djs.math.Matrix3} m The matrix.
+ * @return {!o3djs.math.Matrix3} The inverse of m.
+ */
+o3djs.math.inverse3 = function(m) {
+ var t00 = m[1][1] * m[2][2] - m[1][2] * m[2][1];
+ var t10 = m[0][1] * m[2][2] - m[0][2] * m[2][1];
+ var t20 = m[0][1] * m[1][2] - m[0][2] * m[1][1];
+ var d = 1.0 / (m[0][0] * t00 - m[1][0] * t10 + m[2][0] * t20);
+ return [[d * t00, -d * t10, d * t20],
+ [-d * (m[1][0] * m[2][2] - m[1][2] * m[2][0]),
+ d * (m[0][0] * m[2][2] - m[0][2] * m[2][0]),
+ -d * (m[0][0] * m[1][2] - m[0][2] * m[1][0])],
+ [d * (m[1][0] * m[2][1] - m[1][1] * m[2][0]),
+ -d * (m[0][0] * m[2][1] - m[0][1] * m[2][0]),
+ d * (m[0][0] * m[1][1] - m[0][1] * m[1][0])]];
+};
+
+/**
+ * Computes the inverse of a 4-by-4 matrix.
+ * @param {!o3djs.math.Matrix4} m The matrix.
+ * @return {!o3djs.math.Matrix4} The inverse of m.
+ */
+o3djs.math.inverse4 = function(m) {
+ var tmp_0 = m[2][2] * m[3][3];
+ var tmp_1 = m[3][2] * m[2][3];
+ var tmp_2 = m[1][2] * m[3][3];
+ var tmp_3 = m[3][2] * m[1][3];
+ var tmp_4 = m[1][2] * m[2][3];
+ var tmp_5 = m[2][2] * m[1][3];
+ var tmp_6 = m[0][2] * m[3][3];
+ var tmp_7 = m[3][2] * m[0][3];
+ var tmp_8 = m[0][2] * m[2][3];
+ var tmp_9 = m[2][2] * m[0][3];
+ var tmp_10 = m[0][2] * m[1][3];
+ var tmp_11 = m[1][2] * m[0][3];
+ var tmp_12 = m[2][0] * m[3][1];
+ var tmp_13 = m[3][0] * m[2][1];
+ var tmp_14 = m[1][0] * m[3][1];
+ var tmp_15 = m[3][0] * m[1][1];
+ var tmp_16 = m[1][0] * m[2][1];
+ var tmp_17 = m[2][0] * m[1][1];
+ var tmp_18 = m[0][0] * m[3][1];
+ var tmp_19 = m[3][0] * m[0][1];
+ var tmp_20 = m[0][0] * m[2][1];
+ var tmp_21 = m[2][0] * m[0][1];
+ var tmp_22 = m[0][0] * m[1][1];
+ var tmp_23 = m[1][0] * m[0][1];
+
+ var t0 = (tmp_0 * m[1][1] + tmp_3 * m[2][1] + tmp_4 * m[3][1]) -
+ (tmp_1 * m[1][1] + tmp_2 * m[2][1] + tmp_5 * m[3][1]);
+ var t1 = (tmp_1 * m[0][1] + tmp_6 * m[2][1] + tmp_9 * m[3][1]) -
+ (tmp_0 * m[0][1] + tmp_7 * m[2][1] + tmp_8 * m[3][1]);
+ var t2 = (tmp_2 * m[0][1] + tmp_7 * m[1][1] + tmp_10 * m[3][1]) -
+ (tmp_3 * m[0][1] + tmp_6 * m[1][1] + tmp_11 * m[3][1]);
+ var t3 = (tmp_5 * m[0][1] + tmp_8 * m[1][1] + tmp_11 * m[2][1]) -
+ (tmp_4 * m[0][1] + tmp_9 * m[1][1] + tmp_10 * m[2][1]);
+
+ var d = 1.0 / (m[0][0] * t0 + m[1][0] * t1 + m[2][0] * t2 + m[3][0] * t3);
+
+ return [[d * t0, d * t1, d * t2, d * t3],
+ [d * ((tmp_1 * m[1][0] + tmp_2 * m[2][0] + tmp_5 * m[3][0]) -
+ (tmp_0 * m[1][0] + tmp_3 * m[2][0] + tmp_4 * m[3][0])),
+ d * ((tmp_0 * m[0][0] + tmp_7 * m[2][0] + tmp_8 * m[3][0]) -
+ (tmp_1 * m[0][0] + tmp_6 * m[2][0] + tmp_9 * m[3][0])),
+ d * ((tmp_3 * m[0][0] + tmp_6 * m[1][0] + tmp_11 * m[3][0]) -
+ (tmp_2 * m[0][0] + tmp_7 * m[1][0] + tmp_10 * m[3][0])),
+ d * ((tmp_4 * m[0][0] + tmp_9 * m[1][0] + tmp_10 * m[2][0]) -
+ (tmp_5 * m[0][0] + tmp_8 * m[1][0] + tmp_11 * m[2][0]))],
+ [d * ((tmp_12 * m[1][3] + tmp_15 * m[2][3] + tmp_16 * m[3][3]) -
+ (tmp_13 * m[1][3] + tmp_14 * m[2][3] + tmp_17 * m[3][3])),
+ d * ((tmp_13 * m[0][3] + tmp_18 * m[2][3] + tmp_21 * m[3][3]) -
+ (tmp_12 * m[0][3] + tmp_19 * m[2][3] + tmp_20 * m[3][3])),
+ d * ((tmp_14 * m[0][3] + tmp_19 * m[1][3] + tmp_22 * m[3][3]) -
+ (tmp_15 * m[0][3] + tmp_18 * m[1][3] + tmp_23 * m[3][3])),
+ d * ((tmp_17 * m[0][3] + tmp_20 * m[1][3] + tmp_23 * m[2][3]) -
+ (tmp_16 * m[0][3] + tmp_21 * m[1][3] + tmp_22 * m[2][3]))],
+ [d * ((tmp_14 * m[2][2] + tmp_17 * m[3][2] + tmp_13 * m[1][2]) -
+ (tmp_16 * m[3][2] + tmp_12 * m[1][2] + tmp_15 * m[2][2])),
+ d * ((tmp_20 * m[3][2] + tmp_12 * m[0][2] + tmp_19 * m[2][2]) -
+ (tmp_18 * m[2][2] + tmp_21 * m[3][2] + tmp_13 * m[0][2])),
+ d * ((tmp_18 * m[1][2] + tmp_23 * m[3][2] + tmp_15 * m[0][2]) -
+ (tmp_22 * m[3][2] + tmp_14 * m[0][2] + tmp_19 * m[1][2])),
+ d * ((tmp_22 * m[2][2] + tmp_16 * m[0][2] + tmp_21 * m[1][2]) -
+ (tmp_20 * m[1][2] + tmp_23 * m[2][2] + tmp_17 * m[0][2]))]];
+};
+
+/**
+ * Computes the determinant of the cofactor matrix obtained by removal
+ * of a specified row and column. This is a helper function for the general
+ * determinant and matrix inversion functions.
+ * @param {!o3djs.math.Matrix} a The original matrix.
+ * @param {number} x The row to be removed.
+ * @param {number} y The column to be removed.
+ * @return {number} The determinant of the matrix obtained by removing
+ * row x and column y from a.
+ */
+o3djs.math.codet = function(a, x, y) {
+ var size = a.length;
+ var b = [];
+ var ai = 0;
+ for (var bi = 0; bi < size - 1; ++bi) {
+ if (ai == x)
+ ai++;
+ b[bi] = [];
+ var aj = 0;
+ for (var bj = 0; bj < size - 1; ++bj) {
+ if (aj == y)
+ aj++;
+ b[bi][bj] = a[ai][aj];
+ aj++;
+ }
+ ai++;
+ }
+ return o3djs.math.det(b);
+};
+
+/**
+ * Computes the determinant of an arbitrary square matrix.
+ * @param {!o3djs.math.Matrix} m The matrix.
+ * @return {number} the determinant of m.
+ */
+o3djs.math.det = function(m) {
+ var d = m.length;
+ if (d <= 4) {
+ return o3djs.math['det' + d](m);
+ }
+ var r = 0.0;
+ var sign = 1;
+ var row = m[0];
+ var mLength = m.length;
+ for (var y = 0; y < mLength; y++) {
+ r += sign * row[y] * o3djs.math.codet(m, 0, y);
+ sign *= -1;
+ }
+ return r;
+};
+
+/**
+ * Computes the inverse of an arbitrary square matrix.
+ * @param {!o3djs.math.Matrix} m The matrix.
+ * @return {!o3djs.math.Matrix} The inverse of m.
+ */
+o3djs.math.inverse = function(m) {
+ var d = m.length;
+ if (d <= 4) {
+ return o3djs.math['inverse' + d](m);
+ }
+ var r = [];
+ var size = m.length;
+ for (var j = 0; j < size; ++j) {
+ r[j] = [];
+ for (var i = 0; i < size; ++i)
+ r[j][i] = ((i + j) % 2 ? -1 : 1) * o3djs.math.codet(m, i, j);
+ }
+ return o3djs.math.divMatrixScalar(r, o3djs.math.det(m));
+};
+
+/**
+ * Performs Graham-Schmidt orthogonalization on the vectors which make up the
+ * given matrix and returns the result in the rows of a new matrix. When
+ * multiplying many orthogonal matrices together, errors can accumulate causing
+ * the product to fail to be orthogonal. This function can be used to correct
+ * that.
+ * @param {!o3djs.math.Matrix} m The matrix.
+ * @return {!o3djs.math.Matrix} A matrix whose rows are obtained from the
+ * rows of m by the Graham-Schmidt process.
+ */
+o3djs.math.orthonormalize = function(m) {
+ var r = [];
+ var mLength = m.length;
+ for (var i = 0; i < mLength; ++i) {
+ var v = m[i];
+ for (var j = 0; j < i; ++j) {
+ v = o3djs.math.subVector(v, o3djs.math.mulScalarVector(
+ o3djs.math.dot(r[j], m[i]), r[j]));
+ }
+ r[i] = o3djs.math.normalize(v);
+ }
+ return r;
+};
+
+/**
+ * Computes the inverse of a 4-by-4 matrix.
+ * Note: It is faster to call this than o3djs.math.inverse.
+ * @param {!o3djs.math.Matrix4} m The matrix.
+ * @return {!o3djs.math.Matrix4} The inverse of m.
+ */
+o3djs.math.matrix4.inverse = function(m) {
+ return o3djs.math.inverse4(m);
+};
+
+/**
+ * Multiplies two 4-by-4 matrices; assumes that the given matrices are 4-by-4.
+ * Note: It is faster to call this than o3djs.math.mul.
+ * @param {!o3djs.math.Matrix4} a The matrix on the left.
+ * @param {!o3djs.math.Matrix4} b The matrix on the right.
+ * @return {!o3djs.math.Matrix4} The matrix product of a and b.
+ */
+o3djs.math.matrix4.mul = function(a, b) {
+ return o3djs.math.mulMatrixMatrix4(a, b);
+};
+
+/**
+ * Computes the determinant of a 4-by-4 matrix.
+ * Note: It is faster to call this than o3djs.math.det.
+ * @param {!o3djs.math.Matrix4} m The matrix.
+ * @return {number} The determinant of m.
+ */
+o3djs.math.matrix4.det = function(m) {
+ return o3djs.math.det4(m);
+};
+
+/**
+ * Copies a Matrix4.
+ * Note: It is faster to call this than o3djs.math.copy.
+ * @param {!o3djs.math.Matrix4} m The matrix.
+ * @return {!o3djs.math.Matrix4} A copy of m.
+ */
+o3djs.math.matrix4.copy = function(m) {
+ return o3djs.math.copyMatrix(m);
+};
+
+/**
+ * Sets the upper 3-by-3 block of matrix a to the upper 3-by-3 block of matrix
+ * b; assumes that a and b are big enough to contain an upper 3-by-3 block.
+ * @param {!o3djs.math.Matrix4} a A matrix.
+ * @param {!o3djs.math.Matrix3} b A 3-by-3 matrix.
+ * @return {!o3djs.math.Matrix4} a once modified.
+ */
+o3djs.math.matrix4.setUpper3x3 = function(a, b) {
+ var b0 = b[0];
+ var b1 = b[1];
+ var b2 = b[2];
+
+ a[0].splice(0, 3, b0[0], b0[1], b0[2]);
+ a[1].splice(0, 3, b1[0], b1[1], b1[2]);
+ a[2].splice(0, 3, b2[0], b2[1], b2[2]);
+
+ return a;
+};
+
+/**
+ * Returns a 3-by-3 matrix mimicking the upper 3-by-3 block of m; assumes m
+ * is big enough to contain an upper 3-by-3 block.
+ * @param {!o3djs.math.Matrix4} m The matrix.
+ * @return {!o3djs.math.Matrix3} The upper 3-by-3 block of m.
+ */
+o3djs.math.matrix4.getUpper3x3 = function(m) {
+ return [m[0].slice(0, 3), m[1].slice(0, 3), m[2].slice(0, 3)];
+};
+
+/**
+ * Sets the translation component of a 4-by-4 matrix to the given
+ * vector.
+ * @param {!o3djs.math.Matrix4} a The matrix.
+ * @param {(!o3djs.math.Vector3|!o3djs.math.Vector4)} v The vector.
+ * @return {!o3djs.math.Matrix4} a once modified.
+ */
+o3djs.math.matrix4.setTranslation = function(a, v) {
+ a[3].splice(0, 4, v[0], v[1], v[2], 1);
+ return a;
+};
+
+/**
+ * Returns the translation component of a 4-by-4 matrix as a vector with 3
+ * entries.
+ * @param {!o3djs.math.Matrix4} m The matrix.
+ * @return {!o3djs.math.Vector3} The translation component of m.
+ */
+o3djs.math.matrix4.getTranslation = function(m) {
+ return m[3].slice(0, 3);
+};
+
+/**
+ * Takes a 4-by-4 matrix and a vector with 3 entries,
+ * interprets the vector as a point, transforms that point by the matrix, and
+ * returns the result as a vector with 3 entries.
+ * @param {!o3djs.math.Matrix4} m The matrix.
+ * @param {!o3djs.math.Vector3} v The point.
+ * @return {!o3djs.math.Vector3} The transformed point.
+ */
+o3djs.math.matrix4.transformPoint = function(m, v) {
+ var v0 = v[0];
+ var v1 = v[1];
+ var v2 = v[2];
+ var m0 = m[0];
+ var m1 = m[1];
+ var m2 = m[2];
+ var m3 = m[3];
+
+ var d = v0 * m0[3] + v1 * m1[3] + v2 * m2[3] + m3[3];
+ return [(v0 * m0[0] + v1 * m1[0] + v2 * m2[0] + m3[0]) / d,
+ (v0 * m0[1] + v1 * m1[1] + v2 * m2[1] + m3[1]) / d,
+ (v0 * m0[2] + v1 * m1[2] + v2 * m2[2] + m3[2]) / d];
+};
+
+/**
+ * Takes a 4-by-4 matrix and a vector with 4 entries, transforms that vector by
+ * the matrix, and returns the result as a vector with 4 entries.
+ * @param {!o3djs.math.Matrix4} m The matrix.
+ * @param {!o3djs.math.Vector4} v The point in homogenous coordinates.
+ * @return {!o3djs.math.Vector4} The transformed point in homogenous
+ * coordinates.
+ */
+o3djs.math.matrix4.transformVector4 = function(m, v) {
+ var v0 = v[0];
+ var v1 = v[1];
+ var v2 = v[2];
+ var v3 = v[3];
+ var m0 = m[0];
+ var m1 = m[1];
+ var m2 = m[2];
+ var m3 = m[3];
+
+ return [v0 * m0[0] + v1 * m1[0] + v2 * m2[0] + v3 * m3[0],
+ v0 * m0[1] + v1 * m1[1] + v2 * m2[1] + v3 * m3[1],
+ v0 * m0[2] + v1 * m1[2] + v2 * m2[2] + v3 * m3[2],
+ v0 * m0[3] + v1 * m1[3] + v2 * m2[3] + v3 * m3[3]];
+};
+
+/**
+ * Takes a 4-by-4 matrix and a vector with 3 entries, interprets the vector as a
+ * direction, transforms that direction by the matrix, and returns the result;
+ * assumes the transformation of 3-dimensional space represented by the matrix
+ * is parallel-preserving, i.e. any combination of rotation, scaling and
+ * translation, but not a perspective distortion. Returns a vector with 3
+ * entries.
+ * @param {!o3djs.math.Matrix4} m The matrix.
+ * @param {!o3djs.math.Vector3} v The direction.
+ * @return {!o3djs.math.Vector3} The transformed direction.
+ */
+o3djs.math.matrix4.transformDirection = function(m, v) {
+ var v0 = v[0];
+ var v1 = v[1];
+ var v2 = v[2];
+ var m0 = m[0];
+ var m1 = m[1];
+ var m2 = m[2];
+ var m3 = m[3];
+
+ return [v0 * m0[0] + v1 * m1[0] + v2 * m2[0],
+ v0 * m0[1] + v1 * m1[1] + v2 * m2[1],
+ v0 * m0[2] + v1 * m1[2] + v2 * m2[2]];
+};
+
+/**
+ * Takes a 4-by-4 matrix m and a vector v with 3 entries, interprets the vector
+ * as a normal to a surface, and computes a vector which is normal upon
+ * transforming that surface by the matrix. The effect of this function is the
+ * same as transforming v (as a direction) by the inverse-transpose of m. This
+ * function assumes the transformation of 3-dimensional space represented by the
+ * matrix is parallel-preserving, i.e. any combination of rotation, scaling and
+ * translation, but not a perspective distortion. Returns a vector with 3
+ * entries.
+ * @param {!o3djs.math.Matrix4} m The matrix.
+ * @param {!o3djs.math.Vector3} v The normal.
+ * @return {!o3djs.math.Vector3} The transformed normal.
+ */
+o3djs.math.matrix4.transformNormal = function(m, v) {
+ var mInverse = o3djs.math.inverse4(m);
+ var v0 = v[0];
+ var v1 = v[1];
+ var v2 = v[2];
+ var mi0 = mInverse[0];
+ var mi1 = mInverse[1];
+ var mi2 = mInverse[2];
+ var mi3 = mInverse[3];
+
+ return [v0 * mi0[0] + v1 * mi0[1] + v2 * mi0[2],
+ v0 * mi1[0] + v1 * mi1[1] + v2 * mi1[2],
+ v0 * mi2[0] + v1 * mi2[1] + v2 * mi2[2]];
+};
+
+/**
+ * Creates a 4-by-4 identity matrix.
+ * @return {!o3djs.math.Matrix4} The 4-by-4 identity.
+ */
+o3djs.math.matrix4.identity = function() {
+ return [
+ [1, 0, 0, 0],
+ [0, 1, 0, 0],
+ [0, 0, 1, 0],
+ [0, 0, 0, 1]
+ ];
+};
+
+/**
+ * Computes a 4-by-4 perspective transformation matrix given the angular height
+ * of the frustum, the aspect ratio, and the near and far clipping planes. The
+ * arguments define a frustum extending in the negative z direction. The given
+ * angle is the vertical angle of the frustum, and the horizontal angle is
+ * determined to produce the given aspect ratio. The arguments near and far are
+ * the distances to the near and far clipping planes. Note that near and far
+ * are not z coordinates, but rather they are distances along the negative
+ * z-axis. The matrix generated sends the viewing frustum to the unit box.
+ * We assume a unit box extending from -1 to 1 in the x and y dimensions and
+ * from 0 to 1 in the z dimension.
+ * @param {number} angle The camera angle from top to bottom (in radians).
+ * @param {number} aspect The aspect ratio width / height.
+ * @param {number} near The depth (negative z coordinate)
+ * of the near clipping plane.
+ * @param {number} far The depth (negative z coordinate)
+ * of the far clipping plane.
+ * @return {!o3djs.math.Matrix4} The perspective matrix.
+ */
+o3djs.math.matrix4.perspective = function(angle, aspect, near, far) {
+ var f = Math.tan(0.5 * (Math.PI - angle));
+ var range = near - far;
+
+ return [
+ [f / aspect, 0, 0, 0],
+ [0, f, 0, 0],
+ [0, 0, far / range, -1],
+ [0, 0, near * far / range, 0]
+ ];
+};
+
+/**
+ * Computes a 4-by-4 orthographic projection matrix given the coordinates of the
+ * planes defining the axis-aligned, box-shaped viewing volume. The matrix
+ * generated sends that box to the unit box. Note that although left and right
+ * are x coordinates and bottom and top are y coordinates, near and far
+ * are not z coordinates, but rather they are distances along the negative
+ * z-axis. We assume a unit box extending from -1 to 1 in the x and y
+ * dimensions and from 0 to 1 in the z dimension.
+ * @param {number} left The x coordinate of the left plane of the box.
+ * @param {number} right The x coordinate of the right plane of the box.
+ * @param {number} bottom The y coordinate of the bottom plane of the box.
+ * @param {number} top The y coordinate of the right plane of the box.
+ * @param {number} near The negative z coordinate of the near plane of the box.
+ * @param {number} far The negative z coordinate of the far plane of the box.
+ * @return {!o3djs.math.Matrix4} The orthographic projection matrix.
+ */
+o3djs.math.matrix4.orthographic =
+ function(left, right, bottom, top, near, far) {
+ return [
+ [2 / (right - left), 0, 0, 0],
+ [0, 2 / (top - bottom), 0, 0],
+ [0, 0, 1 / (near - far), 0],
+ [(left + right) / (left - right),
+ (bottom + top) / (bottom - top),
+ near / (near - far), 1]
+ ];
+};
+
+/**
+ * Computes a 4-by-4 perspective transformation matrix given the left, right,
+ * top, bottom, near and far clipping planes. The arguments define a frustum
+ * extending in the negative z direction. The arguments near and far are the
+ * distances to the near and far clipping planes. Note that near and far are not
+ * z coordinates, but rather they are distances along the negative z-axis. The
+ * matrix generated sends the viewing frustum to the unit box. We assume a unit
+ * box extending from -1 to 1 in the x and y dimensions and from 0 to 1 in the z
+ * dimension.
+ * @param {number} left The x coordinate of the left plane of the box.
+ * @param {number} right The x coordinate of the right plane of the box.
+ * @param {number} bottom The y coordinate of the bottom plane of the box.
+ * @param {number} top The y coordinate of the right plane of the box.
+ * @param {number} near The negative z coordinate of the near plane of the box.
+ * @param {number} far The negative z coordinate of the far plane of the box.
+ * @return {!o3djs.math.Matrix4} The perspective projection matrix.
+ */
+o3djs.math.matrix4.frustum = function(left, right, bottom, top, near, far) {
+ var dx = (right - left);
+ var dy = (top - bottom);
+ var dz = (near - far);
+ return [
+ [2 * near / dx, 0, 0, 0],
+ [0, 2 * near / dy, 0, 0],
+ [(left + right) / dx, (top + bottom) / dy, far / dz, -1],
+ [0, 0, near * far / dz, 0]];
+};
+
+/**
+ * Computes a 4-by-4 look-at transformation. The transformation generated is
+ * an orthogonal rotation matrix with translation component. The translation
+ * component sends the eye to the origin. The rotation component sends the
+ * vector pointing from the eye to the target to a vector pointing in the
+ * negative z direction, and also sends the up vector into the upper half of
+ * the yz plane.
+ * @param {(!o3djs.math.Vector3|!o3djs.math.Vector4)} eye The position
+ * of the eye.
+ * @param {(!o3djs.math.Vector3|!o3djs.math.Vector4)} target The
+ * position meant to be viewed.
+ * @param {(!o3djs.math.Vector3|!o3djs.math.Vector4)} up A vector
+ * pointing up.
+ * @return {!o3djs.math.Matrix4} The look-at matrix.
+ */
+o3djs.math.matrix4.lookAt = function(eye, target, up) {
+ var vz = o3djs.math.normalize(
+ o3djs.math.subVector(eye, target).slice(0, 3)).concat(0);
+ var vx = o3djs.math.normalize(
+ o3djs.math.cross(up, vz)).concat(0);
+ var vy = o3djs.math.cross(vz, vx).concat(0);
+
+ return o3djs.math.inverse([vx, vy, vz, eye.concat(1)]);
+};
+
+/**
+ * Takes two 4-by-4 matrices, a and b, and computes the product in the order
+ * that pre-composes b with a. In other words, the matrix returned will
+ * transform by b first and then a. Note this is subtly different from just
+ * multiplying the matrices together. For given a and b, this function returns
+ * the same object in both row-major and column-major mode.
+ * @param {!o3djs.math.Matrix4} a A 4-by-4 matrix.
+ * @param {!o3djs.math.Matrix4} b A 4-by-4 matrix.
+ * @return {!o3djs.math.Matrix4} the composition of a and b, b first then a.
+ */
+o3djs.math.matrix4.composition = function(a, b) {
+ var a0 = a[0];
+ var a1 = a[1];
+ var a2 = a[2];
+ var a3 = a[3];
+ var b0 = b[0];
+ var b1 = b[1];
+ var b2 = b[2];
+ var b3 = b[3];
+ var a00 = a0[0];
+ var a01 = a0[1];
+ var a02 = a0[2];
+ var a03 = a0[3];
+ var a10 = a1[0];
+ var a11 = a1[1];
+ var a12 = a1[2];
+ var a13 = a1[3];
+ var a20 = a2[0];
+ var a21 = a2[1];
+ var a22 = a2[2];
+ var a23 = a2[3];
+ var a30 = a3[0];
+ var a31 = a3[1];
+ var a32 = a3[2];
+ var a33 = a3[3];
+ var b00 = b0[0];
+ var b01 = b0[1];
+ var b02 = b0[2];
+ var b03 = b0[3];
+ var b10 = b1[0];
+ var b11 = b1[1];
+ var b12 = b1[2];
+ var b13 = b1[3];
+ var b20 = b2[0];
+ var b21 = b2[1];
+ var b22 = b2[2];
+ var b23 = b2[3];
+ var b30 = b3[0];
+ var b31 = b3[1];
+ var b32 = b3[2];
+ var b33 = b3[3];
+ return [[a00 * b00 + a10 * b01 + a20 * b02 + a30 * b03,
+ a01 * b00 + a11 * b01 + a21 * b02 + a31 * b03,
+ a02 * b00 + a12 * b01 + a22 * b02 + a32 * b03,
+ a03 * b00 + a13 * b01 + a23 * b02 + a33 * b03],
+ [a00 * b10 + a10 * b11 + a20 * b12 + a30 * b13,
+ a01 * b10 + a11 * b11 + a21 * b12 + a31 * b13,
+ a02 * b10 + a12 * b11 + a22 * b12 + a32 * b13,
+ a03 * b10 + a13 * b11 + a23 * b12 + a33 * b13],
+ [a00 * b20 + a10 * b21 + a20 * b22 + a30 * b23,
+ a01 * b20 + a11 * b21 + a21 * b22 + a31 * b23,
+ a02 * b20 + a12 * b21 + a22 * b22 + a32 * b23,
+ a03 * b20 + a13 * b21 + a23 * b22 + a33 * b23],
+ [a00 * b30 + a10 * b31 + a20 * b32 + a30 * b33,
+ a01 * b30 + a11 * b31 + a21 * b32 + a31 * b33,
+ a02 * b30 + a12 * b31 + a22 * b32 + a32 * b33,
+ a03 * b30 + a13 * b31 + a23 * b32 + a33 * b33]];
+};
+
+/**
+ * Takes two 4-by-4 matrices, a and b, and modifies a to be the product in the
+ * order that pre-composes b with a. The matrix a, upon modification will
+ * transform by b first and then a. Note this is subtly different from just
+ * multiplying the matrices together. For given a and b, a, upon modification,
+ * will be the same object in both row-major and column-major mode.
+ * @param {!o3djs.math.Matrix4} a A 4-by-4 matrix.
+ * @param {!o3djs.math.Matrix4} b A 4-by-4 matrix.
+ * @return {!o3djs.math.Matrix4} a once modified.
+ */
+o3djs.math.matrix4.compose = function(a, b) {
+ var a0 = a[0];
+ var a1 = a[1];
+ var a2 = a[2];
+ var a3 = a[3];
+ var b0 = b[0];
+ var b1 = b[1];
+ var b2 = b[2];
+ var b3 = b[3];
+ var a00 = a0[0];
+ var a01 = a0[1];
+ var a02 = a0[2];
+ var a03 = a0[3];
+ var a10 = a1[0];
+ var a11 = a1[1];
+ var a12 = a1[2];
+ var a13 = a1[3];
+ var a20 = a2[0];
+ var a21 = a2[1];
+ var a22 = a2[2];
+ var a23 = a2[3];
+ var a30 = a3[0];
+ var a31 = a3[1];
+ var a32 = a3[2];
+ var a33 = a3[3];
+ var b00 = b0[0];
+ var b01 = b0[1];
+ var b02 = b0[2];
+ var b03 = b0[3];
+ var b10 = b1[0];
+ var b11 = b1[1];
+ var b12 = b1[2];
+ var b13 = b1[3];
+ var b20 = b2[0];
+ var b21 = b2[1];
+ var b22 = b2[2];
+ var b23 = b2[3];
+ var b30 = b3[0];
+ var b31 = b3[1];
+ var b32 = b3[2];
+ var b33 = b3[3];
+ a[0].splice(0, 4, a00 * b00 + a10 * b01 + a20 * b02 + a30 * b03,
+ a01 * b00 + a11 * b01 + a21 * b02 + a31 * b03,
+ a02 * b00 + a12 * b01 + a22 * b02 + a32 * b03,
+ a03 * b00 + a13 * b01 + a23 * b02 + a33 * b03);
+ a[1].splice(0, 4, a00 * b10 + a10 * b11 + a20 * b12 + a30 * b13,
+ a01 * b10 + a11 * b11 + a21 * b12 + a31 * b13,
+ a02 * b10 + a12 * b11 + a22 * b12 + a32 * b13,
+ a03 * b10 + a13 * b11 + a23 * b12 + a33 * b13);
+ a[2].splice(0, 4, a00 * b20 + a10 * b21 + a20 * b22 + a30 * b23,
+ a01 * b20 + a11 * b21 + a21 * b22 + a31 * b23,
+ a02 * b20 + a12 * b21 + a22 * b22 + a32 * b23,
+ a03 * b20 + a13 * b21 + a23 * b22 + a33 * b23),
+ a[3].splice(0, 4, a00 * b30 + a10 * b31 + a20 * b32 + a30 * b33,
+ a01 * b30 + a11 * b31 + a21 * b32 + a31 * b33,
+ a02 * b30 + a12 * b31 + a22 * b32 + a32 * b33,
+ a03 * b30 + a13 * b31 + a23 * b32 + a33 * b33);
+ return a;
+};
+
+/**
+ * Creates a 4-by-4 matrix which translates by the given vector v.
+ * @param {(!o3djs.math.Vector3|!o3djs.math.Vector4)} v The vector by
+ * which to translate.
+ * @return {!o3djs.math.Matrix4} The translation matrix.
+ */
+o3djs.math.matrix4.translation = function(v) {
+ return [
+ [1, 0, 0, 0],
+ [0, 1, 0, 0],
+ [0, 0, 1, 0],
+ [v[0], v[1], v[2], 1]
+ ];
+};
+
+/**
+ * Modifies the given 4-by-4 matrix by translation by the given vector v.
+ * @param {!o3djs.math.Matrix4} m The matrix.
+ * @param {(!o3djs.math.Vector3|!o3djs.math.Vector4)} v The vector by
+ * which to translate.
+ * @return {!o3djs.math.Matrix4} m once modified.
+ */
+o3djs.math.matrix4.translate = function(m, v) {
+ var v0 = v[0];
+ var v1 = v[1];
+ var v2 = v[2];
+ var m0 = m[0];
+ var m1 = m[1];
+ var m2 = m[2];
+ var m3 = m[3];
+ var m00 = m0[0];
+ var m01 = m0[1];
+ var m02 = m0[2];
+ var m03 = m0[3];
+ var m10 = m1[0];
+ var m11 = m1[1];
+ var m12 = m1[2];
+ var m13 = m1[3];
+ var m20 = m2[0];
+ var m21 = m2[1];
+ var m22 = m2[2];
+ var m23 = m2[3];
+ var m30 = m3[0];
+ var m31 = m3[1];
+ var m32 = m3[2];
+ var m33 = m3[3];
+
+ m3.splice(0, 4, m00 * v0 + m10 * v1 + m20 * v2 + m30,
+ m01 * v0 + m11 * v1 + m21 * v2 + m31,
+ m02 * v0 + m12 * v1 + m22 * v2 + m32,
+ m03 * v0 + m13 * v1 + m23 * v2 + m33);
+
+ return m;
+};
+
+/**
+ * Creates a 4-by-4 matrix which scales in each dimension by an amount given by
+ * the corresponding entry in the given vector; assumes the vector has three
+ * entries.
+ * @param {!o3djs.math.Vector3} v A vector of
+ * three entries specifying the factor by which to scale in each dimension.
+ * @return {!o3djs.math.Matrix4} The scaling matrix.
+ */
+o3djs.math.matrix4.scaling = function(v) {
+ return [
+ [v[0], 0, 0, 0],
+ [0, v[1], 0, 0],
+ [0, 0, v[2], 0],
+ [0, 0, 0, 1]
+ ];
+};
+
+/**
+ * Modifies the given 4-by-4 matrix, scaling in each dimension by an amount
+ * given by the corresponding entry in the given vector; assumes the vector has
+ * three entries.
+ * @param {!o3djs.math.Matrix4} m The matrix to be modified.
+ * @param {!o3djs.math.Vector3} v A vector of three entries specifying the
+ * factor by which to scale in each dimension.
+ * @return {!o3djs.math.Matrix4} m once modified.
+ */
+o3djs.math.matrix4.scale = function(m, v) {
+ var v0 = v[0];
+ var v1 = v[1];
+ var v2 = v[2];
+
+ var m0 = m[0];
+ var m1 = m[1];
+ var m2 = m[2];
+ var m3 = m[3];
+
+ m0.splice(0, 4, v0 * m0[0], v0 * m0[1], v0 * m0[2], v0 * m0[3]);
+ m1.splice(0, 4, v1 * m1[0], v1 * m1[1], v1 * m1[2], v1 * m1[3]);
+ m2.splice(0, 4, v2 * m2[0], v2 * m2[1], v2 * m2[2], v2 * m2[3]);
+
+ return m;
+};
+
+/**
+ * Creates a 4-by-4 matrix which rotates around the x-axis by the given angle.
+ * @param {number} angle The angle by which to rotate (in radians).
+ * @return {!o3djs.math.Matrix4} The rotation matrix.
+ */
+o3djs.math.matrix4.rotationX = function(angle) {
+ var c = Math.cos(angle);
+ var s = Math.sin(angle);
+
+ return [
+ [1, 0, 0, 0],
+ [0, c, s, 0],
+ [0, -s, c, 0],
+ [0, 0, 0, 1]
+ ];
+};
+
+/**
+ * Modifies the given 4-by-4 matrix by a rotation around the x-axis by the given
+ * angle.
+ * @param {!o3djs.math.Matrix4} m The matrix.
+ * @param {number} angle The angle by which to rotate (in radians).
+ * @return {!o3djs.math.Matrix4} m once modified.
+ */
+o3djs.math.matrix4.rotateX = function(m, angle) {
+ var m0 = m[0];
+ var m1 = m[1];
+ var m2 = m[2];
+ var m3 = m[3];
+ var m10 = m1[0];
+ var m11 = m1[1];
+ var m12 = m1[2];
+ var m13 = m1[3];
+ var m20 = m2[0];
+ var m21 = m2[1];
+ var m22 = m2[2];
+ var m23 = m2[3];
+ var c = Math.cos(angle);
+ var s = Math.sin(angle);
+
+ m1.splice(0, 4, c * m10 + s * m20,
+ c * m11 + s * m21,
+ c * m12 + s * m22,
+ c * m13 + s * m23);
+ m2.splice(0, 4, c * m20 - s * m10,
+ c * m21 - s * m11,
+ c * m22 - s * m12,
+ c * m23 - s * m13);
+
+ return m;
+};
+
+/**
+ * Creates a 4-by-4 matrix which rotates around the y-axis by the given angle.
+ * @param {number} angle The angle by which to rotate (in radians).
+ * @return {!o3djs.math.Matrix4} The rotation matrix.
+ */
+o3djs.math.matrix4.rotationY = function(angle) {
+ var c = Math.cos(angle);
+ var s = Math.sin(angle);
+
+ return [
+ [c, 0, -s, 0],
+ [0, 1, 0, 0],
+ [s, 0, c, 0],
+ [0, 0, 0, 1]
+ ];
+};
+
+/**
+ * Modifies the given 4-by-4 matrix by a rotation around the y-axis by the given
+ * angle.
+ * @param {!o3djs.math.Matrix4} m The matrix.
+ * @param {number} angle The angle by which to rotate (in radians).
+ * @return {!o3djs.math.Matrix4} m once modified.
+ */
+o3djs.math.matrix4.rotateY = function(m, angle) {
+ var m0 = m[0];
+ var m1 = m[1];
+ var m2 = m[2];
+ var m3 = m[3];
+ var m00 = m0[0];
+ var m01 = m0[1];
+ var m02 = m0[2];
+ var m03 = m0[3];
+ var m20 = m2[0];
+ var m21 = m2[1];
+ var m22 = m2[2];
+ var m23 = m2[3];
+ var c = Math.cos(angle);
+ var s = Math.sin(angle);
+
+ m0.splice(0, 4, c * m00 - s * m20,
+ c * m01 - s * m21,
+ c * m02 - s * m22,
+ c * m03 - s * m23);
+ m2.splice(0, 4, c * m20 + s * m00,
+ c * m21 + s * m01,
+ c * m22 + s * m02,
+ c * m23 + s * m03);
+
+ return m;
+};
+
+/**
+ * Creates a 4-by-4 matrix which rotates around the z-axis by the given angle.
+ * @param {number} angle The angle by which to rotate (in radians).
+ * @return {!o3djs.math.Matrix4} The rotation matrix.
+ */
+o3djs.math.matrix4.rotationZ = function(angle) {
+ var c = Math.cos(angle);
+ var s = Math.sin(angle);
+
+ return [
+ [c, s, 0, 0],
+ [-s, c, 0, 0],
+ [0, 0, 1, 0],
+ [0, 0, 0, 1]
+ ];
+};
+
+/**
+ * Modifies the given 4-by-4 matrix by a rotation around the z-axis by the given
+ * angle.
+ * @param {!o3djs.math.Matrix4} m The matrix.
+ * @param {number} angle The angle by which to rotate (in radians).
+ * @return {!o3djs.math.Matrix4} m once modified.
+ */
+o3djs.math.matrix4.rotateZ = function(m, angle) {
+ var m0 = m[0];
+ var m1 = m[1];
+ var m2 = m[2];
+ var m3 = m[3];
+ var m00 = m0[0];
+ var m01 = m0[1];
+ var m02 = m0[2];
+ var m03 = m0[3];
+ var m10 = m1[0];
+ var m11 = m1[1];
+ var m12 = m1[2];
+ var m13 = m1[3];
+ var c = Math.cos(angle);
+ var s = Math.sin(angle);
+
+ m0.splice(0, 4, c * m00 + s * m10,
+ c * m01 + s * m11,
+ c * m02 + s * m12,
+ c * m03 + s * m13);
+ m1.splice(0, 4, c * m10 - s * m00,
+ c * m11 - s * m01,
+ c * m12 - s * m02,
+ c * m13 - s * m03);
+
+ return m;
+};
+
+/**
+ * Creates a 4-by-4 rotation matrix. Interprets the entries of the given
+ * vector as angles by which to rotate around the x, y and z axes, returns a
+ * a matrix which rotates around the x-axis first, then the y-axis, then the
+ * z-axis.
+ * @param {!o3djs.math.Vector3} v A vector of angles (in radians).
+ * @return {!o3djs.math.Matrix4} The rotation matrix.
+ */
+o3djs.math.matrix4.rotationZYX = function(v) {
+ var sinx = Math.sin(v[0]);
+ var cosx = Math.cos(v[0]);
+ var siny = Math.sin(v[1]);
+ var cosy = Math.cos(v[1]);
+ var sinz = Math.sin(v[2]);
+ var cosz = Math.cos(v[2]);
+
+ var coszsiny = cosz * siny;
+ var sinzsiny = sinz * siny;
+
+ return [
+ [cosz * cosy, sinz * cosy, -siny, 0],
+ [coszsiny * sinx - sinz * cosx,
+ sinzsiny * sinx + cosz * cosx,
+ cosy * sinx,
+ 0],
+ [coszsiny * cosx + sinz * sinx,
+ sinzsiny * cosx - cosz * sinx,
+ cosy * cosx,
+ 0],
+ [0, 0, 0, 1]
+ ];
+};
+
+/**
+ * Modifies a 4-by-4 matrix by a rotation. Interprets the coordinates of the
+ * given vector as angles by which to rotate around the x, y and z axes, rotates
+ * around the x-axis first, then the y-axis, then the z-axis.
+ * @param {!o3djs.math.Matrix4} m The matrix.
+ * @param {!o3djs.math.Vector3} v A vector of angles (in radians).
+ * @return {!o3djs.math.Matrix4} m once modified.
+ */
+o3djs.math.matrix4.rotateZYX = function(m, v) {
+ var sinX = Math.sin(v[0]);
+ var cosX = Math.cos(v[0]);
+ var sinY = Math.sin(v[1]);
+ var cosY = Math.cos(v[1]);
+ var sinZ = Math.sin(v[2]);
+ var cosZ = Math.cos(v[2]);
+
+ var cosZSinY = cosZ * sinY;
+ var sinZSinY = sinZ * sinY;
+
+ var r00 = cosZ * cosY;
+ var r01 = sinZ * cosY;
+ var r02 = -sinY;
+ var r10 = cosZSinY * sinX - sinZ * cosX;
+ var r11 = sinZSinY * sinX + cosZ * cosX;
+ var r12 = cosY * sinX;
+ var r20 = cosZSinY * cosX + sinZ * sinX;
+ var r21 = sinZSinY * cosX - cosZ * sinX;
+ var r22 = cosY * cosX;
+
+ var m0 = m[0];
+ var m1 = m[1];
+ var m2 = m[2];
+ var m3 = m[3];
+
+ var m00 = m0[0];
+ var m01 = m0[1];
+ var m02 = m0[2];
+ var m03 = m0[3];
+ var m10 = m1[0];
+ var m11 = m1[1];
+ var m12 = m1[2];
+ var m13 = m1[3];
+ var m20 = m2[0];
+ var m21 = m2[1];
+ var m22 = m2[2];
+ var m23 = m2[3];
+ var m30 = m3[0];
+ var m31 = m3[1];
+ var m32 = m3[2];
+ var m33 = m3[3];
+
+ m0.splice(0, 4,
+ r00 * m00 + r01 * m10 + r02 * m20,
+ r00 * m01 + r01 * m11 + r02 * m21,
+ r00 * m02 + r01 * m12 + r02 * m22,
+ r00 * m03 + r01 * m13 + r02 * m23);
+
+ m1.splice(0, 4,
+ r10 * m00 + r11 * m10 + r12 * m20,
+ r10 * m01 + r11 * m11 + r12 * m21,
+ r10 * m02 + r11 * m12 + r12 * m22,
+ r10 * m03 + r11 * m13 + r12 * m23);
+
+ m2.splice(0, 4,
+ r20 * m00 + r21 * m10 + r22 * m20,
+ r20 * m01 + r21 * m11 + r22 * m21,
+ r20 * m02 + r21 * m12 + r22 * m22,
+ r20 * m03 + r21 * m13 + r22 * m23);
+
+ return m;
+};
+
+/**
+ * Creates a 4-by-4 matrix which rotates around the given axis by the given
+ * angle.
+ * @param {(!o3djs.math.Vector3|!o3djs.math.Vector4)} axis The axis
+ * about which to rotate.
+ * @param {number} angle The angle by which to rotate (in radians).
+ * @return {!o3djs.math.Matrix4} A matrix which rotates angle radians
+ * around the axis.
+ */
+o3djs.math.matrix4.axisRotation = function(axis, angle) {
+ var x = axis[0];
+ var y = axis[1];
+ var z = axis[2];
+ var n = Math.sqrt(x * x + y * y + z * z);
+ x /= n;
+ y /= n;
+ z /= n;
+ var xx = x * x;
+ var yy = y * y;
+ var zz = z * z;
+ var c = Math.cos(angle);
+ var s = Math.sin(angle);
+ var oneMinusCosine = 1 - c;
+
+ return [
+ [xx + (1 - xx) * c,
+ x * y * oneMinusCosine + z * s,
+ x * z * oneMinusCosine - y * s,
+ 0],
+ [x * y * oneMinusCosine - z * s,
+ yy + (1 - yy) * c,
+ y * z * oneMinusCosine + x * s,
+ 0],
+ [x * z * oneMinusCosine + y * s,
+ y * z * oneMinusCosine - x * s,
+ zz + (1 - zz) * c,
+ 0],
+ [0, 0, 0, 1]
+ ];
+};
+
+/**
+ * Modifies the given 4-by-4 matrix by rotation around the given axis by the
+ * given angle.
+ * @param {!o3djs.math.Matrix4} m The matrix.
+ * @param {(!o3djs.math.Vector3|!o3djs.math.Vector4)} axis The axis
+ * about which to rotate.
+ * @param {number} angle The angle by which to rotate (in radians).
+ * @return {!o3djs.math.Matrix4} m once modified.
+ */
+o3djs.math.matrix4.axisRotate = function(m, axis, angle) {
+ var x = axis[0];
+ var y = axis[1];
+ var z = axis[2];
+ var n = Math.sqrt(x * x + y * y + z * z);
+ x /= n;
+ y /= n;
+ z /= n;
+ var xx = x * x;
+ var yy = y * y;
+ var zz = z * z;
+ var c = Math.cos(angle);
+ var s = Math.sin(angle);
+ var oneMinusCosine = 1 - c;
+
+ var r00 = xx + (1 - xx) * c;
+ var r01 = x * y * oneMinusCosine + z * s;
+ var r02 = x * z * oneMinusCosine - y * s;
+ var r10 = x * y * oneMinusCosine - z * s;
+ var r11 = yy + (1 - yy) * c;
+ var r12 = y * z * oneMinusCosine + x * s;
+ var r20 = x * z * oneMinusCosine + y * s;
+ var r21 = y * z * oneMinusCosine - x * s;
+ var r22 = zz + (1 - zz) * c;
+
+ var m0 = m[0];
+ var m1 = m[1];
+ var m2 = m[2];
+ var m3 = m[3];
+
+ var m00 = m0[0];
+ var m01 = m0[1];
+ var m02 = m0[2];
+ var m03 = m0[3];
+ var m10 = m1[0];
+ var m11 = m1[1];
+ var m12 = m1[2];
+ var m13 = m1[3];
+ var m20 = m2[0];
+ var m21 = m2[1];
+ var m22 = m2[2];
+ var m23 = m2[3];
+ var m30 = m3[0];
+ var m31 = m3[1];
+ var m32 = m3[2];
+ var m33 = m3[3];
+
+ m0.splice(0, 4,
+ r00 * m00 + r01 * m10 + r02 * m20,
+ r00 * m01 + r01 * m11 + r02 * m21,
+ r00 * m02 + r01 * m12 + r02 * m22,
+ r00 * m03 + r01 * m13 + r02 * m23);
+
+ m1.splice(0, 4,
+ r10 * m00 + r11 * m10 + r12 * m20,
+ r10 * m01 + r11 * m11 + r12 * m21,
+ r10 * m02 + r11 * m12 + r12 * m22,
+ r10 * m03 + r11 * m13 + r12 * m23);
+
+ m2.splice(0, 4,
+ r20 * m00 + r21 * m10 + r22 * m20,
+ r20 * m01 + r21 * m11 + r22 * m21,
+ r20 * m02 + r21 * m12 + r22 * m22,
+ r20 * m03 + r21 * m13 + r22 * m23);
+
+ return m;
+};
+
+/**
+ * Sets each function in the namespace o3djs.math to the row major
+ * version in o3djs.math.rowMajor (provided such a function exists in
+ * o3djs.math.rowMajor). Call this function to establish the row major
+ * convention.
+ */
+o3djs.math.installRowMajorFunctions = function() {
+ for (var f in o3djs.math.rowMajor) {
+ o3djs.math[f] = o3djs.math.rowMajor[f];
+ }
+};
+
+/**
+ * Sets each function in the namespace o3djs.math to the column major
+ * version in o3djs.math.columnMajor (provided such a function exists in
+ * o3djs.math.columnMajor). Call this function to establish the column
+ * major convention.
+ */
+o3djs.math.installColumnMajorFunctions = function() {
+ for (var f in o3djs.math.columnMajor) {
+ o3djs.math[f] = o3djs.math.columnMajor[f];
+ }
+};
+
+/**
+ * Sets each function in the namespace o3djs.math to the error checking
+ * version in o3djs.math.errorCheck (provided such a function exists in
+ * o3djs.math.errorCheck).
+ */
+o3djs.math.installErrorCheckFunctions = function() {
+ for (var f in o3djs.math.errorCheck) {
+ o3djs.math[f] = o3djs.math.errorCheck[f];
+ }
+};
+
+/**
+ * Sets each function in the namespace o3djs.math to the error checking free
+ * version in o3djs.math.errorCheckFree (provided such a function exists in
+ * o3djs.math.errorCheckFree).
+ */
+o3djs.math.installErrorCheckFreeFunctions = function() {
+ for (var f in o3djs.math.errorCheckFree) {
+ o3djs.math[f] = o3djs.math.errorCheckFree[f];
+ }
+}
+
+// By default, install the row-major functions.
+o3djs.math.installRowMajorFunctions();
+
+// By default, install prechecking.
+o3djs.math.installErrorCheckFunctions();
diff --git a/o3d/samples/o3djs/pack.js b/o3d/samples/o3djs/pack.js
new file mode 100644
index 0000000..3e4d086
--- /dev/null
+++ b/o3d/samples/o3djs/pack.js
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains various functions for helping setup
+ * packs for o3d. It puts them in the "pack" module on the o3djs
+ * object.
+ *
+ * Note: This library is only a sample. It is not meant to be some official
+ * library. It is provided only as example code.
+ *
+ */
+
+o3djs.provide('o3djs.pack');
+
+o3djs.require('o3djs.material');
+o3djs.require('o3djs.shape');
+
+/**
+ * A Module with utilities for dealing with packs..
+ * @namespace
+ */
+o3djs.pack = o3djs.pack || {};
+
+/**
+ * Prepares a pack for rendering.
+ *
+ * @param {!o3d.Pack} pack Pack to prepare.
+ * @param {!o3djs.rendergraph.ViewInfo} viewInfo as returned from
+ * o3djs.rendergraph.createView.
+ * @param {!o3d.Pack} opt_effectPack Pack to create effects in. If this is
+ * not specifed the pack to prepare above will be used.
+ *
+ * @see o3djs.material.prepareMaterials
+ * @see o3djs.shape.prepareShapes
+ */
+o3djs.pack.preparePack = function(pack, viewInfo, opt_effectPack) {
+ o3djs.material.prepareMaterials(pack, viewInfo, opt_effectPack);
+ o3djs.shape.prepareShapes(pack);
+};
+
diff --git a/o3d/samples/o3djs/particles.js b/o3d/samples/o3djs/particles.js
new file mode 100644
index 0000000..075c363
--- /dev/null
+++ b/o3d/samples/o3djs/particles.js
@@ -0,0 +1,1051 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains various functions and classes for rendering
+ * gpu based particles.
+ *
+ * TODO: Add 3d oriented particles.
+ */
+
+o3djs.provide('o3djs.particles');
+
+o3djs.require('o3djs.math');
+
+/**
+ * A Module with various GPU particle functions and classes.
+ * Note: GPU particles have the issue that they are not sorted per particle
+ * but rather per emitter.
+ * @namespace
+ */
+o3djs.particles = o3djs.particles || {};
+
+/**
+ * Enum for pre-made particle states.
+ * @enum
+ */
+o3djs.particles.ParticleStateIds = {
+ BLEND: 0,
+ ADD: 1,
+ BLEND_PREMULTIPLY: 2,
+ BLEND_NO_ALPHA: 3,
+ SUBTRACT: 4,
+ INVERSE: 5};
+
+/**
+ * Particle Effect strings
+ * @type {!Array.<{name: string, fxString: string}>}
+ */
+o3djs.particles.FX_STRINGS = [
+ { name: 'particle3d', fxString: '' +
+ 'float4x4 worldViewProjection : WORLDVIEWPROJECTION;\n' +
+ 'float4x4 world : WORLD;\n' +
+ 'float3 worldVelocity;\n' +
+ 'float3 worldAcceleration;\n' +
+ 'float timeRange;\n' +
+ 'float time;\n' +
+ 'float timeOffset;\n' +
+ 'float frameDuration;\n' +
+ 'float numFrames;\n' +
+ '\n' +
+ '// We need to implement 1D!\n' +
+ 'sampler rampSampler;\n' +
+ 'sampler colorSampler;\n' +
+ '\n' +
+ 'struct VertexShaderInput {\n' +
+ ' float4 uvLifeTimeFrameStart : POSITION; // uv, lifeTime, frameStart\n' +
+ ' float4 positionStartTime : TEXCOORD0; // position.xyz, startTime\n' +
+ ' float4 velocityStartSize : TEXCOORD1; // velocity.xyz, startSize\n' +
+ ' float4 accelerationEndSize : TEXCOORD2; // acceleration.xyz, endSize\n' +
+ ' float4 spinStartSpinSpeed : TEXCOORD3; // spinStart.x, spinSpeed.y\n' +
+ ' float4 orientation : TEXCOORD4; // orientation\n' +
+ ' float4 colorMult : COLOR; //\n' +
+ '};\n' +
+ '\n' +
+ 'struct PixelShaderInput {\n' +
+ ' float4 position : POSITION;\n' +
+ ' float2 texcoord : TEXCOORD0;\n' +
+ ' float1 percentLife : TEXCOORD1;\n' +
+ ' float4 colorMult: TEXCOORD2;\n' +
+ '};\n' +
+ '\n' +
+ 'PixelShaderInput vertexShaderFunction(VertexShaderInput input) {\n' +
+ ' PixelShaderInput output;\n' +
+ '\n' +
+ ' float2 uv = input.uvLifeTimeFrameStart.xy;\n' +
+ ' float lifeTime = input.uvLifeTimeFrameStart.z;\n' +
+ ' float frameStart = input.uvLifeTimeFrameStart.w;\n' +
+ ' float3 position = input.positionStartTime.xyz;\n' +
+ ' float startTime = input.positionStartTime.w;\n' +
+ ' float3 velocity = mul(float4(input.velocityStartSize.xyz, 0),\n' +
+ ' world).xyz + worldVelocity;\n' +
+ ' float startSize = input.velocityStartSize.w;\n' +
+ ' float3 acceleration = mul(float4(input.accelerationEndSize.xyz, 0),\n' +
+ ' world).xyz + worldAcceleration;\n' +
+ ' float endSize = input.accelerationEndSize.w;\n' +
+ ' float spinStart = input.spinStartSpinSpeed.x;\n' +
+ ' float spinSpeed = input.spinStartSpinSpeed.y;\n' +
+ '\n' +
+ ' float localTime = fmod((time - timeOffset - startTime), timeRange);\n' +
+ ' float percentLife = localTime / lifeTime;\n' +
+ '\n' +
+ ' float frame = fmod(floor(localTime / frameDuration + frameStart),\n' +
+ ' numFrames);\n' +
+ ' float uOffset = frame / numFrames;\n' +
+ ' float u = uOffset + (uv.x + 0.5) * (1 / numFrames);\n' +
+ '\n' +
+ ' output.texcoord = float2(u, uv.y + 0.5);\n' +
+ ' output.colorMult = input.colorMult;\n' +
+ '\n' +
+ ' float size = lerp(startSize, endSize, percentLife);\n' +
+ ' float s = sin(spinStart + spinSpeed * localTime);\n' +
+ ' float c = cos(spinStart + spinSpeed * localTime);\n' +
+ '\n' +
+ ' float4 rotatedPoint = float4((uv.x * c + uv.y * s) * size, 0,\n' +
+ ' (uv.x * s - uv.y * c) * size, 1);\n' +
+ ' float3 center = velocity * localTime +\n' +
+ ' acceleration * localTime * localTime + \n' +
+ ' position;\n' +
+ ' \n' +
+ ' float4 q2 = input.orientation + input.orientation;\n' +
+ ' float4 qx = input.orientation.xxxw * q2.xyzx;\n' +
+ ' float4 qy = input.orientation.xyyw * q2.xyzy;\n' +
+ ' float4 qz = input.orientation.xxzw * q2.xxzz;\n' +
+ ' \n' +
+ ' float4x4 localMatrix = float4x4(\n' +
+ ' (1.0f - qy.y) - qz.z, \n' +
+ ' qx.y + qz.w, \n' +
+ ' qx.z - qy.w,\n' +
+ ' 0,\n' +
+ ' \n' +
+ ' qx.y - qz.w, \n' +
+ ' (1.0f - qx.x) - qz.z, \n' +
+ ' qy.z + qx.w,\n' +
+ ' 0,\n' +
+ ' \n' +
+ ' qx.z + qy.w, \n' +
+ ' qy.z - qx.w, \n' +
+ ' (1.0f - qx.x) - qy.y,\n' +
+ ' 0,\n' +
+ ' \n' +
+ ' center.x, center.y, center.z, 1);\n' +
+ ' rotatedPoint = mul(rotatedPoint, localMatrix);\n' +
+ ' output.position = mul(rotatedPoint, worldViewProjection);\n' +
+ ' output.percentLife = percentLife;\n' +
+ ' return output;\n' +
+ '}\n' +
+ '\n' +
+ 'float4 pixelShaderFunction(PixelShaderInput input): COLOR {\n' +
+ ' float4 colorMult = tex2D(rampSampler, \n' +
+ ' float2(input.percentLife, 0.5)) *\n' +
+ ' input.colorMult;\n' +
+ ' float4 color = tex2D(colorSampler, input.texcoord) * colorMult;\n' +
+ ' return color;\n' +
+ '}\n' +
+ '\n' +
+ '// #o3d VertexShaderEntryPoint vertexShaderFunction\n' +
+ '// #o3d PixelShaderEntryPoint pixelShaderFunction\n' +
+ '// #o3d MatrixLoadOrder RowMajor\n'},
+ { name: 'particle2d', fxString: '' +
+ 'float4x4 viewProjection : VIEWPROJECTION;\n' +
+ 'float4x4 world : WORLD;\n' +
+ 'float4x4 viewInverse : VIEWINVERSE;\n' +
+ 'float3 worldVelocity;\n' +
+ 'float3 worldAcceleration;\n' +
+ 'float timeRange;\n' +
+ 'float time;\n' +
+ 'float timeOffset;\n' +
+ 'float frameDuration;\n' +
+ 'float numFrames;\n' +
+ '\n' +
+ '// We need to implement 1D!\n' +
+ 'sampler rampSampler;\n' +
+ 'sampler colorSampler;\n' +
+ '\n' +
+ 'struct VertexShaderInput {\n' +
+ ' float4 uvLifeTimeFrameStart : POSITION; // uv, lifeTime, frameStart\n' +
+ ' float4 positionStartTime : TEXCOORD0; // position.xyz, startTime\n' +
+ ' float4 velocityStartSize : TEXCOORD1; // velocity.xyz, startSize\n' +
+ ' float4 accelerationEndSize : TEXCOORD2; // acceleration.xyz, endSize\n' +
+ ' float4 spinStartSpinSpeed : TEXCOORD3; // spinStart.x, spinSpeed.y\n' +
+ ' float4 colorMult : COLOR; //\n' +
+ '};\n' +
+ '\n' +
+ 'struct PixelShaderInput {\n' +
+ ' float4 position : POSITION;\n' +
+ ' float2 texcoord : TEXCOORD0;\n' +
+ ' float1 percentLife : TEXCOORD1;\n' +
+ ' float4 colorMult: TEXCOORD2;\n' +
+ '};\n' +
+ '\n' +
+ 'PixelShaderInput vertexShaderFunction(VertexShaderInput input) {\n' +
+ ' PixelShaderInput output;\n' +
+ '\n' +
+ ' float2 uv = input.uvLifeTimeFrameStart.xy;\n' +
+ ' float lifeTime = input.uvLifeTimeFrameStart.z;\n' +
+ ' float frameStart = input.uvLifeTimeFrameStart.w;\n' +
+ ' float3 position = input.positionStartTime.xyz;\n' +
+ ' float startTime = input.positionStartTime.w;\n' +
+ ' float3 velocity = mul(float4(input.velocityStartSize.xyz, 0),\n' +
+ ' world).xyz + worldVelocity;\n' +
+ ' float startSize = input.velocityStartSize.w;\n' +
+ ' float3 acceleration = mul(float4(input.accelerationEndSize.xyz, 0),\n' +
+ ' world).xyz + worldAcceleration;\n' +
+ ' float endSize = input.accelerationEndSize.w;\n' +
+ ' float spinStart = input.spinStartSpinSpeed.x;\n' +
+ ' float spinSpeed = input.spinStartSpinSpeed.y;\n' +
+ '\n' +
+ ' float localTime = fmod((time - timeOffset - startTime), timeRange);\n' +
+ ' float percentLife = localTime / lifeTime;\n' +
+ '\n' +
+ ' float frame = fmod(floor(localTime / frameDuration + frameStart),\n' +
+ ' numFrames);\n' +
+ ' float uOffset = frame / numFrames;\n' +
+ ' float u = uOffset + (uv.x + 0.5) * (1 / numFrames);\n' +
+ '\n' +
+ ' output.texcoord = float2(u, uv.y + 0.5);\n' +
+ ' output.colorMult = input.colorMult;\n' +
+ '\n' +
+ ' float3 basisX = viewInverse[0].xyz;\n' +
+ ' float3 basisZ = viewInverse[1].xyz;\n' +
+ '\n' +
+ ' float size = lerp(startSize, endSize, percentLife);\n' +
+ ' float s = sin(spinStart + spinSpeed * localTime);\n' +
+ ' float c = cos(spinStart + spinSpeed * localTime);\n' +
+ '\n' +
+ ' float2 rotatedPoint = float2(uv.x * c + uv.y * s, \n' +
+ ' -uv.x * s + uv.y * c);\n' +
+ ' float3 localPosition = float3(basisX * rotatedPoint.x +\n' +
+ ' basisZ * rotatedPoint.y) * size +\n' +
+ ' velocity * localTime +\n' +
+ ' acceleration * localTime * localTime + \n' +
+ ' position;\n' +
+ '\n' +
+ ' output.position = mul(float4(localPosition + world[3].xyz, 1), \n' +
+ ' viewProjection);\n' +
+ ' output.percentLife = percentLife;\n' +
+ ' return output;\n' +
+ '}\n' +
+ '\n' +
+ 'float4 pixelShaderFunction(PixelShaderInput input): COLOR {\n' +
+ ' float4 colorMult = tex2D(rampSampler, \n' +
+ ' float2(input.percentLife, 0.5)) *\n' +
+ ' input.colorMult;\n' +
+ ' float4 color = tex2D(colorSampler, input.texcoord) * colorMult;\n' +
+ ' return color;\n' +
+ '}\n' +
+ '\n' +
+ '// #o3d VertexShaderEntryPoint vertexShaderFunction\n' +
+ '// #o3d PixelShaderEntryPoint pixelShaderFunction\n' +
+ '// #o3d MatrixLoadOrder RowMajor\n'}];
+
+/**
+ * Creates a particle system.
+ * You only need one of these to run multiple emitters of different types
+ * of particles.
+ * @param {!o3d.Pack} pack The pack for the particle system to manage resources.
+ * @param {!o3djs.rendergraph.viewInfo} viewInfo A viewInfo so the particle
+ * system can do the default setup. The only thing used from viewInfo
+ * is the zOrderedDrawList. If that is not where you want your particles,
+ * after you create the particleEmitter use
+ * particleEmitter.material.drawList = myDrawList to set it to something
+ * else.
+ * @param {!o3d.ParamFloat} opt_clockParam A ParamFloat to be the default
+ * clock for emitters of this particle system.
+ * @param {!function(): number} opt_randomFunction A function that returns
+ * a random number between 0.0 and 1.0. This allows you to pass in a
+ * pseudo random function if you need particles that are reproducable.
+ * @return {!o3djs.particles.ParticleSystem} The created particle system.
+ */
+o3djs.particles.createParticleSystem = function(pack,
+ viewInfo,
+ opt_clockParam,
+ opt_randomFunction) {
+ return new o3djs.particles.ParticleSystem(pack,
+ viewInfo,
+ opt_clockParam,
+ opt_randomFunction);
+};
+
+/**
+ * An Object to manage Particles.
+ * @constructor
+ * @param {!o3d.Pack} pack The pack for the particle system to manage resources.
+ * @param {!o3djs.rendergraph.ViewInfo} viewInfo A viewInfo so the particle
+ * system can do the default setup. The only thing used from viewInfo
+ * is the zOrderedDrawList. If that is not where you want your particles,
+ * after you create the particleEmitter use
+ * particleEmitter.material.drawList = myDrawList to set it to something
+ * else.
+ * @param {!o3d.ParamFloat} opt_clockParam A ParamFloat to be the default
+ * clock for emitters of this particle system.
+ */
+o3djs.particles.ParticleSystem = function(pack,
+ viewInfo,
+ opt_clockParam,
+ opt_randomFunction) {
+ var o3d = o3djs.base.o3d;
+ var particleStates = [];
+ var effects = [];
+ for (var ee = 0; ee < o3djs.particles.FX_STRINGS.length; ++ee) {
+ var info = o3djs.particles.FX_STRINGS[ee];
+ var effect = pack.createObject('Effect');
+ effect.name = info.name;
+ effect.loadFromFXString(info.fxString);
+ effects.push(effect);
+ }
+
+ var stateInfos = {};
+ stateInfos[o3djs.particles.ParticleStateIds.BLEND] = {
+ 'SourceBlendFunction':
+ o3djs.base.o3d.State.BLENDFUNC_SOURCE_ALPHA,
+ 'DestinationBlendFunction':
+ o3djs.base.o3d.State.BLENDFUNC_INVERSE_SOURCE_ALPHA};
+
+ stateInfos[o3djs.particles.ParticleStateIds.ADD] = {
+ 'SourceBlendFunction':
+ o3djs.base.o3d.State.BLENDFUNC_SOURCE_ALPHA,
+ 'DestinationBlendFunction':
+ o3djs.base.o3d.State.BLENDFUNC_ONE};
+
+ stateInfos[o3djs.particles.ParticleStateIds.BLEND_PREMULTIPLY] = {
+ 'SourceBlendFunction':
+ o3djs.base.o3d.State.BLENDFUNC_ONE,
+ 'DestinationBlendFunction':
+ o3djs.base.o3d.State.BLENDFUNC_INVERSE_SOURCE_ALPHA};
+
+ stateInfos[o3djs.particles.ParticleStateIds.BLEND_NO_ALPHA] = {
+ 'SourceBlendFunction':
+ o3djs.base.o3d.State.BLENDFUNC_SOURCE_COLOR,
+ 'DestinationBlendFunction':
+ o3djs.base.o3d.State.BLENDFUNC_INVERSE_SOURCE_COLOR};
+
+ stateInfos[o3djs.particles.ParticleStateIds.SUBTRACT] = {
+ 'SourceBlendFunction':
+ o3djs.base.o3d.State.BLENDFUNC_SOURCE_ALPHA,
+ 'DestinationBlendFunction':
+ o3djs.base.o3d.State.BLENDFUNC_INVERSE_SOURCE_ALPHA,
+ 'BlendEquation':
+ o3djs.base.o3d.State.BLEND_REVERSE_SUBTRACT};
+
+ stateInfos[o3djs.particles.ParticleStateIds.INVERSE] = {
+ 'SourceBlendFunction':
+ o3djs.base.o3d.State.BLENDFUNC_INVERSE_DESTINATION_COLOR,
+ 'DestinationBlendFunction':
+ o3djs.base.o3d.State.BLENDFUNC_INVERSE_SOURCE_COLOR};
+
+ for (var key in o3djs.particles.ParticleStateIds) {
+ var state = pack.createObject('State');
+ var id = o3djs.particles.ParticleStateIds[key];
+ particleStates[id] = state;
+ state.getStateParam('ZWriteEnable').value = false;
+ state.getStateParam('CullMode').value = o3d.State.CULL_NONE;
+
+ var info = stateInfos[id];
+ for (var stateName in info) {
+ state.getStateParam(stateName).value = info[stateName];
+ }
+ }
+
+ var colorTexture = pack.createTexture2D(8, 8, o3d.Texture.ARGB8, 1, false);
+ var pixelBase = [0, 0.20, 0.70, 1, 0.70, 0.20, 0, 0];
+ var pixels = [];
+ for (var yy = 0; yy < 8; ++yy) {
+ for (var xx = 0; xx < 8; ++xx) {
+ var pixel = pixelBase[xx] * pixelBase[yy];
+ pixels.push(pixel, pixel, pixel, pixel);
+ }
+ }
+ colorTexture.set(0, pixels);
+ var rampTexture = pack.createTexture2D(3, 1, o3d.Texture.ARGB8, 1, false);
+ rampTexture.set(0, [1, 1, 1, 1,
+ 1, 1, 1, 0.5,
+ 1, 1, 1, 0]);
+
+ if (!opt_clockParam) {
+ this.counter_ = pack.createObject('SecondCounter');
+ opt_clockParam = this.counter_.getParam('count');
+ }
+
+ this.randomFunction_ = opt_randomFunction || function() {
+ return Math.random();
+ };
+
+ /**
+ * The states for the various blend modes.
+ * @type {!Array.<!o3d.State>}
+ */
+ this.particleStates = particleStates;
+
+ /**
+ * The default ParamFloat to use as the clock for emitters created by
+ * this system.
+ * @type {!o3d.ParamFloat}
+ */
+ this.clockParam = opt_clockParam;
+
+ /**
+ * The pack used to manage particle system resources.
+ * @type {!o3d.Pack}
+ */
+ this.pack = pack;
+
+ /**
+ * The viewInfo that is used to get drawLists.
+ * @type {!o3djs.rendergraph.ViewInfo}
+ */
+ this.viewInfo = viewInfo;
+
+ /**
+ * The effects for particles.
+ * @type {!Array.<!o3d.Effect>}
+ */
+ this.effects = effects;
+
+
+ /**
+ * The default color texture for particles.
+ * @type {!o3d.Texture2D}
+ */
+ this.defaultColorTexture = colorTexture;
+
+
+ /**
+ * The default ramp texture for particles.
+ * @type {!o3d.Texture2D}
+ */
+ this.defaultRampTexture = rampTexture;
+};
+
+/**
+ * A ParticleSpec specifies how to emit particles.
+ *
+ * NOTE: For all particle functions you can specific a ParticleSpec as a
+ * Javascript object, only specifying the fields that you care about.
+ *
+ * <pre>
+ * emitter.setParameters({
+ * numParticles: 40,
+ * lifeTime: 2,
+ * timeRange: 2,
+ * startSize: 50,
+ * endSize: 90,
+ * positionRange: [10, 10, 10],
+ * velocity:[0, 0, 60], velocityRange: [15, 15, 15],
+ * acceleration: [0, 0, -20],
+ * spinSpeedRange: 4}
+ * );
+ * </pre>
+ *
+ * Many of these parameters are in pairs. For paired paramters each particle
+ * specfic value is set like this
+ *
+ * particle.field = value + Math.random() - 0.5 * valueRange * 2;
+ *
+ * or in English
+ *
+ * particle.field = value plus or minus valueRange.
+ *
+ * So for example, if you wanted a value from 10 to 20 you'd pass 15 for value
+ * and 5 for valueRange because
+ *
+ * 15 + or - 5 = (10 to 20)
+ *
+ * @constructor
+ */
+o3djs.particles.ParticleSpec = function() {
+ /**
+ * The number of particles to emit.
+ * @type {number}
+ */
+ this.numParticles = 1;
+
+ /**
+ * The number of frames in the particle texture.
+ * @type {number}
+ */
+ this.numFrames = 1;
+
+ /**
+ * The frame duration at which to animate the particle texture in seconds per
+ * frame.
+ * @type {number}
+ */
+ this.frameDuration = 1;
+
+ /**
+ * The initial frame to display for a particular particle.
+ * @type {number}
+ */
+ this.frameStart = 0;
+
+ /**
+ * The frame start range.
+ * @type {number}
+ */
+ this.frameStartRange = 0;
+
+ /**
+ * The life time of the entire particle system.
+ * To make a particle system be continuous set this to match the lifeTime.
+ * @type {number}
+ */
+ this.timeRange = 99999999;
+
+ /**
+ * The startTime of a particle.
+ * @type {number?}
+ */
+ this.startTime = null;
+ // TODO: Describe what happens if this is not set. I still have some
+ // work to do there.
+
+ /**
+ * The lifeTime of a particle.
+ * @type {number}
+ */
+ this.lifeTime = 1;
+
+ /**
+ * The lifeTime range.
+ * @type {number}
+ */
+ this.lifeTimeRange = 0;
+
+ /**
+ * The starting size of a particle.
+ * @type {number}
+ */
+ this.startSize = 1;
+
+ /**
+ * The starting size range.
+ * @type {number}
+ */
+ this.startSizeRange = 0;
+
+ /**
+ * The ending size of a particle.
+ * @type {number}
+ */
+ this.endSize = 1;
+
+ /**
+ * The ending size range.
+ * @type {number}
+ */
+ this.endSizeRange = 0;
+
+ /**
+ * The starting position of a particle in local space.
+ * @type {!o3djs.math.Vector3}
+ */
+ this.position = [0, 0, 0];
+
+ /**
+ * The starting position range.
+ * @type {!o3djs.math.Vector3}
+ */
+ this.positionRange = [0, 0, 0];
+
+ /**
+ * The velocity of a paritcle in local space.
+ * @type {!o3djs.math.Vector3}
+ */
+ this.velocity = [0, 0, 0];
+
+ /**
+ * The velocity range.
+ * @type {!o3djs.math.Vector3}
+ */
+ this.velocityRange = [0, 0, 0];
+
+ /**
+ * The acceleration of a particle in local space.
+ * @type {!o3djs.math.Vector3}
+ */
+ this.acceleration = [0, 0, 0];
+
+ /**
+ * The accleration range.
+ * @type {!o3djs.math.Vector3}
+ */
+ this.accelerationRange = [0, 0, 0];
+
+ /**
+ * The starting spin value for a particle in radians.
+ * @type {number}
+ */
+ this.spinStart = 0;
+
+ /**
+ * The spin start range.
+ * @type {number}
+ */
+ this.spinStartRange = 0;
+
+ /**
+ * The spin speed of a particle in radians.
+ * @type {number}
+ */
+ this.spinSpeed = 0;
+
+ /**
+ * The spin speed range.
+ * @type {number}
+ */
+ this.spinSpeedRange = 0;
+
+ /**
+ * The color multiplier of a particle.
+ * @type {!o3djs.math.Vector4}
+ */
+ this.colorMult = [1, 1, 1, 1];
+
+ /**
+ * The color multiplier range.
+ * @type {!o3djs.math.Vector4} colorMultRange
+ */
+ this.colorMultRange = [0, 0, 0, 0];
+
+ /**
+ * The velocity of all paritcles in world space.
+ * @type {!o3djs.math.Vector3}
+ */
+ this.worldVelocity = [0, 0, 0];
+
+ /**
+ * The acceleration of all paritcles in world space.
+ * @type {!o3djs.math.Vector3}
+ */
+ this.worldAcceleration = [0, 0, 0];
+
+ /**
+ * Whether these particles are oriented in 2d or 3d. true = 2d, false = 3d.
+ * @type {boolean}
+ */
+ this.billboard = true;
+
+ /**
+ * The orientation of a particle. This is only used if billboard is false.
+ * @type {!o3djs.quaternions.Quaterinion}
+ */
+ this.orientation = [0, 0, 0, 1];
+};
+
+/**
+ * Creates a particle emitter.
+ * @param {!o3d.Texture} opt_texture The texture to use for the particles.
+ * If you don't supply a texture a default is provided.
+ * @param {!o3d.ParamFloat} opt_clockParam A ParamFloat to be the clock for
+ * the emitter.
+ * @return {!o3djs.particles.ParticleEmitter} The new emitter.
+ */
+o3djs.particles.ParticleSystem.prototype.createParticleEmitter =
+ function(opt_texture, opt_clockParam) {
+ return new o3djs.particles.ParticleEmitter(this, opt_texture, opt_clockParam);
+};
+
+/**
+ * A ParticleEmitter
+ * @constructor
+ * @param {!o3djs.particles.ParticleSystem} particleSystem The particle system
+ * to manage this emitter.
+ * @param {!o3d.Texture} opt_texture The texture to use for the particles.
+ * If you don't supply a texture a default is provided.
+ * @param {!o3d.ParamFloat} opt_clockParam A ParamFloat to be the clock for
+ * the emitter.
+ */
+o3djs.particles.ParticleEmitter = function(particleSystem,
+ opt_texture,
+ opt_clockParam) {
+
+ opt_clockParam = opt_clockParam || particleSystem.clockParam;
+
+ var o3d = o3djs.base.o3d;
+ var pack = particleSystem.pack;
+ var viewInfo = particleSystem.viewInfo;
+ var material = pack.createObject('Material');
+ material.name = 'particles';
+ material.drawList = viewInfo.zOrderedDrawList;
+ material.effect = particleSystem.effects[1];
+ particleSystem.effects[1].createUniformParameters(material);
+ material.getParam('time').bind(opt_clockParam);
+
+ var rampSampler = pack.createObject('Sampler');
+ rampSampler.texture = particleSystem.defaultRampTexture;
+ rampSampler.addressModeU = o3d.Sampler.CLAMP;
+
+ var colorSampler = pack.createObject('Sampler');
+ colorSampler.texture = opt_texture || particleSystem.defaultColorTexture;
+ colorSampler.addressModeU = o3d.Sampler.CLAMP;
+ colorSampler.addressModeV = o3d.Sampler.CLAMP;
+
+ material.getParam('rampSampler').value = rampSampler;
+ material.getParam('colorSampler').value = colorSampler;
+
+ var vertexBuffer = pack.createObject('VertexBuffer');
+ var uvLifeTimeFrameStartField = vertexBuffer.createField('FloatField', 4);
+ var positionStartTimeField = vertexBuffer.createField('FloatField', 4);
+ var velocityStartSizeField = vertexBuffer.createField('FloatField', 4);
+ var accelerationEndSizeField = vertexBuffer.createField('FloatField', 4);
+ var spinStartSpinSpeedField = vertexBuffer.createField('FloatField', 4);
+ var orientationField = vertexBuffer.createField('FloatField', 4);
+ var colorMultField = vertexBuffer.createField('FloatField', 4);
+
+ var indexBuffer = pack.createObject('IndexBuffer');
+
+ var streamBank = pack.createObject('StreamBank');
+ streamBank.setVertexStream(o3d.Stream.POSITION, 0,
+ uvLifeTimeFrameStartField, 0);
+ streamBank.setVertexStream(o3d.Stream.TEXCOORD, 0,
+ positionStartTimeField, 0);
+ streamBank.setVertexStream(o3d.Stream.TEXCOORD, 1,
+ velocityStartSizeField, 0);
+ streamBank.setVertexStream(o3d.Stream.TEXCOORD, 2,
+ accelerationEndSizeField, 0);
+ streamBank.setVertexStream(o3d.Stream.TEXCOORD, 3,
+ spinStartSpinSpeedField, 0);
+ streamBank.setVertexStream(o3d.Stream.TEXCOORD, 4,
+ orientationField, 0);
+ streamBank.setVertexStream(o3d.Stream.COLOR, 0,
+ colorMultField, 0);
+
+ var shape = pack.createObject('Shape');
+ var primitive = pack.createObject('Primitive');
+ primitive.material = material;
+ primitive.owner = shape;
+ primitive.streamBank = streamBank;
+ primitive.indexBuffer = indexBuffer;
+ primitive.primitiveType = o3d.Primitive.TRIANGLELIST;
+ primitive.createDrawElement(pack, null);
+
+ this.vertexBuffer_ = vertexBuffer;
+ this.uvLifeTimeFrameStartField_ = uvLifeTimeFrameStartField;
+ this.positionStartTimeField_ = positionStartTimeField;
+ this.velocityStartSizeField_ = velocityStartSizeField;
+ this.accelerationEndSizeField_ = accelerationEndSizeField;
+ this.spinStartSpinSpeedField_ = spinStartSpinSpeedField;
+ this.orientationField_ = orientationField;
+ this.colorMultField_ = colorMultField;
+ this.indexBuffer_ = indexBuffer;
+ this.streamBank_ = streamBank;
+ this.primitive_ = primitive;
+ this.rampSampler_ = rampSampler;
+ this.rampTexture_ = particleSystem.defaultRampTexture;
+ this.colorSampler_ = colorSampler;
+
+ /**
+ * The particle system managing this emitter.
+ * @type {!o3djs.particles.ParticleSystem}
+ */
+ this.particleSystem = particleSystem;
+
+ /**
+ * The Shape used to render these particles.
+ * @type {!o3d.Shape}
+ */
+ this.shape = shape;
+
+ /**
+ * The material used by this emitter.
+ * @type {!o3d.Matrerial}
+ */
+ this.material = material;
+
+ /**
+ * The param that is the source for the time for this emitter.
+ * @type {!o3d.ParamFloat}
+ */
+ this.clockParam = opt_clockParam;
+};
+
+/**
+ * Sets the blend state for the particles.
+ * You can use this to set the emitter to draw with BLEND, ADD, SUBTRACT, etc.
+ * @param {o3djs.particles.ParticleStateIds} stateId The state you want.
+ */
+o3djs.particles.ParticleEmitter.prototype.setState = function(stateId) {
+ this.material.state = this.particleSystem.particleStates[stateId];
+};
+
+/**
+ * Sets the colorRamp for the particles.
+ * The colorRamp is used as a multiplier for the texture. When a particle
+ * starts it is multiplied by the first color, as it ages to progressed
+ * through the colors in the ramp.
+ *
+ * <pre>
+ * particleEmitter.setColorRamp([
+ * 1, 0, 0, 1, // red
+ * 0, 1, 0, 1, // green
+ * 1, 0, 1, 0]); // purple but with zero alpha
+ * </pre>
+ *
+ * The code above sets the particle to start red, change to green then
+ * fade out while changing to purple.
+ *
+ * @param {!Array.<number>} colorRamp An array of color values in
+ * the form RGBA.
+ */
+o3djs.particles.ParticleEmitter.prototype.setColorRamp = function(colorRamp) {
+ var width = colorRamp.length / 4;
+ if (width % 1 != 0) {
+ throw 'colorRamp must have multiple of 4 entries';
+ }
+
+ if (this.rampTexture_ == this.particleSystem.defaultRampTexture) {
+ this.rampTexture_ = null;
+ }
+
+ if (this.rampTexture_ && this.rampTexture_.width != width) {
+ this.particleSystem.pack.removeObject(this.rampTexture_);
+ this.rampTexture_ = null;
+ }
+
+ if (!this.rampTexture_) {
+ this.rampTexture_ = this.particleSystem.pack.createTexture2D(
+ width, 1, o3djs.base.o3d.Texture.ARGB8, 1, false);
+ }
+
+ this.rampTexture_.set(0, colorRamp);
+ this.rampSampler_.texture = this.rampTexture_;
+};
+
+/**
+ * Sets the parameters of the particle emitter.
+ *
+ * Each of these parameters are in pairs. The used to create a table
+ * of particle parameters. For each particle a specfic value is
+ * set like this
+ *
+ * particle.field = value + Math.random() - 0.5 * valueRange * 2;
+ *
+ * or in English
+ *
+ * particle.field = value plus or minus valueRange.
+ *
+ * So for example, if you wanted a value from 10 to 20 you'd pass 15 for value
+ * and 5 for valueRange because
+ *
+ * 15 + or - 5 = (10 to 20)
+ *
+ * @param {!o3djs.particles.ParticleSpec} parameters The parameters for the
+ * emitters.
+ * @param {!function(number, !o3djs.particles.ParticleSpec): void}
+ * opt_perParticleParamSetter A function that is called for each particle to
+ * allow it's parameters to be adjusted per particle. The number is the
+ * index of the particle being created, in other words, if numParticles is
+ * 20 this value will be 0 to 19. The ParticleSpec is a spec for this
+ * particular particle. You can set any per particle value before returning.
+ */
+o3djs.particles.ParticleEmitter.prototype.setParameters = function(
+ parameters,
+ opt_perParticleParamSetter) {
+ var defaults = new o3djs.particles.ParticleSpec();
+ for (var key in parameters) {
+ if (typeof defaults[key] === 'undefined') {
+ throw 'unknown particle parameter "' + key + '"';
+ }
+ defaults[key] = parameters[key];
+ }
+
+ var numParticles = defaults.numParticles;
+
+ var uvLifeTimeFrameStart = [];
+ var positionStartTime = [];
+ var velocityStartSize = [];
+ var accelerationEndSize = [];
+ var spinStartSpinSpeed = [];
+ var orientation = [];
+ var colorMults = [];
+
+ // Set the globals.
+ this.material.effect =
+ this.particleSystem.effects[defaults.billboard ? 1 : 0];
+ this.material.getParam('timeRange').value = defaults.timeRange;
+ this.material.getParam('numFrames').value = defaults.numFrames;
+ this.material.getParam('frameDuration').value = defaults.frameDuration;
+ this.material.getParam('worldVelocity').value = defaults.worldVelocity;
+ this.material.getParam('worldAcceleration').value =
+ defaults.worldAcceleration;
+
+ var corners = [
+ [-0.5, -0.5],
+ [+0.5, -0.5],
+ [+0.5, +0.5],
+ [-0.5, +0.5]];
+
+ var random = this.particleSystem.randomFunction_;
+
+ var plusMinus = function(range) {
+ return (random() - 0.5) * range * 2;
+ };
+
+ var plusMinusVector = function(range) {
+ var v = [];
+ for (var ii = 0; ii < range.length; ++ii) {
+ v.push(plusMinus(range[ii]));
+ }
+ return v;
+ };
+
+ for (var ii = 0; ii < numParticles; ++ii) {
+ if (opt_perParticleParamSetter) {
+ opt_perParticleParamSetter(ii, defaults);
+ }
+ var pLifeTime = defaults.lifeTime;
+ var pStartTime = (defaults.startTime === null) ?
+ (ii * defaults.lifeTime / numParticles) : defaults.startTime;
+ var pFrameStart = defaults.frameStart + plusMinus(defaults.frameStartRange);
+ var pPosition = o3djs.math.addVector(
+ defaults.position, plusMinusVector(defaults.positionRange));
+ var pVelocity = o3djs.math.addVector(
+ defaults.velocity, plusMinusVector(defaults.velocityRange));
+ var pAcceleration = o3djs.math.addVector(
+ defaults.acceleration, plusMinusVector(defaults.accelerationRange));
+ var pColorMult = o3djs.math.addVector(
+ defaults.colorMult, plusMinusVector(defaults.colorMultRange));
+ var pSpinStart = defaults.spinStart + plusMinus(defaults.spinStartRange);
+ var pSpinSpeed = defaults.spinSpeed + plusMinus(defaults.spinSpeedRange);
+ var pStartSize = defaults.startSize + plusMinus(defaults.startSizeRange);
+ var pEndSize = defaults.endSize + plusMinus(defaults.endSizeRange);
+ var pOrientation = defaults.orientation;
+
+ // make each corner of the particle.
+ for (var jj = 0; jj < 4; ++jj) {
+ uvLifeTimeFrameStart.push(corners[jj][0], corners[jj][1], pLifeTime,
+ pFrameStart);
+ positionStartTime.push(
+ pPosition[0], pPosition[1], pPosition[2], pStartTime);
+ velocityStartSize.push(
+ pVelocity[0], pVelocity[1], pVelocity[2], pStartSize);
+ accelerationEndSize.push(
+ pAcceleration[0], pAcceleration[1], pAcceleration[2], pEndSize);
+ spinStartSpinSpeed.push(pSpinStart, pSpinSpeed, 0, 0);
+ orientation.push(
+ pOrientation[0], pOrientation[1], pOrientation[2], pOrientation[3]);
+ colorMults.push(
+ pColorMult[0], pColorMult[1], pColorMult[2], pColorMult[3]);
+ }
+ }
+
+ if (this.vertexBuffer_.numElements != numParticles * 4) {
+ this.vertexBuffer_.allocateElements(numParticles * 4);
+
+ var indices = [];
+ for (var ii = 0; ii < numParticles; ++ii) {
+ // Make 2 triangles for the quad.
+ var startIndex = ii * 4
+ indices.push(startIndex + 0, startIndex + 1, startIndex + 2);
+ indices.push(startIndex + 0, startIndex + 2, startIndex + 3);
+ }
+ this.indexBuffer_.set(indices);
+ }
+
+ this.uvLifeTimeFrameStartField_.setAt(0, uvLifeTimeFrameStart);
+ this.positionStartTimeField_.setAt(0, positionStartTime);
+ this.velocityStartSizeField_.setAt(0, velocityStartSize);
+ this.accelerationEndSizeField_.setAt(0, accelerationEndSize);
+ this.spinStartSpinSpeedField_.setAt(0, spinStartSpinSpeed);
+ this.orientationField_.setAt(0, orientation);
+ this.colorMultField_.setAt(0, colorMults);
+
+ this.primitive_.numberPrimitives = numParticles * 2;
+ this.primitive_.numberVertices = numParticles * 4;
+};
+
+/**
+ * Creates a OneShot particle emitter instance.
+ * You can use this for dust puffs, explosions, fireworks, etc...
+ * @param {!o3d.Transform} opt_parent The parent for the oneshot.
+ * @return {!o3djs.particles.OneShot} A OneShot object.
+ */
+o3djs.particles.ParticleEmitter.prototype.createOneShot = function(opt_parent) {
+ return new o3djs.particles.OneShot(this, opt_parent);
+};
+
+/**
+ * An object to manage a particle emitter instance as a one shot. Examples of
+ * one shot effects are things like an explosion, some fireworks.
+ * @constructor
+ * @param {!o3djs.particles.ParticleEmitter} emitter The emitter to use for the
+ * one shot.
+ * @param {!o3d.Transform} opt_parent The parent for this one shot.
+ */
+o3djs.particles.OneShot = function(emitter, opt_parent) {
+ var pack = emitter.particleSystem.pack;
+ this.emitter_ = emitter;
+
+ /**
+ * Transform for OneShot.
+ * @type {!o3d.Transform}
+ */
+ this.transform = pack.createObject('Transform');
+ this.transform.visible = false;
+ this.transform.addShape(emitter.shape);
+ this.timeOffsetParam_ =
+ this.transform.createParam('timeOffset', 'ParamFloat');
+ if (opt_parent) {
+ this.setParent(opt_parent);
+ }
+};
+
+/**
+ * Sets the parent transform for this OneShot.
+ * @param {!o3d.Transform} parent The parent for this one shot.
+ */
+o3djs.particles.OneShot.prototype.setParent = function(parent) {
+ this.transform.parent = parent;
+};
+
+/**
+ * Triggers the oneshot.
+ *
+ * Note: You must have set the parent either at creation, with setParent, or by
+ * passing in a parent here.
+ *
+ * @param {!o3djs.math.Vector3} opt_position The position of the one shot
+ * relative to its parent.
+ * @param {!o3d.Transform} opt_parent The parent for this one shot.
+ */
+o3djs.particles.OneShot.prototype.trigger = function(opt_position, opt_parent) {
+ if (opt_parent) {
+ this.setParent(opt_parent);
+ }
+ if (opt_position) {
+ this.transform.identity();
+ this.transform.translate(opt_position);
+ }
+ this.transform.visible = true;
+ this.timeOffsetParam_.value = this.emitter_.clockParam.value;
+};
diff --git a/o3d/samples/o3djs/picking.js b/o3d/samples/o3djs/picking.js
new file mode 100644
index 0000000..a40eb5d
--- /dev/null
+++ b/o3d/samples/o3djs/picking.js
@@ -0,0 +1,560 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains functions for implementing picking.
+ * It puts them in the "picking" module on the o3djs object.
+ *
+ *
+ * This example shows one way to implement picking. Because O3D is shader
+ * agnostic we can't handle picking automatically since we have no way of
+ * knowing what the developer is going to do with their shaders. On the other
+ * hand, we can provide various functions that make it possible to do your own
+ * picking. Only you know which objects are pickable and which are not. For
+ * example if you are making an RTS game, only you would know that units are
+ * pickable but ground and explosions are not and that neither is your HUD.
+ * Similarly, only you would know how your shaders manipulate the vertices
+ * passed to them.
+ *
+ * It's possible that someone, maybe us, will create an engine to use o3d
+ * that given a bunch of restrictions and flags on the data it accepts can
+ * do picking in a more automatic way but that is not the goal of the o3d
+ * api. Its goal is to provide a LOW-LEVEL shader agnostic API.
+ */
+
+o3djs.provide('o3djs.picking');
+
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.dump');
+
+/**
+ * A Module for picking.
+ * @namespace
+ */
+o3djs.picking = o3djs.picking || {};
+
+/**
+ * A ray.
+ * @type {{near: !o3djs.math.Vector3, far: !o3djs.math.Vector3}}
+ */
+o3djs.picking.Ray = goog.typedef;
+
+/**
+ * Creates a new PickInfo.
+ * @param {!o3djs.picking.ShapeInfo} shapeInfo The ShapeInfo that
+ * was picked.
+ * @param {!o3d.RayIntersectionInfo} rayIntersectionInfo Information
+ * about the pick.
+ * @param {!o3djs.math.Vector3} worldIntersectionPosition
+ * world position of intersection.
+ * @return {!o3djs.picking.PickInfo} The new PickInfo.
+ */
+o3djs.picking.createPickInfo = function(shapeInfo,
+ rayIntersectionInfo,
+ worldIntersectionPosition) {
+ return new o3djs.picking.PickInfo(shapeInfo,
+ rayIntersectionInfo,
+ worldIntersectionPosition);
+};
+
+/**
+ * Creates a new ShapeInfo.
+ * @param {!o3d.Shape} shape Shape to keep info about.
+ * @param {!o3djs.picking.TransformInfo} parent Parent transform of the shape.
+ * @return {!o3djs.picking.ShapeInfo} The new ShapeInfo.
+ */
+o3djs.picking.createShapeInfo = function(shape, parent) {
+ return new o3djs.picking.ShapeInfo(shape, parent);
+};
+
+/**
+ * Creates a new TransformInfo.
+ * @param {!o3d.Transform} transform Transform to keep info about.
+ * @param {o3djs.picking.TransformInfo} parent Parent transform of the
+ * transform. Can be null.
+ * @return {!o3djs.picking.TransformInfo} The new TransformInfo.
+ */
+o3djs.picking.createTransformInfo = function(transform, parent) {
+ return new o3djs.picking.TransformInfo(transform, parent);
+};
+
+/**
+ * Convert a pixel position relative to the top left corner of the client area
+ * into the corresponding ray through the frustum in world space.
+ * @param {number} clientXPosition x position relative to client area.
+ * @param {number} clientYPosition y position relative to client area.
+ * @param {!o3djs.math.Matrix4} view View matrix to transform with.
+ * @param {!o3djs.math.Matrix4} projection Projection matrix to transform
+ * with.
+ * @param {number} clientWidth width of client area.
+ * @param {number} clientHeight height of client area.
+ * @return {!o3djs.picking.Ray} ray in world space.
+ */
+o3djs.picking.clientPositionToWorldRayEx = function(clientXPosition,
+ clientYPosition,
+ view,
+ projection,
+ clientWidth,
+ clientHeight) {
+ // compute the world position of a ray going through the view frustum
+ var inverseViewProjectionMatrix = o3djs.math.inverse(
+ o3djs.math.matrix4.composition(projection, view));
+ // normScreenX, normScreenY are in frustum coordinates.
+ var normScreenX = clientXPosition / (clientWidth * 0.5) - 1;
+ var normScreenY = -(clientYPosition / (clientHeight * 0.5) - 1);
+
+ // Apply inverse view-projection matrix to get the ray in world coordinates.
+ return {
+ near: o3djs.math.matrix4.transformPoint(
+ inverseViewProjectionMatrix, [normScreenX, normScreenY, 0]),
+ far: o3djs.math.matrix4.transformPoint(
+ inverseViewProjectionMatrix, [normScreenX, normScreenY, 1])
+ };
+};
+
+/**
+ * Convert a pixel position relative to the top left corner of the client area
+ * into the corresponding ray through the frustum in world space.
+ * @param {number} clientXPosition x position relative to client area.
+ * @param {number} clientYPosition y position relative to client area.
+ * @param {!o3d.DrawContext} drawContext DrawContext to get view and
+ * projection matrices from.
+ * @param {number} clientWidth width of client area.
+ * @param {number} clientHeight height of client area.
+ * @return {!o3djs.picking.Ray} ray in world space.
+ */
+o3djs.picking.clientPositionToWorldRay = function(clientXPosition,
+ clientYPosition,
+ drawContext,
+ clientWidth,
+ clientHeight) {
+ return o3djs.picking.clientPositionToWorldRayEx(
+ clientXPosition,
+ clientYPosition,
+ drawContext.view,
+ drawContext.projection,
+ clientWidth,
+ clientHeight);
+};
+
+/**
+ * A local dump function so we can easily comment it out.
+ * @param {string} msg Message to dump.
+ */
+o3djs.picking.dprint = function(msg) {
+ //o3djs.dump.dump(msg);
+};
+
+/**
+ * A local dump function so we can easily comment it out.
+ * @param {string} label Label to print before value.
+ * @param {!o3djs.math.Vector3} float3 Value to print.
+ * @param {string} prefix optional prefix for indenting.
+ */
+o3djs.picking.dprintPoint3 = function(label, float3, prefix) {
+ //o3djs.dump.dumpPoint3(label, float3, prefix);
+};
+
+/**
+ * A local dump function so we can easily comment it out.
+ * @param {string} label Label to put in front of dump.
+ * @param {!o3d.BoundingBox} boundingBox BoundingBox to dump.
+ * @param {string} opt_prefix optional prefix for indenting.
+ */
+o3djs.picking.dprintBoundingBox = function(label,
+ boundingBox,
+ opt_prefix) {
+ //o3djs.dump.dumpBoundingBox(label, boundingBox, opt_prefix);
+};
+
+/**
+ * A local dump function so we can easily comment it out.
+ * @param {string} label Label to print before value.
+ * @param {!o3d.RayIntersectionInfo} rayIntersectionInfo Value to print.
+ */
+o3djs.picking.dumpRayIntersectionInfo = function(label,
+ rayIntersectionInfo) {
+ o3djs.picking.dprint(label + ' : valid = ' +
+ rayIntersectionInfo.valid + ' : intersected = ' +
+ rayIntersectionInfo.intersected);
+ if (rayIntersectionInfo.intersected) {
+ o3djs.picking.dprint(
+ ' : pos: ' +
+ rayIntersectionInfo.position[0] + ', ' +
+ rayIntersectionInfo.position[1] + ', ' +
+ rayIntersectionInfo.position[2] + ', ');
+ }
+ o3djs.picking.dprint('\n');
+};
+
+/**
+ * Creates a new PickInfo. Used to return picking information.
+ * @constructor
+ * @param {!o3djs.picking.ShapeInfo} shapeInfo The ShapeInfo that
+ * was picked.
+ * @param {!o3d.RayIntersectionInfo} rayIntersectionInfo Information
+ * about the pick.
+ * @param {!o3djs.math.Vector3} worldIntersectionPosition world position of
+ * intersection.
+ */
+o3djs.picking.PickInfo = function(shapeInfo,
+ rayIntersectionInfo,
+ worldIntersectionPosition) {
+ /**
+ * The ShapeInfo that was picked.
+ * @type {!o3djs.picking.ShapeInfo}
+ */
+ this.shapeInfo = shapeInfo;
+
+ /**
+ * Information about the pick.
+ * @type {!o3d.RayInterstionInfo}
+ */
+ this.rayIntersectionInfo = rayIntersectionInfo;
+
+ /**
+ * The worldIntersectionPosition world position of intersection.
+ * @type {!o3djs.math.Vector3}
+ */
+ this.worldIntersectionPosition = worldIntersectionPosition
+};
+
+/**
+ * Creates a new ShapeInfo. Used to store information about Shapes.
+ * @constructor
+ * @param {!o3d.Shape} shape Shape to keep info about.
+ * @param {!o3djs.picking.TransformInfo} parent Parent transform of the shape.
+ */
+o3djs.picking.ShapeInfo = function(shape, parent) {
+ /**
+ * The Shape for this ShapeInfo
+ * @type {!o3d.Shape}
+ */
+ this.shape = shape;
+
+ /**
+ * The parent TransformInfo of this Shape.
+ * @type {!o3djs.picking.TransformInfo}
+ */
+ this.parent = parent;
+
+ /**
+ * The bounding box for this Shape
+ * @type {o3d.BoundingBox}
+ */
+ this.boundingBox = null;
+
+ this.update();
+};
+
+/**
+ * Gets the BoundingBox of the Shape in this ShapeInfo.
+ * @return {o3d.BoundingBox} The Shape's BoundingBox.
+ */
+o3djs.picking.ShapeInfo.prototype.getBoundingBox = function() {
+ return this.boundingBox;
+};
+
+/**
+ * Updates the BoundingBox of the Shape in this ShapeInfo.
+ */
+o3djs.picking.ShapeInfo.prototype.update = function() {
+ var elements = this.shape.elements;
+ if (elements.length > 0) {
+ this.boundingBox = elements[0].getBoundingBox(0);
+ for (var ee = 1; ee < elements.length; ee++) {
+ this.boundingBox = this.boundingBox.add(elements[ee].getBoundingBox(0));
+ }
+ }
+};
+
+/**
+ * Attempts to "pick" this Shape by checking for the intersection of a ray
+ * in world space to the triangles this shape uses.
+ * @param {!o3djs.picking.Ray} worldRay A ray in world space to pick against.
+ * @return {o3djs.picking.PickInfo} Information about the picking.
+ * null if the ray did not intersect any triangles.
+ */
+o3djs.picking.ShapeInfo.prototype.pick = function(worldRay) {
+ var worldMatrix = this.parent.transform.getUpdatedWorldMatrix()
+ var inverseWorldMatrix = o3djs.math.inverse(worldMatrix);
+ var relativeNear = o3djs.math.matrix4.transformPoint(
+ inverseWorldMatrix, worldRay.near);
+ var relativeFar = o3djs.math.matrix4.transformPoint(
+ inverseWorldMatrix, worldRay.far);
+ var rayIntersectionInfo =
+ this.boundingBox.intersectRay(relativeNear,
+ relativeFar);
+
+ o3djs.picking.dumpRayIntersectionInfo('SHAPE(box): ' + this.shape.name,
+ rayIntersectionInfo);
+
+ if (rayIntersectionInfo.intersected) {
+ var elements = this.shape.elements;
+ for (var e = 0; e < elements.length; e++) {
+ var element = elements[e];
+ rayIntersectionInfo = element.intersectRay(
+ 0,
+ o3djs.base.o3d.State.CULL_CCW,
+ relativeNear,
+ relativeFar);
+ o3djs.picking.dumpRayIntersectionInfo(
+ 'SHAPE(tris): ' + this.shape.name + ' : element ' + element.name,
+ rayIntersectionInfo);
+
+ // TODO: get closest element not just first element.
+ if (rayIntersectionInfo.intersected) {
+ var worldIntersectionPosition = o3djs.math.matrix4.transformPoint(
+ worldMatrix, rayIntersectionInfo.position);
+ return o3djs.picking.createPickInfo(this,
+ rayIntersectionInfo,
+ worldIntersectionPosition);
+ }
+ }
+ }
+ return null;
+};
+
+/**
+ * Dumps info about a ShapeInfo
+ * @param {string} prefix optional prefix for indenting.
+ */
+o3djs.picking.ShapeInfo.prototype.dump = function(prefix) {
+ o3djs.picking.dprint(prefix + 'SHAPE: ' + this.shape.name + '\n');
+ o3djs.picking.dprintPoint3('bb min',
+ this.boundingBox.minExtent,
+ prefix + ' ');
+ o3djs.picking.dprintPoint3('bb max',
+ this.boundingBox.maxExtent,
+ prefix + ' ');
+};
+
+/**
+ * Creates a new TransformInfo. Used to store information about Transforms.
+ * @constructor
+ * @param {!o3d.Transform} transform Transform to keep info about.
+ * @param {o3djs.picking.TransformInfo} parent Parent transformInfo of the
+ * transform. Can be null.
+ */
+o3djs.picking.TransformInfo = function(transform, parent) {
+ this.childTransformInfos = {}; // Object of TransformInfo by id
+ this.shapeInfos = {}; // Object of ShapeInfo by id
+ /**
+ * The transform of this transform info.
+ * @type {!o3d.Transform}
+ */
+ this.transform = transform;
+ /**
+ * The transform of this transform info.
+ * @type {o3djs.picking.TransformInfo}
+ */
+ this.parent = parent;
+ this.boundingBox = null;
+};
+
+/**
+ * Gets the BoundingBox of the Transform in this TransformInfo.
+ * @return {o3d.BoundingBox} The Transform's BoundingBox.
+ */
+o3djs.picking.TransformInfo.prototype.getBoundingBox = function() {
+ return this.boundingBox;
+};
+
+/**
+ * Updates the shape and child lists for this TransformInfo and recomputes its
+ * BoundingBox.
+ */
+o3djs.picking.TransformInfo.prototype.update = function() {
+ var newChildTransformInfos = {};
+ var newShapeInfos = {};
+ var children = this.transform.children;
+ for (var c = 0; c < children.length; c++) {
+ var child = children[c];
+ var transformInfo = this.childTransformInfos[child.clientId];
+ if (!transformInfo) {
+ transformInfo = o3djs.picking.createTransformInfo(child, this);
+ } else {
+ // clear the boundingBox so we'll regenerate it.
+ transformInfo.boundingBox = null;
+ }
+ transformInfo.update();
+ newChildTransformInfos[child.clientId] = transformInfo;
+ }
+ var shapes = this.transform.shapes;
+ for (var s = 0; s < shapes.length; s++) {
+ var shape = shapes[s];
+ var shapeInfo = this.shapeInfos[shape.clientId];
+ if (!shapeInfo) {
+ shapeInfo = o3djs.picking.createShapeInfo(shape, this);
+ } else {
+ // unless the vertices or elements change there is no need to
+ // recompute this.
+ // shape.update();
+ }
+ newShapeInfos[shape.clientId] = shapeInfo;
+ }
+
+ // o3djs.picking.dprint(
+ // 'num Children: ' + children.length + '\n');
+ // o3djs.picking.dprint(
+ // 'num Shapes: ' + shapes.length + '\n');
+
+ this.childTransformInfos = newChildTransformInfos;
+ this.shapeInfos = newShapeInfos;
+
+ var boundingBox = null;
+ for (var key in newShapeInfos) {
+ var shapeInfo = newShapeInfos[key];
+ var box = shapeInfo.getBoundingBox().mul(this.transform.localMatrix);
+ if (!boundingBox) {
+ boundingBox = box;
+ } else if (box) {
+ boundingBox = boundingBox.add(box);
+ }
+ }
+
+ for (var key in newChildTransformInfos) {
+ var transformInfo = newChildTransformInfos[key];
+ // Note: If there is no shape at the leaf on this branch
+ // there will be no bounding box.
+ var box = transformInfo.getBoundingBox();
+ if (box) {
+ if (!boundingBox) {
+ boundingBox = box.mul(this.transform.localMatrix);
+ } else {
+ boundingBox = boundingBox.add(box.mul(this.transform.localMatrix));
+ }
+ }
+ }
+
+ this.boundingBox = boundingBox;
+};
+
+/**
+ * Attempts to "pick" this TransformInfo by checking for the intersection of a
+ * ray in world space to the boundingbox of the TransformInfo. If intesection
+ * is succesful recursively calls its children and shapes to try to find
+ * a single Shape that is hit by the ray.
+ * @param {!o3djs.picking.Ray} worldRay A ray in world space to pick against.
+ * @return {o3djs.picking.PickInfo} Information about the picking.
+ * null if the ray did not intersect any triangles.
+ */
+o3djs.picking.TransformInfo.prototype.pick = function(worldRay) {
+ if (this.boundingBox) {
+ var inverseWorldMatrix = o3djs.math.matrix4.identity();
+ if (this.parent) {
+ inverseWorldMatrix = o3djs.math.inverse(
+ this.parent.transform.getUpdatedWorldMatrix());
+ }
+ var relativeNear =
+ o3djs.math.matrix4.transformPoint(inverseWorldMatrix, worldRay.near);
+ var relativeFar =
+ o3djs.math.matrix4.transformPoint(inverseWorldMatrix, worldRay.far);
+ var rayIntersectionInfo =
+ this.boundingBox.intersectRay(relativeNear, relativeFar);
+ o3djs.picking.dumpRayIntersectionInfo(
+ 'TRANSFORM(box): ' + this.transform.name, rayIntersectionInfo);
+
+ if (rayIntersectionInfo.intersected) {
+ var closestPickInfo = null;
+ var minDistance = -1;
+ for (var key in this.childTransformInfos) {
+ var transformInfo = this.childTransformInfos[key];
+ var pickInfo = transformInfo.pick(worldRay);
+ if (pickInfo) {
+ // is this closer than the last one?
+ var distance = o3djs.math.lengthSquared(
+ o3djs.math.subVector(worldRay.near,
+ pickInfo.worldIntersectionPosition));
+ if (!closestPickInfo || distance < minDistance) {
+ minDistance = distance;
+ closestPickInfo = pickInfo;
+ }
+ }
+ }
+
+ for (var key in this.shapeInfos) {
+ var shapeInfo = this.shapeInfos[key];
+ var pickInfo = shapeInfo.pick(worldRay);
+ if (pickInfo) {
+ // is this closer than the last one?
+ var distance = o3djs.math.lengthSquared(
+ o3djs.math.subVector(worldRay.near,
+ pickInfo.worldIntersectionPosition));
+ if (!closestPickInfo || distance < minDistance) {
+ minDistance = distance;
+ closestPickInfo = pickInfo;
+ }
+ }
+ }
+ return closestPickInfo;
+ }
+ }
+ return null;
+};
+
+/**
+ * Dumps info about a TransformInfo
+ * @param {string} prefix optional prefix for indenting.
+ */
+o3djs.picking.TransformInfo.prototype.dump = function(prefix) {
+ prefix = prefix || '';
+
+ o3djs.picking.dprint(prefix + 'TRANSFORM: ' + this.transform.name +
+ '\n');
+
+ if (this.boundingBox) {
+ o3djs.picking.dprintPoint3('bb min',
+ this.boundingBox.minExtent,
+ prefix + ' ');
+ o3djs.picking.dprintPoint3('bb max',
+ this.boundingBox.maxExtent,
+ prefix + ' ');
+ } else {
+ o3djs.picking.dprint(prefix + ' bb *NA*\n');
+ }
+
+ o3djs.picking.dprint(prefix + '--Shapes--\n');
+ for (var key in this.shapeInfos) {
+ var shapeInfo = this.shapeInfos[key];
+ shapeInfo.dump(prefix + ' ');
+ }
+
+ o3djs.picking.dprint(prefix + '--Children--\n');
+ for (var key in this.childTransformInfos) {
+ var transformInfo = this.childTransformInfos[key];
+ transformInfo.dump(prefix + ' ');
+ }
+};
+
+
+
diff --git a/o3d/samples/o3djs/primitives.js b/o3d/samples/o3djs/primitives.js
new file mode 100644
index 0000000..d3df686
--- /dev/null
+++ b/o3d/samples/o3djs/primitives.js
@@ -0,0 +1,1698 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains functions to create geometric primitives for
+ * o3d. It puts them in the "primitives" module on the o3djs object.
+ *
+ * For more information about o3d see http://code.google.com/p/o3d
+ *
+ *
+ * Requires base.js
+ */
+
+o3djs.provide('o3djs.primitives');
+
+o3djs.require('o3djs.math');
+
+/**
+ * A Module for creating primitives.
+ * @namespace
+ */
+o3djs.primitives = o3djs.primitives || {};
+
+
+/**
+ * Sets the bounding box and zSortPoint for a primitive based on its vertices
+ *
+ * @param {!o3d.Primitive} primitive Primitive to set culling info for.
+ */
+o3djs.primitives.setCullingInfo = function(primitive) {
+ var box = primitive.getBoundingBox(0);
+ primitive.boundingBox = box;
+ var minExtent = box.minExtent;
+ var maxExtent = box.maxExtent;
+ primitive.zSortPoint = o3djs.math.divVectorScalar(
+ o3djs.math.addVector(minExtent, maxExtent), 2);
+};
+
+/**
+ * Used to store the elements of a stream.
+ * @param {number} numComponents The number of numerical components per
+ * element.
+ * @param {!o3d.Stream.Semantic} semantic The semantic of the stream.
+ * @param {number} opt_semanticIndex The semantic index of the stream.
+ * Defaults to zero.
+ * @constructor
+ */
+o3djs.primitives.VertexStreamInfo = function(numComponents,
+ semantic,
+ opt_semanticIndex) {
+ /**
+ * The number of numerical components per element.
+ * @type {number}
+ */
+ this.numComponents = numComponents;
+
+ /**
+ * The semantic of the stream.
+ * @type {!o3d.Stream.Semantic}
+ */
+ this.semantic = semantic;
+
+ /**
+ * The semantic index of the stream.
+ * @type {number}
+ */
+ this.semanticIndex = opt_semanticIndex || 0;
+
+ /**
+ * The elements of the stream.
+ * @type {!Array.<number>}
+ */
+ this.elements = [];
+
+ /**
+ * Adds an element to this VertexStreamInfo. The number of values passed must
+ * match the number of components for this VertexStreamInfo.
+ * @param {number} value1 First value.
+ * @param {number} opt_value2 Second value.
+ * @param {number} opt_value3 Third value.
+ * @param {number} opt_value4 Fourth value.
+ */
+ this.addElement = function(value1, opt_value2, opt_value3, opt_value4) { };
+
+ /**
+ * Sets an element on this VertexStreamInfo. The number of values passed must
+ * match the number of components for this VertexStreamInfo.
+ * @param {number} index Index of element to set.
+ * @param {number} value1 First value.
+ * @param {number} opt_value2 Second value.
+ * @param {number} opt_value3 Third value.
+ * @param {number} opt_value4 Fourth value.
+ */
+ this.setElement = function(
+ index, value1, opt_value2, opt_value3, opt_value4) { };
+
+ /**
+ * Adds an element to this VertexStreamInfo. The number of values in the
+ * vector must match the number of components for this VertexStreamInfo.
+ * @param {!Array.<number>} vector Array of values for element.
+ */
+ this.addElementVector = function(vector) { }; // replaced below.
+
+ /**
+ * Sets an element on this VertexStreamInfo. The number of values in the
+ * vector must match the number of components for this VertexStreamInfo.
+ * @param {number} index Index of element to set.
+ * @param {!Array.<number>} vector Array of values for element.
+ */
+ this.setElementVector = function(index, vector) { }; // replaced below.
+
+ /**
+ * Sets an element on this VertexStreamInfo. The number of values in the
+ * vector will match the number of components for this VertexStreamInfo.
+ * @param {number} index Index of element to set.
+ * @return {!Array.<number>} Array of values for element.
+ */
+ this.getElementVector = function(index) { return []; }; // replaced below.
+
+ switch (numComponents) {
+ case 1:
+ this.addElement = function(value) {
+ this.elements.push(value);
+ }
+ this.getElement = function(index) {
+ return this.elements[index];
+ }
+ this.setElement = function(index, value) {
+ this.elements[index] = value;
+ }
+ break;
+ case 2:
+ this.addElement = function(value0, value1) {
+ this.elements.push(value0, value1);
+ }
+ this.addElementVector = function(vector) {
+ this.elements.push(vector[0], vector[1]);
+ }
+ this.getElementVector = function(index) {
+ return this.elements.slice(index * numComponents,
+ (index + 1) * numComponents);
+ }
+ this.setElement = function(index, value0, value1) {
+ this.elements[index * numComponents + 0] = value0;
+ this.elements[index * numComponents + 1] = value1;
+ }
+ this.setElementVector = function(index, vector) {
+ this.elements[index * numComponents + 0] = vector[0];
+ this.elements[index * numComponents + 1] = vector[1];
+ }
+ break;
+ case 3:
+ this.addElement = function(value0, value1, value2) {
+ this.elements.push(value0, value1, value2);
+ }
+ this.addElementVector = function(vector) {
+ this.elements.push(vector[0], vector[1], vector[2]);
+ }
+ this.getElementVector = function(index) {
+ return this.elements.slice(index * numComponents,
+ (index + 1) * numComponents);
+ }
+ this.setElement = function(index, value0, value1, value2) {
+ this.elements[index * numComponents + 0] = value0;
+ this.elements[index * numComponents + 1] = value1;
+ this.elements[index * numComponents + 2] = value2;
+ }
+ this.setElementVector = function(index, vector) {
+ this.elements[index * numComponents + 0] = vector[0];
+ this.elements[index * numComponents + 1] = vector[1];
+ this.elements[index * numComponents + 2] = vector[2];
+ }
+ break;
+ case 4:
+ this.addElement = function(value0, value1, value2, value3) {
+ this.elements.push(value0, value1, value2, value3);
+ }
+ this.addElementVector = function(vector) {
+ this.elements.push(vector[0], vector[1], vector[2], vector[3]);
+ }
+ this.getElementVector = function(index) {
+ return this.elements.slice(index * numComponents,
+ (index + 1) * numComponents);
+ }
+ this.setElement = function(index, value0, value1, value2, value3) {
+ this.elements[index * numComponents + 0] = value0;
+ this.elements[index * numComponents + 1] = value1;
+ this.elements[index * numComponents + 2] = value2;
+ this.elements[index * numComponents + 3] = value3;
+ }
+ this.setElementVector = function(index, vector) {
+ this.elements[index * numComponents + 0] = vector[0];
+ this.elements[index * numComponents + 1] = vector[1];
+ this.elements[index * numComponents + 2] = vector[2];
+ this.elements[index * numComponents + 3] = vector[3];
+ }
+ break;
+ default:
+ throw 'A stream must contain between 1 and 4 components';
+ }
+};
+
+/**
+ * Get the number of elements in the stream.
+ * @return {number} The number of elements in the stream.
+ */
+o3djs.primitives.VertexStreamInfo.prototype.numElements = function() {
+ return this.elements.length / this.numComponents;
+};
+
+/**
+ * Create a VertexStreamInfo.
+ * @param {number} numComponents The number of numerical components per
+ * element.
+ * @param {!o3d.Stream.Semantic} semantic The semantic of the stream.
+ * @param {number} opt_semanticIndex The semantic index of the stream.
+ * Defaults to zero.
+ * @return {!o3djs.primitives.VertexStreamInfo} The new stream.
+ */
+o3djs.primitives.createVertexStreamInfo = function(numComponents,
+ semantic,
+ opt_semanticIndex) {
+ return new o3djs.primitives.VertexStreamInfo(numComponents,
+ semantic,
+ opt_semanticIndex);
+};
+
+/**
+ * VertexInfo. Used to store vertices and indices.
+ * @constructor
+ */
+o3djs.primitives.VertexInfo = function() {
+ this.streams = [];
+ this.indices = [];
+};
+
+/**
+ * Add a new stream to the VertexInfo, replacing it with a new empty one
+ * if it already exists.
+ * @param {number} numComponents The number of components per vector.
+ * @param {!o3d.Stream.Semantic} semantic The semantic of the stream.
+ * @param {number} opt_semanticIndex The semantic index of the stream.
+ * Defaults to zero.
+ * @return {!o3djs.primitives.VertexStreamInfo} The new stream.
+ */
+o3djs.primitives.VertexInfo.prototype.addStream = function(
+ numComponents,
+ semantic,
+ opt_semanticIndex) {
+ this.removeStream(semantic, opt_semanticIndex);
+ var stream = o3djs.primitives.createVertexStreamInfo(
+ numComponents,
+ semantic,
+ opt_semanticIndex);
+ this.streams.push(stream);
+ return stream;
+};
+
+/**
+ * Find a stream in the VertexInfo.
+ * @param {!o3d.Stream.Semantic} semantic The semantic of the stream.
+ * @param {number} opt_semanticIndex The semantic index of the stream.
+ * Defaults to zero.
+ * @return {o3djs.primitives.VertexStreamInfo} The stream or null if it
+ * is not present.
+ */
+o3djs.primitives.VertexInfo.prototype.findStream = function(
+ semantic,
+ opt_semanticIndex) {
+ opt_semanticIndex = opt_semanticIndex || 0;
+ for (var i = 0; i < this.streams.length; ++i) {
+ if (this.streams[i].semantic === semantic &&
+ this.streams[i].semanticIndex == opt_semanticIndex) {
+ return this.streams[i];
+ }
+ }
+ return null;
+};
+
+/**
+ * Remove a stream from the VertexInfo. Does nothing if a matching stream
+ * does not exist.
+ * @param {!o3d.Stream.Semantic} semantic The semantic of the stream.
+ * @param {number} opt_semanticIndex The semantic index of the stream.
+ * Defaults to zero.
+ */
+o3djs.primitives.VertexInfo.prototype.removeStream = function(
+ semantic,
+ opt_semanticIndex) {
+ opt_semanticIndex = opt_semanticIndex || 0;
+ for (var i = 0; i < this.streams.length; ++i) {
+ if (this.streams[i].semantic === semantic &&
+ this.streams[i].semanticIndex == opt_semanticIndex) {
+ this.streams.splice(i, 1);
+ return;
+ }
+ }
+};
+
+/**
+ * Returns the number of triangles represented by the VertexInfo.
+ * @return {number} The number of triangles represented by VertexInfo.
+ */
+o3djs.primitives.VertexInfo.prototype.numTriangles = function() {
+ return this.indices.length / 3;
+};
+
+/**
+ * Adds a triangle.
+ * @param {number} index1 The index of the first vertex of the triangle.
+ * @param {number} index2 The index of the second vertex of the triangle.
+ * @param {number} index3 The index of the third vertex of the triangle.
+ */
+o3djs.primitives.VertexInfo.prototype.addTriangle = function(
+ index1, index2, index3) {
+ this.indices.push(index1, index2, index3);
+};
+
+/**
+ * Gets the vertex indices of the triangle at the given triangle index.
+ * @param {number} triangleIndex The index of the triangle.
+ * @return {!Array.<number>} An array of three triangle indices.
+ */
+o3djs.primitives.VertexInfo.prototype.getTriangle = function(
+ triangleIndex) {
+ var indexIndex = triangleIndex * 3;
+ return [this.indices[indexIndex + 0],
+ this.indices[indexIndex + 1],
+ this.indices[indexIndex + 2]];
+};
+
+/**
+ * Sets the vertex indices of thye triangle at the given triangle index.
+ * @param {number} triangleIndex The index of the triangle.
+ * @param {number} index1 The index of the first vertex of the triangle.
+ * @param {number} index2 The index of the second vertex of the triangle.
+ * @param {number} index3 The index of the third vertex of the triangle.
+ */
+o3djs.primitives.VertexInfo.prototype.setTriangle = function(
+ triangleIndex, index1, index2, index3) {
+ var indexIndex = triangleIndex * 3;
+ this.indices[indexIndex + 0] = index1;
+ this.indices[indexIndex + 1] = index2;
+ this.indices[indexIndex + 2] = index3;
+};
+
+/**
+ * Validates that all the streams contain the same number of elements, that
+ * all the indices are within range and that a position stream is present.
+ */
+o3djs.primitives.VertexInfo.prototype.validate = function() {
+ // Check the position stream is present.
+ var positionStream = this.findStream(o3djs.base.o3d.Stream.POSITION);
+ if (!positionStream)
+ throw 'POSITION stream is missing';
+
+ // Check all the streams have the same number of elements.
+ var numElements = positionStream.numElements();
+ for (var s = 0; s < this.streams.length; ++s) {
+ if (this.streams[s].numElements() !== numElements) {
+ throw 'Stream ' + s + ' contains ' + this.streams[s].numElements() +
+ ' elements whereas the POSITION stream contains ' + numElements;
+ }
+ }
+
+ // Check all the indices are in range.
+ for (var i = 0; i < this.indices.length; ++i) {
+ if (this.indices[i] < 0 || this.indices[i] >= numElements) {
+ throw 'The index ' + this.indices[i] + ' is out of range [0, ' +
+ numElements + ']';
+ }
+ }
+};
+
+/**
+ * Creates a shape from a VertexInfo
+ * @param {!o3d.Pack} pack Pack to create objects in.
+ * @param {!o3d.Material} material to use.
+ * @return {!o3d.Shape} The created shape.
+ */
+o3djs.primitives.VertexInfo.prototype.createShape = function(
+ pack,
+ material) {
+ this.validate();
+
+ var positionStream = this.findStream(o3djs.base.o3d.Stream.POSITION);
+ var numVertices = positionStream.numElements();
+
+ // create a shape and primitive for the vertices.
+ var shape = pack.createObject('Shape');
+ var primitive = pack.createObject('Primitive');
+ var streamBank = pack.createObject('StreamBank');
+ primitive.owner = shape;
+ primitive.streamBank = streamBank;
+ primitive.material = material;
+ primitive.numberPrimitives = this.indices.length / 3;
+ primitive.primitiveType = o3djs.base.o3d.Primitive.TRIANGLELIST;
+ primitive.numberVertices = numVertices;
+ primitive.createDrawElement(pack, null);
+
+ // Calculate the tangent and binormal or provide defaults or fail if the
+ // effect requires either and they are not present.
+ var streamInfos = material.effect.getStreamInfo();
+ for (var s = 0; s < streamInfos.length; ++s) {
+ var semantic = streamInfos[s].semantic;
+ var semanticIndex = streamInfos[s].semanticIndex;
+
+ var requiredStream = this.findStream(semantic, semanticIndex);
+ if (!requiredStream) {
+ switch (semantic) {
+ case o3djs.base.o3d.Stream.TANGENT:
+ case o3djs.base.o3d.Stream.BINORMAL:
+ this.addTangentStreams(semanticIndex);
+ break;
+ case o3djs.base.o3d.Stream.COLOR:
+ requiredStream = this.addStream(4, semantic, semanticIndex);
+ for (var i = 0; i < numVertices; ++i) {
+ requiredStream.addElement(1, 1, 1, 1);
+ }
+ break;
+ default:
+ throw 'Missing stream for semantic ' + semantic +
+ ' with semantic index ' + semanticIndex;
+ }
+ }
+ }
+
+ // These next few lines take our javascript streams and load them into a
+ // 'buffer' where the 3D hardware can find them. We have to do this
+ // because the 3D hardware can't 'see' javascript data until we copy it to
+ // a buffer.
+ var vertexBuffer = pack.createObject('VertexBuffer');
+ var fields = [];
+ for (var s = 0; s < this.streams.length; ++s) {
+ var stream = this.streams[s];
+ var fieldType = (stream.semantic == o3djs.base.o3d.Stream.COLOR &&
+ stream.numComponents == 4) ? 'UByteNField' : 'FloatField';
+ fields[s] = vertexBuffer.createField(fieldType, stream.numComponents);
+ streamBank.setVertexStream(stream.semantic,
+ stream.semanticIndex,
+ fields[s],
+ 0);
+ }
+ vertexBuffer.allocateElements(numVertices);
+ for (var s = 0; s < this.streams.length; ++s) {
+ fields[s].setAt(0, this.streams[s].elements);
+ }
+
+ var indexBuffer = pack.createObject('IndexBuffer');
+ indexBuffer.set(this.indices);
+ primitive.indexBuffer = indexBuffer;
+ o3djs.primitives.setCullingInfo(primitive);
+ return shape;
+};
+
+/**
+ * Reorients the vertices, positions and normals, of this vertexInfo by the
+ * given matrix. In other words, it multiplies each vertex by the given matrix
+ * and each normal by the inverse-transpose of the given matrix.
+ * @param {!o3djs.math.Matrix4} matrix Matrix by which to multiply.
+ */
+o3djs.primitives.VertexInfo.prototype.reorient = function(matrix) {
+ var math = o3djs.math;
+ var matrixInverse = math.inverse(math.matrix4.getUpper3x3(matrix));
+
+ for (var s = 0; s < this.streams.length; ++s) {
+ var stream = this.streams[s];
+ if (stream.numComponents == 3) {
+ var numElements = stream.numElements();
+ switch (stream.semantic) {
+ case o3djs.base.o3d.Stream.POSITION:
+ for (var i = 0; i < numElements; ++i) {
+ stream.setElementVector(i,
+ math.matrix4.transformPoint(matrix,
+ stream.getElementVector(i)));
+ }
+ break;
+ case o3djs.base.o3d.Stream.NORMAL:
+ for (var i = 0; i < numElements; ++i) {
+ stream.setElementVector(i,
+ math.matrix4.transformNormal(matrix,
+ stream.getElementVector(i)));
+ }
+ break;
+ case o3djs.base.o3d.Stream.TANGENT:
+ case o3djs.base.o3d.Stream.BINORMAL:
+ for (var i = 0; i < numElements; ++i) {
+ stream.setElementVector(i,
+ math.matrix4.transformDirection(matrix,
+ stream.getElementVector(i)));
+ }
+ break;
+ }
+ }
+ }
+};
+
+/**
+ * Calculate tangents and binormals based on the positions, normals and
+ * texture coordinates found in existing streams.
+ * @param {number} opt_semanticIndex The semantic index of the texture
+ * coordinate to use and the tangent and binormal streams to add. Defaults
+ * to zero.
+ */
+o3djs.primitives.VertexInfo.prototype.addTangentStreams =
+ function(opt_semanticIndex) {
+ opt_semanticIndex = opt_semanticIndex || 0;
+ var math = o3djs.math;
+
+ this.validate();
+
+ // Find and validate the position, normal and texture coordinate frames.
+ var positionStream = this.findStream(o3djs.base.o3d.Stream.POSITION);
+ if (!positionStream)
+ throw 'Cannot calculate tangent frame because POSITION stream is missing';
+ if (positionStream.numComponents != 3)
+ throw 'Cannot calculate tangent frame because POSITION stream is not 3D';
+
+ var normalStream = this.findStream(o3djs.base.o3d.Stream.NORMAL);
+ if (!normalStream)
+ throw 'Cannot calculate tangent frame because NORMAL stream is missing';
+ if (normalStream.numComponents != 3)
+ throw 'Cannot calculate tangent frame because NORMAL stream is not 3D';
+
+ var texCoordStream = this.findStream(o3djs.base.o3d.Stream.TEXCOORD,
+ opt_semanticIndex);
+ if (!texCoordStream)
+ throw 'Cannot calculate tangent frame because TEXCOORD stream ' +
+ opt_semanticIndex + ' is missing';
+
+ // Maps from position, normal key to tangent and binormal matrix.
+ var tangentFrames = {};
+
+ // Rounds a vector to integer components.
+ function roundVector(v) {
+ return [Math.round(v[0]), Math.round(v[1]), Math.round(v[2])];
+ }
+
+ // Generates a key for the tangentFrames map from a position and normal
+ // vector. Rounds position and normal to allow some tolerance.
+ function tangentFrameKey(position, normal) {
+ return roundVector(math.mulVectorScalar(position, 100)) + ',' +
+ roundVector(math.mulVectorScalar(normal, 100));
+ }
+
+ // Accumulates into the tangent and binormal matrix at the approximate
+ // position and normal.
+ function addTangentFrame(position, normal, tangent, binormal) {
+ var key = tangentFrameKey(position, normal);
+ var frame = tangentFrames[key];
+ if (!frame) {
+ frame = [[0, 0, 0], [0, 0, 0]];
+ }
+ frame = math.addMatrix(frame, [tangent, binormal]);
+ tangentFrames[key] = frame;
+ }
+
+ // Get the tangent and binormal matrix at the approximate position and
+ // normal.
+ function getTangentFrame(position, normal) {
+ var key = tangentFrameKey(position, normal);
+ return tangentFrames[key];
+ }
+
+ var numTriangles = this.numTriangles();
+ for (var triangleIndex = 0; triangleIndex < numTriangles; ++triangleIndex) {
+ // Get the vertex indices, uvs and positions for the triangle.
+ var vertexIndices = this.getTriangle(triangleIndex);
+ var uvs = [];
+ var positions = [];
+ var normals = [];
+ for (var i = 0; i < 3; ++i) {
+ var vertexIndex = vertexIndices[i];
+ uvs[i] = texCoordStream.getElementVector(vertexIndex);
+ positions[i] = positionStream.getElementVector(vertexIndex);
+ normals[i] = normalStream.getElementVector(vertexIndex);
+ }
+
+ // Calculate the tangent and binormal for the triangle using method
+ // described in Maya documentation appendix A: tangent and binormal
+ // vectors.
+ var tangent = [0, 0, 0];
+ var binormal = [0, 0, 0];
+ for (var axis = 0; axis < 3; ++axis) {
+ var edge1 = [positions[1][axis] - positions[0][axis],
+ uvs[1][0] - uvs[0][0], uvs[1][1] - uvs[0][1]];
+ var edge2 = [positions[2][axis] - positions[0][axis],
+ uvs[2][0] - uvs[0][0], uvs[2][1] - uvs[0][1]];
+ var edgeCross = math.normalize(math.cross(edge1, edge2));
+ if (edgeCross[0] == 0) {
+ edgeCross[0] = 1;
+ }
+ tangent[axis] = -edgeCross[1] / edgeCross[0];
+ binormal[axis] = -edgeCross[2] / edgeCross[0];
+ }
+
+ // Normalize the tangent and binornmal.
+ var tangentLength = math.length(tangent);
+ if (tangentLength > 0.001) {
+ tangent = math.mulVectorScalar(tangent, 1 / tangentLength);
+ }
+ var binormalLength = math.length(binormal);
+ if (binormalLength > 0.001) {
+ binormal = math.mulVectorScalar(binormal, 1 / binormalLength);
+ }
+
+ // Accumulate the tangent and binormal into the tangent frame map.
+ for (var i = 0; i < 3; ++i) {
+ addTangentFrame(positions[i], normals[i], tangent, binormal);
+ }
+ }
+
+ // Add the tangent and binormal streams.
+ var tangentStream = this.addStream(3,
+ o3djs.base.o3d.Stream.TANGENT,
+ opt_semanticIndex);
+ var binormalStream = this.addStream(3,
+ o3djs.base.o3d.Stream.BINORMAL,
+ opt_semanticIndex);
+
+ // Extract the tangent and binormal for each vertex.
+ var numVertices = positionStream.numElements();
+ for (var vertexIndex = 0; vertexIndex < numVertices; ++vertexIndex) {
+ var position = positionStream.getElementVector(vertexIndex);
+ var normal = normalStream.getElementVector(vertexIndex);
+ var frame = getTangentFrame(position, normal);
+
+ // Orthonormalize the tangent with respect to the normal.
+ var tangent = frame[0];
+ tangent = math.subVector(
+ tangent, math.mulVectorScalar(normal, math.dot(normal, tangent)));
+ var tangentLength = math.length(tangent);
+ if (tangentLength > 0.001) {
+ tangent = math.mulVectorScalar(tangent, 1 / tangentLength);
+ }
+
+ // Orthonormalize the binormal with respect to the normal and the tangent.
+ var binormal = frame[1];
+ binormal = math.subVector(
+ binormal, math.mulVectorScalar(tangent, math.dot(tangent, binormal)));
+ binormal = math.subVector(
+ binormal, math.mulVectorScalar(normal, math.dot(normal, binormal)));
+ var binormalLength = math.length(binormal);
+ if (binormalLength > 0.001) {
+ binormal = math.mulVectorScalar(binormal, 1 / binormalLength);
+ }
+
+ tangentStream.setElementVector(vertexIndex, tangent);
+ binormalStream.setElementVector(vertexIndex, binormal);
+ }
+};
+
+/**
+ * Creates a new VertexInfo.
+ * @return {!o3djs.primitives.VertexInfo} The new VertexInfo.
+ */
+o3djs.primitives.createVertexInfo = function() {
+ return new o3djs.primitives.VertexInfo();
+};
+
+/**
+ * Creates sphere vertices.
+ * The created sphere has position, normal and uv streams.
+ *
+ * @param {number} radius radius of the sphere.
+ * @param {number} subdivisionsAxis number of steps around the sphere.
+ * @param {number} subdivisionsHeight number of vertically on the sphere.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3djs.primitives.VertexInfo} The created sphere vertices.
+ */
+o3djs.primitives.createSphereVertices = function(radius,
+ subdivisionsAxis,
+ subdivisionsHeight,
+ opt_matrix) {
+ if (subdivisionsAxis <= 0 || subdivisionsHeight <= 0) {
+ throw RangeError('subdivisionAxis and subdivisionHeight must be > 0');
+ }
+
+ // We are going to generate our sphere by iterating through its
+ // spherical coordinates and generating 2 triangles for each quad on a
+ // ring of the sphere.
+
+ var vertexInfo = o3djs.primitives.createVertexInfo();
+ var positionStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.POSITION);
+ var normalStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.NORMAL);
+ var texCoordStream = vertexInfo.addStream(
+ 2, o3djs.base.o3d.Stream.TEXCOORD, 0);
+
+ // Generate the individual vertices in our vertex buffer.
+ for (var y = 0; y <= subdivisionsHeight; y++) {
+ for (var x = 0; x <= subdivisionsAxis; x++) {
+ // Generate a vertex based on its spherical coordinates
+ var u = x / subdivisionsAxis;
+ var v = y / subdivisionsHeight;
+ var theta = 2 * Math.PI * u;
+ var phi = Math.PI * v;
+ var sinTheta = Math.sin(theta);
+ var cosTheta = Math.cos(theta);
+ var sinPhi = Math.sin(phi);
+ var cosPhi = Math.cos(phi);
+ var ux = cosTheta * sinPhi;
+ var uy = cosPhi;
+ var uz = sinTheta * sinPhi;
+ positionStream.addElement(radius * ux, radius * uy, radius * uz);
+ normalStream.addElement(ux, uy, uz);
+ texCoordStream.addElement(1 - u, 1 - v);
+ }
+ }
+ var numVertsAround = subdivisionsAxis + 1;
+
+ for (var x = 0; x < subdivisionsAxis; x++) {
+ for (var y = 0; y < subdivisionsHeight; y++) {
+ // Make triangle 1 of quad.
+ vertexInfo.addTriangle(
+ (y + 0) * numVertsAround + x,
+ (y + 0) * numVertsAround + x + 1,
+ (y + 1) * numVertsAround + x);
+
+ // Make triangle 2 of quad.
+ vertexInfo.addTriangle(
+ (y + 1) * numVertsAround + x,
+ (y + 0) * numVertsAround + x + 1,
+ (y + 1) * numVertsAround + x + 1);
+ }
+ }
+
+ if (opt_matrix) {
+ vertexInfo.reorient(opt_matrix);
+ }
+ return vertexInfo;
+};
+
+/**
+ * Creates a sphere.
+ * The created sphere has position, normal and uv streams.
+ *
+ * @param {!o3d.Pack} pack Pack to create sphere elements in.
+ * @param {!o3d.Material} material to use.
+ * @param {number} radius radius of the sphere.
+ * @param {number} subdivisionsAxis number of steps around the sphere.
+ * @param {number} subdivisionsHeight number of vertically on the sphere.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3d.Shape} The created sphere.
+ *
+ * @see o3d.Pack
+ * @see o3d.Shape
+ */
+o3djs.primitives.createSphere = function(pack,
+ material,
+ radius,
+ subdivisionsAxis,
+ subdivisionsHeight,
+ opt_matrix) {
+ var vertexInfo = o3djs.primitives.createSphereVertices(
+ radius,
+ subdivisionsAxis,
+ subdivisionsHeight,
+ opt_matrix);
+
+ return vertexInfo.createShape(pack, material);
+};
+
+/**
+ * Array of the indices of corners of each face of a cube.
+ * @private
+ * @type {!Array.<!Array.<number>>}
+ */
+o3djs.primitives.CUBE_FACE_INDICES_ = [
+ [3, 7, 5, 1],
+ [0, 4, 6, 2],
+ [6, 7, 3, 2],
+ [0, 1, 5, 4],
+ [5, 7, 6, 4],
+ [2, 3, 1, 0]
+];
+
+/**
+ * Creates the vertices and indices for a cube. The
+ * cube will be created around the origin. (-size / 2, size / 2)
+ * The created cube has position, normal and uv streams.
+ *
+ * @param {number} size Width, height and depth of the cube.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3djs.primitives.VertexInfo} The created cube vertices.
+ */
+o3djs.primitives.createCubeVertices = function(size, opt_matrix) {
+ var k = size / 2;
+
+ var cornerVertices = [
+ [-k, -k, -k],
+ [+k, -k, -k],
+ [-k, +k, -k],
+ [+k, +k, -k],
+ [-k, -k, +k],
+ [+k, -k, +k],
+ [-k, +k, +k],
+ [+k, +k, +k]
+ ];
+
+ var faceNormals = [
+ [+1, +0, +0],
+ [-1, +0, +0],
+ [+0, +1, +0],
+ [+0, -1, +0],
+ [+0, +0, +1],
+ [+0, +0, -1]
+ ];
+
+ var uvCoords = [
+ [0, 0],
+ [1, 0],
+ [1, 1],
+ [0, 1]
+ ];
+
+ var vertexInfo = o3djs.primitives.createVertexInfo();
+ var positionStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.POSITION);
+ var normalStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.NORMAL);
+ var texCoordStream = vertexInfo.addStream(
+ 2, o3djs.base.o3d.Stream.TEXCOORD, 0);
+
+ for (var f = 0; f < 6; ++f) {
+ var faceIndices = o3djs.primitives.CUBE_FACE_INDICES_[f];
+ for (var v = 0; v < 4; ++v) {
+ var position = cornerVertices[faceIndices[v]];
+ var normal = faceNormals[f];
+ var uv = uvCoords[v];
+
+ // Each face needs all four vertices because the normals and texture
+ // coordinates are not all the same.
+ positionStream.addElementVector(position);
+ normalStream.addElementVector(normal);
+ texCoordStream.addElementVector(uv);
+
+ // Two triangles make a square face.
+ var offset = 4 * f;
+ vertexInfo.addTriangle(offset + 0, offset + 1, offset + 2);
+ vertexInfo.addTriangle(offset + 0, offset + 2, offset + 3);
+ }
+ }
+
+ if (opt_matrix) {
+ vertexInfo.reorient(opt_matrix);
+ }
+ return vertexInfo;
+};
+
+/**
+ * Creates a cube.
+ * The cube will be created around the origin. (-size / 2, size / 2)
+ * The created cube has position, normal and uv streams.
+ *
+ * @param {!o3d.Pack} pack Pack to create cube elements in.
+ * @param {!o3d.Material} material to use.
+ * @param {number} size Width, height and depth of the cube.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3d.Shape} The created cube.
+ *
+ * @see o3d.Pack
+ * @see o3d.Shape
+ */
+o3djs.primitives.createCube = function(pack,
+ material,
+ size,
+ opt_matrix) {
+ var vertexInfo = o3djs.primitives.createCubeVertices(size, opt_matrix);
+ return vertexInfo.createShape(pack, material);
+};
+
+/**
+ * Creates a box. The box will be created around the origin.
+ * The created box has position, normal and uv streams.
+ *
+ * @param {!o3d.Pack} pack Pack to create Box elements in.
+ * @param {!o3d.Material} material to use.
+ * @param {number} width Width of the box.
+ * @param {number} height Height of the box.
+ * @param {number} depth Depth of the box.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3d.Shape} The created Box.
+ *
+ * @see o3d.Pack
+ * @see o3d.Shape
+ */
+o3djs.primitives.createBox = function(pack,
+ material,
+ width,
+ height,
+ depth,
+ opt_matrix) {
+ var vertexInfo = o3djs.primitives.createCubeVertices(1);
+ vertexInfo.reorient([[width, 0, 0, 0],
+ [0, height, 0, 0],
+ [0, 0, depth, 0],
+ [0, 0, 0, 1]]);
+
+ if (opt_matrix) {
+ vertexInfo.reorient(opt_matrix);
+ }
+ return vertexInfo.createShape(pack, material);
+};
+
+/**
+ * Creates a cube with varying vertex colors. The cube will be created
+ * around the origin. (-size / 2, size / 2)
+ *
+ * @param {!o3d.Pack} pack Pack to create cube elements in.
+ * @param {!o3d.Material} material to use.
+ * @param {number} size Width, height and depth of the cube.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3d.Shape} The created cube.
+ *
+ * @see o3d.Pack
+ * @see o3d.Shape
+ */
+o3djs.primitives.createRainbowCube = function(pack,
+ material,
+ size,
+ opt_matrix) {
+ var vertexInfo = o3djs.primitives.createCubeVertices(size, opt_matrix);
+ var colorStream = vertexInfo.addStream(
+ 4, o3djs.base.o3d.Stream.COLOR);
+
+ var colors = [
+ [1, 0, 0, 1],
+ [0, 1, 0, 1],
+ [0, 0, 1, 1],
+ [1, 1, 0, 1],
+ [0, 1, 1, 1],
+ [1, 0, 1, 1],
+ [0, .5, .3, 1],
+ [.3, 0, .5, 1]
+ ];
+
+ var vertices = vertexInfo.vertices;
+ for (var f = 0; f < 6; ++f) {
+ var faceIndices = o3djs.primitives.CUBE_FACE_INDICES_[f];
+ for (var v = 0; v < 4; ++v) {
+ var color = colors[faceIndices[v]];
+ colorStream.addElementVector(color);
+ }
+ }
+
+ return vertexInfo.createShape(pack, material);
+};
+
+/**
+ * Creates disc vertices. The disc will be in the xz plane, centered
+ * at the origin. When creating, at least 3 divisions, or pie pieces, need
+ * to be specified, otherwise the triangles making up the disc will be
+ * degenerate. You can also specify the number of radial pieces (opt_stacks).
+ * A value of 1 for opt_stacks will give you a simple disc of pie pieces. If
+ * you want to create an annulus by omitting some of the center stacks, you
+ * can specify the stack at which to start creating triangles. Finally,
+ * stackPower allows you to have the widths increase or decrease as you move
+ * away from the center. This is particularly useful when using the disc as a
+ * ground plane with a fixed camera such that you don't need the resolution of
+ * small triangles near the perimeter. For example, a value of 2 will produce
+ * stacks whose ouside radius increases with the square of the stack index. A
+ * value of 1 will give uniform stacks.
+ *
+ * @param {number} radius Radius of the ground plane.
+ * @param {number} divisions Number of triangles in the ground plane
+ * (at least 3).
+ * @param {number} opt_stacks Number of radial divisions (default=1).
+ * @param {number} opt_startStack Which radial division to start dividing at.
+ * @param {number} opt_stackPower Power to raise stack size to for decreasing
+ * width.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3djs.primitives.VertexInfo} The created plane vertices.
+ */
+o3djs.primitives.createDiscVertices = function(radius,
+ divisions,
+ opt_stacks,
+ opt_startStack,
+ opt_stackPower,
+ opt_matrix) {
+ if (divisions < 3) {
+ throw RangeError('divisions must be at least 3');
+ }
+
+ var stacks = opt_stacks ? opt_stacks : 1;
+ var startStack = opt_startStack ? opt_startStack : 0;
+ var stackPower = opt_stackPower ? opt_stackPower : 1;
+
+ var vertexInfo = o3djs.primitives.createVertexInfo();
+ var positionStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.POSITION);
+ var normalStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.NORMAL);
+ var texCoordStream = vertexInfo.addStream(
+ 2, o3djs.base.o3d.Stream.TEXCOORD, 0);
+
+ // Initialize the center vertex.
+ // x y z nx ny nz r g b a u v
+ var firstIndex = 0;
+
+ if (startStack == 0) {
+ positionStream.addElement(0, 0, 0);
+ normalStream.addElement(0, 1, 0);
+ texCoordStream.addElement(0, 0);
+ firstIndex++;
+ }
+
+ // Build the disk one stack at a time.
+ for (var currentStack = Math.max(startStack, 1);
+ currentStack <= stacks;
+ ++currentStack) {
+ var stackRadius = radius * Math.pow(currentStack / stacks, stackPower);
+
+ for (var i = 0; i < divisions; ++i) {
+ var theta = 2.0 * Math.PI * i / divisions;
+ var x = stackRadius * Math.cos(theta);
+ var z = stackRadius * Math.sin(theta);
+
+ positionStream.addElement(x, 0, z);
+ normalStream.addElement(0, 1, 0);
+ texCoordStream.addElement(x, z);
+
+ if (currentStack > startStack) {
+ // a, b, c and d are the indices of the vertices of a quad. unless
+ // the current stack is the one closest to the center, in which case
+ // the vertices a and b connect to the center vertex.
+ var a = firstIndex + (i + 1) % divisions;
+ var b = firstIndex + i;
+ if (currentStack > 1) {
+ var c = firstIndex + i - divisions;
+ var d = firstIndex + (i + 1) % divisions - divisions;
+
+ // Make a quad of the vertices a, b, c, d.
+ vertexInfo.addTriangle(a, b, c);
+ vertexInfo.addTriangle(a, c, d);
+ } else {
+ // Make a single triangle of a, b and the center.
+ vertexInfo.addTriangle(0, a, b);
+ }
+ }
+ }
+
+ firstIndex += divisions;
+ }
+
+ if (opt_matrix) {
+ vertexInfo.reorient(opt_matrix);
+ }
+ return vertexInfo;
+};
+
+/**
+ * Creates a disc shape. The disc will be in the xz plane, centered
+ * at the origin. When creating, at least 3 divisions, or pie pieces, need
+ * to be specified, otherwise the triangles making up the disc will be
+ * degenerate. You can also specify the number of radial pieces (opt_stacks).
+ * A value of 1 for opt_stacks will give you a simple disc of pie pieces. If
+ * you want to create an annulus by omitting some of the center stacks, you
+ * can specify the stack at which to start creating triangles. Finally,
+ * stackPower allows you to have the widths increase or decrease as you move
+ * away from the center. This is particularly useful when using the disc as a
+ * ground plane with a fixed camera such that you don't need the resolution of
+ * small triangles near the perimeter. For example, a value of 2 will produce
+ * stacks whose ouside radius increases with the square of the stack index. A
+ * value of 1 will give uniform stacks.
+ *
+ * @param {!o3d.Pack} pack Pack to create disc elements in.
+ * @param {!o3d.Material} material to use.
+ * @param {number} radius Radius of the disc.
+ * @param {number} divisions Number of triangles in the disc (at least 3).
+ * @param {number} stacks Number of radial divisions.
+ * @param {number} startStack Which radial division to start dividing at.
+ * @param {number} stackPower Power to raise stack size to for decreasing width.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3d.Shape} The created disc.
+ *
+ * @see o3d.Pack
+ * @see o3d.Shape
+ */
+o3djs.primitives.createDisc = function(pack, material,
+ radius, divisions, stacks,
+ startStack, stackPower,
+ opt_matrix) {
+ var vertexInfo = o3djs.primitives.createDiscVertices(radius, divisions,
+ stacks,
+ startStack,
+ stackPower,
+ opt_matrix);
+
+ return vertexInfo.createShape(pack, material);
+};
+
+/**
+ * Creates cylinder vertices. The cylinder will be created around the origin
+ * along the y-axis. The created cylinder has position, normal and uv streams.
+ *
+ * @param {number} radius Radius of cylinder.
+ * @param {number} height Height of cylinder.
+ * @param {number} radialSubdivisions The number of subdivisions around the
+ * cylinder.
+ * @param {number} verticalSubdivisions The number of subdivisions down the
+ * cylinder.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {o3djs.primitives.VertexInfo} The created cylinder vertices.
+ */
+o3djs.primitives.createCylinderVertices = function(radius,
+ height,
+ radialSubdivisions,
+ verticalSubdivisions,
+ opt_matrix) {
+ if (radialSubdivisions < 1) {
+ throw RangeError('radialSubdivisions must be 1 or greater');
+ }
+
+ if (verticalSubdivisions < 1) {
+ throw RangeError('verticalSubdivisions must be 1 or greater');
+ }
+
+ var vertexInfo = o3djs.primitives.createVertexInfo();
+ var positionStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.POSITION);
+ var normalStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.NORMAL);
+ var texCoordStream = vertexInfo.addStream(
+ 2, o3djs.base.o3d.Stream.TEXCOORD, 0);
+
+ var indices = [];
+ var vertices = [];
+ var vertsAroundEdge = radialSubdivisions + 1;
+
+ for (var yy = -2; yy <= verticalSubdivisions + 2; ++yy) {
+ var ringRadius = radius;
+ var v = yy / verticalSubdivisions
+ var y = height * v;
+ if (yy < 0) {
+ y = 0;
+ v = 1;
+ } else if (yy > verticalSubdivisions) {
+ y = height;
+ v = 1;
+ }
+ if (yy == -2 || yy == verticalSubdivisions + 2) {
+ ringRadius = 0;
+ v = 0;
+ }
+ y -= height / 2;
+ for (var ii = 0; ii < vertsAroundEdge; ++ii) {
+ var sin = Math.sin(ii * Math.PI * 2 / radialSubdivisions);
+ var cos = Math.cos(ii * Math.PI * 2 / radialSubdivisions);
+ positionStream.addElement(sin * ringRadius, y, cos * ringRadius);
+ normalStream.addElement(
+ (yy < 0 || yy > verticalSubdivisions) ? 0 : sin,
+ (yy < 0) ? -1 : (yy > verticalSubdivisions ? 1 : 0),
+ (yy < 0 || yy > verticalSubdivisions) ? 0 : cos);
+ texCoordStream.addElement(ii / radialSubdivisions, v);
+ }
+ }
+
+ var trisAround = radialSubdivisions * 2;
+ for (var yy = 0; yy < verticalSubdivisions + 4; ++yy) {
+ for (var ii = 0; ii < radialSubdivisions; ++ii) {
+ vertexInfo.addTriangle(vertsAroundEdge * (yy + 0) + 0 + ii,
+ vertsAroundEdge * (yy + 0) + 1 + ii,
+ vertsAroundEdge * (yy + 1) + 1 + ii);
+ vertexInfo.addTriangle(vertsAroundEdge * (yy + 0) + 0 + ii,
+ vertsAroundEdge * (yy + 1) + 1 + ii,
+ vertsAroundEdge * (yy + 1) + 0 + ii);
+ }
+ }
+
+ if (opt_matrix) {
+ vertexInfo.reorient(opt_matrix);
+ }
+ return vertexInfo;
+};
+
+/**
+ * Creates cylinder a cylinder shape. The cylinder will be created around the
+ * origin along the y-axis. The created cylinder has position, normal and uv
+ * streams.
+ *
+ * @param {!o3d.Pack} pack Pack to create cylinder elements in.
+ * @param {!o3d.Material} material to use.
+ * @param {number} radius Radius of cylinder.
+ * @param {number} depth Depth of cylinder.
+ * @param {number} radialSubdivisions The number of subdivisions around the
+ * cylinder.
+ * @param {number} verticalSubdivisions The number of subdivisions down the
+ * cylinder.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3d.Shape} The created cylinder.
+ */
+o3djs.primitives.createCylinder = function(pack,
+ material,
+ radius,
+ depth,
+ radialSubdivisions,
+ verticalSubdivisions,
+ opt_matrix) {
+ var vertexInfo = o3djs.primitives.createCylinderVertices(
+ radius,
+ depth,
+ radialSubdivisions,
+ verticalSubdivisions,
+ opt_matrix);
+ return vertexInfo.createShape(pack, material);
+};
+
+/**
+ * Creates wedge vertices, wedge being an extruded triangle. The wedge will be
+ * created around the 3 2d points passed in and extruded along the z axis. The
+ * created wedge has position, normal and uv streams.
+ *
+ * @param {!Array.<!Array.<number>>} inPoints Array of 2d points in the format
+ * [[x1, y1], [x2, y2], [x3, y3]] that describe a 2d triangle.
+ * @param {number} depth The depth to extrude the triangle.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3djs.primitives.VertexInfo} The created cylinder vertices.
+ */
+o3djs.primitives.createWedgeVertices = function(inPoints, depth,
+ opt_matrix) {
+ var math = o3djs.math;
+
+ var vertexInfo = o3djs.primitives.createVertexInfo();
+ var positionStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.POSITION);
+ var normalStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.NORMAL);
+ var texCoordStream = vertexInfo.addStream(
+ 2, o3djs.base.o3d.Stream.TEXCOORD, 0);
+
+ var z1 = -depth * 0.5;
+ var z2 = depth * 0.5;
+ var face = [];
+ var indices = [];
+ var points = [[inPoints[0][0], inPoints[0][1]],
+ [inPoints[1][0], inPoints[1][1]],
+ [inPoints[2][0], inPoints[2][1]]];
+
+ face[0] = math.cross(
+ math.normalize([points[1][0] - points[0][0],
+ points[1][1] - points[0][1],
+ z1 - z1]),
+ math.normalize([points[1][0] - points[1][0],
+ points[1][1] - points[1][1],
+ z2 - z1]));
+ face[1] = math.cross(
+ math.normalize([points[2][0] - points[1][0],
+ points[2][1] - points[1][1],
+ z1 - z1]),
+ math.normalize([points[2][0] - points[2][0],
+ points[2][1] - points[2][1],
+ z2 - z1]));
+ face[2] = math.cross(
+ [points[0][0] - points[2][0], points[0][1] - points[2][1], z1 - z1],
+ [points[0][0] - points[0][0], points[0][1] - points[0][1], z2 - z1]);
+
+ positionStream.addElement(points[0][0], points[0][1], z1);
+ normalStream.addElement(0, 0, -1);
+ texCoordStream.addElement(0, 1);
+ positionStream.addElement(points[1][0], points[1][1], z1);
+ normalStream.addElement(0, 0, -1);
+ texCoordStream.addElement(1, 0);
+ positionStream.addElement(points[2][0], points[2][1], z1);
+ normalStream.addElement(0, 0, -1);
+ texCoordStream.addElement(0, 0);
+ // back
+ positionStream.addElement(points[0][0], points[0][1], z2);
+ normalStream.addElement(0, 0, 1);
+ texCoordStream.addElement(0, 1);
+ positionStream.addElement(points[1][0], points[1][1], z2);
+ normalStream.addElement(0, 0, 1);
+ texCoordStream.addElement(1, 0);
+ positionStream.addElement(points[2][0], points[2][1], z2);
+ normalStream.addElement(0, 0, 1);
+ texCoordStream.addElement(0, 0);
+ // face 0
+ positionStream.addElement(points[0][0], points[0][1], z1);
+ normalStream.addElement(face[0][0], face[0][1], face[0][2]);
+ texCoordStream.addElement(0, 1);
+ positionStream.addElement(points[1][0], points[1][1], z1);
+ normalStream.addElement(face[0][0], face[0][1], face[0][2]);
+ texCoordStream.addElement(0, 0);
+ positionStream.addElement(points[1][0], points[1][1], z2);
+ normalStream.addElement(face[0][0], face[0][1], face[0][2]);
+ texCoordStream.addElement(1, 0);
+ positionStream.addElement(points[0][0], points[0][1], z2);
+ normalStream.addElement(face[0][0], face[0][1], face[0][2]);
+ texCoordStream.addElement(1, 1);
+ // face 1
+ positionStream.addElement(points[1][0], points[1][1], z1);
+ normalStream.addElement(face[1][0], face[1][1], face[1][2]);
+ texCoordStream.addElement(0, 1);
+ positionStream.addElement(points[2][0], points[2][1], z1);
+ normalStream.addElement(face[1][0], face[1][1], face[1][2]);
+ texCoordStream.addElement(0, 0);
+ positionStream.addElement(points[2][0], points[2][1], z2);
+ normalStream.addElement(face[1][0], face[1][1], face[1][2]);
+ texCoordStream.addElement(1, 0);
+ positionStream.addElement(points[1][0], points[1][1], z2);
+ normalStream.addElement(face[1][0], face[1][1], face[1][2]);
+ texCoordStream.addElement(1, 1);
+ // face 2
+ positionStream.addElement(points[2][0], points[2][1], z1);
+ normalStream.addElement(face[2][0], face[2][1], face[2][2]);
+ texCoordStream.addElement(0, 1);
+ positionStream.addElement(points[0][0], points[0][1], z1);
+ normalStream.addElement(face[2][0], face[2][1], face[2][2]);
+ texCoordStream.addElement(0, 0);
+ positionStream.addElement(points[0][0], points[0][1], z2);
+ normalStream.addElement(face[2][0], face[2][1], face[2][2]);
+ texCoordStream.addElement(1, 0);
+ positionStream.addElement(points[2][0], points[2][1], z2);
+ normalStream.addElement(face[2][0], face[2][1], face[2][2]);
+ texCoordStream.addElement(1, 1);
+
+ var indices = [0, 2, 1,
+ 3, 4, 5,
+ 6, 7, 8,
+ 6, 8, 9,
+ 10, 11, 12,
+ 10, 12, 13,
+ 14, 15, 16,
+ 14, 16, 17];
+
+ if (opt_matrix) {
+ vertexInfo.reorient(opt_matrix);
+ }
+ return vertexInfo;
+};
+
+/**
+ * Creates a wedge shape. A wedge being an extruded triangle. The wedge will
+ * be created around the 3 2d points passed in and extruded along the z-axis.
+ * The created wedge has position, normal and uv streams.
+ *
+ * @param {!o3d.Pack} pack Pack to create wedge elements in.
+ * @param {!o3d.Material} material to use.
+ * @param {!Array.<!Array.<number>>} points Array of 2d points in the format
+ * [[x1, y1], [x2, y2], [x3, y3]] that describe a 2d triangle.
+ * @param {number} depth The depth to extrude the triangle.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3d.Shape} The created wedge.
+ */
+o3djs.primitives.createWedge = function(pack,
+ material,
+ points,
+ depth,
+ opt_matrix) {
+ var vertexInfo = o3djs.primitives.createWedgeVertices(points,
+ depth,
+ opt_matrix);
+ return vertexInfo.createShape(pack, material);
+};
+
+/**
+ * Creates prism vertices by extruding a polygon. The prism will be created
+ * around the 2d points passed in and extruded along the z axis. The end caps
+ * of the prism are constructed using a triangle fan originating at point 0,
+ * so a non-convex polygon might not get the desired shape, but it will if it
+ * is convex with respect to point 0. Texture coordinates map each face of
+ * the wall exactly to the unit square. Texture coordinates on the front
+ * and back faces are scaled such that the bounding rectangle of the polygon
+ * is mapped to the unit square. The created prism has position, normal,
+ * uv streams.
+ *
+ * @param {!Array.<!Array.<number>>} points Array of 2d points in the format
+ * [[x1, y1], [x2, y2], [x3, y3],...] that describe a 2d polygon.
+ * @param {number} depth The depth to extrude the triangle.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3djs.primitives.VertexInfo} The created cylinder vertices.
+ */
+o3djs.primitives.createPrismVertices = function(points,
+ depth,
+ opt_matrix) {
+ if (points.length < 3) {
+ throw RangeError('there must be 3 or more points');
+ }
+
+ var backZ = -0.5 * depth;
+ var frontZ = 0.5 * depth;
+ var normals = [];
+
+ var vertexInfo = o3djs.primitives.createVertexInfo();
+ var positionStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.POSITION);
+ var normalStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.NORMAL);
+ var texCoordStream = vertexInfo.addStream(
+ 2, o3djs.base.o3d.Stream.TEXCOORD, 0);
+
+ // Normals for the wall faces.
+ var n = points.length;
+
+ for (var i = 0; i < n; ++i) {
+ var j = (i + 1) % n;
+ var x = points[j][0] - points[i][0];
+ var y = points[j][1] - points[i][1];
+ var length = Math.sqrt(x * x + y * y);
+ normals[i] = [y / length, -x / length, 0];
+ }
+
+ // Compute the minimum and maxiumum x and y coordinates of points in the
+ // polygon.
+ var minX = points[0][0];
+ var minY = points[0][1];
+ var maxX = points[0][0];
+ var maxY = points[0][1];
+ for (var i = 1; i < n; ++i) {
+ var x = points[i][0];
+ var y = points[i][1];
+ minX = Math.min(minX, x);
+ minY = Math.min(minY, y);
+ maxX = Math.max(maxX, x);
+ maxY = Math.max(maxY, y);
+ }
+
+ // Scale the x and y coordinates of the points of the polygon to fit the
+ // bounding rectangle, and use the scaled coordinates for the uv
+ // of the front and back cap.
+ var frontUV = [];
+ var backUV = [];
+ var rangeX = maxX - minX;
+ var rangeY = maxY - minY;
+ for (var i = 0; i < n; ++i) {
+ frontUV[i] = [
+ (points[i][0] - minX) / rangeX,
+ (points[i][1] - minY) / rangeY
+ ];
+ backUV[i] = [
+ (maxX - points[i][0]) / rangeX,
+ (points[i][1] - minY) / rangeY
+ ];
+ }
+
+ for (var i = 0; i < n; ++i) {
+ var j = (i + 1) % n;
+ // Vertex on the back face.
+ positionStream.addElement(points[i][0], points[i][1], backZ);
+ normalStream.addElement(0, 0, -1);
+ texCoordStream.addElement(backUV[i][0], backUV[i][1]);
+
+ // Vertex on the front face.
+ positionStream.addElement(points[i][0], points[i][1], frontZ),
+ normalStream.addElement(0, 0, 1);
+ texCoordStream.addElement(frontUV[i][0], frontUV[i][1]);
+
+ // Vertices for a quad on the wall.
+ positionStream.addElement(points[i][0], points[i][1], backZ),
+ normalStream.addElement(normals[i][0], normals[i][1], normals[i][2]);
+ texCoordStream.addElement(0, 1);
+
+ positionStream.addElement(points[j][0], points[j][1], backZ),
+ normalStream.addElement(normals[i][0], normals[i][1], normals[i][2]);
+ texCoordStream.addElement(0, 0);
+
+ positionStream.addElement(points[j][0], points[j][1], frontZ),
+ normalStream.addElement(normals[i][0], normals[i][1], normals[i][2]);
+ texCoordStream.addElement(1, 0);
+
+ positionStream.addElement(points[i][0], points[i][1], frontZ),
+ normalStream.addElement(normals[i][0], normals[i][1], normals[i][2]);
+ texCoordStream.addElement(1, 1);
+
+ if (i > 0 && i < n - 1) {
+ // Triangle for the back face.
+ vertexInfo.addTriangle(0, 6 * (i + 1), 6 * i);
+
+ // Triangle for the front face.
+ vertexInfo.addTriangle(1, 6 * i + 1, 6 * (i + 1) + 1);
+ }
+
+ // Quad on the wall.
+ vertexInfo.addTriangle(6 * i + 2, 6 * i + 3, 6 * i + 4);
+ vertexInfo.addTriangle(6 * i + 2, 6 * i + 4, 6 * i + 5);
+ }
+
+ if (opt_matrix) {
+ vertexInfo.reorient(opt_matrix);
+ }
+ return vertexInfo;
+};
+
+/**
+ * Creates a prism shape by extruding a polygon. The prism will be created
+ * around the 2d points passed in an array and extruded along the z-axis.
+ * The end caps of the prism are constructed using a triangle fan originating
+ * at the first point, so a non-convex polygon might not get the desired
+ * shape, but it will if it is convex with respect to the first point.
+ * Texture coordinates map each face of the wall exactly to the unit square.
+ * Texture coordinates on the front and back faces are scaled such that the
+ * bounding rectangle of the polygon is mapped to the unit square.
+ * The created prism has position, normal and uv streams.
+ *
+ * @param {!o3d.Pack} pack Pack to create wedge elements in.
+ * @param {!o3d.Material} material to use.
+ * @param {!Array.<!Array.<number>>} points Array of 2d points in the format:
+ * [[x1, y1], [x2, y2], [x3, y3],...] that describe a 2d polygon.
+ * @param {number} depth The depth to extrude the triangle.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3d.Shape} The created wedge.
+ */
+o3djs.primitives.createPrism = function(pack,
+ material,
+ points,
+ depth,
+ opt_matrix) {
+ var vertexInfo = o3djs.primitives.createPrismVertices(points,
+ depth,
+ opt_matrix);
+ return vertexInfo.createShape(pack, material);
+};
+
+/**
+ * Creates XZ plane vertices.
+ * The created plane has position, normal and uv streams.
+ *
+ * @param {number} width Width of the plane.
+ * @param {number} depth Depth of the plane.
+ * @param {number} subdivisionsWidth Number of steps across the plane.
+ * @param {number} subdivisionsDepth Number of steps down the plane.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3djs.primitives.VertexInfo} The created plane vertices.
+ */
+o3djs.primitives.createPlaneVertices = function(width,
+ depth,
+ subdivisionsWidth,
+ subdivisionsDepth,
+ opt_matrix) {
+ if (subdivisionsWidth <= 0 || subdivisionsDepth <= 0) {
+ throw RangeError('subdivisionWidth and subdivisionDepth must be > 0');
+ }
+
+ var vertexInfo = o3djs.primitives.createVertexInfo();
+ var positionStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.POSITION);
+ var normalStream = vertexInfo.addStream(
+ 3, o3djs.base.o3d.Stream.NORMAL);
+ var texCoordStream = vertexInfo.addStream(
+ 2, o3djs.base.o3d.Stream.TEXCOORD, 0);
+
+ // Generate the individual vertices in our vertex buffer.
+ for (var z = 0; z <= subdivisionsDepth; z++) {
+ for (var x = 0; x <= subdivisionsWidth; x++) {
+ var u = x / subdivisionsWidth;
+ var v = z / subdivisionsDepth;
+ positionStream.addElement(width * u - width * 0.5,
+ 0,
+ depth * v - depth * 0.5);
+ normalStream.addElement(0, 1, 0);
+ texCoordStream.addElement(u, 1 - v);
+ }
+ }
+
+ var numVertsAcross = subdivisionsWidth + 1;
+
+ for (var z = 0; z < subdivisionsDepth; z++) {
+ for (var x = 0; x < subdivisionsWidth; x++) {
+ // triangle 1 of quad
+ vertexInfo.addTriangle(
+ (z + 0) * numVertsAcross + x,
+ (z + 1) * numVertsAcross + x,
+ (z + 0) * numVertsAcross + x + 1);
+
+ // triangle 2 of quad
+ vertexInfo.addTriangle(
+ (z + 1) * numVertsAcross + x,
+ (z + 1) * numVertsAcross + x + 1,
+ (z + 0) * numVertsAcross + x + 1);
+ }
+ }
+
+ if (opt_matrix) {
+ vertexInfo.reorient(opt_matrix);
+ }
+ return vertexInfo;
+};
+
+/**
+ * Creates an XZ plane.
+ * The created plane has position, normal and uv streams.
+ *
+ * @param {!o3d.Pack} pack Pack to create plane elements in.
+ * @param {!o3d.Material} material to use.
+ * @param {number} width Width of the plane.
+ * @param {number} depth Depth of the plane.
+ * @param {number} subdivisionsWidth Number of steps across the plane.
+ * @param {number} subdivisionsDepth Number of steps down the plane.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3d.Shape} The created plane.
+ *
+ * @see o3d.Pack
+ * @see o3d.Shape
+ */
+o3djs.primitives.createPlane = function(pack,
+ material,
+ width,
+ depth,
+ subdivisionsWidth,
+ subdivisionsDepth,
+ opt_matrix) {
+ var vertexInfo = o3djs.primitives.createPlaneVertices(
+ width,
+ depth,
+ subdivisionsWidth,
+ subdivisionsDepth,
+ opt_matrix);
+
+ return vertexInfo.createShape(pack, material);
+};
+
+/**
+ * Creates an XZ fade plane, where the alpha channel of the color stream
+ * fades from 1 to 0.
+ * The created plane has position, normal, uv and vertex color streams.
+ *
+ * @param {!o3d.Pack} pack Pack to create plane elements in.
+ * @param {!o3d.Material} material to use.
+ * @param {number} width Width of the plane.
+ * @param {number} depth Depth of the plane.
+ * @param {number} subdivisionsWidth Number of steps across the plane.
+ * @param {number} subdivisionsDepth Number of steps down the plane.
+ * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply
+ * all the vertices.
+ * @return {!o3d.Shape} The created plane.
+ *
+ * @see o3d.Pack
+ * @see o3d.Shape
+ */
+o3djs.primitives.createFadePlane = function(pack,
+ material,
+ width,
+ depth,
+ subdivisionsWidth,
+ subdivisionsDepth,
+ opt_matrix) {
+ var vertexInfo = o3djs.primitives.createPlaneVertices(
+ width,
+ depth,
+ subdivisionsWidth,
+ subdivisionsDepth,
+ opt_matrix);
+ var colorStream = vertexInfo.addStream(4, o3djs.base.o3d.Stream.COLOR);
+ for (var z = 0; z <= subdivisionsDepth; z++) {
+ var alpha = z / subdivisionsDepth;
+ for (var x = 0; x <= subdivisionsWidth; x++) {
+ colorStream.addElement(1, 1, 1, alpha);
+ }
+ }
+ return vertexInfo.createShape(pack, material);
+};
+
diff --git a/o3d/samples/o3djs/quaternions.js b/o3d/samples/o3djs/quaternions.js
new file mode 100644
index 0000000..e1263cb
--- /dev/null
+++ b/o3d/samples/o3djs/quaternions.js
@@ -0,0 +1,479 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @fileoverview This file contains various functions for quaternion arithmetic
+ * and converting between rotation matrices and quaternions. It adds them to
+ * the "quaternions" module on the o3djs object. Javascript arrays with
+ * four entries are used to represent quaternions, and functions are provided
+ * for doing operations on those.
+ *
+ * Operations are done assuming quaternions are of the form:
+ * q[0] + q[1]i + q[2]j + q[3]k and using the hamiltonian rules for
+ * multiplication as described on Brougham Bridge:
+ * i^2 = j^2 = k^2 = ijk = -1.
+ *
+ */
+
+o3djs.provide('o3djs.quaternions');
+
+/**
+ * A Module for quaternion math.
+ * @namespace
+ */
+o3djs.quaternions = o3djs.quaternions || {};
+
+/**
+ * A Quaternion.
+ * @type {!Array.<number>}
+ */
+o3djs.quaternions.Quaterion = goog.typedef;
+
+/**
+ * Quickly determines if the object a is a scalar or a quaternion;
+ * assumes that the argument is either a number (scalar), or an array of
+ * numbers.
+ * @param {(number|!o3djs.quaternions.Quaterion)} a A number or array the type
+ * of which is in question.
+ * @return {string} Either the string 'Scalar' or 'Quaternion'.
+ */
+o3djs.quaternions.mathType = function(a) {
+ if (typeof(a) === 'number')
+ return 'Scalar';
+ return 'Quaternion';
+};
+
+/**
+ * Copies a quaternion.
+ * @param {!o3djs.quaternions.Quaterion} q The quaternion.
+ * @return {!o3djs.quaternions.Quaterion} A new quaternion identical to q.
+ */
+o3djs.quaternions.copy = function(q) {
+ return q.slice();
+};
+
+/**
+ * Negates a quaternion.
+ * @param {!o3djs.quaternions.Quaterion} q The quaternion.
+ * @return {!o3djs.quaternions.Quaterion} -q.
+ */
+o3djs.quaternions.negative = function(q) {
+ return [-q[0], -q[1], -q[2], -q[3]];
+};
+
+/**
+ * Adds two Quaternions.
+ * @param {!o3djs.quaternions.Quaterion} a Operand Quaternion.
+ * @param {!o3djs.quaternions.Quaterion} b Operand Quaternion.
+ * @return {!o3djs.quaternions.Quaterion} The sum of a and b.
+ */
+o3djs.quaternions.addQuaternionQuaternion = function(a, b) {
+ return [a[0] + b[0],
+ a[1] + b[1],
+ a[2] + b[2],
+ a[3] + b[3]];
+};
+
+/**
+ * Adds a quaternion to a scalar.
+ * @param {!o3djs.quaternions.Quaterion} a Operand Quaternion.
+ * @param {number} b Operand Scalar.
+ * @return {!o3djs.quaternions.Quaterion} The sum of a and b.
+ */
+o3djs.quaternions.addQuaternionScalar = function(a, b) {
+ return a.slice(0, 3).concat(a[3] + b);
+};
+
+/**
+ * Adds a scalar to a quaternion.
+ * @param {number} a Operand scalar.
+ * @param {!o3djs.quaternions.Quaterion} b Operand quaternion.
+ * @return {!o3djs.quaternions.Quaterion} The sum of a and b.
+ */
+o3djs.quaternions.addScalarQuaternion = function(a, b) {
+ return b.slice(0, 3).concat(a + b[3]);
+};
+
+/**
+ * Subtracts two quaternions.
+ * @param {!o3djs.quaternions.Quaterion} a Operand quaternion.
+ * @param {!o3djs.quaternions.Quaterion} b Operand quaternion.
+ * @return {!o3djs.quaternions.Quaterion} The difference a - b.
+ */
+o3djs.quaternions.subQuaternionQuaternion = function(a, b) {
+ return [a[0] - b[0],
+ a[1] - b[1],
+ a[2] - b[2],
+ a[3] - b[3]];
+};
+
+/**
+ * Subtracts a scalar from a quaternion.
+ * @param {!o3djs.quaternions.Quaterion} a Operand quaternion.
+ * @param {number} b Operand scalar.
+ * @return {!o3djs.quaternions.Quaterion} The difference a - b.
+ */
+o3djs.quaternions.subQuaternionScalar = function(a, b) {
+ return a.slice(0, 3).concat(a[3] - b);
+};
+
+/**
+ * Subtracts a quaternion from a scalar.
+ * @param {number} a Operand scalar.
+ * @param {!o3djs.quaternions.Quaterion} b Operand quaternion.
+ * @return {!o3djs.quaternions.Quaterion} The difference a - b.
+ */
+o3djs.quaternions.subScalarQuaternion = function(a, b) {
+ return [-b[0], -b[1], -b[2], a - b[3]];
+};
+
+/**
+ * Multiplies a scalar by a quaternion.
+ * @param {Array} k The scalar.
+ * @param {Array} q The quaternion.
+ * @return {Array} The product of k and q.
+ */
+o3djs.quaternions.mulScalarQuaternion = function(k, q) {
+ return [k * q[0], k * q[1], k * q[2], k * q[3]];
+};
+
+/**
+ * Multiplies a quaternion by a scalar.
+ * @param {!o3djs.quaternions.Quaterion} q The Quaternion.
+ * @param {number} k The scalar.
+ * @return {!o3djs.quaternions.Quaterion} The product of k and v.
+ */
+o3djs.quaternions.mulQuaternionScalar = function(q, k) {
+ return [k * q[0], k * q[1], k * q[2], k * q[3]];
+};
+
+/**
+ * Multiplies two quaternions.
+ * @param {!o3djs.quaternions.Quaterion} a Operand quaternion.
+ * @param {!o3djs.quaternions.Quaterion} b Operand quaternion.
+ * @return {!o3djs.quaternions.Quaterion} The quaternion product a * b.
+ */
+o3djs.quaternions.mulQuaternionQuaternion = function(a, b) {
+ var aX = a[0];
+ var aY = a[1];
+ var aZ = a[2];
+ var aW = a[3];
+ var bX = b[0];
+ var bY = b[1];
+ var bZ = b[2];
+ var bW = b[3];
+
+ return [
+ aW * bX + aX * bW + aY * bZ - aZ * bY,
+ aW * bY + aY * bW + aZ * bX - aX * bZ,
+ aW * bZ + aZ * bW + aX * bY - aY * bX,
+ aW * bW - aX * bX - aY * bY - aZ * bZ];
+};
+
+/**
+ * Divides two quaternions; assumes the convention that a/b = a*(1/b).
+ * @param {!o3djs.quaternions.Quaterion} a Operand quaternion.
+ * @param {!o3djs.quaternions.Quaterion} b Operand quaternion.
+ * @return {!o3djs.quaternions.Quaterion} The quaternion quotient a / b.
+ */
+o3djs.quaternions.divQuaternionQuaternion = function(a, b) {
+ var aX = a[0];
+ var aY = a[1];
+ var aZ = a[2];
+ var aW = a[3];
+ var bX = b[0];
+ var bY = b[1];
+ var bZ = b[2];
+ var bW = b[3];
+
+ var d = 1 / (bW * bW + bX * bX + bY * bY + bZ * bZ);
+ return [
+ (aX * bW - aW * bX - aY * bZ + aZ * bY) * d,
+ (aX * bZ - aW * bY + aY * bW - aZ * bX) * d,
+ (aY * bX + aZ * bW - aW * bZ - aX * bY) * d,
+ (aW * bW + aX * bX + aY * bY + aZ * bZ) * d];
+};
+
+/**
+ * Divides a Quaternion by a scalar.
+ * @param {!o3djs.quaternions.Quaterion} q The quaternion.
+ * @param {number} k The scalar.
+ * @return {!o3djs.quaternions.Quaterion} q The quaternion q divided by k.
+ */
+o3djs.quaternions.divQuaternionScalar = function(q, k) {
+ return [q[0] / k, q[1] / k, q[2] / k, q[3] / k];
+};
+
+/**
+ * Divides a scalar by a quaternion.
+ * @param {number} a Operand scalar.
+ * @param {!o3djs.quaternions.Quaterion} b Operand quaternion.
+ * @return {!o3djs.quaternions.Quaterion} The quaternion product.
+ */
+o3djs.quaternions.divScalarQuaternion = function(a, b) {
+ var b0 = b[0];
+ var b1 = b[1];
+ var b2 = b[2];
+ var b3 = b[3];
+
+ var d = 1 / (b0 * b0 + b1 * b1 + b2 * b2 + b3 * b3);
+ return [-a * b0 * d, -a * b1 * d, -a * b2 * d, a * b3 * d];
+};
+
+/**
+ * Computes the multiplicative inverse of a quaternion.
+ * @param {!o3djs.quaternions.Quaterion} q The quaternion.
+ * @return {!o3djs.quaternions.Quaterion} The multiplicative inverse of q.
+ */
+o3djs.quaternions.inverse = function(q) {
+ var q0 = q[0];
+ var q1 = q[1];
+ var q2 = q[2];
+ var q3 = q[3];
+
+ var d = 1 / (q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
+ return [-q0 * d, -q1 * d, -q2 * d, q3 * d];
+};
+
+/**
+ * Multiplies two objects which are either scalars or quaternions.
+ * @param {(!o3djs.quaternions.Quaterion|number)} a Operand.
+ * @param {(!o3djs.quaternions.Quaterion|number)} b Operand.
+ * @return {(!o3djs.quaternions.Quaterion|number)} The product of a and b.
+ */
+o3djs.quaternions.mul = function(a, b) {
+ return o3djs.quaternions['mul' + o3djs.quaternions.mathType(a) +
+ o3djs.quaternions.mathType(b)](a, b);
+};
+
+/**
+ * Divides two objects which are either scalars or quaternions.
+ * @param {(!o3djs.quaternions.Quaterion|number)} a Operand.
+ * @param {(!o3djs.quaternions.Quaterion|number)} b Operand.
+ * @return {(!o3djs.quaternions.Quaterion|number)} The quotient of a and b.
+ */
+o3djs.quaternions.div = function(a, b) {
+ return o3djs.quaternions['div' + o3djs.quaternions.mathType(a) +
+ o3djs.quaternions.mathType(b)](a, b);
+};
+
+/**
+ * Adds two objects which are either scalars or quaternions.
+ * @param {(!o3djs.quaternions.Quaterion|number)} a Operand.
+ * @param {(!o3djs.quaternions.Quaterion|number)} b Operand.
+ * @return {(!o3djs.quaternions.Quaterion|number)} The sum of a and b.
+ */
+o3djs.quaternions.add = function(a, b) {
+ return o3djs.quaternions['add' + o3djs.quaternions.mathType(a) +
+ o3djs.quaternions.mathType(b)](a, b);
+};
+
+/**
+ * Subtracts two objects which are either scalars or quaternions.
+ * @param {(!o3djs.quaternions.Quaterion|number)} a Operand.
+ * @param {(!o3djs.quaternions.Quaterion|number)} b Operand.
+ * @return {(!o3djs.quaternions.Quaterion|number)} The difference of a and b.
+ */
+o3djs.quaternions.sub = function(a, b) {
+ return o3djs.quaternions['sub' + o3djs.quaternions.mathType(a) +
+ o3djs.quaternions.mathType(b)](a, b);
+};
+
+/**
+ * Computes the length of a Quaternion, i.e. the square root of the
+ * sum of the squares of the coefficients.
+ * @param {!o3djs.quaternions.Quaterion} a The Quaternion.
+ * @return {number} The length of a.
+ */
+o3djs.quaternions.length = function(a) {
+ return Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2] + a[3] * a[3]);
+};
+
+/**
+ * Computes the square of the length of a quaternion, i.e. the sum of the
+ * squares of the coefficients.
+ * @param {!o3djs.quaternions.Quaterion} a The quaternion.
+ * @return {number} The square of the length of a.
+ */
+o3djs.quaternions.lengthSquared = function(a) {
+ return a[0] * a[0] + a[1] * a[1] + a[2] * a[2] + a[3] * a[3];
+};
+
+/**
+ * Divides a Quaternion by its length and returns the quotient.
+ * @param {!o3djs.quaternions.Quaterion} a The Quaternion.
+ * @return {!o3djs.quaternions.Quaterion} A unit length quaternion pointing in
+ * the same direction as a.
+ */
+o3djs.quaternions.normalize = function(a) {
+ var d = 1 / Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2] + a[3] * a[3]);
+ return [a[0] * d, a[1] * d, a[2] * d, a[3] * d];
+};
+
+/**
+ * Computes the conjugate of the given quaternion.
+ * @param {!o3djs.quaternions.Quaterion} q The quaternion.
+ * @return {!o3djs.quaternions.Quaterion} The conjugate of q.
+ */
+o3djs.quaternions.conjugate = function(q) {
+ return [-q[0], -q[1], -q[2], q[3]];
+};
+
+
+/**
+ * Creates a quaternion which rotates around the x-axis by the given angle.
+ * @param {number} angle The angle by which to rotate (in radians).
+ * @return {!o3djs.quaternions.Quaterion} The quaternion.
+ */
+o3djs.quaternions.rotationX = function(angle) {
+ return [Math.sin(angle / 2), 0, 0, Math.cos(angle / 2)];
+};
+
+/**
+ * Creates a quaternion which rotates around the y-axis by the given angle.
+ * @param {number} angle The angle by which to rotate (in radians).
+ * @return {!o3djs.quaternions.Quaterion} The quaternion.
+ */
+o3djs.quaternions.rotationY = function(angle) {
+ return [0, Math.sin(angle / 2), 0, Math.cos(angle / 2)];
+};
+
+/**
+ * Creates a quaternion which rotates around the z-axis by the given angle.
+ * @param {number} angle The angle by which to rotate (in radians).
+ * @return {!o3djs.quaternions.Quaterion} The quaternion.
+ */
+o3djs.quaternions.rotationZ = function(angle) {
+ return [0, 0, Math.sin(angle / 2), Math.cos(angle / 2)];
+};
+
+/**
+ * Creates a quaternion which rotates around the given axis by the given
+ * angle.
+ * @param {!o3djs.math.Vector3} axis The axis about which to rotate.
+ * @param {number} angle The angle by which to rotate (in radians).
+ * @return {!o3djs.quaternions.Quaterion} A quaternion which rotates angle
+ * radians around the axis.
+ */
+o3djs.quaternions.axisRotation = function(axis, angle) {
+ var d = 1 / Math.sqrt(axis[0] * axis[0] +
+ axis[1] * axis[1] +
+ axis[2] * axis[2]);
+ var sin = Math.sin(angle / 2);
+ var cos = Math.cos(angle / 2);
+ return [sin * axis[0] * d, sin * axis[1] * d, sin * axis[2] * d, cos];
+};
+
+/**
+ * Computes a 4-by-4 rotation matrix (with trivial translation component)
+ * given a quaternion. We assume the convention that to rotate a vector v by
+ * a quaternion r means to express that vector as a quaternion q by letting
+ * q = [v[0], v[1], v[2], 0] and then obtain the rotated vector by evaluating
+ * the expression (r * q) / r.
+ * @param {!o3djs.quaternions.Quaterion} q The quaternion.
+ * @return {!o3djs.math.Matrix4} A 4-by-4 rotation matrix.
+ */
+o3djs.quaternions.quaternionToRotation = function(q) {
+ var qX = q[0];
+ var qY = q[1];
+ var qZ = q[2];
+ var qW = q[3];
+
+ var qWqW = qW * qW;
+ var qWqX = qW * qX;
+ var qWqY = qW * qY;
+ var qWqZ = qW * qZ;
+ var qXqW = qX * qW;
+ var qXqX = qX * qX;
+ var qXqY = qX * qY;
+ var qXqZ = qX * qZ;
+ var qYqW = qY * qW;
+ var qYqX = qY * qX;
+ var qYqY = qY * qY;
+ var qYqZ = qY * qZ;
+ var qZqW = qZ * qW;
+ var qZqX = qZ * qX;
+ var qZqY = qZ * qY;
+ var qZqZ = qZ * qZ;
+
+ var d = qWqW + qXqX + qYqY + qZqZ;
+
+ return [
+ [(qWqW + qXqX - qYqY - qZqZ) / d,
+ 2 * (qWqZ + qXqY) / d,
+ 2 * (qXqZ - qWqY) / d, 0],
+ [2 * (qXqY - qWqZ) / d,
+ (qWqW - qXqX + qYqY - qZqZ) / d,
+ 2 * (qWqX + qYqZ) / d, 0],
+ [2 * (qWqY + qXqZ) / d,
+ 2 * (qYqZ - qWqX) / d,
+ (qWqW - qXqX - qYqY + qZqZ) / d, 0],
+ [0, 0, 0, 1]];
+};
+
+/**
+ * Computes a quaternion whose rotation is equivalent to the given matrix.
+ * @param {(!o3djs.math.Matrix4|!o3djs.math.Matrix3)} m A 3-by-3 or 4-by-4
+ * rotation matrix.
+ * @return {!o3djs.quaternions.Quaterion} A quaternion q such that
+ * quaternions.quaternionToRotation(q) is m.
+ */
+o3djs.quaternions.rotationToQuaternion = function(m) {
+ var u;
+ var v;
+ var w;
+
+ // Choose u, v, and w such that u is the index of the biggest diagonal entry
+ // of m, and u v w is an even permutation of 0 1 and 2.
+ if (m[0][0] > m[1][1] && m[0][0] > m[2][2]) {
+ u = 0;
+ v = 1;
+ w = 2;
+ } else if (m[1][1] > m[0][0] && m[1][1] > m[2][2]) {
+ u = 1;
+ v = 2;
+ w = 0;
+ } else {
+ u = 2;
+ v = 0;
+ w = 1;
+ }
+
+ var r = Math.sqrt(1 + m[u][u] - m[v][v] - m[w][w]);
+ var q = [];
+ q[u] = 0.5 * r;
+ q[v] = 0.5 * (m[v][u] + m[u][v]) / r;
+ q[w] = 0.5 * (m[u][w] + m[w][u]) / r;
+ q[3] = 0.5 * (m[v][w] - m[w][v]) / r;
+
+ return q;
+};
+
diff --git a/o3d/samples/o3djs/rendergraph.js b/o3d/samples/o3djs/rendergraph.js
new file mode 100644
index 0000000..dad66a0
--- /dev/null
+++ b/o3d/samples/o3djs/rendergraph.js
@@ -0,0 +1,420 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains various functions for helping create
+ * render graphs for o3d. It puts them in the "rendergraph" module on
+ * the o3djs object.
+ *
+ * Note: This library is only a sample. It is not meant to be some official
+ * library. It is provided only as example code.
+ *
+ */
+
+o3djs.provide('o3djs.rendergraph');
+
+/**
+ * A Module for creating render graphs.
+ * @namespace
+ */
+o3djs.rendergraph = o3djs.rendergraph || {};
+
+/**
+ * Creates a basic render graph setup to draw opaque and transparent
+ * 3d objects.
+ * @param {!o3d.Pack} pack Pack to manage created objects.
+ * @param {!o3d.Transform} treeRoot root Transform of tree to render.
+ * @param {!o3d.RenderNode} opt_parent RenderNode to build this view under.
+ * @param {!o3djs.math.Vector4} opt_clearColor color to clear view.
+ * @param {number} opt_priority Optional base priority for created objects.
+ * @param {!o3djs.math.Vector4} opt_viewport viewport settings for view.
+ * @param {!o3d.DrawList} opt_performanceDrawList Optional DrawList to
+ * use for performanceDrawPass.
+ * @param {!o3d.DrawList} opt_zOrderedDrawList Optional DrawList to
+ * use for zOrderedDrawPass.
+ * @return {!o3djs.rendergraph.ViewInfo} A ViewInfo object with info about
+ * everything created.
+ */
+o3djs.rendergraph.createView = function(pack,
+ treeRoot,
+ opt_parent,
+ opt_clearColor,
+ opt_priority,
+ opt_viewport,
+ opt_performanceDrawList,
+ opt_zOrderedDrawList) {
+ return new o3djs.rendergraph.ViewInfo(pack,
+ treeRoot,
+ opt_parent,
+ opt_clearColor,
+ opt_priority,
+ opt_viewport,
+ opt_performanceDrawList,
+ opt_zOrderedDrawList);
+};
+
+/**
+ * Creates a basic render graph setup to draw opaque and transparent
+ * 3d objects.
+ * @param {!o3d.Pack} pack Pack to manage created objects.
+ * @param {!o3d.Transform} treeRoot root Transform of tree to render.
+ * @param {!o3d.RenderNode} opt_parent RenderNode to build this view under.
+ * @param {!o3djs.math.Vector4} opt_clearColor color to clear view.
+ * @param {number} opt_priority Optional base priority for created objects.
+ * @param {!o3djs.math.Vector4} opt_viewport viewport settings for view.
+ * @return {!o3djs.rendergraph.ViewInfo} A ViewInfo object with info about
+ * everything created.
+ */
+o3djs.rendergraph.createBasicView = function(pack,
+ treeRoot,
+ opt_parent,
+ opt_clearColor,
+ opt_priority,
+ opt_viewport) {
+ return o3djs.rendergraph.createView(pack,
+ treeRoot,
+ opt_parent,
+ opt_clearColor,
+ opt_priority,
+ opt_viewport);
+};
+
+/**
+ * Creates an extra view render graph setup to draw opaque and transparent
+ * 3d objects based on a previously created view. It uses the previous view
+ * to share draw lists and to set the priority.
+ * @param {!o3djs.rendergraph.ViewInfo} viewInfo ViewInfo returned from
+ * createBasicView.
+ * @param {!o3djs.math.Vector4} opt_viewport viewport settings for view.
+ * @param {!o3djs.math.Vector4} opt_clearColor color to clear view.
+ * @param {number} opt_priority base priority for created objects.
+ * @return {!o3djs.rendergraph.ViewInfo} A ViewInfo object with info about
+ * everything created.
+ */
+o3djs.rendergraph.createExtraView = function(viewInfo,
+ opt_viewport,
+ opt_clearColor,
+ opt_priority) {
+ return o3djs.rendergraph.createView(viewInfo.pack,
+ viewInfo.treeRoot,
+ viewInfo.renderGraphRoot,
+ opt_clearColor,
+ opt_priority,
+ opt_viewport,
+ viewInfo.performanceDrawList,
+ viewInfo.zOrderedDrawList);
+};
+
+/**
+ * A ViewInfo object creates the standard o3d objects needed for
+ * a single 3d view. Those include a ClearBuffer followed by a TreeTraveral
+ * followed by 2 DrawPasses all of which are children of a Viewport. On top of
+ * those a DrawContext and optionally 2 DrawLists although you can pass in your
+ * own DrawLists if there is a reason to reuse the same DrawLists such was with
+ * mulitple views of the same scene.
+ *
+ * The render graph created is something like:
+ * <pre>
+ * [Viewport]
+ * |
+ * +------+--------+------------------+---------------------+
+ * | | | |
+ * [ClearBuffer] [TreeTraversal] [Performance StateSet] [ZOrdered StateSet]
+ * | |
+ * [Performance DrawPass] [ZOrdered DrawPass]
+ * </pre>
+ *
+ * @constructor
+ * @param {!o3d.Pack} pack Pack to manage created objects.
+ * @param {!o3d.Transform} treeRoot root Transform of tree to render.
+ * @param {!o3d.RenderNode} opt_parent RenderNode to build this view under.
+ * @param {!o3djs.math.Vector4} opt_clearColor color to clear view.
+ * @param {number} opt_priority Optional base priority for created objects.
+ * @param {!o3djs.math.Vector4} opt_viewport viewport settings for view.
+ * @param {!o3d.DrawList} opt_performanceDrawList DrawList to use for
+ * performanceDrawPass.
+ * @param {!o3d.DrawList} opt_zOrderedDrawList DrawList to use for
+ * zOrderedDrawPass.
+ */
+o3djs.rendergraph.ViewInfo = function(pack,
+ treeRoot,
+ opt_parent,
+ opt_clearColor,
+ opt_priority,
+ opt_viewport,
+ opt_performanceDrawList,
+ opt_zOrderedDrawList) {
+ var clearColor = opt_clearColor || [0.5, 0.5, 0.5, 1.0];
+
+ var viewPriority = opt_priority || 0;
+ var priority = 0;
+
+ // Create Viewport.
+ var viewport = pack.createObject('Viewport');
+ if (opt_viewport) {
+ viewport.viewport = opt_viewport;
+ }
+ viewport.priority = viewPriority;
+
+ // Create a clear buffer.
+ var clearBuffer = pack.createObject('ClearBuffer');
+ clearBuffer.clearColor = clearColor;
+ clearBuffer.priority = priority++;
+ clearBuffer.parent = viewport;
+
+ // Create DrawLists
+ var performanceDrawList;
+ if (opt_performanceDrawList) {
+ performanceDrawList = opt_performanceDrawList;
+ this.ownPerformanceDrawList_ = false;
+ } else {
+ performanceDrawList = pack.createObject('DrawList');
+ this.ownPerformanceDrawList_ = true;
+ }
+ var zOrderedDrawList;
+ if (opt_zOrderedDrawList) {
+ zOrderedDrawList = opt_zOrderedDrawList;
+ this.ownZOrderedDrawList_ = false;
+ } else {
+ zOrderedDrawList = pack.createObject('DrawList');
+ this.ownZOrderedDrawList_ = true;
+ }
+
+ // Create DrawContext.
+ var drawContext = pack.createObject('DrawContext');
+
+ // Creates a TreeTraversal and parents it to the root.
+ var treeTraversal = pack.createObject('TreeTraversal');
+ treeTraversal.priority = priority++;
+ treeTraversal.parent = viewport;
+
+ // Creates an empty stateSet to parent the performanceDrawPass to
+ // just to make it easier to do certain effects.
+ var performanceStateSet = pack.createObject('StateSet');
+ var performanceState = pack.createObject('State');
+ performanceStateSet.state = performanceState;
+ performanceStateSet.priority = priority++;
+ performanceStateSet.parent = viewport;
+ performanceState.getStateParam('ColorWriteEnable').value = 7;
+
+ // Creates a DrawPass for performance shapes.
+ var performanceDrawPass = pack.createObject('DrawPass');
+ performanceDrawPass.drawList = performanceDrawList;
+ performanceDrawPass.parent = performanceStateSet;
+
+ // Creates a StateSet so everything on the zOrderedDrawPass is assumed
+ // to need alpha blending with the typical settings.
+ var zOrderedStateSet = pack.createObject('StateSet');
+ var zOrderedState = pack.createObject('State');
+ zOrderedState.getStateParam('AlphaBlendEnable').value = true;
+ zOrderedState.getStateParam('SourceBlendFunction').value =
+ o3djs.base.o3d.State.BLENDFUNC_SOURCE_ALPHA;
+ zOrderedState.getStateParam('DestinationBlendFunction').value =
+ o3djs.base.o3d.State.BLENDFUNC_INVERSE_SOURCE_ALPHA;
+ zOrderedState.getStateParam('AlphaTestEnable').value = true;
+ zOrderedState.getStateParam('AlphaComparisonFunction').value =
+ o3djs.base.o3d.State.CMP_GREATER;
+ zOrderedState.getStateParam('ColorWriteEnable').value = 7;
+
+ zOrderedStateSet.state = zOrderedState;
+ zOrderedStateSet.priority = priority++;
+ zOrderedStateSet.parent = viewport;
+
+ // Creates a DrawPass for zOrdered shapes.
+ var zOrderedDrawPass = pack.createObject('DrawPass');
+ zOrderedDrawPass.drawList = zOrderedDrawList;
+ zOrderedDrawPass.sortMethod = o3djs.base.o3d.DrawList.BY_Z_ORDER;
+ zOrderedDrawPass.parent = zOrderedStateSet;
+
+ // Register the passlists and drawcontext with the TreeTraversal
+ treeTraversal.registerDrawList(performanceDrawList, drawContext, true);
+ treeTraversal.registerDrawList(zOrderedDrawList, drawContext, true);
+ treeTraversal.transform = treeRoot;
+
+ /**
+ * Pack that manages the objects created for this ViewInfo.
+ * @type {!o3d.Pack}
+ */
+ this.pack = pack;
+
+ /**
+ * The RenderNode this ViewInfo render graph subtree is parented under.
+ * @type {(!o3d.RenderNode|undefined)}
+ */
+ this.renderGraphRoot = opt_parent;
+
+ /**
+ * The root node of the transform graph this ViewInfo renders.
+ * @type {!o3d.Transform}
+ */
+ this.treeRoot = treeRoot;
+
+ /**
+ * The root of the subtree of the render graph this ViewInfo is managing.
+ * If you want to set the priority of a ViewInfo's rendergraph subtree use
+ * <pre>
+ * viewInfo.root.priority = desiredPriority;
+ * </pre>
+ * @type {!o3d.RenderGraph}
+ */
+ this.root = viewport;
+
+ /**
+ * The Viewport RenderNode created for this ViewInfo.
+ * @type {!o3d.Viewport}
+ */
+ this.viewport = viewport;
+
+ /**
+ * The ClearBuffer RenderNode created for this ViewInfo.
+ * @type {!o3d.ClearBuffer}
+ */
+ this.clearBuffer = clearBuffer;
+
+ /**
+ * The StateSet RenderNode above the performance DrawPass in this ViewInfo
+ * @type {!o3d.StateSet}
+ */
+ this.performanceStateSet = performanceStateSet;
+
+ /**
+ * The State object used by the performanceStateSet object in this ViewInfo.
+ * By default, no states are set here.
+ * @type {!o3d.State}
+ */
+ this.performanceState = performanceState;
+
+ /**
+ * The DrawList used for the performance draw pass. Generally for opaque
+ * materials.
+ * @type {!o3d.DrawList}
+ */
+ this.performanceDrawList = performanceDrawList;
+
+ /**
+ * The StateSet RenderNode above the ZOrdered DrawPass in this ViewInfo
+ * @type {!o3d.StateSet}
+ */
+ this.zOrderedStateSet = zOrderedStateSet;
+
+ /**
+ * The State object used by the zOrderedStateSet object in this ViewInfo.
+ * By default AlphaBlendEnable is set to true, SourceBlendFucntion is set to
+ * State.BLENDFUNC_SOURCE_ALPHA and DestinationBlendFunction is set to
+ * State.BLENDFUNC_INVERSE_SOURCE_ALPHA
+ * @type {!o3d.State}
+ */
+ this.zOrderedState = zOrderedState;
+
+ /**
+ * The DrawList used for the zOrdered draw pass. Generally for transparent
+ * materials.
+ * @type {!o3d.DrawList}
+ */
+ this.zOrderedDrawList = zOrderedDrawList;
+
+ /**
+ * The DrawContext used by this ViewInfo.
+ * @type {!o3d.DrawContext}
+ */
+ this.drawContext = drawContext;
+
+ /**
+ * The TreeTraversal used by this ViewInfo.
+ * @type {!o3d.TreeTraversal}
+ */
+ this.treeTraversal = treeTraversal;
+
+ /**
+ * The DrawPass used with the performance DrawList created by this ViewInfo.
+ * @type {!o3d.DrawPass}
+ */
+ this.performanceDrawPass = performanceDrawPass;
+
+ /**
+ * The DrawPass used with the zOrdered DrawList created by this ViewInfo.
+ * @type {!o3d.DrawPass}
+ */
+ this.zOrderedDrawPass = zOrderedDrawPass;
+
+ /**
+ * The highest priority used for objects under the Viewport RenderNode created
+ * by this ViewInfo.
+ * @type {number}
+ */
+ this.priority = priority;
+
+ // Parent whatever the root is to the parent passed in.
+ if (opt_parent) {
+ this.root.parent = opt_parent;
+ }
+};
+
+/**
+ * Destroys the various objects created for the view.
+ *
+ * @param {Boolean} opt_destroyDrawContext True if you want view's DrawContext
+ * destroyed. Default = true.
+ * @param {Boolean} opt_destroyDrawList True if you want view's DrawLists
+ * destroyed. Default = true.
+ */
+o3djs.rendergraph.ViewInfo.prototype.destroy = function(
+ opt_destroyDrawContext,
+ opt_destroyDrawList) {
+ if (opt_destroyDrawContext === undefined) {
+ opt_destroyDrawContext = true;
+ }
+ if (opt_destroyDrawList === undefined) {
+ opt_destroyDrawList = true;
+ }
+ // Remove everything we created from the pack.
+ this.pack.removeObject(this.viewport);
+ this.pack.removeObject(this.clearBuffer);
+ if (this.ownPerformanceDrawList_ && opt_destroyDrawList) {
+ this.pack.removeObject(this.performanceDrawList);
+ }
+ if (this.ownZOrderedDrawList_ && opt_destroyDrawList) {
+ this.pack.removeObject(this.zOrderedDrawList);
+ }
+ if (opt_destroyDrawContext) {
+ this.pack.removeObject(this.drawContext);
+ }
+ this.pack.removeObject(this.treeTraversal);
+ this.pack.removeObject(this.performanceDrawPass);
+ this.pack.removeObject(this.zOrderedDrawPass);
+ // Remove our substree from its parent.
+ this.viewport.parent = null;
+
+ // At this point, IF nothing else is referencing any of these objects
+ // they should get removed.
+};
+
diff --git a/o3d/samples/o3djs/scene.js b/o3d/samples/o3djs/scene.js
new file mode 100644
index 0000000..c17b517
--- /dev/null
+++ b/o3d/samples/o3djs/scene.js
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains various functions and classes for dealing
+ * with 3d scenes.
+ */
+
+o3djs.provide('o3djs.scene');
+
+o3djs.require('o3djs.io');
+o3djs.require('o3djs.serialization');
+
+/**
+ * A Module with various scene functions and classes.
+ * @namespace
+ */
+o3djs.scene = o3djs.scene || {};
+
+/**
+ * Loads a scene.
+ * @param {!o3d.Client} client An O3D client object.
+ * @param {!o3d.Pack} pack Pack to load scene into.
+ * @param {!o3d.Transform} parent Transform to parent scene under.
+ * @param {string} url URL of scene to load.
+ * @param {!function(!o3d.Pack, !o3d.Transform, *): void} callback
+ * Callback when scene is loaded. It will be passed the pack, the parent and
+ * an exception which is null on success.
+ * @param {!o3djs.serialization.Options} opt_options Options passed into the
+ * loader.
+ * @return {!o3djs.io.LoadInfo} A LoadInfo for tracking progress.
+ * @see o3djs.loader.createLoader
+ */
+o3djs.scene.loadScene = function(client,
+ pack,
+ parent,
+ url,
+ callback,
+ opt_options) {
+ // Starts the deserializer once the entire archive is available.
+ function onFinished(archiveInfo, exception) {
+ if (!exception) {
+ var finishCallback = function(pack, parent, exception) {
+ archiveInfo.destroy();
+ callback(pack, parent, exception);
+ };
+ o3djs.serialization.deserializeArchive(archiveInfo,
+ 'scene.json',
+ client,
+ pack,
+ parent,
+ finishCallback,
+ opt_options);
+ } else {
+ archiveInfo.destroy();
+ callback(pack, parent, exception);
+ }
+ }
+ return o3djs.io.loadArchive(pack, url, onFinished);
+};
+
+
diff --git a/o3d/samples/o3djs/serialization.js b/o3d/samples/o3djs/serialization.js
new file mode 100644
index 0000000..a8b4ea5
--- /dev/null
+++ b/o3d/samples/o3djs/serialization.js
@@ -0,0 +1,744 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file provides support for deserializing (loading)
+ * transform graphs from JSON files.
+ *
+ */
+
+o3djs.provide('o3djs.serialization');
+
+o3djs.require('o3djs.error');
+
+/**
+ * A Module for handling events related to o3d and various browsers.
+ * @namespace
+ */
+o3djs.serialization = o3djs.serialization || {};
+
+/**
+ * The oldest supported version of the serializer. It isn't necessary to
+ * increment this version whenever the format changes. Only change it when the
+ * deserializer becomes incapable of deserializing an older version.
+ * @type {number}
+ */
+o3djs.serialization.supportedVersion = 5;
+
+/**
+ * Options for deserialization.
+ *
+ * opt_animSource is an optional ParamFloat that will be bound as the source
+ * param for all animation time params in the scene. opt_async is a bool that
+ * will make the deserialization process async.
+ *
+ * @type {{opt_animSource: !o3d.ParamFloat, opt_async: boolean}}
+ */
+o3djs.serialization.Options = goog.typedef;
+
+/**
+ * A Deserializer incrementally deserializes a transform graph.
+ * @constructor
+ * @param {!o3d.Pack} pack The pack to deserialize into.
+ * @param {!Object} json An object tree conforming to the JSON rules.
+ */
+o3djs.serialization.Deserializer = function(pack, json) {
+ /**
+ * The pack to deserialize into.
+ * @type {!o3d.Pack}
+ */
+ this.pack = pack;
+
+ /**
+ * An object tree conforming to the JSON rules.
+ * @type {!Object}
+ */
+ this.json = json;
+
+ /**
+ * The archive from which assets referenced from JSON are retreived.
+ * @type {o3djs.io.ArchiveInfo}
+ */
+ this.archiveInfo = null;
+
+ /**
+ * A map from classname to a function that will create
+ * instances of objects. Add entries to support additional classes.
+ * @type {!Object}
+ */
+ this.createCallbacks = {
+ 'o3d.VertexBuffer': function(deserializer, json) {
+ var object = deserializer.pack.createObject('o3d.VertexBuffer');
+ if ('custom' in json) {
+ var rawData = deserializer.archiveInfo.getFileByURI(
+ 'vertex-buffers.bin');
+ object.set(rawData,
+ json.custom.binaryRange[0],
+ json.custom.binaryRange[1] - json.custom.binaryRange[0]);
+ for (var i = 0; i < json.custom.fields.length; ++i) {
+ deserializer.addObject(json.custom.fields[i], object.fields[i]);
+ }
+ }
+ return object;
+ },
+
+ 'o3d.SourceBuffer': function(deserializer, json) {
+ var object = deserializer.pack.createObject('o3d.SourceBuffer');
+ if ('custom' in json) {
+ var rawData = deserializer.archiveInfo.getFileByURI(
+ 'vertex-buffers.bin');
+ object.set(rawData,
+ json.custom.binaryRange[0],
+ json.custom.binaryRange[1] - json.custom.binaryRange[0]);
+ for (var i = 0; i < json.custom.fields.length; ++i) {
+ deserializer.addObject(json.custom.fields[i], object.fields[i]);
+ }
+ }
+ return object;
+ },
+
+ 'o3d.IndexBuffer': function(deserializer, json) {
+ var object = deserializer.pack.createObject('o3d.IndexBuffer');
+ if ('custom' in json) {
+ var rawData = deserializer.archiveInfo.getFileByURI(
+ 'index-buffers.bin');
+ object.set(rawData,
+ json.custom.binaryRange[0],
+ json.custom.binaryRange[1] - json.custom.binaryRange[0]);
+ for (var i = 0; i < json.custom.fields.length; ++i) {
+ deserializer.addObject(json.custom.fields[i], object.fields[i]);
+ }
+ }
+ return object;
+ },
+
+ 'o3d.Texture2D': function(deserializer, json) {
+ if ('o3d.uri' in json.params) {
+ var uri = json.params['o3d.uri'].value;
+ var rawData = deserializer.archiveInfo.getFileByURI(uri);
+ if (!rawData) {
+ throw 'Could not find texture ' + uri + ' in the archive';
+ }
+ return deserializer.pack.createTextureFromRawData(
+ rawData,
+ true);
+ } else {
+ return deserializer.pack.createTexture2D(
+ json.custom.width,
+ json.custom.height,
+ json.custom.format,
+ json.custom.levels,
+ json.custom.renderSurfacesEnabled);
+ }
+ },
+
+ 'o3d.TextureCUBE': function(deserializer, json) {
+ if ('o3d.uri' in json.params) {
+ var uri = json.params['o3d.uri'].value;
+ var rawData = deserializer.archiveInfo.getFileByURI(uri);
+ if (!rawData) {
+ throw 'Could not find texture ' + uri + ' in the archive';
+ }
+ return deserializer.pack.createTextureFromRawData(
+ rawData,
+ true);
+ } else {
+ return deserializer.pack.createTextureCUBE(
+ json.custom.edgeLength,
+ json.custom.format,
+ json.custom.levels,
+ json.custom.renderSurfacesEnabled);
+ }
+ }
+ };
+
+ /**
+ * A map from classname to a function that will initialize
+ * instances of the given class from JSON data. Add entries to support
+ * additional classes.
+ * @type {!Object}
+ */
+ this.initCallbacks = {
+ 'o3d.Curve': function(deserializer, object, json) {
+ if ('custom' in json) {
+ var rawData = deserializer.archiveInfo.getFileByURI('curve-keys.bin');
+ object.set(rawData,
+ json.custom.binaryRange[0],
+ json.custom.binaryRange[1] - json.custom.binaryRange[0]);
+ }
+ },
+
+ 'o3d.Effect': function(deserializer, object, json) {
+ var uriParam = object.getParam('o3d.uri');
+ if (uriParam) {
+ var rawData = deserializer.archiveInfo.getFileByURI(uriParam.value);
+ if (!rawData) {
+ throw 'Cannot find shader ' + uriParam.value + ' in archive.';
+ }
+ if (!object.loadFromFXString(rawData.stringValue)) {
+ throw 'Cannot load shader ' + uriParam.value + ' in archive.';
+ }
+ }
+ },
+
+ 'o3d.Skin': function(deserializer, object, json) {
+ if ('custom' in json) {
+ var rawData = deserializer.archiveInfo.getFileByURI('skins.bin');
+ object.set(rawData,
+ json.custom.binaryRange[0],
+ json.custom.binaryRange[1] - json.custom.binaryRange[0]);
+ }
+ },
+
+ 'o3d.SkinEval': function(deserializer, object, json) {
+ if ('custom' in json) {
+ for (var i = 0; i < json.custom.vertexStreams.length; ++i) {
+ var streamJson = json.custom.vertexStreams[i];
+ var field = deserializer.getObjectById(streamJson.stream.field);
+ object.setVertexStream(streamJson.stream.semantic,
+ streamJson.stream.semanticIndex,
+ field,
+ streamJson.stream.startIndex);
+ if ('bind' in streamJson) {
+ var source = deserializer.getObjectById(streamJson.bind);
+ object.bindStream(source,
+ streamJson.stream.semantic,
+ streamJson.stream.semanticIndex);
+ }
+ }
+ }
+ },
+
+ 'o3d.StreamBank': function(deserializer, object, json) {
+ if ('custom' in json) {
+ for (var i = 0; i < json.custom.vertexStreams.length; ++i) {
+ var streamJson = json.custom.vertexStreams[i];
+ var field = deserializer.getObjectById(streamJson.stream.field);
+ object.setVertexStream(streamJson.stream.semantic,
+ streamJson.stream.semanticIndex,
+ field,
+ streamJson.stream.startIndex);
+ if ('bind' in streamJson) {
+ var source = deserializer.getObjectById(streamJson.bind);
+ object.bindStream(source,
+ streamJson.stream.semantic,
+ streamJson.stream.semanticIndex);
+ }
+ }
+ }
+ }
+ };
+
+ if (!('version' in json)) {
+ throw 'Version in JSON file was missing.';
+ }
+
+ if (json.version < o3djs.serialization.supportedVersion) {
+ throw 'Version in JSON file was ' + json.version +
+ ' but expected at least version ' +
+ o3djs.serialization.supportedVersion + '.';
+ }
+
+ if (!('objects' in json)) {
+ throw 'Objects array in JSON file was missing.';
+ }
+
+ /**
+ * An array of all objects deserialized so far, indexed by object id. Id zero
+ * means null.
+ * @type {!Array.<(Object|undefined)>}
+ * @private
+ */
+ this.objectsById_ = [null];
+
+ /**
+ * An array of objects deserialized so far, indexed by position in the JSON.
+ * @type {!Array.<Object>}
+ * @private
+ */
+ this.objectsByIndex_ = [];
+
+ /**
+ * Array of all classes present in the JSON.
+ * @type {!Array.<string>}
+ * @private
+ */
+ this.classNames_ = [];
+ for (var className in json.objects) {
+ this.classNames_.push(className);
+ }
+
+ /**
+ * The current phase_ of deserialization. In phase_ 0, objects
+ * are created and their ids registered. In phase_ 1, objects are
+ * initialized from JSON data.
+ * @type {number}
+ * @private
+ */
+ this.phase_ = 0;
+
+ /**
+ * Index of the next class to be deserialized in classNames_.
+ * @type {number}
+ * @private
+ */
+ this.nextClassIndex_ = 0;
+
+ /**
+ * Index of the next object of the current class to be deserialized.
+ * @type {number}
+ * @private
+ */
+ this.nextObjectIndex_ = 0;
+
+ /**
+ * Index of the next object to be deserialized in objectsByIndex_.
+ * @type {number}
+ * @private
+ */
+ this.globalObjectIndex_ = 0;
+};
+
+/**
+ * Get the object with the given id.
+ * @param {number} id The id to lookup.
+ * @return {(Object|undefined)} The object with the given id.
+ */
+o3djs.serialization.Deserializer.prototype.getObjectById = function(id) {
+ return this.objectsById_[id];
+};
+
+/**
+ * When a creation or init callback creates an object that the Deserializer
+ * is not aware of, it can associate it with an id using this function, so that
+ * references to the object can be resolved.
+ * @param {number} id The is of the object.
+ * @param {!Object} object The object to register.
+ */
+o3djs.serialization.Deserializer.prototype.addObject = function(
+ id, object) {
+ this.objectsById_[id] = object;
+};
+
+/**
+ * Deserialize a value. Identifies reference values and converts
+ * their object id into an object reference. Otherwise returns the
+ * value unchanged.
+ * @param {*} valueJson The JSON representation of the value.
+ * @return {*} The JavaScript representation of the value.
+ */
+o3djs.serialization.Deserializer.prototype.deserializeValue = function(
+ valueJson) {
+ if (typeof(valueJson) === 'object') {
+ if (valueJson === null) {
+ return null;
+ }
+
+ var valueAsObject = /** @type {!Object} */ (valueJson);
+ if ('length' in valueAsObject) {
+ for (var i = 0; i != valueAsObject.length; ++i) {
+ valueAsObject[i] = this.deserializeValue(valueAsObject[i]);
+ }
+ return valueAsObject;
+ }
+
+ var refId = valueAsObject['ref'];
+ if (refId !== undefined) {
+ var referenced = this.objectsById_[refId];
+ if (referenced === undefined) {
+ throw 'Could not find object with id ' + refId + '.';
+ }
+ return referenced;
+ }
+ }
+
+ return valueJson;
+};
+
+/**
+ * Sets the value of a param on an object or binds a param to another.
+ * @param {!Object} object The object holding the param.
+ * @param {(string|number)} paramName The name of the param.
+ * @param {!Object} propertyJson The JSON representation of the value.
+ * @private
+ */
+o3djs.serialization.Deserializer.prototype.setParamValue_ = function(
+ object, paramName, propertyJson) {
+ var param = object.getParam(paramName);
+ if (param === null)
+ return;
+
+ var valueJson = propertyJson['value'];
+ if (valueJson !== undefined) {
+ param.value = this.deserializeValue(valueJson);
+ }
+
+ var bindId = propertyJson['bind'];
+ if (bindId !== undefined) {
+ var referenced = this.objectsById_[bindId];
+ if (referenced === undefined) {
+ throw 'Could not find output param with id ' + bindId + '.';
+ }
+ param.bind(referenced);
+ }
+};
+
+/**
+ * Creates a param on an object and adds it's id so that other objects can
+ * reference it.
+ * @param {!Object} object The object to hold the param.
+ * @param {(string|number)} paramName The name of the param.
+ * @param {!Object} propertyJson The JSON representation of the value.
+ * @private
+ */
+o3djs.serialization.Deserializer.prototype.createAndIdentifyParam_ =
+ function(object, paramName, propertyJson) {
+ var propertyClass = propertyJson['class'];
+ var param;
+ if (propertyClass !== undefined) {
+ param = object.createParam(paramName, propertyClass);
+ } else {
+ param = object.getParam(paramName);
+ }
+
+ var paramId = propertyJson['id'];
+ if (paramId !== undefined && param !== null) {
+ this.objectsById_[paramId] = param;
+ }
+};
+
+/**
+ * First pass: create all objects and additional params. We need two
+ * passes to support references to objects that appear later in the
+ * JSON.
+ * @param {number} amountOfWork The number of loop iterations to perform of
+ * this phase_.
+ * @private
+ */
+o3djs.serialization.Deserializer.prototype.createObjectsPhase_ =
+ function(amountOfWork) {
+ for (; this.nextClassIndex_ < this.classNames_.length;
+ ++this.nextClassIndex_) {
+ var className = this.classNames_[this.nextClassIndex_];
+ var classJson = this.json.objects[className];
+ var numObjects = classJson.length;
+ for (; this.nextObjectIndex_ < numObjects; ++this.nextObjectIndex_) {
+ if (amountOfWork-- <= 0)
+ return;
+
+ var objectJson = classJson[this.nextObjectIndex_];
+ var object = undefined;
+ if ('id' in objectJson) {
+ object = this.objectsById_[objectJson.id];
+ }
+ if (object === undefined) {
+ if (className in this.createCallbacks) {
+ object = this.createCallbacks[className](this, objectJson);
+ } else {
+ object = this.pack.createObject(className);
+ }
+ }
+ this.objectsByIndex_[this.globalObjectIndex_++] = object;
+ if ('id' in objectJson) {
+ this.objectsById_[objectJson.id] = object;
+ }
+ if ('params' in objectJson) {
+ if ('length' in objectJson.params) {
+ for (var paramIndex = 0; paramIndex != objectJson.params.length;
+ ++paramIndex) {
+ var paramJson = objectJson.params[paramIndex];
+ this.createAndIdentifyParam_(object, paramIndex,
+ paramJson);
+ }
+ } else {
+ for (var paramName in objectJson.params) {
+ var paramJson = objectJson.params[paramName];
+ this.createAndIdentifyParam_(object, paramName, paramJson);
+ }
+ }
+ }
+ }
+ this.nextObjectIndex_ = 0;
+ }
+
+ if (this.nextClassIndex_ === this.classNames_.length) {
+ this.nextClassIndex_ = 0;
+ this.nextObjectIndex_ = 0;
+ this.globalObjectIndex_ = 0;
+ ++this.phase_;
+ }
+};
+
+/**
+ * Second pass: set property and parameter values and bind parameters.
+ * @param {number} amountOfWork The number of loop iterations to perform of
+ * this phase_.
+ * @private
+ */
+o3djs.serialization.Deserializer.prototype.setPropertiesPhase_ = function(
+ amountOfWork) {
+ for (; this.nextClassIndex_ < this.classNames_.length;
+ ++this.nextClassIndex_) {
+ var className = this.classNames_[this.nextClassIndex_];
+ var classJson = this.json.objects[className];
+ var numObjects = classJson.length;
+ for (; this.nextObjectIndex_ < numObjects; ++this.nextObjectIndex_) {
+ if (amountOfWork-- <= 0)
+ return;
+
+ var objectJson = classJson[this.nextObjectIndex_];
+ var object = this.objectsByIndex_[this.globalObjectIndex_++];
+ if ('properties' in objectJson) {
+ for (var propertyName in objectJson.properties) {
+ if (propertyName in object) {
+ var propertyJson = objectJson.properties[propertyName];
+ var propertyValue = this.deserializeValue(propertyJson);
+ object[propertyName] = propertyValue;
+ }
+ };
+ }
+ if ('params' in objectJson) {
+ if ('length' in objectJson.params) {
+ for (var paramIndex = 0; paramIndex != objectJson.params.length;
+ ++paramIndex) {
+ var paramJson = objectJson.params[paramIndex];
+ this.setParamValue_(/** @type {!Object} */ (object),
+ paramIndex,
+ paramJson);
+ }
+ } else {
+ for (var paramName in objectJson.params) {
+ var paramJson = objectJson.params[paramName];
+ this.setParamValue_(/** @type {!Object} */ (object),
+ paramName,
+ paramJson);
+ }
+ }
+ }
+ if (className in this.initCallbacks) {
+ this.initCallbacks[className](this, object, objectJson);
+ }
+ }
+ this.nextObjectIndex_ = 0;
+ }
+
+ if (this.nextClassIndex_ === this.classNames_.length) {
+ this.nextClassIndex_ = 0;
+ this.nextObjectIndex_ = 0;
+ this.globalObjectIndex_ = 0;
+ ++this.phase_;
+ }
+};
+
+/**
+ * Perform a certain number of iterations of the deserializer. Keep calling this
+ * function until it returns false.
+ * @param {number} opt_amountOfWork The number of loop iterations to run. If
+ * not specified, runs the deserialization to completion.
+ * @return {boolean} Whether work remains to be done.
+ */
+o3djs.serialization.Deserializer.prototype.run = function(
+ opt_amountOfWork) {
+ if (!opt_amountOfWork) {
+ while (this.run(10000)) {
+ }
+ return false;
+ } else {
+ switch (this.phase_) {
+ case 0:
+ this.createObjectsPhase_(opt_amountOfWork);
+ break;
+ case 1:
+ this.setPropertiesPhase_(opt_amountOfWork);
+ break;
+ }
+ return this.phase_ < 2;
+ }
+};
+
+/**
+ * Deserializes (loads) a transform graph in the background. Invokes
+ * a callback function on completion passing the pack and the thrown
+ * exception on failure or the pack and a null exception on success.
+ * @param {!o3d.Client} client An O3D client object.
+ * @param {!o3d.Pack} pack The pack to create the deserialized objects
+ * in.
+ * @param {number} time The amount of the time (in seconds) the deserializer
+ * should aim to complete in.
+ * @param {!function(o3d.Pack, *): void} callback The function that
+ * is called on completion. The second parameter is null on success or
+ * the thrown exception on failure.
+ */
+o3djs.serialization.Deserializer.prototype.runBackground = function(
+ client, pack, time, callback) {
+ // TODO: This seems like it needs to be more granular than the
+ // top level.
+ // TODO: Passing in the time you want it to take seems counter
+ // intuitive. I want pass in a % of CPU so I can effectively say
+ // "deserialize this in such a way so as not to affect my app's
+ // performance". callbacksRequired = numObjects / amountPerCallback where
+ // amountPerCallback = number I can do per frame and not affect performance
+ // too much.
+ var workToDo = this.json.objects.length * 2;
+ var timerCallbacks = time * 60;
+ var amountPerCallback = workToDo / timerCallbacks;
+ var intervalId;
+ var that = this;
+ function deserializeMore() {
+ var exception = null;
+ var finished = false;
+ var failed = false;
+ var errorCollector = o3djs.error.createErrorCollector(client);
+ try {
+ finished = !that.run(amountPerCallback);
+ } catch(e) {
+ failed = true;
+ finished = true;
+ exception = e;
+ }
+ if (errorCollector.errors.length > 0) {
+ finished = true;
+ exception = errorCollector.errors.join('\n') +
+ (exception ? ('\n' + exception.toString()) : '');
+ }
+ errorCollector.finish();
+ if (finished) {
+ window.clearInterval(intervalId);
+ callback(pack, exception);
+ }
+ }
+
+ intervalId = window.setInterval(deserializeMore, 1000 / 60);
+};
+
+/**
+ * Creates a deserializer that will incrementally deserialize a
+ * transform graph. The deserializer object has a method
+ * called run that does a fixed amount of work and returns.
+ * It returns true until the transform graph is fully deserialized.
+ * It returns false from then on.
+ * @param {!o3d.Pack} pack The pack to create the deserialized
+ * objects in.
+ * @param {!Object} json An object tree conforming to the JSON rules.
+ * @return {!o3djs.serialization.Deserializer} A deserializer object.
+ */
+o3djs.serialization.createDeserializer = function(pack, json) {
+ return new o3djs.serialization.Deserializer(pack, json);
+};
+
+/**
+ * Deserializes a transform graph.
+ * @param {!o3d.Pack} pack The pack to create the deserialized
+ * objects in.
+ * @param {!Object} json An object tree conforming to the JSON rules.
+ */
+o3djs.serialization.deserialize = function(pack, json) {
+ var deserializer = o3djs.serialization.createDeserializer(pack, json);
+ deserializer.run();
+};
+
+/**
+ * Deserializes a single json object named 'scene.json' from a loaded
+ * o3djs.io.ArchiveInfo.
+ * @param {!o3djs.io.archiveInfo} archiveInfo Archive to load from.
+ * @param {string} sceneJsonUri The relative URI of the scene JSON file within
+ * the archive.
+ * @param {!o3d.Client} client An O3D client object.
+ * @param {!o3d.Pack} pack The pack to create the deserialized objects
+ * in.
+ * @param {!o3d.Transform} parent Transform to parent loaded stuff from.
+ * @param {!function(!o3d.Pack, !o3d.Transform, *): void} callback A function
+ * that will be called when deserialization is finished. It will be passed
+ * the pack, the parent transform, and an exception which will be null on
+ * success.
+ * @param {!o3djs.serialization.Options} opt_options Options.
+ */
+o3djs.serialization.deserializeArchive = function(archiveInfo,
+ sceneJsonUri,
+ client,
+ pack,
+ parent,
+ callback,
+ opt_options) {
+ opt_options = opt_options || { };
+ var jsonFile = archiveInfo.getFileByURI(sceneJsonUri);
+ if (!jsonFile) {
+ throw 'Could not find ' + sceneJsonUri + ' in archive';
+ }
+ var parsed = eval('(' + jsonFile.stringValue + ')');
+ var deserializer = o3djs.serialization.createDeserializer(pack, parsed);
+
+ deserializer.addObject(parsed.o3d_rootObject_root, parent);
+ deserializer.archiveInfo = archiveInfo;
+
+ var finishCallback = function(pack, exception) {
+ if (!exception) {
+ var objects = pack.getObjects('o3d.animSourceOwner', 'o3d.ParamObject');
+ if (objects.length > 0) {
+ // Rebind the output connections of the animSource to the user's param.
+ if (opt_options.opt_animSource) {
+ var animSource = objects[0].getParam('animSource');
+ var outputConnections = animSource.outputConnections;
+ for (var ii = 0; ii < outputConnections.length; ++ii) {
+ outputConnections[ii].bind(opt_options.opt_animSource);
+ }
+ }
+ // Remove special object from pack.
+ for (var ii = 0; ii < objects.length; ++ii) {
+ pack.removeObject(objects[ii]);
+ }
+ }
+ }
+ callback(pack, parent, exception);
+ };
+
+ if (opt_options.opt_async) {
+ // TODO: Remove the 5. See deserializer.runBackground comments.
+ deserializer.runBackground(client, pack, 5, finishCallback);
+ } else {
+ var exception = null;
+ var errorCollector = o3djs.error.createErrorCollector(client);
+ try {
+ deserializer.run();
+ } catch (e) {
+ exception = e;
+ }
+ if (errorCollector.errors.length > 0) {
+ exception = errorCollector.errors.join('\n') +
+ (exception ? ('\n' + exception.toString()) : '');
+ }
+ errorCollector.finish();
+ finishCallback(pack, exception);
+ }
+};
+
+
diff --git a/o3d/samples/o3djs/shape.js b/o3d/samples/o3djs/shape.js
new file mode 100644
index 0000000..7b36c16
--- /dev/null
+++ b/o3d/samples/o3djs/shape.js
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains various functions for helping setup
+ * shapes for o3d. It puts them in the "shape" module on the o3djs
+ * object.
+ *
+ * Note: This library is only a sample. It is not meant to be some official
+ * library. It is provided only as example code.
+ *
+ */
+
+o3djs.provide('o3djs.shape');
+
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.element');
+
+/**
+ * A Module for shapes.
+ * @namespace
+ */
+o3djs.shape = o3djs.shape || {};
+
+/**
+ * Adds missing tex coord streams to a shape's elements.
+ * @param {!o3d.Shape} shape Shape to add missing streams to.
+ * @see o3djs.element.addMissingTexCoordStreams
+ */
+o3djs.shape.addMissingTexCoordStreams = function(shape) {
+ var elements = shape.elements;
+ for (var ee = 0; ee < elements.length; ++ee) {
+ var element = elements[ee];
+ o3djs.element.addMissingTexCoordStreams(element);
+ }
+};
+
+/**
+ * Sets the bounding box and z sort points of a shape's elements.
+ * @param {!o3d.Shape} shape Shape to set info on.
+ */
+o3djs.shape.setBoundingBoxesAndZSortPoints = function(shape) {
+ var elements = shape.elements;
+ for (var ee = 0; ee < elements.length; ++ee) {
+ var element = elements[ee];
+ o3djs.element.setBoundingBoxAndZSortPoint(element);
+ }
+};
+
+/**
+ * Prepares a shape by setting its boundingBox, zSortPoint and creating
+ * DrawElements.
+ * @param {!o3d.Pack} pack Pack to manage created objects.
+ * @param {!o3d.Shape} shape Shape to prepare.
+ */
+o3djs.shape.prepareShape = function(pack, shape) {
+ shape.createDrawElements(pack, null);
+ o3djs.shape.setBoundingBoxesAndZSortPoints(shape);
+ o3djs.shape.addMissingTexCoordStreams(shape);
+};
+
+/**
+ * Prepares all the shapes in the given pack by setting their boundingBox,
+ * zSortPoint and creating DrawElements.
+ * @param {!o3d.Pack} pack Pack to manage created objects.
+ */
+o3djs.shape.prepareShapes = function(pack) {
+ var shapes = pack.getObjectsByClassName('o3d.Shape');
+ for (var ss = 0; ss < shapes.length; ++ss) {
+ o3djs.shape.prepareShape(pack, shapes[ss]);
+ }
+};
+
+/**
+ * Attempts to delete the parts of a shape that were created by
+ * duplicateShape as well as any drawElements attached to it.
+ * @param {!o3d.Shape} shape shape to delete.
+ * @param {!o3d.Pack} pack Pack to release objects from.
+ */
+o3djs.shape.deleteDuplicateShape = function(shape, pack) {
+ var elements = shape.elements;
+ for (var ee = 0; ee < elements.length; ee++) {
+ var element = elements[ee];
+ var drawElements = element.drawElements;
+ for (var dd = 0; dd < drawElements.length; dd++) {
+ var drawElement = drawElements[dd];
+ pack.removeObject(drawElement);
+ }
+ pack.removeObject(element);
+ }
+ pack.removeObject(shape);
+};
+
+/**
+ * Copies a shape's elements and streambank or buffers so the two will share
+ * streambanks, vertex and index buffers.
+ * @param {!o3d.Pack} pack Pack to manage created objects.
+ * @param {!o3d.Shape} source The Shape to copy.
+ * @return {!o3d.Shape} the new copy of the shape.
+ */
+o3djs.shape.duplicateShape = function(pack, source) {
+ var newShape = pack.createObject('Shape');
+ var elements = source.elements;
+ for (var ee = 0; ee < elements.length; ee++) {
+ var newElement = o3djs.element.duplicateElement(pack, elements[ee]);
+ newElement.owner = newShape;
+ }
+ newShape.createDrawElements(pack, null);
+ return newShape;
+};
+
diff --git a/o3d/samples/o3djs/simple.js b/o3d/samples/o3djs/simple.js
new file mode 100644
index 0000000..a02e84d
--- /dev/null
+++ b/o3d/samples/o3djs/simple.js
@@ -0,0 +1,556 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains functions to make it extremely simple
+ * to get something on the screen in o3d. The disadvantage is it
+ * is less flexible and creates inefficient assets.
+ *
+ * Example
+ *
+ * <pre>
+ * &lt;html&gt;&lt;body&gt;
+ * &lt;script type="text/javascript" src="o3djs/all.js"&gt;
+ * &lt;/script&gt;
+ * &lt;script type="text/javascript"&gt;
+ * window.init = init;
+ *
+ * function init() {
+ * o3djs.base.makeClients(initStep2);
+ * }
+ *
+ * function initStep2(clientElements) {
+ * var clientElement = clientElements[0];
+ *
+ * // Create an o3djs.simple object to manage things in a simple way.
+ * g_simple = o3djs.simple.create(clientElement);
+ *
+ * // Create a cube.
+ * g_cube = g_simple.createCube(50);
+ *
+ * // DONE!
+ * }
+ * &lt;/script&gt;
+ * &lt;div id="o3d" style="width: 600px; height: 600px"&gt;&lt;/div&gt;
+ * &lt;/body&gt;&lt;/html&gt;
+ * </pre>
+ *
+ * Some more examples:
+ *
+ * g_cube.setDiffuseColor(1, 0, 0, 1); // Cube is now red.
+ * g_cube.transform.translate(10, 0, 0); // Cube translates.
+ * g_cube.loadTexture('http://google.com/someimage.jpg"); // Cube is textured
+ *
+ *
+ * Note: This library is only a sample. It is not meant to be some official
+ * library. It is provided only as example code.
+ *
+ */
+
+o3djs.provide('o3djs.simple');
+
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.material');
+o3djs.require('o3djs.effect');
+o3djs.require('o3djs.shape');
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.pack');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.io');
+
+/**
+ * A Module for using o3d in a very simple way.
+ * @namespace
+ */
+o3djs.simple = o3djs.simple || {};
+
+/**
+ * Creates an o3djs.simple library object that helps manage o3d
+ * for the extremely simple cases.
+ *
+ * <pre>
+ * &lt;html&gt;&lt;body&gt;
+ * &lt;script type="text/javascript" src="o3djs/all.js"&gt;
+ * &lt;/script&gt;
+ * &lt;script type="text/javascript"&gt;
+ * windows.onload = init;
+ *
+ * function init() {
+ * o3djs.base.makeClients(initStep2);
+ * }
+ *
+ * function initStep2(clientElements) {
+ * var clientElement = clientElements[0];
+ *
+ * // Create an o3djs.simple object to manage things in a simple way.
+ * g_simple = o3djs.simple.create(clientElement);
+ *
+ * // Create a cube.
+ * g_cube = g_simple.createCube(50);
+ *
+ * // DONE!
+ * }
+ * &lt;/script&gt;
+ * &lt;div id="o3d" style="width: 600px; height: 600px"&gt;&lt;/div&gt;
+ * &lt;/body&gt;&lt;/html&gt;
+ * </pre>
+ *
+ * @param {!Element} clientObject O3D.Plugin Object.
+ * @return {!o3djs.simple.SimpleInfo} Javascript object that hold info for the
+ * simple library.
+ *
+ */
+o3djs.simple.create = function(clientObject) {
+ return new o3djs.simple.SimpleInfo(clientObject);
+};
+
+/**
+ * A SimpleInfo contains information for the simple library.
+ * @constructor
+ * @param {!Element} clientObject O3D.Plugin Object.
+ */
+o3djs.simple.SimpleInfo = function(clientObject) {
+ this.clientObject = clientObject;
+ this.o3d = clientObject.o3d;
+ this.math = o3djs.math;
+ this.client = clientObject.client;
+ this.pack = this.client.createPack();
+ this.viewInfo = o3djs.rendergraph.createBasicView(
+ this.pack,
+ this.client.root,
+ this.client.renderGraphRoot);
+ this.id = 0;
+
+ // Create 1 non-textured material and 1 textured material.
+ //
+ // TODO: Refactor.
+ // This is slightly backward. What we really want is to be able to request
+ // an effect of a specific type from our shader builder but the current shader
+ // builder expects a material to already exist. So, we create a material here
+ // just to pass it to the shader builder, then we keep the effect it created
+ // but throw away the material.
+ //
+ // TODO: Fix shader builder so it creates diffuseColorMult,
+ // diffuseColorOffset and diffuseTexture so
+ // diffuse = diffuseTexture * diffuseColorMult + diffuseColorOffset.
+
+ var material = this.pack.createObject('Material');
+
+ o3djs.effect.attachStandardShader(this.pack,
+ material,
+ [0, 0, 0],
+ 'phong');
+
+ this.nonTexturedEffect = material.effect;
+ this.pack.removeObject(material);
+
+ var material = this.pack.createObject('Material');
+ var samplerParam = material.createParam('diffuseSampler', 'ParamSampler');
+ o3djs.effect.attachStandardShader(this.pack,
+ material,
+ [0, 0, 0],
+ 'phong');
+
+ this.texturedEffect = material.effect;
+ this.pack.removeObject(material);
+
+ this.globalParamObject = this.pack.createObject('ParamObject');
+ this.lightWorldPosParam = this.globalParamObject.createParam('lightWorldPos',
+ 'ParamFloat3');
+ this.lightColorParam = this.globalParamObject.createParam('lightColor',
+ 'ParamFloat4');
+ this.setLightColor(1, 1, 1, 1);
+ this.setLightPosition(255, 150, 150); // same as camera.
+
+ // Attempt to setup a resonable default perspective matrix.
+ this.zNear = 0.1;
+ this.zFar = 1000;
+ this.fieldOfView = this.math.degToRad(45);
+ this.setPerspectiveMatrix_();
+
+ // Attempt to setup a resonable default view.
+ this.cameraPosition = [250, 150, 150];
+ this.cameraTarget = [0, 0, 0];
+ this.cameraUp = [0, 1, 0];
+ this.setViewMatrix_();
+};
+
+/**
+ * Creates a SimpleShape. A SimpleShape manages a transform with 1 shape that
+ * holds 1 primitive and 1 unique material.
+ * @param {!o3d.Shape} shape that holds 1 primitive and 1 unique material.
+ * @param {!o3d.Material} material assigned to shape.
+ * @return {!o3djs.simple.SimpleShape} the created SimpleShape.
+ */
+o3djs.simple.SimpleInfo.prototype.createSimpleShape = function(shape,
+ material) {
+ shape.createDrawElements(this.pack, null);
+ var transform = this.pack.createObject('Transform');
+ transform.parent = this.client.root;
+ transform.addShape(shape);
+ return new o3djs.simple.SimpleShape(this, transform, material);
+};
+
+/**
+ * Sets the perspective matrix.
+ * @private
+ */
+o3djs.simple.SimpleInfo.prototype.setPerspectiveMatrix_ = function() {
+ this.viewInfo.drawContext.projection = this.math.matrix4.perspective(
+ this.fieldOfView,
+ this.client.width / this.client.height,
+ this.zNear,
+ this.zFar);
+};
+
+/**
+ * Sets the view matrix.
+ * @private
+ */
+o3djs.simple.SimpleInfo.prototype.setViewMatrix_ = function() {
+ this.viewInfo.drawContext.view = this.math.matrix4.lookAt(
+ this.cameraPosition,
+ this.cameraTarget,
+ this.cameraUp);
+};
+
+/**
+ * Sets the field of view
+ * @param {number} fieldOfView in Radians.
+ *
+ * For degrees use setFieldOfView(o3djs.math.degToRad(degrees)).
+ */
+o3djs.simple.SimpleInfo.prototype.setFieldOfView =
+ function(fieldOfView) {
+ this.fieldOfView = fieldOfView;
+ this.setPerspectiveMatrix_();
+};
+
+/**
+ * Sets the z clip range.
+ * @param {number} zNear near z value.
+ * @param {number} zFar far z value.
+ */
+o3djs.simple.SimpleInfo.prototype.setZClip = function(zNear, zFar) {
+ this.zNear = zNear;
+ this.zFar = zFar;
+ this.setPerspectiveMatrix_();
+};
+
+/**
+ * Sets the light position
+ * @param {number} x x position.
+ * @param {number} y y position.
+ * @param {number} z z position.
+ */
+o3djs.simple.SimpleInfo.prototype.setLightPosition = function(x, y, z) {
+ this.lightWorldPosParam.set(x, y, z);
+};
+
+/**
+ * Sets the light color
+ * @param {number} r red (0-1).
+ * @param {number} g green (0-1).
+ * @param {number} b blue (0-1).
+ * @param {number} a alpha (0-1).
+ */
+o3djs.simple.SimpleInfo.prototype.setLightColor = function(r, g, b, a) {
+ this.lightColorParam.set(r, g, b, a);
+};
+
+/**
+ * Sets the camera position
+ * @param {number} x x position.
+ * @param {number} y y position.
+ * @param {number} z z position.
+ */
+o3djs.simple.SimpleInfo.prototype.setCameraPosition = function(x, y, z) {
+ this.cameraPosition = [x, y, z];
+ this.setViewMatrix_();
+};
+
+/**
+ * Sets the camera target
+ * @param {number} x x position.
+ * @param {number} y y position.
+ * @param {number} z z position.
+ */
+o3djs.simple.SimpleInfo.prototype.setCameraTarget = function(x, y, z) {
+ this.cameraTarget = [x, y, z];
+ this.setViewMatrix_();
+};
+
+/**
+ * Sets the camera up
+ * @param {number} x x position.
+ * @param {number} y y position.
+ * @param {number} z z position.
+ */
+o3djs.simple.SimpleInfo.prototype.setCameraUp = function(x, y, z) {
+ this.cameraUp = [x, y, z];
+ this.setViewMatrix_();
+};
+
+/**
+ * Create meterial from effect.
+ * @param {!o3d.Effect} effect Effect to use for material.
+ * @return {!o3d.Material} The created material.
+ */
+o3djs.simple.SimpleInfo.prototype.createMaterialFromEffect =
+ function(effect) {
+ var material = this.pack.createObject('Material');
+ material.drawList = this.viewInfo.performanceDrawList;
+ material.effect = effect;
+ effect.createUniformParameters(material);
+ material.getParam('lightWorldPos').bind(this.lightWorldPosParam);
+ material.getParam('lightColor').bind(this.lightColorParam);
+ return material;
+};
+
+/**
+ * Create a new non-textured material.
+ * @param {string} type Type of material 'phong', 'lambert', 'constant'.
+ * @return {!o3d.Material} The created material.
+ */
+o3djs.simple.SimpleInfo.prototype.createNonTexturedMaterial =
+ function(type) {
+ var material = this.createMaterialFromEffect(this.nonTexturedEffect);
+ material.getParam('diffuse').set(1, 1, 1, 1);
+ material.getParam('emissive').set(0, 0, 0, 1);
+ material.getParam('ambient').set(0, 0, 0, 1);
+ material.getParam('specular').set(1, 1, 1, 1);
+ material.getParam('shininess').value = 20;
+ return material;
+};
+
+/**
+ * @param {string} type Type of material 'phong', 'lambert', 'constant'.
+ * @return {!o3d.Material} The created material.
+ */
+o3djs.simple.SimpleInfo.prototype.createTexturedMaterial =
+ function(type) {
+ var material = this.createMaterialFromEffect(this.texturedEffect);
+ var samplerParam = material.getParam('diffuseSampler');
+ var sampler = this.pack.createObject('Sampler');
+ samplerParam.value = sampler;
+ return material;
+};
+
+/**
+ * Creates a cube and adds it to the root of this SimpleInfo's transform graph.
+ * @param {number} size Width, height and depth of the cube.
+ * @return {!o3djs.simple.SimpleShape} A Javascript object to manage the
+ * shape.
+ */
+o3djs.simple.SimpleInfo.prototype.createCube = function(size) {
+ var material = this.createNonTexturedMaterial('phong');
+ var shape = o3djs.primitives.createCube(this.pack, material, size);
+ return this.createSimpleShape(shape, material);
+};
+
+/**
+ * Creates a box and adds it to the root of this SimpleInfo's transform graph.
+ * @param {number} width Width of the box.
+ * @param {number} height Height of the box.
+ * @param {number} depth Depth of the box.
+ * @return {!o3djs.simple.SimpleShape} A Javascript object to manage the
+ * shape.
+ */
+o3djs.simple.SimpleInfo.prototype.createBox = function(width,
+ height,
+ depth) {
+ var material = this.createNonTexturedMaterial('phong');
+ var shape = o3djs.primitives.createBox(this.pack,
+ material,
+ width,
+ height,
+ depth);
+ return this.createSimpleShape(shape, material);
+};
+
+/**
+ * Creates a sphere and adds it to the root of this SimpleInfo's transform
+ * graph.
+ * @param {number} radius radius of sphere.
+ * @param {number} smoothness determines the number of subdivisions.
+ * @return {!o3djs.simple.SimpleShape} A Javascript object to manage the
+ * shape.
+ */
+o3djs.simple.SimpleInfo.prototype.createSphere = function(radius,
+ smoothness) {
+ var material = this.createNonTexturedMaterial('phong');
+ var shape = o3djs.primitives.createSphere(this.pack,
+ material,
+ radius,
+ smoothness * 2,
+ smoothness);
+ return this.createSimpleShape(shape, material);
+};
+
+/**
+ * Loads a scene from a URL.
+ * TODO: Implement
+ * @param {string} url Url of scene to load.
+ * @return {!o3djs.simple.Scene} A Javascript object to manage th scene.
+ */
+o3djs.simple.SimpleInfo.prototype.loadScene = function(url) {
+ throw('not implemented');
+ return null;
+};
+
+/**
+ * Moves the camera so everything in the current scene is visible.
+ */
+o3djs.simple.SimpleInfo.prototype.viewAll = function() {
+ var bbox = o3djs.util.getBoundingBoxOfTree(this.client.root);
+ var target = this.math.lerpVector(bbox.minExtent, bbox.maxExtent, 0.5);
+ this.setCameraTarget(target[0], target[1], target[2]);
+ // TODO: Refactor this so it takes a vector from the current camera
+ // position to the center of the scene and moves the camera along that
+ // vector away from the center of the scene until for the given fieldOfView
+ // everything is visible.
+ var diag = this.math.distance(bbox.minExtent, bbox.maxExtent);
+ var eye = this.math.addVector(target, [
+ bbox.maxExtent[0],
+ bbox.minExtent[1] + 0.5 * diag,
+ bbox.maxExtent[2]]);
+ this.setCameraPosition(eye[0], eye[1], eye[2]);
+ this.setZClip(diag / 1000, diag * 10);
+};
+
+/**
+ * A SimpleShape manages a transform with 1 shape that holds 1 primitive
+ * and 1 unique material.
+ * @constructor
+ * @param {!o3djs.simple.SimpleInfo} simpleInfo SimpleInfo to manage this shape.
+ * @param {!o3d.Transform} transform Transform with 1 shape that holds 1
+ * primitive and 1 unique material.
+ * @param {!o3d.Material} material assigned to shape.
+ */
+o3djs.simple.SimpleShape = function(simpleInfo, transform, material) {
+ /**
+ * The SimpleInfo managing this SimpleShape.
+ * @type {!o3djs.simple.SimpleInfo}
+ */
+ this.simpleInfo = simpleInfo;
+
+ /**
+ * The transform for this SimpleShape.
+ * @type {!o3d.Transform}
+ */
+ this.transform = transform;
+
+ /**
+ * The material for this SimpleShape.
+ * @type {!o3d.Material}
+ */
+ this.material = material;
+};
+
+/**
+ * Sets the material for this SimpleShape, deleting any old one.
+ * @param {!o3d.Material} material new material.
+ */
+o3djs.simple.SimpleShape.prototype.setMaterial = function(material) {
+ var old_material = this.material;
+ this.simpleInfo.pack.removeObject(old_material);
+ this.transform.shapes[0].elements[0].material = material;
+ this.material = material;
+};
+
+/**
+ * Sets the diffuse color of this shape.
+ * @param {number} r Red (0-1).
+ * @param {number} g Green (0-1).
+ * @param {number} b Blue (0-1).
+ * @param {number} a Alpha (0-1).
+ */
+o3djs.simple.SimpleShape.prototype.setDiffuseColor =
+ function(r, g, b, a) {
+ var material = this.material;
+ material.getParam('diffuse').set(r, g, b, a);
+ if (a < 1) {
+ material.drawList = this.simpleInfo.viewInfo.zOrderedDrawList;
+ } else {
+ material.drawList = this.simpleInfo.viewInfo.performanceDrawList;
+ }
+};
+
+/**
+ * Gets the texture on this shape.
+ * @return {o3d.Texture} The texture on this shape. May be null.
+ */
+o3djs.simple.SimpleShape.prototype.getTexture = function() {
+ var material = this.material;
+ var samplerParam = material.getParam('diffuseSampler');
+ if (samplerParam.className == 'o3d.ParamSampler') {
+ return samplerParam.texture;
+ }
+ return null;
+};
+
+/**
+ * Loads a texture onto the given shape. It will replace the material
+ * if it needs to with one that supports a texture. Note that the texture
+ * is loaded asynchronously and so the result of this call may appear several
+ * seconds after it is called depending on how long it takes to download the
+ * texture.
+ * @param {string} url Url of texture.
+ */
+o3djs.simple.SimpleShape.prototype.loadTexture = function(url) {
+ var that = this;
+ o3djs.io.loadTexture(
+ this.simpleInfo.pack,
+ url,
+ function(texture, exception) {
+ if (!exception) {
+ // See if this is a textured material.
+ var material = that.material;
+ if (material.effect != that.simpleInfo.texturedEffect) {
+ // replace the material with a textured one.
+ var new_material = that.simpleInfo.createTexturedMaterial('phong');
+ new_material.copyParams(material);
+ // Reset the effect since copy Params just copied the non-textured
+ // one.
+ new_material.effect = that.simpleInfo.texturedEffect;
+ that.setMaterial(new_material);
+ material = new_material;
+ }
+ var samplerParam = material.getParam('diffuseSampler');
+ samplerParam.value.texture = texture;
+ } else {
+ alert('Load texture file returned failure. \n' + exception);
+ }
+ });
+};
+
diff --git a/o3d/samples/o3djs/test.js b/o3d/samples/o3djs/test.js
new file mode 100644
index 0000000..84fd88a
--- /dev/null
+++ b/o3d/samples/o3djs/test.js
@@ -0,0 +1,342 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This is a simple unit testing library used to test the
+ * sample utilities
+ *
+ *
+ */
+o3djs.provide('o3djs.test');
+
+/**
+ * A unit testing library
+ */
+o3djs.test = o3djs.test || {};
+
+/**
+ * Class of errors thrown by assertions
+ * @param {string} message The assertion message.
+ * @this o3djs.test.AssertionError
+ */
+o3djs.test.AssertionError = function(message) {
+ this.message = message;
+
+ /**
+ * Returns the error message.
+ * @return {String} The error message.
+ */
+ this.toString = function() {
+ return message;
+ };
+};
+
+/**
+ * Runs all the tests found in the given suite. Every function with a
+ * name beginning with 'test' is considered to be a test.
+ * @param {!Object} suite The object containing the test suite.
+ * @param {!Object} opt_reporter An optional object to which the results
+ * of the test run are reported.
+ * @return {boolean} Whether all the tests passed.
+ */
+o3djs.test.runTests = function(suite, opt_reporter) {
+ try {
+ opt_reporter = opt_reporter || o3djs.test.documentReporter;
+
+ var passCount = 0;
+ var failCount = 0;
+ for (var propertyName in suite) {
+ if (propertyName.substring(0, 4) !== 'test')
+ continue;
+
+ if (typeof(suite[propertyName]) !== 'function')
+ continue;
+
+ try {
+ suite[propertyName]();
+ } catch (e) {
+ ++failCount;
+ opt_reporter.reportFail(propertyName, String(e));
+ continue;
+ }
+
+ ++passCount;
+ opt_reporter.reportPass(propertyName);
+ }
+
+ opt_reporter.reportSummary(passCount, failCount);
+ return failCount == 0;
+ }
+ catch (e) {
+ return false;
+ }
+};
+
+/**
+ * Converts a value to the string representation used in assertion messages.
+ * @private
+ * @param {*} value The value to convert.
+ * @param {number} opt_depth The depth of references to follow for nested
+ * objects. Defaults to 3.
+ * @return {string} The string representation.
+ */
+o3djs.test.valueToString_ = function(value, opt_depth) {
+ if (opt_depth === undefined) {
+ opt_depth = 3;
+ }
+ var string;
+ if (typeof(value) === 'object') {
+ if (value !== null) {
+ if (opt_depth === 0) {
+ string = '?';
+ } else {
+ if (o3djs.base.isArray(value)) {
+ var valueAsArray = /** @type {!Array.<*>} */ (value);
+ string = '[';
+ var separator = '';
+ for (var i = 0; i < valueAsArray.length; ++i) {
+ string += separator +
+ o3djs.test.valueToString_(valueAsArray[i], opt_depth - 1);
+ separator = ', ';
+ }
+ string += ']';
+ } else {
+ var valueAsObject = /** @type {!Object} */ (value);
+ string = '{';
+ var separator = '';
+ for (var propertyName in valueAsObject) {
+ if (typeof(valueAsObject[propertyName]) !== 'function') {
+ string += separator + propertyName + ': ' +
+ o3djs.test.valueToString_(valueAsObject[propertyName],
+ opt_depth - 1);
+ separator = ', ';
+ }
+ }
+ string += '}';
+ }
+ }
+ } else {
+ string = "null";
+ }
+ } else if (typeof(value) === 'string') {
+ string = '"' + value + '"';
+ } else {
+ string = String(value);
+ }
+ return string;
+};
+
+/**
+ * Asserts that a value is true from within a test
+ * @param {boolean} value The value to test.
+ */
+o3djs.test.assertTrue = function(value) {
+ if (!value) {
+ throw new o3djs.test.AssertionError(
+ 'assertTrue failed for ' +
+ o3djs.test.valueToString_(value));
+ }
+};
+
+/**
+ * Asserts that a value is false from within a test
+ * @param {boolean} value The value to test.
+ */
+o3djs.test.assertFalse = function(value) {
+ if (value) {
+ throw new o3djs.test.AssertionError(
+ 'assertFalse failed for ' +
+ o3djs.test.valueToString_(value));
+ }
+};
+
+/**
+ * Asserts that a value is null from within a test
+ * @param {*} value The value to test.
+ */
+o3djs.test.assertNull = function(value) {
+ if (value !== null) {
+ throw new o3djs.test.AssertionError(
+ 'assertNull failed for ' +
+ o3djs.test.valueToString_(value));
+ }
+};
+
+/**
+ * Asserts that an expected value is equal to an actual value.
+ * @param {*} expected The expected value.
+ * @param {*} actual The actual value.
+ */
+o3djs.test.assertEquals = function(expected, actual) {
+ if (expected !== actual) {
+ throw new o3djs.test.AssertionError(
+ 'assertEquals failed: expected ' +
+ o3djs.test.valueToString_(expected) + ' but got ' +
+ o3djs.test.valueToString_(actual));
+ }
+};
+
+/**
+ * Asserts that an expected value is close to an actual value
+ * within a tolerance of 0.001.
+ * @param {number} expected The expected value.
+ * @param {number} actual The actual value.
+ */
+o3djs.test.assertClose = function(expected, actual) {
+ if (actual < expected - 0.001 || actual > expected + 0.001) {
+ throw new o3djs.test.AssertionError(
+ 'assertClose failed: expected ' +
+ o3djs.test.valueToString_(expected) + ' but got ' +
+ o3djs.test.valueToString_(actual));
+ }
+};
+
+/**
+ * Determines whether the elements of a pair of arrays are equal.
+ * @private
+ * @param {!Array.<*>} expected The expected array.
+ * @param {!Array.<*>} actual The actual array.
+ * @return {boolean} Whether the arrays are equal.
+ */
+o3djs.test.compareArrays_ = function(expected, actual) {
+ if (expected.length !== actual.length) {
+ return false;
+ }
+ for (var i = 0; i != expected.length; ++i) {
+ if (o3djs.base.isArray(expected[i]) && o3djs.base.isArray(actual[i])) {
+ var expectedAsArray = /** @type {!Array.<*>} */ (expected[i]);
+ var actualAsArray = /** @type {!Array.<*>} */ (actual[i]);
+ if (!o3djs.test.compareArrays_(expectedAsArray, actualAsArray)) {
+ return false;
+ }
+ } else if (expected[i] !== actual[i]) {
+ return false;
+ }
+ }
+ return true;
+};
+
+/**
+ * Asserts that an expected array is equal to an actual array.
+ * @param {!Array.<*>} expected The expected array.
+ * @param {!Array.<*>} actual The actual array.
+ */
+o3djs.test.assertArrayEquals = function(expected, actual) {
+ if (!o3djs.base.isArray(expected)) {
+ throw new o3djs.test.AssertionError(
+ 'assertArrayEquals failed: expected value ' +
+ o3djs.test.valueToString_(expected) +
+ ' is not an array');
+ }
+ if (!o3djs.base.isArray(actual)) {
+ throw new o3djs.test.AssertionError(
+ 'assertArrayEquals failed: actual value ' +
+ o3djs.test.valueToString_(actual) +
+ ' is not an array');
+ }
+ if (!o3djs.test.compareArrays_(expected, actual)) {
+ throw new o3djs.test.AssertionError(
+ 'assertArrayEquals failed: expected ' +
+ o3djs.test.valueToString_(expected) + ' but got ' +
+ o3djs.test.valueToString_(actual));
+ }
+};
+
+/**
+ * Creates a DOM paragraph object for the given text and color.
+ * @private
+ * @param {string} text The text of the message.
+ * @param {string} opt_color The optional color of the message.
+ * @return {!Element} A DOM paragraph object.
+ */
+o3djs.test.createReportParagraph_ = function(text, opt_color) {
+ var textNode = document.createTextNode(text);
+ var paragraph = document.createElement('p');
+ paragraph.appendChild(textNode);
+ if (opt_color !== undefined) {
+ paragraph.style.color = opt_color;
+ }
+ return paragraph;
+};
+
+/**
+ * A reporter that reports messages to the document (i.e. the DOM).
+ * @type {!Object}
+ */
+o3djs.test.documentReporter = {
+ /**
+ * A Report div.
+ * @private
+ * @this {Object}
+ */
+ getReportDiv_: function() {
+ if (!this.reportDiv_) {
+ this.reportDiv_ = document.createElement('div');
+ document.body.appendChild(this.reportDiv_);
+ }
+ return this.reportDiv_;
+ },
+ /**
+ * Reports a test passed.
+ * @param {string} testName The name of the test.
+ * @this {Object}
+ */
+ reportPass: function(testName) {
+ var paragraph = o3djs.test.createReportParagraph_(
+ testName + ' : PASS', 'green');
+ this.getReportDiv_().appendChild(paragraph);
+ },
+ /**
+ * Reports a test failed.
+ * @param {string} testName The name of the test.
+ */
+ reportFail: function(testName, message) {
+ var paragraph = o3djs.test.createReportParagraph_(
+ testName + ' : FAIL : ' + message, 'red');
+ var reportDiv = this.getReportDiv_();
+ reportDiv.insertBefore(paragraph,
+ reportDiv.firstChild);
+ },
+ /**
+ * Reports a test summary.
+ * @param {number} passCount The number of tests that passed.
+ * @param {number} failCount The number of tests that failed.
+ * @this {Object}
+ */
+ reportSummary: function(passCount, failCount) {
+ var paragraph = o3djs.test.createReportParagraph_(
+ passCount + ' passed, ' + failCount + ' failed', 'blue');
+ var reportDiv = this.getReportDiv_();
+ reportDiv.insertBefore(paragraph,
+ reportDiv.firstChild);
+ }
+};
diff --git a/o3d/samples/o3djs/util.js b/o3d/samples/o3djs/util.js
new file mode 100644
index 0000000..de283ba
--- /dev/null
+++ b/o3d/samples/o3djs/util.js
@@ -0,0 +1,928 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains various utility functions for o3d. It
+ * puts them in the "util" module on the o3djs object.
+ *
+ */
+
+o3djs.provide('o3djs.util');
+
+o3djs.require('o3djs.io');
+o3djs.require('o3djs.event');
+o3djs.require('o3djs.error');
+
+/**
+ * A Module with various utilities.
+ * @namespace
+ */
+o3djs.util = o3djs.util || {};
+
+/**
+ * The name of the o3d plugin. Used to find the plugin when checking
+ * for its version.
+ * @type {string}
+ */
+o3djs.util.PLUGIN_NAME = 'O3D Plugin';
+
+/**
+ * The version of the plugin needed to use this version of the javascript
+ * utility libraries.
+ * @type {string}
+ */
+o3djs.util.REQUIRED_VERSION = '0.1.34.4';
+
+/**
+ * A URL at which to download the client.
+ * @type {string}
+ */
+o3djs.util.PLUGIN_DOWNLOAD_URL = 'http://tools.google.com/dlpage/o3d';
+
+/**
+ * The Renderer InitStatus constants so we don't need an o3d object to look
+ * them up.
+ * @enum {number}
+ */
+o3djs.util.rendererInitStatus = {
+ NO_PLUGIN: -1,
+ UNINITIALIZED: 0,
+ SUCCESS: 1,
+ OUT_OF_RESOURCES: 2,
+ GPU_NOT_UP_TO_SPEC: 3,
+ INITIALIZATION_ERROR: 4
+};
+
+/**
+ * This implements a JavaScript version of currying. Currying allows you to
+ * take a function and fix its initial arguments, resulting in a function
+ * expecting only the remaining arguments when it is invoked. For example:
+ * <pre>
+ * function add(a, b) {
+ * return a + b;
+ * }
+ * var increment = o3djs.util.curry(add, 1);
+ * var result = increment(10);
+ * </pre>
+ * Now result equals 11.
+ * @param {!function(...): *} func The function to curry.
+ * @return {!function(...): *} The curried function.
+ */
+o3djs.util.curry = function(func) {
+ var outerArgs = [];
+ for (var i = 1; i < arguments.length; ++i) {
+ outerArgs.push(arguments[i]);
+ }
+ return function() {
+ var innerArgs = outerArgs.slice();
+ for (var i = 0; i < arguments.length; ++i) {
+ innerArgs.push(arguments[i]);
+ }
+ return func.apply(this, innerArgs);
+ }
+}
+
+/**
+ * Gets the URI in which the current page is located, omitting the file name.
+ * @return {string} The base URI of the page. If the page is
+ * "http://some.com/folder/somepage.html" returns
+ * "http://some.com/folder/".
+ */
+o3djs.util.getCurrentURI = function() {
+ var path = window.location.href;
+ var index = path.lastIndexOf('/');
+ return path.substring(0, index + 1);
+};
+
+/**
+ * Given a URI that is relative to the current page, returns the absolute
+ * URI.
+ * @param {string} uri URI relative to the current page.
+ * @return {string} Absolute uri. If the page is
+ * "http://some.com/folder/sompage.html" and you pass in
+ * "images/someimage.jpg" will return
+ * "http://some.com/folder/images/someimage.jpg".
+ */
+o3djs.util.getAbsoluteURI = function(uri) {
+ return o3djs.util.getCurrentURI() + uri;
+};
+
+/**
+ * Searches an array for a specific value.
+ * @param {!Array.<*>} array Array to search.
+ * @param {*} value Value to search for.
+ * @return {boolean} True if value is in array.
+ */
+o3djs.util.arrayContains = function(array, value) {
+ for (var i = 0; i < array.length; i++) {
+ if (array[i] == value) {
+ return true;
+ }
+ }
+ return false;
+};
+
+/**
+ * Searches for all transforms with a "o3d.tags" ParamString
+ * that contains specific tag keywords assuming comma separated
+ * words.
+ * @param {!o3d.Transform} treeRoot Root of tree to search for tags.
+ * @param {string} searchTags Tags to look for. eg "camera", "ogre,dragon".
+ * @return {!Array.<!o3d.Transform>} Array of transforms.
+ */
+o3djs.util.getTransformsInTreeByTags = function(treeRoot, searchTags) {
+ var splitTags = searchTags.split(',');
+ var transforms = treeRoot.getTransformsInTree();
+ var found = [];
+ for (var n = 0; n < transforms.length; n++) {
+ var tagParam = transforms[n].getParam('collada.tags');
+ if (tagParam) {
+ var tags = tagParam.value.split(',');
+ for (var t = 0; t < tags.length; t++) {
+ if (o3djs.util.arrayContains(splitTags, tags[t])) {
+ found[found.length] = transforms[n];
+ break;
+ }
+ }
+ }
+ }
+ return found;
+};
+
+/**
+ * Finds transforms in the tree by prefix.
+ * @param {!o3d.Transform} treeRoot Root of tree to search.
+ * @param {string} prefix Prefix to look for.
+ * @return {!Array.<!o3d.Transform>} Array of transforms matching prefix.
+ */
+o3djs.util.getTransformsInTreeByPrefix = function(treeRoot, prefix) {
+ var found = [];
+ var transforms = treeRoot.getTransformsInTree();
+ for (var ii = 0; ii < transforms.length; ii++) {
+ var transform = transforms[ii];
+ if (transform.name.indexOf(prefix) == 0) {
+ found[found.length] = transform;
+ }
+ }
+ return found;
+};
+
+/**
+ * Finds the bounding box of all primitives in the tree, in the local space of
+ * the tree root. This will use existing bounding boxes on transforms and
+ * elements, but not create new ones.
+ * @param {!o3d.Transform} treeRoot Root of tree to search.
+ * @return {!o3d.BoundingBox} The boundinding box of the tree.
+ */
+o3djs.util.getBoundingBoxOfTree = function(treeRoot) {
+ // If we already have a bounding box, use that one.
+ var box = treeRoot.boundingBox;
+ if (box.valid) {
+ return box;
+ }
+ var o3d = o3djs.base.o3d;
+ // Otherwise, create it as the union of all the children bounding boxes and
+ // all the shape bounding boxes.
+ var transforms = treeRoot.children;
+ for (var i = 0; i < transforms.length; ++i) {
+ var transform = transforms[i];
+ var childBox = o3djs.util.getBoundingBoxOfTree(transform);
+ if (childBox.valid) {
+ // transform by the child local matrix.
+ childBox = childBox.mul(transform.localMatrix);
+ if (box.valid) {
+ box = box.add(childBox);
+ } else {
+ box = childBox;
+ }
+ }
+ }
+ var shapes = treeRoot.shapes;
+ for (var i = 0; i < shapes.length; ++i) {
+ var elements = shapes[i].elements;
+ for (var j = 0; j < elements.length; ++j) {
+ var elementBox = elements[j].boundingBox;
+ if (!elementBox.valid) {
+ elementBox = elements[j].getBoundingBox(0);
+ }
+ if (box.valid) {
+ box = box.add(elementBox);
+ } else {
+ box = elementBox;
+ }
+ }
+ }
+ return box;
+};
+
+/**
+ * Returns the smallest power of 2 that is larger than or equal to size.
+ * @param {number} size Size to get power of 2 for.
+ * @return {number} smallest power of 2 that is larger than or equal to size.
+ */
+o3djs.util.getPowerOfTwoSize = function(size) {
+ var powerOfTwo = 1;
+ while (size) {
+ size = size >> 1;
+ powerOfTwo = powerOfTwo << 1;
+ }
+ return powerOfTwo;
+};
+
+/**
+ * Gets the version of the installed plugin.
+ * @return {?string} version string in 'major.minor.revision.build' format.
+ * If the plugin does not exist returns null.
+ */
+o3djs.util.getPluginVersion = function() {
+ var version = null;
+ var description = null;
+ if (navigator.plugins != null && navigator.plugins.length > 0) {
+ var plugin = navigator.plugins[o3djs.util.PLUGIN_NAME];
+ if (plugin) {
+ description = plugin.description;
+ }
+ } else if (o3djs.base.IsMSIE()) {
+ try {
+ var activeXObject = new ActiveXObject('o3d_host.O3DHostControl');
+ description = activeXObject.description;
+ } catch (e) {
+ // O3D plugin was not found.
+ }
+ }
+ if (description) {
+ var re = /.*version:(\d+)\.(\d+)\.(\d+)\.(\d+).*/;
+ // Parse the version out of the description.
+ var parts = re.exec(description);
+ if (parts && parts.length == 5) {
+ // make sure the format is #.#.#.# no whitespace, no trailing comments
+ version = '' + parseInt(parts[1], 10) + '.' +
+ parseInt(parts[2], 10) + '.' +
+ parseInt(parts[3], 10) + '.' +
+ parseInt(parts[4], 10);
+ }
+ }
+ return version;
+};
+
+/**
+ * Checks if the required version of the plugin in available.
+ * @param {string} requiredVersion version string in
+ * "major.minor.revision.build" format. You can leave out any non-important
+ * numbers for example "3" = require major version 3, "2.4" = require major
+ * version 2, minor version 4.
+ * @return {boolean} True if the required version is available.
+ */
+o3djs.util.requiredVersionAvailable = function(requiredVersion) {
+ var version = o3djs.util.getPluginVersion();
+ if (!version) {
+ return false;
+ }
+ var haveParts = version.split('.');
+ var requiredParts = requiredVersion.split('.');
+ if (requiredParts.length > 4) {
+ throw RangeError('requiredVersion has more than 4 parts!');
+ }
+ for (var pp = 0; pp < requiredParts.length; ++pp) {
+ var have = parseInt(haveParts[pp], 10);
+ var required = parseInt(requiredParts[pp], 10);
+ if (have < required) {
+ return false;
+ }
+ if (have > required) {
+ return true;
+ }
+ }
+ return true;
+};
+
+/**
+ * Offers the user the option to download the plugin.
+ *
+ * Finds all divs with the id "^o3d" and inserts a message and link
+ * inside to download the plugin. If no areas exist OR if none of them are
+ * large enough for the message then displays an alert.
+ *
+ * @param {string} opt_id The id to look for. This can be a regular
+ * expression. The default is "^o3d".
+ * @param {string} opt_tag The type of tag to look for. The default is "div".
+ */
+o3djs.util.offerPlugin = function(opt_id, opt_tag) {
+ var tag = opt_tag || 'div';
+ var id = opt_id || '^o3d';
+ var havePlugin = o3djs.util.requiredVersionAvailable('');
+ var elements = document.getElementsByTagName(tag);
+ var addedMessage = false;
+ // TODO: This needs to be localized OR we could insert a html like
+ // <script src="http://google.com/o3d_plugin_dl"></script>
+ // in which case google could serve the message localized and update the
+ // link.
+ var subMessage =
+ (havePlugin ?
+ 'This page requires a newer version of the O3D plugin.' :
+ 'This page requires the O3D plugin to be installed.');
+ var message =
+ '<div style="background: lightblue; width: 100%; height: 100%; ' +
+ 'text-align:center;">' +
+ '<br/><br/>' + subMessage + '<br/>' +
+ '<a href="' + o3djs.util.PLUGIN_DOWNLOAD_URL +
+ '">Click here to download.</a>' +
+ '</div>'
+ for (var ee = 0; ee < elements.length; ++ee) {
+ var element = elements[ee];
+ if (element.id && element.id.match(id)) {
+ if (element.clientWidth >= 200 &&
+ element.clientHeight >= 200 &&
+ element.style.display.toLowerCase() != 'none' &&
+ element.style.visibility.toLowerCase() != 'hidden') {
+ addedMessage = true;
+ element.innerHTML = message;
+ }
+ }
+ }
+ if (!addedMessage) {
+ if (confirm(subMessage + '\n\nClick OK to download.')) {
+ window.location = o3djs.util.PLUGIN_DOWNLOAD_URL;
+ }
+ }
+};
+
+/**
+ * Tells the user their graphics card is not able to run the plugin or is out
+ * of resources etc.
+ *
+ * Finds all divs with the id "^o3d" and inserts a message. If no areas
+ * exist OR if none of them are large enough for the message then displays an
+ * alert.
+ *
+ * @param {!o3d.Renderer.InitStatus} initStatus The initializaion status of
+ * the renderer.
+ * @param {string} error An error message. Will be '' if there is no message.
+ * @param {string} opt_id The id to look for. This can be a regular
+ * expression. The default is "^o3d".
+ * @param {string} opt_tag The type of tag to look for. The default is "div".
+ */
+o3djs.util.informNoGraphics = function(initStatus, error, opt_id, opt_tag) {
+ var tag = opt_tag || 'div';
+ var id = opt_id || '^o3d';
+ var elements = document.getElementsByTagName(tag);
+ var addedMessage = false;
+ var subMessage;
+ var message;
+ var alertMessage = '';
+ var alertFunction = function() { };
+
+ var moreInfo = function(error) {
+ var html = '';
+ if (error.length > 0) {
+ html = '' +
+ '<br/><br/><div>More Info:<br/>' + error + '</div>';
+ }
+ return html;
+ };
+
+ // TODO: This needs to be localized OR we could insert a html like
+ // <script src="http://google.com/o3d_plugin_dl"></script>
+ // in which case google could serve the message localized and update the
+ // link.
+ if (initStatus == o3djs.util.rendererInitStatus.GPU_NOT_UP_TO_SPEC) {
+ subMessage =
+ 'We are terribly sorry but it appears your graphics card is not ' +
+ 'able to run o3d. We are working on a solution.';
+ message =
+ '<div style="background: lightgray; width: 100%; height: 100%; ' +
+ 'text-align: center;">' +
+ '<br/><br/>' + subMessage +
+ '<br/><br/><a href="' + o3djs.util.PLUGIN_DOWNLOAD_URL +
+ '">Click Here to go the O3D website</a>' +
+ moreInfo(error) +
+ '</div>';
+ alertMessage = '\n\nClick OK to go to the o3d website.';
+ alertFunction = function() {
+ window.location = o3djs.util.PLUGIN_DOWNLOAD_URL;
+ };
+ } else if (initStatus == o3djs.util.rendererInitStatus.OUT_OF_RESOURCES) {
+ subMessage =
+ 'Your graphics system appears to be out of resources. Try closing ' +
+ 'some applications and then refreshing this page.';
+ message =
+ '<div style="background: lightgray; width: 100%; height: 100%; ' +
+ 'text-align: center;">' +
+ '<br/><br/>' + subMessage +
+ moreInfo(error) +
+ '</div>';
+ } else {
+ subMessage =
+ 'A unknown error has prevented O3D from starting. Try downloading ' +
+ 'new drivers or checking for OS updates.';
+ message =
+ '<div style="background: lightgray; width: 100%; height: 100%; ' +
+ 'text-align: center;">' +
+ '<br/><br/>' + subMessage +
+ moreInfo(error) +
+ '</div>';
+ }
+ for (var ee = 0; ee < elements.length; ++ee) {
+ var element = elements[ee];
+ if (element.id && element.id.match(id)) {
+ if (element.clientWidth >= 200 &&
+ element.clientHeight >= 200 &&
+ element.style.display.toLowerCase() != 'none' &&
+ element.style.visibility.toLowerCase() != 'hidden') {
+ addedMessage = true;
+ element.innerHTML = message;
+ }
+ }
+ }
+ if (!addedMessage) {
+ if (confirm(subMessage + alertMessage)) {
+ alertFunction();
+ }
+ }
+};
+
+/**
+ * Handles failure to create the plugin.
+ *
+ * @param {!o3d.Renderer.InitStatus} initStatus The initializaion status of
+ * the renderer.
+ * @param {string} error An error message. Will be '' if there is no message.
+ * @param {string} opt_id The id to look for. This can be a regular
+ * expression. The default is "^o3d".
+ * @param {string} opt_tag The type of tag to look for. The default is "div".
+ */
+o3djs.util.informPluginFailure = function(initStatus, error, opt_id, opt_tag) {
+ if (initStatus == o3djs.util.rendererInitStatus.NO_PLUGIN) {
+ o3djs.util.offerPlugin(opt_id, opt_tag);
+ } else {
+ o3djs.util.informNoGraphics(initStatus, error, opt_id, opt_tag);
+ }
+};
+
+/**
+ * Utility to get the text contents of a DOM element with a particular ID.
+ * Currently only supports textarea and script nodes.
+ * @param {string} id The Node id.
+ * @return {string} The text content.
+ */
+o3djs.util.getElementContentById = function(id) {
+ // DOM manipulation is not currently supported in IE.
+ o3djs.BROWSER_ONLY = true;
+
+ var node = document.getElementById(id);
+ if (!node) {
+ throw 'getElementContentById could not find node with id ' + id;
+ }
+ switch (node.tagName) {
+ case 'TEXTAREA':
+ return node.value;
+ case 'SCRIPT':
+ return node.text;
+ default:
+ throw 'getElementContentById does not no how to get content from a ' +
+ node.tagName + ' element';
+ }
+ return node.value;
+};
+
+/**
+ * Utility to get an element from the DOM by ID. This must be used from V8
+ * in preference to document.getElementById because we do not currently
+ * support invoking methods on DOM objects in IE.
+ * @param {string} id The Node id.
+ * @return {Node} The node or null if not found.
+ */
+o3djs.util.getElementById = function(id) {
+ o3djs.BROWSER_ONLY = true;
+ return document.getElementById(id);
+};
+
+/**
+ * Identifies a JavaScript engine.
+ */
+o3djs.util.Engine = {
+ /**
+ * The JavaScript engine provided by the browser.
+ */
+ BROWSER: 0,
+ /**
+ * The V8 JavaScript engine embedded in the plugin.
+ */
+ V8: 1
+};
+
+/**
+ * The engine selected as the main engine (the one the makeClients callback
+ * will be invoked on).
+ * @private
+ * @type {o3djs.util.Engine}
+ */
+o3djs.util.mainEngine_ = o3djs.util.Engine.BROWSER;
+
+/**
+ * Select an engine to use as the main engine (the one the makeClients
+ * callback will be invoked on). If an embedded engine is requested, one
+ * element must be identified with the id 'o3d'. The callback will be invoked
+ * in this element.
+ * @param {o3djs.util.Engine} engine The engine.
+ */
+o3djs.util.setMainEngine = function(engine) {
+ o3djs.util.mainEngine_ = engine;
+};
+
+/**
+ * A regex used to cleanup the string representation of a function before
+ * it is evaled.
+ * @private
+ * @type {!RegExp}
+ */
+o3djs.util.fixFunctionString_ = /^\s*function\s+[^\s]+\s*\(([^)]*)\)/
+
+/**
+ * Evaluate a callback function in the V8 engine.
+ * @param {!Object} clientElement The plugin containing the V8 engine.
+ * @param {!function(...): *} callback A function to call.
+ * @param {!Object} thisArg The value to be bound to "this".
+ * @param {!Array.<*>} args The arguments to pass to the callback.
+ * @return {*} The result of calling the callback.
+ */
+o3djs.util.callV8 = function(clientElement, callback, thisArg, args) {
+ // Sometimes a function will be converted to a string like this:
+ // function foo(a, b) { ... }
+ // In this case, convert to this form:
+ // function(a, b) { ... }
+ var functionString = callback.toString();
+ functionString = functionString.replace(o3djs.util.fixFunctionString_,
+ 'function($1)');
+
+ // Make a V8 function that will invoke the callback.
+ var v8Code =
+ 'function(thisArg, args) {\n' +
+ ' var localArgs = [];\n' +
+ ' var numArgs = args.length;\n' +
+ ' for (var i = 0; i < numArgs; ++i) {\n' +
+ ' localArgs.push(args[i]);\n' +
+ ' }\n' +
+ ' var func = ' + functionString + ';\n' +
+ ' return func.apply(thisArg, localArgs);\n' +
+ '}\n';
+
+ // Evaluate the function in V8.
+ var v8Function = clientElement.eval(v8Code);
+ return v8Function(thisArg, args);
+};
+
+/**
+ * A regex to remove .. from a URI.
+ * @private
+ * @type {!RegExp}
+ */
+o3djs.util.stripDotDot_ = /\/[^\/]+\/\.\./;
+
+/**
+ * Turn a URI into an absolute URI.
+ * @param {string} uri The URI.
+ * @return {string} The absolute URI.
+ */
+o3djs.util.toAbsoluteUri = function(uri) {
+ if (uri.indexOf('://') == -1) {
+ var baseUri = document.location.toString();
+ var lastSlash = baseUri.lastIndexOf('/');
+ if (lastSlash != -1) {
+ baseUri = baseUri.substring(0, lastSlash);
+ }
+ uri = baseUri + '/' + uri;
+ }
+
+ do {
+ var lastUri = uri;
+ uri = uri.replace(o3djs.util.stripDotDot_, '');
+ } while (lastUri !== uri);
+
+ return uri;
+};
+
+/**
+ * The script URIs.
+ * @type {!Array.<string>}
+ */
+o3djs.util.scriptUris_ = [];
+
+/**
+ * Add a script URI. Scripts that are referenced from script tags that are
+ * within this URI are automatically loaded into the alternative JavaScript
+ * main JavaScript engine. Do not include directories of scripts that are
+ * included with o3djs.require. These are always available. This mechanism
+ * is not able to load scripts in a different domain from the document.
+ * @param {string} uri The URI.
+ */
+o3djs.util.addScriptUri = function(uri) {
+ o3djs.util.scriptUris_.push(o3djs.util.toAbsoluteUri(uri));
+};
+
+/**
+ * Determine whether a URI is a script URI that should be loaded into the
+ * alternative main JavaScript engine.
+ * @param {string} uri The URI.
+ * @return {boolean} Whether it is a script URI.
+ */
+o3djs.util.isScriptUri = function(uri) {
+ uri = o3djs.util.toAbsoluteUri(uri);
+ for (var i = 0; i < o3djs.util.scriptUris_.length; ++i) {
+ var scriptUri = o3djs.util.scriptUris_[i];
+ if (uri.substring(0, scriptUri.length) === scriptUri) {
+ return true;
+ }
+ }
+ return false;
+};
+
+/**
+ * Concatenate the text of all the script tags in the document and invokes
+ * the callback when complete. This function is asynchronous if any of the
+ * script tags reference JavaScript through a URI.
+ * @return {string} The script tag text.
+ */
+o3djs.util.getScriptTagText_ = function() {
+ var scriptTagText = '';
+ var scriptElements = document.getElementsByTagName('script');
+ for (var i = 0; i < scriptElements.length; ++i) {
+ var scriptElement = scriptElements[i];
+ if (scriptElement.type === '' ||
+ scriptElement.type === 'text/javascript') {
+ if ('text' in scriptElement && scriptElement.text) {
+ scriptTagText += scriptElement.text;
+ }
+ if ('src' in scriptElement && scriptElement.src &&
+ o3djs.util.isScriptUri(scriptElement.src)) {
+ // It would be better to make this an asynchronous load but the script
+ // file is very likely to be in the browser cache because it should
+ // have just been loaded via the browser script tag.
+ scriptTagText += o3djs.io.loadTextFileSynchronous(scriptElement.src);
+ }
+ }
+ }
+ return scriptTagText;
+};
+
+/**
+ * Creates a client element. In other words it creates an <OBJECT> tag for
+ * o3d. Note that the browser may not have initialized the plugin before
+ * returning.
+ * @param {!Element} element The DOM element under which the client element
+ * will be appended.
+ * @param {string} opt_features A comma separated list of the
+ * features you need for your application. The current list of features:
+ * <li>FloatingPointTextures: Includes the formats R32F, ABGR16F and
+ * ABGR32F</li>
+ * The features are case sensitive.
+ * @param {string} opt_requestVersion version string in
+ * "major.minor.revision.build" format. You can leave out any non-important
+ * numbers for example "3" = request major version 3, "2.4" = request major
+ * version 2, minor version 4. If no string is passed in the newest version
+ * of the plugin will be created.
+ * @return {Element} O3D element or null if requested version is not
+ * available.
+ */
+o3djs.util.createClient = function(element, opt_features, opt_requestVersion) {
+ if (opt_requestVersion &&
+ !o3djs.util.requiredVersionAvailable(opt_requestVersion)) {
+ return null;
+ }
+ var objElem;
+ // TODO: Use opt_requiredVersion to set a version so the plugin
+ // can make sure it offers that version of the API.
+ // Note: The IE version of the plug-in does not receive attributes during
+ // construction, unless the innerHTML construction style is used.
+ if (o3djs.base.IsMSIE()) {
+ element.innerHTML =
+ '<OBJECT ' +
+ 'WIDTH="100%" HEIGHT="100%"' +
+ 'CLASSID="CLSID:9666A772-407E-4F90-BC37-982E8160EB2D">' +
+ '<PARAM name="o3d_features" value="' + opt_features + '"/>' +
+ '<PARAM name="version" value="' + opt_requestVersion + '"/>' +
+ '</OBJECT>';
+ objElem = element.childNodes[0];
+ } else {
+ objElem = document.createElement('object');
+ objElem.type = 'application/vnd.o3d.auto';
+ objElem.style.width = '100%';
+ objElem.style.height = '100%';
+ objElem.setAttribute('o3d_features', opt_features);
+ objElem.version = opt_requestVersion;
+ element.appendChild(objElem);
+ }
+
+ return objElem;
+};
+
+/**
+ * Finds all divs with the an id that starts with "o3d" and inserts a client
+ * area inside.
+ *
+ * NOTE: the size of the client area is always set to 100% which means the div
+ * must have its size set or managed by the browser. Examples:
+ *
+ * -- A div of a specific size --
+ * &lt;div id="o3d" style="width:800px; height:600px">&lt;/div>
+ *
+ * -- A div that fills its containing element --
+ * &lt;div id="o3d" style="width:100%; height:100%">&lt;/div>
+ *
+ * In both cases, a DOCTYPE is probably required.
+ *
+ * You can also request certain features by adding the attribute
+ * 'o3d_features' as in
+ *
+ * &lt;div id="o3d" o3d_features="FloatingPointTextures">&lt;/div>
+ *
+ * This allows you to specify different features per area. Otherwise you can
+ * request features as an argument to this function.
+ *
+ * @param {!function(Array.<!Element>): void} callback Function to call when
+ * client objects have been created.
+ * @param {string} opt_features A comma separated list of the
+ * features you need for your application. The current list of features:
+ *
+ * <li>FloatingPointTextures: Includes the formats R32F, ABGR16F and
+ * ABGR32F</li>
+ * <li>LargeGeometry: Allows buffers to have more than 65534 elements.</li>
+ * <li>NotAntiAliased: Turns off anti-aliasing</li>
+ * <li>InitStatus=X: Where X is a number. Allows simulatation of the plugin
+ * failing</li>
+ *
+ * The features are case sensitive.
+ * @param {string} opt_requiredVersion version string in
+ * "major.minor.revision.build" format. You can leave out any
+ * non-important numbers for example "3" = require major version 3,
+ * "2.4" = require major version 2, minor version 4. If no string is
+ * passed in the version of the needed by this version of the javascript
+ * libraries will be created.
+ * @param {!function(!o3d.Renderer.InitStatus, string, string, string): void}
+ * opt_failureCallback Function to call if the plugin does not exist, if the
+ * required version is not installed, or if for some other reason the plugin
+ * can not start. If this function is not specified or is null the default
+ * behavior of leading the user to the download page will be provided.
+ * @param {string} opt_id The id to look for. This can be a regular
+ * expression. The default is "^o3d".
+ * @param {string} opt_tag The type of tag to look for. The default is "div".
+ */
+o3djs.util.makeClients = function(callback,
+ opt_features,
+ opt_requiredVersion,
+ opt_failureCallback,
+ opt_id,
+ opt_tag) {
+ var tag = opt_tag || 'div';
+ var id = opt_id || '^o3d';
+ opt_failureCallback = opt_failureCallback || o3djs.util.informPluginFailure;
+ opt_requiredVersion = opt_requiredVersion ||
+ o3djs.util.REQUIRED_VERSION;
+ if (!o3djs.util.requiredVersionAvailable(opt_requiredVersion)) {
+ opt_failureCallback(o3djs.util.rendererInitStatus.NO_PLUGIN, '', id, tag);
+ } else {
+ var clientElements = [];
+ var elements = document.getElementsByTagName(tag);
+ var mainClientElement = null;
+ for (var ee = 0; ee < elements.length; ++ee) {
+ var element = elements[ee];
+ if (element.id && element.id.match(id)) {
+ var features = opt_features;
+ if (!features) {
+ var o3d_features = element.getAttribute('o3d_features');
+ if (o3d_features) {
+ features = o3d_features;
+ } else {
+ features = '';
+ }
+ }
+
+ var objElem = o3djs.util.createClient(element, features);
+ clientElements.push(objElem);
+
+ // If the callback is to be invoked in an embedded JavaScript engine,
+ // one element must be identified with the id 'o3d'. This callback
+ // will be invoked in the element identified as such.
+ if (element.id === 'o3d') {
+ mainClientElement = objElem;
+ }
+ }
+ }
+
+ // Chrome 1.0 sometimes doesn't create the plugin instance. To work
+ // around this, force a re-layout by changing the plugin size until it
+ // is loaded. We toggle between 1 pixel and 100% until the plugin has
+ // loaded.
+ var chromeWorkaround = o3djs.base.IsChrome10();
+ {
+ // Wait for the browser to initialize the clients.
+ var clearId = window.setInterval(function() {
+ var initStatus = 0;
+ var error = '';
+ var o3d;
+ for (var cc = 0; cc < clientElements.length; ++cc) {
+ var element = clientElements[cc];
+ o3d = element.o3d;
+ if (!o3d) {
+ if (chromeWorkaround) {
+ if (element.style.width != '100%') {
+ element.style.width = '100%';
+ } else {
+ element.style.width = '1px';
+ }
+ }
+ return;
+ }
+ if (chromeWorkaround && element.style.width != '100%') {
+ // The plugin has loaded but it may not be the right size yet.
+ element.style.width = '100%';
+ return;
+ }
+ var status = clientElements[cc].client.rendererInitStatus;
+ // keep the highest status. This is the worst status.
+ if (status > initStatus) {
+ initStatus = status;
+ error = clientElements[cc].client.lastError;
+ }
+ }
+
+ window.clearInterval(clearId);
+
+ // If the plugin could not initialize the graphics delete all of
+ // the plugin objects
+ if (initStatus > 0 && initStatus != o3d.Renderer.SUCCESS) {
+ for (var cc = 0; cc < clientElements.length; ++cc) {
+ var clientElement = clientElements[cc];
+ clientElement.parentNode.removeChild(clientElement);
+ }
+ opt_failureCallback(initStatus, error, id, tag);
+ } else {
+ o3djs.base.snapshotProvidedNamespaces();
+
+ // TODO: Is this needed with the new event code?
+ for (var cc = 0; cc < clientElements.length; ++cc) {
+ o3djs.base.initV8(clientElements[cc]);
+ o3djs.event.startKeyboardEventSynthesis(clientElements[cc]);
+ o3djs.error.setDefaultErrorHandler(clientElements[cc].client);
+ }
+ o3djs.base.init(clientElements[0]);
+
+ switch (o3djs.util.mainEngine_) {
+ case o3djs.util.Engine.BROWSER:
+ callback(clientElements);
+ break;
+ case o3djs.util.Engine.V8:
+ if (!mainClientElement) {
+ throw 'V8 engine was requested but there is no element with' +
+ ' the id "o3d"';
+ }
+
+ // Retreive the code from the script tags and eval it in V8 to
+ // duplicate the browser environment.
+ var scriptTagText = o3djs.util.getScriptTagText_();
+ mainClientElement.eval(scriptTagText);
+
+ // Invoke the vallback in V8.
+ o3djs.util.callV8(mainClientElement,
+ callback,
+ o3djs.global,
+ [clientElements]);
+ break;
+ default:
+ throw 'Unknown engine ' + o3djs.util.mainEngine_;
+ }
+ }
+ }, 10);
+ }
+ }
+};
+
diff --git a/o3d/samples/particles.html b/o3d/samples/particles.html
new file mode 100644
index 0000000..e3670db
--- /dev/null
+++ b/o3d/samples/particles.html
@@ -0,0 +1,497 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+Particles.
+
+This example shows using the javascript particle library.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Particles.
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.quaternions');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.particles');
+o3djs.require('o3djs.loader');
+
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_viewInfo;
+var g_pack;
+var g_particleSystem;
+var g_clockParam;
+var g_textures = [];
+var g_emitters = []; // so we can find in the debugger to edit in real time.
+
+/**
+ * Loads a texture.
+ * @param {!o3djs.loader.Loader} loader Loader to use to load texture.
+ * @param {string} url relativel url of texture.
+ * @param {number} index Index at which to record texture.
+ */
+function loadTexture(loader, url, index) {
+ loader.loadTexture(
+ g_pack,
+ o3djs.util.getAbsoluteURI(url),
+ function(texture, exception) {
+ if (exception) {
+ alert(exception);
+ } else {
+ g_textures[index] = texture;
+ }
+ });
+}
+
+/**
+ * Returns a deterministic pseudorandom number bewteen 0 and 1
+ * @return {number} a random number between 0 and 1
+ */
+var g_randSeed = 0;
+var g_randRange = Math.pow(2, 32);
+function pseudoRandom() {
+ return (g_randSeed = (134775813 * g_randSeed + 1) % g_randRange) /
+ g_randRange;
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ // These are here so they are shared by both V8 and the browser.
+ window.g_finished = false; // for selenium
+ window.g_timeMult = 1;
+ window.g_clock = 0;
+
+ // Comment out the line below to run the sample in the browser JavaScript
+ // engine. This may be helpful for debugging.
+ o3djs.util.setMainEngine(o3djs.util.Engine.V8);
+
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and creates one shape.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+ window.g_client = g_client = o3dElement.client;
+
+ // Creates a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(30), // 30 degree fov.
+ g_client.width / g_client.height,
+ 0.1, // Near plane.
+ 5000); // Far plane.
+
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [500, 1000, 1000], // eye
+ [0, 200, 0], // target
+ [0, 1, 0]); // up
+
+
+
+ // Load textures. This happens asynchronously.
+ var loader = o3djs.loader.createLoader(initStep3);
+ loadTexture(loader, 'assets/particle-anim.png', 0);
+ loadTexture(loader, 'assets/ripple.png', 1);
+ loader.finish();
+}
+
+function initStep3() {
+ // Normally we wouldn't pass in a clock and the particle system would handle
+ // this for me but for selenium testing we need to be able to control the
+ // clock so we're passing in our own clock param.
+ var paramObject = g_pack.createObject('ParamObject');
+ g_clockParam = paramObject.createParam('clock', 'ParamFloat');
+
+ // Normally we wouldn't pass in a random function but for selenium we need
+ // the particle system to produce the exact same results each time so
+ // we're passing in a predictable random function.
+ g_particleSystem = o3djs.particles.createParticleSystem(g_pack,
+ g_viewInfo,
+ g_clockParam,
+ pseudoRandom);
+ setupFlame();
+ setupNaturalGasFlame();
+ setupSmoke();
+ setupWhiteEnergy();
+ setupGoogle();
+ setupRain();
+ setupRipples();
+ setupAnim();
+ setupBall();
+ setupCube();
+
+ // Setup an onrender callback for animation.
+ g_client.setRenderCallback(onrender);
+
+ window.g_finished = true; // for selenium testing.
+}
+
+function setupFlame() {
+ var transform = g_pack.createObject('Transform');
+ transform.parent = g_client.root;
+ transform.translate(-300, 0, 0);
+
+ var emitter = g_particleSystem.createParticleEmitter();
+ g_emitters.push(emitter);
+ emitter.setState(o3djs.particles.ParticleStateIds.ADD);
+ emitter.setColorRamp(
+ [1, 1, 0, 1,
+ 1, 0, 0, 1,
+ 0, 0, 0, 1,
+ 0, 0, 0, 0.5,
+ 0, 0, 0, 0]);
+ emitter.setParameters({
+ numParticles: 20,
+ lifeTime: 2,
+ timeRange: 2,
+ startSize: 50,
+ endSize: 90,
+ velocity:[0, 60, 0], velocityRange: [15, 15, 15],
+ worldAcceleration: [0, -20, 0],
+ spinSpeedRange: 4});
+ transform.addShape(emitter.shape);
+}
+
+function setupNaturalGasFlame() {
+ var transform = g_pack.createObject('Transform');
+ transform.parent = g_client.root;
+ transform.translate(-200, 0, 0);
+
+ var emitter = g_particleSystem.createParticleEmitter();
+ g_emitters.push(emitter);
+ emitter.setState(o3djs.particles.ParticleStateIds.ADD);
+ emitter.setColorRamp(
+ [0.2, 0.2, 1, 1,
+ 0, 0, 1, 1,
+ 0, 0, 1, 0.5,
+ 0, 0, 1, 0]);
+ emitter.setParameters({
+ numParticles: 20,
+ lifeTime: 2,
+ timeRange: 2,
+ startSize: 50,
+ endSize: 20,
+ velocity:[0, 60, 0],
+ worldAcceleration: [0, -20, 0],
+ spinSpeedRange: 4});
+ transform.addShape(emitter.shape);
+}
+
+function setupSmoke() {
+ var transform = g_pack.createObject('Transform');
+ transform.parent = g_client.root;
+ transform.translate(-100, 0, 0);
+
+ var emitter = g_particleSystem.createParticleEmitter();
+ g_emitters.push(emitter);
+ emitter.setState(o3djs.particles.ParticleStateIds.BLEND);
+ emitter.setColorRamp(
+ [0, 0, 0, 1,
+ 0, 0, 0, 0]);
+ emitter.setParameters({
+ numParticles: 20,
+ lifeTime: 2,
+ timeRange: 2,
+ startSize: 100,
+ endSize: 150,
+ velocity: [0, 200, 0], velocityRange: [20, 0, 20],
+ worldAcceleration: [0, -25, 0],
+ spinSpeedRange: 4});
+ transform.addShape(emitter.shape);
+}
+
+function setupWhiteEnergy() {
+ var transform = g_pack.createObject('Transform');
+ transform.parent = g_client.root;
+ transform.translate(0, 0, 0);
+
+ var emitter = g_particleSystem.createParticleEmitter();
+ g_emitters.push(emitter);
+ emitter.setState(o3djs.particles.ParticleStateIds.ADD);
+ emitter.setColorRamp(
+ [1, 1, 1, 1,
+ 1, 1, 1, 0]);
+ emitter.setParameters({
+ numParticles: 80,
+ lifeTime: 2,
+ timeRange: 2,
+ startSize: 100,
+ endSize: 100,
+ positionRange: [100, 0, 100],
+ velocityRange: [20, 0, 20]});
+ transform.addShape(emitter.shape);
+}
+
+function setupRipples() {
+ var transform = g_pack.createObject('Transform');
+ transform.parent = g_client.root;
+ transform.translate(-200, 0, 300);
+
+ var emitter = g_particleSystem.createParticleEmitter(g_textures[1]);
+ g_emitters.push(emitter);
+ emitter.setState(o3djs.particles.ParticleStateIds.BLEND);
+ emitter.setColorRamp(
+ [0.7, 0.8, 1, 1,
+ 1, 1, 1, 0]);
+ emitter.setParameters({
+ numParticles: 20,
+ lifeTime: 2,
+ timeRange: 2,
+ startSize: 50,
+ endSize: 200,
+ positionRange: [100, 0, 100],
+ billboard: false});
+ transform.addShape(emitter.shape);
+}
+
+function setupGoogle() {
+ var image = [
+ '.XXXX...XXXXX...XXXXX.',
+ 'X....X.......X..X....X',
+ 'X....X...XXXXX..X....X',
+ 'X....X.......X..X....X',
+ '.XXXX...XXXXX...XXXXX.'];
+ var height = image.length;
+ var width = image[0].length;
+
+ // Make an array of positions based on the text image.
+ var positions = [];
+ for (var yy = 0; yy < height; ++yy) {
+ for (var xx = 0; xx < width; ++xx) {
+ if (image[yy].substring(xx, xx + 1) == 'X') {
+ positions.push([(xx - width * 0.5) * 10,
+ -(yy - height * 0.5) * 10]);
+ }
+ }
+ }
+ var transform = g_pack.createObject('Transform');
+ transform.parent = g_client.root;
+ transform.translate(100, 200, 0);
+
+ var emitter = g_particleSystem.createParticleEmitter();
+ g_emitters.push(emitter);
+ emitter.setState(o3djs.particles.ParticleStateIds.ADD);
+ emitter.setColorRamp(
+ [1, 0, 0, 1,
+ 0, 1, 0, 1,
+ 0, 0, 1, 1,
+ 1, 1, 0, 0]);
+ emitter.setParameters({
+ numParticles: positions.length * 4,
+ lifeTime: 2,
+ timeRange: 2,
+ startSize: 25,
+ endSize: 50,
+ positionRange: [2, 0, 2],
+ velocity: [1, 0, 1]},
+ function(particleIndex, parameters) {
+ //var index = particleIndex;
+ var index = Math.floor(pseudoRandom() * positions.length);
+ index = Math.min(index, positions.length - 1);
+ parameters.position[0] = positions[index][0];
+ parameters.position[1] = positions[index][1];
+ });
+ transform.addShape(emitter.shape);
+}
+
+function setupRain() {
+ var transform = g_pack.createObject('Transform');
+ transform.parent = g_client.root;
+ transform.translate(200, 200, 0);
+
+ var emitter = g_particleSystem.createParticleEmitter();
+ g_emitters.push(emitter);
+ emitter.setState(o3djs.particles.ParticleStateIds.BLEND);
+ emitter.setColorRamp(
+ [0.2, 0.2, 1, 1]);
+ emitter.setParameters({
+ numParticles: 80,
+ lifeTime: 2,
+ timeRange: 2,
+ startSize: 5,
+ endSize: 5,
+ positionRange: [100, 0, 100],
+ velocity: [0,-150,0]});
+ transform.addShape(emitter.shape);
+}
+
+function setupAnim(texture) {
+ var transform = g_pack.createObject('Transform');
+ transform.parent = g_client.root;
+ transform.translate(300, 0, 0);
+
+ var emitter = g_particleSystem.createParticleEmitter(g_textures[0]);
+ g_emitters.push(emitter);
+ emitter.setColorRamp(
+ [1, 1, 1, 1,
+ 1, 1, 1, 1,
+ 1, 1, 1, 0]);
+ emitter.setParameters({
+ numParticles: 20,
+ numFrames: 8,
+ frameDuration: 0.25,
+ frameStartRange: 8,
+ lifeTime: 2,
+ timeRange: 2,
+ startSize: 50,
+ endSize: 90,
+ positionRange: [10, 10, 10],
+ velocity:[0, 200, 0], velocityRange: [75, 15, 75],
+ acceleration: [0, -150, 0],
+ spinSpeedRange: 1});
+ transform.addShape(emitter.shape);
+}
+
+function setupBall() {
+ var transform = g_pack.createObject('Transform');
+ transform.parent = g_client.root;
+ transform.translate(-400, 0, -200);
+
+ var emitter = g_particleSystem.createParticleEmitter(g_textures[1]);
+ g_emitters.push(emitter);
+ emitter.setState(o3djs.particles.ParticleStateIds.BLEND);
+ emitter.setColorRamp(
+ [1, 1, 1, 1,
+ 1, 1, 1, 0]);
+ emitter.setParameters({
+ numParticles: 300,
+ lifeTime: 2,
+ timeRange: 2,
+ startSize: 10,
+ endSize: 50,
+ colorMult: [1, 1, 0.5, 1], colorMultRange: [0, 0, 0.5, 0],
+ billboard: false},
+ function(particleIndex, parameters) {
+ var matrix = g_math.matrix4.rotationY(pseudoRandom() * Math.PI * 2);
+ g_math.matrix4.rotateX(matrix, pseudoRandom() * Math.PI);
+ var position = g_math.matrix4.transformDirection(matrix, [0, 100, 0]);
+ parameters.position = position;
+ parameters.orientation = o3djs.quaternions.rotationToQuaternion(matrix);
+ });
+ transform.addShape(emitter.shape);
+}
+
+function setupCube() {
+ var transform = g_pack.createObject('Transform');
+ transform.parent = g_client.root;
+ transform.translate(200, 0, -300);
+
+ var emitter = g_particleSystem.createParticleEmitter(g_textures[1]);
+ g_emitters.push(emitter);
+ emitter.setState(o3djs.particles.ParticleStateIds.ADD);
+ emitter.setColorRamp(
+ [1, 1, 1, 1,
+ 0, 0, 1, 1,
+ 1, 1, 1, 0]);
+ emitter.setParameters({
+ numParticles: 300,
+ lifeTime: 2,
+ timeRange: 2,
+ startSize: 10,
+ endSize: 50,
+ colorMult: [0.8, 0.9, 1, 1],
+ billboard: false},
+ function(particleIndex, parameters) {
+ var matrix = g_math.matrix4.rotationY(
+ Math.floor(pseudoRandom() * 4) * Math.PI * 0.5);
+ g_math.matrix4.rotateX(matrix,
+ Math.floor(pseudoRandom() * 3) * Math.PI * 0.5);
+ parameters.orientation = o3djs.quaternions.rotationToQuaternion(matrix);
+ var position = g_math.matrix4.transformDirection(
+ matrix,
+ [pseudoRandom() * 200 - 100, 100, pseudoRandom() * 200 - 100]);
+ parameters.position = position;
+ });
+ transform.addShape(emitter.shape);
+}
+
+/**
+ * Called every frame.
+ * @param {!o3d.RenderEvent} renderEvent Rendering Information.
+ */
+function onrender(renderEvent) {
+ var elapsedTime = renderEvent.elapsedTime;
+ window.g_clock += elapsedTime * window.g_timeMult;
+
+ var cameraClock = window.g_clock * 0.3;
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [Math.sin(cameraClock) * 1500, 500, Math.cos(cameraClock) * 1500], // eye
+ [0, 100, 0], // target
+ [0, 1, 0]); // up
+
+ g_clockParam.value = window.g_clock;
+}
+
+/**
+ * Remove any callbacks so they don't get called after the page has unloaded.
+ */
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+</script>
+</head>
+<body onload="init();" onunload="unload();">
+<h1>Particles</h1>
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 800px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/samples/phongshading.html b/o3d/samples/phongshading.html
new file mode 100644
index 0000000..479cdf6
--- /dev/null
+++ b/o3d/samples/phongshading.html
@@ -0,0 +1,367 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+O3D Tutorial B5
+
+In this tutorial, we generate a simple spherical mesh using Javascript and
+shade it using Phong illumination.
+
+We calculate the various lighting components (ambient,diffuse,specular)
+and combine them in our vertex/pixel shaders to draw the correct color for each
+pixel in the scene.
+
+The scene is illuminated by a single red light and the sphere is white.
+(ie ambient, diffuse, and specular reflection constants of the material = 1)
+
+In this sample, we generate the projection matrix dynamically from the size
+of the o3d plugin.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Tutorial B5: Phong Shading
+</title>
+<!-- Our javascript code -->
+<script type="text/javascript" src="o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+
+// Events
+// Run the init() function once the page has finished loading.
+// unload() when the page is unloaded.
+window.onload = init;
+window.onunload = unload;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_o3dElement;
+var g_viewInfo;
+var g_pack;
+var g_o3dWidth = -1;
+var g_o3dHeight = -1;
+
+// Our view and projection matrices
+// The view matrix transforms objects from world space to view space.
+var g_view_matrix;
+// The projection matrix projects objects from view space to the screen.
+var g_proj_matrix;
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D, loads the effect, and draws the sphere.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ g_o3dElement = clientElements[0];
+ g_o3d = g_o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = g_o3dElement.client;
+
+ // Create a g_pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ /* Load the effect for our sphere from our file.
+ Effects, stored in a hidden textarea for simplicity, contain the
+ functions that define the vertex and pixel shaders used by our shape.
+
+ Here, we calculate phong illumination in our vertex shader and pass the
+ resultant color to our pixel shader, which does nothing except output its
+ given (input) color.
+ */
+ var effect = g_pack.createObject('Effect');
+ effect.loadFromFXString(document.getElementById('shader').value);
+
+ // Create a Material for the effect.
+ var myMaterial = g_pack.createObject('Material');
+
+ // Apply our effect to this material.
+ myMaterial.effect = effect;
+
+ // Set the material's drawList
+ myMaterial.drawList = g_viewInfo.performanceDrawList;
+
+ // Create the params the effect needs on the material.
+ effect.createUniformParameters(myMaterial);
+
+ // Create a sphere at the origin with radius 1.
+ var myShape = o3djs.primitives.createSphere(g_pack,
+ myMaterial,
+ 1,
+ 70,
+ 70);
+
+ // Set up the individual parameters in our effect file.
+
+ // Light position
+ var light_pos_param = myMaterial.getParam('light_pos');
+ light_pos_param.value = [10, 10, 20];
+
+ // Phong components of the light source
+ var light_ambient_param = myMaterial.getParam('light_ambient');
+ var light_diffuse_param = myMaterial.getParam('light_diffuse');
+ var light_specular_param = myMaterial.getParam('light_specular');
+
+ // White ambient light
+ light_ambient_param.value = [0.04, 0.04, 0.04, 1];
+ // Reddish diffuse light
+ light_diffuse_param.value = [0.8, 0, 0, 1];
+ // White specular light
+ light_specular_param.value = [0.5, 0.5, 0.5, 1];
+
+ // Shininess of the material (for specular lighting)
+ var shininess_param = myMaterial.getParam('shininess');
+ shininess_param.value = 30.0;
+
+ // Position of the camera.
+ // (should be the same as the 'eye' position given below)
+ var camera_pos_param = myMaterial.getParam('camera_pos');
+ // Camera is at (0, 0, 3).
+ camera_pos_param.value = [0, 0, 3];
+
+ // Now create our view matrix by defining coordinates for the
+ // target, eye, and up vectors and using the g_math.matrix4.lookAt(..)
+ // helper function to create the matrix.
+
+ // Eye-position, this is where our camera is at.
+ var eye = [0, 0, 3];
+
+ // Target, this is where our camera is pointed at.
+ var target = [0, 0, 0];
+
+ // Up-vector, this tells the camera which direction is 'up'.
+ // We define the positive y-direction to be up in this example.
+ var up = [0, 1, 0];
+
+ g_view_matrix = g_math.matrix4.lookAt(eye, target, up);
+
+ // Generate the projection and viewProjection matrices based
+ // on the g_o3d plugin size by calling resize().
+ resize();
+
+ // Now attach the sphere to the root of the transform graph.
+ var root = g_client.root;
+ root.addShape(myShape);
+
+ // If we don't check the size of the client area every frame we don't get a
+ // chance to adjust the perspective matrix fast enough to keep up with the
+ // browser resizing us.
+ g_client.setRenderCallback(resize);
+}
+
+// Generates the projection matrix based on the size of the o3d plugin
+// and calculates the view-projection matrix.
+function resize() {
+ var newWidth = g_client.width;
+ var newHeight = g_client.height;
+
+ if (newWidth != g_o3dWidth || newHeight != g_o3dHeight) {
+ g_o3dWidth = newWidth;
+ g_o3dHeight = newHeight;
+
+ // Create our projection matrix, with a vertical field of view of 45 degrees
+ // a near clipping plane of 0.1 and far clipping plane of 100.
+ g_proj_matrix = g_math.matrix4.perspective(
+ g_math.degToRad(45),
+ g_o3dWidth / g_o3dHeight,
+ 0.1,
+ 100);
+
+ // Set the view and projection matrix
+ g_viewInfo.drawContext.view = g_view_matrix;
+ g_viewInfo.drawContext.projection = g_proj_matrix;
+ }
+}
+
+/**
+ * Removes any callbacks so they don't get called after the page has unloaded.
+ */
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+</script>
+</head>
+<body>
+<table width="100%" style="height:100%;">
+ <tr><td>
+<h1>Phong shading</h1>
+<p>
+This tutorial shows how we generate a custom mesh and
+perform Phong illumination using a shader.
+</p>
+<p>
+This sample displays a Phong shaded white sphere lit by a single red light.
+</p>
+<table id="container" width="90%" style="height:60%;"><tr><td height="100%">
+<!-- Start of g_o3d plugin -->
+<div id="o3d" style="width: 600px; height: 600px;"></div>
+<!-- End of g_o3d plugin -->
+</td></tr></table>
+<!-- a simple way to get a multiline string -->
+<textarea id="shader" name="shader" cols="80" rows="20"
+ style="display: none;">
+// The 4x4 world view projection matrix.
+float4x4 worldViewProjection : WorldViewProjection;
+
+// positions of the light and camera
+float3 light_pos;
+float3 camera_pos;
+
+// phong lighting components of the light source
+float4 light_ambient;
+float4 light_diffuse;
+float4 light_specular;
+
+// shininess of the material. (for specular lighting)
+float shininess;
+
+// input parameters for our vertex shader
+struct VertexShaderInput {
+ float4 postion : POSITION;
+ float3 normal : NORMAL;
+ float4 color : COLOR;
+};
+
+// input parameters for our pixel shader
+// also the output parameters for our vertex shader
+struct PixelShaderInput {
+ float4 postion : POSITION;
+ float3 lightVector : TEXCOORD0;
+ float3 normal : TEXCOORD1;
+ float3 viewPosition : TEXCOORD2;
+ float4 color : COLOR;
+};
+
+/**
+ * Vertex Shader - vertex shader for phong illumination
+ */
+PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ /**
+ * We use the standard phong illumination equation here.
+ * We restrict (clamp) the dot products so that we
+ * don't get any negative values.
+ * All vectors are normalized for proper calculations.
+ *
+ * The output color is the summation of the
+ * ambient, diffuse, and specular contributions.
+ *
+ * Note that we have to transform each vertex and normal
+ * by the world view projection matrix first.
+ */
+ PixelShaderInput output;
+
+ output.postion = mul(input.postion, worldViewProjection);
+
+ /**
+ * lightVector - light vector
+ * normal - normal vector
+ * viewPosition - view vector (from camera)
+ */
+
+ // NOTE: In this case we do not need to multiply by any matrices since the
+ // WORLD transformation matrix is the identity. If you were moving the
+ // object such that the WORLD transform matrix was not the identity, you
+ // would need to multiply the normal by the WORLDINVERSETTRANSFORM matrix
+ // since the normal is in object space. Other values (light_pos, camera_pos)
+ // are already in world space.
+ float3 lightVector = light_pos - input.postion.xyz;
+ float3 normal = input.normal;
+ float3 viewPosition = camera_pos - input.postion.xyz;
+
+ output.lightVector = lightVector;
+ output.normal = normal;
+ output.viewPosition = viewPosition;
+ output.color = input.color;
+ return output;
+}
+
+/**
+ * Pixel Shader
+ */
+float4 pixelShaderFunction(PixelShaderInput input): COLOR {
+ float3 lightVector = normalize(input.lightVector);
+ float3 normal = normalize(input.normal);
+ float3 viewPosition = normalize(input.viewPosition);
+ float3 halfVector = normalize(lightVector + viewPosition);
+
+ // use lit function to calculate phong shading
+ // x component contains the ambient coefficient
+ // y component contains the diffuse coefficient:
+ // max(dot(normal, lightVector),0)
+ // z component contains the specular coefficient:
+ // dot(normal, lightVector) < 0 || dot(normal, halfVector) < 0 ?
+ // 0 : pow(dot(normal, halfVector), shininess)
+ // NOTE: This is actually Blinn-Phong shading, not Phong shading
+ // which would use the reflection vector instead of the half vector
+
+ float4 phong_coeff = lit(dot(normal, lightVector),
+ dot(normal, halfVector), shininess);
+
+ float4 ambient = light_ambient * phong_coeff.x * input.color;
+ float4 diffuse = light_diffuse * phong_coeff.y * input.color;
+ float4 specular = light_specular * phong_coeff.z * input.color;
+
+ return ambient + diffuse + specular;
+}
+
+// Here we tell our effect file *which* functions are
+// our vertex and pixel shaders.
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
+</textarea>
+</td></tr></table>
+</body>
+</html>
diff --git a/o3d/samples/picking.html b/o3d/samples/picking.html
new file mode 100644
index 0000000..cd9ddd1
--- /dev/null
+++ b/o3d/samples/picking.html
@@ -0,0 +1,288 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+O3D Picking Example.
+
+This example shows one way to implement picking. Because O3D is shader
+agnostic we can't handle picking automatically since we have no way of knowing
+what the developer is going to do with their shaders. On the other hand, we can
+provide various functions that make it possible to do your own picking. Only you
+know which objects are pickable and which are not. For example if you are
+making an RTS game, only you would know that units are pickable but ground and
+explosions are not and that neither is your HUD.
+
+It's possible that someone, maybe us, will create an engine to use o3d
+that given a bunch of restrictions and flags on the data it excepts can
+do picking in a more automatic way but that is not the goal of the o3d api.
+Its goal is to provide a LOW-LEVEL shader agnostic API.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+O3D Picking Example.
+</title>
+<!-- Include default javascript library functions-->
+<script type="text/javascript" src="o3djs/base.js"></script>
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.pack');
+o3djs.require('o3djs.camera');
+o3djs.require('o3djs.picking');
+o3djs.require('o3djs.scene');
+
+// Events
+// init() once the page has finished loading.
+// unload() when the page is unloaded.
+window.onload = init;
+window.onunload = unload;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_pack;
+var g_viewInfo;
+var g_treeInfo; // information about the transform graph.
+var g_pickInfoElem;
+var g_selectedInfo = null;
+var g_flashTimer = 0;
+var g_highlightMaterial;
+var g_highlightShape;
+var g_finished = false; // for selenium testing.
+
+function updateInfo() {
+ if (!g_treeInfo) {
+ g_treeInfo = o3djs.picking.createTransformInfo(g_client.root,
+ null);
+ }
+ g_treeInfo.update();
+}
+
+function unSelectAll() {
+ if (g_selectedInfo) {
+ // Remove it from the transform of the selected object.
+ g_selectedInfo.shapeInfo.parent.transform.removeShape(g_highlightShape);
+ // Remove everything related to it.
+ o3djs.shape.deleteDuplicateShape(g_highlightShape, g_pack);
+ g_highlightShape = null;
+ g_selectedInfo = null;
+ }
+}
+
+function select(pickInfo) {
+ unSelectAll();
+ if (pickInfo) {
+ g_selectedInfo = pickInfo;
+ // make a copy of the selected shape so we can use it to highlight.
+ g_highlightShape = o3djs.shape.duplicateShape(
+ g_pack,
+ g_selectedInfo.shapeInfo.shape,
+ 'highlight_');
+ // Set all of it's elements to use the highlight material.
+ var elements = g_highlightShape.elements;
+ for (var ee = 0; ee < elements.length; ee++) {
+ elements[ee].material = g_highlightMaterial;
+ }
+
+ // Add it to the same transform
+ g_selectedInfo.shapeInfo.parent.transform.addShape(g_highlightShape);
+ g_flashTimer = 0.0; // make it change color immediately.
+ }
+}
+
+function pick(e) {
+ var worldRay = o3djs.picking.clientPositionToWorldRay(
+ e.x,
+ e.y,
+ g_viewInfo.drawContext,
+ g_client.width,
+ g_client.height);
+ unSelectAll();
+
+ // Update the entire tree in case anything moved.
+ g_treeInfo.update();
+
+ var pickInfo = g_treeInfo.pick(worldRay);
+ if (pickInfo) {
+ select(pickInfo);
+ g_pickInfoElem.innerHTML = pickInfo.shapeInfo.shape.name;
+ } else {
+ g_pickInfoElem.innerHTML = '--nothing--';
+ }
+}
+
+function onrender(renderEvent) {
+ g_flashTimer += renderEvent.elapsedTime;
+ g_flashTimer = g_flashTimer % 0.5;
+ if (g_selectedInfo) {
+ if (g_flashTimer < 0.25) {
+ g_highlightMaterial.getParam('color').value = [1, 1, 1, 1];
+ } else {
+ g_highlightMaterial.getParam('color').value = [0, 0, 0, 1];
+ }
+ }
+}
+
+/**
+ * Loads a scene into the transform graph.
+ * @param {!o3d.Pack} pack Pack to load scene into.
+ * @param {string} fileName filename of the scene.
+ * @param {!o3d.Transform} parent parent node in the transform graph to
+ * which to load the scene into.
+ */
+function loadScene(pack, fileName, parent) {
+ // Get our full path to the scene
+ var scenePath = o3djs.util.getCurrentURI() + fileName;
+
+ // Load the file given the full path, and call the callback function
+ // when its done loading.
+ o3djs.scene.loadScene(g_client, pack, parent, scenePath, callback);
+
+ /**
+ * Our callback is called once the scene has been loaded into memory
+ * from the web or locally.
+ * @param {!o3d.Pack} pack The pack that was passed in above.
+ * @param {!o3d.Transform} parent The parent that was passed in above.
+ * @param {*} exception null if loading succeeded.
+ */
+ function callback(pack, parent, exception) {
+ if (exception) {
+ alert('Could not load: ' + fileName + '\n' + exception);
+ return;
+ }
+ // Get a cameraInfo (an object with a view and projection matrix)
+ // using our javascript library function
+ var cameraInfo = o3djs.camera.getViewAndProjectionFromCameras(
+ parent,
+ g_client.width,
+ g_client.height);
+
+ // Copy the one from the file to ours.
+ g_viewInfo.drawContext.view = cameraInfo.view;
+ g_viewInfo.drawContext.projection = cameraInfo.projection;
+
+ // Generate draw elements and setup material draw lists.
+ o3djs.pack.preparePack(pack, g_viewInfo);
+
+ // Update our info
+ updateInfo();
+
+ g_treeInfo.dump('');
+
+ g_finished = true; // for selenium testing.
+ }
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and loads the scene into the transform graph.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ var o3dElement = clientElements[0];
+ o3dElement.name = 'o3dObj'; // This is only for our selenium tests.
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = o3dElement.client;
+
+ g_pickInfoElem = document.getElementById('pickInfo');
+
+ // Creates a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Create a material for highlighting.
+ g_highlightMaterial = g_pack.createObject('Material');
+ g_highlightMaterial.drawList = g_viewInfo.performanceDrawList;
+ var effect = g_pack.createObject('Effect');
+ o3djs.effect.loadEffect(effect, 'shaders/solid-color.shader');
+ g_highlightMaterial.effect = effect;
+ effect.createUniformParameters(g_highlightMaterial);
+ // Setup a state to bring the lines forward.
+ var state = g_pack.createObject('State');
+ state.getStateParam('PolygonOffset2').value = -1.0;
+ state.getStateParam('FillMode').value = g_o3d.State.WIREFRAME;
+ g_highlightMaterial.state = state;
+
+ // Creates a transform to put our data on.
+ var my_data_root = g_pack.createObject('Transform');
+
+ // Connects our root to the client's root.
+ my_data_root.parent = g_client.root;
+
+ // Load the scene into the transform graph as a child of my_data_root
+ loadScene(g_pack, 'assets/seven_shapes.o3dtgz', my_data_root);
+
+ g_client.setRenderCallback(onrender);
+
+ // Start picking; it won't do anything until the scene finishes loading.
+ o3djs.event.addEventListener(o3dElement, 'mousedown', pick);
+}
+
+/**
+ * Removes any callbacks so they don't get called after the page has unloaded.
+ */
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+</script>
+</head>
+<body>
+<h1>Picking</h1>
+Click on an object
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 600px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+<div style="font-family: sans-serif; font-size: large;">PICKED: <span id="pickInfo"></span></div>
+</body>
+</html>
diff --git a/o3d/samples/pingpong/instructions.gif b/o3d/samples/pingpong/instructions.gif
new file mode 100644
index 0000000..d142083
--- /dev/null
+++ b/o3d/samples/pingpong/instructions.gif
Binary files differ
diff --git a/o3d/samples/pingpong/logo.gif b/o3d/samples/pingpong/logo.gif
new file mode 100644
index 0000000..379d196
--- /dev/null
+++ b/o3d/samples/pingpong/logo.gif
Binary files differ
diff --git a/o3d/samples/pingpong/o3dPingPong.html b/o3d/samples/pingpong/o3dPingPong.html
new file mode 100644
index 0000000..dea886e
--- /dev/null
+++ b/o3d/samples/pingpong/o3dPingPong.html
@@ -0,0 +1,862 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<title>o3dPingPong</title>
+
+<script type="text/javascript" src="../o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.event');
+
+var shader='uniform float4x4 worldViewProjMatrix : WORLDVIEWPROJECTION;\n'+
+'void vs(\n'+
+' float4 pos: POSITION,\n'+
+' float4 col: COLOR,\n'+
+'\n'+
+' out float4 oPos: POSITION,\n'+
+' out float4 color : COLOR0)\n'+
+'{\n'+
+' oPos = mul(pos,worldViewProjMatrix);\n'+
+'\n'+
+' color=col; //float4(1,0,0,1);\n'+
+'}\n'+
+'\n'+
+'void fs(\n'+
+' float4 color: COLOR,\n'+
+' out float4 oCol: COLOR)\n'+
+'{\n'+
+' oCol=color;\n'+
+'}\n'+
+'// #o3d VertexShaderEntryPoint vs\n'+
+'// #o3d PixelShaderEntryPoint fs\n'+
+'// #o3d MatrixLoadOrder RowMajor\n';
+
+var camera = {};
+camera.eye = {};
+camera.target = {};
+camera.eye.x = 0;
+camera.eye.y = -100;
+camera.eye.z = 100;
+camera.target.x = 0;
+camera.target.y = 0;
+camera.target.z = 0;
+
+var on = false;
+var cube;
+var none;
+var view;
+var viewInfo;
+var g_material;
+var angleY=0;
+var angleX=0;
+var o3d;
+var math;
+var client;
+var pack;
+var g_finished = false; // for selenium
+
+var game = {};
+
+function updateView() {
+ var matrix = math.matrix4.identity();
+ math.matrix4.rotateZYX(matrix, [angleX, angleY, 0]);
+ math.matrix4.scale(matrix, math.mulScalarVector(.25, [1, 1, 1]));
+ view.localMatrix = matrix;
+}
+
+var key = {};
+key.LEFT = 37;
+key.UP = 38;
+key.RIGHT = 39;
+key.DOWN = 40;
+
+
+key.A = 65
+key.S = 83
+key.D = 68
+key.W = 87
+
+function keyPressed(e) {
+ /*if (e.keyCode == key.A) rotateY(-0.1);
+ else if (e.keyCode == key.D) rotateY(0.1)
+ else if (e.keyCode == key.W) rotateX(-0.1)
+ else if (e.keyCode == key.S) rotateX(0.1)*/
+}
+
+function move_instance(node, x, y, z, h) {
+ node.identity();
+ node.translate(x, y, z);
+}
+
+var entityID = 0;
+var transformNodes = new Array();
+var meshNodes = new Array();
+
+function createBox(x, y, z, w, d, h, color, parentNode, fadeToColor) {
+
+ var boxVerts = [
+ 0, 0, 0, // 0
+ w, 0, 0, // 1
+ w, 0, h, // 2
+ 0, 0, h, // 3
+ 0, d, 0, // 4
+ w, d, 0, // 5
+ w, d, h, // 6
+ 0, d, h, // 7
+ 0, 0, 0, // 0b
+ w, 0, 0, // 1b
+ w, 0, h, // 2b
+ 0, 0, h, // 3b
+ 0, d, 0, // 4b
+ w, d, 0, // 5b
+ w, d, h, // 6b
+ 0, d, h // 7b
+ ];
+
+ var boxIndices = [
+ 0, 1, 3, 1, 2, 3, // front
+ 0+8, 3+8, 1+8, 1+8, 3+8, 2+8, // frontb
+
+ 5+8, 4+8, 6+8, 4+8, 7+8, 6+8, // back
+ 5+8, 6+8, 4+8, 4+8, 6+8, 7+8, // backb
+
+ 4, 0, 7, 0, 3, 7, // left
+ 4+8, 7+8, 0+8, 0+8, 7+8, 3+8, // leftb
+
+ 1+8, 5+8, 2+8, 5+8, 6+8, 2+8, // right
+ 1, 2+8, 5+8, 5+8, 2+8, 6+8, // rightb
+
+ 4, 5, 0, 5, 1, 0, // top
+ 4+8, 0+8, 5+8, 5+8, 0+8, 1+8, // top2
+
+ 6+8, 7+8, 2+8, 7+8, 3+8, 2+8, // bottom
+ 6+8, 2+8, 7+8, 7+8, 2+8, 3+8 // bottom2
+ ];
+
+
+ var boxIndicesOneSided = [
+ 0, 1, 3, 1, 2, 3, // front
+ 5+8, 4+8, 6+8, 4+8, 7+8, 6+8, // back
+ 4, 0, 7, 0, 3, 7, // left
+ 1+8, 5+8, 2+8, 5+8, 6+8, 2+8, // right
+ 4, 5, 0, 5, 1, 0, // top
+ 6+8, 7+8, 2+8, 7+8, 3+8, 2+8 // bottom
+ ];
+
+ if (color != undefined) {
+ var r = color.r;
+ var g = color.g;
+ var b = color.b;
+ var a = color.a;
+ if (fadeToColor == color) {
+ var boxColors = [
+ r, g, b, a, // 0
+ r, g, b, a, // 1
+ r, g, b, a, // 2
+ r, g, b, a, // 3
+ r, g, b, a, // 4
+ r, g, b, a, // 5
+ r, g, b, a, // 6
+ r, g, b, a, // 7
+ r, g, b, a, // 0b
+ r, g, b, a, // 1b
+ r, g, b, a, // 2b
+ r, g, b, a, // 3b
+ r, g, b, a, // 4b
+ r, g, b, a, // 5b
+ r, g, b, a, // 6b
+ r, g, b, a // 7b
+ ];
+
+ } else if (fadeToColor != undefined) {
+ r2 = fadeToColor.r;
+ g2 = fadeToColor.g;
+ b2 = fadeToColor.b;
+ var boxColors = [
+ r2, g2, b2, a, // 0
+ r2, g2, b2, a, // 1
+ r, g, b, a, //
+ r, g, b, a, // 3
+ r2, g2, b2, a, // 4
+ r2, g2, b2, a, // 5
+ r, g, b, a, // 6
+ r, g, b, a, // 7
+ r2, g2, b2, a, // 0b
+ r2, g2, b2, a, // 1b
+ r, g, b, a, // 2b
+ r, g, b, a, // 3b
+ r2, g2, b2, a, // 4b
+ r2, g2, b2, a, // 5b
+ r, g, b, a, // 6b
+ r, g, b, a // 7b
+ ];
+
+ } else {
+ var boxColors = [
+ r, g, b, a, // 0
+ r, g, b, a, // 1
+ r*.7, g*.7, b*.7, a, // 2
+ r*.7, g*.7, b*.7, a, // 3
+ r, g, b, a, // 4
+ r, g, b, a, // 5
+ r*.7, g*.7, b*.7, a, // 6
+ r*.7, g*.7, b*.7, a, // 7
+ r*.8, g*.8, b*.8, a, // 0b
+ r*.8, g*.8, b*.8, a, // 1b
+ r*.8, g*.8, b*.8, a, // 2b
+ r*.8, g*.8, b*.8, a, // 3b
+ r*.4, g*.4, b*.4, a, // 4b
+ r*.4, g*.4, b*.4, a, // 5b
+ r*.4, g*.4, b*.4, a, // 6b
+ r*.4, g*.4, b*.4, a // 7b
+ ];
+ }
+ } else {
+ var boxColors = [
+ 1, 0, 0, 1, // 0
+ 1, 0, 0, 1, // 1
+ 1, 0, 0, 1, // 2
+ 1, 0, 0, 1, // 3
+ 0, 1, 0, 1, // 4
+ 0, 1, 0, 1, // 5
+ 0, 1, 0, 1, // 6
+ 0, 1, 0, 1, // 7
+ .5, 0, 0, 1, // 0b
+ .5, 0, 0, 1, // 1b
+ .5, 0, 0, 1, // 2b
+ .5, 0, 0, 1, // 3b
+ 0, .5, 0, 1, // 4b
+ 0, .5, 0, 1, // 5b
+ 0, .5, 0, 1, // 6b
+ 0, .5, 0, 1 // 7b
+ ];
+ }
+
+ entityID++;
+
+ transformName = 'transform' + entityID;
+ shapeName = 'shape' + entityID;
+ meshName = 'mesh' + entityID;
+ transformNodes[entityID] = pack.createObject('Transform');
+
+ transformNodes[entityID].translate(x, y, z);
+
+ meshNodes[entityID] = pack.createObject('Shape');
+ transformNodes[entityID].addShape(meshNodes[entityID])
+ var primitive = pack.createObject('Primitive');
+ var streamBank = pack.createObject('StreamBank');
+ primitive.owner = meshNodes[entityID];
+ primitive.streamBank = streamBank;
+
+ primitive.material = g_material;
+
+ var vertBuffer = pack.createObject('VertexBuffer');
+ var positionField = vertBuffer.createField('FloatField', 3);
+ vertBuffer.set(boxVerts);
+
+ var colorBuffer = pack.createObject('VertexBuffer');
+ var colorField = colorBuffer.createField('FloatField', 4);
+ colorBuffer.set(boxColors);
+
+ var indexBuffer = pack.createObject('IndexBuffer');
+ indexBuffer.set(boxIndices);
+
+ streamBank.setVertexStream(o3d.Stream.POSITION,
+ 0,
+ positionField,
+ 0);
+ streamBank.setVertexStream(o3d.Stream.COLOR,
+ 0,
+ colorField,
+ 0);
+ primitive.indexBuffer = indexBuffer;
+
+ primitive.primitiveType = o3d.Primitive.TRIANGLELIST;
+ primitive.numberPrimitives = 24;
+ primitive.numberVertices = 16;
+
+ if (parentNode != undefined) {
+ transformNodes[entityID].parent = parentNode;
+ } else {
+ transformNodes[entityID].parent = view;
+ }
+
+ primitive.createDrawElement(pack, null);
+
+ return transformNodes[entityID];
+}
+
+var Color = function(r, g, b, a) {
+ if (r>1 || g>1 || b>1) {
+ r = r/255;
+ g = g/255;
+ b = b/255;
+ }
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ this.a = a;
+}
+function scrollMe(e) {
+ if (e.deltaY < 0) {
+ camera.eye.x *= 11/12;
+ camera.eye.y *= 11/12;
+ camera.eye.z *= 11/12;
+
+ } else {
+ camera.eye.x *= (1+1/12);
+ camera.eye.y *= (1+1/12);
+ camera.eye.z *= (1+1/12);
+ }
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ var o3dElement = clientElements[0];
+ o3dElement.id = 'o3dObj';
+ o3d = o3dElement.o3d;
+ math = o3djs.math;
+ client = o3dElement.client;
+
+ pack = client.createPack();
+
+ // Create the render graph for a view.
+ viewInfo = o3djs.rendergraph.createBasicView(
+ pack,
+ client.root,
+ client.renderGraphRoot);
+
+ var effect = pack.createObject('Effect');
+ effect.loadFromFXString(shader);
+ g_material = pack.createObject('Material');
+ g_material.drawList = viewInfo.performanceDrawList;
+ g_material.effect = effect;
+ effect.createUniformParameters(g_material);
+
+ view = pack.createObject('Transform');
+ view.parent = client.root;
+
+ var root = client.root;
+
+ var o3d_width = client.width;
+ var o3d_height = client.height;
+
+ viewInfo.drawContext.projection=math.matrix4.perspective(
+ math.degToRad(30), o3d_width / o3d_height, 1, 5000);
+ target = [0, 0, 0];
+ eye = [0, -100, 0];
+ up = [0, 0, 1];
+ viewInfo.drawContext.view = math.matrix4.lookAt(eye, target, up);
+
+ bg = pack.createObject('Transform');
+ bg.translate(0, 0, 1);
+ bg.parent = client.root;
+
+ red = new Color(1, 0, 0, 1);
+ blue = new Color(0, 0, 1, 1);
+ wallColor = new Color(0, .8, .4, 1);
+ green = new Color(0, .5, 0, 1);
+ lineColor = new Color(0, .7, 0, 1);
+ shadow = new Color(0, .3, 0, .5);
+ silver = new Color(.8, .8, .8, 1);
+ pink = new Color(1, .8, .8, 1);
+
+ window.field = createBox(-120, -80, -10, 240, 160, 10, green)
+ window.centerline = createBox(-3, -80, 0, 6, 160, .5, lineColor)
+ window.paddle1 = createBox(-110, 0, 5, 10, 30, 30, silver);
+ window.shadow1 = createBox(-107, 3, 1, 10, 30, 0, shadow);
+
+ window.paddle2 = createBox(-110, 0, 5, 10, 30, 30, silver);
+ window.shadow2 = createBox(-107, 3, 1, 10, 30, 0, shadow);
+
+ window.ball = createBox(0, 0, 2, 10, 10, 10, pink);
+ window.ballShadow = createBox(0, 0, 1, 10, 10, 0, shadow);
+
+ window.bottomWall = createBox(-120, -90, -10, 240, 10, 15, wallColor);
+ window.topWall = createBox(-120, 80, -10, 240, 10, 15, wallColor);
+
+ suRed = new Color(.7, 0, 0, 1)
+ suGreen = new Color(0, .7, 0, 1)
+ suBlue = new Color(0, 0, .7, 1)
+ suGround = new Color(210, 208, 185, 1);
+ suSky = new Color(212*.8, 225*.8, 225*.8, 1);
+
+ window.xaxis = createBox(0, 0, 0, 5000, .25, .25, suRed, undefined);
+ window.yaxis = createBox(0, 0, 0, .25, 5000, .25, suGreen, undefined);
+ window.zaxis = createBox(0, 0, 0, .25, .25, 5000, suBlue, undefined);
+
+ white = new Color(1, 1, 1, 1);
+
+ mybox3 = createBox(-1500, -1500, -1500, 3000, 3000, 3000, suSky, bg, suGround);
+ bg_Ground1 = createBox(-1500, 1490, -1500, 3000, 10, 1500, suGround, bg, suGround);
+ bg_GroundR = createBox(1490, -1500, -1500, 10, 3000, 1500, suGround, bg, suGround);
+ bg_GroundL = createBox(-1490, -1500, -1500, 10, 3000, 1500, suGround, bg, suGround);
+ bg_Sky = createBox(-1500, 1490, 0, 3000, 10, 1500, suSky, bg, white);
+ bg_SkyR = createBox(1490, -1500, 0, 10, 3000, 1500, suSky, bg, white);
+ bg_SkyL = createBox(-1490, -1500, 0, 10, 3000, 1500, suSky, bg, white);
+
+ on=true;
+ updateView();
+
+ o3djs.event.addEventListener(o3dElement, 'wheel', scrollMe);
+
+ updateLayout();
+
+ g_finished = true; // for selenium
+}
+
+function rotateX(delta) {
+ angleX=angleX+delta;
+ updateView();
+}
+
+function rotateY(delta) {
+ angleY=angleY+delta;
+ updateView();
+}
+
+function toggle() {
+ if (on) {
+ cube.parentNode = none;
+ } else {
+ cube.parentNode = view;
+ }
+
+ on = !on;
+}
+
+function startGame() {
+ if (window.gameStarted == true) {
+ return
+ }
+ window.gameStarted = true;
+ document.getElementById('o3d').style.visibility = 'visible';
+
+ // Wait until the plug-in has initialized before starting the game.
+ var clearId = window.setInterval(function() {
+ if (client) {
+ client.setRenderCallback(nextFrame);
+ } else {
+ return;
+ }
+ window.clearInterval(clearId);
+ }, 10);
+}
+
+function updateLayout() {
+ document.getElementById('focusHolder').focus();
+ document.body.scrollTop = 0;
+
+}
+
+function uninit() {
+ if (client) {
+ client.cleanup();
+ }
+}
+
+window.onload = init;
+window.onunload = uninit;
+
+</script>
+<body style="overflow:hidden" onscroll="updateLayout();">
+
+<table border=0 width=100% height=100% style="overflow:hidden" >
+<tr><td id="clientBanner" align=center style="cursor:pointer;background-image:url(logo.gif);background-position:center center;background-repeat:no-repeat" onclick="startGame();">
+<div id="client" name="client" >
+<div id="o3d" style="width: 800px; height: 600px; visibility: hidden;"></div>
+<br/><img src="instructions.gif"></div>
+<form>
+<input id="focusHolder" style="position:absolute;top:-100px;">
+</form>
+
+</td></tr></table>
+
+
+</body>
+
+<script>
+
+keyIsDown = new Array();
+
+document.onkeydown = function(e) {
+ var keycode;
+ if (window.event) {
+ keycode = window.event.keyCode;
+ } else if (e) {
+ keycode = e.which;
+ }
+ keyIsDown[keycode] = true;
+}
+
+document.onkeyup = function(e) {
+ var keycode;
+ if (window.event) {
+ keycode = window.event.keyCode;
+ } else if (e) {
+ keycode = e.which;
+ }
+ keyIsDown[keycode] = false;
+
+}
+
+
+var x = 1000+10;
+var y = 1000+20;
+var z = 1;
+
+var BACKSPACE=8
+var TAB=9
+var ENTER=13
+var SHIFT=16
+var CTRL=17
+var ALT=18
+var ESCAPE=27
+var PAGEUP=33
+var PAGEDOWN=34
+var END=35
+var HOME=36
+var LEFT=37
+var UP=38
+var RIGHT=39
+var DOWN=40
+var DELETE=46
+var SPACE=32
+
+var spriteInfo = new Array();
+
+
+var leftID = 0
+var rightID = 1
+var ballID = 2
+var myPaddleID = leftID
+var enemyPaddleID = rightID
+var nextServeDirection = -1
+var isServing = true;
+var loop = 0;
+
+
+
+spriteInfo[leftID] = new Array();
+spriteInfo[rightID] = new Array();
+spriteInfo[ballID] = new Array();
+
+spriteInfo[leftID].velocityY = 0;
+spriteInfo[leftID].velocityX = 0;
+spriteInfo[leftID].x = 10;
+spriteInfo[leftID].y = 80;
+
+spriteInfo[rightID].velocityY = 0;
+spriteInfo[rightID].velocityX = 0;
+spriteInfo[rightID].x = 230;
+spriteInfo[rightID].y = 80;
+
+spriteInfo[ballID].velocityY = 0;
+spriteInfo[ballID].velocityX = 0;
+spriteInfo[ballID].x = 120;
+spriteInfo[ballID].y = 80;
+
+var SCREENHEIGHT = 160;
+var SCREENWIDTH = 240;
+var PADDLE
+
+var BALLWIDTH = 30
+var BALLHEIGHT = 32
+
+var BALLPAD = 20
+var PADDLEHEIGHT = 30
+var PADDLEWIDTH = 10
+var PADDLEACCELY = 10 // acceleration rate of the paddle
+var PADDLEACCELX = 10 // acceleration rate of the paddle
+var SCREENWIDTH = 240
+var SCREENHEIGHT = 160
+var SCREENCENTERX = 120
+var SCREENCENTERY = 80
+
+var COMPUTERACCURACYX=90 // accuracy in pixels that the computer tries to get its paddle within
+var COMPUTERACCURACYY=10
+
+var points = [0, 0, 0];
+function ScorePoint(playerNumber) {
+
+ if (playerNumber == myPaddleID) {
+ //alert('You Score!');
+ } else {
+ //alert('You Suck!');
+ }
+ spriteInfo[ballID].velocityX = 0;
+ spriteInfo[ballID].velocityY = 0;
+
+ spriteInfo[ballID].x = SCREENWIDTH/2;
+ spriteInfo[ballID].y = SCREENHEIGHT/2;
+ points[playerNumber]++;
+ pts = points[playerNumber];
+
+ ptBox = createBox(-140 + playerNumber*280, -80+pts*15, 0, 10, 10, 3, red);
+
+
+}
+
+
+function nextFrame(render_event) {
+
+ var elapsedTime = render_event.elapsedTime;
+
+ spriteInfo[ballID].x += spriteInfo[ballID].velocityX * elapsedTime;
+ spriteInfo[ballID].y += spriteInfo[ballID].velocityY * elapsedTime;
+
+ spriteInfo[leftID].x += spriteInfo[leftID].velocityX * elapsedTime;
+ spriteInfo[leftID].y += spriteInfo[leftID].velocityY * elapsedTime;
+
+ spriteInfo[rightID].x += spriteInfo[rightID].velocityX * elapsedTime;
+ spriteInfo[rightID].y += spriteInfo[rightID].velocityY * elapsedTime;
+
+ // move my paddle
+ if (keyIsDown[UP]) {
+ spriteInfo[myPaddleID].velocityY -= PADDLEACCELY;
+ } else if (keyIsDown[DOWN]) {
+ spriteInfo[myPaddleID].velocityY += PADDLEACCELY;
+ } else {
+ spriteInfo[myPaddleID].velocityY -= spriteInfo[myPaddleID].velocityY * elapsedTime;
+ }
+
+ if (keyIsDown[LEFT]) {
+ spriteInfo[myPaddleID].velocityX -= PADDLEACCELX;
+ } else if (keyIsDown[RIGHT]) {
+ spriteInfo[myPaddleID].velocityX += PADDLEACCELX;
+ } else {
+ spriteInfo[myPaddleID].velocityX -= spriteInfo[myPaddleID].velocityX * elapsedTime;
+ }
+
+ if (keyIsDown[key.A]) camera.eye.x -= 100 * elapsedTime;
+ if (keyIsDown[key.D]) camera.eye.x += 100 * elapsedTime;
+ if (keyIsDown[key.W]) camera.eye.z += 100 * elapsedTime;
+ if (keyIsDown[key.S]) camera.eye.z -= 100 * elapsedTime;
+
+ target = [0, 0, 0];
+ eye = [camera.eye.x, camera.eye.y, camera.eye.z];
+ up = [0, 0, 1];
+ viewInfo.drawContext.view = math.matrix4.lookAt(eye, target, up);
+
+ // make sure my paddle can't go off the top & bottom of screen
+ if (spriteInfo[myPaddleID].y < PADDLEHEIGHT/2) {
+ spriteInfo[myPaddleID].y = PADDLEHEIGHT/2;
+ spriteInfo[myPaddleID].velocityY = 0;
+ } else if (spriteInfo[myPaddleID].y > SCREENHEIGHT-PADDLEHEIGHT/2) {
+ spriteInfo[myPaddleID].y = SCREENHEIGHT-PADDLEHEIGHT/2;
+ spriteInfo[myPaddleID].velocityY = 0;
+ }
+
+ if (spriteInfo[myPaddleID].x < PADDLEWIDTH) {
+ spriteInfo[myPaddleID].x = PADDLEWIDTH;
+ spriteInfo[myPaddleID].velocityX = 0;
+ } else if (spriteInfo[myPaddleID].x > SCREENWIDTH/2-BALLPAD) {
+ spriteInfo[myPaddleID].x = SCREENWIDTH/2-BALLPAD;
+ spriteInfo[myPaddleID].velocityX = 0;
+ }
+
+ // handle bouncing
+ MoveComputer();
+
+ if ( spriteInfo[ballID].x + BALLWIDTH/2 > spriteInfo[rightID].x &&
+ spriteInfo[ballID].x + BALLWIDTH/2 < spriteInfo[rightID].x + PADDLEWIDTH &&
+ spriteInfo[ballID].y > spriteInfo[rightID].y-PADDLEHEIGHT/2 &&
+ spriteInfo[ballID].y < spriteInfo[rightID].y+PADDLEHEIGHT/2
+ && spriteInfo[ballID].velocityX > 0) {
+
+ spriteInfo[ballID].velocityX *= -1;
+ spriteInfo[ballID].velocityY += spriteInfo[rightID].velocityY/2
+ if (spriteInfo[rightID].velocityX < 0) {
+ spriteInfo[ballID].velocityX += spriteInfo[rightID].velocityX;
+ }
+
+
+ } else if (spriteInfo[ballID].x - BALLWIDTH/2 < spriteInfo[leftID].x &&
+ spriteInfo[ballID].x - BALLWIDTH/2 > spriteInfo[leftID].x - PADDLEWIDTH &&
+ spriteInfo[ballID].y > spriteInfo[leftID].y-PADDLEHEIGHT/2 &&
+ spriteInfo[ballID].y < spriteInfo[leftID].y+PADDLEHEIGHT/2
+ && spriteInfo[ballID].velocityX < 0) {
+
+ spriteInfo[ballID].velocityX *= -1;
+ spriteInfo[ballID].velocityY += spriteInfo[leftID].velocityY/2;
+ if (spriteInfo[leftID].velocityX > 0) {
+ spriteInfo[ballID].velocityX += spriteInfo[leftID].velocityX;
+ }
+
+ }
+
+
+
+ if (isServing) {
+ loop++;
+ if (loop > 20) {
+ loop=0;
+ spriteInfo[ballID].isVisible = true;
+ spriteInfo[ballID].velocityY = ((Math.random()*4)-2)*20;
+ spriteInfo[ballID].velocityX = (((Math.random()*1)+2) * nextServeDirection)*20;
+ isServing = false;
+ }
+ }
+
+
+
+ // bounce along top or bottom
+ if ((spriteInfo[ballID].y > SCREENHEIGHT-10 || spriteInfo[ballID].y < 10) && isServing == false) {
+ spriteInfo[ballID].velocityY *= -1;
+ }
+
+
+ if (spriteInfo[ballID].x < -50) {
+ // right player scores a point!
+ ScorePoint(rightID);
+ nextServeDirection = -1;
+ isServing = true;
+ }
+
+ if (spriteInfo[ballID].x > SCREENWIDTH+50) {
+ // left player scores a point!
+ ScorePoint(leftID);
+ nextServeDirection = -1;
+ isServing = true;
+ }
+
+
+ move_instance(paddle1, -120+spriteInfo[myPaddleID].x-PADDLEWIDTH/2, 80-spriteInfo[myPaddleID].y-PADDLEHEIGHT/2, 5, 30);
+ move_instance(shadow1, -120+spriteInfo[myPaddleID].x+5-PADDLEWIDTH/2, 80-spriteInfo[myPaddleID].y-PADDLEHEIGHT/2+5, 1, 0);
+
+ move_instance(paddle2, -120+spriteInfo[rightID].x-PADDLEWIDTH/2, 80-spriteInfo[rightID].y-PADDLEHEIGHT/2, 5, 30);
+ move_instance(shadow2, -120+spriteInfo[rightID].x+5-PADDLEWIDTH/2, 80-spriteInfo[rightID].y-PADDLEHEIGHT/2+5, 1, 0);
+
+ move_instance(ball, -120+spriteInfo[ballID].x-5, 80-(spriteInfo[ballID].y+5), 10, 10);
+ move_instance(ballShadow, -120+spriteInfo[ballID].x-5+5, 80-(spriteInfo[ballID].y+0), 1, 0);
+}
+
+function MoveComputer() {
+// calculate where the AI wants to be
+ if (spriteInfo[ballID].velocityX > 0) {
+
+ // if the ball is coming our way, calculate where it will go
+ targetX = spriteInfo[ballID].x;
+ targetY = spriteInfo[ballID].y;
+
+ } else {
+
+ // else return to center
+ targetX = 220;
+ targetY = 80;
+
+ }
+
+ if (ComputerDistanceX() < COMPUTERACCURACYX) {
+
+ spriteInfo[rightID].velocityX = spriteInfo[rightID].velocityX/2;
+
+ } else {
+
+ if (targetX < spriteInfo[rightID].x) {
+ spriteInfo[rightID].velocityX -= PADDLEACCELX;
+ } else if (targetX > spriteInfo[rightID].x) {
+ spriteInfo[rightID].velocityX += PADDLEACCELX;
+ } else {
+ spriteInfo[rightID].velocityX = spriteInfo[rightID].velocityX/2;
+ }
+ }
+
+ if (ComputerDistanceY() < COMPUTERACCURACYY) {
+
+ spriteInfo[rightID].velocityY = spriteInfo[rightID].velocityY/2;
+
+ } else {
+
+ if (targetY < spriteInfo[rightID].y) {
+ spriteInfo[rightID].velocityY -= PADDLEACCELY;
+ } else if (targetY > spriteInfo[rightID].y) {
+ spriteInfo[rightID].velocityY += PADDLEACCELY;
+ } else {
+ spriteInfo[rightID].velocityY = spriteInfo[rightID].velocityY/2;
+ }
+
+ }
+
+ // make sure paddle can't go off the top & bottom of screen
+ if (spriteInfo[rightID].y < PADDLEHEIGHT/2) {
+ spriteInfo[rightID].y = PADDLEHEIGHT/2;
+ spriteInfo[rightID].velocityY = 0;
+ } else if (spriteInfo[rightID].y > SCREENHEIGHT-PADDLEHEIGHT/2) {
+ spriteInfo[rightID].y = SCREENHEIGHT-PADDLEHEIGHT/2;
+ spriteInfo[rightID].velocityY = 0;
+ }
+
+ if (spriteInfo[rightID].x > SCREENWIDTH) {
+ spriteInfo[rightID].x = SCREENWIDTH-PADDLEWIDTH;
+ spriteInfo[rightID].velocityX = 0;
+ } else if (spriteInfo[rightID].x < SCREENWIDTH/2+BALLPAD) {
+ spriteInfo[rightID].x = SCREENWIDTH/2+BALLPAD;
+ spriteInfo[rightID].velocityX = 0;
+ }
+
+
+}
+
+function ComputerDistanceX() {
+ var distanceX;
+ distanceX = spriteInfo[rightID].x - spriteInfo[ballID].x;
+ if (distanceX < 0) {
+ distanceX = distanceX * -1;
+ }
+ return distanceX;
+}
+
+function ComputerDistanceY() {
+ var distanceY;
+ distanceY = spriteInfo[ballID].y - spriteInfo[rightID].y;
+ if (distanceY < 0) {
+ distanceY = distanceY * -1;
+ }
+ return distanceY;
+}
+
+
+
+</script>
+
diff --git a/o3d/samples/primitives.html b/o3d/samples/primitives.html
new file mode 100644
index 0000000..1dbee40
--- /dev/null
+++ b/o3d/samples/primitives.html
@@ -0,0 +1,245 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+O3D Primitives
+
+This sample shows how to use the functions in the primitives utility library
+to make various shapes.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Primitives
+</title>
+<script type="text/javascript" src="o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.effect');
+
+// global variables
+var g_o3dElement;
+var g_client;
+var g_o3d;
+var g_math;
+var g_pack;
+var g_viewInfo;
+
+var g_lightPosition = [5, 5, 7];
+var g_eyePosition = [1, 3, 12];
+
+/**
+ * Creates the client area.
+ */
+function initClient() {
+ window.g_finished = false; // for selenium testing.
+
+ // Runs the sample in V8. Comment out this line to run it in the browser
+ // JavaScript engine, for example if you want to debug it.
+ o3djs.util.setMainEngine(o3djs.util.Engine.V8);
+
+ o3djs.util.makeClients(main);
+}
+
+/**
+ * Initializes global variables, positions camera, draws shapes.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function main(clientElements) {
+ // Init global variables.
+ initGlobals(clientElements);
+
+ // Set up the view and projection transformations.
+ initContext();
+
+ // Add the shapes to the transform heirarchy.
+ createShapes();
+
+ window.g_finished = true; // for selenium testing.
+}
+
+/**
+ * Initializes global variables and libraries.
+ */
+function initGlobals(clientElements) {
+ g_o3dElement = clientElements[0];
+ window.g_client = g_client = g_o3dElement.client;
+ g_o3d = g_o3dElement.o3d;
+ g_math = o3djs.math;
+
+ // Create a pack to manage the objects created.
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+}
+
+/**
+ * Sets up reasonable view and projection matrices.
+ */
+function initContext() {
+ // Set up a perspective transformation for the projection.
+ g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(30), // 30 degree frustum.
+ g_o3dElement.clientWidth / g_o3dElement.clientHeight, // Aspect ratio.
+ 1, // Near plane.
+ 5000); // Far plane.
+
+ // Set up our view transformation to look towards the world origin where the
+ // primitives are located.
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ g_eyePosition, // eye
+ [0, 0, 0], // target
+ [0, 1, 0]); // up
+}
+
+/**
+ * Creates a phong material based on the given single color.
+ * @param {Array} baseColor An array with 4 entries, the R,G,B, and A components
+ * of a color.
+ * @return {Material} A phong material whose overall pigment is baseColor.
+ */
+function createPhongMaterial(baseColor) {
+ // Create a new, empty Material object.
+ var material = g_pack.createObject('Material');
+
+ o3djs.effect.attachStandardShader(
+ g_pack, material, g_lightPosition, 'phong');
+
+ material.drawList = g_viewInfo.performanceDrawList;
+
+ // Assign parameters to the phong material.
+ material.getParam('emissive').value = [0, 0, 0, 1];
+ material.getParam('ambient').value = g_math.mulScalarVector(0.1, baseColor);
+ material.getParam('diffuse').value = g_math.mulScalarVector(0.9, baseColor);
+ material.getParam('specular').value = [.2, .2, .2, 1];
+ material.getParam('shininess').value = 20;
+
+ return material;
+}
+
+/**
+ * Creates shapes using the primitives utility library, and adds them to the
+ * transform graph at the root node.
+ */
+function createShapes() {
+ var cube = o3djs.primitives.createCube(
+ g_pack,
+ createPhongMaterial([0,1,0,1]), // A green phong-shaded material.
+ Math.sqrt(2)); // The length of each side of the cube.
+
+ var sphere = o3djs.primitives.createSphere(
+ g_pack,
+ createPhongMaterial([1,0,0,1]),
+ 1.0, // Radius of the sphere.
+ 30, // Number of meridians.
+ 20); // Number of parallels.
+
+ var cylinder = o3djs.primitives.createCylinder(
+ g_pack,
+ createPhongMaterial([1,0,1,1]),
+ 0.5, // Radius.
+ 1.5, // Depth.
+ 20, // Number of radial subdivisions.
+ 20); // Number of vertical subdivisions.
+
+ var plane = o3djs.primitives.createPlane(
+ g_pack,
+ createPhongMaterial([0,1,1,1]),
+ 1, // Width.
+ 1.618, // Depth.
+ 3, // Horizontal subdivisions.
+ 3); // Vertical subdivisions.
+
+ // Make a polygon to extrude for the prism.
+ var polygon = [];
+ var n = 10;
+ for (var i = 0; i < n; ++i) {
+ var theta = 2.0 * i * Math.PI / n;
+ var radius = (i % 2) ? 1 : 0.382;
+ polygon.push([radius * Math.cos(theta), radius * Math.sin(theta)]);
+ }
+
+ var prism = o3djs.primitives.createPrism(
+ g_pack,
+ createPhongMaterial([1,1,0,1]),
+ polygon, // The profile polygon to be extruded.
+ 1); // The depth of the extrusion.
+
+ var disc = o3djs.primitives.createDisc(
+ g_pack,
+ createPhongMaterial([1,0,0,1]),
+ 1, // Radius.
+ 7, // Divisions.
+ 2, // Stacks (optional).
+ 0, // Start Stack (optional).
+ 2); // Stack Power (optional).
+
+ // Add the shapes to the transforms.
+ var transformTable = [
+ {shape: cube, translation: [-2, 1, 0]},
+ {shape: sphere, translation: [0, 1, 0]},
+ {shape: cylinder, translation: [2, 1, 0]},
+ {shape: plane, translation: [-2, -1, 0]},
+ {shape: prism, translation: [0, -1, 0]},
+ {shape: disc, translation: [2, -1, 0]}
+ ];
+
+ for (var tt = 0; tt < transformTable.length; ++tt) {
+ var transform = g_pack.createObject('Transform');
+ transform.addShape(transformTable[tt].shape);
+ transform.translate(transformTable[tt].translation);
+ transform.parent = g_client.root;
+ }
+}
+
+
+</script>
+</head>
+<body onload="initClient()">
+<h1>Primitives</h1>
+This example shows how to use the primitives utility library to make various
+shapes.
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 600px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/samples/procedural-texture.html b/o3d/samples/procedural-texture.html
new file mode 100644
index 0000000..b160f1a
--- /dev/null
+++ b/o3d/samples/procedural-texture.html
@@ -0,0 +1,258 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+O3D Tutorial
+
+In this tutorial, we load and display a scene in O3D and then
+apply a procedural texture to it.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Applying a Procedural Texture to a scene
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.pack');
+o3djs.require('o3djs.camera');
+o3djs.require('o3djs.scene');
+
+// Events
+// init() once the page has finished loading.
+window.onload = init;
+
+// global variables
+var g_o3d;
+var g_client;
+var g_viewInfo;
+var g_pack;
+var g_finished = false; // for selenium testing
+
+/**
+ * Loads a scene into the transform graph.
+ * @param {!o3d.Pack} pack Pack to load scene into.
+ * @param {string} fileName filename of the scene.
+ * @param {!o3d.Transform} parent parent node in the transform graph to
+ * which to load the scene into.
+ */
+function loadScene(pack, fileName, parent) {
+ // Get our full path to the scene
+ var scenePath = o3djs.util.getCurrentURI() + fileName;
+
+ // Load the file given the full path, and call the callback function
+ // when its done loading.
+ o3djs.scene.loadScene(g_client, pack, parent, scenePath, callback);
+
+ /**
+ * Our callback is called once the scene has been loaded into memory
+ * from the web or locally.
+ * @param {!o3d.Pack} pack The pack that was passed in above.
+ * @param {!o3d.Transform} parent The parent that was passed in above.
+ * @param {*} exception null if loading succeeded.
+ */
+ function callback(pack, parent, exception) {
+ if (exception) {
+ alert('Could not load: ' + fileName + '\n' + exception);
+ return;
+ }
+ // Get a CameraInfo (an object with a view and projection matrix)
+ // using our javascript library function
+ var cameraInfo = o3djs.camera.getViewAndProjectionFromCameras(
+ parent,
+ g_client.width,
+ g_client.height);
+
+ // Copy the view and projection to the draw context.
+ g_viewInfo.drawContext.view = cameraInfo.view;
+ g_viewInfo.drawContext.projection = cameraInfo.projection;
+
+ // Add our checker pattern
+ createCheckerBoard(pack, 'Material__26');
+
+ // Generate draw elements and setup material draw lists.
+ o3djs.pack.preparePack(pack, g_viewInfo);
+
+ g_finished = true; // for selenium testing.
+ }
+}
+
+/**
+ * In order to apply the texture you must know the material name which you wish
+ * to change. The bulk of the calculation takes place in the shader itself.
+ * Alternatively, you could change the first or all the materials if you don't
+ * know the material name.
+ * @param {!o3d.Pack} pack The pack the scene was loaded into.
+ * @param {string} materialName Name of material you wish to turn into checkers.
+ */
+function createCheckerBoard(pack, materialName) {
+ var effectString = document.getElementById('checkerShader').value;
+ var effect = g_pack.createObject('Effect');
+ effect.loadFromFXString(effectString);
+ var materials = pack.getObjects(materialName, 'o3d.Material');
+ for (var m = 0; m < materials.length; m++) {
+ var material = materials[m];
+ // Set the material to use the checker effect.
+ material.effect = effect;
+ // Create any parameters the effect needs on the material.
+ effect.createUniformParameters(material);
+ }
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and loads the scene into the transform graph.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_o3d = o3dElement.o3d;
+ g_client = o3dElement.client;
+
+ // Creates a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Creates a transform to put our data on.
+ var myDataRoot = g_pack.createObject('Transform');
+
+ // Connects our root to the client's root.
+ myDataRoot.parent = g_client.root;
+
+ // Load the scene into the transform graph as a child myDataRoot
+ loadScene(g_pack, 'assets/teapot.o3dtgz', myDataRoot);
+}
+</script>
+</head>
+<body>
+<h1>Applying a Procedural Texture to a scene</h1>
+This tutorial shows how to apply a procedural texture to a scene.
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 600px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+<!-- Don't render the textarea -->
+<div style="display:none">
+<textarea id="checkerShader" name="checkerShader" cols="80" rows="20">
+ // The 4x4 world view projection matrix.
+ float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+
+ // input parameters for our vertex shader
+ struct a2v {
+ float4 position : POSITION;
+ float3 normal : NORMAL;
+ float2 texcoord : TEXCOORD0;
+ };
+
+ // input parameters for our pixel shader
+ struct v2f {
+ float4 position : POSITION;
+ float4 col : COLOR;
+ float2 texcoord : TEXCOORD0;
+ };
+
+ // function for getting the checker pattern
+ float4 checker(float2 uv) {
+ float checkSize = 4;
+ float fmodResult = fmod(floor(checkSize * uv.x) + floor(checkSize * uv.y),
+ 2.0);
+ if (fmodResult < 1) {
+ return float4(0, 1, 1, 1);
+ } else {
+ return float4(1, 0, 1, 1);
+ }
+ }
+
+ /**
+ * Our vertex shader. In the vertex shader, we calculate the lighting.
+ * Then we'll combine it with our checker pattern in the pixel shader.
+ */
+ v2f vertexShaderFunction(a2v input) {
+ v2f output;
+ output.position = mul(input.position, worldViewProjection);
+ /**
+ * lightVector - light vector
+ * normal - normal vector
+ * We put the light such that it illuminates our model.
+ */
+
+ float4 diffuseColor = float4(1, 1, 1, 1);
+ float3 lightPos = float3(1000, -1000, 1000);
+
+ float3 lightVector = normalize(lightPos - input.position.xyz);
+ float3 normal = normalize(input.normal);
+ float4 diffuse = dot(normal, lightVector) * diffuseColor;
+
+ output.col = diffuse;
+ output.texcoord = input.texcoord;
+ return output;
+ }
+
+ /**
+ * Our pixel shader. We take the lighting color we got from the vertex sahder
+ * and combine it with our checker pattern.
+ */
+ float4 pixelShaderFunction(v2f input): COLOR {
+ float4 check = checker(input.texcoord);
+ float4 color = input.col * check;
+ return color;
+ }
+
+ // Here we tell our effect file *which* functions are
+ // our vertex and pixel shaders.
+
+ // #o3d VertexShaderEntryPoint vertexShaderFunction
+ // #o3d PixelShaderEntryPoint pixelShaderFunction
+ // #o3d MatrixLoadOrder RowMajor
+</textarea>
+</div>
+</body>
+</html>
diff --git a/o3d/samples/render-mode.html b/o3d/samples/render-mode.html
new file mode 100644
index 0000000..19736204
--- /dev/null
+++ b/o3d/samples/render-mode.html
@@ -0,0 +1,283 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<title>
+ Render Mode Example.
+</title>
+<style type="text/css">
+ html, body {
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ border: none;
+ }
+</style>
+</head>
+<body onload="init();" onunload="uninit();">
+<script type="text/javascript" src="o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.quaternions');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.pack');
+o3djs.require('o3djs.arcball');
+o3djs.require('o3djs.event');
+o3djs.require('o3djs.scene');
+
+var g_root;
+var g_o3d;
+var g_o3dElement
+var g_math;
+var g_quaternions;
+var g_client;
+var g_aball;
+var g_thisRot;
+var g_lastRot;
+var g_pack;
+var g_viewInfo;
+var g_o3dWidth = -1;
+var g_o3dHeight = -1;
+var g_framesRendered = 0;
+
+var g_camera = {
+ eye: [0, 0, 5],
+ target: [0, 0, 0]
+};
+
+var g_dragging = false;
+
+function startDragging(e) {
+ g_lastRot = g_thisRot;
+ g_aball.click([e.x, e.y]);
+ g_dragging = true;
+}
+
+function drag(e) {
+ if (g_dragging) {
+ var rotationQuat = g_aball.drag([e.x, e.y]);
+ var rot_mat = g_quaternions.quaternionToRotation(rotationQuat);
+ g_thisRot = g_math.matrix4.mul(g_lastRot, rot_mat);
+ var m = g_root.localMatrix;
+ g_math.matrix4.setUpper3x3(m, g_thisRot);
+ g_root.localMatrix = m;
+
+ updateClient();
+ }
+}
+
+function stopDragging(e) {
+ g_dragging = false;
+}
+
+function updateClient() {
+ // If we are in RENDERMODE_ON_DEMAND mode then set the render mode again
+ // which will cause the client re-render the display.
+ if (g_client.renderMode == g_o3d.Client.RENDERMODE_ON_DEMAND) {
+ g_client.render();
+ }
+}
+
+function scrollMe(e) {
+ if (e.deltaY) {
+ g_camera.eye =
+ g_math.mulScalarVector((e.deltaY < 0 ? 11 : 13) / 12, g_camera.eye);
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(g_camera.eye,
+ g_camera.target,
+ [0, 1, 0]);
+ updateClient();
+ }
+}
+
+function loadFile(path) {
+ function callback(pack, parent, exception) {
+ if (exception) {
+ alert('Could not load: ' + path + '\n' + exception);
+ return;
+ }
+ // Generate draw elements and setup material draw lists.
+ o3djs.pack.preparePack(pack, g_viewInfo);
+ }
+ // Create a new transform for the loaded file
+ var parent = g_pack.createObject('Transform');
+ parent.parent = g_client.root;
+
+ if (path != null) {
+ o3djs.scene.loadScene(g_client, g_pack, parent, path, callback);
+ }
+
+ return parent;
+}
+
+function setClientSize() {
+ var newWidth = g_client.width;
+ var newHeight = g_client.height;
+
+ if (newWidth != g_o3dWidth || newHeight != g_o3dHeight) {
+ g_o3dWidth = newWidth;
+ g_o3dHeight = newHeight;
+
+ // Set the perspective projection matrix
+ g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(45), g_o3dWidth / g_o3dHeight, 0.1, 100);
+
+ // Sets a new area size for arcball.
+ g_aball.setAreaSize(g_o3dWidth, g_o3dHeight);
+
+ //o3djs.dump.dump("areaWidth: " + g_o3dWidth + "\n");
+ //o3djs.dump.dump("areaHeight: " + g_o3dHeight + "\n");
+ }
+}
+
+function resize() {
+ setClientSize();
+}
+
+function setRenderMode(event) {
+ var mode = (event) ? event.target.value : window.event.srcElement.value;
+ switch (mode) {
+ case 'continuous':
+ g_client.renderMode = g_o3d.Client.RENDERMODE_CONTINUOUS;
+ break;
+ case 'ondemand':
+ g_client.renderMode = g_o3d.Client.RENDERMODE_ON_DEMAND;
+ break;
+ }
+}
+
+function onRender() {
+ g_framesRendered++;
+ g_viewInfo.clearBuffer.clearColor = [
+ 1 / 38 * (g_framesRendered % 38),
+ 1 / 39 * (g_framesRendered % 39),
+ 1 / 41 * (g_framesRendered % 41),
+ 1];
+ resize();
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and loads the scene into the transform graph.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ var path = window.location.href;
+ var index = path.lastIndexOf('/');
+ path = path.substring(0, index + 1) + 'assets/teapot.o3dtgz';
+
+ g_o3dElement = clientElements[0];
+ g_o3d = g_o3dElement.o3d;
+ g_math = o3djs.math;
+ g_quaternions = o3djs.quaternions;
+ g_client = g_o3dElement.client;
+
+ g_pack = g_client.createPack();
+ g_lastRot = g_math.matrix4.identity();
+ g_thisRot = g_math.matrix4.identity();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ var root = g_client.root;
+
+ var target = [0, 0, 0];
+ var eye = [0, 0, 5];
+ var up = [0, 1, 0];
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(eye, target, up);
+
+ g_aball = o3djs.arcball.create(100, 100);
+ setClientSize();
+
+ g_root = loadFile(path);
+
+ o3djs.event.addEventListener(g_o3dElement, 'mousedown', startDragging);
+ o3djs.event.addEventListener(g_o3dElement, 'mousemove', drag);
+ o3djs.event.addEventListener(g_o3dElement, 'mouseup', stopDragging);
+ o3djs.event.addEventListener(g_o3dElement, 'wheel', scrollMe);
+ document.getElementById('rendermode0').onclick = setRenderMode;
+ document.getElementById('rendermode1').onclick = setRenderMode;
+
+ g_client.setRenderCallback(onRender);
+}
+
+/**
+ * Removes any callbacks so they don't get called after the page has unloaded.
+ */
+function uninit() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+</script>
+<table width="100%" style="height:100%;">
+<tr width="100%" style="height:100%;"><td valign="middle" align="center">
+<h1>
+Render Mode Example.
+</h1>
+<p>The point of this demo is to show that for certain kinds of 3d applications
+ that don't have animation, for example a map viewer, you can set the client's
+render mode to RENDERMODE_ON_DEMAND which will render the 3d one time and then
+stop rendering. This has the advantage of not wasting CPU cycles rendering
+something that does not change over and over.</p>
+<p>The background changes color each time the scene is rendered. The sample
+should start with the scene being constantly rendered but click
+RENDERMODE_ON_DEMAND and you'll see it rendered only when it needs to be
+as determined by the sample. </p>
+<table id="container" width="90%" height="60%" border="2">
+<tr height="100%"><td>
+<div id="o3d" style="width: 100%; height: 100%;"></div>
+</td></tr>
+</table>
+<input type="radio" id="rendermode0" name="rendermode" value="continuous" checked="true"><label for="rendermode0">RENDERMODE_CONTINUOUS (draw as often as possible)</label><br/>
+<input type="radio" id="rendermode1" name="rendermode" value="ondemand"><label for="rendermode1">RENDERMODE_ON_DEMAND (draw once then as the OS requests it like when a window is uncovered)</label><br/>
+<p>
+Drag The Mouse To Rotate<br/>
+Scrollwheel To Zoom<br/>
+Resize The Window To Resize The View
+</p>
+</td></tr></table>
+</body>
+</html>
+
+
diff --git a/o3d/samples/render-targets.html b/o3d/samples/render-targets.html
new file mode 100644
index 0000000..75d78b7
--- /dev/null
+++ b/o3d/samples/render-targets.html
@@ -0,0 +1,340 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+This sample shows how to setup a basic render graph making use of RenderSurface
+nodes to implement render-target functionality.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+O3D: Render-Target Sample
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.pack');
+o3djs.require('o3djs.camera');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.scene');
+
+// constants.
+var RENDER_TARGET_WIDTH = 512;
+var RENDER_TARGET_HEIGHT = 512;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_viewInfo;
+var g_pack;
+var g_teapotPack;
+var g_teapotRoot;
+var g_cubeRoot;
+
+/**
+ * Loads a scene into the transform graph.
+ * @param {!o3d.Pack} pack Pack to load scene into.
+ * @param {string} fileName filename of the scene.
+ * @param {!o3d.Transform} parent parent node in the transform graph to
+ * which to load the scene into.
+ * @param {!o3djs.rendergraph.ViewInfo} viewInfo who's view and projection will
+ * be set from the scene after it's loaded.
+ */
+function loadScene(pack, fileName, parent, viewInfo) {
+ // Get our full path to the scene
+ var scenePath = o3djs.util.getCurrentURI() + fileName;
+
+ // Load the file given the full path, and call the callback function
+ // when its done loading.
+ o3djs.scene.loadScene(g_client, pack, parent, scenePath, callback);
+
+ /**
+ * Our callback is called once the scene has been loaded into memory
+ * from the web or locally.
+ * @param {!o3d.Pack} pack The pack that was passed in above.
+ * @param {!o3d.Transform} parent The parent that was passed in above.
+ * @param {*} exception null if loading succeeded.
+ */
+ function callback(pack, parent, exception) {
+ if (exception) {
+ alert('Could not load: ' + fileName + '\n' + exception);
+ return;
+ }
+ // Get a CameraInfo (an object with a view and projection matrix)
+ // using our javascript library function
+ var cameraInfo = o3djs.camera.getViewAndProjectionFromCameras(
+ parent,
+ RENDER_TARGET_WIDTH,
+ RENDER_TARGET_HEIGHT);
+
+ // Copy the view and projection to the passed in viewInfo structure..
+ viewInfo.drawContext.view = cameraInfo.view;
+ viewInfo.drawContext.projection = cameraInfo.projection;
+
+ // Generate draw elements and setup material draw lists.
+ o3djs.pack.preparePack(pack, viewInfo);
+
+ window.g_finished = true; // for selenium testing.
+ }
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ // These are here so that they are visible to both the browser (so
+ // selenium sees them) and the embedded V8 engine.
+ window.g_clock = 0;
+ window.g_timeMult = 1;
+ window.g_finished = false; // for selenium testing.
+
+ // Comment out the line below to run the sample in the browser
+ // JavaScript engine. This may be helpful for debugging.
+ o3djs.util.setMainEngine(o3djs.util.Engine.V8);
+
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and loads the scene into the transform graph.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ var o3d = clientElements[0];
+ g_o3d = o3d.o3d;
+ g_math = o3djs.math;
+
+ // Set window.g_client as well. Otherwise when the sample runs in
+ // V8, selenium won't be able to find this variable (it can only see
+ // the browser environment).
+ window.g_client = g_client = o3d.client;
+
+ // Creates a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create a sub-pack to manage the resources required for the teapot.
+ g_teapotPack = g_client.createPack();
+
+ // Create the texture required for the color render-target.
+ var texture2d = g_pack.createTexture2D(RENDER_TARGET_WIDTH,
+ RENDER_TARGET_HEIGHT,
+ g_o3d.Texture.XRGB8, 1, true);
+ var renderSurface = texture2d.getRenderSurface(0, g_pack);
+
+ // Create the depth-stencil buffer required when rendering the teapot.
+ var depthSurface = g_pack.createDepthStencilSurface(RENDER_TARGET_WIDTH,
+ RENDER_TARGET_HEIGHT);
+
+ g_cubeRoot = g_pack.createObject('Transform');
+ g_teapotRoot = g_pack.createObject('Transform');
+ g_cubeRoot.parent = g_client.root;
+ g_teapotRoot.parent = g_client.root;
+
+ var teapotRenderRoot = g_pack.createObject('RenderNode');
+ teapotRenderRoot.priority = 0;
+ var cubeRenderRoot = g_pack.createObject('RenderNode');
+ cubeRenderRoot.priority = 1;
+
+ teapotRenderRoot.parent = g_client.renderGraphRoot;
+ cubeRenderRoot.parent = g_client.renderGraphRoot;
+
+ var renderSurfaceSet = g_pack.createObject('RenderSurfaceSet');
+ renderSurfaceSet.renderSurface = renderSurface;
+ renderSurfaceSet.renderDepthStencilSurface = depthSurface;
+ renderSurfaceSet.parent = teapotRenderRoot;
+
+ // Create the render graph for a view.
+ var teapotViewInfo = o3djs.rendergraph.createBasicView(
+ g_teapotPack,
+ g_teapotRoot,
+ renderSurfaceSet);
+
+ var cubeViewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_cubeRoot,
+ cubeRenderRoot);
+
+ var myMaterial = g_pack.createObject('Material');
+ myMaterial.drawList = cubeViewInfo.performanceDrawList;
+
+ var effect = g_pack.createObject('Effect');
+ var fxString = o3djs.util.getElementContentById('fx');
+ effect.loadFromFXString(fxString);
+
+ // Apply our effect to this material.
+ myMaterial.effect = effect;
+ effect.createUniformParameters(myMaterial);
+
+ var samplerParam = myMaterial.getParam('texSampler0');
+ var sampler = g_pack.createObject('Sampler');
+ sampler.texture = texture2d;
+ samplerParam.value = sampler;
+
+ // Create a transform to put our data on.
+ var myDataRoot = g_pack.createObject('Transform');
+
+ // Connect our root to the client.
+ myDataRoot.parent = g_teapotRoot;
+ var vec3 = [0.5, 0.5, 0.5];
+
+ var cubeShape = o3djs.primitives.createCube(g_pack,
+ myMaterial,
+ 300.0);
+
+ // Attach the cube to the root of the transform graph.
+ g_cubeRoot.addShape(cubeShape);
+
+ o3djs.pack.preparePack(g_pack, cubeViewInfo);
+
+ // Points at the origin.
+ var target = [0, 0, 0];
+ // Puts the camera outside the origin.
+ var eye = [500, 500, 500];
+ // Defines UP as Y up.
+ var up = [0, 1, 0];
+ cubeViewInfo.drawContext.view = g_math.matrix4.lookAt(eye,
+ target,
+ up);
+
+ cubeViewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(30),
+ g_client.width / g_client.height, 1, 5000);
+
+ // Load the scene into the transform graph as a child myDataRoot
+ loadScene(g_teapotPack, 'assets/teapot.o3dtgz', myDataRoot, teapotViewInfo);
+
+ // Set a render callback.
+ g_client.setRenderCallback(onRender);
+}
+
+/**
+ * Called every frame.
+ * @param {!o3d.RenderEvent} renderEvent Rendering Information.
+ */
+function onRender(renderEvent) {
+ var elapsedTime = renderEvent.elapsedTime;
+ // Update g_clock in the browser and cache a V8 copy that can be
+ // accessed efficiently. g_clock must be in the browser for selenium.
+ var clock = window.g_clock + elapsedTime * window.g_timeMult;
+ window.g_clock = clock;
+
+ g_teapotRoot.identity();
+ g_teapotRoot.rotateX(clock);
+ g_teapotRoot.rotateY(clock * 1.3);
+
+ g_cubeRoot.identity();
+ g_cubeRoot.rotateX(clock * 0.4);
+ g_cubeRoot.rotateY(clock * 0.1);
+}
+
+/**
+ * Cleanup before exiting.
+ */
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+</script>
+</head>
+<body onload="init()" onunload="unload()">
+<h1>Basic Render-Target Example</h1>
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 600px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+<!--
+ We embed the code for our effect inside this hidden textarea.
+ Effects contain the functions that define
+ the vertex and pixel shaders used by our shape.
+-->
+<!-- Don't render the textarea -->
+<div style="display:none">
+<textarea id="fx" name="fx" cols="80" rows="20">
+ // The 4x4 world view projection matrix.
+ float4x4 viewProjection : WorldViewProjection;
+
+ sampler texSampler0;
+
+ // input parameters for our vertex shader
+ struct VertexShaderInput {
+ float4 position : POSITION;
+ float2 texcoord : TEXCOORD0;
+ };
+
+ // input parameters for our pixel shader
+ // also the output parameters for our vertex shader
+ struct PixelShaderInput {
+ float4 position : POSITION;
+ float2 texcoord : TEXCOORD0;
+ };
+
+ /**
+ * Vertex Shader performing basic viewing transformation.
+ */
+ PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ /**
+ * We transform each vertex by the view projection matrix to bring
+ * it from world space to projection space.
+ *
+ * We return its color unchanged.
+ */
+ PixelShaderInput output;
+
+ output.position = mul(input.position, viewProjection);
+ output.texcoord = input.texcoord;
+ return output;
+ }
+ /**
+ * Pixel Shader
+ */
+ float4 pixelShaderFunction(PixelShaderInput input): COLOR {
+ return tex2D(texSampler0, input.texcoord * 5) + float4(0.2, 0.2, 0.0, 1.0);
+ }
+
+ // Here we tell our effect file the functions which specify our vertex
+ // and pixel shaders.
+ // #o3d VertexShaderEntryPoint vertexShaderFunction
+ // #o3d PixelShaderEntryPoint pixelShaderFunction
+ // #o3d MatrixLoadOrder RowMajor
+</textarea>
+</div>
+</body>
+
+</html>
diff --git a/o3d/samples/rotatemodel.html b/o3d/samples/rotatemodel.html
new file mode 100644
index 0000000..eb9c2da
--- /dev/null
+++ b/o3d/samples/rotatemodel.html
@@ -0,0 +1,247 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+O3D Tutorial A2
+
+In this tutorial, we show how to perform transformations on a scene that
+we have loaded.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Tutorial A2: Transformations
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="o3djs/base.js"></script>
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.camera');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.pack');
+o3djs.require('o3djs.scene');
+
+// Events
+// Run the init() function once the page has finished loading.
+window.onload = init;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_viewInfo;
+var g_pack;
+var g_keyPressDelta = 0.05;
+var g_finished = false; // for selenium testing
+
+// root node of scene
+var g_sceneRoot;
+
+/**
+ * Loads a scene into the transform graph.
+ * @param {!o3d.Pack} pack Pack to load scene into.
+ * @param {string} fileName filename of the scene.
+ * @param {!o3d.Transform} parent parent node in the transform graph to
+ * which to load the scene into.
+ */
+function loadScene(pack, fileName, parent) {
+ // Get our full path to the scene
+ var scenePath = o3djs.util.getCurrentURI() + fileName;
+
+ // Load the file given the full path, and call the callback function
+ // when its done loading.
+ o3djs.scene.loadScene(g_client, pack, parent, scenePath, callback);
+
+ /**
+ * Our callback is called once the scene has been loaded into memory
+ * from the web or locally.
+ * @param {!o3d.Pack} pack The pack that was passed in above.
+ * @param {!o3d.Transform} parent The parent that was passed in above.
+ * @param {*} exception null if loading succeeded.
+ */
+ function callback(pack, parent, exception) {
+ if (exception) {
+ alert('Could not load: ' + fileName + '\n' + exception);
+ return;
+ }
+ // Get a CameraInfo (an object with a view and projection matrix)
+ // using our javascript library function
+ var cameraInfo = o3djs.camera.getViewAndProjectionFromCameras(
+ parent,
+ g_client.width,
+ g_client.height);
+
+ // Copy the view and projection to the draw context.
+ g_viewInfo.drawContext.view = cameraInfo.view;
+ g_viewInfo.drawContext.projection = cameraInfo.projection;
+
+ // Generate draw elements and setup material draw lists.
+ o3djs.pack.preparePack(pack, g_viewInfo);
+
+ g_finished = true; // for selenium testing
+ }
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and loads the scene into the transform graph.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = o3dElement.client;
+
+ // Create a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Create a transform node to act as the 'root' of the scene
+ g_sceneRoot = g_pack.createObject('Transform');
+ // Attach it to the root of the transform graph.
+ g_sceneRoot.parent = g_client.root;
+
+ // Load the scene into the transform graph as a child of the g_sceneRoot.
+ loadScene(g_pack, 'assets/teapot.o3dtgz', g_sceneRoot);
+
+ // Execute keyPressedCallback(..) when we detect a keypress on the document.
+ window.document.onkeypress = keyPressedCallback;
+}
+
+/**
+ * Function performing the rotate action in response to a key-press.
+ * Rotates the scene based on key pressed. (w ,s, a, d). Note that the x and
+ * y-axis referenced here are relative to the current view of the scene.
+ * @param {keyPressed} The letter pressed, in lower case.
+ * @param {delta} The angle by which the scene should be rotated.
+ * @return true if an action was taken.
+ */
+function keyPressedAction(keyPressed, delta) {
+ var actionTaken = false;
+ switch(keyPressed) {
+ case 'a':
+ g_sceneRoot.localMatrix =
+ g_math.matrix4.mul(g_sceneRoot.localMatrix,
+ g_math.matrix4.rotationY(-delta));
+ actionTaken = true;
+ break;
+ case 'd':
+ g_sceneRoot.localMatrix =
+ g_math.matrix4.mul(g_sceneRoot.localMatrix,
+ g_math.matrix4.rotationY(delta));
+ actionTaken = true;
+ break;
+ case 'w':
+ g_sceneRoot.localMatrix =
+ g_math.matrix4.mul(g_sceneRoot.localMatrix,
+ g_math.matrix4.rotationX(-delta));
+ actionTaken = true;
+ break;
+ case 's':
+ g_sceneRoot.localMatrix =
+ g_math.matrix4.mul(g_sceneRoot.localMatrix,
+ g_math.matrix4.rotationX(delta));
+ actionTaken = true;
+ break;
+ }
+ return actionTaken;
+}
+
+/**
+ * Callback for the keypress event.
+ * Invokes the action to be performed for the key pressed.
+ * @param {event} keyPress event passed to us by javascript.
+ */
+function keyPressedCallback(event) {
+ event = event || window.event;
+
+ // Ignore accelerator key messages.
+ if (event.metaKey)
+ return;
+
+ var keyChar = String.fromCharCode(o3djs.event.getEventKeyChar(event));
+ // Just in case they have capslock on.
+ keyChar = keyChar.toLowerCase();
+
+ if (keyPressedAction(keyChar, g_keyPressDelta)) {
+ o3djs.event.cancel(event);
+ }
+}
+
+/**
+ * Resets the view of the scene by resetting its local matrix to the identity
+ * matrix.
+ */
+function resetView() {
+ g_sceneRoot.identity();
+}
+
+</script>
+</head>
+<body>
+<h1>Transformations</h1>
+This tutorial shows how to perform transformations on a scene that
+has been loaded.
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 600px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+<p>Use W, S, A, D to rotate.</p>
+<form name="default_form" action="#" method="get">
+<input type="button" value="Reset view" onclick="resetView()" />
+<input type="button" value="W" id="W"
+ onclick="keyPressedAction('w', g_keyPressDelta);" />
+<input type="button" value="S" id="S"
+ onclick="keyPressedAction('s', g_keyPressDelta);" />
+<input type="button" value="A" id="A"
+ onclick="keyPressedAction('a', g_keyPressDelta);" />
+<input type="button" value="D" id="D"
+ onclick="keyPressedAction('d', g_keyPressDelta);" />
+</form>
+</body>
+</html>
diff --git a/o3d/samples/sampler_index.html b/o3d/samples/sampler_index.html
new file mode 100644
index 0000000..b080a7c
--- /dev/null
+++ b/o3d/samples/sampler_index.html
@@ -0,0 +1,187 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+
+ <script src="interactive_sampler_assets/utils.js" type="text/javascript" charset="utf-8"></script>
+
+ <!-- CSS -->
+ <link rel="stylesheet" href="third_party/lightbox/lightbox.css"
+ media="screen,projection" type="text/css" />
+
+ <!--[if !IE]><!-->
+ <link rel="stylesheet" href="interactive_sampler_assets/styles.css" media="screen,projection" type="text/css" />
+ <!-- <![endif]-->
+ <!--[if IE]>
+ <link rel="stylesheet" href="interactive_sampler_assets/styles_ie.css" media="screen,projection" type="text/css" />
+ <![endif]-->
+
+ <!-- JavaScript -->
+
+ <title>O3D Interactive Sampler</title>
+
+ <script type="text/javascript"
+ src="third_party/lightbox/prototype.js"></script>
+ <script type="text/javascript"
+ src="third_party/lightbox/lightbox-iframe.js"></script>
+ <script src="third_party/codemirror/js/codemirror.js" type="text/javascript"
+ charset="utf-8"></script>
+ <link rel="stylesheet" type="text/css"
+ href="third_party/codemirror/css/docs.css"/>
+
+ <script type="text/javascript" charset="utf-8">
+
+ var is;
+
+ function init() {
+
+ is = new InteractiveSample();
+ is.init(document.getElementById('editor'));
+ }
+
+ function showInline() {
+ _gel('outputContainer').style.display = 'table-cell';
+ var codeContainer = _gel('codeContainer');
+ var codeIframe = codeContainer.getElementsByTagName('iframe')[0];
+ codeIframe.style.width = "100%";
+ }
+
+
+ addEvent(window, 'load', init);
+ </script>
+</head>
+<body>
+ <div id="container">
+ <div class="bluediv">
+ <table style="width:100%;">
+ <tr>
+ <td>
+ <h1 id="header">Client 3D Interactive Samples</h1>
+ </td>
+ <td id="googleLogoTD">
+ <img src="interactive_sampler_assets/images/google-small.png" />
+ </td>
+ </tr>
+ </table>
+
+ </div>
+
+ <div id="content">
+ <table class="contentTable">
+ <tr>
+
+ <td id="selectContainer">
+ <h2>Pick</h2>
+ <div id="selectCode">
+ </div>
+
+ </td>
+
+ <td id="codeContainer">
+ <h2>Edit</h2>
+
+ <div id="tab_bar">
+ </div>
+
+ <div id="button_bar">
+ &nbsp;
+ <!-- <input type="button" value="<- | ->" onclick="is.increaseWidth();" /> -->
+ <!-- <input type="button" value="-" onclick="is.decreaseCodeBoxHeight();" />
+ <input type="button" value="+" onclick="is.increaseCodeBoxHeight();"/> -->
+ </div>
+
+ <!-- todo check styles on here -->
+ <div id="editor" style="border: 1px solid black; margin-top: 2px"></div>
+ </td>
+ <script type="text/javascript" charset="utf-8">
+ var htmlEditor = new CodeMirror(document.getElementById('editor'), {
+ parserfile : 'parsexml.js',
+ stylesheet : 'third_party/codemirror/css/xmlcolors.css',
+ path : 'third_party/codemirror/js/',
+ height : '550px',
+ content: ''
+ });
+
+ var jsEditor = new CodeMirror(document.getElementById('editor'), {
+ parserfile : ["tokenizejavascript.js", "parsejavascript.js"],
+ stylesheet : 'third_party/codemirror/css/jscolors.css',
+ autoMatchParens : true,
+ path : 'third_party/codemirror/js/',
+ height : '550px',
+ content: ''
+ });
+
+ jsEditor.frame.style.display = 'none';
+
+ </script>
+ <td id="outputContainer">
+ <h2>Run</h2>
+ <div style="width: 300px;margin-top:18px;">
+ <a href="iframeit.html" style="text-decoration: none"
+ class="lbOn"><input type="button" value="Run In New Window" onclick="is.prepareAllCodeRun();" /></a>
+ <input type="button" id="runHere" value="Run Here" onclick="is.runJS();" />
+ <div id="o3d" style="width: 300px; height: 300px; background: gray;"></div></div>
+ </td>
+
+
+ </tr>
+
+
+
+ <tr>
+ <td class="footer">
+ <div style="background-image: url(interactive_sampler_assets/images/sprites.gif);background-position: -28px -28px;height:53px;margin:12px 0pt;width:143px;"></div>
+ </td>
+ <td class="footer" style="vertical-align: middle; font-size:80%; text-align: center">
+ ©2008 Google -
+ <a href="http://code.google.com/">Code Home</a> - <a href="http://www.google.com/accounts/TOS">Site Terms of Service</a> -
+ <a href="http://www.google.com/privacy.html">Privacy Policy</a> -
+ <a href="http://code.google.com/more/">Site Directory</a>
+ </td>
+ </tr>
+ </table>
+ </div>
+</div>
+
+<div id="HTMLforInlineJavascript" style="display:none;"></div>
+
+
+<!-- Scripts at bottom means better loading time ! -->
+<!-- <script src="codepress/codepress.js" type="text/javascript"></script> -->
+<script src="interactive_samples.js" type="text/javascript" charset="utf-8"></script>
+<script src="interactive_logic.js" type="text/javascript" charset="utf-8"></script>
+</body>
+</html>
diff --git a/o3d/samples/scatter-chart.html b/o3d/samples/scatter-chart.html
new file mode 100644
index 0000000..d6d72527d
--- /dev/null
+++ b/o3d/samples/scatter-chart.html
@@ -0,0 +1,426 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+Scatter Chart Example
+
+This sample demonstrates how to plot a 3d scatter chart and how to rotate and zoom it
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+3D Scatter Chart
+</title>
+<script type="text/javascript" src="o3djs/base.js"></script>
+<script type="text/javascript" src="assets/teapot_vertices.js"></script>
+
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.quaternions');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.effect');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.arcball');
+o3djs.require('o3djs.event');
+
+// The initClient() function runs when the page has finished loading.
+window.onload = initClient;
+
+// global variables
+var g_o3dElement;
+var g_client;
+var g_o3d;
+var g_math;
+var g_quaternions;
+var g_pack;
+var g_viewInfo;
+var g_modelRoot;
+var g_eyeView;
+var g_cubeShape;
+var g_material;
+var g_aball;
+var g_thisRot;
+var g_lastRot;
+var g_zoomFactor;
+var g_dragging;
+var g_finished = false; // for selenium testing.
+
+/**
+ * Creates the client area.
+ */
+function initClient() {
+ o3djs.util.makeClients(main);
+}
+
+/**
+ * Initializes global variables, positions camera, draws the 3D chart.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function main(clientElements) {
+ // Init global variables.
+ initGlobals(clientElements);
+
+ // Set up the view and projection transformations.
+ initContext();
+
+ // Setup rendering on demand only.
+ g_client.renderMode = g_o3d.Client.RENDERMODE_ON_DEMAND;
+
+ // Add the 3D chart model to the transform hierarchy.
+ create3dChartModel();
+
+ // Start rendering.
+ g_client.render();
+
+ // Execute keyPressed(..) when we detect a keypress on the window or
+ // on the o3d object.
+ window.document.onkeypress = keyPressed;
+ g_o3dElement.onkeypress = keyPressed;
+
+ o3djs.event.addEventListener(g_o3dElement, 'mousedown', startDragging);
+ o3djs.event.addEventListener(g_o3dElement, 'mousemove', drag);
+ o3djs.event.addEventListener(g_o3dElement, 'mouseup', stopDragging);
+ o3djs.event.addEventListener(g_o3dElement, 'wheel', scrollMe);
+
+ g_finished = true; // for selenium testing.
+}
+
+/**
+ * Initializes global variables and libraries.
+ */
+function initGlobals(clientElements) {
+ // init o3d globals.
+ g_o3dElement = clientElements[0];
+ g_client = g_o3dElement.client;
+ g_o3d = g_o3dElement.o3d;
+ g_math = o3djs.math;
+ g_quaternions = o3djs.quaternions;
+
+ // Create an arcball.
+ g_aball = o3djs.arcball.create(g_o3dElement.clientWidth,
+ g_o3dElement.clientHeight);
+
+ // Create a pack to manage the objects created.
+ g_pack = g_client.createPack();
+
+ // Create a transform node to act as the 'root' of the model.
+ // Attach it to the root of the transform graph.
+ g_modelRoot = g_pack.createObject('Transform');
+ g_modelRoot.parent = g_client.root;
+
+ // Create the render graph for the view.
+ var clearColor = [.98, .98, .98, 1];
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot,
+ clearColor);
+
+ // Create a material for the objects rendered.
+ g_material = createPhongMaterial([1, 1, 1, 1]);
+
+ // Create a cube shape to simulate the scatter points.
+ g_cubeShape = o3djs.primitives.createCube(
+ g_pack,
+ g_material,
+ 1);
+}
+
+/**
+ * Initialize the original view of the model.
+ */
+function initContext() {
+ g_eyeView = [-35, 60, 140];
+ g_zoomFactor = 1.03;
+ g_dragging = false;
+ g_modelRoot.identity();
+ g_lastRot = g_math.matrix4.identity();
+ g_thisRot = g_math.matrix4.identity();
+
+ // Set up a perspective transformation for the projection.
+ g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(40), // 30 degree frustum.
+ g_o3dElement.clientWidth / g_o3dElement.clientHeight, // Aspect ratio.
+ 1, // Near plane.
+ 10000); // Far plane.
+
+ // Set up our view transformation to look towards the axes origin.
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ g_eyeView, // eye
+ [0, 0, 0], // target
+ [0, 1, 0]); // up
+}
+
+/**
+ * Reset to the original view of the model.
+ */
+function resetView() {
+ initContext();
+ g_client.render();
+}
+
+/**
+ * Creates a phong material based on the given single color.
+ * @param {Array} baseColor An array with 4 entries, the R,G,B, and A components
+ * of a color.
+ * @return {!o3d.Material} A phong material whose overall pigment is baseColor.
+ */
+function createPhongMaterial(baseColor) {
+ // Create a new, empty Material object.
+ var material = g_pack.createObject('Material');
+
+ var lightPosition = [1000, 2000, 3000];
+ o3djs.effect.attachStandardShader(
+ g_pack, material, lightPosition, 'phong');
+
+ material.drawList = g_viewInfo.performanceDrawList;
+
+ // Assign parameters to the phong material.
+ material.getParam('emissive').value = [0, 0, 0, 1];
+ material.getParam('ambient').value =
+ [.1 * baseColor[0], .1 * baseColor[1], .1 * baseColor[2], 1];
+ material.getParam('diffuse').value =
+ [.9 * baseColor[0], .9 * baseColor[1], .9 * baseColor[2], 1];
+ material.getParam('specular').value = [.5, .5, .5, 1];
+ material.getParam('shininess').value = 50;
+ material.getParam('lightColor').value = [1, 1, 1, 1];
+
+ return material;
+}
+
+/**
+ * Create a 3D Scatter object by plotting each data point on a 3D model.
+ */
+function createScatterObject() {
+ // size of a scatter point (represented as a small cube).
+ var pointSize = 0.5;
+
+ for (var i = 0; i < g_teapotVertices.length; i += 3) {
+ var varX = g_teapotVertices[i];
+ var varY = g_teapotVertices[i+2];
+ var varZ = g_teapotVertices[i+1];
+
+ // create the transform for the cube.
+ var transform = g_pack.createObject('Transform');
+ transform.parent = g_modelRoot;
+ transform.addShape(g_cubeShape);
+ // translate and scale the point correctly relative to origin
+ transform.translate(varX, varY, varZ);
+ transform.scale(pointSize, pointSize, pointSize);
+ transform.createParam('diffuse', 'ParamFloat4').value = [1, 0, 0, 1];
+ }
+}
+
+/**
+ * Creates a 3D chart model.
+ */
+function create3dChartModel() {
+ // create the x,y,z axes - use the cylinder primitive.
+ var cylinder_length = 90;
+ var cylinder_radius = 0.15;
+ var cylinder_subdivisions = 6;
+
+ var cylinder = o3djs.primitives.createCylinder(
+ g_pack,
+ g_material,
+ cylinder_radius, // Radius.
+ cylinder_length, // Depth.
+ cylinder_subdivisions, // Number of subdivisions.
+ 1);
+
+ var cylinder_x = g_pack.createObject('Transform');
+ cylinder_x.parent = g_modelRoot;
+ cylinder_x.addShape(cylinder);
+ cylinder_x.createParam('diffuse', 'ParamFloat4').value = [1, 0.2, 0.2, 1];
+ cylinder_x.rotateZ(Math.PI / 2);
+
+ var cylinder_y = g_pack.createObject('Transform');
+ cylinder_y.parent = g_modelRoot;
+ cylinder_y.addShape(cylinder);
+ cylinder_y.createParam('diffuse', 'ParamFloat4').value = [0.2, 1, 0.2, 1];
+ cylinder_y.rotateX(Math.PI / 2);
+
+ var cylinder_z = g_pack.createObject('Transform');
+ cylinder_z.parent = g_modelRoot;
+ cylinder_z.addShape(cylinder);
+ cylinder_z.createParam('diffuse', 'ParamFloat4').value = [0.2, 0.2, 1, 1];
+
+ // Create the scatter object.
+ createScatterObject();
+}
+
+/**
+ * Callback for the keypress event.
+ * Rotates the 3D model along the x, y or z-axes based on key pressed.
+ * Zooms in and out by moving the viewpoint.
+ * @param {event} event keyPress event passed to us by javascript.
+ */
+function keyPressed(event) {
+ event = event || window.event;
+
+ // Ignore accelerator key messages.
+ if (event.metaKey)
+ return;
+ var keyChar = String.fromCharCode(o3djs.event.getEventKeyChar(event));
+ keyChar = keyChar.toLowerCase();
+
+ var delta = 0.03; // rotation delta.
+ // Create an array associating the keystroke to an axis about which to rotate.
+ // Then dereference that array to get the axis.
+
+ switch(keyChar) {
+ case 'a':
+ g_modelRoot.localMatrix =
+ g_math.matrix4.mul(g_modelRoot.localMatrix,
+ g_math.matrix4.rotationY(-delta));
+ break;
+ case 'd':
+ g_modelRoot.localMatrix =
+ g_math.matrix4.mul(g_modelRoot.localMatrix,
+ g_math.matrix4.rotationY(delta));
+ break;
+ case 'w':
+ g_modelRoot.localMatrix =
+ g_math.matrix4.mul(g_modelRoot.localMatrix,
+ g_math.matrix4.rotationX(-delta));
+ break;
+ case 's':
+ g_modelRoot.localMatrix =
+ g_math.matrix4.mul(g_modelRoot.localMatrix,
+ g_math.matrix4.rotationX(delta));
+ break;
+ case 'l':
+ g_modelRoot.localMatrix =
+ g_math.matrix4.mul(g_modelRoot.localMatrix,
+ g_math.matrix4.rotationZ(-delta));
+ break;
+ case 'k':
+ g_modelRoot.localMatrix =
+ g_math.matrix4.mul(g_modelRoot.localMatrix,
+ g_math.matrix4.rotationZ(delta));
+ break;
+ case 'i':
+ ZoomInOut(g_zoomFactor);
+ break;
+ case 'o':
+ ZoomInOut(1.0 / g_zoomFactor);
+ break;
+ }
+
+ o3djs.event.cancel(event);
+
+ g_client.render();
+}
+
+/**
+ * Zooms the model in / out by changing the viewpoint.
+ * @param {number} zoom zooming factor.
+ */
+function ZoomInOut(zoom) {
+ for (i = 0; i < g_eyeView.length; i += 1) {
+ g_eyeView[i] = g_eyeView[i] / zoom;
+ }
+
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ g_eyeView, // eye
+ [0, 0, 0], // target
+ [0, 1, 0]); // up
+}
+
+/**
+ * Start mouse dragging.
+ * @param {event} e event.
+ */
+function startDragging(e) {
+ g_lastRot = g_thisRot;
+ g_aball.click([e.x, e.y]);
+ g_dragging = true;
+}
+
+/**
+ * Use the arcball to rotate the model.
+ * Computes the rotation matrix.
+ * @param {event} e event.
+ */
+function drag(e) {
+ if (g_dragging) {
+ var rotationQuat = g_aball.drag([e.x, e.y]);
+ var rot_mat = g_quaternions.quaternionToRotation(rotationQuat);
+ g_thisRot = g_math.matrix4.mul(g_lastRot, rot_mat);
+ var m = g_modelRoot.localMatrix;
+ g_math.matrix4.setUpper3x3(m, g_thisRot);
+ g_modelRoot.localMatrix = m;
+
+ g_client.render();
+ }
+}
+
+/**
+ * Stop dragging.
+ * @param {event} e event.
+ */
+function stopDragging(e) {
+ g_dragging = false;
+}
+
+/**
+ * Using the mouse wheel zoom in and out of the model.
+ * @param {event} e event.
+ */
+function scrollMe(e) {
+ var zoom = (e.deltaY < 0) ? 1 / g_zoomFactor : g_zoomFactor;
+ ZoomInOut(zoom);
+ g_client.render();
+}
+
+</script>
+</head>
+
+<body>
+<h2>Scatter Chart - rotate &amp; zoom with mouse or keyboard</h2>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 600px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+<form name="default_form" action="#" method="get" >
+<div style="font-size:10">
+Rotate: (W, S), (A, D), (K, L) &nbsp; &nbsp; &nbsp;
+Zoom: (I, O)&nbsp; &nbsp; &nbsp;
+<input type="button" value="Reset View" onclick="resetView()"
+style="font-size:10"/>
+</div>
+</form>
+
+</body>
+</html>
diff --git a/o3d/samples/shader-test.html b/o3d/samples/shader-test.html
new file mode 100644
index 0000000..a437370
--- /dev/null
+++ b/o3d/samples/shader-test.html
@@ -0,0 +1,403 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+O3D Tutorial
+
+In this tutorial, we load a scene file and then apply various shaders to it.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Shader Test
+</title>
+<style type="text/css">
+ html, body {
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ border: none;
+ }
+</style>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.pack');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.camera');
+o3djs.require('o3djs.effect');
+o3djs.require('o3djs.loader');
+
+// Events
+// init() once the page has finished loading.
+// unload() when the page is unloaded.
+window.onload = init;
+window.onunload = unload;
+
+// global variables
+var g_o3d;
+var g_client;
+var g_viewInfo;
+var g_o3dElement;
+var g_pack;
+var g_root;
+var g_math;
+var g_timeMult = 1;
+var g_finished = false; // for selenium testing
+var g_currentTimeParam;
+var g_clock = 0;
+var g_shaderSelection = 0;
+var g_rotateOn = true;
+var g_o3dWidth; // width of our client area
+var g_o3dHeight; // height of our client area
+var g_shaders = ['diffuse',
+ 'checker',
+ 'bump',
+ 'bump',
+ 'texture-only',
+ 'texture-colormult',
+ 'tangent',
+ 'binormal',
+ 'normal',
+ 'solid-color',
+ 'vertex-color',
+ 'phong-with-colormult'];
+var g_effects = [];
+var g_bumpTextureSampler;
+var g_bumpBumpsSampler;
+
+// Our view and projection matrices
+// The view matrix transforms objects from world space to view space.
+var g_viewMatrix;
+// The projection matrix projects objects from view space to the screen.
+var g_projMatrix;
+
+/**
+ * Returns the path of where the file is located
+ * with the trailing slash
+ */
+function getCurrentPath() {
+ var path = window.location.href;
+ var index = path.lastIndexOf('/');
+ return path.substring(0, index + 1);
+}
+
+/**
+ * Turn rotation on.
+ */
+function turnRotateOn() {
+ g_rotateOn = true;
+}
+
+/**
+ * Turn rotation off.
+ */
+function turnRotateOff() {
+ g_rotateOn = false;
+}
+
+/**
+ * This is the code to animate the rotation.
+ * @param {o3d.RenderEvent} render_event The render event.
+ */
+function onrender(render_event) {
+ var elapsedTime = render_event.elapsedTime * g_timeMult;
+ var newWidth = g_client.width;
+ var newHeight = g_client.height;
+ if (newWidth != g_o3dWidth || newHeight != g_o3dHeight) {
+ g_o3dWidth = newWidth;
+ g_o3dHeight = newHeight;
+ setupCamera(g_pack, g_root);
+ }
+
+ g_clock += elapsedTime * (g_rotateOn ? 1 : 0);
+ g_root.identity();
+ g_root.rotateY(g_clock);
+
+ g_currentTimeParam.value = g_clock;
+}
+
+/**
+ * Sets the camera based on the imported file.
+ * @param {!o3d.Pack} pack Pack to create context in.
+ * @param {!o3d.Transform} root Root of tree to search for camera info.
+ */
+function setupCamera(pack, root) {
+ // Get a CameraInfo (an object with a view and projection matrix)
+ // using our javascript library function
+ var cameraInfo = o3djs.camera.getViewAndProjectionFromCameras(root,
+ g_o3dWidth,
+ g_o3dHeight);
+
+ // Copy the view and projection to the draw context.
+ g_viewInfo.drawContext.view = cameraInfo.view;
+ g_viewInfo.drawContext.projection = cameraInfo.projection;
+}
+
+/**
+ * Our callback is called once the scene has been loaded into memory
+ * from the web or locally.
+ * @param {!o3d.Pack} pack Variable referring to the scene's pack.
+ * @param {!o3d.Transform} parent Transform parent.
+ * @param {*} exception null if loading succeeded.
+ */
+function sceneCallback(pack, parent, exception) {
+ if (exception) {
+ alert('Could not load scene\n' + exception);
+ return;
+ }
+ setupCamera(pack, parent);
+
+ // Start with diffuse as default
+ applyShader(pack, 0);
+
+ // Add some vertex colors because the teapot does not have any
+ // and yet the vertex color shader requires them.
+ var streamBanks = pack.getObjectsByClassName('o3d.StreamBank');
+ for (var ss = 0; ss < streamBanks.length; ++ss) {
+ var streamBank = streamBanks[ss];
+ if (!streamBank.getVertexStream(g_o3d.Stream.COLOR, 0)) {
+ var positionField = streamBank.getVertexStream(g_o3d.Stream.POSITION,
+ 0).field;
+ var buffer = positionField.buffer;
+ var numElements = buffer.numElements;
+ var colorField = buffer.createField('FloatField', 4);
+
+ var colors = [];
+ for (var nn = 0; nn < numElements; ++nn) {
+ colors.push((nn * 40 / numElements) % 1,
+ (nn * 61 / numElements) % 1,
+ (nn * 83 / numElements) % 1,
+ 1);
+ }
+ colorField.setAt(0, colors);
+ streamBank.setVertexStream(g_o3d.Stream.COLOR, 0, colorField, 0);
+ }
+ }
+
+ // Generate draw elements and setup material draw lists.
+ o3djs.pack.preparePack(pack, g_viewInfo);
+}
+
+/**
+ * Looks up a Param and if it exists sets its value.
+ * @param {!o3d.ParamObject} object object to look for param on.
+ * @param {string} name name of Param to look up.
+ * @param {*} value Value to set param.
+ */
+function setParam(object, paramName, value) {
+ var param = object.getParam(paramName);
+ if (param) {
+ param.value = value;
+ }
+}
+
+/**
+ * Apply the desired shader to our scene.
+ * @param {!o3d.Pack} pack Variable referring to the scene's pack.
+ * @param {number} shaderNumber Index into g_effects of which shader to use.
+ */
+function applyShader(pack, shaderNumber) {
+ var materials = pack.getObjectsByClassName('o3d.Material');
+ // Make the change to each material. For our teapot, there is only one
+ // material.
+ for (var m = 0; m < materials.length; m++) {
+ var material = materials[m];
+ g_effects[shaderNumber].createUniformParameters(material);
+ material.effect = g_effects[shaderNumber];
+
+ // Set our shader values
+ var colorParamValue = [0.8, 0.8, 0.8, 1];
+ var lightPosParamValue = [600, 600, 1000];
+
+ setParam(material, 'lightPos', lightPosParamValue);
+ setParam(material, 'lightWorldPos', lightPosParamValue);
+ setParam(material, 'cameraEye', [197.58, -63.5702, 0]);
+ setParam(material, 'color', colorParamValue);
+ setParam(material, 'colorMult', [.75, .75, 75., 1]);
+ // only use the texture input addition to bump mapping if on selection 3
+ setParam(material, 'useTexture', (g_shaderSelection == 3) ? 1 : 0);
+ setParam(material, 'lightIntensity', [0.8, 0.8, 0.8, 1]);
+ setParam(material, 'ambientIntensity', [0.2, 0.2, 0.2, 1]);
+ setParam(material, 'emissive', [0, 0, 0, 1]);
+ setParam(material, 'ambient', [1, 1, 1, 1]);
+ setParam(material, 'diffuse', colorParamValue);
+ setParam(material, 'specular', [0.5, 0.5, 0.5, 1]);
+ setParam(material, 'shininess', 50);
+ setParam(material, 'BumpSampler', g_bumpBumpsSampler);
+ setParam(material, 'AmbientSampler', g_bumpTextureSampler);
+ setParam(material, 'DiffuseSampler', g_bumpTextureSampler);
+ setParam(material, 'texSampler0', g_bumpTextureSampler);
+
+ var timeParam = material.getParam('inputTime');
+ if (timeParam) {
+ timeParam.bind(g_currentTimeParam);
+ }
+ }
+}
+
+/**
+ * Creates our client area by looking for <div>s with an id that starts with
+ * "o3d".
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and loads the scene into the transform graph.
+ */
+function initStep2(clientElements) {
+ // Initialize global variables and libraries.
+ g_o3dElement = clientElements[0];
+ g_o3d = g_o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = g_o3dElement.client;
+
+ // Get the width and height of our client area. We will need this to create
+ // a projection matrix.
+ g_o3dWidth = g_client.width;
+ g_o3dHeight = g_client.height;
+
+ // Creates a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Creates a transform to put our data on.
+ g_root = g_pack.createObject('Transform');
+
+ // Connects our root to the client's root.
+ g_root.parent = g_client.root;
+
+ var paramObject = g_pack.createObject('ParamObject');
+ g_currentTimeParam = paramObject.createParam('timeParam','ParamFloat');
+
+ // Load effects
+ for(var s = 0; s < g_shaders.length; s++) {
+ g_effects[s] = g_pack.createObject('Effect');
+ var shaderString = 'shaders/' + g_shaders[s] + '.shader';
+ o3djs.effect.loadEffect(g_effects[s], shaderString);
+ }
+
+ var loader = o3djs.loader.createLoader(initStep3);
+ loader.loadTexture(g_pack, 'assets/normalmap.dds',
+ function(texture, exception) {
+ if (exception) {
+ alert(exception);
+ } else {
+ g_bumpBumpsSampler = g_pack.createObject('Sampler');
+ g_bumpBumpsSampler.texture = texture;
+ g_bumpBumpsSampler.mipFilter = g_o3d.Sampler.LINEAR;
+ }
+ });
+ loader.loadTexture(g_pack, 'assets/rock_texture.jpg',
+ function(texture, exception) {
+ if (exception) {
+ alert(exception);
+ } else {
+ g_bumpTextureSampler = g_pack.createObject('Sampler');
+ g_bumpTextureSampler.texture = texture;
+ g_bumpTextureSampler.mipFilter = g_o3d.Sampler.LINEAR;
+ }
+ });
+ var scenePath = getCurrentPath() + 'assets/teapot.o3dtgz';
+ loader.loadScene(g_client, g_pack, g_root, scenePath, sceneCallback);
+ loader.finish();
+}
+
+function initStep3() {
+ o3djs.event.addEventListener(g_o3dElement, 'mousedown', turnRotateOff);
+ o3djs.event.addEventListener(g_o3dElement, 'mouseup', turnRotateOn);
+
+ g_rotateOn = true;
+
+ // Tell our example to rotate.
+ g_client.setRenderCallback(onrender);
+
+ g_finished = true; // for selenium testing.
+}
+
+/**
+ * Swaps which shader we are using and applies it.
+ */
+function changeShader() {
+ var shaderNumber = document.getElementById("shaderSelect").value;
+ g_shaderSelection = parseFloat(shaderNumber);
+ applyShader(g_pack, g_shaderSelection);
+}
+
+/**
+ * Removes any callbacks so they don't get called after the page has unloaded.
+ */
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+
+</script>
+</head>
+<body>
+<h1>Shader Test</h1>
+This example is useful for testing a shader or checking a scene. Clicking on the scene will temporarily stop rotation.
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 100%; height: 80%;"></div>
+<!-- End of O3D plugin -->
+<p>
+<select id='shaderSelect' name='shaderSelect' onChange='changeShader()'>
+<option value='0' selected>Diffuse</option>
+<option value='1'>Checker</option>
+<option value='2'>Bump</option>
+<option value='3'>Bump With Texture</option>
+<option value='4'>Texture Only</option>
+<option value='5'>Texture with Color Multiplier</option>
+<option value='6'>Tangent</option>
+<option value='7'>Binormal</option>
+<option value='8'>Normal</option>
+<option value='9'>Solid Color</option>
+<option value='10'>Vertex Color</option>
+<option value='11'>Blinn-Phong with Color Multiplier</option>
+</select>
+</body>
+</html>
diff --git a/o3d/samples/shaders/README b/o3d/samples/shaders/README
new file mode 100644
index 0000000..d5367d4d
--- /dev/null
+++ b/o3d/samples/shaders/README
@@ -0,0 +1,16 @@
+README for samples/shaders
+
+This directory contains files of shader code. In order to use them, they should
+be loaded into an html textarea in your application or loaded using
+o3djs.effect.loadEffect. NOTE: o3djs.effect.loadEffect can only load
+files from the same domain as the application webpage and the files must be at
+or below the current directory of the webpage
+
+The purpose of this directory is not to be *the* library of shaders, but to
+provide some sample shaders as examples and starting points for people to build
+their own.
+
+Each shader has its own requirements. Some shaders need different streams or
+require certain types of texture samplers so a shader will not work with just
+any 3d object. Look at the shader and determine what its requirements are before
+using it in your application.
diff --git a/o3d/samples/shaders/binormal.shader b/o3d/samples/shaders/binormal.shader
new file mode 100644
index 0000000..1be9416
--- /dev/null
+++ b/o3d/samples/shaders/binormal.shader
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// The 4x4 world view projection matrix.
+float4x4 worldViewProjection : WorldViewProjection;
+
+// input parameters for our vertex shader
+struct VertexShaderInput {
+ float3 position : POSITION;
+ float3 binormal : BINORMAL;
+};
+
+// input parameters for our pixel shader
+// also the output parameters for our vertex shader
+struct PixelShaderInput {
+ float4 position : POSITION;
+ float3 binormal : TEXCOORD1;
+};
+
+PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ PixelShaderInput output;
+ output.position = mul(float4(input.position, 1.0), worldViewProjection);
+ output.binormal = input.binormal;
+ return output;
+}
+
+float4 pixelShaderFunction(PixelShaderInput input): COLOR {
+ return float4(normalize(input.binormal) * 0.5 + float3(0.5, 0.5, 0.5), 1);
+}
+
+// Here we tell our effect file *which* functions are
+// our vertex and pixel shaders.
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
diff --git a/o3d/samples/shaders/bump.shader b/o3d/samples/shaders/bump.shader
new file mode 100644
index 0000000..4d9eefa
--- /dev/null
+++ b/o3d/samples/shaders/bump.shader
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// The 4x4 world view projection matrix.
+float4x4 worldViewProjection : WorldViewProjection;
+float4x4 worldInverseTranspose : WorldInverseTranspose;
+float4x4 world : World;
+float4x4 viewInverse : ViewInverse;
+
+// whether to use texture
+float useTexture;
+
+float3 lightWorldPos;
+float4 lightIntensity;
+float4 ambientIntensity;
+float4 emissive;
+float4 ambient;
+float4 diffuse;
+float4 specular;
+float shininess;
+
+sampler2D AmbientSampler;
+sampler2D DiffuseSampler;
+sampler2D BumpSampler;
+
+// input parameters for our vertex shader
+struct VertexShaderInput {
+ float4 position : POSITION;
+ float4 normal : NORMAL;
+ float4 tangent : TANGENT;
+ float4 binormal : BINORMAL;
+ float2 texcoord0 : TEXCOORD0;
+};
+
+// input parameters for our pixel shader
+// also the output parameters for our vertex shader
+struct PixelShaderInput {
+ float4 position : POSITION;
+ float2 texcoord0 : TEXCOORD0;
+ float3 normal : TEXCOORD1;
+ float3 binormal : TEXCOORD2;
+ float3 tangent : TEXCOORD3;
+ float3 worldPosition : TEXCOORD4;
+};
+
+PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ PixelShaderInput output;
+
+ // Transform position into clip space.
+ output.position = mul(input.position, worldViewProjection);
+
+ // Transform the tangent frame into world space.
+ output.tangent = mul(input.tangent, worldInverseTranspose).xyz;
+ output.binormal = mul(input.binormal, worldInverseTranspose).xyz;
+ output.normal = mul(input.normal, worldInverseTranspose).xyz;
+
+ // Pass through the texture coordinates.
+ output.texcoord0 = input.texcoord0;
+
+ // Calculate surface position in world space. Used for lighting.
+ output.worldPosition = mul(input.position, world).xyz;
+
+ return output;
+}
+
+float4 pixelShaderFunction(PixelShaderInput input): COLOR {
+ // Construct a transform from tangent space into world space.
+ float3x3 tangentToWorld = float3x3(input.tangent,
+ input.binormal,
+ input.normal);
+
+ // Read the tangent space normal from the normal map and remove the bias so
+ // they are in the range [-0.5,0.5]. There is no need to scale by 2 because
+ // the vector will soon be normalized.
+ float3 tangentNormal = tex2D(BumpSampler, input.texcoord0.xy).xyz -
+ float3(0.5, 0.5, 0.5);
+
+ // Transform the normal into world space.
+ float3 worldNormal = mul(tangentNormal, tangentToWorld);
+ worldNormal = normalize(worldNormal);
+
+ // Read the diffuse and ambient colors.
+ float4 textureAmbient = float4(1, 1, 1, 1);
+ float4 textureDiffuse = float4(1, 1, 1, 1);
+ if (useTexture == 1) {
+ textureAmbient = tex2D(AmbientSampler, input.texcoord0.xy);
+ textureDiffuse = tex2D(DiffuseSampler, input.texcoord0.xy);
+ }
+
+ // Apply lighting in world space in case the world transform contains scaling.
+ float3 surfaceToLight = normalize(lightWorldPos.xyz -
+ input.worldPosition.xyz);
+ float3 surfaceToView = normalize(viewInverse[3].xyz - input.worldPosition);
+ float3 halfVector = normalize(surfaceToLight + surfaceToView);
+ float4 litResult = lit(dot(worldNormal, surfaceToLight),
+ dot(worldNormal, halfVector), shininess);
+ float4 outColor = ambientIntensity * ambient * textureAmbient;
+ outColor += lightIntensity * (diffuse * textureDiffuse * litResult.y +
+ specular * litResult.z);
+ outColor += emissive;
+ return float4(outColor.rgb, diffuse.a * textureDiffuse.a);
+}
+
+// Here we tell our effect file *which* functions are
+// our vertex and pixel shaders.
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
diff --git a/o3d/samples/shaders/checker.shader b/o3d/samples/shaders/checker.shader
new file mode 100644
index 0000000..d47c954
--- /dev/null
+++ b/o3d/samples/shaders/checker.shader
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// The 4x4 world view projection matrix.
+float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+float4x4 worldInverseTranspose : WORLDINVERSETRANSPOSE;
+float4x4 world : WORLD;
+
+// Default and light position
+float4 ambientIntensity;
+float4 ambient;
+float4 diffuse;
+float3 lightWorldPos;
+float3 cameraEye;
+
+// input parameters for our vertex shader
+struct VertexShaderInput {
+ float4 position : POSITION;
+ float4 normal : NORMAL;
+ float2 texcoord : TEXCOORD0;
+};
+
+// input parameters for our pixel shader
+struct PixelShaderInput {
+ float4 position : POSITION;
+ float2 texcoord : TEXCOORD0;
+ float3 normal : TEXCOORD1;
+ float3 worldPosition : TEXCOORD2;
+};
+
+// function for getting the checker pattern
+float4 checker(float2 uv) {
+ float checkSize = 4;
+ float fmodResult = fmod(floor(checkSize * uv.x) + floor(checkSize * uv.y),
+ 2.0);
+ if (fmodResult < 1) {
+ return float4(0, 1, 1, 1); // turquiose
+ } else {
+ return float4(1, 0, 1, 1); // magenta
+ }
+}
+
+/**
+ * Our vertex shader. In the vertex shader, we calculate the lighting.
+ * Then we'll combine it with our checker pattern input the pixel shader.
+ */
+PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ PixelShaderInput output;
+
+ // Transform position into clip space.
+ output.position = mul(input.position, worldViewProjection);
+
+ // Transform normal into world space, where we can do lighting
+ // calculations even if the world transform contains scaling.
+ output.normal = mul(input.normal, worldInverseTranspose).xyz;
+
+ // Calculate surface position in world space.
+ output.worldPosition = mul(input.position, world).xyz;
+
+ output.texcoord = input.texcoord;
+
+ return output;
+}
+
+/**
+ * Our pixel shader. We take the lighting color we got from the vertex sahder
+ * and combine it with our checker pattern. We only need to use the x
+ * coordinate of our input.col because we gave it uniform color
+ */
+float4 pixelShaderFunction(PixelShaderInput input): COLOR {
+ float3 surfaceToLight = normalize(lightWorldPos - input.worldPosition);
+
+ float3 worldNormal = normalize(input.normal);
+
+ // Apply diffuse lighting in world space in case the world transform
+ // contains scaling.
+ float4 check = checker(input.texcoord);
+ float4 directionalIntensity = saturate(dot(worldNormal, surfaceToLight));
+ float4 outColor = (ambientIntensity * ambient +
+ directionalIntensity * diffuse) * check;
+ return float4(outColor.rgb, diffuse.a);
+}
+
+// Here we tell our effect file *which* functions are
+// our vertex and pixel shaders.
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
diff --git a/o3d/samples/shaders/diffuse.shader b/o3d/samples/shaders/diffuse.shader
new file mode 100644
index 0000000..fcd03eb
--- /dev/null
+++ b/o3d/samples/shaders/diffuse.shader
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// The 4x4 world view projection matrix.
+float4x4 worldViewProjection : WorldViewProjection;
+float4x4 worldInverseTranspose : WorldInverseTranspose;
+float4x4 world : World;
+
+float4 ambientIntensity;
+float4 lightIntensity;
+float4 ambient;
+float4 diffuse;
+float3 lightWorldPos;
+
+// input parameters for our vertex shader
+struct VertexShaderInput {
+ float4 position : POSITION; // Position vector of vertex
+ float4 normal : NORMAL;
+};
+
+// input parameters for our pixel shader
+struct PixelShaderInput {
+ float4 position : POSITION;
+ float3 normal : TEXCOORD0;
+ float3 worldPosition : TEXCOORD1;
+};
+
+/**
+ * Our vertex shader.
+ */
+PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ PixelShaderInput output;
+
+ // Transform position into clip space.
+ output.position = mul(input.position, worldViewProjection);
+
+ // Transform normal into world space, where we can do lighting
+ // calculations even if the world transform contains scaling.
+ output.normal = mul(input.normal, worldInverseTranspose).xyz;
+
+ // Calculate surface position in world space.
+ output.worldPosition = mul(input.position, world).xyz;
+
+ return output;
+}
+
+/**
+ * Our pixel shader.
+ */
+float4 pixelShaderFunction(PixelShaderInput input): COLOR {
+ float3 surfaceToLight = normalize(lightWorldPos - input.worldPosition);
+
+ float3 worldNormal = normalize(input.normal);
+
+ // Apply diffuse lighting in world space in case the world transform
+ // contains scaling.
+ float4 directionalIntensity = lightIntensity *
+ saturate(dot(worldNormal, surfaceToLight));
+ float4 outColor = ambientIntensity * ambient + directionalIntensity * diffuse;
+ return float4(outColor.rgb, diffuse.a);
+}
+
+// Here we tell our effect file *which* functions are
+// our vertex and pixel shaders.
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
diff --git a/o3d/samples/shaders/normal.shader b/o3d/samples/shaders/normal.shader
new file mode 100644
index 0000000..24d52a8
--- /dev/null
+++ b/o3d/samples/shaders/normal.shader
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// The 4x4 world view projection matrix.
+float4x4 worldViewProjection : WorldViewProjection;
+
+// input parameters for our vertex shader
+struct VertexShaderInput {
+ float3 position : POSITION;
+ float3 normal : NORMAL;
+};
+
+// input parameters for our pixel shader
+// also the output parameters for our vertex shader
+struct PixelShaderInput {
+ float4 position : POSITION;
+ float3 normal : TEXCOORD0;
+};
+
+PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ PixelShaderInput output;
+ output.position = mul(float4(input.position, 1.0), worldViewProjection);
+ output.normal = input.normal;
+ return output;
+}
+
+float4 pixelShaderFunction(PixelShaderInput input): COLOR {
+ return float4(normalize(input.normal) * 0.5 + float3(0.5, 0.5, 0.5), 1);
+}
+
+// Here we tell our effect file *which* functions are
+// our vertex and pixel shaders.
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
diff --git a/o3d/samples/shaders/one-channel-texture.shader b/o3d/samples/shaders/one-channel-texture.shader
new file mode 100644
index 0000000..f012b6d
--- /dev/null
+++ b/o3d/samples/shaders/one-channel-texture.shader
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+
+// The texture sampler is used to access the texture bitmap in the fragment
+// shader.
+sampler texSampler0;
+
+// input parameters for our vertex shader
+struct PixelShaderInput {
+ float4 position : POSITION;
+ float2 texcoord : TEXCOORD0; // Texture coordinates
+};
+
+// input parameters for our pixel shader
+struct VertexShaderInput {
+ float4 position : POSITION;
+ float2 texcoord : TEXCOORD0; // Texture coordinates
+};
+
+/**
+ * Our vertex shader
+ */
+PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ PixelShaderInput output;
+ output.position = mul(input.position, worldViewProjection);
+ output.texcoord = input.texcoord;
+ return output;
+}
+
+/**
+ * Given the texture coordinates, our pixel shader grabs the corresponding
+ * color from the texture.
+ *
+ * NOTE: GL and D3D do NOT share compatible 1 channel texture formats.
+ *
+ * In D3D the sampler will return
+ *
+ * R = channel 0
+ * G = const 1
+ * B = const 1
+ * A = const 1
+ *
+ * In GL the sampler will return
+ *
+ * R = channel 0
+ * G = channel 0
+ * B = channel 0
+ * A = channel 0
+ *
+ * What that means is only R works across platforms. G, B and A are undefined
+ * and if you use them you'll get the wrong results.
+ */
+float4 pixelShaderFunction(PixelShaderInput input): COLOR {
+ // ** Use only valid channels. ** ---------+
+ // |
+ // V
+ return tex2D(texSampler0, input.texcoord).rrrr;
+}
+
+// Here we tell our effect file *which* functions are
+// our vertex and pixel shaders.
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
diff --git a/o3d/samples/shaders/phong-vertex-anim.shader b/o3d/samples/shaders/phong-vertex-anim.shader
new file mode 100644
index 0000000..b3da37f
--- /dev/null
+++ b/o3d/samples/shaders/phong-vertex-anim.shader
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// This shader expects to be passed vertices that are part of an x-z plane mesh
+// because it assumes the normal for each vertex before being offset by the
+// animation is 0, 1, 0.
+
+uniform float4x4 viewProjection : VIEWPROJECTION;
+uniform float3 lightWorldPos;
+uniform float4 lightIntensity;
+uniform float4x4 world : WORLD;
+uniform float4x4 viewInverse : VIEWINVERSE;
+uniform float4x4 worldInverseTranspose : WORLDINVERSETRANSPOSE;
+uniform float4 ambientIntensity;
+uniform float4 emissive;
+uniform float4 ambient;
+uniform float4 diffuse;
+uniform float4 specular;
+uniform float shininess;
+uniform float time;
+
+struct VertexShaderInput {
+ float4 position : POSITION;
+};
+
+struct PixelShaderInput {
+ float4 position : POSITION;
+ float3 normal : TEXCOORD1;
+ float3 worldPosition : TEXCOORD4;
+};
+
+PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ PixelShaderInput output;
+ float4 worldPosition = mul(input.position, world);
+ float animValue = time + (worldPosition.x + worldPosition.z) * 0.4;
+ float animSin = sin(animValue);
+ float animCos = cos(animValue);
+ float4 position = float4(
+ worldPosition.x,
+ worldPosition.y + animSin,
+ worldPosition.z,
+ 1);
+ output.position = mul(position, viewProjection);
+ float3 normal = normalize(float3(animCos, abs(animSin), animCos));
+ output.normal = mul(float4(normal, 0), worldInverseTranspose).xyz;
+ output.worldPosition = position.xyz;
+
+ return output;
+}
+
+float4 pixelShaderFunction(PixelShaderInput input) : COLOR {
+ float3 surfaceToLight = normalize(lightWorldPos - input.worldPosition);
+ float3 worldNormal = normalize(input.normal);
+ float3 surfaceToView = normalize(viewInverse[3].xyz - input.worldPosition);
+ float3 halfVector = normalize(surfaceToLight + surfaceToView);
+ float4 litResult = lit(dot(worldNormal, surfaceToLight),
+ dot(worldNormal, halfVector), shininess);
+ float4 outColor = ambientIntensity * ambient;
+ outColor += lightIntensity * (diffuse * litResult.y +
+ specular * litResult.z);
+ outColor += emissive;
+ return float4(outColor.rgb, diffuse.a);
+}
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
diff --git a/o3d/samples/shaders/phong-with-colormult.shader b/o3d/samples/shaders/phong-with-colormult.shader
new file mode 100644
index 0000000..756b6bb
--- /dev/null
+++ b/o3d/samples/shaders/phong-with-colormult.shader
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+uniform float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+uniform float4x4 world : WORLD;
+uniform float4x4 viewInverse : VIEWINVERSE;
+uniform float4x4 worldInverseTranspose : WORLDINVERSETRANSPOSE;
+uniform float3 lightWorldPos;
+uniform float4 ambientIntensity;
+uniform float4 lightIntensity;
+uniform float4 emissive;
+uniform float4 ambient;
+uniform float4 colorMult;
+uniform float4 diffuse;
+uniform float4 specular;
+uniform float shininess;
+
+struct VertexShaderInput {
+ float4 position : POSITION;
+ float4 normal : NORMAL;
+};
+
+struct PixelShaderInput {
+ float4 position : POSITION;
+ float3 normal : TEXCOORD1;
+ float3 worldPosition : TEXCOORD4;
+};
+
+PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ PixelShaderInput output;
+ output.position = mul(input.position, worldViewProjection);
+ float3 worldPosition = mul(input.position, world).xyz;
+ output.normal = mul(input.normal, worldInverseTranspose).xyz;
+ output.worldPosition = worldPosition;
+
+ return output;
+}
+
+float4 pixelShaderFunction(PixelShaderInput input) : COLOR {
+ float3 surfaceToLight = normalize(lightWorldPos - input.worldPosition);
+ float3 worldNormal = normalize(input.normal);
+ float3 surfaceToView = normalize(viewInverse[3].xyz - input.worldPosition);
+ float3 halfVector = normalize(surfaceToLight + surfaceToView);
+ float4 litResult = lit(dot(worldNormal, surfaceToLight),
+ dot(worldNormal, halfVector), shininess);
+ float4 outColor = ambientIntensity * ambient * colorMult;
+ outColor += lightIntensity * (diffuse * colorMult * litResult.y +
+ specular * litResult.z);
+ outColor += emissive;
+ return float4(outColor.rgb, diffuse.a * colorMult.a);
+}
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
diff --git a/o3d/samples/shaders/solid-color.shader b/o3d/samples/shaders/solid-color.shader
new file mode 100644
index 0000000..78d662f
--- /dev/null
+++ b/o3d/samples/shaders/solid-color.shader
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// The 4x4 world view projection matrix.
+float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+float4 color;
+
+// input parameters for our vertex shader
+struct VertexShaderInput {
+ float4 position : POSITION;
+};
+
+// input parameters for our pixel shader
+// also the output parameters for our vertex shader
+struct PixelShaderInput {
+ float4 position : POSITION;
+};
+
+/**
+ * Vertex Shader
+ */
+PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ /**
+ * We transform each vertex by the world view projection matrix to bring
+ * it from world space to projection space.
+ *
+ * We return its color unchanged.
+ */
+ PixelShaderInput output;
+
+ output.position = mul(input.position, worldViewProjection);
+ return output;
+}
+/**
+ * Pixel Shader - pixel shader does nothing but return the color.
+ */
+float4 pixelShaderFunction(PixelShaderInput input): COLOR {
+ return color;
+}
+
+// Here we tell our effect file the functions
+// which specify our vertex and pixel shaders.
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
diff --git a/o3d/samples/shaders/tangent.shader b/o3d/samples/shaders/tangent.shader
new file mode 100644
index 0000000..c0e953f
--- /dev/null
+++ b/o3d/samples/shaders/tangent.shader
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// The 4x4 world view projection matrix.
+float4x4 worldViewProjection : WorldViewProjection;
+
+// input parameters for our vertex shader
+struct VertexShaderInput {
+ float3 position : POSITION;
+ float3 tangent : TANGENT;
+};
+
+// input parameters for our pixel shader
+// also the output parameters for our vertex shader
+struct PixelShaderInput {
+ float4 position : POSITION;
+ float3 tangent : TEXCOORD2;
+};
+
+PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ PixelShaderInput output;
+ output.position = mul(float4(input.position, 1.0), worldViewProjection);
+ output.tangent = input.tangent;
+ return output;
+}
+
+float4 pixelShaderFunction(PixelShaderInput input): COLOR {
+ return float4(normalize(input.tangent) * 0.5 + float3(0.5, 0.5, 0.5), 1);
+}
+
+// Here we tell our effect file *which* functions are
+// our vertex and pixel shaders.
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
diff --git a/o3d/samples/shaders/texture-colormult.shader b/o3d/samples/shaders/texture-colormult.shader
new file mode 100644
index 0000000..b253e48
--- /dev/null
+++ b/o3d/samples/shaders/texture-colormult.shader
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+
+// This parameter lets us adjust the color and fade things in or out.
+float4 colorMult;
+
+// The texture sampler is used to access the texture bitmap in the fragment
+// shader.
+sampler texSampler0;
+
+// input parameters for our vertex shader
+struct VertexShaderInput {
+ float4 position : POSITION;
+ float2 texcoord : TEXCOORD0; // Texture coordinates
+};
+
+// input parameters for our pixel shader
+struct PixelShaderInput {
+ float4 position : POSITION;
+ float2 texcoord : TEXCOORD0; // Texture coordinates
+};
+
+/**
+ * Our vertex shader.
+ */
+PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ PixelShaderInput output;
+ output.position = mul(input.position, worldViewProjection);
+ output.texcoord = input.texcoord;
+ return output;
+}
+
+/* Given the texture coordinates, our pixel shader grabs the corresponding
+ * color from the texture.
+ */
+float4 pixelShaderFunction(PixelShaderInput input): COLOR {
+ return tex2D(texSampler0, input.texcoord) * colorMult;
+}
+
+// Here we tell our effect file *which* functions are
+// our vertex and pixel shaders.
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
diff --git a/o3d/samples/shaders/texture-only.shader b/o3d/samples/shaders/texture-only.shader
new file mode 100644
index 0000000..2f09dfe
--- /dev/null
+++ b/o3d/samples/shaders/texture-only.shader
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+
+// The texture sampler is used to access the texture bitmap in the fragment
+// shader.
+sampler texSampler0;
+
+// input parameters for our vertex shader
+struct PixelShaderInput {
+ float4 position : POSITION;
+ float2 texcoord : TEXCOORD0; // Texture coordinates
+};
+
+// input parameters for our pixel shader
+struct VertexShaderInput {
+ float4 position : POSITION;
+ float2 texcoord : TEXCOORD0; // Texture coordinates
+};
+
+/**
+ * Our vertex shader
+ */
+PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ PixelShaderInput output;
+ output.position = mul(input.position, worldViewProjection);
+ output.texcoord = input.texcoord;
+ return output;
+}
+
+/* Given the texture coordinates, our pixel shader grabs the corresponding
+ * color from the texture.
+ */
+float4 pixelShaderFunction(PixelShaderInput input): COLOR {
+ return tex2D(texSampler0, input.texcoord);
+}
+
+// Here we tell our effect file *which* functions are
+// our vertex and pixel shaders.
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
diff --git a/o3d/samples/shaders/vertex-color.shader b/o3d/samples/shaders/vertex-color.shader
new file mode 100644
index 0000000..216f554
--- /dev/null
+++ b/o3d/samples/shaders/vertex-color.shader
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// The 4x4 world view projection matrix.
+float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+
+// input parameters for our vertex shader
+struct PixelShaderInput {
+ float4 position : POSITION;
+ float4 color : COLOR;
+};
+
+// input parameters for our pixel shader
+// also the output parameters for our vertex shader
+struct VertexShaderInput {
+ float4 position : POSITION;
+ float4 color: COLOR;
+};
+
+/**
+ * Vertex Shader - our vertex shader
+ */
+PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ /**
+ * Our vertex shader projects the vertices onto the screen.
+ * We return its color unchanged.
+ */
+ PixelShaderInput output;
+
+ output.position = mul(input.position, worldViewProjection);
+ output.color = input.color;
+ return output;
+}
+
+/**
+ * pixel shader does nothing but return whatever color it was given.
+ */
+float4 pixelShaderFunction(PixelShaderInput input): COLOR {
+ return input.color;
+}
+
+// Here we tell our effect file the functions
+// which specify our vertex and pixel shaders.
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
diff --git a/o3d/samples/shaders/yuv2rgb.shader b/o3d/samples/shaders/yuv2rgb.shader
new file mode 100644
index 0000000..359c2bc
--- /dev/null
+++ b/o3d/samples/shaders/yuv2rgb.shader
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+// This shader takes a Y'UV420p image as a single greyscale plane, and
+// converts it to RGB by sampling the correct parts of the image, and
+// by converting the colorspace to RGB on the fly.
+
+// Projection matrix for the camera.
+float4x4 worldViewProjection : WorldViewProjection;
+
+// These represent the image dimensions of the SOURCE IMAGE (not the
+// Y'UV420p image). This is the same as the dimensions of the Y'
+// portion of the Y'UV420p image. They are set from JavaScript.
+float imageWidth;
+float imageHeight;
+
+// This is the texture sampler where the greyscale Y'UV420p image is
+// accessed.
+sampler textureSampler;
+
+// These are the input/output parameters for our vertex shader
+struct VertexShaderInput {
+ float4 position : POSITION;
+ float2 texcoord : TEXCOORD0; // Texture coordinates
+};
+
+// These are the input/output parameters for our pixel shader.
+struct PixelShaderInput {
+ float4 position : POSITION;
+ float2 texcoord : TEXCOORD0; // Texture coordinates
+};
+
+/**
+ * This fetches an individual Y pixel from the image, given the current
+ * texture coordinates (which range from 0 to 1 on the source texture
+ * image). They are mapped to the portion of the image that contains
+ * the Y component.
+ *
+ * @param position This is the position of the main image that we're
+ * trying to render, in parametric coordinates.
+ */
+float getYPixel(float2 position) {
+ position.y = (position.y * 2.0 / 3.0) + (1.0 / 3.0);
+ return tex2D(textureSampler, position).x;
+}
+
+/**
+ * This does the crazy work of calculating the planar position (the
+ * position in the byte stream of the image) of the U or V pixel, and
+ * then converting that back to x and y coordinates, so that we can
+ * account for the fact that V is appended to U in the image, and the
+ * complications that causes (see below for a diagram).
+ *
+ * @param position This is the position of the main image that we're
+ * trying to render, in pixels.
+ *
+ * @param planarOffset This is an offset to add to the planar address
+ * we calculate so that we can find the U image after the V
+ * image.
+ */
+float2 mapCommon(float2 position, float planarOffset) {
+ planarOffset += (imageWidth * floor(position.y / 2.0)) / 2.0 +
+ floor((imageWidth - 1.0 - position.x) / 2.0);
+ float x = int(imageWidth - 1.0 - floor(fmod(planarOffset, imageWidth)));
+ float y = int(floor(planarOffset / imageWidth));
+ return float2((x + 0.5) / imageWidth, (y + 0.5) / (1.5 * imageHeight));
+}
+
+/**
+ * This is a helper function for mapping pixel locations to a texture
+ * coordinate for the U plane.
+ *
+ * @param position This is the position of the main image that we're
+ * trying to render, in pixels.
+ */
+float2 mapU(float2 position) {
+ float planarOffset = (imageWidth * imageHeight) / 4.0;
+ return mapCommon(position, planarOffset);
+}
+
+/**
+ * This is a helper function for mapping pixel locations to a texture
+ * coordinate for the V plane.
+ *
+ * @param position This is the position of the main image that we're
+ * trying to render, in pixels.
+ */
+float2 mapV(float2 position) {
+ return mapCommon(position, 0.0);
+}
+
+/**
+ * The vertex shader does nothing but returns the position of the
+ * vertex using the world view projection matrix.
+ */
+PixelShaderInput vertexShaderMain(VertexShaderInput input) {
+ PixelShaderInput output;
+ output.position = mul(input.position, worldViewProjection);
+
+ output.texcoord = input.texcoord;
+ return output;
+}
+
+/**
+ * Given the texture coordinates, our pixel shader grabs the right
+ * value from each channel of the source image, converts it from Y'UV
+ * to RGB, and returns the result.
+ *
+ * Each U and V pixel provides color information for a 2x2 block of Y
+ * pixels. The U and V planes are just appended to the Y image.
+ *
+ * For images that have a height divisible by 4, things work out nicely.
+ * For images that are merely divisible by 2, it's not so nice
+ * (and YUV420 doesn't work for image sizes not divisible by 2).
+ *
+ * Here is a 6x6 image, with the layout of the planes of U and V.
+ * Notice that the V plane starts halfway through the last scanline
+ * that has U on it.
+ *
+ * 1 +---+---+---+---+---+---+
+ * | Y | Y | Y | Y | Y | Y |
+ * +---+---+---+---+---+---+
+ * | Y | Y | Y | Y | Y | Y |
+ * +---+---+---+---+---+---+
+ * | Y | Y | Y | Y | Y | Y |
+ * +---+---+---+---+---+---+
+ * | Y | Y | Y | Y | Y | Y |
+ * +---+---+---+---+---+---+
+ * | Y | Y | Y | Y | Y | Y |
+ * +---+---+---+---+---+---+
+ * | Y | Y | Y | Y | Y | Y |
+ * .3 +---+---+---+---+---+---+
+ * | U | U | U | U | U | U |
+ * +---+---+---+---+---+---+
+ * | U | U | U | V | V | V |
+ * +---+---+---+---+---+---+
+ * | V | V | V | V | V | V |
+ * 0 +---+---+---+---+---+---+
+ * 0 1
+ *
+ * Here is a 4x4 image, where the U and V planes are nicely split into
+ * separable blocks.
+ *
+ * 1 +---+---+---+---+
+ * | Y | Y | Y | Y |
+ * +---+---+---+---+
+ * | Y | Y | Y | Y |
+ * +---+---+---+---+
+ * | Y | Y | Y | Y |
+ * +---+---+---+---+
+ * | Y | Y | Y | Y |
+ * .3 +---+---+---+---+
+ * | U | U | U | U |
+ * +---+---+---+---+
+ * | V | V | V | V |
+ * 0 +---+---+---+---+
+ * 0 1
+ *
+ */
+float4 pixelShaderMain(PixelShaderInput input): COLOR {
+ // Calculate what image pixel we're on, since we have to calculate
+ // the location in the image stream, using floor in several places
+ // which makes it hard to use parametric coordinates.
+ float2 pixelPosition = float2(floor(imageWidth * input.texcoord.x),
+ floor(imageHeight * input.texcoord.y));
+
+ // We can use the parametric coordinates to get the Y channel, since it's
+ // a relatively normal image.
+ float yChannel = getYPixel(input.texcoord);
+
+ // As noted above, the U and V planes are smashed onto the end of
+ // the image in an odd way (in our 2D texture mapping, at least), so
+ // these mapping functions take care of that oddness.
+ float uChannel = tex2D(textureSampler, mapU(pixelPosition)).x;
+ float vChannel = tex2D(textureSampler, mapV(pixelPosition)).x;
+
+ // This does the colorspace conversion from Y'UV to RGB as a matrix
+ // multiply. It also does the offset of the U and V channels from
+ // [0,1] to [-.5,.5] as part of the transform.
+ float4 channels = float4(yChannel, uChannel, vChannel, 1.0);
+ float3x4 conversion = float3x4(1.0, 0.0, 1.402, -0.701,
+ 1.0, -0.344, -0.714, 0.529,
+ 1.0, 1.772, 0.0, -0.886);
+ float3 rgb = mul(conversion, channels);
+
+ // This is another Y'UV transform that can be used, but it doesn't
+ // accurately transform my source image. Your images may well fare
+ // better with it, however, considering they come from a different
+ // source, and because I'm not sure that my original was converted
+ // to Y'UV420p with the same RGB->YUV (or YCrCb) conversion as
+ // yours.
+ //
+ // float4 channels = float4(yChannel, uChannel, vChannel, 1.0);
+ // float3x4 conversion = float3x4(1.0, 0.0, 1.13983, -0.569915,
+ // 1.0, -0.39465, -0.58060, 0.487625,
+ // 1.0, 2.03211, 0.0, -1.016055);
+ // float3 rgb = mul(conversion, channels);
+
+ return float4(rgb, 1.0);
+}
+
+// Here we tell our effect file *which* functions are
+// our vertex and pixel shaders.
+// #o3d VertexShaderEntryPoint vertexShaderMain
+// #o3d PixelShaderEntryPoint pixelShaderMain
+// #o3d MatrixLoadOrder RowMajor
diff --git a/o3d/samples/simple.html b/o3d/samples/simple.html
new file mode 100644
index 0000000..846a3b1
--- /dev/null
+++ b/o3d/samples/simple.html
@@ -0,0 +1,108 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+Simple. This example uses the simple.js library which is an example of a wrapper
+library that attempts to make O3D super simple to use. See the comments below.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Simple
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.simple');
+
+// Events
+// init() once the page has finished loading.
+window.onload = init;
+
+// global variables
+// we make these global so we can easily access them from the debugger.
+var g_simple;
+var g_cube;
+var g_finished = false; // for selenium testing
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes our app.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ var o3dElement = clientElements[0];
+
+ // Create an o3djs.simple object to manage things in a simple way.
+ g_simple = o3djs.simple.create(o3dElement);
+
+ // Create a cube.
+ g_cube = g_simple.createCube(50);
+
+ // You should now have a cube on the screen!
+ // Examples of other commands you can issue (live from firebug if you want)
+ //
+ // g_cube.transform.translate(0, 0, -50); // translate the cube.
+ // g_cube.setDiffuseColor(1, 0, 0, 1); // make the cube red.
+ // g_cube.loadTexture("http://someplace.org/somefile.jpg"); // now textured.
+ // g_simple.setCameraPosition(200, 100, -50); // move the camera
+ // g_simple.setCameraTarget(0, 10, 0); // move the camera's target
+ // g_simple.setFieldOfView(30 * Math.PI / 180); // change the field of view.
+ // g_sphere = g_simple.createSphere(20, 10); // create a sphere.
+ //
+ // Try typing these commands from firebug live!
+
+ g_finished = true; // for selenium testing.
+}
+
+</script>
+</head>
+<body>
+<h1>Simple</h1>
+Using the simple library.<br/>
+View the source of this sample to see the point.<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 600px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/samples/simpletexture.html b/o3d/samples/simpletexture.html
new file mode 100644
index 0000000..6e1c688
--- /dev/null
+++ b/o3d/samples/simpletexture.html
@@ -0,0 +1,179 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+O3D Tutorial B3
+
+In this tutorial, we show how to use textures in o3d.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Tutorial B3: Textures
+</title>
+<script type="text/javascript" src="o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.effect');
+o3djs.require('o3djs.io');
+
+// Events
+// Run the init() once the page has finished loading.
+window.onload = init;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_pack;
+var g_viewInfo;
+var g_finished = false; // for selenium testing
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D, loads the effect, and draws the quad.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initialize global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = o3dElement.client;
+
+ // Create a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Setup an orthographic projection
+ g_viewInfo.drawContext.projection = g_math.matrix4.orthographic(
+ -g_client.width * 0.5,
+ g_client.width * 0.5,
+ -g_client.height * 0.5,
+ g_client.height * 0.5,
+ 0.1,
+ 1000);
+
+ // move the camera above the origin looking down.
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [0, 500, 0], // eye
+ [0, 0, 0], // target
+ [0, 0, -1]); // up
+
+ // Create and load the effect.
+ var effect = g_pack.createObject('Effect');
+ o3djs.effect.loadEffect(effect, 'shaders/texture-only.shader');
+
+ // Create a Material for the effect.
+ var myMaterial = g_pack.createObject('Material');
+
+ // Set the material's drawList
+ myMaterial.drawList = g_viewInfo.performanceDrawList;
+
+ // Apply our effect to this material.
+ myMaterial.effect = effect;
+
+ // Create the params the effect needs on the material.
+ effect.createUniformParameters(myMaterial);
+
+ // Create a quad.
+ var myShape = o3djs.primitives.createPlane(g_pack,
+ myMaterial,
+ 300, // width
+ 300, // height
+ 1, // quads across
+ 1); // quads down
+
+ // Get the material's sampler parameter
+ var sampler_param = myMaterial.getParam('texSampler0');
+ var sampler = g_pack.createObject('Sampler');
+ sampler_param.value = sampler;
+
+ // Load our texture. This happens asynchronously.
+ var url = o3djs.util.getCurrentURI() + 'assets/texture_b3.jpg';
+ o3djs.io.loadTexture(g_pack, url, function(texture, exception) {
+ if (exception) {
+ alert(exception);
+ } else {
+ sampler.texture = texture;
+ // adjust the scale of our transform to match the aspect ratio of
+ // the texture. Of course we could also have waited until now to build
+ // our plane and set its width and height to match instead of scaling
+ // here.
+ var textureWidth = texture.width;
+ var textureHeight = texture.height;
+ var hScale = 1;
+ var vScale = 1;
+ if (textureWidth > textureHeight) {
+ vScale = textureHeight / textureWidth;
+ } else if (textureHeight > textureWidth) {
+ hScale = textureWidth / textureHeight;
+ }
+ // We now attach our quad to the root of the transform graph.
+ // We do this after the texture has loaded, otherwise we'd be attempting
+ // to display something invalid.
+ var root = g_client.root;
+ root.addShape(myShape);
+ root.scale(hScale, 1, vScale);
+
+ g_finished = true; // for selenium testing.
+ }
+ });
+}
+</script>
+</head>
+<body>
+<h1>Simple texturing</h1>
+This tutorial shows how we use textures in O3D.
+<br/>
+
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 600px; height: 600px"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/samples/simpleviewer/assets/empty.txt b/o3d/samples/simpleviewer/assets/empty.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/o3d/samples/simpleviewer/assets/empty.txt
diff --git a/o3d/samples/simpleviewer/simpleviewer.html b/o3d/samples/simpleviewer/simpleviewer.html
new file mode 100644
index 0000000..88b24aa
--- /dev/null
+++ b/o3d/samples/simpleviewer/simpleviewer.html
@@ -0,0 +1,365 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+ <title>
+ Simple Scene Viewer
+ </title>
+<style type="text/css">
+html, body {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ height: 100%;
+}
+</style>
+</head>
+<body onload="init();" onunload="uninit();">
+<script type="text/javascript" src="../o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.quaternions');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.pack');
+o3djs.require('o3djs.arcball');
+o3djs.require('o3djs.scene');
+
+var g_root;
+var g_o3d;
+var g_math;
+var g_quaternions;
+var g_client;
+var g_aball;
+var g_thisRot;
+var g_lastRot;
+var g_pack = null;
+var g_mainPack;
+var g_viewInfo;
+var g_lightPosParam;
+var g_loadingElement;
+var g_o3dWidth = -1;
+var g_o3dHeight = -1;
+var g_o3dElement;
+var g_finished = false; // for selenium
+
+var g_camera = {
+ farPlane: 5000,
+ nearPlane:0.1
+};
+
+var g_dragging = false;
+
+function startDragging(e) {
+ g_lastRot = g_thisRot;
+
+ g_aball.click([e.x, e.y]);
+
+ g_dragging = true;
+}
+
+function drag(e) {
+ if (g_dragging) {
+ var rotationQuat = g_aball.drag([e.x, e.y]);
+ var rot_mat = g_quaternions.quaternionToRotation(rotationQuat);
+ g_thisRot = g_math.matrix4.mul(g_lastRot, rot_mat);
+
+ var m = g_root.localMatrix;
+ g_math.matrix4.setUpper3x3(m, g_thisRot);
+ g_root.localMatrix = m;
+ }
+}
+
+function stopDragging(e) {
+ g_dragging = false;
+}
+
+function updateCamera() {
+ var up = [0, 1, 0];
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(g_camera.eye,
+ g_camera.target,
+ up);
+ g_lightPosParam.value = g_camera.eye;
+}
+
+function updateProjection() {
+ // Create a perspective projection matrix.
+ g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(45), g_o3dWidth / g_o3dHeight, g_camera.nearPlane,
+ g_camera.farPlane);
+}
+
+function scrollMe(e) {
+ if (e.deltaY) {
+ var t = 1;
+ if (e.deltaY > 0)
+ t = 11 / 12;
+ else
+ t = 13 / 12;
+ g_camera.eye = g_math.lerpVector(g_camera.target, g_camera.eye, t);
+
+ updateCamera();
+ }
+}
+
+function enableInput(enable) {
+ document.getElementById("url").disabled = !enable;
+ document.getElementById("load").disabled = !enable;
+}
+
+function loadFile(context, path) {
+ function callback(pack, parent, exception) {
+ enableInput(true);
+ if (exception) {
+ alert("Could not load: " + path + "\n" + exception);
+ g_loadingElement.innerHTML = "loading failed.";
+ } else {
+ g_loadingElement.innerHTML = "loading finished.";
+ // Generate draw elements and setup material draw lists.
+ o3djs.pack.preparePack(pack, g_viewInfo);
+ var bbox = o3djs.util.getBoundingBoxOfTree(g_client.root);
+ g_camera.target = g_math.lerpVector(bbox.minExtent, bbox.maxExtent, 0.5);
+ var diag = g_math.length(g_math.subVector(bbox.maxExtent,
+ bbox.minExtent));
+ g_camera.eye = g_math.addVector(g_camera.target, [0, 0, 1.5 * diag]);
+ g_camera.nearPlane = diag / 1000;
+ g_camera.farPlane = diag * 10;
+ setClientSize();
+ updateCamera();
+ updateProjection();
+
+ // Manually connect all the materials' lightWorldPos params to the context
+ var materials = pack.getObjectsByClassName('o3d.Material');
+ for (var m = 0; m < materials.length; ++m) {
+ var material = materials[m];
+ var param = material.getParam('lightWorldPos');
+ if (param) {
+ param.bind(g_lightPosParam);
+ }
+ }
+
+ g_finished = true; // for selenium
+
+ // Comment out the next line to dump lots of info.
+ if (false) {
+ o3djs.dump.dump('---dumping context---\n');
+ o3djs.dump.dumpParamObject(context);
+
+ o3djs.dump.dump('---dumping root---\n');
+ o3djs.dump.dumpTransformTree(g_client.root);
+
+ o3djs.dump.dump('---dumping render root---\n');
+ o3djs.dump.dumpRenderNodeTree(g_client.renderGraphRoot);
+
+ o3djs.dump.dump('---dump g_pack shapes---\n');
+ var shapes = pack.getObjectsByClassName('o3d.Shape');
+ for (var t = 0; t < shapes.length; t++) {
+ o3djs.dump.dumpShape(shapes[t]);
+ }
+
+ o3djs.dump.dump('---dump g_pack materials---\n');
+ var materials = pack.getObjectsByClassName('o3d.Material');
+ for (var t = 0; t < materials.length; t++) {
+ o3djs.dump.dump (
+ ' ' + t + ' : ' + materials[t].className +
+ ' : "' + materials[t].name + '"\n');
+ o3djs.dump.dumpParams(materials[t], ' ');
+ }
+
+ o3djs.dump.dump('---dump g_pack textures---\n');
+ var textures = pack.getObjectsByClassName('o3d.Texture');
+ for (var t = 0; t < textures.length; t++) {
+ o3djs.dump.dumpTexture(textures[t]);
+ }
+
+ o3djs.dump.dump('---dump g_pack effects---\n');
+ var effects = pack.getObjectsByClassName('o3d.Effect');
+ for (var t = 0; t < effects.length; t++) {
+ o3djs.dump.dump (' ' + t + ' : ' + effects[t].className +
+ ' : "' + effects[t].name + '"\n');
+ o3djs.dump.dumpParams(effects[t], ' ');
+ }
+ }
+ }
+ }
+
+ g_pack = g_client.createPack();
+
+ // Create a new transform for the loaded file
+ var parent = g_pack.createObject('Transform');
+ parent.parent = g_client.root;
+ if (path != null) {
+ g_loadingElement.innerHTML = "Loading: " + path;
+ enableInput(false);
+ try {
+ o3djs.scene.loadScene(g_client, g_pack, parent, path, callback);
+ } catch (e) {
+ enableInput(true);
+ g_loadingElement.innerHTML = "loading failed : " + e;
+ }
+ }
+
+ return parent;
+}
+
+function setClientSize() {
+ var newWidth = parseInt(g_client.width);
+ var newHeight = parseInt(g_client.height);
+
+ if (newWidth != g_o3dWidth || newHeight != g_o3dHeight) {
+ g_o3dWidth = newWidth;
+ g_o3dHeight = newHeight;
+
+ updateProjection();
+
+ // Sets a new area size for arcball.
+ g_aball.setAreaSize(g_o3dWidth, g_o3dHeight);
+ }
+}
+
+/**
+ * Called every frame.
+ */
+function onRender() {
+ // If we don't check the size of the client area every frame we don't get a
+ // chance to adjust the perspective matrix fast enough to keep up with the
+ // browser resizing us.
+ setClientSize();
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and loads the scene into the transform graph.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ var path = window.location.href;
+ var index = path.lastIndexOf('/');
+ path = path.substring(0, index+1) + 'assets/cube.o3dtgz';
+ var url = document.getElementById("url").value = path;
+ g_loadingElement = document.getElementById('loading');
+
+ g_o3dElement = clientElements[0];
+ g_o3d = g_o3dElement.o3d;
+ g_math = o3djs.math;
+ g_quaternions = o3djs.quaternions;
+ g_client = g_o3dElement.client;
+
+ g_mainPack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_mainPack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ g_lastRot = g_math.matrix4.identity();
+ g_thisRot = g_math.matrix4.identity();
+
+ var root = g_client.root;
+
+ g_aball = o3djs.arcball.create(100, 100);
+ setClientSize();
+
+ // Set the light at the same position as the camera to create a headlight
+ // that illuminates the object straight on.
+ var paramObject = g_mainPack.createObject('ParamObject');
+ g_lightPosParam = paramObject.createParam('lightWorldPos', 'ParamFloat3');
+ g_camera.target = [0, 0, 0];
+ g_camera.eye = [0, 0, 5];
+ updateCamera();
+
+ doload()
+
+ o3djs.event.addEventListener(g_o3dElement, 'mousedown', startDragging);
+ o3djs.event.addEventListener(g_o3dElement, 'mousemove', drag);
+ o3djs.event.addEventListener(g_o3dElement, 'mouseup', stopDragging);
+ o3djs.event.addEventListener(g_o3dElement, 'wheel', scrollMe);
+
+ g_client.setRenderCallback(onRender);
+}
+
+/**
+ * Removes any callbacks so they don't get called after the page has unloaded.
+ */
+function uninit() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+
+function doload() {
+ if (g_root) {
+ g_root.parent = null;
+ g_root = null;
+ }
+ if (g_pack) {
+ g_pack.destroy();
+ g_pack = null;
+ }
+ var url = document.getElementById('url').value;
+ g_root = loadFile(g_viewInfo.drawContext, url);
+}
+</script>
+<table width="100%" style="height:100%;">
+ <tr><td valign="middle" align="center" height="100%">
+<table width="100%" style="height:100%;"><tr><td height="100%">
+<h1>
+Simple Scene Viewer
+</h1>
+<table id="container" width="90%" style="height:60%;" border="2"><tr><td height="100%">
+<div id="o3d" style="width: 100%; height: 100%;"></div>
+</td></tr></table>
+<p>
+</p>
+
+<input type="text" id="url" size="100">
+<input type="button" id="load" onclick="doload();" value="load"><BR>
+
+Drag The Mouse To Rotate<br/>
+Scrollwheel To Zoom<br/>
+Resize The Window To Resize The View
+<div style="color: red;" id="loading"></div>
+</td></tr></table>
+</td></tr></table>
+</body>
+</html>
+
+
diff --git a/o3d/samples/skinning.html b/o3d/samples/skinning.html
new file mode 100644
index 0000000..67d3281
--- /dev/null
+++ b/o3d/samples/skinning.html
@@ -0,0 +1,276 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+O3D Skinning example.
+
+Shows a simple skinned cylinder.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Skinning.
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.effect');
+o3djs.require('o3djs.primitives');
+
+// Events
+// init() once the page has finished loading.
+// unload() when the page is unloaded.
+window.onload = init;
+window.onunload = unload;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_viewInfo;
+var g_pack;
+var g_transforms = [];
+var g_finished = false; // for selenium testing.
+var g_clock = 0;
+var g_timeMult = 1;
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and creates one shape.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = o3dElement.client;
+
+ // Creates a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Set our projection matrix, with a vertical field of view of 45 degrees
+ // a near clipping plane of 0.1 and far clipping plane of 10000.
+ g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(45),
+ g_client.width / g_client.height,
+ 0.1,
+ 10000);
+
+ // Create and load the effect.
+ var effect = g_pack.createObject('Effect');
+ o3djs.effect.loadEffect(effect, 'shaders/phong-with-colormult.shader');
+
+ // Create a Material for the effect.
+ var material = g_pack.createObject('Material');
+
+ // Apply our effect to this material.
+ material.effect = effect;
+
+ // Set the material's drawList
+ material.drawList = g_viewInfo.performanceDrawList;
+
+ // Create the params the effect needs on the material.
+ effect.createUniformParameters(material);
+
+ // Set the material parameters.
+ material.getParam('lightWorldPos').value = [1000, 1000, 0];
+ material.getParam('lightIntensity').value = [1, 1, 1, 1];
+ material.getParam('ambientIntensity').value = [0.1, 0.1, 0.1, 1];
+ material.getParam('ambient').value = [1, 1, 1, 1];
+ material.getParam('diffuse').value = [1, 0.2, 1, 1];
+ material.getParam('specular').value = [0.5, 0.5, 0.5, 1];
+ material.getParam('shininess').value = 50;
+ material.getParam('colorMult').value = [1, 1, 1, 1];
+
+ // Create a cylinder.
+ var vertexInfo = o3djs.primitives.createCylinderVertices(
+ 40, 200, 12, 20,
+ [[1, 0, 0, 0],
+ [0, 1, 0, 0],
+ [0, 0, 1, 0],
+ [0, 100, 0, 1]]);
+ var shape = vertexInfo.createShape(g_pack, material);
+
+ // Create an ParamArray to hold matrices for skinning.
+ var matrices = g_pack.createObject('ParamArray');
+
+ // Create a Skin to hold the influences and bind pose.
+ var skin = g_pack.createObject('Skin');
+
+ // Create a SkinEval to use the Skin.
+ var skinEval = g_pack.createObject('SkinEval');
+
+ // Tell the SkinEval which matrices and Skin to use.
+ skinEval.matrices = matrices;
+ skinEval.skin = skin;
+
+ // Create 11 matrices on our ParamArray.
+ matrices.resize(11, 'o3d.ParamMatrix4');
+
+ // Create 11 transforms for the bones and parent them into a chain.
+ for (var ii = 0; ii <= 10; ++ii) {
+ var transform = g_pack.createObject('Transform');
+ g_transforms[ii] = transform;
+ if (ii > 0) {
+ transform.translate(0, 20, 0);
+ }
+ transform.parent = ii == 0 ? g_client.root : g_transforms[ii - 1];
+ // Bind the world matrix of the transform to the corresponding matrix in the
+ // ParamArray.
+ matrices.getParam(ii).bind(transform.getParam('worldMatrix'));
+ // Store the inverse world matrix of each transform as the bind pose for the
+ // skin.
+ skin.setInverseBindPoseMatrix(ii, g_math.inverse(transform.worldMatrix));
+ }
+
+ // Add the cylinder to the root transform.
+ g_transforms[0].addShape(shape);
+
+ // Skinning happens in world space so bind the transform of the shape
+ // to the SkinEval so it can put our skin in object space.
+ skinEval.getParam('base').bind(g_transforms[0].getParam('worldMatrix'));
+
+ // Make our source data for skinning and setup the influences for each bone.
+ // We only need the position and normals.
+ var positions = [];
+ var normals = [];
+ var positionStream = vertexInfo.findStream(g_o3d.Stream.POSITION);
+ var normalStream = vertexInfo.findStream(g_o3d.Stream.NORMAL);
+ var numVertices = positionStream.numElements();
+ for (var ii = 0; ii < numVertices; ++ii) {
+ // Choose the bones to influence the vertex based on its height.
+ var y = positionStream.getElementVector(ii)[1] / 20;
+ var influence = y % 1;
+ var bone = Math.floor(y);
+ var bone_2 = bone + 1;
+ if (bone_2 > 10) {
+ bone_2 = 10;
+ }
+ skin.setVertexInfluences(ii, [bone, 1 - influence, bone_2, influence]);
+ }
+
+ // Create a SourceBuffer for the Skin and set the vertices on it.
+ var sourceBuffer = g_pack.createObject('SourceBuffer');
+ var positionField = sourceBuffer.createField('FloatField', 3);
+ var normalField = sourceBuffer.createField('FloatField', 3);
+ sourceBuffer.allocateElements(numVertices);
+ positionField.setAt(0, positionStream.elements);
+ normalField.setAt(0, normalStream.elements);
+
+ // Tell the skinEval how to use the SourceBuffer
+ skinEval.setVertexStream(g_o3d.Stream.POSITION,
+ 0,
+ positionField,
+ 0);
+ skinEval.setVertexStream(g_o3d.Stream.NORMAL,
+ 0,
+ normalField,
+ 0);
+
+ // Bind the Primitive's position and normal streams
+ // to the SkinEval so the SkinEval will fill them in each frame.
+ var streamBank = shape.elements[0].streamBank;
+ streamBank.bindStream(skinEval, g_o3d.Stream.POSITION, 0);
+ streamBank.bindStream(skinEval, g_o3d.Stream.POSITION, 0);
+
+ // Setup an onrender callback for animation.
+ g_client.setRenderCallback(onrender);
+
+ g_finished = true; // for selenium testing.
+}
+
+/**
+ * Called every frame.
+ * @param {!o3d.RenderEvent} renderEvent Info for rendering.
+ */
+function onrender(renderEvent) {
+ // Get the number of seconds since the last render.
+ var elapsedTime = renderEvent.elapsedTime;
+ g_clock += elapsedTime * g_timeMult;
+
+ var x = Math.sin(g_clock * 0.3) * 400;
+ var z = Math.cos(g_clock * 0.3) * 400;
+ var y = Math.sin(g_clock * 0.7) * 50 + 100;
+
+ // spin the camera.
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [x, y, z], // eye
+ [0, 100, 0], // target
+ [0, 1, 0]); // up
+
+ // Make our bone chain bend.
+ var rotation = Math.PI / g_transforms.length * Math.sin(g_clock * 1);
+ for (var ii = 1; ii < g_transforms.length; ++ii) {
+ var transform = g_transforms[ii];
+ transform.identity();
+ transform.translate(0, 20, 0);
+ transform.rotateX(rotation);
+ }
+}
+
+/**
+ * Removes any callbacks so they don't get called after the page has unloaded.
+ */
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+</script>
+</head>
+<body>
+<h1>Skinning</h1>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 800px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/samples/sobel.html b/o3d/samples/sobel.html
new file mode 100644
index 0000000..f325c48
--- /dev/null
+++ b/o3d/samples/sobel.html
@@ -0,0 +1,342 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+This sample shows how to create a Sobel edge-detect image processing shader,
+using render targets.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+O3D: Sobel Shader Sample
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.pack');
+o3djs.require('o3djs.camera');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.scene');
+
+// Events
+// init() once the page has finished loading.
+window.onload = init;
+window.onunload = uninit;
+
+// constants.
+var RENDER_TARGET_WIDTH = 512;
+var RENDER_TARGET_HEIGHT = 512;
+
+// global variables
+var g_o3d;
+var g_client;
+var g_math;
+var g_pack;
+var g_teapotRoot;
+var g_renderGraphRoot;
+var g_clock = 0;
+var g_timeMult = 1;
+var g_finished = false; // for selenium testing
+
+/**
+ * Loads a scene into the transform graph, and prepares its elements for
+ * insertion into the render graph.
+ * @param {!o3d.Pack} pack Pack to load scene into.
+ * @param {string} fileName filename of the scene.
+ * @param {!o3d.Transform} parent parent node in the transform graph to
+ * which to load the scene into.
+ * @param {!o3djs.rendergraph.ViewInfo} viewInfo who's view and projection will
+ * be set from the scene after it's loaded.
+ */
+function loadScene(pack, fileName, parent, viewInfo) {
+ // Get our full path to the scene
+ var scenePath = o3djs.util.getCurrentURI() + fileName;
+
+ // Load the scene given the full path, and call the callback function
+ // when its done loading.
+ o3djs.scene.loadScene(g_client, pack, parent, scenePath, callback);
+
+ /**
+ * Our callback is called once the scene has been loaded into memory
+ * from the web or locally.
+ * @param {!o3d.Pack} pack The pack that was passed in above.
+ * @param {!o3d.Transform} parent The parent that was passed in above.
+ * @param {*} exception null if loading succeeded.
+ */
+ function callback(pack, parent, exception) {
+ if (exception) {
+ alert('Could not load: ' + fileName + '\n' + exception);
+ return;
+ }
+ // Get a CameraInfo (an object with a view and projection matrix)
+ // using our javascript library function
+ var cameraInfo = o3djs.camera.getViewAndProjectionFromCameras(
+ parent,
+ RENDER_TARGET_WIDTH,
+ RENDER_TARGET_HEIGHT);
+
+ // Copy the view and projection to the passed in viewInfo structure..
+ viewInfo.drawContext.view = cameraInfo.view;
+ viewInfo.drawContext.projection = cameraInfo.projection;
+
+ // Generate draw elements and setup material draw lists.
+ o3djs.pack.preparePack(pack, viewInfo);
+
+ g_finished = true; // for selenium testing.
+ }
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and loads the scene into the transform graph.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ var o3d = clientElements[0];
+ g_o3d = o3d.o3d;
+ g_math = o3djs.math;
+ g_client = o3d.client;
+
+ // Creates a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create the texture required for the color render-target.
+ var texture = g_pack.createTexture2D(RENDER_TARGET_WIDTH,
+ RENDER_TARGET_HEIGHT,
+ g_o3d.Texture.XRGB8, 1, true);
+
+ g_teapotRoot = g_pack.createObject('Transform');
+
+ var renderGraphRoot = g_client.renderGraphRoot;
+
+ var renderSurfaceSet = createRenderSurfaceSet(texture);
+
+ // Create the render graph for the teapot view, drawing the teapot into
+ // texture (via renderSurfaceSet).
+ var teapotViewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_teapotRoot,
+ renderSurfaceSet,
+ [1, 1, 1, 1]);
+
+ var renderNode = createSobelPass(texture, g_client.renderGraphRoot);
+
+ // Load the scene into the transform graph as a child g_teapotRoot
+ loadScene(g_pack, 'assets/teapot.o3dtgz', g_teapotRoot, teapotViewInfo);
+
+ // Set a render callback.
+ g_client.setRenderCallback(onRender);
+}
+
+function createSobelMaterial(viewInfo, kernelSize) {
+ var convFXString = document.getElementById('convFX').value;
+ var convEffect = g_pack.createObject('Effect');
+ convEffect.loadFromFXString(convFXString);
+
+ var convMaterial = g_pack.createObject('Material');
+ convMaterial.drawList = viewInfo.performanceDrawList;
+ convMaterial.effect = convEffect;
+ convEffect.createUniformParameters(convMaterial);
+ return convMaterial;
+}
+
+function createRenderSurfaceSet(texture) {
+ var renderSurface = texture.getRenderSurface(0, g_pack);
+
+ // Create the depth-stencil buffer required when rendering this pass.
+ var depthSurface = g_pack.createDepthStencilSurface(RENDER_TARGET_WIDTH,
+ RENDER_TARGET_HEIGHT);
+
+ var renderSurfaceSet = g_pack.createObject('RenderSurfaceSet');
+ renderSurfaceSet.renderSurface = renderSurface;
+ renderSurfaceSet.renderDepthStencilSurface = depthSurface;
+ renderSurfaceSet.parent = g_client.renderGraphRoot;
+ return renderSurfaceSet;
+}
+
+function createSobelPass(srcTexture, renderGraphRoot) {
+ // Create a root Transform for the image processing scene.
+ var root = g_pack.createObject('Transform');
+
+ // Create a basic view for the image processing scene.
+ var viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ root,
+ renderGraphRoot,
+ [1, 1, 1, 1]);
+
+ var material = createSobelMaterial(viewInfo);
+ var quadShape = o3djs.primitives.createPlane(g_pack,
+ material,
+ 2.0,
+ 2.0,
+ 1,
+ 1);
+
+ // Attach the quad to the image processing scene.
+ root.addShape(quadShape);
+
+ // Rotate the view so we're looking at the XZ plane (where our quad is)
+ // Point the camera along the -Y axis
+ var target = [0, -1, 0];
+ // Put the camera at the origin.
+ var eye = [0, 0, 0];
+ // Define the up-vector as +Z
+ var up = [0, 0, 1];
+ viewInfo.drawContext.view = g_math.matrix4.lookAt(eye, target, up);
+
+ // Create an orthographic projection.
+ viewInfo.drawContext.projection = g_math.matrix4.orthographic(
+ -1, 1, -1, 1, -1, 1);
+
+ // Generate draw elements and setup material draw lists for the
+ // image processing scene.
+ o3djs.pack.preparePack(g_pack, viewInfo);
+
+ setSobelParameters(material, srcTexture);
+ return renderGraphRoot;
+}
+
+function setSobelParameters(material, texture) {
+ var imageParam = material.getParam('image');
+ var imageIncrement = material.getParam('imageIncrement');
+ var sampler = g_pack.createObject('Sampler');
+ sampler.texture = texture;
+ sampler.addressModeU = g_o3d.Sampler.CLAMP;
+ sampler.addressModeV = g_o3d.Sampler.CLAMP;
+ sampler.minFilter = g_o3d.Sampler.POINT;
+ sampler.magFilter = g_o3d.Sampler.POINT;
+ sampler.mipFilter = g_o3d.Sampler.NONE;
+ imageParam.value = sampler;
+ imageIncrement.value = [1.0 / texture.width, 1.0 / texture.height];
+ var paramArray = g_pack.createObject('ParamArray');
+}
+
+/**
+ * Called every frame.
+ * @param {o3d.RenderEvent} renderEvent Rendering Information.
+ */
+function onRender(renderEvent) {
+ var elapsedTime = renderEvent.elapsedTime;
+ g_clock += elapsedTime * g_timeMult;
+
+ g_teapotRoot.identity();
+ g_teapotRoot.rotateX(g_clock);
+ g_teapotRoot.rotateY(g_clock * 1.3);
+}
+
+/**
+ * Cleanup before exiting.
+ */
+function uninit() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+</script>
+</head>
+<body>
+<h1>Sobel Edge Detection Shader Example</h1>
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 512px; height: 512px;"></div>
+<!-- End of O3D plugin -->
+<!--
+ We embed the code for our effect inside this hidden textarea.
+ Effects contain the functions that define
+ the vertex and pixel shaders used by our shape.
+-->
+<!-- Don't render the textarea -->
+<div style="display:none">
+<textarea id="convFX" name="convFX" cols="80" rows="20">
+float4x4 worldViewProj : WorldViewProjection;
+sampler2D image;
+float2 imageIncrement;
+
+struct VertexShaderInput {
+ float4 position : POSITION;
+ float2 imageCoord : TEXCOORD0;
+};
+
+struct PixelShaderInput {
+ float4 position : POSITION;
+ float2 imageCoord : TEXCOORD0;
+};
+
+PixelShaderInput SobelVS(VertexShaderInput input) {
+ PixelShaderInput output;
+ output.position = mul(input.position, worldViewProj);
+ output.imageCoord = input.imageCoord;
+ return output;
+}
+
+float lum(float4 c) {
+ return dot(c.rgb, float3(0.3, 0.59, 0.11));
+}
+
+float4 SobelPS(PixelShaderInput input) : COLOR {
+ float2 imageCoord = input.imageCoord;
+ float t00 = lum(tex2D(image, imageCoord + imageIncrement * float2(-1, -1)));
+ float t10 = lum(tex2D(image, imageCoord + imageIncrement * float2( 0, -1)));
+ float t20 = lum(tex2D(image, imageCoord + imageIncrement * float2( 1, -1)));
+ float t01 = lum(tex2D(image, imageCoord + imageIncrement * float2(-1, 0)));
+ float t21 = lum(tex2D(image, imageCoord + imageIncrement * float2( 1, 0)));
+ float t02 = lum(tex2D(image, imageCoord + imageIncrement * float2(-1, 1)));
+ float t12 = lum(tex2D(image, imageCoord + imageIncrement * float2( 0, 1)));
+ float t22 = lum(tex2D(image, imageCoord + imageIncrement * float2( 1, 1)));
+ float2 grad;
+ grad.x = t00 + 2.0 * t01 + t02 - t20 - 2.0 * t21 - t22;
+ grad.y = t00 + 2.0 * t10 + t20 - t02 - 2.0 * t12 - t22;
+ float len = length(grad);
+ return float4(len, len, len, 1.0);
+}
+
+// #o3d VertexShaderEntryPoint SobelVS
+// #o3d PixelShaderEntryPoint SobelPS
+// #o3d MatrixLoadOrder RowMajor
+</textarea>
+</div>
+</body>
+
+</html>
diff --git a/o3d/samples/stencil_example.html b/o3d/samples/stencil_example.html
new file mode 100644
index 0000000..824f76a
--- /dev/null
+++ b/o3d/samples/stencil_example.html
@@ -0,0 +1,439 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+O3D Stencil Example
+
+In this example I load 9 scene files. One file contains 3 rings and 3 circles.
+I find the circles and draw them into the stencil buffer with different Ids.
+The rings and the circles are drawn with an orthographic camera. The circles
+are manually added to a separate DrawList used by a separate DrawPass so that
+they get drawn last. Then I load 4 scenes, the teapot, torus, pyramid and pipe
+and for each one a skydome. I set a state on each of those so they will get
+masked by the stencils and for each pair I give them a different
+drawContext(viewand projection matrix)
+
+The result is 4 separate 3d scenes on the same screen with irregular shaped
+"windows" into them.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD
+HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Stencil Example
+</title>
+<!-- Include sample javascript library functions -->
+<script type="text/javascript" src="o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.pack');
+o3djs.require('o3djs.camera');
+o3djs.require('o3djs.material');
+o3djs.require('o3djs.scene');
+
+// Events
+// init() once the page has finished loading.
+// unload() when the page is unloaded.
+window.onload = init;
+window.onunload = unload;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_viewInfo;
+var g_stencilPack;
+var g_stencilRoot;
+var g_treeTraversal;
+var g_scenePack = new Array();
+var g_o3dWidth; // width of our client area
+var g_o3dHeight; // height of our client area
+var g_sceneStates = new Array(); // States of stuff inside the stencils
+var G_NUM_SCENES = 4;
+var g_clock = 0;
+var g_timeMult = 1
+var g_finished = false; // for selenium testing
+
+var g_locators = new Array(); // the locator for each shape
+
+/**
+ * Loads a scene into the transform graph.
+ * @param {!o3d.Pack} pack Pack to load scene into.
+ * @param {string} fileName filename of the scene.
+ * @param {!o3d.Transform} parent parent node in the transform graph to
+ * which to load the scene into.
+ * @param {function(!o3d.Pack, !o3d.Transform, boolean): void} callback Function
+ * to call when scene loads.
+ */
+function loadScene(pack, fileName, parent, callback) {
+ // Get our full path to the scene
+ var scenePath = o3djs.util.getCurrentURI() + fileName;
+
+ // Load the scene given the full path, and call the callback function
+ // when its done loading.
+ o3djs.scene.loadScene(g_client, pack, parent, scenePath, callback);
+}
+
+function onrender(renderEvent) {
+ var elapsedTime = renderEvent.elapsedTime;
+
+ g_clock += elapsedTime * g_timeMult;
+
+ for (var ii = 0; ii < g_locators.length; ii++) {
+ var locator = g_locators[ii];
+ var rotation =
+ g_math.matrix4.rotationX(Math.sin(g_clock * (ii + 1)) * 0.5);
+ var translation = g_math.matrix4.getTranslation(locator.localMatrix);
+
+ locator.localMatrix =
+ g_math.matrix4.setTranslation(rotation, translation);
+ }
+
+ var rotation = g_math.matrix4.rotationZ(g_clock);
+ var translation = g_stencilRoot.localMatrix[3];
+
+ g_stencilRoot.localMatrix =
+ g_math.matrix4.setTranslation(rotation, translation);
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and loads the scenes.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initialize global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = o3dElement.client;
+
+ // Creates packs to manage our resources/assets
+ g_stencilPack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_stencilPack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Get the tree traversal so we can register more DrawLists.
+ g_treeTraversal = g_viewInfo.treeTraversal;
+
+ // Get the width and height of our client area. We will need this to create
+ // a projection matrix.
+ g_o3dWidth = g_client.width;
+ g_o3dHeight = g_client.height;
+
+ // Set clear color to blue
+ g_viewInfo.clearBuffer.clearColor = [0.1, 0.1, 1.0, 1.0];
+
+ // Creates transforms to put our data on.
+ g_stencilRoot = g_stencilPack.createObject('Transform');
+ // Connects our roots to the client's root.
+ g_stencilRoot.parent = g_client.root;
+
+ var mySceneRoot = new Array();
+ var myLocalDrawPass = new Array();
+ for (var mm = 0; mm < G_NUM_SCENES; mm++) {
+ g_scenePack[mm] = g_client.createPack();
+ mySceneRoot[mm] = g_scenePack[mm].createObject('Transform');
+ mySceneRoot[mm].parent = g_client.root;
+ var localDrawList = g_scenePack[mm].createObject('DrawList');
+ var localDrawPass = g_stencilPack.createObject('DrawPass');
+ var localStateSet = g_stencilPack.createObject('StateSet');
+ var localState = g_stencilPack.createObject('State');
+ localStateSet.priority = 4;
+ localStateSet.state = localState;
+ // make a state that passes only for a particular stencil value
+ localState.getStateParam('StencilEnable').value = true;
+ localState.getStateParam('StencilReference').value = mm;
+ localState.getStateParam('StencilComparisonFunction').value =
+ g_o3d.State.CMP_EQUAL;
+ localStateSet.parent = g_viewInfo.viewport;
+ localDrawPass.drawList = localDrawList;
+ localDrawPass.parent = localStateSet;
+ myLocalDrawPass[mm] = localDrawPass;
+ }
+
+ // Load the scene into the transform graph as a child my_data_root
+ loadScene(g_stencilPack, 'assets/stencil_frame.o3dtgz', g_stencilRoot,
+ setupStencilsAndOverlay);
+
+ /**
+ * Our callback is called once the scene has been loaded into memory
+ * from the web or locally.
+ */
+ function setupStencilsAndOverlay() {
+ // Get a cameraInfo (a view and projection matrix)
+ // using our javascript library function
+ var cameraInfo = o3djs.camera.getViewAndProjectionFromCameras(
+ g_stencilRoot,
+ g_o3dWidth,
+ g_o3dHeight);
+
+ var backDrawContext = g_stencilPack.createObject('DrawContext');
+ backDrawContext.view = cameraInfo.view;
+ backDrawContext.projection = cameraInfo.projection;
+
+ // create 2 DrawPasses that uses this context to draw things as in the
+ // stencil_frame.o3dtgz file which I already setup with an orthographic
+ // projection matrix. Each one needs a draw list as well. Also create a
+ // StateSet to set the stencil render states for everything drawn
+ // by the back DrawPass.
+
+ // first one to draw the 3 circles into the stencil.
+ var backDrawList = g_stencilPack.createObject('DrawList');
+ var backDrawPass = g_stencilPack.createObject('DrawPass');
+ var backStateSet = g_stencilPack.createObject('StateSet');
+ var backState = g_stencilPack.createObject('State');
+ backDrawPass.parent = backStateSet;
+ backDrawPass.drawList = backDrawList;
+ // after tree_traversal, before opaque
+ backStateSet.priority = g_viewInfo.treeTraversal.priority + 0.5;
+ backStateSet.parent = g_viewInfo.viewport;
+ backStateSet.state = backState;
+ g_treeTraversal.registerDrawList(backDrawList, backDrawContext, true);
+ backState.getStateParam('StencilEnable').value = true;
+ backState.getStateParam('StencilPassOperation').value =
+ g_o3d.State.STENCIL_REPLACE;
+ backState.getStateParam('StencilComparisonFunction').value =
+ g_o3d.State.CMP_ALWAYS;
+ backState.getStateParam('ZEnable').value = false;
+ backState.getStateParam('ZWriteEnable').value = false;
+ backState.getStateParam('ColorWriteEnable').value = 0;
+
+ // Next one to draw the 3 rings in front of everything
+ var frontDrawList = g_stencilPack.createObject('DrawList');
+ var frontDrawPass = g_stencilPack.createObject('DrawPass');
+ frontDrawPass.drawList = frontDrawList;
+ frontDrawPass.priority = 9001; // after our special clear.
+ frontDrawPass.parent = g_viewInfo.viewport;
+ g_treeTraversal.registerDrawList(frontDrawList, backDrawContext, true);
+
+ // find all the pCylinder1 Transforms (the stencil discs)
+ var stencils = g_stencilRoot.getTransformsByNameInTree('pCylinder1');
+
+ // put each stencils at the beginning of the render graph
+ for (var ss = 0; ss < stencils.length; ss++) {
+ // there should be only one shape under each pCylinder1
+ var shape = stencils[ss].shapes[0];
+
+ // make a new material.
+ var material = g_stencilPack.createObject('Material');
+
+ // copy the old material's params.
+ material.copyParams(shape.elements[0].material);
+
+ // tell things drawn with this material to get drawn in the back draw
+ // list.
+ material.drawList = backDrawList;
+
+ // create DrawElements using this material.
+ shape.createDrawElements(g_stencilPack, material);
+
+ o3djs.material.attachStandardEffect(g_stencilPack,
+ material,
+ g_viewInfo,
+ 'phong');
+
+ // make state that renders into the stencil only
+ var state = g_stencilPack.createObject('State');
+ material.state = state;
+ state.getStateParam('StencilReference').value = ss + 1;
+ }
+
+ // clear the zbuffer before drawing the circles
+ var clearBuffer = g_stencilPack.createObject('ClearBuffer');
+ clearBuffer.clearColorFlag = false;
+ clearBuffer.clearStencilFlag = false;
+ clearBuffer.priority = 9000;
+ clearBuffer.parent = g_viewInfo.viewport;
+
+ // put each circle at the end of the render graph
+
+ // find all the pPipe? Transforms
+ var circles = o3djs.util.getTransformsInTreeByPrefix(g_stencilRoot,
+ 'pPipe');
+ for (var cc = 0; cc < circles.length; cc++) {
+ var transform = circles[cc];
+ // there should be only one shape under each pPipe
+ var shape = transform.shapes[0];
+ // there should be only one element in each shape.
+ var primitive = shape.elements[0];
+ // set it's material to draw in the front draw list.
+ primitive.material.drawList = frontDrawList;
+ o3djs.material.attachStandardEffect(g_stencilPack,
+ primitive.material,
+ g_viewInfo,
+ 'phong');
+
+ // generate draw elements for it.
+ shape.createDrawElements(g_stencilPack,
+ null);
+ }
+
+ loadScene(g_scenePack[0], 'assets/teapot.o3dtgz', mySceneRoot[0],
+ setupScene0);
+ }
+
+ function setupScene0() {
+ loadScene(g_scenePack[0], 'assets/dome1.o3dtgz', mySceneRoot[0],
+ setupDome0);
+ }
+
+ function setupDome0() {
+ // Stencil 0 is the center.
+ setupScene(g_scenePack[0], mySceneRoot[0], 0);
+ loadScene(g_scenePack[1], 'assets/part1.o3dtgz', mySceneRoot[1],
+ setupScene1);
+ }
+
+ function setupScene1() {
+ loadScene(g_scenePack[1], 'assets/dome2.o3dtgz', mySceneRoot[1],
+ setupDome1);
+ }
+
+ function setupDome1() {
+ // stencil 3 is the top left.
+ setupScene(g_scenePack[1], mySceneRoot[1], 3);
+ loadScene(g_scenePack[2], 'assets/part2.o3dtgz', mySceneRoot[2],
+ setupScene2);
+ }
+
+ function setupScene2() {
+ loadScene(g_scenePack[2], 'assets/dome3.o3dtgz', mySceneRoot[2],
+ setupDome2);
+ }
+
+ function setupDome2() {
+ // stencil 2 is the top right.
+ setupScene(g_scenePack[2], mySceneRoot[2], 2);
+ loadScene(g_scenePack[3], 'assets/part3.o3dtgz', mySceneRoot[3],
+ setupScene3);
+ }
+
+ function setupScene3() {
+ loadScene(g_scenePack[3], 'assets/dome4.o3dtgz', mySceneRoot[3],
+ setupDome3);
+ }
+
+ function setupDome3() {
+ // stencil 1 is the bottom.
+ setupScene(g_scenePack[3], mySceneRoot[3], 1);
+
+ g_client.setRenderCallback(onrender);
+ g_finished = true; // for selenium testing.
+ }
+
+ function setupScene(pack, root, stencil_id) {
+ // Get a cameraInfo (a Transform with a view and projection matrix)
+ // using our javascript library function
+ var cameraInfo = o3djs.camera.getViewAndProjectionFromCameras(
+ root,
+ g_o3dWidth,
+ g_o3dHeight);
+
+ // Create a new draw context and set it to use the camera from the scene
+ // we just loaded.
+ var sceneContext = pack.createObject('DrawContext');
+ sceneContext.view = cameraInfo.view;
+ sceneContext.projection = cameraInfo.projection;
+
+ g_treeTraversal.registerDrawList(myLocalDrawPass[stencil_id].drawList,
+ sceneContext,
+ true);
+
+ // save off the locators. Our shapes are parented to the so we can rotate
+ // them for animation
+ var locators = o3djs.util.getTransformsInTreeByPrefix(root,
+ 'locator');
+ g_locators[g_locators.length] = root;
+
+ // get all the shapes in the pack we just loaded.
+ var shapes = pack.getObjectsByClassName('o3d.Shape');
+ for (var ss = 0; ss < shapes.length; ss++) {
+ var shape = shapes[ss];
+ var elements = shape.elements;
+ for (var ee = 0; ee < elements.length; ++ee) {
+ var element = elements[ee];
+ element.material.drawList = myLocalDrawPass[stencil_id].drawList;
+ o3djs.material.attachStandardEffect(g_stencilPack,
+ element.material,
+ g_viewInfo,
+ 'phong');
+ }
+ }
+
+ root.createDrawElements(pack, null);
+ }
+}
+
+/**
+ * Removes any callbacks so they don't get called after the page has unloaded.
+ */
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+</script>
+</head>
+<body>
+<h1>Stencil Example</h1>
+Shows 4 &quot;Views&quot; using 4 stencils, and 9 scene files.
+4 shapes, 4 skydomes, 1 frame drawn with an orthographic projection.
+<br/>
+<!-- Start of O3D plugin -->
+<!--
+NOTE: Because we are using cameras directly from the loaded scene and
+beacuse we are using an orthographic camera the dimensions of our client
+area must have the same aspect ratio as the original scene in Maya otherwise
+we will not get the same results.
+
+The original scene size was 1280x720
+-->
+<div id="o3d" style="width: 800px; height: 450px"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/samples/texturesamplers.html b/o3d/samples/texturesamplers.html
new file mode 100644
index 0000000..b7f0da4
--- /dev/null
+++ b/o3d/samples/texturesamplers.html
@@ -0,0 +1,249 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+O3D Texture Samplers example.
+
+Demonstrates how to create samplers and set their various states.
+-->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Texture Samplers
+</title>
+<script type="text/javascript" src="o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.effect');
+o3djs.require('o3djs.event');
+o3djs.require('o3djs.io');
+
+// Events
+// Run the init() once the page has finished loading.
+window.onload = init;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_pack;
+var g_viewInfo;
+var g_commonEffect;
+var g_eye;
+var g_target;
+var g_up;
+var g_finished = false; // for selenium testing
+
+function scrollMe(e) {
+ g_eye = g_math.mulScalarVector((e.deltaY < 0 ? 11 : 13) / 12, g_eye);
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(g_eye, g_target, g_up);
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D, loads the effect, and draws the quads.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initialize global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = o3dElement.client;
+
+ // Create a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Create and load the effect.
+ g_commonEffect = g_pack.createObject('Effect');
+ o3djs.effect.loadEffect(g_commonEffect, 'shaders/texture-only.shader');
+
+ // Create our projection matrix, with a vertical field of view of 45 degrees
+ // a near clipping plane of 0.1 and far clipping plane of 100.
+ var proj_matrix = g_math.matrix4.perspective(
+ g_math.degToRad(45),
+ g_client.width / g_client.height,
+ 0.1,
+ 100);
+
+ // Create the view matrix which tells the camera which way to point to.
+ g_eye = [0, 4, 0];
+ g_target = [0, 0, 0];
+ g_up = [0, 0, -1];
+ var view_matrix = g_math.matrix4.lookAt(g_eye, g_target, g_up);
+
+ g_viewInfo.drawContext.view = view_matrix;
+ g_viewInfo.drawContext.projection = proj_matrix;
+
+ var samplers = [];
+ var transforms = [];
+ for (var xx = 0; xx < 3; xx++) {
+ for (var yy = 0; yy < 2; yy++) {
+ var index = yy * 3 + xx;
+
+ // Create a new Material for the quad.
+ var material = g_pack.createObject('Material');
+
+ // Set the material's drawList
+ material.drawList = g_viewInfo.performanceDrawList;
+ material.effect = g_commonEffect;
+
+ // Create Params on the material for all the uniform parameters needed by
+ // the effect. The one we are really interested in is the Param for the
+ // sampler which will be called "texSampler0"
+ // (the name it has in the shader string).
+ g_commonEffect.createUniformParameters(material);
+
+ // Create a quad and position it.
+ var verts = o3djs.primitives.createPlaneVertices(1, 1, 1, 1);
+ var texCoordStream = verts.findStream(g_o3d.Stream.TEXCOORD, 0);
+ // make the UVs go from 0 to 2 instead of 0 to 1
+ for (var v = 0; v < texCoordStream.elements.length; ++v) {
+ texCoordStream.elements[v] *= 2;
+ }
+ var shape = verts.createShape(g_pack, material);
+
+ // Make a transform for each quad.
+ var transform = g_pack.createObject('Transform');
+ transform.translate([(xx - 1) * 1.2, 0, (0.5 - yy) * -1.2]);
+ transform.addShape(shape);
+
+ // Find the material used by the shape.
+ var primitive = shape.elements[0];
+ var material = primitive.material;
+ var p = material.params;
+
+ // Get the sampler param on the material.
+ var samplerParam = material.getParam('texSampler0');
+
+ // Create a new sampler for the material.
+ var sampler = g_pack.createObject('Sampler');
+ samplerParam.value = sampler;
+ samplers[index] = sampler;
+ transforms[index] = transform;
+ }
+ }
+
+ // Set the sampler states on each sampler.
+ // Looking at the generated image, the samplers correspond to the
+ // following quads:
+ // [0] [1] [2]
+ // [3] [4] [5]
+ // Sampler states not specified assume their default values which are:
+ // addressModeU = Sampler.WRAP
+ // addressModeV = Sampler.WRAP
+ // minFilter = Sampler.LINEAR
+ // magFilter = Sampler.LINEAR
+ // mipFilter = Sampler.POINT
+ // borderColor = Float4(0, 0, 0, 0)
+ // maxAnisotropy = 1
+
+ // Rotates quad and uses anisotropic filtering.
+ transforms[1].rotateZ(-Math.PI / 2.5);
+ samplers[1].addressModeU = g_o3d.Sampler.WRAP;
+ samplers[1].addressModeV = g_o3d.Sampler.WRAP;
+ samplers[1].minFilter = g_o3d.Sampler.ANISOTROPIC;
+ samplers[1].maxAnisotropy = 4;
+
+ // Uses BORDER addressing mode with a red border.
+ samplers[2].addressModeU = g_o3d.Sampler.BORDER;
+ samplers[2].addressModeV = g_o3d.Sampler.BORDER;
+ samplers[2].borderColor = [1, 0, 0, 1];
+
+ // Uses POINT sampling for minification.
+ samplers[3].addressModeU = g_o3d.Sampler.WRAP;
+ samplers[3].addressModeV = g_o3d.Sampler.WRAP;
+ samplers[3].minFilter = g_o3d.Sampler.POINT;
+
+ // Rotates quad and uses default (linear) filtering.
+ // Compare results to quad #1.
+ transforms[4].rotateZ(-Math.PI / 2.5);
+ samplers[4].addressModeU = g_o3d.Sampler.WRAP;
+ samplers[4].addressModeV = g_o3d.Sampler.WRAP;
+ samplers[4].minFilter = g_o3d.Sampler.LINEAR;
+ samplers[4].magFilter = g_o3d.Sampler.LINEAR;
+
+ // Uses MIRROR addressing mode to mirror the texture on both axes.
+ samplers[5].addressModeU = g_o3d.Sampler.MIRROR;
+ samplers[5].addressModeV = g_o3d.Sampler.MIRROR;
+
+ // Load our texture!
+ var url = o3djs.util.getCurrentURI() + 'assets/texture_b3.jpg';
+ o3djs.io.loadTexture(g_pack, url, function(texture, exception) {
+ if (exception) {
+ alert(exception);
+ } else {
+ // Use the same texture for all the samplers.
+ for (var ii = 0; ii < samplers.length; ii++) {
+ samplers[ii].texture = texture;
+ }
+ // Parent the transforms. We do this here because until the texture
+ // is loaded our quads are not ready to display.
+ for (var ii = 0; ii < transforms.length; ii++) {
+ transforms[ii].parent = g_client.root;
+ }
+ g_finished = true; // for selenium testing.
+ }
+ });
+
+ o3djs.event.addEventListener(o3dElement, 'wheel', scrollMe);
+}
+</script>
+</head>
+<body>
+<h1>Texture Sampler Example</h1>
+This tutorial demonstrates various texture sampler settings.
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 700px; height: 600px"></div>
+<p>Scrollwheel To Zoom</p><br/>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/samples/third_party/codemirror/LICENSE b/o3d/samples/third_party/codemirror/LICENSE
new file mode 100644
index 0000000..12134c0
--- /dev/null
+++ b/o3d/samples/third_party/codemirror/LICENSE
@@ -0,0 +1,23 @@
+ Copyright (c) 2007 Marijn Haverbeke
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any
+ damages arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any
+ purpose, including commercial applications, and to alter it and
+ redistribute it freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must
+ not claim that you wrote the original software. If you use this
+ software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must
+ not be misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+
+ Marijn Haverbeke
+ marijnh at gmail
diff --git a/o3d/samples/third_party/codemirror/README.o3d b/o3d/samples/third_party/codemirror/README.o3d
new file mode 100644
index 0000000..dcee190
--- /dev/null
+++ b/o3d/samples/third_party/codemirror/README.o3d
@@ -0,0 +1,5 @@
+This is the CodeMirror tool, Copyright (c) 2007 Marijn Haverbeke, version 0.58.
+Please see the LICENSE file. None of the code was modified, but some files
+haven't been included for simplicity.
+The originally version is available at:
+http://marijn.haverbeke.nl/codemirror/codemirror-0.58.zip
diff --git a/o3d/samples/third_party/codemirror/css/csscolors.css b/o3d/samples/third_party/codemirror/css/csscolors.css
new file mode 100644
index 0000000..100c93f
--- /dev/null
+++ b/o3d/samples/third_party/codemirror/css/csscolors.css
@@ -0,0 +1,47 @@
+.editbox {
+ margin: .4em;
+ padding: 0;
+ font-family: monospace;
+ font-size: 10pt;
+ color: black;
+}
+
+pre.code, .editbox {
+ color: #666666;
+}
+
+.editbox p {
+ margin: 0;
+}
+
+span.css-at {
+ color: #770088;
+}
+
+span.css-unit {
+ color: #228811;
+}
+
+span.css-value {
+ color: #770088;
+}
+
+span.css-identifier {
+ color: black;
+}
+
+span.css-important {
+ color: #0000FF;
+}
+
+span.css-colorcode {
+ color: #004499;
+}
+
+span.css-comment {
+ color: #AA7700;
+}
+
+span.css-string {
+ color: #AA2222;
+}
diff --git a/o3d/samples/third_party/codemirror/css/docs.css b/o3d/samples/third_party/codemirror/css/docs.css
new file mode 100644
index 0000000..4906d0b
--- /dev/null
+++ b/o3d/samples/third_party/codemirror/css/docs.css
@@ -0,0 +1,38 @@
+body {
+ margin: 0;
+ font-family: tahoma, arial, sans-serif;
+ padding: 3em 6em;
+ color: black;
+}
+
+h1 {
+ font-size: 22pt;
+}
+
+h2 {
+ font-size: 14pt;
+}
+
+p.rel {
+ padding-left: 2em;
+ text-indent: -2em;
+}
+
+div.border {
+ border: 1px solid black;
+ padding: 3px;
+}
+
+code {
+ font-family: courier, monospace;
+ font-size: 90%;
+ color: #277;
+}
+
+pre.code {
+ margin: 1.1em 12px;
+ border: 1px solid #CCCCCC;
+ color: black;
+ padding: .4em;
+ font-family: courier, monospace;
+}
diff --git a/o3d/samples/third_party/codemirror/css/jscolors.css b/o3d/samples/third_party/codemirror/css/jscolors.css
new file mode 100644
index 0000000..950b440
--- /dev/null
+++ b/o3d/samples/third_party/codemirror/css/jscolors.css
@@ -0,0 +1,47 @@
+.editbox {
+ margin: .4em;
+ padding: 0;
+ font-family: monospace;
+ font-size: 10pt;
+ color: black;
+}
+
+pre.code, .editbox {
+ color: #666666;
+}
+
+.editbox p {
+ margin: 0;
+}
+
+span.js-keyword {
+ color: #770088;
+}
+
+span.js-atom {
+ color: #228811;
+}
+
+span.js-variable {
+ color: black;
+}
+
+span.js-variabledef {
+ color: #0000FF;
+}
+
+span.js-localvariable {
+ color: #004499;
+}
+
+span.js-property {
+ color: black;
+}
+
+span.js-comment {
+ color: #AA7700;
+}
+
+span.js-string {
+ color: #AA2222;
+}
diff --git a/o3d/samples/third_party/codemirror/css/xmlcolors.css b/o3d/samples/third_party/codemirror/css/xmlcolors.css
new file mode 100644
index 0000000..aa26579
--- /dev/null
+++ b/o3d/samples/third_party/codemirror/css/xmlcolors.css
@@ -0,0 +1,51 @@
+.editbox {
+ margin: .4em;
+ padding: 0;
+ font-family: monospace;
+ font-size: 10pt;
+ color: black;
+}
+
+.editbox p {
+ margin: 0;
+}
+
+span.xml-tagname {
+ color: #A0B;
+}
+
+span.xml-attribute {
+ color: #281;
+}
+
+span.xml-punctuation {
+ color: black;
+}
+
+span.xml-attname {
+ color: #00F;
+}
+
+span.xml-comment {
+ color: #A70;
+}
+
+span.xml-cdata {
+ color: #48A;
+}
+
+span.xml-processing {
+ color: #999;
+}
+
+span.xml-entity {
+ color: #A22;
+}
+
+span.xml-error {
+ color: #F00;
+}
+
+span.xml-text {
+ color: black;
+}
diff --git a/o3d/samples/third_party/codemirror/js/codemirror.js b/o3d/samples/third_party/codemirror/js/codemirror.js
new file mode 100644
index 0000000..9b7b5db
--- /dev/null
+++ b/o3d/samples/third_party/codemirror/js/codemirror.js
@@ -0,0 +1,189 @@
+/* CodeMirror main module
+ *
+ * Implements the CodeMirror constructor and prototype, which take care
+ * of initializing the editor frame, and providing the outside interface.
+ */
+
+// The CodeMirrorConfig object is used to specify a default
+// configuration. If you specify such an object before loading this
+// file, the values you put into it will override the defaults given
+// below. You can also assign to it after loading.
+var CodeMirrorConfig = window.CodeMirrorConfig || {};
+
+var CodeMirror = (function(){
+ function setDefaults(object, defaults) {
+ for (var option in defaults) {
+ if (!object.hasOwnProperty(option))
+ object[option] = defaults[option];
+ }
+ }
+ function forEach(array, action) {
+ for (var i = 0; i < array.length; i++)
+ action(array[i]);
+ }
+
+ // These default options can be overridden by passing a set of
+ // options to a specific CodeMirror constructor. See manual.html for
+ // their meaning.
+ setDefaults(CodeMirrorConfig, {
+ stylesheet: "",
+ path: "",
+ parserfile: [],
+ basefiles: ["util.js", "stringstream.js", "select.js", "undo.js", "editor.js", "tokenize.js"],
+ linesPerPass: 15,
+ passDelay: 200,
+ continuousScanning: false,
+ saveFunction: null,
+ onChange: null,
+ undoDepth: 20,
+ undoDelay: 800,
+ disableSpellcheck: true,
+ textWrapping: true,
+ readOnly: false,
+ width: "100%",
+ height: "300px",
+ autoMatchParens: false,
+ parserConfig: null,
+ dumbTabs: false
+ });
+
+ function CodeMirror(place, options) {
+ // Use passed options, if any, to override defaults.
+ this.options = options = options || {};
+ setDefaults(options, CodeMirrorConfig);
+
+ var frame = this.frame = document.createElement("IFRAME");
+ frame.style.border = "0";
+ frame.style.width = options.width;
+ frame.style.height = options.height;
+ // display: block occasionally suppresses some Firefox bugs, so we
+ // always add it, redundant as it sounds.
+ frame.style.display = "block";
+
+ if (place.appendChild)
+ place.appendChild(frame);
+ else
+ place(frame);
+
+ // Link back to this object, so that the editor can fetch options
+ // and add a reference to itself.
+ frame.CodeMirror = this;
+ this.win = frame.contentWindow;
+
+ if (typeof options.parserfile == "string")
+ options.parserfile = [options.parserfile];
+ if (typeof options.stylesheet == "string")
+ options.stylesheet = [options.stylesheet];
+
+ var html = ["<html><head>"];
+ forEach(options.stylesheet, function(file) {
+ html.push("<link rel=\"stylesheet\" type=\"text/css\" href=\"" + file + "\"/>");
+ });
+ forEach(options.basefiles.concat(options.parserfile), function(file) {
+ html.push("<script type=\"text/javascript\" src=\"" + options.path + file + "\"></script>");
+ });
+ html.push("</head><body style=\"border-width: 0;\" class=\"editbox\" spellcheck=\"" +
+ (options.disableSpellcheck ? "false" : "true") + "\"></body></html>");
+
+ var doc = this.win.document;
+ doc.open();
+ doc.write(html.join(""));
+ doc.close();
+ }
+
+ CodeMirror.prototype = {
+ getCode: function() {
+ return this.editor.getCode();
+ },
+ setCode: function(code) {
+ this.editor.importCode(code);
+ },
+ focus: function() {
+ this.win.focus();
+ },
+ jumpToChar: function(start, end) {
+ this.editor.jumpToChar(start, end);
+ this.focus();
+ },
+ jumpToLine: function(line) {
+ this.editor.jumpToLine(line);
+ this.focus();
+ },
+ currentLine: function() {
+ return this.editor.currentLine();
+ },
+ selection: function() {
+ return this.editor.selectedText();
+ },
+ reindent: function() {
+ this.editor.reindent();
+ },
+ replaceSelection: function(text, focus) {
+ var result = this.editor.replaceSelection(text);
+ if (focus) this.focus();
+ return result;
+ },
+ replaceChars: function(text, start, end) {
+ this.editor.replaceChars(text, start, end);
+ },
+ getSearchCursor: function(string, fromCursor) {
+ return this.editor.getSearchCursor(string, fromCursor);
+ }
+ };
+
+ CodeMirror.replace = function(element) {
+ if (typeof element == "string")
+ element = document.getElementById(element);
+ return function(newElement) {
+ element.parentNode.replaceChild(newElement, element);
+ };
+ };
+
+ CodeMirror.fromTextArea = function(area, options) {
+ if (typeof area == "string")
+ area = document.getElementById(area);
+
+ options = options || {};
+ if (area.style.width) options.width = area.style.width;
+ if (area.style.height) options.height = area.style.height;
+ if (options.content == null) options.content = area.value;
+
+ if (area.form) {
+ function updateField() {
+ area.value = mirror.getCode();
+ }
+ if (typeof area.form.addEventListener == "function")
+ area.form.addEventListener("submit", updateField, false);
+ else
+ area.form.attachEvent("onsubmit", updateField);
+ }
+
+ function insert(frame) {
+ if (area.nextSibling)
+ area.parentNode.insertBefore(frame, area.nextSibling);
+ else
+ area.parentNode.appendChild(frame);
+ }
+
+ area.style.display = "none";
+ var mirror = new CodeMirror(insert, options);
+ return mirror;
+ };
+
+ CodeMirror.isProbablySupported = function() {
+ // This is rather awful, but can be useful.
+ var match;
+ if (window.opera)
+ return Number(window.opera.version()) >= 9.52;
+ else if (/Apple Computers, Inc/.test(navigator.vendor) && (match = navigator.userAgent.match(/Version\/(\d+(?:\.\d+)?)\./)))
+ return Number(match[1]) >= 3;
+ else if (document.selection && window.ActiveXObject && (match = navigator.userAgent.match(/MSIE (\d+(?:\.\d*)?)\b/)))
+ return Number(match[1]) >= 6;
+ else if (match = navigator.userAgent.match(/gecko\/(\d{8})/i))
+ return Number(match[1]) >= 20050901;
+ else
+ return null;
+ };
+
+ return CodeMirror;
+})();
diff --git a/o3d/samples/third_party/codemirror/js/editor.js b/o3d/samples/third_party/codemirror/js/editor.js
new file mode 100644
index 0000000..d963025
--- /dev/null
+++ b/o3d/samples/third_party/codemirror/js/editor.js
@@ -0,0 +1,1052 @@
+/* The Editor object manages the content of the editable frame. It
+ * catches events, colours nodes, and indents lines. This file also
+ * holds some functions for transforming arbitrary DOM structures into
+ * plain sequences of <span> and <br> elements
+ */
+
+var Editor = (function(){
+ // The HTML elements whose content should be suffixed by a newline
+ // when converting them to flat text.
+ var newlineElements = {"P": true, "DIV": true, "LI": true};
+
+ // Create a set of white-space characters that will not be collapsed
+ // by the browser, but will not break text-wrapping either.
+ function safeWhiteSpace(n) {
+ var buffer = [], nb = true;
+ for (; n > 0; n--) {
+ buffer.push((nb || n == 1) ? nbsp : " ");
+ nb = !nb;
+ }
+ return buffer.join("");
+ }
+
+ function splitSpaces(string) {
+ if (string == " ") return nbsp;
+ else return string.replace(/[\t \u00a0]{2,}/g, function(s) {return safeWhiteSpace(s.length);});
+ }
+ function asEditorLines(string) {
+ return splitSpaces(string.replace(/\u00a0/g, " ")).replace(/\r\n?/g, "\n").split("\n");
+ }
+
+ var internetExplorer = document.selection && window.ActiveXObject && /MSIE/.test(navigator.userAgent);
+
+ // Helper function for traverseDOM. Flattens an arbitrary DOM node
+ // into an array of textnodes and <br> tags.
+ function simplifyDOM(root) {
+ var doc = root.ownerDocument;
+ var result = [];
+ var leaving = false;
+
+ function simplifyNode(node) {
+ if (node.nodeType == 3) {
+ var text = node.nodeValue = splitSpaces(node.nodeValue.replace(/[\n\r]/g, ""));
+ if (text.length) leaving = false;
+ result.push(node);
+ }
+ else if (node.nodeName == "BR" && node.childNodes.length == 0) {
+ leaving = true;
+ result.push(node);
+ }
+ else {
+ forEach(node.childNodes, simplifyNode);
+ if (!leaving && newlineElements.hasOwnProperty(node.nodeName)) {
+ leaving = true;
+ result.push(doc.createElement("BR"));
+ }
+ }
+ }
+
+ simplifyNode(root);
+ return result;
+ }
+
+ // Creates a MochiKit-style iterator that goes over a series of DOM
+ // nodes. The values it yields are strings, the textual content of
+ // the nodes. It makes sure that all nodes up to and including the
+ // one whose text is being yielded have been 'normalized' to be just
+ // <span> and <br> elements.
+ // See the story.html file for some short remarks about the use of
+ // continuation-passing style in this iterator.
+ function traverseDOM(start){
+ function yield(value, c){cc = c; return value;}
+ function push(fun, arg, c){return function(){return fun(arg, c);};}
+ function stop(){cc = stop; throw StopIteration;};
+ var cc = push(scanNode, start, stop);
+ var owner = start.ownerDocument;
+ var nodeQueue = [];
+
+ // Create a function that can be used to insert nodes after the
+ // one given as argument.
+ function pointAt(node){
+ var parent = node.parentNode;
+ var next = node.nextSibling;
+ return function(newnode) {
+ parent.insertBefore(newnode, next);
+ };
+ }
+ var point = null;
+
+ // Insert a normalized node at the current point. If it is a text
+ // node, wrap it in a <span>, and give that span a currentText
+ // property -- this is used to cache the nodeValue, because
+ // directly accessing nodeValue is horribly slow on some browsers.
+ // The dirty property is used by the highlighter to determine
+ // which parts of the document have to be re-highlighted.
+ function insertPart(part){
+ var text = "\n";
+ if (part.nodeType == 3) {
+ text = part.nodeValue;
+ var span = owner.createElement("SPAN");
+ span.className = "part";
+ span.appendChild(part);
+ part = span;
+ part.currentText = text;
+ }
+ part.dirty = true;
+ nodeQueue.push(part);
+ point(part);
+ return text;
+ }
+
+ // Extract the text and newlines from a DOM node, insert them into
+ // the document, and yield the textual content. Used to replace
+ // non-normalized nodes.
+ function writeNode(node, c){
+ var toYield = [];
+ forEach(simplifyDOM(node), function(part) {
+ toYield.push(insertPart(part));
+ });
+ return yield(toYield.join(""), c);
+ }
+
+ // Check whether a node is a normalized <span> element.
+ function partNode(node){
+ if (node.nodeName == "SPAN" && node.childNodes.length == 1 && node.firstChild.nodeType == 3){
+ node.currentText = node.firstChild.nodeValue;
+ return true;
+ }
+ return false;
+ }
+
+ // Handle a node. Add its successor to the continuation if there
+ // is one, find out whether the node is normalized. If it is,
+ // yield its content, otherwise, normalize it (writeNode will take
+ // care of yielding).
+ function scanNode(node, c){
+ if (node.nextSibling)
+ c = push(scanNode, node.nextSibling, c);
+
+ if (partNode(node)){
+ nodeQueue.push(node);
+ return yield(node.currentText, c);
+ }
+ else if (node.nodeName == "BR") {
+ nodeQueue.push(node);
+ return yield("\n", c);
+ }
+ else {
+ point = pointAt(node);
+ removeElement(node);
+ return writeNode(node, c);
+ }
+ }
+
+ // MochiKit iterators are objects with a next function that
+ // returns the next value or throws StopIteration when there are
+ // no more values.
+ return {next: function(){return cc();}, nodes: nodeQueue};
+ }
+
+ // Determine the text size of a processed node.
+ function nodeSize(node) {
+ if (node.nodeName == "BR")
+ return 1;
+ else
+ return node.currentText.length;
+ }
+
+ // Search backwards through the top-level nodes until the next BR or
+ // the start of the frame.
+ function startOfLine(node) {
+ while (node && node.nodeName != "BR")
+ node = node.previousSibling;
+ return node;
+ }
+
+ function cleanText(text) {
+ return text.replace(/\u00a0/g, " ");
+ }
+
+ // Client interface for searching the content of the editor. Create
+ // these by calling CodeMirror.getSearchCursor. To use, call
+ // findNext on the resulting object -- this returns a boolean
+ // indicating whether anything was found, and can be called again to
+ // skip to the next find. Use the select and replace methods to
+ // actually do something with the found locations.
+ function SearchCursor(editor, string, fromCursor) {
+ this.editor = editor;
+ this.history = editor.history;
+ this.history.commit();
+
+ // Are we currently at an occurrence of the search string?
+ this.atOccurrence = false;
+ // The object stores a set of nodes coming after its current
+ // position, so that when the current point is taken out of the
+ // DOM tree, we can still try to continue.
+ this.fallbackSize = 15;
+ var cursor;
+ // Start from the cursor when specified and a cursor can be found.
+ if (fromCursor && (cursor = select.cursorPos(this.editor.container))) {
+ this.line = cursor.node;
+ this.offset = cursor.offset;
+ }
+ else {
+ this.line = null;
+ this.offset = 0;
+ }
+ this.valid = !!string;
+
+ // Create a matcher function based on the kind of string we have.
+ var target = string.split("\n"), self = this;;
+ this.matches = (target.length == 1) ?
+ // For one-line strings, searching can be done simply by calling
+ // indexOf on the current line.
+ function() {
+ var match = cleanText(self.history.textAfter(self.line).slice(self.offset)).indexOf(string);
+ if (match > -1)
+ return {from: {node: self.line, offset: self.offset + match},
+ to: {node: self.line, offset: self.offset + match + string.length}};
+ } :
+ // Multi-line strings require internal iteration over lines, and
+ // some clunky checks to make sure the first match ends at the
+ // end of the line and the last match starts at the start.
+ function() {
+ var firstLine = cleanText(self.history.textAfter(self.line).slice(self.offset));
+ var match = firstLine.lastIndexOf(target[0]);
+ if (match == -1 || match != firstLine.length - target[0].length)
+ return false;
+ var startOffset = self.offset + match;
+
+ var line = self.history.nodeAfter(self.line);
+ for (var i = 1; i < target.length - 1; i++) {
+ if (cleanText(self.history.textAfter(line)) != target[i])
+ return false;
+ line = self.history.nodeAfter(line);
+ }
+
+ if (cleanText(self.history.textAfter(line)).indexOf(target[target.length - 1]) != 0)
+ return false;
+
+ return {from: {node: self.line, offset: startOffset},
+ to: {node: line, offset: target[target.length - 1].length}};
+ };
+ }
+
+ SearchCursor.prototype = {
+ findNext: function() {
+ if (!this.valid) return false;
+ this.atOccurrence = false;
+ var self = this;
+
+ // Go back to the start of the document if the current line is
+ // no longer in the DOM tree.
+ if (this.line && !this.line.parentNode) {
+ this.line = null;
+ this.offset = 0;
+ }
+
+ // Set the cursor's position one character after the given
+ // position.
+ function saveAfter(pos) {
+ if (self.history.textAfter(pos.node).length < pos.offset) {
+ self.line = pos.node;
+ self.offset = pos.offset + 1;
+ }
+ else {
+ self.line = self.history.nodeAfter(pos.node);
+ self.offset = 0;
+ }
+ }
+
+ while (true) {
+ var match = this.matches();
+ // Found the search string.
+ if (match) {
+ this.atOccurrence = match;
+ saveAfter(match.from);
+ return true;
+ }
+ this.line = this.history.nodeAfter(this.line);
+ this.offset = 0;
+ // End of document.
+ if (!this.line) {
+ this.valid = false;
+ return false;
+ }
+ }
+ },
+
+ select: function() {
+ if (this.atOccurrence) {
+ select.setCursorPos(this.editor.container, this.atOccurrence.from, this.atOccurrence.to);
+ select.scrollToCursor(this.editor.container);
+ }
+ },
+
+ replace: function(string) {
+ if (this.atOccurrence) {
+ var end = this.editor.replaceRange(this.atOccurrence.from, this.atOccurrence.to, string);
+ this.line = end.node;
+ this.offset = end.offset;
+ this.atOccurrence = false;
+ }
+ }
+ };
+
+ // The Editor object is the main inside-the-iframe interface.
+ function Editor(options) {
+ this.options = options;
+ this.parent = parent;
+ this.doc = document;
+ this.container = this.doc.body;
+ this.win = window;
+ this.history = new History(this.container, this.options.undoDepth, this.options.undoDelay,
+ this, options.onChange);
+
+ if (!Editor.Parser)
+ throw "No parser loaded.";
+ if (options.parserConfig && Editor.Parser.configure)
+ Editor.Parser.configure(options.parserConfig);
+
+ if (!options.textWrapping)
+ this.container.style.whiteSpace = "pre";
+
+ select.setCursorPos(this.container, {node: null, offset: 0});
+
+ this.dirty = [];
+ if (options.content)
+ this.importCode(options.content);
+ else // FF acts weird when the editable document is completely empty
+ this.container.appendChild(this.doc.createElement("BR"));
+
+ if (!options.readOnly) {
+ if (options.continuousScanning !== false) {
+ this.scanner = this.documentScanner(options.linesPerPass);
+ this.delayScanning();
+ }
+
+ function setEditable() {
+ // In IE, designMode frames can not run any scripts, so we use
+ // contentEditable instead.
+ if (document.body.contentEditable != undefined && /MSIE/.test(navigator.userAgent))
+ document.body.contentEditable = "true";
+ else
+ document.designMode = "on";
+ }
+
+ // If setting the frame editable fails, try again when the user
+ // focus it (happens when the frame is not visible on
+ // initialisation, in Firefox).
+ try {
+ setEditable();
+ }
+ catch(e) {
+ var focusEvent = addEventHandler(document, "focus", function() {
+ removeEventHandler(focusEvent);
+ setEditable();
+ });
+ }
+
+ addEventHandler(document, "keydown", method(this, "keyDown"));
+ addEventHandler(document, "keypress", method(this, "keyPress"));
+ addEventHandler(document, "keyup", method(this, "keyUp"));
+ addEventHandler(document.body, "paste", method(this, "markCursorDirty"));
+ addEventHandler(document.body, "cut", method(this, "markCursorDirty"));
+ if (this.options.autoMatchParens)
+ addEventHandler(document.body, "click", method(this, "scheduleParenBlink"));
+ }
+ }
+
+ function isSafeKey(code) {
+ return (code >= 16 && code <= 18) || // shift, control, alt
+ (code >= 33 && code <= 40); // arrows, home, end
+ }
+
+ Editor.prototype = {
+ // Import a piece of code into the editor.
+ importCode: function(code) {
+ this.history.push(null, null, asEditorLines(code));
+ this.history.reset();
+ },
+
+ // Extract the code from the editor.
+ getCode: function() {
+ if (!this.container.firstChild)
+ return "";
+
+ var accum = [];
+ forEach(traverseDOM(this.container.firstChild), method(accum, "push"));
+ return cleanText(accum.join(""));
+ },
+
+ // Move the cursor to the start of a specific line (counting from 1).
+ jumpToLine: function(line) {
+ if (line <= 1 || !this.container.firstChild) {
+ select.focusAfterNode(null, this.container);
+ }
+ else {
+ var pos = this.container.firstChild;
+ while (true) {
+ if (pos.nodeName == "BR") line--;
+ if (line <= 1 || !pos.nextSibling) break;
+ pos = pos.nextSibling;
+ }
+ select.focusAfterNode(pos, this.container);
+ }
+ select.scrollToCursor(this.container);
+ },
+
+ // Find the line that the cursor is currently on.
+ currentLine: function() {
+ var pos = select.cursorPos(this.container, true), line = 1;
+ if (!pos) return 1;
+ for (cursor = pos.node; cursor; cursor = cursor.previousSibling)
+ if (cursor.nodeName == "BR") line++;
+ return line;
+ },
+
+ // Retrieve the selected text.
+ selectedText: function() {
+ var h = this.history;
+ h.commit();
+
+ var start = select.cursorPos(this.container, true),
+ end = select.cursorPos(this.container, false);
+ if (!start || !end) return "";
+
+ if (start.node == end.node)
+ return h.textAfter(start.node).slice(start.offset, end.offset);
+
+ var text = [h.textAfter(start.node).slice(start.offset)];
+ for (pos = h.nodeAfter(start.node); pos != end.node; pos = h.nodeAfter(pos))
+ text.push(h.textAfter(pos));
+ text.push(h.textAfter(end.node).slice(0, end.offset));
+ return cleanText(text.join("\n"));
+ },
+
+ // Replace the selection with another piece of text.
+ replaceSelection: function(text) {
+ this.history.commit();
+ var start = select.cursorPos(this.container, true),
+ end = select.cursorPos(this.container, false);
+ if (!start || !end) return false;
+
+ end = this.replaceRange(start, end, text);
+ select.setCursorPos(this.container, start, end);
+ return true;
+ },
+
+ replaceRange: function(from, to, text) {
+ var lines = asEditorLines(text);
+ lines[0] = this.history.textAfter(from.node).slice(0, from.offset) + lines[0];
+ var lastLine = lines[lines.length - 1];
+ lines[lines.length - 1] = lastLine + this.history.textAfter(to.node).slice(to.offset);
+ var end = this.history.nodeAfter(to.node);
+ this.history.push(from.node, end, lines);
+ return {node: this.history.nodeBefore(end),
+ offset: lastLine.length};
+ },
+
+ getSearchCursor: function(string, fromCursor) {
+ return new SearchCursor(this, string, fromCursor);
+ },
+
+ // Re-indent the whole buffer
+ reindent: function() {
+ if (this.container.firstChild)
+ this.indentRegion(null, this.container.lastChild);
+ },
+
+ // Intercept enter and tab, and assign their new functions.
+ keyDown: function(event) {
+ // Don't scan when the user is typing.
+ this.delayScanning();
+ // Schedule a paren-highlight event, if configured.
+ if (this.options.autoMatchParens)
+ this.scheduleParenBlink();
+
+ if (event.keyCode == 13) { // enter
+ if (event.ctrlKey) {
+ this.reparseBuffer();
+ }
+ else {
+ select.insertNewlineAtCursor(this.win);
+ this.indentAtCursor();
+ select.scrollToCursor(this.container);
+ }
+ event.stop();
+ }
+ else if (event.keyCode == 9) { // tab
+ this.handleTab();
+ event.stop();
+ }
+ else if (event.ctrlKey) {
+ if (event.keyCode == 90 || event.keyCode == 8) { // Z, backspace
+ this.history.undo();
+ event.stop();
+ }
+ else if (event.keyCode == 89) { // Y
+ this.history.redo();
+ event.stop();
+ }
+ else if (event.keyCode == 83 && this.options.saveFunction) { // S
+ this.options.saveFunction();
+ event.stop();
+ }
+ }
+ },
+
+ // Check for characters that should re-indent the current line,
+ // and prevent Opera from handling enter and tab anyway.
+ keyPress: function(event) {
+ var electric = Editor.Parser.electricChars;
+ // Hack for Opera, and Firefox on OS X, in which stopping a
+ // keydown event does not prevent the associated keypress event
+ // from happening, so we have to cancel enter and tab again
+ // here.
+ if (event.code == 13 || event.code == 9)
+ event.stop();
+ else if ((event.character == "[" || event.character == "]") && event.ctrlKey)
+ event.stop(), this.blinkParens();
+ else if (electric && electric.indexOf(event.character) != -1)
+ this.parent.setTimeout(method(this, "indentAtCursor"), 0);
+ },
+
+ // Mark the node at the cursor dirty when a non-safe key is
+ // released.
+ keyUp: function(event) {
+ if (internetExplorer)
+ this.container.createTextRange().execCommand("unlink");
+
+ if (!isSafeKey(event.keyCode))
+ this.markCursorDirty();
+ },
+
+ // Indent the line following a given <br>, or null for the first
+ // line. If given a <br> element, this must have been highlighted
+ // so that it has an indentation method. Returns the whitespace
+ // element that has been modified or created (if any).
+ indentLineAfter: function(start) {
+ // whiteSpace is the whitespace span at the start of the line,
+ // or null if there is no such node.
+ var whiteSpace = start ? start.nextSibling : this.container.firstChild;
+ if (whiteSpace && !hasClass(whiteSpace, "whitespace"))
+ whiteSpace = null;
+
+ // Sometimes the start of the line can influence the correct
+ // indentation, so we retrieve it.
+ var firstText = whiteSpace ? whiteSpace.nextSibling : (start ? start.nextSibling : this.container.firstChild);
+ var nextChars = (start && firstText && firstText.currentText) ? firstText.currentText : "";
+
+ // Ask the lexical context for the correct indentation, and
+ // compute how much this differs from the current indentation.
+ var indent = start ? start.indentation(nextChars) : 0;
+ var indentDiff = indent - (whiteSpace ? whiteSpace.currentText.length : 0);
+
+ // If there is too much, this is just a matter of shrinking a span.
+ if (indentDiff < 0) {
+ if (indent == 0) {
+ removeElement(whiteSpace);
+ whiteSpace = null;
+ }
+ else {
+ whiteSpace.currentText = safeWhiteSpace(indent);
+ whiteSpace.firstChild.nodeValue = whiteSpace.currentText;
+ }
+ }
+ // Not enough...
+ else if (indentDiff > 0) {
+ // If there is whitespace, we grow it.
+ if (whiteSpace) {
+ whiteSpace.currentText = safeWhiteSpace(indent);
+ whiteSpace.firstChild.nodeValue = whiteSpace.currentText;
+ }
+ // Otherwise, we have to add a new whitespace node.
+ else {
+ whiteSpace = this.doc.createElement("SPAN");
+ whiteSpace.className = "part whitespace";
+ whiteSpace.appendChild(this.doc.createTextNode(safeWhiteSpace(indent)));
+ if (start)
+ insertAfter(whiteSpace, start);
+ else
+ this.container.insertBefore(whiteSpace, this.container.firstChild);
+ }
+ }
+ return whiteSpace;
+ },
+
+ // Re-highlight the selected part of the document.
+ highlightAtCursor: function() {
+ var pos = select.selectionTopNode(this.container, true);
+ var to = select.selectionTopNode(this.container, false);
+ if (pos === false || !to) return;
+ // Skip one node ahead to make sure the cursor itself is
+ // *inside* a highlighted line.
+ if (to.nextSibling) to = to.nextSibling;
+
+ var sel = select.markSelection(this.win);
+ var toIsText = to.nodeType == 3;
+ if (!toIsText) to.dirty = true;
+
+ // Highlight lines as long as to is in the document and dirty.
+ while (to.parentNode == this.container && (toIsText || to.dirty)) {
+ var result = this.highlight(pos, 1, true);
+ if (result) pos = result.node;
+ if (!result || result.left) break;
+ }
+ select.selectMarked(sel);
+ },
+
+ // When tab is pressed with text selected, the whole selection is
+ // re-indented, when nothing is selected, the line with the cursor
+ // is re-indented.
+ handleTab: function() {
+ if (this.options.dumbTabs) {
+ select.insertTabAtCursor(this.win);
+ }
+ else {
+ var start = select.selectionTopNode(this.container, true),
+ end = select.selectionTopNode(this.container, false);
+ if (start === false || end === false) return;
+
+ if (start == end)
+ this.indentAtCursor();
+ else
+ this.indentRegion(start, end);
+ }
+ },
+
+ // Delay (or initiate) the next paren blink event.
+ scheduleParenBlink: function() {
+ if (this.parenEvent) this.parent.clearTimeout(this.parenEvent);
+ this.parenEvent = this.parent.setTimeout(method(this, "blinkParens"), 300);
+ },
+
+ // Take the token before the cursor. If it contains a character in
+ // '()[]{}', search for the matching paren/brace/bracket, and
+ // highlight them in green for a moment, or red if no proper match
+ // was found.
+ blinkParens: function() {
+ // Clear the event property.
+ if (this.parenEvent) this.parent.clearTimeout(this.parenEvent);
+ this.parenEvent = null;
+
+ // Extract a 'paren' from a piece of text.
+ function paren(node) {
+ if (node.currentText) {
+ var match = node.currentText.match(/^[\s\u00a0]*([\(\)\[\]{}])[\s\u00a0]*$/);
+ return match && match[1];
+ }
+ }
+ // Determine the direction a paren is facing.
+ function forward(ch) {
+ return /[\(\[\{]/.test(ch);
+ }
+
+ this.highlightAtCursor();
+ var cursor = select.selectionTopNode(this.container, true), ch, self = this;
+ if (!cursor || !(ch = paren(cursor))) return;
+ // We only look for tokens with the same className.
+ var className = cursor.className, dir = forward(ch), match = matching[ch];
+
+ // Since parts of the document might not have been properly
+ // highlighted, and it is hard to know in advance which part we
+ // have to scan, we just try, and when we find dirty nodes we
+ // abort, parse them, and re-try.
+ function tryFindMatch() {
+ var stack = [], ch, ok = true;;
+ for (var runner = cursor; runner; runner = dir ? runner.nextSibling : runner.previousSibling) {
+ if (runner.className == className && runner.nodeName == "SPAN" && (ch = paren(runner))) {
+ if (forward(ch) == dir)
+ stack.push(ch);
+ else if (!stack.length)
+ ok = false;
+ else if (stack.pop() != matching[ch])
+ ok = false;
+ if (!stack.length) break;
+ }
+ else if (runner.dirty || runner.nodeName != "SPAN" && runner.nodeName != "BR") {
+ return {node: runner, status: "dirty"};
+ }
+ }
+ return {node: runner, status: runner && ok};
+ }
+ // Temporarily give the relevant nodes a colour.
+ function blink(node, ok) {
+ node.style.fontWeight = "bold";
+ node.style.color = ok ? "#8F8" : "#F88";
+ self.parent.setTimeout(function() {node.style.fontWeight = ""; node.style.color = "";}, 500);
+ }
+
+ while (true) {
+ var found = tryFindMatch();
+ if (found.status == "dirty") {
+ this.highlight(found.node, 1);
+ // Needed because in some corner cases a highlight does not
+ // reach a node.
+ found.node.dirty = false;
+ continue;
+ }
+ else {
+ blink(cursor, found.status);
+ if (found.node) blink(found.node, found.status);
+ break;
+ }
+ }
+ },
+
+ // Adjust the amount of whitespace at the start of the line that
+ // the cursor is on so that it is indented properly.
+ indentAtCursor: function() {
+ if (!this.container.firstChild) return;
+ // The line has to have up-to-date lexical information, so we
+ // highlight it first.
+ this.highlightAtCursor();
+ var cursor = select.selectionTopNode(this.container, false);
+ // If we couldn't determine the place of the cursor,
+ // there's nothing to indent.
+ if (cursor === false)
+ return;
+ var lineStart = startOfLine(cursor);
+ var whiteSpace = this.indentLineAfter(lineStart);
+ if (cursor == lineStart && whiteSpace)
+ cursor = whiteSpace;
+ // This means the indentation has probably messed up the cursor.
+ if (cursor == whiteSpace)
+ select.focusAfterNode(cursor, this.container);
+ },
+
+ // Indent all lines whose start falls inside of the current
+ // selection.
+ indentRegion: function(current, end) {
+ var sel = select.markSelection(this.win);
+ if (!current)
+ this.indentLineAfter(current);
+ else
+ current = startOfLine(current.previousSibling);
+ end = startOfLine(end);
+
+ while (true) {
+ var result = this.highlight(current, 1);
+ var next = result ? result.node : null;
+
+ while (current != next)
+ current = current ? current.nextSibling : this.container.firstChild;
+ if (next)
+ this.indentLineAfter(next);
+ if (current == end)
+ break;
+ }
+ select.selectMarked(sel);
+ },
+
+ // Find the node that the cursor is in, mark it as dirty, and make
+ // sure a highlight pass is scheduled.
+ markCursorDirty: function() {
+ var cursor = select.selectionTopNode(this.container, false);
+ if (cursor !== false && this.container.firstChild) {
+ this.scheduleHighlight();
+ this.addDirtyNode(cursor || this.container.firstChild);
+ }
+ },
+
+ reparseBuffer: function() {
+ forEach(this.container.childNodes, function(node) {node.dirty = true;});
+ if (this.container.firstChild)
+ this.addDirtyNode(this.container.firstChild);
+ },
+
+ // Add a node to the set of dirty nodes, if it isn't already in
+ // there.
+ addDirtyNode: function(node) {
+ node = node || this.container.firstChild;
+ if (!node) return;
+
+ for (var i = 0; i < this.dirty.length; i++)
+ if (this.dirty[i] == node) return;
+
+ if (node.nodeType != 3)
+ node.dirty = true;
+ this.dirty.push(node);
+ },
+
+ // Cause a highlight pass to happen in options.passDelay
+ // milliseconds. Clear the existing timeout, if one exists. This
+ // way, the passes do not happen while the user is typing, and
+ // should as unobtrusive as possible.
+ scheduleHighlight: function() {
+ // Timeouts are routed through the parent window, because on
+ // some browsers designMode windows do not fire timeouts.
+ var self = this;
+ this.parent.clearTimeout(this.highlightTimeout);
+ this.highlightTimeout = this.parent.setTimeout(function(){self.highlightDirty();}, this.options.passDelay);
+ },
+
+ // Fetch one dirty node, and remove it from the dirty set.
+ getDirtyNode: function() {
+ while (this.dirty.length > 0) {
+ var found = this.dirty.pop();
+ // IE8 sometimes throws an unexplainable 'invalid argument'
+ // exception for found.parentNode
+ try {
+ // If the node has been coloured in the meantime, or is no
+ // longer in the document, it should not be returned.
+ if ((found.dirty || found.nodeType == 3) && found.parentNode)
+ return found;
+ } catch (e) {}
+ }
+ return null;
+ },
+
+ // Pick dirty nodes, and highlight them, until
+ // options.linesPerPass lines have been highlighted. The highlight
+ // method will continue to next lines as long as it finds dirty
+ // nodes. It returns an object indicating the amount of lines
+ // left, and information about the place where it stopped. If
+ // there are dirty nodes left after this function has spent all
+ // its lines, it shedules another highlight to finish the job.
+ highlightDirty: function(force) {
+ var lines = force ? Infinity : this.options.linesPerPass;
+ var sel = select.markSelection(this.win);
+ var start;
+ while (lines > 0 && (start = this.getDirtyNode())){
+ var result = this.highlight(start, lines);
+ if (result) {
+ lines = result.left;
+ if (result.node && result.dirty)
+ this.addDirtyNode(result.node);
+ }
+ }
+ select.selectMarked(sel);
+ if (start)
+ this.scheduleHighlight();
+ return this.dirty.length == 0;
+ },
+
+ // Creates a function that, when called through a timeout, will
+ // continuously re-parse the document.
+ documentScanner: function(linesPer) {
+ var self = this, pos = null;
+ return function() {
+ // If the current node is no longer in the document... oh
+ // well, we start over.
+ if (pos && pos.parentNode != self.container)
+ pos = null;
+ var sel = select.markSelection(self.win);
+ var result = self.highlight(pos, linesPer, true);
+ select.selectMarked(sel);
+ pos = result ? (result.node && result.node.nextSibling) : null;
+ self.delayScanning();
+ };
+ },
+
+ // Starts the continuous scanning process for this document after
+ // a given interval.
+ delayScanning: function() {
+ if (this.scanner) {
+ this.parent.clearTimeout(this.documentScan);
+ this.documentScan = this.parent.setTimeout(this.scanner, this.options.continuousScanning);
+ }
+ },
+
+ // The function that does the actual highlighting/colouring (with
+ // help from the parser and the DOM normalizer). Its interface is
+ // rather overcomplicated, because it is used in different
+ // situations: ensuring that a certain line is highlighted, or
+ // highlighting up to X lines starting from a certain point. The
+ // 'from' argument gives the node at which it should start. If
+ // this is null, it will start at the beginning of the frame. When
+ // a number of lines is given with the 'lines' argument, it will
+ // colour no more than that amount. If at any time it comes across
+ // a 'clean' line (no dirty nodes), it will stop, except when
+ // 'cleanLines' is true.
+ highlight: function(from, lines, cleanLines){
+ var container = this.container, self = this;
+
+ if (!container.firstChild)
+ return;
+ // Backtrack to the first node before from that has a partial
+ // parse stored.
+ while (from && (!from.parserFromHere || from.dirty))
+ from = from.previousSibling;
+ // If we are at the end of the document, do nothing.
+ if (from && !from.nextSibling)
+ return;
+
+ // Check whether a part (<span> node) and the corresponding token
+ // match.
+ function correctPart(token, part){
+ return !part.reduced && part.currentText == token.value && part.className == "part " + token.style;
+ }
+ // Shorten the text associated with a part by chopping off
+ // characters from the front. Note that only the currentText
+ // property gets changed. For efficiency reasons, we leave the
+ // nodeValue alone -- we set the reduced flag to indicate that
+ // this part must be replaced.
+ function shortenPart(part, minus){
+ part.currentText = part.currentText.substring(minus);
+ part.reduced = true;
+ }
+ // Create a part corresponding to a given token.
+ function tokenPart(token){
+ var part = self.doc.createElement("SPAN");
+ part.className = "part " + token.style;
+ part.appendChild(self.doc.createTextNode(token.value));
+ part.currentText = token.value;
+ return part;
+ }
+
+ // Get the token stream. If from is null, we start with a new
+ // parser from the start of the frame, otherwise a partial parse
+ // is resumed.
+ var traversal = traverseDOM(from ? from.nextSibling : container.firstChild),
+ stream = stringStream(traversal),
+ parsed = from ? from.parserFromHere(stream) : Editor.Parser.make(stream);
+
+ // parts is an interface to make it possible to 'delay' fetching
+ // the next DOM node until we are completely done with the one
+ // before it. This is necessary because often the next node is
+ // not yet available when we want to proceed past the current
+ // one.
+ var parts = {
+ current: null,
+ // Fetch current node.
+ get: function(){
+ if (!this.current)
+ this.current = traversal.nodes.shift();
+ return this.current;
+ },
+ // Advance to the next part (do not fetch it yet).
+ next: function(){
+ this.current = null;
+ },
+ // Remove the current part from the DOM tree, and move to the
+ // next.
+ remove: function(){
+ container.removeChild(this.get());
+ this.current = null;
+ },
+ // Advance to the next part that is not empty, discarding empty
+ // parts.
+ getNonEmpty: function(){
+ var part = this.get();
+ // Allow empty nodes when they are alone on a line, needed
+ // for the FF cursor bug workaround (see select.js,
+ // insertNewlineAtCursor).
+ while (part && part.nodeName == "SPAN" && part.currentText == ""){
+ var old = part;
+ this.remove();
+ part = this.get();
+ // Adjust selection information, if any. See select.js for details.
+ select.replaceSelection(old.firstChild, part.firstChild || part, 0, 0);
+ }
+ return part;
+ }
+ };
+
+ var lineDirty = false, lineNodes = 0;
+
+ // This forEach loops over the tokens from the parsed stream, and
+ // at the same time uses the parts object to proceed through the
+ // corresponding DOM nodes.
+ forEach(parsed, function(token){
+ var part = parts.getNonEmpty();
+
+ if (token.value == "\n"){
+ // The idea of the two streams actually staying synchronized
+ // is such a long shot that we explicitly check.
+ if (part.nodeName != "BR")
+ throw "Parser out of sync. Expected BR.";
+
+ if (part.dirty || !part.indentation) lineDirty = true;
+ if (lineDirty) self.history.touch(from);
+ from = part;
+
+ // Every <br> gets a copy of the parser state and a lexical
+ // context assigned to it. The first is used to be able to
+ // later resume parsing from this point, the second is used
+ // for indentation.
+ part.parserFromHere = parsed.copy();
+ part.indentation = token.indentation;
+ part.dirty = false;
+ // A clean line with more than one node means we are done.
+ // Throwing a StopIteration is the way to break out of a
+ // MochiKit forEach loop.
+ if ((lines !== undefined && --lines <= 0) || (!lineDirty && lineNodes > 1 && !cleanLines))
+ throw StopIteration;
+ lineDirty = false; lineNodes = 0;
+ parts.next();
+ }
+ else {
+ if (part.nodeName != "SPAN")
+ throw "Parser out of sync. Expected SPAN.";
+ if (part.dirty)
+ lineDirty = true;
+ lineNodes++;
+
+ // If the part matches the token, we can leave it alone.
+ if (correctPart(token, part)){
+ part.dirty = false;
+ parts.next();
+ }
+ // Otherwise, we have to fix it.
+ else {
+ lineDirty = true;
+ // Insert the correct part.
+ var newPart = tokenPart(token);
+ container.insertBefore(newPart, part);
+ var tokensize = token.value.length;
+ var offset = 0;
+ // Eat up parts until the text for this token has been
+ // removed, adjusting the stored selection info (see
+ // select.js) in the process.
+ while (tokensize > 0) {
+ part = parts.get();
+ var partsize = part.currentText.length;
+ select.replaceSelection(part.firstChild, newPart.firstChild, tokensize, offset);
+ if (partsize > tokensize){
+ shortenPart(part, tokensize);
+ tokensize = 0;
+ }
+ else {
+ tokensize -= partsize;
+ offset += partsize;
+ parts.remove();
+ }
+ }
+ }
+ }
+ });
+ if (lineDirty) this.history.touch(from);
+
+ // The function returns some status information that is used by
+ // hightlightDirty to determine whether and where it has to
+ // continue.
+ return {left: lines,
+ node: parts.get(),
+ dirty: lineDirty};
+ }
+ };
+
+ return Editor;
+})();
+
+addEventHandler(window, "load", function() {
+ var CodeMirror = window.frameElement.CodeMirror;
+ CodeMirror.editor = new Editor(CodeMirror.options);
+ if (CodeMirror.options.initCallback) {
+ this.parent.setTimeout(function(){
+ CodeMirror.options.initCallback(CodeMirror);
+ }, 0);
+ }
+});
diff --git a/o3d/samples/third_party/codemirror/js/mirrorframe.js b/o3d/samples/third_party/codemirror/js/mirrorframe.js
new file mode 100644
index 0000000..56a6c42
--- /dev/null
+++ b/o3d/samples/third_party/codemirror/js/mirrorframe.js
@@ -0,0 +1,83 @@
+/* Demonstration of embedding CodeMirror in a bigger application. The
+ * interface defined here is a mess of prompts and confirms, and
+ * should probably not be used in a real project.
+ */
+
+function MirrorFrame(place, options) {
+ this.home = document.createElement("DIV");
+ if (place.appendChild)
+ place.appendChild(this.home);
+ else
+ place(this.home);
+
+ var self = this;
+ function makeButton(name, action) {
+ var button = document.createElement("INPUT");
+ button.type = "button";
+ button.value = name;
+ self.home.appendChild(button);
+ button.onclick = function(){self[action].call(self);};
+ }
+
+ makeButton("Search", "search");
+ makeButton("Replace", "replace");
+ makeButton("Current line", "line");
+ makeButton("Jump to line", "jump");
+ makeButton("Insert constructor", "macro");
+ makeButton("Indent all", "reindent");
+
+ this.mirror = new CodeMirror(this.home, options);
+}
+
+MirrorFrame.prototype = {
+ search: function() {
+ var text = prompt("Enter search term:", "");
+ if (!text) return;
+
+ var first = true;
+ do {
+ var cursor = this.mirror.getSearchCursor(text, first);
+ first = false;
+ while (cursor.findNext()) {
+ cursor.select();
+ if (!confirm("Search again?"))
+ return;
+ }
+ } while (confirm("End of document reached. Start over?"));
+ },
+
+ replace: function() {
+ // This is a replace-all, but it is possible to implement a
+ // prompting replace.
+ var from = prompt("Enter search string:", ""), to;
+ if (from) to = prompt("What should it be replaced with?", "");
+ if (!to) return;
+
+ var cursor = this.mirror.getSearchCursor(from, false);
+ while (cursor.findNext())
+ cursor.replace(to);
+ },
+
+ jump: function() {
+ var line = prompt("Jump to line:", "");
+ if (line && !isNaN(Number(line)))
+ this.mirror.jumpToLine(Number(line));
+ },
+
+ line: function() {
+ alert("The cursor is currently at line " + this.mirror.currentLine());
+ this.mirror.focus();
+ },
+
+ macro: function() {
+ var name = prompt("Name your constructor:", "");
+ if (name) {
+ if (!this.mirror.replaceSelection("function " + name + "() {\n \n}\n\n" + name + ".prototype = {\n \n};\n", true))
+ alert("Place the cursor in the document first.");
+ }
+ },
+
+ reindent: function() {
+ this.mirror.reindent();
+ }
+};
diff --git a/o3d/samples/third_party/codemirror/js/parsecss.js b/o3d/samples/third_party/codemirror/js/parsecss.js
new file mode 100644
index 0000000..a49a8b0
--- /dev/null
+++ b/o3d/samples/third_party/codemirror/js/parsecss.js
@@ -0,0 +1,155 @@
+/* Simple parser for CSS */
+
+var CSSParser = Editor.Parser = (function() {
+ var tokenizeCSS = (function() {
+ function normal(source, setState) {
+ var ch = source.next();
+ if (ch == "@") {
+ source.nextWhile(matcher(/\w/));
+ return "css-at";
+ }
+ else if (ch == "/" && source.equals("*")) {
+ setState(inCComment);
+ return null;
+ }
+ else if (ch == "<" && source.equals("!")) {
+ setState(inSGMLComment);
+ return null;
+ }
+ else if (ch == "=") {
+ return "css-compare";
+ }
+ else if (source.equals("=") && (ch == "~" || ch == "|")) {
+ source.next();
+ return "css-compare";
+ }
+ else if (ch == "\"" || ch == "'") {
+ setState(inString(ch));
+ return null;
+ }
+ else if (ch == "#") {
+ source.nextWhile(matcher(/\w/));
+ return "css-hash";
+ }
+ else if (ch == "!") {
+ source.nextWhile(matcher(/[ \t]/));
+ source.nextWhile(matcher(/\w/));
+ return "css-important";
+ }
+ else if (/\d/.test(ch)) {
+ source.nextWhile(matcher(/[\w.%]/));
+ return "css-unit";
+ }
+ else if (/[,.+>*\/]/.test(ch)) {
+ return "css-select-op";
+ }
+ else if (/[;{}:\[\]]/.test(ch)) {
+ return "css-punctuation";
+ }
+ else {
+ source.nextWhile(matcher(/[\w\\\-_]/));
+ return "css-identifier";
+ }
+ }
+
+ function inCComment(source, setState) {
+ var maybeEnd = false;
+ while (!source.endOfLine()) {
+ var ch = source.next();
+ if (maybeEnd && ch == "/") {
+ setState(normal);
+ break;
+ }
+ maybeEnd = (ch == "*");
+ }
+ return "css-comment";
+ }
+
+ function inSGMLComment(source, setState) {
+ var dashes = 0;
+ while (!source.endOfLine()) {
+ var ch = source.next();
+ if (dashes >= 2 && ch == ">") {
+ setState(normal);
+ break;
+ }
+ dashes = (ch == "-") ? dashes + 1 : 0;
+ }
+ return "css-comment";
+ }
+
+ function inString(quote) {
+ return function(source, setState) {
+ var escaped = false;
+ while (!source.endOfLine()) {
+ var ch = source.next();
+ if (ch == quote && !escaped)
+ break;
+ escaped = ch == "\\";
+ }
+ if (!escaped)
+ setState(normal);
+ return "css-string";
+ };
+ }
+
+ return function(source, startState) {
+ return tokenizer(source, startState || normal);
+ };
+ })();
+
+ function indentCSS(inBraces, inRule, base) {
+ return function(nextChars) {
+ if (!inBraces || /^\}/.test(nextChars)) return base;
+ else if (inRule) return base + 4;
+ else return base + 2;
+ };
+ }
+
+ // This is a very simplistic parser -- since CSS does not really
+ // nest, it works acceptably well, but some nicer colouroing could
+ // be provided with a more complicated parser.
+ function parseCSS(source, basecolumn) {
+ basecolumn = basecolumn || 0;
+ var tokens = tokenizeCSS(source);
+ var inBraces = false, inRule = false;
+
+ var iter = {
+ next: function() {
+ var token = tokens.next(), style = token.style, content = token.content;
+
+ if (style == "css-identifier" && inRule)
+ token.style = "css-value";
+ if (style == "css-hash")
+ token.style = inRule ? "css-colorcode" : "css-identifier";
+
+ if (content == "\n")
+ token.indentation = indentCSS(inBraces, inRule, basecolumn);
+
+ if (content == "{")
+ inBraces = true;
+ else if (content == "}")
+ inBraces = inRule = false;
+ else if (inBraces && content == ";")
+ inRule = false;
+ else if (inBraces && style != "css-comment" && style != "whitespace")
+ inRule = true;
+
+ return token;
+ },
+
+ copy: function() {
+ var _inBraces = inBraces, _inRule = inRule, _tokenState = tokens.state;
+ return function(source) {
+ tokens = tokenizeCSS(source, _tokenState);
+ inBraces = _inBraces;
+ inRule = _inRule;
+ return iter;
+ };
+ }
+ };
+ return iter;
+ }
+
+ return {make: parseCSS, electricChars: "}"};
+})();
diff --git a/o3d/samples/third_party/codemirror/js/parsehtmlmixed.js b/o3d/samples/third_party/codemirror/js/parsehtmlmixed.js
new file mode 100644
index 0000000..f093b63
--- /dev/null
+++ b/o3d/samples/third_party/codemirror/js/parsehtmlmixed.js
@@ -0,0 +1,66 @@
+var HTMLMixedParser = Editor.Parser = (function() {
+ if (!(CSSParser && JSParser && XMLParser))
+ throw new Error("CSS, JS, and XML parsers must be loaded for HTML mixed mode to work.");
+ XMLParser.configure({useHTMLKludges: true});
+
+ function stringAhead(stream, string) {
+ stream.nextWhile(matcher(/[\s\u00a0]/));
+ var found = stream.matches(string, false);
+ stream.reset();
+ return found;
+ }
+
+ function parseMixed(stream) {
+ var htmlParser = XMLParser.make(stream), localParser = null, inTag = false;
+ var iter = {next: top, copy: copy};
+
+ function top() {
+ var token = htmlParser.next();
+ if (token.content == "<")
+ inTag = true;
+ else if (token.style == "xml-tagname" && inTag === true)
+ inTag = token.content.toLowerCase();
+ else if (token.content == ">") {
+ if (inTag == "script")
+ iter.next = local(JSParser, "</script");
+ else if (inTag == "style")
+ iter.next = local(CSSParser, "</style");
+ inTag = false;
+ }
+ return token;
+ }
+ function local(parser, tag) {
+ localParser = parser.make(stream, htmlParser.indentation() + 2);
+ return function() {
+ if (stringAhead(stream, tag)) {
+ localParser = null;
+ iter.next = top;
+ return top();
+ }
+ var token = localParser.next();
+ var lt = token.value.lastIndexOf("<"), sz = Math.min(token.value.length - lt, tag.length);
+ if (lt != -1 && token.value.slice(lt, lt + sz).toLowerCase() == tag.slice(0, sz) && stringAhead(stream, tag.slice(sz))) {
+ stream.push(token.value.slice(lt));
+ token.value = token.value.slice(0, lt);
+ }
+ return token;
+ };
+ }
+
+ function copy() {
+ var _html = htmlParser.copy(), _local = localParser && localParser.copy(),
+ _next = iter.next, _inTag = inTag;
+ return function(_stream) {
+ stream = _stream;
+ htmlParser = _html(_stream);
+ localParser = _local && _local(_stream);
+ iter.next = _next;
+ inTag = _inTag;
+ return iter;
+ };
+ }
+ return iter;
+ }
+
+ return {make: parseMixed, electricChars: "{}/"};
+})();
diff --git a/o3d/samples/third_party/codemirror/js/parsejavascript.js b/o3d/samples/third_party/codemirror/js/parsejavascript.js
new file mode 100644
index 0000000..afd5a29
--- /dev/null
+++ b/o3d/samples/third_party/codemirror/js/parsejavascript.js
@@ -0,0 +1,322 @@
+/* Parse function for JavaScript. Makes use of the tokenizer from
+ * tokenizejavascript.js. Note that your parsers do not have to be
+ * this complicated -- if you don't want to recognize local variables,
+ * in many languages it is enough to just look for braces, semicolons,
+ * parentheses, etc, and know when you are inside a string or comment.
+ *
+ * See manual.html for more info about the parser interface.
+ */
+
+var JSParser = Editor.Parser = (function() {
+ // Token types that can be considered to be atoms.
+ var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true};
+ // Constructor for the lexical context objects.
+ function JSLexical(indented, column, type, align, prev) {
+ // indentation at start of this line
+ this.indented = indented;
+ // column at which this scope was opened
+ this.column = column;
+ // type of scope ('vardef', 'stat' (statement), 'form' (special form), '[', '{', or '(')
+ this.type = type;
+ // '[', '{', or '(' blocks that have any text after their opening
+ // character are said to be 'aligned' -- any lines below are
+ // indented all the way to the opening character.
+ if (align != null)
+ this.align = align;
+ // Parent scope, if any.
+ this.prev = prev;
+ }
+ // My favourite JavaScript indentation rules.
+ function indentJS(lexical) {
+ return function(firstChars) {
+ var firstChar = firstChars && firstChars.charAt(0);
+ var closing = firstChar == lexical.type;
+ if (lexical.type == "vardef")
+ return lexical.indented + 4;
+ else if (lexical.type == "form" && firstChar == "{")
+ return lexical.indented;
+ else if (lexical.type == "stat" || lexical.type == "form")
+ return lexical.indented + 2;
+ else if (lexical.align)
+ return lexical.column - (closing ? 1 : 0);
+ else
+ return lexical.indented + (closing ? 0 : 2);
+ };
+ }
+
+ // The parser-iterator-producing function itself.
+ function parseJS(input, basecolumn) {
+ // Wrap the input in a token stream
+ var tokens = tokenizeJavaScript(input);
+ // The parser state. cc is a stack of actions that have to be
+ // performed to finish the current statement. For example we might
+ // know that we still need to find a closing parenthesis and a
+ // semicolon. Actions at the end of the stack go first. It is
+ // initialized with an infinitely looping action that consumes
+ // whole statements.
+ var cc = [statements];
+ // Context contains information about the current local scope, the
+ // variables defined in that, and the scopes above it.
+ var context = null;
+ // The lexical scope, used mostly for indentation.
+ var lexical = new JSLexical((basecolumn || 0) - 2, 0, "block", false);
+ // Current column, and the indentation at the start of the current
+ // line. Used to create lexical scope objects.
+ var column = 0;
+ var indented = 0;
+ // Variables which are used by the mark, cont, and pass functions
+ // below to communicate with the driver loop in the 'next'
+ // function.
+ var consume, marked;
+
+ // The iterator object.
+ var parser = {next: next, copy: copy};
+
+ function next(){
+ // Start by performing any 'lexical' actions (adjusting the
+ // lexical variable), or the operations below will be working
+ // with the wrong lexical state.
+ while(cc[cc.length - 1].lex)
+ cc.pop()();
+
+ // Fetch a token.
+ var token = tokens.next();
+
+ // Adjust column and indented.
+ if (token.type == "whitespace" && column == 0)
+ indented = token.value.length;
+ column += token.value.length;
+ if (token.content == "\n"){
+ indented = column = 0;
+ // If the lexical scope's align property is still undefined at
+ // the end of the line, it is an un-aligned scope.
+ if (!("align" in lexical))
+ lexical.align = false;
+ // Newline tokens get an indentation function associated with
+ // them.
+ token.indentation = indentJS(lexical);
+ }
+ // No more processing for meaningless tokens.
+ if (token.type == "whitespace" || token.type == "comment")
+ return token;
+ // When a meaningful token is found and the lexical scope's
+ // align is undefined, it is an aligned scope.
+ if (!("align" in lexical))
+ lexical.align = true;
+
+ // Execute actions until one 'consumes' the token and we can
+ // return it. Marked is used to
+ while(true){
+ consume = marked = false;
+ // Take and execute the topmost action.
+ cc.pop()(token.type, token.content);
+ if (consume){
+ // Marked is used to change the style of the current token.
+ if (marked)
+ token.style = marked;
+ // Here we differentiate between local and global variables.
+ else if (token.type == "variable" && inScope(token.content))
+ token.style = "js-localvariable";
+ return token;
+ }
+ }
+ }
+
+ // This makes a copy of the parser state. It stores all the
+ // stateful variables in a closure, and returns a function that
+ // will restore them when called with a new input stream. Note
+ // that the cc array has to be copied, because it is contantly
+ // being modified. Lexical objects are not mutated, and context
+ // objects are not mutated in a harmful way, so they can be shared
+ // between runs of the parser.
+ function copy(){
+ var _context = context, _lexical = lexical, _cc = cc.concat([]), _tokenState = tokens.state;
+
+ return function(input){
+ context = _context;
+ lexical = _lexical;
+ cc = _cc.concat([]); // copies the array
+ column = indented = 0;
+ tokens = tokenizeJavaScript(input, _tokenState);
+ return parser;
+ };
+ }
+
+ // Helper function for pushing a number of actions onto the cc
+ // stack in reverse order.
+ function push(fs){
+ for (var i = fs.length - 1; i >= 0; i--)
+ cc.push(fs[i]);
+ }
+ // cont and pass are used by the action functions to add other
+ // actions to the stack. cont will cause the current token to be
+ // consumed, pass will leave it for the next action.
+ function cont(){
+ push(arguments);
+ consume = true;
+ }
+ function pass(){
+ push(arguments);
+ consume = false;
+ }
+ // Used to change the style of the current token.
+ function mark(style){
+ marked = style;
+ }
+
+ // Push a new scope. Will automatically link the the current
+ // scope.
+ function pushcontext(){
+ context = {prev: context, vars: {"this": true, "arguments": true}};
+ }
+ // Pop off the current scope.
+ function popcontext(){
+ context = context.prev;
+ }
+ // Register a variable in the current scope.
+ function register(varname){
+ if (context){
+ mark("js-variabledef");
+ context.vars[varname] = true;
+ }
+ }
+ // Check whether a variable is defined in the current scope.
+ function inScope(varname){
+ var cursor = context;
+ while (cursor) {
+ if (cursor.vars[varname])
+ return true;
+ cursor = cursor.prev;
+ }
+ return false;
+ }
+
+ // Push a new lexical context of the given type.
+ function pushlex(type){
+ var result = function(){
+ lexical = new JSLexical(indented, column, type, null, lexical)
+ };
+ result.lex = true;
+ return result;
+ }
+ // Pop off the current lexical context.
+ function poplex(){
+ lexical = lexical.prev;
+ }
+ poplex.lex = true;
+ // The 'lex' flag on these actions is used by the 'next' function
+ // to know they can (and have to) be ran before moving on to the
+ // next token.
+
+ // Creates an action that discards tokens until it finds one of
+ // the given type.
+ function expect(wanted){
+ return function(type){
+ if (type == wanted) cont();
+ else cont(arguments.callee);
+ };
+ }
+
+ // Looks for a statement, and then calls itself.
+ function statements(type){
+ return pass(statement, statements);
+ }
+ // Dispatches various types of statements based on the type of the
+ // current token.
+ function statement(type){
+ if (type == "var") cont(pushlex("vardef"), vardef1, expect(";"), poplex);
+ else if (type == "keyword a") cont(pushlex("form"), expression, statement, poplex);
+ else if (type == "keyword b") cont(pushlex("form"), statement, poplex);
+ else if (type == "{") cont(pushlex("}"), block, poplex);
+ else if (type == "function") cont(functiondef);
+ else if (type == "for") cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"), poplex, statement, poplex);
+ else if (type == "variable") cont(pushlex("stat"), maybelabel);
+ else if (type == "case") cont(expression, expect(":"));
+ else if (type == "catch") cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"), statement, poplex, popcontext);
+ else pass(pushlex("stat"), expression, expect(";"), poplex);
+ }
+ // Dispatch expression types.
+ function expression(type){
+ if (atomicTypes.hasOwnProperty(type)) cont(maybeoperator);
+ else if (type == "function") cont(functiondef);
+ else if (type == "keyword c") cont(expression);
+ else if (type == "(") cont(pushlex(")"), expression, expect(")"), poplex);
+ else if (type == "operator") cont(expression);
+ else if (type == "[") cont(pushlex("]"), commasep(expression), expect("]"), poplex);
+ else if (type == "{") cont(pushlex("}"), commasep(objprop), expect("}"), poplex);
+ }
+ // Called for places where operators, function calls, or
+ // subscripts are valid. Will skip on to the next action if none
+ // is found.
+ function maybeoperator(type){
+ if (type == "operator") cont(expression);
+ else if (type == "(") cont(pushlex(")"), expression, commasep(expression), expect(")"), poplex);
+ else if (type == ".") cont(property, maybeoperator);
+ else if (type == "[") cont(pushlex("]"), expression, expect("]"), poplex);
+ }
+ // When a statement starts with a variable name, it might be a
+ // label. If no colon follows, it's a regular statement.
+ function maybelabel(type){
+ if (type == ":") cont(poplex, statement);
+ else pass(maybeoperator, expect(";"), poplex);
+ }
+ // Property names need to have their style adjusted -- the
+ // tokenizer think they are variables.
+ function property(type){
+ if (type == "variable") {mark("js-property"); cont();}
+ }
+ // This parses a property and its value in an object literal.
+ function objprop(type){
+ if (type == "variable") mark("js-property");
+ if (atomicTypes.hasOwnProperty(type)) cont(expect(":"), expression);
+ }
+ // Parses a comma-separated list of the things that are recognized
+ // by the 'what' argument.
+ function commasep(what){
+ function proceed(type) {
+ if (type == ",") cont(what, proceed);
+ };
+ return function() {
+ pass(what, proceed);
+ };
+ }
+ // Look for statements until a closing brace is found.
+ function block(type){
+ if (type == "}") cont();
+ else pass(statement, block);
+ }
+ // Variable definitions are split into two actions -- 1 looks for
+ // a name or the end of the definition, 2 looks for an '=' sign or
+ // a comma.
+ function vardef1(type, value){
+ if (type == "variable"){register(value); cont(vardef2);}
+ else cont();
+ }
+ function vardef2(type){
+ if (type == "operator") cont(expression, vardef2);
+ else if (type == ",") cont(vardef1);
+ }
+ // For loops.
+ function forspec1(type, value){
+ if (type == "var") cont(vardef1, forspec2);
+ else cont(expression, forspec2);
+ }
+ function forspec2(type){
+ if (type == ",") cont(forspec1);
+ if (type == ";") cont(expression, expect(";"), expression);
+ }
+ // A function definition creates a new context, and the variables
+ // in its argument list have to be added to this context.
+ function functiondef(type, value){
+ if (type == "variable"){register(value); cont(functiondef);}
+ else if (type == "(") cont(pushcontext, commasep(funarg), expect(")"), statement, popcontext);
+ }
+ function funarg(type, value){
+ if (type == "variable"){register(value); cont();}
+ }
+
+ return parser;
+ }
+
+ return {make: parseJS, electricChars: "{}"};
+})();
diff --git a/o3d/samples/third_party/codemirror/js/parsesparql.js b/o3d/samples/third_party/codemirror/js/parsesparql.js
new file mode 100644
index 0000000..2f2b904
--- /dev/null
+++ b/o3d/samples/third_party/codemirror/js/parsesparql.js
@@ -0,0 +1,162 @@
+Editor.Parser = (function() {
+ function wordRegexp(words) {
+ return new RegExp("^(?:" + words.join("|") + ")$", "i");
+ }
+ var ops = wordRegexp(["str", "lang", "langmatches", "datatype", "bound", "sameterm", "isiri", "isuri",
+ "isblank", "isliteral", "union", "a"]);
+ var keywords = wordRegexp(["base", "prefix", "select", "distinct", "reduced", "construct", "describe",
+ "ask", "from", "named", "where", "order", "limit", "offset", "filter", "optional",
+ "graph", "by", "asc", "desc", ]);
+ var operatorChars = /[*+\-<>=&|]/;
+
+ var tokenizeSparql = (function() {
+ function normal(source, setState) {
+ var ch = source.next();
+ if (ch == "$" || ch == "?") {
+ source.nextWhile(matcher(/[\w\d]/));
+ return "sp-var";
+ }
+ else if (ch == "<" && !source.applies(matcher(/[\s\u00a0=]/))) {
+ source.nextWhile(matcher(/[^\s\u00a0>]/));
+ if (source.equals(">")) source.next();
+ return "sp-uri";
+ }
+ else if (ch == "\"" || ch == "'") {
+ setState(inLiteral(ch));
+ return null;
+ }
+ else if (/[{}\(\),\.;\[\]]/.test(ch)) {
+ return "sp-punc";
+ }
+ else if (ch == "#") {
+ while (!source.endOfLine()) source.next();
+ return "sp-comment";
+ }
+ else if (operatorChars.test(ch)) {
+ source.nextWhile(matcher(operatorChars));
+ return "sp-operator";
+ }
+ else if (ch == ":") {
+ source.nextWhile(matcher(/[\w\d\._\-]/));
+ return "sp-prefixed";
+ }
+ else {
+ source.nextWhile(matcher(/[_\w\d]/));
+ if (source.equals(":")) {
+ source.next();
+ source.nextWhile(matcher(/[\w\d_\-]/));
+ return "sp-prefixed";
+ }
+ var word = source.get(), type;
+ if (ops.test(word))
+ type = "sp-operator";
+ else if (keywords.test(word))
+ type = "sp-keyword";
+ else
+ type = "sp-word";
+ return {style: type, content: word};
+ }
+ }
+
+ function inLiteral(quote) {
+ return function(source, setState) {
+ var escaped = false;
+ while (!source.endOfLine()) {
+ var ch = source.next();
+ if (ch == quote && !escaped) {
+ setState(normal);
+ break;
+ }
+ escaped = ch == "\\";
+ }
+ return "sp-literal";
+ };
+ }
+
+ return function(source, startState) {
+ return tokenizer(source, startState || normal);
+ };
+ })();
+
+ function indentSparql(context) {
+ return function(nextChars) {
+ var firstChar = nextChars && nextChars.charAt(0);
+ if (/[\]\}]/.test(firstChar))
+ while (context && context.type == "pattern") context = context.prev;
+
+ var closing = context && firstChar == matching[context.type];
+ if (!context)
+ return 0;
+ else if (context.type == "pattern")
+ return context.col;
+ else if (context.align)
+ return context.col - (closing ? context.width : 0);
+ else
+ return context.indent + (closing ? 0 : 2);
+ }
+ }
+
+ function parseSparql(source) {
+ var tokens = tokenizeSparql(source);
+ var context = null, indent = 0, col = 0;
+ function pushContext(type, width) {
+ context = {prev: context, indent: indent, col: col, type: type, width: width};
+ }
+ function popContext() {
+ context = context.prev;
+ }
+
+ var iter = {
+ next: function() {
+ var token = tokens.next(), type = token.style, content = token.content, width = token.value.length;
+
+ if (content == "\n") {
+ token.indentation = indentSparql(context);
+ indent = col = 0;
+ if (context && context.align == null) context.align = false;
+ }
+ else if (type == "whitespace" && col == 0) {
+ indent = width;
+ }
+ else if (type != "sp-comment" && context && context.align == null) {
+ context.align = true;
+ }
+
+ if (content != "\n") col += width;
+
+ if (/[\[\{\(]/.test(content)) {
+ pushContext(content, width);
+ }
+ else if (/[\]\}\)]/.test(content)) {
+ while (context && context.type == "pattern")
+ popContext();
+ if (context && content == matching[context.type])
+ popContext();
+ }
+ else if (content == "." && context && context.type == "pattern") {
+ popContext();
+ }
+ else if ((type == "sp-word" || type == "sp-prefixed" || type == "sp-uri" || type == "sp-var" || type == "sp-literal") &&
+ context && /[\{\[]/.test(context.type)) {
+ pushContext("pattern", width);
+ }
+
+ return token;
+ },
+
+ copy: function() {
+ var _context = context, _indent = indent, _col = col, _tokenState = tokens.state;
+ return function(source) {
+ tokens = tokenizeSparql(source, _tokenState);
+ context = _context;
+ indent = _indent;
+ col = _col;
+ return iter;
+ };
+ }
+ };
+ return iter;
+ }
+
+ return {make: parseSparql, electricChars: "}]"};
+})();
diff --git a/o3d/samples/third_party/codemirror/js/parsexml.js b/o3d/samples/third_party/codemirror/js/parsexml.js
new file mode 100644
index 0000000..73acd16
--- /dev/null
+++ b/o3d/samples/third_party/codemirror/js/parsexml.js
@@ -0,0 +1,292 @@
+/* This file defines an XML parser, with a few kludges to make it
+ * useable for HTML. autoSelfClosers defines a set of tag names that
+ * are expected to not have a closing tag, and doNotIndent specifies
+ * the tags inside of which no indentation should happen (see Config
+ * object). These can be disabled by passing the editor an object like
+ * {useHTMLKludges: false} as parserConfig option.
+ */
+
+var XMLParser = Editor.Parser = (function() {
+ var Kludges = {
+ autoSelfClosers: {"br": true, "img": true, "hr": true, "link": true, "input": true,
+ "meta": true, "col": true, "frame": true, "base": true, "area": true},
+ doNotIndent: {"pre": true}
+ };
+ var NoKludges = {autoSelfClosers: {}, doNotIndent: {}};
+ var UseKludges = Kludges;
+
+ // Simple stateful tokenizer for XML documents. Returns a
+ // MochiKit-style iterator, with a state property that contains a
+ // function encapsulating the current state. See tokenize.js.
+ var tokenizeXML = (function() {
+ function inText(source, setState) {
+ var ch = source.next();
+ if (ch == "<") {
+ if (source.equals("!")) {
+ source.next();
+ if (source.equals("[")) {
+ if (source.matches("[CDATA[", true)) {
+ setState(inBlock("xml-cdata", "]]>"));
+ return null;
+ }
+ else {
+ return "xml-text";
+ }
+ }
+ else if (source.matches("--", true)) {
+ setState(inBlock("xml-comment", "-->"));
+ return null;
+ }
+ else {
+ return "xml-text";
+ }
+ }
+ else if (source.equals("?")) {
+ source.next();
+ setState(inBlock("xml-processing", "?>"));
+ return null;
+ }
+ else {
+ if (source.equals("/")) source.next();
+ setState(inTag);
+ return "xml-punctuation";
+ }
+ }
+ else if (ch == "&") {
+ while (!source.endOfLine()) {
+ if (source.next() == ";")
+ break;
+ }
+ return "xml-entity";
+ }
+ else {
+ source.nextWhile(matcher(/[^&<\n]/));
+ return "xml-text";
+ }
+ }
+
+ function inTag(source, setState) {
+ var ch = source.next();
+ if (ch == ">") {
+ setState(inText);
+ return "xml-punctuation";
+ }
+ else if (/[?\/]/.test(ch) && source.equals(">")) {
+ source.next();
+ setState(inText);
+ return "xml-punctuation";
+ }
+ else if (ch == "=") {
+ return "xml-punctuation";
+ }
+ else if (/[\'\"]/.test(ch)) {
+ setState(inAttribute(ch));
+ return null;
+ }
+ else {
+ source.nextWhile(matcher(/[^\s\u00a0=<>\"\'\/?]/));
+ return "xml-name";
+ }
+ }
+
+ function inAttribute(quote) {
+ return function(source, setState) {
+ while (!source.endOfLine()) {
+ if (source.next() == quote) {
+ setState(inTag);
+ break;
+ }
+ }
+ return "xml-attribute";
+ };
+ }
+
+ function inBlock(style, terminator) {
+ return function(source, setState) {
+ var matches = [];
+ while (!source.endOfLine()) {
+ var ch = source.next(), newMatches = [];
+ if (ch == terminator.charAt(0))
+ matches.push(terminator);
+ for (var i = 0; i < matches.length; i++) {
+ var match = matches[i];
+ if (match.charAt(0) == ch) {
+ if (match.length == 1) {setState(inText); break;}
+ else newMatches.push(match.slice(1));
+ }
+ }
+ matches = newMatches;
+ }
+ return style;
+ };
+ }
+
+ return function(source, startState) {
+ return tokenizer(source, startState || inText);
+ };
+ })();
+
+ // The parser. The structure of this function largely follows that of
+ // parseJavaScript in parsejavascript.js (there is actually a bit more
+ // shared code than I'd like), but it is quite a bit simpler.
+ function parseXML(source) {
+ var tokens = tokenizeXML(source);
+ var cc = [base];
+ var tokenNr = 0, indented = 0;
+ var currentTag = null, context = null;
+ var consume, marked;
+
+ function push(fs) {
+ for (var i = fs.length - 1; i >= 0; i--)
+ cc.push(fs[i]);
+ }
+ function cont() {
+ push(arguments);
+ consume = true;
+ }
+ function pass() {
+ push(arguments);
+ consume = false;
+ }
+
+ function mark(style) {
+ marked = style;
+ }
+ function expect(text) {
+ return function(style, content) {
+ if (content == text) cont();
+ else mark("xml-error") || cont(arguments.callee);
+ };
+ }
+
+ function pushContext(tagname, startOfLine) {
+ var noIndent = UseKludges.doNotIndent.hasOwnProperty(tagname) || (context && context.noIndent);
+ context = {prev: context, name: tagname, indent: indented, startOfLine: startOfLine, noIndent: noIndent};
+ }
+ function popContext() {
+ context = context.prev;
+ }
+ function computeIndentation(baseContext) {
+ return function(nextChars) {
+ var context = baseContext;
+ if (context && context.noIndent)
+ return 0;
+ if (context && /^<\//.test(nextChars))
+ context = context.prev;
+ while (context && !context.startOfLine)
+ context = context.prev;
+ if (context)
+ return context.indent + 2;
+ else
+ return 0;
+ };
+ }
+
+ function base() {
+ return pass(element, base);
+ }
+ var harmlessTokens = {"xml-text": true, "xml-entity": true, "xml-comment": true,
+ "xml-cdata": true, "xml-processing": true};
+ function element(style, content) {
+ if (content == "<") cont(tagname, attributes, endtag(tokenNr == 1));
+ else if (content == "</") cont(closetagname, expect(">"));
+ else if (content == "<?") cont(tagname, attributes, expect("?>"));
+ else if (harmlessTokens.hasOwnProperty(style)) cont();
+ else mark("xml-error") || cont();
+ }
+ function tagname(style, content) {
+ if (style == "xml-name") {
+ currentTag = content.toLowerCase();
+ mark("xml-tagname");
+ cont();
+ }
+ else {
+ currentTag = null;
+ pass();
+ }
+ }
+ function closetagname(style, content) {
+ if (style == "xml-name" && context && content.toLowerCase() == context.name) {
+ popContext();
+ mark("xml-tagname");
+ }
+ else {
+ mark("xml-error");
+ }
+ cont();
+ }
+ function endtag(startOfLine) {
+ return function(style, content) {
+ if (content == "/>" || (content == ">" && UseKludges.autoSelfClosers.hasOwnProperty(currentTag))) cont();
+ else if (content == ">") pushContext(currentTag, startOfLine) || cont();
+ else mark("xml-error") || cont(arguments.callee);
+ };
+ }
+ function attributes(style) {
+ if (style == "xml-name") mark("xml-attname") || cont(attribute, attributes);
+ else pass();
+ }
+ function attribute(style, content) {
+ if (content == "=") cont(value);
+ else if (content == ">" || content == "/>") pass(endtag);
+ else pass();
+ }
+ function value(style) {
+ if (style == "xml-attribute") cont(value);
+ else pass();
+ }
+
+ return {
+ indentation: function() {return indented;},
+
+ next: function(){
+ var token = tokens.next();
+ if (token.style == "whitespace" && tokenNr == 0)
+ indented = token.value.length;
+ else
+ tokenNr++;
+ if (token.content == "\n") {
+ indented = tokenNr = 0;
+ token.indentation = computeIndentation(context);
+ }
+
+ if (token.style == "whitespace" || token.type == "xml-comment")
+ return token;
+
+ while(true){
+ consume = marked = false;
+ cc.pop()(token.style, token.content);
+ if (consume){
+ if (marked)
+ token.style = marked;
+ return token;
+ }
+ }
+ },
+
+ copy: function(){
+ var _cc = cc.concat([]), _tokenState = tokens.state, _context = context;
+ var parser = this;
+
+ return function(input){
+ cc = _cc.concat([]);
+ tokenNr = indented = 0;
+ context = _context;
+ tokens = tokenizeXML(input, _tokenState);
+ return parser;
+ };
+ }
+ };
+ }
+
+ return {
+ make: parseXML,
+ electricChars: "/",
+ configure: function(config) {
+ if (config.useHTMLKludges)
+ UseKludges = Kludges;
+ else
+ UseKludges = NoKludges;
+ }
+ };
+})();
diff --git a/o3d/samples/third_party/codemirror/js/select.js b/o3d/samples/third_party/codemirror/js/select.js
new file mode 100644
index 0000000..383a7f1
--- /dev/null
+++ b/o3d/samples/third_party/codemirror/js/select.js
@@ -0,0 +1,500 @@
+/* Functionality for finding, storing, and restoring selections
+ *
+ * This does not provide a generic API, just the minimal functionality
+ * required by the CodeMirror system.
+ */
+
+// Namespace object.
+var select = {};
+
+(function() {
+ var ie_selection = document.selection && document.selection.createRangeCollection;
+
+ // Find the 'top-level' (defined as 'a direct child of the node
+ // passed as the top argument') node that the given node is
+ // contained in. Return null if the given node is not inside the top
+ // node.
+ function topLevelNodeAt(node, top) {
+ while (node && node.parentNode != top)
+ node = node.parentNode;
+ return node;
+ }
+
+ // Find the top-level node that contains the node before this one.
+ function topLevelNodeBefore(node, top) {
+ while (!node.previousSibling && node.parentNode != top)
+ node = node.parentNode;
+ return topLevelNodeAt(node.previousSibling, top);
+ }
+
+ // Used to prevent restoring a selection when we do not need to.
+ var documentChanged = false;
+
+ var fourSpaces = "\u00a0\u00a0\u00a0\u00a0";
+
+ // Most functions are defined in two ways, one for the IE selection
+ // model, one for the W3C one.
+ if (ie_selection) {
+ // Store the current selection in such a way that it can be
+ // restored after we manipulated the DOM tree. For IE, we store
+ // pixel coordinates.
+ select.markSelection = function (win) {
+ var selection = win.document.selection;
+ var start = selection.createRange(), end = start.duplicate();
+ var bookmark = start.getBookmark();
+ start.collapse(true);
+ end.collapse(false);
+
+ documentChanged = false;
+ var body = win.document.body;
+ // And we better hope no fool gave this window a padding or a
+ // margin, or all these computations will be in vain.
+ return {start: {x: start.boundingLeft + body.scrollLeft - 1,
+ y: start.boundingTop + body.scrollTop},
+ end: {x: end.boundingLeft + body.scrollLeft - 1,
+ y: end.boundingTop + body.scrollTop},
+ window: win,
+ bookmark: bookmark};
+ };
+
+ // Restore a stored selection.
+ select.selectMarked = function(sel) {
+ if (!sel || !documentChanged)
+ return;
+
+ documentChanged = false;
+ var range1 = sel.window.document.body.createTextRange(), range2 = range1.duplicate();
+ var done = false;
+ if (sel.start.y >= 0 && sel.end.y < sel.window.document.body.clientHeight) {
+ // This can fail for various hard-to-handle reasons, so we
+ // fall back to moveToBookmark when it throws.
+ try {
+ range1.moveToPoint(sel.start.x, sel.start.y);
+ range2.moveToPoint(sel.end.x, sel.end.y);
+ range1.setEndPoint("EndToStart", range2);
+ done = true;
+ } catch(e) {}
+ }
+ if (!done) done = range1.moveToBookmark(sel.bookmark);
+ if (done) range1.select();
+ };
+
+
+ // See W3C model for the actual role of this function. Here it
+ // just sets a flag indicating the selection should be restored.
+ select.replaceSelection = function(){
+ documentChanged = true;
+ };
+
+ // Get the top-level node that one end of the cursor is inside or
+ // after. Note that this returns false for 'no cursor', and null
+ // for 'start of document'.
+ select.selectionTopNode = function(container, start) {
+ var selection = container.ownerDocument.selection;
+ if (!selection) return false;
+
+ var range = selection.createRange();
+ range.collapse(start);
+ var around = range.parentElement();
+ if (around && isAncestor(container, around)) {
+ // Only use this node if the selection is not at its start.
+ var range2 = range.duplicate();
+ range2.moveToElementText(around);
+ if (range.compareEndPoints("StartToStart", range2) == -1)
+ return topLevelNodeAt(around, container);
+ }
+ // Fall-back hack
+ range.pasteHTML("<span id='xxx-temp-xxx'></span>");
+ var temp = container.ownerDocument.getElementById("xxx-temp-xxx");
+ if (temp) {
+ var result = topLevelNodeBefore(temp, container);
+ removeElement(temp);
+ return result;
+ }
+ return false;
+ };
+
+ // Place the cursor after this.start. This is only useful when
+ // manually moving the cursor instead of restoring it to its old
+ // position.
+ select.focusAfterNode = function(node, container) {
+ var range = container.ownerDocument.body.createTextRange();
+ range.moveToElementText(node || container);
+ range.collapse(!node);
+ range.select();
+ };
+
+ function insertAtCursor(window, html) {
+ var selection = window.document.selection;
+ if (selection) {
+ var range = selection.createRange();
+ range.pasteHTML(html);
+ range.collapse(false);
+ range.select();
+ }
+ }
+
+ // Used to normalize the effect of the enter key, since browsers
+ // do widely different things when pressing enter in designMode.
+ select.insertNewlineAtCursor = function(window) {
+ insertAtCursor(window, "<br/>");
+ };
+
+ select.insertTabAtCursor = function(window) {
+ insertAtCursor(window, fourSpaces);
+ };
+
+ // Get the BR node at the start of the line on which the cursor
+ // currently is, and the offset into the line. Returns null as
+ // node if cursor is on first line.
+ select.cursorPos = function(container, start) {
+ var selection = container.ownerDocument.selection;
+ if (!selection) return null;
+
+ var topNode = select.selectionTopNode(container, start);
+ while (topNode && topNode.nodeName != "BR")
+ topNode = topNode.previousSibling;
+
+ var range = selection.createRange(), range2 = range.duplicate();
+ range.collapse(start);
+ if (topNode) {
+ range2.moveToElementText(topNode);
+ range2.collapse(false);
+ }
+ else {
+ // When nothing is selected, we can get all kinds of funky errors here.
+ try { range2.moveToElementText(container); }
+ catch (e) { return null; }
+ range2.collapse(true);
+ }
+ range.setEndPoint("StartToStart", range2);
+
+ return {node: topNode, offset: range.text.length};
+ };
+
+ select.setCursorPos = function(container, from, to) {
+ function rangeAt(pos) {
+ var range = container.ownerDocument.body.createTextRange();
+ if (!pos.node) {
+ range.moveToElementText(container);
+ range.collapse(true);
+ }
+ else {
+ range.moveToElementText(pos.node);
+ range.collapse(false);
+ }
+ range.move("character", pos.offset);
+ return range;
+ }
+
+ var range = rangeAt(from);
+ if (to && to != from)
+ range.setEndPoint("EndToEnd", rangeAt(to));
+ range.select();
+ }
+
+ // Make sure the cursor is visible.
+ select.scrollToCursor = function(container) {
+ var selection = container.ownerDocument.selection;
+ if (!selection) return null;
+ selection.createRange().scrollIntoView();
+ };
+ }
+ // W3C model
+ else {
+ // This is used to fix an issue with getting the scroll position
+ // in Opera.
+ var opera_scroll = !window.scrollX && !window.scrollY;
+
+ // Store start and end nodes, and offsets within these, and refer
+ // back to the selection object from those nodes, so that this
+ // object can be updated when the nodes are replaced before the
+ // selection is restored.
+ select.markSelection = function (win) {
+ documentChanged = false;
+ var selection = win.getSelection();
+ if (!selection || selection.rangeCount == 0)
+ return null;
+ var range = selection.getRangeAt(0);
+
+ var result = {start: {node: range.startContainer, offset: range.startOffset},
+ end: {node: range.endContainer, offset: range.endOffset},
+ window: win,
+ scrollX: opera_scroll && win.document.body.scrollLeft,
+ scrollY: opera_scroll && win.document.body.scrollTop};
+
+ // We want the nodes right at the cursor, not one of their
+ // ancestors with a suitable offset. This goes down the DOM tree
+ // until a 'leaf' is reached (or is it *up* the DOM tree?).
+ function normalize(point){
+ while (point.node.nodeType != 3 && point.node.nodeName != "BR") {
+ var newNode = point.node.childNodes[point.offset] || point.node.nextSibling;
+ point.offset = 0;
+ while (!newNode && point.node.parentNode) {
+ point.node = point.node.parentNode;
+ newNode = point.node.nextSibling;
+ }
+ point.node = newNode;
+ if (!newNode)
+ break;
+ }
+ }
+
+ normalize(result.start);
+ normalize(result.end);
+ // Make the links back to the selection object (see
+ // replaceSelection).
+ if (result.start.node)
+ result.start.node.selectStart = result.start;
+ if (result.end.node)
+ result.end.node.selectEnd = result.end;
+
+ return result;
+ };
+
+ select.selectMarked = function (sel) {
+ if (!sel || !documentChanged)
+ return;
+ var win = sel.window;
+ var range = win.document.createRange();
+
+ function setPoint(point, which) {
+ if (point.node) {
+ // Remove the link back to the selection.
+ delete point.node["select" + which];
+ // Some magic to generalize the setting of the start and end
+ // of a range.
+ if (point.offset == 0)
+ range["set" + which + "Before"](point.node);
+ else
+ range["set" + which](point.node, point.offset);
+ }
+ else {
+ range.setStartAfter(win.document.body.lastChild || win.document.body);
+ }
+ }
+
+ // Have to restore the scroll position of the frame in Opera.
+ if (opera_scroll){
+ sel.window.document.body.scrollLeft = sel.scrollX;
+ sel.window.document.body.scrollTop = sel.scrollY;
+ }
+ setPoint(sel.end, "End");
+ setPoint(sel.start, "Start");
+ selectRange(range, win);
+ };
+
+ // This is called by the code in codemirror.js whenever it is
+ // replacing a part of the DOM tree. The function sees whether the
+ // given oldNode is part of the current selection, and updates
+ // this selection if it is. Because nodes are often only partially
+ // replaced, the length of the part that gets replaced has to be
+ // taken into account -- the selection might stay in the oldNode
+ // if the newNode is smaller than the selection's offset. The
+ // offset argument is needed in case the selection does move to
+ // the new object, and the given length is not the whole length of
+ // the new node (part of it might have been used to replace
+ // another node).
+ select.replaceSelection = function(oldNode, newNode, length, offset) {
+ documentChanged = true;
+ function replace(which) {
+ var selObj = oldNode["select" + which];
+ if (selObj) {
+ if (selObj.offset > length) {
+ selObj.offset -= length;
+ }
+ else {
+ newNode["select" + which] = selObj;
+ delete oldNode["select" + which];
+ selObj.node = newNode;
+ selObj.offset += (offset || 0);
+ }
+ }
+ }
+ replace("Start");
+ replace("End");
+ };
+
+ // Helper for selecting a range object.
+ function selectRange(range, window) {
+ var selection = window.getSelection();
+ selection.removeAllRanges();
+ selection.addRange(range);
+ };
+ function selectionRange(window) {
+ var selection = window.getSelection();
+ if (!selection || selection.rangeCount == 0)
+ return false;
+ else
+ return selection.getRangeAt(0);
+ }
+
+ // Finding the top-level node at the cursor in the W3C is, as you
+ // can see, quite an involved process.
+ select.selectionTopNode = function(container, start) {
+ var range = selectionRange(container.ownerDocument.defaultView);
+ if (!range) return false;
+
+ var node = start ? range.startContainer : range.endContainer;
+ var offset = start ? range.startOffset : range.endOffset;
+ // Work around (yet another) bug in Opera's selection model.
+ if (window.opera && !start && range.endContainer == container && range.endOffset == range.startOffset + 1 &&
+ container.childNodes[range.startOffset] && container.childNodes[range.startOffset].nodeName == "BR")
+ offset--;
+
+ // For text nodes, we look at the node itself if the cursor is
+ // inside, or at the node before it if the cursor is at the
+ // start.
+ if (node.nodeType == 3){
+ if (offset > 0)
+ return topLevelNodeAt(node, container);
+ else
+ return topLevelNodeBefore(node, container);
+ }
+ // Occasionally, browsers will return the HTML node as
+ // selection. If the offset is 0, we take the start of the frame
+ // ('after null'), otherwise, we take the last node.
+ else if (node.nodeName == "HTML") {
+ return (offset == 1 ? null : container.lastChild);
+ }
+ // If the given node is our 'container', we just look up the
+ // correct node by using the offset.
+ else if (node == container) {
+ return (offset == 0) ? null : node.childNodes[offset - 1];
+ }
+ // In any other case, we have a regular node. If the cursor is
+ // at the end of the node, we use the node itself, if it is at
+ // the start, we use the node before it, and in any other
+ // case, we look up the child before the cursor and use that.
+ else {
+ if (offset == node.childNodes.length)
+ return topLevelNodeAt(node, container);
+ else if (offset == 0)
+ return topLevelNodeBefore(node, container);
+ else
+ return topLevelNodeAt(node.childNodes[offset - 1], container);
+ }
+ };
+
+ select.focusAfterNode = function(node, container) {
+ var win = container.ownerDocument.defaultView,
+ range = win.document.createRange();
+ range.setStartBefore(container.firstChild || container);
+ // In Opera, setting the end of a range at the end of a line
+ // (before a BR) will cause the cursor to appear on the next
+ // line, so we set the end inside of the start node when
+ // possible.
+ if (node && !node.firstChild)
+ range.setEndAfter(node);
+ else if (node)
+ range.setEnd(node, node.childNodes.length);
+ else
+ range.setEndBefore(container.firstChild || container);
+ range.collapse(false);
+ selectRange(range, win);
+ };
+
+ insertNodeAtCursor = function(window, node) {
+ var range = selectionRange(window);
+ if (!range) return;
+
+ range.deleteContents();
+ range.insertNode(node);
+ range.setEndAfter(node);
+ range.collapse(false);
+ selectRange(range, window);
+ return node;
+ }
+
+ select.insertNewlineAtCursor = function(window) {
+ insertNodeAtCursor(window, window.document.createElement("BR"));
+ };
+
+ select.insertTabAtCursor = function(window) {
+ insertNodeAtCursor(window, window.document.createTextNode(fourSpaces));
+ };
+
+ select.cursorPos = function(container, start) {
+ var range = selectionRange(window);
+ if (!range) return;
+
+ var topNode = select.selectionTopNode(container, start);
+ while (topNode && topNode.nodeName != "BR")
+ topNode = topNode.previousSibling;
+
+ range = range.cloneRange();
+ range.collapse(start);
+ if (topNode)
+ range.setStartAfter(topNode);
+ else
+ range.setStartBefore(container);
+ return {node: topNode, offset: range.toString().length};
+ };
+
+ select.setCursorPos = function(container, from, to) {
+ var win = container.ownerDocument.defaultView,
+ range = win.document.createRange();
+
+ function setPoint(node, offset, side) {
+ if (!node)
+ node = container.firstChild;
+ else
+ node = node.nextSibling;
+
+ if (!node)
+ return;
+
+ if (offset == 0) {
+ range["set" + side + "Before"](node);
+ return true;
+ }
+
+ var backlog = []
+ function decompose(node) {
+ if (node.nodeType == 3)
+ backlog.push(node);
+ else
+ forEach(node.childNodes, decompose);
+ }
+ while (true) {
+ while (node && !backlog.length) {
+ decompose(node);
+ node = node.nextSibling;
+ }
+ var cur = backlog.shift();
+ if (!cur) return false;
+
+ var length = cur.nodeValue.length;
+ if (length >= offset) {
+ range["set" + side](cur, offset);
+ return true;
+ }
+ offset -= length;
+ }
+ }
+
+ to = to || from;
+ if (setPoint(to.node, to.offset, "End") && setPoint(from.node, from.offset, "Start"))
+ selectRange(range, win);
+ };
+
+ select.scrollToCursor = function(container) {
+ var body = container.ownerDocument.body, win = container.ownerDocument.defaultView;
+ var element = select.selectionTopNode(container, true) || container.firstChild;
+
+ // In Opera, BR elements *always* have a scrollTop property of zero. Go Opera.
+ while (element && !element.offsetTop)
+ element = element.previousSibling;
+
+ var y = 0, pos = element;
+ while (pos && pos.offsetParent) {
+ y += pos.offsetTop;
+ pos = pos.offsetParent;
+ }
+
+ var screen_y = y - body.scrollTop;
+ if (screen_y < 0 || screen_y > win.innerHeight - 10)
+ win.scrollTo(0, y);
+ };
+ }
+}());
diff --git a/o3d/samples/third_party/codemirror/js/stringstream.js b/o3d/samples/third_party/codemirror/js/stringstream.js
new file mode 100644
index 0000000..17ff8a5
--- /dev/null
+++ b/o3d/samples/third_party/codemirror/js/stringstream.js
@@ -0,0 +1,111 @@
+/* String streams are the things fed to parsers (which can feed them
+ * to a tokenizer if they want). They provide peek and next methods
+ * for looking at the current character (next 'consumes' this
+ * character, peek does not), and a get method for retrieving all the
+ * text that was consumed since the last time get was called.
+ *
+ * An easy mistake to make is to let a StopIteration exception finish
+ * the token stream while there are still characters pending in the
+ * string stream (hitting the end of the buffer while parsing a
+ * token). To make it easier to detect such errors, the strings throw
+ * an exception when this happens.
+ */
+
+(function(){
+ // Generic operations that apply to stringstreams.
+ var base = {
+ more: function() {
+ return this.peek() !== null;
+ },
+ applies: function(test) {
+ var next = this.peek();
+ return (next !== null && test(next));
+ },
+ nextWhile: function(test) {
+ while (this.applies(test))
+ this.next();
+ },
+ equals: function(ch) {
+ return ch === this.peek();
+ },
+ endOfLine: function() {
+ var next = this.peek();
+ return next == null || next == "\n";
+ },
+ matches: function(string, caseSensitive) {
+ for (var i = 0; i < string.length; i++) {
+ var ch = this.peek();
+ if (!ch || string.charAt(i) != (caseSensitive ? ch : ch.toLowerCase()))
+ return false;
+ this.next();
+ }
+ return true;
+ }
+ };
+
+ // Make a string stream out of an iterator that returns strings. This
+ // is applied to the result of traverseDOM (see codemirror.js), and
+ // the resulting stream is fed to the parser.
+ window.stringStream = function(source){
+ source = iter(source);
+ var current = "", pos = 0;
+ var peeked = null, accum = "";
+
+ return update({
+ peek: function() {
+ if (!peeked) {
+ try {peeked = this.step();}
+ catch (e) {
+ if (e != StopIteration) throw e;
+ else peeked = null;
+ }
+ }
+ return peeked;
+ },
+ step: function() {
+ if (peeked) {
+ var temp = peeked;
+ peeked = null;
+ return temp;
+ }
+ while (pos == current.length) {
+ accum += current;
+ current = ""; // In case source.next() throws
+ pos = 0;
+ current = source.next();
+ }
+ return current.charAt(pos++);
+
+ },
+ next: function() {
+ try {return this.step();}
+ catch (e) {
+ if (e == StopIteration && accum.length > 0)
+ throw "End of stringstream reached without emptying buffer ('" + accum + "').";
+ else
+ throw e;
+ }
+ },
+ get: function() {
+ var temp = accum;
+ var realPos = peeked ? pos - 1 : pos;
+ accum = "";
+ if (realPos > 0){
+ temp += current.slice(0, realPos);
+ current = current.slice(realPos);
+ pos = peeked ? 1 : 0;
+ }
+ return temp;
+ },
+ reset: function() {
+ current = accum + current;
+ accum = "";
+ pos = 0;
+ peeked = null;
+ },
+ push: function(str) {
+ current = current.slice(0, pos) + str + current.slice(pos);
+ }
+ }, base);
+ };
+})();
diff --git a/o3d/samples/third_party/codemirror/js/tokenize.js b/o3d/samples/third_party/codemirror/js/tokenize.js
new file mode 100644
index 0000000..379a12e
--- /dev/null
+++ b/o3d/samples/third_party/codemirror/js/tokenize.js
@@ -0,0 +1,57 @@
+// A frame for simple tokenizers. Takes care of newlines and
+// white-space, and of getting the text from the source stream into
+// the token object. A state is a function of two arguments -- a
+// string stream and a setState function. The second can be used to
+// change the tokenizer's state, and can be ignored for stateless
+// tokenizers. This function should advance the stream over a token
+// and return a string or object containing information about the next
+// token, or null to pass and have the (new) state be called to finish
+// the token. When a string is given, it is wrapped in a {style, type}
+// object. In the resulting object, the characters consumed are stored
+// under the content property. Any whitespace following them is also
+// automatically consumed, and added to the value property. (Thus,
+// content is the actual meaningful part of the token, while value
+// contains all the text it spans.)
+
+function tokenizer(source, state) {
+ // Newlines are always a separate token.
+ function isWhiteSpace(ch) {
+ // The messy regexp is because IE's regexp matcher is of the
+ // opinion that non-breaking spaces are no whitespace.
+ return ch != "\n" && /^[\s\u00a0]*$/.test(ch);
+ }
+
+ var tokenizer = {
+ state: state,
+
+ take: function(type) {
+ if (typeof(type) == "string")
+ type = {style: type, type: type};
+
+ type.content = (type.content || "") + source.get();
+ if (!/\n$/.test(type.content))
+ source.nextWhile(isWhiteSpace);
+ type.value = type.content + source.get();
+ return type;
+ },
+
+ next: function () {
+ if (!source.more()) throw StopIteration;
+
+ var type;
+ if (source.equals("\n")) {
+ source.next();
+ return this.take("whitespace");
+ }
+
+ if (source.applies(isWhiteSpace))
+ type = "whitespace";
+ else
+ while (!type)
+ type = this.state(source, function(s) {tokenizer.state = s;});
+
+ return this.take(type);
+ }
+ };
+ return tokenizer;
+}
diff --git a/o3d/samples/third_party/codemirror/js/tokenizejavascript.js b/o3d/samples/third_party/codemirror/js/tokenizejavascript.js
new file mode 100644
index 0000000..24d0529
--- /dev/null
+++ b/o3d/samples/third_party/codemirror/js/tokenizejavascript.js
@@ -0,0 +1,168 @@
+/* Tokenizer for JavaScript code */
+
+var tokenizeJavaScript = (function() {
+ // Advance the stream until the given character (not preceded by a
+ // backslash) is encountered, or the end of the line is reached.
+ function nextUntilUnescaped(source, end, result){
+ var escaped = false;
+ var next;
+ while(!source.endOfLine()){
+ var next = source.next();
+ if (next == end && !escaped)
+ break;
+ escaped = next == "\\";
+ }
+ return result;
+ }
+
+ // A map of JavaScript's keywords. The a/b/c keyword distinction is
+ // very rough, but it gives the parser enough information to parse
+ // correct code correctly (we don't care that much how we parse
+ // incorrect code). The style information included in these objects
+ // is used by the highlighter to pick the correct CSS style for a
+ // token.
+ var keywords = function(){
+ function result(type, style){
+ return {type: type, style: style};
+ }
+ // keywords that take a parenthised expression, and then a
+ // statement (if)
+ var keywordA = result("keyword a", "js-keyword");
+ // keywords that take just a statement (else)
+ var keywordB = result("keyword b", "js-keyword");
+ // keywords that optionally take an expression, and form a
+ // statement (return)
+ var keywordC = result("keyword c", "js-keyword");
+ var operator = result("operator", "js-keyword");
+ var atom = result("atom", "js-atom");
+ return {
+ "if": keywordA, "switch": keywordA, "while": keywordA, "with": keywordA,
+ "else": keywordB, "do": keywordB, "try": keywordB, "finally": keywordB,
+ "return": keywordC, "break": keywordC, "continue": keywordC, "new": keywordC, "delete": keywordC, "throw": keywordC,
+ "in": operator, "typeof": operator, "instanceof": operator,
+ "var": result("var", "js-keyword"), "function": result("function", "js-keyword"), "catch": result("catch", "js-keyword"),
+ "for": result("for", "js-keyword"), "case": result("case", "js-keyword"),
+ "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom
+ };
+ }();
+
+ // Some helper regexp matchers.
+ var isOperatorChar = matcher(/[\+\-\*\&\%\/=<>!\?]/);
+ var isDigit = matcher(/[0-9]/);
+ var isHexDigit = matcher(/[0-9A-Fa-f]/);
+ var isWordChar = matcher(/[\w\$_]/);
+
+ // Wrapper around jsToken that helps maintain parser state (whether
+ // we are inside of a multi-line comment and whether the next token
+ // could be a regular expression).
+ function jsTokenState(inComment, regexp) {
+ return function(source, setState) {
+ var newInComment = inComment;
+ var type = jsToken(inComment, regexp, source, function(c) {newInComment = c;});
+ var newRegexp = type.type == "operator" || type.type == "keyword c" || type.type.match(/^[\[{}\(,;:]$/);
+ if (newRegexp != regexp || newInComment != inComment)
+ setState(jsTokenState(newInComment, newRegexp));
+ return type;
+ };
+ }
+
+ // The token reader, inteded to be used by the tokenizer from
+ // tokenize.js (through jsTokenState). Advances the source stream
+ // over a token, and returns an object containing the type and style
+ // of that token.
+ function jsToken(inComment, regexp, source, setComment) {
+ function readHexNumber(){
+ source.next(); // skip the 'x'
+ source.nextWhile(isHexDigit);
+ return {type: "number", style: "js-atom"};
+ }
+
+ function readNumber() {
+ source.nextWhile(isDigit);
+ if (source.equals(".")){
+ source.next();
+ source.nextWhile(isDigit);
+ }
+ if (source.equals("e") || source.equals("E")){
+ source.next();
+ if (source.equals("-"))
+ source.next();
+ source.nextWhile(isDigit);
+ }
+ return {type: "number", style: "js-atom"};
+ }
+ // Read a word, look it up in keywords. If not found, it is a
+ // variable, otherwise it is a keyword of the type found.
+ function readWord() {
+ source.nextWhile(isWordChar);
+ var word = source.get();
+ var known = keywords.hasOwnProperty(word) && keywords.propertyIsEnumerable(word) && keywords[word];
+ return known ? {type: known.type, style: known.style, content: word} :
+ {type: "variable", style: "js-variable", content: word};
+ }
+ function readRegexp() {
+ nextUntilUnescaped(source, "/");
+ source.nextWhile(matcher(/[gi]/));
+ return {type: "regexp", style: "js-string"};
+ }
+ // Mutli-line comments are tricky. We want to return the newlines
+ // embedded in them as regular newline tokens, and then continue
+ // returning a comment token for every line of the comment. So
+ // some state has to be saved (inComment) to indicate whether we
+ // are inside a /* */ sequence.
+ function readMultilineComment(start){
+ var newInComment = true;
+ var maybeEnd = (start == "*");
+ while (true) {
+ if (source.endOfLine())
+ break;
+ var next = source.next();
+ if (next == "/" && maybeEnd){
+ newInComment = false;
+ break;
+ }
+ maybeEnd = (next == "*");
+ }
+ setComment(newInComment);
+ return {type: "comment", style: "js-comment"};
+ }
+ function readOperator() {
+ source.nextWhile(isOperatorChar);
+ return {type: "operator", style: "js-operator"};
+ }
+
+ // Fetch the next token. Dispatches on first character in the
+ // stream, or first two characters when the first is a slash.
+ var ch = source.next();
+ if (inComment)
+ return readMultilineComment(ch);
+ else if (ch == "\"" || ch == "'")
+ return nextUntilUnescaped(source, ch, {type: "string", style: "js-string"});
+ // with punctuation, the type of the token is the symbol itself
+ else if (/[\[\]{}\(\),;\:\.]/.test(ch))
+ return {type: ch, style: "js-punctuation"};
+ else if (ch == "0" && (source.equals("x") || source.equals("X")))
+ return readHexNumber();
+ else if (isDigit(ch))
+ return readNumber();
+ else if (ch == "/"){
+ if (source.equals("*"))
+ { source.next(); return readMultilineComment(ch); }
+ else if (source.equals("/"))
+ return nextUntilUnescaped(source, null, {type: "comment", style: "js-comment"});
+ else if (regexp)
+ return readRegexp();
+ else
+ return readOperator();
+ }
+ else if (isOperatorChar(ch))
+ return readOperator();
+ else
+ return readWord();
+ }
+
+ // The external interface to the tokenizer.
+ return function(source, startState) {
+ return tokenizer(source, startState || jsTokenState(false, true));
+ };
+})();
diff --git a/o3d/samples/third_party/codemirror/js/undo.js b/o3d/samples/third_party/codemirror/js/undo.js
new file mode 100644
index 0000000..a8edc51
--- /dev/null
+++ b/o3d/samples/third_party/codemirror/js/undo.js
@@ -0,0 +1,386 @@
+/**
+ * Storage and control for undo information within a CodeMirror
+ * editor. 'Why on earth is such a complicated mess required for
+ * that?', I hear you ask. The goal, in implementing this, was to make
+ * the complexity of storing and reverting undo information depend
+ * only on the size of the edited or restored content, not on the size
+ * of the whole document. This makes it necessary to use a kind of
+ * 'diff' system, which, when applied to a DOM tree, causes some
+ * complexity and hackery.
+ *
+ * In short, the editor 'touches' BR elements as it parses them, and
+ * the History stores these. When nothing is touched in commitDelay
+ * milliseconds, the changes are committed: It goes over all touched
+ * nodes, throws out the ones that did not change since last commit or
+ * are no longer in the document, and assembles the rest into zero or
+ * more 'chains' -- arrays of adjacent lines. Links back to these
+ * chains are added to the BR nodes, while the chain that previously
+ * spanned these nodes is added to the undo history. Undoing a change
+ * means taking such a chain off the undo history, restoring its
+ * content (text is saved per line) and linking it back into the
+ * document.
+ */
+
+// A history object needs to know about the DOM container holding the
+// document, the maximum amount of undo levels it should store, the
+// delay (of no input) after which it commits a set of changes, and,
+// unfortunately, the 'parent' window -- a window that is not in
+// designMode, and on which setTimeout works in every browser.
+function History(container, maxDepth, commitDelay, editor, onChange) {
+ this.container = container;
+ this.maxDepth = maxDepth; this.commitDelay = commitDelay;
+ this.editor = editor; this.parent = editor.parent;
+ this.onChange = onChange;
+ // This line object represents the initial, empty editor.
+ var initial = {text: "", from: null, to: null};
+ // As the borders between lines are represented by BR elements, the
+ // start of the first line and the end of the last one are
+ // represented by null. Since you can not store any properties
+ // (links to line objects) in null, these properties are used in
+ // those cases.
+ this.first = initial; this.last = initial;
+ // Similarly, a 'historyTouched' property is added to the BR in
+ // front of lines that have already been touched, and 'firstTouched'
+ // is used for the first line.
+ this.firstTouched = false;
+ // History is the set of committed changes, touched is the set of
+ // nodes touched since the last commit.
+ this.history = []; this.redoHistory = []; this.touched = [];
+}
+
+History.prototype = {
+ // Schedule a commit (if no other touches come in for commitDelay
+ // milliseconds).
+ scheduleCommit: function() {
+ this.parent.clearTimeout(this.commitTimeout);
+ this.commitTimeout = this.parent.setTimeout(method(this, "tryCommit"), this.commitDelay);
+ },
+
+ // Mark a node as touched. Null is a valid argument.
+ touch: function(node) {
+ this.setTouched(node);
+ this.scheduleCommit();
+ },
+
+ // Undo the last change.
+ undo: function() {
+ // Make sure pending changes have been committed.
+ this.commit();
+
+ if (this.history.length) {
+ // Take the top diff from the history, apply it, and store its
+ // shadow in the redo history.
+ this.redoHistory.push(this.updateTo(this.history.pop(), "applyChain"));
+ if (this.onChange) this.onChange();
+ }
+ },
+
+ // Redo the last undone change.
+ redo: function() {
+ this.commit();
+ if (this.redoHistory.length) {
+ // The inverse of undo, basically.
+ this.addUndoLevel(this.updateTo(this.redoHistory.pop(), "applyChain"));
+ if (this.onChange) this.onChange();
+ }
+ },
+
+ // Push a changeset into the document.
+ push: function(from, to, lines) {
+ var chain = [];
+ for (var i = 0; i < lines.length; i++) {
+ var end = (i == lines.length - 1) ? to : this.container.ownerDocument.createElement("BR");
+ chain.push({from: from, to: end, text: lines[i]});
+ from = end;
+ }
+ this.pushChains([chain], from == null && to == null);
+ },
+
+ pushChains: function(chains, doNotHighlight) {
+ this.commit(doNotHighlight);
+ this.addUndoLevel(this.updateTo(chains, "applyChain"));
+ this.redoHistory = [];
+ },
+
+ // Clear the undo history, make the current document the start
+ // position.
+ reset: function() {
+ this.history = []; this.redoHistory = [];
+ },
+
+ textAfter: function(br) {
+ return this.after(br).text;
+ },
+
+ nodeAfter: function(br) {
+ return this.after(br).to;
+ },
+
+ nodeBefore: function(br) {
+ return this.before(br).from;
+ },
+
+ // Commit unless there are pending dirty nodes.
+ tryCommit: function() {
+ if (this.editor.highlightDirty()) this.commit();
+ else this.scheduleCommit();
+ },
+
+ // Check whether the touched nodes hold any changes, if so, commit
+ // them.
+ commit: function(doNotHighlight) {
+ this.parent.clearTimeout(this.commitTimeout);
+ // Make sure there are no pending dirty nodes.
+ if (!doNotHighlight) this.editor.highlightDirty(true);
+ // Build set of chains.
+ var chains = this.touchedChains(), self = this;
+
+ if (chains.length) {
+ this.addUndoLevel(this.updateTo(chains, "linkChain"));
+ this.redoHistory = [];
+ if (this.onChange) this.onChange();
+ }
+ },
+
+ // [ end of public interface ]
+
+ // Update the document with a given set of chains, return its
+ // shadow. updateFunc should be "applyChain" or "linkChain". In the
+ // second case, the chains are taken to correspond the the current
+ // document, and only the state of the line data is updated. In the
+ // first case, the content of the chains is also pushed iinto the
+ // document.
+ updateTo: function(chains, updateFunc) {
+ var shadows = [], dirty = [];
+ for (var i = 0; i < chains.length; i++) {
+ shadows.push(this.shadowChain(chains[i]));
+ dirty.push(this[updateFunc](chains[i]));
+ }
+ if (updateFunc == "applyChain")
+ this.notifyDirty(dirty);
+ return shadows;
+ },
+
+ // Notify the editor that some nodes have changed.
+ notifyDirty: function(nodes) {
+ forEach(nodes, method(this.editor, "addDirtyNode"))
+ this.editor.scheduleHighlight();
+ },
+
+ // Link a chain into the DOM nodes (or the first/last links for null
+ // nodes).
+ linkChain: function(chain) {
+ for (var i = 0; i < chain.length; i++) {
+ var line = chain[i];
+ if (line.from) line.from.historyAfter = line;
+ else this.first = line;
+ if (line.to) line.to.historyBefore = line;
+ else this.last = line;
+ }
+ },
+
+ // Get the line object after/before a given node.
+ after: function(node) {
+ return node ? node.historyAfter : this.first;
+ },
+ before: function(node) {
+ return node ? node.historyBefore : this.last;
+ },
+
+ // Mark a node as touched if it has not already been marked.
+ setTouched: function(node) {
+ if (node) {
+ if (!node.historyTouched) {
+ this.touched.push(node);
+ node.historyTouched = true;
+ }
+ }
+ else {
+ this.firstTouched = true;
+ }
+ },
+
+ // Store a new set of undo info, throw away info if there is more of
+ // it than allowed.
+ addUndoLevel: function(diffs) {
+ this.history.push(diffs);
+ if (this.history.length > this.maxDepth)
+ this.history.shift();
+ },
+
+ // Build chains from a set of touched nodes.
+ touchedChains: function() {
+ var self = this;
+ // Compare two strings, treating nbsps as spaces.
+ function compareText(a, b) {
+ return a.replace(/\u00a0/g, " ") == b.replace(/\u00a0/g, " ");
+ }
+
+ // The temp system is a crummy hack to speed up determining
+ // whether a (currently touched) node has a line object associated
+ // with it. nullTemp is used to store the object for the first
+ // line, other nodes get it stored in their historyTemp property.
+ var nullTemp = null;
+ function temp(node) {return node ? node.historyTemp : nullTemp;}
+ function setTemp(node, line) {
+ if (node) node.historyTemp = line;
+ else nullTemp = line;
+ }
+
+ // Filter out unchanged lines and nodes that are no longer in the
+ // document. Build up line objects for remaining nodes.
+ var lines = [];
+ if (self.firstTouched) self.touched.push(null);
+ forEach(self.touched, function(node) {
+ if (node) {
+ node.historyTouched = false;
+ if (node.parentNode != self.container)
+ return;
+ }
+ else {
+ self.firstTouched = false;
+ }
+
+ var text = [];
+ for (var cur = node ? node.nextSibling : self.container.firstChild;
+ cur && cur.nodeName != "BR"; cur = cur.nextSibling)
+ if (cur.currentText) text.push(cur.currentText);
+
+ var line = {from: node, to: cur, text: text.join("")};
+ var shadow = self.after(node);
+ if (!shadow || !compareText(shadow.text, line.text) || shadow.to != line.to) {
+ lines.push(line);
+ setTemp(node, line);
+ }
+ });
+
+ // Get the BR element after/before the given node.
+ function nextBR(node, dir) {
+ var link = dir + "Sibling", search = node[link];
+ while (search && search.nodeName != "BR")
+ search = search[link];
+ return search;
+ }
+
+ // Assemble line objects into chains by scanning the DOM tree
+ // around them.
+ var chains = []; self.touched = [];
+ forEach(lines, function(line) {
+ // Note that this makes the loop skip line objects that have
+ // been pulled into chains by lines before them.
+ if (!temp(line.from)) return;
+
+ var chain = [], curNode = line.from;
+ // Put any line objects (referred to by temp info) before this
+ // one on the front of the array.
+ while (true) {
+ var curLine = temp(curNode);
+ if (!curLine) break;
+ chain.unshift(curLine);
+ setTemp(curNode, null);
+ if (!curNode) break;
+ curNode = nextBR(curNode, "previous");
+ }
+ curNode = line.to;
+ // Add lines after this one at end of array.
+ while (true) {
+ var curLine = temp(curNode);
+ if (!curLine || !curNode) break;
+ chain.push(curLine);
+ setTemp(curNode, null);
+ curNode = nextBR(curNode, "next");
+ }
+
+ // Chains that can not determine a valid 'shadow' -- a chain
+ // currently stored in the DOM tree that has the same start and
+ // end point -- are put back into the touched set, hoping they
+ // will be valid next time.
+ if (self.after(chain[0].from) && self.before(chain[chain.length - 1].to))
+ chains.push(chain);
+ else
+ forEach(chain, function(line) {self.setTouched(line.from);});
+ });
+
+ return chains;
+ },
+
+ // Find the 'shadow' of a given chain by following the links in the
+ // DOM nodes at its start and end.
+ shadowChain: function(chain) {
+ var shadows = [], next = this.after(chain[0].from), end = chain[chain.length - 1].to;
+ while (true) {
+ shadows.push(next);
+ var nextNode = next.to;
+ if (!nextNode || nextNode == end)
+ break;
+ else
+ next = nextNode.historyAfter;
+ }
+ return shadows;
+ },
+
+ // Update the DOM tree to contain the lines specified in a given
+ // chain, link this chain into the DOM nodes.
+ applyChain: function(chain) {
+ // Some attempt is made to prevent the cursor from jumping
+ // randomly when an undo or redo happens. It still behaves a bit
+ // strange sometimes.
+ var cursor = select.cursorPos(this.container, false), self = this;
+
+ // Remove all nodes in the DOM tree between from and to (null for
+ // start/end of container).
+ function removeRange(from, to) {
+ var pos = from ? from.nextSibling : self.container.firstChild;
+ while (pos != to) {
+ var temp = pos.nextSibling;
+ removeElement(pos);
+ pos = temp;
+ }
+ }
+
+ var start = chain[0].from, end = chain[chain.length - 1].to;
+ // Clear the space where this change has to be made.
+ removeRange(start, end);
+
+ // Build a function that will insert nodes before the end node of
+ // this chain.
+ var insert = end ?
+ function(node) {self.container.insertBefore(node, end);}
+ : function(node) {self.container.appendChild(node);};
+
+ // Insert the content specified by the chain into the DOM tree.
+ for (var i = 0; i < chain.length; i++) {
+ var line = chain[i];
+ // The start and end of the space are already correct, but BR
+ // tags inside it have to be put back.
+ if (i > 0)
+ insert(line.from);
+ // Add the text.
+ var textNode = this.container.ownerDocument.createTextNode(line.text);
+ insert(textNode);
+ // See if the cursor was on this line. Put it back, adjusting
+ // for changed line length, if it was.
+ if (cursor && cursor.node == line.from) {
+ var cursordiff = 0;
+ var prev = this.after(line.from);
+ if (prev && i == chain.length - 1) {
+ // Only adjust if the cursor is after the unchanged part of
+ // the line.
+ for (var match = 0; match < cursor.offset &&
+ line.text.charAt(match) == prev.text.charAt(match); match++);
+ if (cursor.offset > match)
+ cursordiff = line.text.length - prev.text.length;
+ }
+ select.setCursorPos(this.container, {node: line.from, offset: Math.max(0, cursor.offset + cursordiff)});
+ }
+ // Cursor was in removed line, this is last new line.
+ else if (cursor && (i == chain.length - 1) && cursor.node && cursor.node.parentNode != this.container) {
+ select.setCursorPos(this.container, {node: line.from, offset: line.text.length});
+ }
+ }
+
+ // Anchor the chain in the DOM tree.
+ this.linkChain(chain);
+ return start;
+ }
+};
diff --git a/o3d/samples/third_party/codemirror/js/util.js b/o3d/samples/third_party/codemirror/js/util.js
new file mode 100644
index 0000000..f866a29
--- /dev/null
+++ b/o3d/samples/third_party/codemirror/js/util.js
@@ -0,0 +1,134 @@
+/* A few useful utility functions. */
+
+// Capture a method on an object.
+function method(obj, name) {
+ return function() {obj[name].apply(obj, arguments);};
+}
+
+// Write properties from an object into another object.
+function update(obj, from) {
+ for (var name in from)
+ obj[name] = from[name];
+ return obj;
+}
+
+// The value used to signal the end of a sequence in iterators.
+var StopIteration = {toString: function() {return "StopIteration"}};
+
+// Checks whether the argument is an iterator or a regular sequence,
+// turns it into an iterator.
+function iter(seq) {
+ var i = 0;
+ if (seq.next) return seq;
+ else return {
+ next: function() {
+ if (i >= seq.length) throw StopIteration;
+ else return seq[++i];
+ }
+ };
+}
+
+// Apply a function to each element in a sequence.
+function forEach(iter, f) {
+ if (iter.next) {
+ try {while (true) f(iter.next());}
+ catch (e) {if (e != StopIteration) throw e;}
+ }
+ else {
+ for (var i = 0; i < iter.length; i++)
+ f(iter[i]);
+ }
+}
+
+// Map a function over a sequence, producing an array of results.
+function map(iter, f) {
+ var accum = [];
+ forEach(iter, function(val) {accum.push(f(val));});
+ return accum;
+}
+
+// Create a predicate function that tests a string againsts a given
+// regular expression.
+function matcher(regexp){
+ return function(value){return regexp.test(value);};
+}
+
+// Test whether a DOM node has a certain CSS class. Much faster than
+// the MochiKit equivalent, for some reason.
+function hasClass(element, className){
+ var classes = element.className;
+ return classes && new RegExp("(^| )" + className + "($| )").test(classes);
+}
+
+// Insert a DOM node after another node.
+function insertAfter(newNode, oldNode) {
+ var parent = oldNode.parentNode;
+ parent.insertBefore(newNode, oldNode.nextSibling);
+ return newNode;
+}
+
+function removeElement(node) {
+ if (node.parentNode)
+ node.parentNode.removeChild(node);
+}
+
+function clearElement(node) {
+ while (node.firstChild)
+ node.removeChild(node.firstChild);
+}
+
+// Check whether a node is contained in another one.
+function isAncestor(node, child) {
+ while (child = child.parentNode) {
+ if (node == child)
+ return true;
+ }
+ return false;
+}
+
+// The non-breaking space character.
+var nbsp = "\u00a0";
+var matching = {"{": "}", "[": "]", "(": ")",
+ "}": "{", "]": "[", ")": "("};
+
+// Standardize a few unportable event properties.
+function normalizeEvent(event) {
+ if (!event.stopPropagation) {
+ event.stopPropagation = function() {this.cancelBubble = true;};
+ event.preventDefault = function() {this.returnValue = false;};
+ }
+ if (!event.stop) {
+ event.stop = function() {
+ this.stopPropagation();
+ this.preventDefault();
+ };
+ }
+
+ if (event.type == "keypress") {
+ if (event.charCode === 0 || event.charCode == undefined)
+ event.code = event.keyCode;
+ else
+ event.code = event.charCode;
+ event.character = String.fromCharCode(event.code);
+ }
+ return event;
+}
+
+// Portably register event handlers.
+function addEventHandler(node, type, handler) {
+ function wrapHandler(event) {
+ handler(normalizeEvent(event || window.event));
+ }
+ if (typeof node.addEventListener == "function") {
+ node.addEventListener(type, wrapHandler, false);
+ return function() { node.removeEventListener(type, wrapHandler, false); };
+ }
+ else {
+ node.attachEvent("on" + type, wrapHandler);
+ return function() { node.detachEvent("on" + type, wrapHandler); };
+ }
+}
+
+function removeEventHandler(handler) {
+ handler();
+}
diff --git a/o3d/samples/third_party/lightbox/LICENSE-lightbox-iframe.txt b/o3d/samples/third_party/lightbox/LICENSE-lightbox-iframe.txt
new file mode 100644
index 0000000..b4fe311
--- /dev/null
+++ b/o3d/samples/third_party/lightbox/LICENSE-lightbox-iframe.txt
@@ -0,0 +1,66 @@
+According to particletree.com, everything they publish is under the following
+Creative Commons license. This version of lightbox-iframe is a derivative of
+the one from particletree.com, as stated in the source files.
+
+
+http://creativecommons.org/licenses/by/2.5/
+
+
+Attribution 2.5
+
+CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
+License
+
+THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
+
+BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
+
+1. Definitions
+
+"Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
+"Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
+"Licensor" means the individual or entity that offers the Work under the terms of this License.
+"Original Author" means the individual or entity who created the Work.
+"Work" means the copyrightable work of authorship offered under the terms of this License.
+"You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
+2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.
+
+3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
+
+to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
+to create and reproduce Derivative Works;
+to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
+to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works.
+For the avoidance of doubt, where the work is a musical composition:
+
+Performance Royalties Under Blanket Licenses. Licensor waives the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work.
+Mechanical Rights and Statutory Royalties. Licensor waives the exclusive right to collect, whether individually or via a music rights agency or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions).
+Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor waives the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions).
+The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved.
+
+4. Restrictions.The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
+
+You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any credit as required by clause 4(b), as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any credit as required by clause 4(b), as requested.
+If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or (ii) if the Original Author and/or Licensor designate another party or parties (e.g. a sponsor institute, publishing entity, journal) for attribution in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.
+5. Representations, Warranties and Disclaimer
+
+UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
+
+6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. Termination
+
+This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
+Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
+8. Miscellaneous
+
+Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
+Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
+If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
+No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
+This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
+Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
+
+Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, neither party will use the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time.
+
+Creative Commons may be contacted at http://creativecommons.org/.
diff --git a/o3d/samples/third_party/lightbox/LICENSE-prototype.txt b/o3d/samples/third_party/lightbox/LICENSE-prototype.txt
new file mode 100644
index 0000000..61e4918
--- /dev/null
+++ b/o3d/samples/third_party/lightbox/LICENSE-prototype.txt
@@ -0,0 +1,16 @@
+Copyright (c) 2005-2008 Sam Stephenson
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/o3d/samples/third_party/lightbox/lightbox-iframe.js b/o3d/samples/third_party/lightbox/lightbox-iframe.js
new file mode 100644
index 0000000..33e1889
--- /dev/null
+++ b/o3d/samples/third_party/lightbox/lightbox-iframe.js
@@ -0,0 +1,199 @@
+/*
+Created By: Chris Campbell
+Modified By: Noah Winecoff (http://www.findmotive.com)
+Website: http://particletree.com
+Date: 2/1/2006
+
+Inspired by the lightbox implementation found at http://www.huddletogether.com/projects/lightbox/
+*/
+
+/*-------------------------------GLOBAL VARIABLES------------------------------------*/
+
+var detect = navigator.userAgent.toLowerCase();
+var OS,browser,version,total,thestring;
+
+/*-----------------------------------------------------------------------------------------------*/
+
+//Browser detect script origionally created by Peter Paul Koch at http://www.quirksmode.org/
+
+function getBrowserInfo() {
+ if (checkIt('konqueror')) {
+ browser = "Konqueror";
+ OS = "Linux";
+ }
+ else if (checkIt('safari')) browser = "Safari"
+ else if (checkIt('omniweb')) browser = "OmniWeb"
+ else if (checkIt('opera')) browser = "Opera"
+ else if (checkIt('webtv')) browser = "WebTV";
+ else if (checkIt('icab')) browser = "iCab"
+ else if (checkIt('msie')) browser = "Internet Explorer"
+ else if (!checkIt('compatible')) {
+ browser = "Netscape Navigator"
+ version = detect.charAt(8);
+ }
+ else browser = "An unknown browser";
+
+ if (!version) version = detect.charAt(place + thestring.length);
+
+ if (!OS) {
+ if (checkIt('linux')) OS = "Linux";
+ else if (checkIt('x11')) OS = "Unix";
+ else if (checkIt('mac')) OS = "Mac"
+ else if (checkIt('win')) OS = "Windows"
+ else OS = "an unknown operating system";
+ }
+}
+
+function checkIt(string) {
+ place = detect.indexOf(string) + 1;
+ thestring = string;
+ return place;
+}
+
+/*-----------------------------------------------------------------------------------------------*/
+
+Event.observe(window, 'load', initialize, false);
+Event.observe(window, 'load', getBrowserInfo, false);
+Event.observe(window, 'unload', Event.unloadCache, false);
+
+var lightbox = Class.create();
+
+lightbox.prototype = {
+
+ yPos : 0,
+ xPos : 0,
+
+ initialize: function(ctrl) {
+ this.content = ctrl.href;
+ Event.observe(ctrl, 'click', this.activate.bindAsEventListener(this), false);
+ ctrl.onclick = function(){return false;};
+ },
+
+ // Turn everything on - mainly the IE fixes
+ activate: function(){
+ if (browser == 'Internet Explorer'){
+ this.getScroll();
+ this.prepareIE('100%', 'hidden');
+ this.setScroll(0,0);
+ this.hideSelects('hidden');
+ }
+ this.displayLightbox("block");
+ },
+
+ // Ie requires height to 100% and overflow hidden or else you can scroll down past the lightbox
+ prepareIE: function(height, overflow){
+ bod = document.getElementsByTagName('body')[0];
+ bod.style.height = height;
+ bod.style.overflow = overflow;
+
+ htm = document.getElementsByTagName('html')[0];
+ htm.style.height = height;
+ htm.style.overflow = overflow;
+ },
+
+ // In IE, select elements hover on top of the lightbox
+ hideSelects: function(visibility){
+ selects = document.getElementsByTagName('select');
+ for(i = 0; i < selects.length; i++) {
+ selects[i].style.visibility = visibility;
+ }
+ },
+
+ // Taken from lightbox implementation found at http://www.huddletogether.com/projects/lightbox/
+ getScroll: function(){
+ if (self.pageYOffset) {
+ this.yPos = self.pageYOffset;
+ } else if (document.documentElement && document.documentElement.scrollTop){
+ this.yPos = document.documentElement.scrollTop;
+ } else if (document.body) {
+ this.yPos = document.body.scrollTop;
+ }
+ },
+
+ setScroll: function(x, y){
+ window.scrollTo(x, y);
+ },
+
+ displayLightbox: function(display){
+ $('overlay').style.display = display;
+ $('lightbox').style.display = display;
+ if(display != 'none') this.loadInfo();
+ },
+
+ // Write an iFrame instead of using an AJAX call to pull the content
+ loadInfo: function() {
+ info = "<div id='lbContent'><center><a href='#' class='lbAction' rel='deactivate'>close (x)</a></center><iframe frameborder=\"0\" width=\"100%\" height=\"100%\" src=\"" + this.content + "\"</iframe></div>";
+ new Insertion.Before($('lbLoadMessage'), info)
+ $('lightbox').className = "done";
+ this.actions();
+
+ },
+
+ // Search through new links within the lightbox, and attach click event
+ actions: function(){
+ lbActions = document.getElementsByClassName('lbAction');
+
+ for(i = 0; i < lbActions.length; i++) {
+ Event.observe(lbActions[i], 'click', this[lbActions[i].rel].bindAsEventListener(this), false);
+ lbActions[i].onclick = function(){return false;};
+ }
+
+ },
+
+ // Example of creating your own functionality once lightbox is initiated
+ insert: function(e){
+ link = Event.element(e).parentNode;
+ Element.remove($('lbContent'));
+
+ var myAjax = new Ajax.Request(
+ link.href,
+ {method: 'post', parameters: "", onComplete: this.processInfo.bindAsEventListener(this)}
+ );
+
+ },
+
+ // Example of creating your own functionality once lightbox is initiated
+ deactivate: function(){
+ Element.remove($('lbContent'));
+
+ if (browser == "Internet Explorer"){
+ this.setScroll(0,this.yPos);
+ this.prepareIE("auto", "auto");
+ this.hideSelects("visible");
+ }
+
+ this.displayLightbox("none");
+ }
+}
+
+/*-----------------------------------------------------------------------------------------------*/
+
+// Onload, make all links that need to trigger a lightbox active
+function initialize(){
+ addLightboxMarkup();
+ lbox = document.getElementsByClassName('lbOn');
+ for(i = 0; i < lbox.length; i++) {
+ valid = new lightbox(lbox[i]);
+ }
+}
+
+// Add in markup necessary to make this work. Basically two divs:
+// Overlay holds the shadow
+// Lightbox is the centered square that the content is put into.
+function addLightboxMarkup() {
+ bod = document.getElementsByTagName('body')[0];
+ overlay = document.createElement('div');
+ overlay.id = 'overlay';
+ lb = document.createElement('div');
+ lb.id = 'lbinner';
+ // lb.className = 'loading';
+ lb.innerHTML = '<div id="lbLoadMessage">' +
+ 'loading' +
+ '</div>';
+ lbcontainer = document.createElement('div');
+ lbcontainer.id = 'lightbox';
+ lb.className = 'loading';
+ lbcontainer.appendChild(lb);
+ bod.appendChild(overlay);
+ bod.appendChild(lbcontainer);
+} \ No newline at end of file
diff --git a/o3d/samples/third_party/lightbox/lightbox.css b/o3d/samples/third_party/lightbox/lightbox.css
new file mode 100644
index 0000000..363969b
--- /dev/null
+++ b/o3d/samples/third_party/lightbox/lightbox.css
@@ -0,0 +1,74 @@
+/* - - - - - - - - - - - - - - - - - - - - -
+
+Title : Lightbox CSS
+Author : Kevin Hale
+Modified By: Noah Winecoff (http://www.findmotive.com)
+URL : http://particletree.com/features/lightbox-gone-wild/
+
+Created : January 13, 2006
+Modified : February 1, 2006
+
+- - - - - - - - - - - - - - - - - - - - - */
+
+#lightbox{
+ display:none;
+/* position: absolute;*/
+ top:0%;
+/* left:50%;*/
+ z-index:9999;
+ width:100%;
+ height:100%;
+}
+#lightbox[id]{
+ position:fixed;
+}
+
+#lbinner {
+ margin: 0 auto;
+ border:10px solid #555555;
+ background:#FDFCE9;
+ text-align:left;
+ max-width: 900px;
+ width:100%;
+ height:90%;
+ margin-top: 10px;
+ z-index:9999;
+ padding-bottom: 20px;
+}
+
+#overlay{
+ display:none;
+ position:absolute;
+ top:0;
+ left:0;
+ width:100%;
+ height:100%;
+ z-index:5000;
+ background-color:#000;
+ -moz-opacity: 0.8;
+ opacity:.80;
+ filter: alpha(opacity=80);
+}
+#overlay[id]{
+ position:fixed;
+}
+
+#lightbox.done #lbLoadMessage{
+ display:none;
+}
+#lightbox.done #lbContent{
+ display:block;
+ width: 100%;
+ height: 100%;
+}
+#lightbox.loading #lbContent{
+ display:none;
+}
+#lightbox.loading #lbLoadMessage{
+ display:block;
+}
+
+#lightbox.done img{
+ width:100%;
+ height:100%;
+} \ No newline at end of file
diff --git a/o3d/samples/third_party/lightbox/prototype.js b/o3d/samples/third_party/lightbox/prototype.js
new file mode 100644
index 0000000..b19bf53
--- /dev/null
+++ b/o3d/samples/third_party/lightbox/prototype.js
@@ -0,0 +1,1785 @@
+/* Prototype JavaScript framework, version 1.4.0
+ * (c) 2005 Sam Stephenson <sam@conio.net>
+ *
+ * THIS FILE IS AUTOMATICALLY GENERATED. When sending patches, please diff
+ * against the source tree, available from the Prototype darcs repository.
+ *
+ * Prototype is freely distributable under the terms of an MIT-style license.
+ *
+ * For details, see the Prototype web site: http://prototype.conio.net/
+ *
+/*--------------------------------------------------------------------------*/
+
+var Prototype = {
+ Version: '1.4.0',
+ ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
+
+ emptyFunction: function() {},
+ K: function(x) {return x}
+}
+
+var Class = {
+ create: function() {
+ return function() {
+ this.initialize.apply(this, arguments);
+ }
+ }
+}
+
+var Abstract = new Object();
+
+Object.extend = function(destination, source) {
+ for (property in source) {
+ destination[property] = source[property];
+ }
+ return destination;
+}
+
+Object.inspect = function(object) {
+ try {
+ if (object == undefined) return 'undefined';
+ if (object == null) return 'null';
+ return object.inspect ? object.inspect() : object.toString();
+ } catch (e) {
+ if (e instanceof RangeError) return '...';
+ throw e;
+ }
+}
+
+Function.prototype.bind = function() {
+ var __method = this, args = $A(arguments), object = args.shift();
+ return function() {
+ return __method.apply(object, args.concat($A(arguments)));
+ }
+}
+
+Function.prototype.bindAsEventListener = function(object) {
+ var __method = this;
+ return function(event) {
+ return __method.call(object, event || window.event);
+ }
+}
+
+Object.extend(Number.prototype, {
+ toColorPart: function() {
+ var digits = this.toString(16);
+ if (this < 16) return '0' + digits;
+ return digits;
+ },
+
+ succ: function() {
+ return this + 1;
+ },
+
+ times: function(iterator) {
+ $R(0, this, true).each(iterator);
+ return this;
+ }
+});
+
+var Try = {
+ these: function() {
+ var returnValue;
+
+ for (var i = 0; i < arguments.length; i++) {
+ var lambda = arguments[i];
+ try {
+ returnValue = lambda();
+ break;
+ } catch (e) {}
+ }
+
+ return returnValue;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create();
+PeriodicalExecuter.prototype = {
+ initialize: function(callback, frequency) {
+ this.callback = callback;
+ this.frequency = frequency;
+ this.currentlyExecuting = false;
+
+ this.registerCallback();
+ },
+
+ registerCallback: function() {
+ setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+ },
+
+ onTimerEvent: function() {
+ if (!this.currentlyExecuting) {
+ try {
+ this.currentlyExecuting = true;
+ this.callback();
+ } finally {
+ this.currentlyExecuting = false;
+ }
+ }
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+function $() {
+ var elements = new Array();
+
+ for (var i = 0; i < arguments.length; i++) {
+ var element = arguments[i];
+ if (typeof element == 'string')
+ element = document.getElementById(element);
+
+ if (arguments.length == 1)
+ return element;
+
+ elements.push(element);
+ }
+
+ return elements;
+}
+Object.extend(String.prototype, {
+ stripTags: function() {
+ return this.replace(/<\/?[^>]+>/gi, '');
+ },
+
+ stripScripts: function() {
+ return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+ },
+
+ extractScripts: function() {
+ var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+ var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+ return (this.match(matchAll) || []).map(function(scriptTag) {
+ return (scriptTag.match(matchOne) || ['', ''])[1];
+ });
+ },
+
+ evalScripts: function() {
+ return this.extractScripts().map(eval);
+ },
+
+ escapeHTML: function() {
+ var div = document.createElement('div');
+ var text = document.createTextNode(this);
+ div.appendChild(text);
+ return div.innerHTML;
+ },
+
+ unescapeHTML: function() {
+ var div = document.createElement('div');
+ div.innerHTML = this.stripTags();
+ return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
+ },
+
+ toQueryParams: function() {
+ var pairs = this.match(/^\??(.*)$/)[1].split('&');
+ return pairs.inject({}, function(params, pairString) {
+ var pair = pairString.split('=');
+ params[pair[0]] = pair[1];
+ return params;
+ });
+ },
+
+ toArray: function() {
+ return this.split('');
+ },
+
+ camelize: function() {
+ var oStringList = this.split('-');
+ if (oStringList.length == 1) return oStringList[0];
+
+ var camelizedString = this.indexOf('-') == 0
+ ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
+ : oStringList[0];
+
+ for (var i = 1, len = oStringList.length; i < len; i++) {
+ var s = oStringList[i];
+ camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
+ }
+
+ return camelizedString;
+ },
+
+ inspect: function() {
+ return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'";
+ }
+});
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+var $break = new Object();
+var $continue = new Object();
+
+var Enumerable = {
+ each: function(iterator) {
+ var index = 0;
+ try {
+ this._each(function(value) {
+ try {
+ iterator(value, index++);
+ } catch (e) {
+ if (e != $continue) throw e;
+ }
+ });
+ } catch (e) {
+ if (e != $break) throw e;
+ }
+ },
+
+ all: function(iterator) {
+ var result = true;
+ this.each(function(value, index) {
+ result = result && !!(iterator || Prototype.K)(value, index);
+ if (!result) throw $break;
+ });
+ return result;
+ },
+
+ any: function(iterator) {
+ var result = true;
+ this.each(function(value, index) {
+ if (result = !!(iterator || Prototype.K)(value, index))
+ throw $break;
+ });
+ return result;
+ },
+
+ collect: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ results.push(iterator(value, index));
+ });
+ return results;
+ },
+
+ detect: function (iterator) {
+ var result;
+ this.each(function(value, index) {
+ if (iterator(value, index)) {
+ result = value;
+ throw $break;
+ }
+ });
+ return result;
+ },
+
+ findAll: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ if (iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ grep: function(pattern, iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ var stringValue = value.toString();
+ if (stringValue.match(pattern))
+ results.push((iterator || Prototype.K)(value, index));
+ })
+ return results;
+ },
+
+ include: function(object) {
+ var found = false;
+ this.each(function(value) {
+ if (value == object) {
+ found = true;
+ throw $break;
+ }
+ });
+ return found;
+ },
+
+ inject: function(memo, iterator) {
+ this.each(function(value, index) {
+ memo = iterator(memo, value, index);
+ });
+ return memo;
+ },
+
+ invoke: function(method) {
+ var args = $A(arguments).slice(1);
+ return this.collect(function(value) {
+ return value[method].apply(value, args);
+ });
+ },
+
+ max: function(iterator) {
+ var result;
+ this.each(function(value, index) {
+ value = (iterator || Prototype.K)(value, index);
+ if (value >= (result || value))
+ result = value;
+ });
+ return result;
+ },
+
+ min: function(iterator) {
+ var result;
+ this.each(function(value, index) {
+ value = (iterator || Prototype.K)(value, index);
+ if (value <= (result || value))
+ result = value;
+ });
+ return result;
+ },
+
+ partition: function(iterator) {
+ var trues = [], falses = [];
+ this.each(function(value, index) {
+ ((iterator || Prototype.K)(value, index) ?
+ trues : falses).push(value);
+ });
+ return [trues, falses];
+ },
+
+ pluck: function(property) {
+ var results = [];
+ this.each(function(value, index) {
+ results.push(value[property]);
+ });
+ return results;
+ },
+
+ reject: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ if (!iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ sortBy: function(iterator) {
+ return this.collect(function(value, index) {
+ return {value: value, criteria: iterator(value, index)};
+ }).sort(function(left, right) {
+ var a = left.criteria, b = right.criteria;
+ return a < b ? -1 : a > b ? 1 : 0;
+ }).pluck('value');
+ },
+
+ toArray: function() {
+ return this.collect(Prototype.K);
+ },
+
+ zip: function() {
+ var iterator = Prototype.K, args = $A(arguments);
+ if (typeof args.last() == 'function')
+ iterator = args.pop();
+
+ var collections = [this].concat(args).map($A);
+ return this.map(function(value, index) {
+ iterator(value = collections.pluck(index));
+ return value;
+ });
+ },
+
+ inspect: function() {
+ return '#<Enumerable:' + this.toArray().inspect() + '>';
+ }
+}
+
+Object.extend(Enumerable, {
+ map: Enumerable.collect,
+ find: Enumerable.detect,
+ select: Enumerable.findAll,
+ member: Enumerable.include,
+ entries: Enumerable.toArray
+});
+var $A = Array.from = function(iterable) {
+ if (!iterable) return [];
+ if (iterable.toArray) {
+ return iterable.toArray();
+ } else {
+ var results = [];
+ for (var i = 0; i < iterable.length; i++)
+ results.push(iterable[i]);
+ return results;
+ }
+}
+
+Object.extend(Array.prototype, Enumerable);
+
+Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+ _each: function(iterator) {
+ for (var i = 0; i < this.length; i++)
+ iterator(this[i]);
+ },
+
+ clear: function() {
+ this.length = 0;
+ return this;
+ },
+
+ first: function() {
+ return this[0];
+ },
+
+ last: function() {
+ return this[this.length - 1];
+ },
+
+ compact: function() {
+ return this.select(function(value) {
+ return value != undefined || value != null;
+ });
+ },
+
+ flatten: function() {
+ return this.inject([], function(array, value) {
+ return array.concat(value.constructor == Array ?
+ value.flatten() : [value]);
+ });
+ },
+
+ without: function() {
+ var values = $A(arguments);
+ return this.select(function(value) {
+ return !values.include(value);
+ });
+ },
+
+ indexOf: function(object) {
+ for (var i = 0; i < this.length; i++)
+ if (this[i] == object) return i;
+ return -1;
+ },
+
+ reverse: function(inline) {
+ return (inline !== false ? this : this.toArray())._reverse();
+ },
+
+ shift: function() {
+ var result = this[0];
+ for (var i = 0; i < this.length - 1; i++)
+ this[i] = this[i + 1];
+ this.length--;
+ return result;
+ },
+
+ inspect: function() {
+ return '[' + this.map(Object.inspect).join(', ') + ']';
+ }
+});
+var Hash = {
+ _each: function(iterator) {
+ for (key in this) {
+ var value = this[key];
+ if (typeof value == 'function') continue;
+
+ var pair = [key, value];
+ pair.key = key;
+ pair.value = value;
+ iterator(pair);
+ }
+ },
+
+ keys: function() {
+ return this.pluck('key');
+ },
+
+ values: function() {
+ return this.pluck('value');
+ },
+
+ merge: function(hash) {
+ return $H(hash).inject($H(this), function(mergedHash, pair) {
+ mergedHash[pair.key] = pair.value;
+ return mergedHash;
+ });
+ },
+
+ toQueryString: function() {
+ return this.map(function(pair) {
+ return pair.map(encodeURIComponent).join('=');
+ }).join('&');
+ },
+
+ inspect: function() {
+ return '#<Hash:{' + this.map(function(pair) {
+ return pair.map(Object.inspect).join(': ');
+ }).join(', ') + '}>';
+ }
+}
+
+function $H(object) {
+ var hash = Object.extend({}, object || {});
+ Object.extend(hash, Enumerable);
+ Object.extend(hash, Hash);
+ return hash;
+}
+ObjectRange = Class.create();
+Object.extend(ObjectRange.prototype, Enumerable);
+Object.extend(ObjectRange.prototype, {
+ initialize: function(start, end, exclusive) {
+ this.start = start;
+ this.end = end;
+ this.exclusive = exclusive;
+ },
+
+ _each: function(iterator) {
+ var value = this.start;
+ do {
+ iterator(value);
+ value = value.succ();
+ } while (this.include(value));
+ },
+
+ include: function(value) {
+ if (value < this.start)
+ return false;
+ if (this.exclusive)
+ return value < this.end;
+ return value <= this.end;
+ }
+});
+
+var $R = function(start, end, exclusive) {
+ return new ObjectRange(start, end, exclusive);
+}
+
+var Ajax = {
+ getTransport: function() {
+ return Try.these(
+ function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+ function() {return new ActiveXObject('Microsoft.XMLHTTP')},
+ function() {return new XMLHttpRequest()}
+ ) || false;
+ },
+
+ activeRequestCount: 0
+}
+
+Ajax.Responders = {
+ responders: [],
+
+ _each: function(iterator) {
+ this.responders._each(iterator);
+ },
+
+ register: function(responderToAdd) {
+ if (!this.include(responderToAdd))
+ this.responders.push(responderToAdd);
+ },
+
+ unregister: function(responderToRemove) {
+ this.responders = this.responders.without(responderToRemove);
+ },
+
+ dispatch: function(callback, request, transport, json) {
+ this.each(function(responder) {
+ if (responder[callback] && typeof responder[callback] == 'function') {
+ try {
+ responder[callback].apply(responder, [request, transport, json]);
+ } catch (e) {}
+ }
+ });
+ }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+ onCreate: function() {
+ Ajax.activeRequestCount++;
+ },
+
+ onComplete: function() {
+ Ajax.activeRequestCount--;
+ }
+});
+
+Ajax.Base = function() {};
+Ajax.Base.prototype = {
+ setOptions: function(options) {
+ this.options = {
+ method: 'post',
+ asynchronous: true,
+ parameters: ''
+ }
+ Object.extend(this.options, options || {});
+ },
+
+ responseIsSuccess: function() {
+ return this.transport.status == undefined
+ || this.transport.status == 0
+ || (this.transport.status >= 200 && this.transport.status < 300);
+ },
+
+ responseIsFailure: function() {
+ return !this.responseIsSuccess();
+ }
+}
+
+Ajax.Request = Class.create();
+Ajax.Request.Events =
+ ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
+ initialize: function(url, options) {
+ this.transport = Ajax.getTransport();
+ this.setOptions(options);
+ this.request(url);
+ },
+
+ request: function(url) {
+ var parameters = this.options.parameters || '';
+ if (parameters.length > 0) parameters += '&_=';
+
+ try {
+ this.url = url;
+ if (this.options.method == 'get' && parameters.length > 0)
+ this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
+
+ Ajax.Responders.dispatch('onCreate', this, this.transport);
+
+ this.transport.open(this.options.method, this.url,
+ this.options.asynchronous);
+
+ if (this.options.asynchronous) {
+ this.transport.onreadystatechange = this.onStateChange.bind(this);
+ setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
+ }
+
+ this.setRequestHeaders();
+
+ var body = this.options.postBody ? this.options.postBody : parameters;
+ this.transport.send(this.options.method == 'post' ? body : null);
+
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ setRequestHeaders: function() {
+ var requestHeaders =
+ ['X-Requested-With', 'XMLHttpRequest',
+ 'X-Prototype-Version', Prototype.Version];
+
+ if (this.options.method == 'post') {
+ requestHeaders.push('Content-type',
+ 'application/x-www-form-urlencoded');
+
+ /* Force "Connection: close" for Mozilla browsers to work around
+ * a bug where XMLHttpReqeuest sends an incorrect Content-length
+ * header. See Mozilla Bugzilla #246651.
+ */
+ if (this.transport.overrideMimeType)
+ requestHeaders.push('Connection', 'close');
+ }
+
+ if (this.options.requestHeaders)
+ requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
+
+ for (var i = 0; i < requestHeaders.length; i += 2)
+ this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
+ },
+
+ onStateChange: function() {
+ var readyState = this.transport.readyState;
+ if (readyState != 1)
+ this.respondToReadyState(this.transport.readyState);
+ },
+
+ header: function(name) {
+ try {
+ return this.transport.getResponseHeader(name);
+ } catch (e) {}
+ },
+
+ evalJSON: function() {
+ try {
+ return eval(this.header('X-JSON'));
+ } catch (e) {}
+ },
+
+ evalResponse: function() {
+ try {
+ return eval(this.transport.responseText);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ respondToReadyState: function(readyState) {
+ var event = Ajax.Request.Events[readyState];
+ var transport = this.transport, json = this.evalJSON();
+
+ if (event == 'Complete') {
+ try {
+ (this.options['on' + this.transport.status]
+ || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
+ || Prototype.emptyFunction)(transport, json);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ if ((this.header('Content-type') || '').match(/^text\/javascript/i))
+ this.evalResponse();
+ }
+
+ try {
+ (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
+ Ajax.Responders.dispatch('on' + event, this, transport, json);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
+ if (event == 'Complete')
+ this.transport.onreadystatechange = Prototype.emptyFunction;
+ },
+
+ dispatchException: function(exception) {
+ (this.options.onException || Prototype.emptyFunction)(this, exception);
+ Ajax.Responders.dispatch('onException', this, exception);
+ }
+});
+
+Ajax.Updater = Class.create();
+
+Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
+ initialize: function(container, url, options) {
+ this.containers = {
+ success: container.success ? $(container.success) : $(container),
+ failure: container.failure ? $(container.failure) :
+ (container.success ? null : $(container))
+ }
+
+ this.transport = Ajax.getTransport();
+ this.setOptions(options);
+
+ var onComplete = this.options.onComplete || Prototype.emptyFunction;
+ this.options.onComplete = (function(transport, object) {
+ this.updateContent();
+ onComplete(transport, object);
+ }).bind(this);
+
+ this.request(url);
+ },
+
+ updateContent: function() {
+ var receiver = this.responseIsSuccess() ?
+ this.containers.success : this.containers.failure;
+ var response = this.transport.responseText;
+
+ if (!this.options.evalScripts)
+ response = response.stripScripts();
+
+ if (receiver) {
+ if (this.options.insertion) {
+ new this.options.insertion(receiver, response);
+ } else {
+ Element.update(receiver, response);
+ }
+ }
+
+ if (this.responseIsSuccess()) {
+ if (this.onComplete)
+ setTimeout(this.onComplete.bind(this), 10);
+ }
+ }
+});
+
+Ajax.PeriodicalUpdater = Class.create();
+Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
+ initialize: function(container, url, options) {
+ this.setOptions(options);
+ this.onComplete = this.options.onComplete;
+
+ this.frequency = (this.options.frequency || 2);
+ this.decay = (this.options.decay || 1);
+
+ this.updater = {};
+ this.container = container;
+ this.url = url;
+
+ this.start();
+ },
+
+ start: function() {
+ this.options.onComplete = this.updateComplete.bind(this);
+ this.onTimerEvent();
+ },
+
+ stop: function() {
+ this.updater.onComplete = undefined;
+ clearTimeout(this.timer);
+ (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+ },
+
+ updateComplete: function(request) {
+ if (this.options.decay) {
+ this.decay = (request.responseText == this.lastText ?
+ this.decay * this.options.decay : 1);
+
+ this.lastText = request.responseText;
+ }
+ this.timer = setTimeout(this.onTimerEvent.bind(this),
+ this.decay * this.frequency * 1000);
+ },
+
+ onTimerEvent: function() {
+ this.updater = new Ajax.Updater(this.container, this.url, this.options);
+ }
+});
+document.getElementsByClassName = function(className, parentElement) {
+ var children = ($(parentElement) || document.body).getElementsByTagName('*');
+ return $A(children).inject([], function(elements, child) {
+ if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
+ elements.push(child);
+ return elements;
+ });
+}
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Element) {
+ var Element = new Object();
+}
+
+Object.extend(Element, {
+ visible: function(element) {
+ return $(element).style.display != 'none';
+ },
+
+ toggle: function() {
+ for (var i = 0; i < arguments.length; i++) {
+ var element = $(arguments[i]);
+ Element[Element.visible(element) ? 'hide' : 'show'](element);
+ }
+ },
+
+ hide: function() {
+ for (var i = 0; i < arguments.length; i++) {
+ var element = $(arguments[i]);
+ element.style.display = 'none';
+ }
+ },
+
+ show: function() {
+ for (var i = 0; i < arguments.length; i++) {
+ var element = $(arguments[i]);
+ element.style.display = '';
+ }
+ },
+
+ remove: function(element) {
+ element = $(element);
+ element.parentNode.removeChild(element);
+ },
+
+ update: function(element, html) {
+ $(element).innerHTML = html.stripScripts();
+ setTimeout(function() {html.evalScripts()}, 10);
+ },
+
+ getHeight: function(element) {
+ element = $(element);
+ return element.offsetHeight;
+ },
+
+ classNames: function(element) {
+ return new Element.ClassNames(element);
+ },
+
+ hasClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ return Element.classNames(element).include(className);
+ },
+
+ addClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ return Element.classNames(element).add(className);
+ },
+
+ removeClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ return Element.classNames(element).remove(className);
+ },
+
+ // removes whitespace-only text node children
+ cleanWhitespace: function(element) {
+ element = $(element);
+ for (var i = 0; i < element.childNodes.length; i++) {
+ var node = element.childNodes[i];
+ if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+ Element.remove(node);
+ }
+ },
+
+ empty: function(element) {
+ return $(element).innerHTML.match(/^\s*$/);
+ },
+
+ scrollTo: function(element) {
+ element = $(element);
+ var x = element.x ? element.x : element.offsetLeft,
+ y = element.y ? element.y : element.offsetTop;
+ window.scrollTo(x, y);
+ },
+
+ getStyle: function(element, style) {
+ element = $(element);
+ var value = element.style[style.camelize()];
+ if (!value) {
+ if (document.defaultView && document.defaultView.getComputedStyle) {
+ var css = document.defaultView.getComputedStyle(element, null);
+ value = css ? css.getPropertyValue(style) : null;
+ } else if (element.currentStyle) {
+ value = element.currentStyle[style.camelize()];
+ }
+ }
+
+ if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
+ if (Element.getStyle(element, 'position') == 'static') value = 'auto';
+
+ return value == 'auto' ? null : value;
+ },
+
+ setStyle: function(element, style) {
+ element = $(element);
+ for (name in style)
+ element.style[name.camelize()] = style[name];
+ },
+
+ getDimensions: function(element) {
+ element = $(element);
+ if (Element.getStyle(element, 'display') != 'none')
+ return {width: element.offsetWidth, height: element.offsetHeight};
+
+ // All *Width and *Height properties give 0 on elements with display none,
+ // so enable the element temporarily
+ var els = element.style;
+ var originalVisibility = els.visibility;
+ var originalPosition = els.position;
+ els.visibility = 'hidden';
+ els.position = 'absolute';
+ els.display = '';
+ var originalWidth = element.clientWidth;
+ var originalHeight = element.clientHeight;
+ els.display = 'none';
+ els.position = originalPosition;
+ els.visibility = originalVisibility;
+ return {width: originalWidth, height: originalHeight};
+ },
+
+ makePositioned: function(element) {
+ element = $(element);
+ var pos = Element.getStyle(element, 'position');
+ if (pos == 'static' || !pos) {
+ element._madePositioned = true;
+ element.style.position = 'relative';
+ // Opera returns the offset relative to the positioning context, when an
+ // element is position relative but top and left have not been defined
+ if (window.opera) {
+ element.style.top = 0;
+ element.style.left = 0;
+ }
+ }
+ },
+
+ undoPositioned: function(element) {
+ element = $(element);
+ if (element._madePositioned) {
+ element._madePositioned = undefined;
+ element.style.position =
+ element.style.top =
+ element.style.left =
+ element.style.bottom =
+ element.style.right = '';
+ }
+ },
+
+ makeClipping: function(element) {
+ element = $(element);
+ if (element._overflow) return;
+ element._overflow = element.style.overflow;
+ if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
+ element.style.overflow = 'hidden';
+ },
+
+ undoClipping: function(element) {
+ element = $(element);
+ if (element._overflow) return;
+ element.style.overflow = element._overflow;
+ element._overflow = undefined;
+ }
+});
+
+var Toggle = new Object();
+Toggle.display = Element.toggle;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.Insertion = function(adjacency) {
+ this.adjacency = adjacency;
+}
+
+Abstract.Insertion.prototype = {
+ initialize: function(element, content) {
+ this.element = $(element);
+ this.content = content.stripScripts();
+
+ if (this.adjacency && this.element.insertAdjacentHTML) {
+ try {
+ this.element.insertAdjacentHTML(this.adjacency, this.content);
+ } catch (e) {
+ if (this.element.tagName.toLowerCase() == 'tbody' || this.element.tagName.toLowerCase() == 'tr') {
+ this.insertContent(this.contentFromAnonymousTable());
+ } else {
+ throw e;
+ }
+ }
+ } else {
+ this.range = this.element.ownerDocument.createRange();
+ if (this.initializeRange) this.initializeRange();
+ this.insertContent([this.range.createContextualFragment(this.content)]);
+ }
+
+ setTimeout(function() {content.evalScripts()}, 10);
+ },
+
+ contentFromAnonymousTable: function() {
+ var div = document.createElement('div');
+ div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
+ return $A(div.childNodes[0].childNodes[0].childNodes);
+ }
+}
+
+var Insertion = new Object();
+
+Insertion.Before = Class.create();
+Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
+ initializeRange: function() {
+ this.range.setStartBefore(this.element);
+ },
+
+ insertContent: function(fragments) {
+ fragments.each((function(fragment) {
+ this.element.parentNode.insertBefore(fragment, this.element);
+ }).bind(this));
+ }
+});
+
+Insertion.Top = Class.create();
+Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
+ initializeRange: function() {
+ this.range.selectNodeContents(this.element);
+ this.range.collapse(true);
+ },
+
+ insertContent: function(fragments) {
+ fragments.reverse(false).each((function(fragment) {
+ this.element.insertBefore(fragment, this.element.firstChild);
+ }).bind(this));
+ }
+});
+
+Insertion.Bottom = Class.create();
+Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
+ initializeRange: function() {
+ this.range.selectNodeContents(this.element);
+ this.range.collapse(this.element);
+ },
+
+ insertContent: function(fragments) {
+ fragments.each((function(fragment) {
+ this.element.appendChild(fragment);
+ }).bind(this));
+ }
+});
+
+Insertion.After = Class.create();
+Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
+ initializeRange: function() {
+ this.range.setStartAfter(this.element);
+ },
+
+ insertContent: function(fragments) {
+ fragments.each((function(fragment) {
+ this.element.parentNode.insertBefore(fragment,
+ this.element.nextSibling);
+ }).bind(this));
+ }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+ initialize: function(element) {
+ this.element = $(element);
+ },
+
+ _each: function(iterator) {
+ this.element.className.split(/\s+/).select(function(name) {
+ return name.length > 0;
+ })._each(iterator);
+ },
+
+ set: function(className) {
+ this.element.className = className;
+ },
+
+ add: function(classNameToAdd) {
+ if (this.include(classNameToAdd)) return;
+ this.set(this.toArray().concat(classNameToAdd).join(' '));
+ },
+
+ remove: function(classNameToRemove) {
+ if (!this.include(classNameToRemove)) return;
+ this.set(this.select(function(className) {
+ return className != classNameToRemove;
+ }).join(' '));
+ },
+
+ toString: function() {
+ return this.toArray().join(' ');
+ }
+}
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+var Field = {
+ clear: function() {
+ for (var i = 0; i < arguments.length; i++)
+ $(arguments[i]).value = '';
+ },
+
+ focus: function(element) {
+ $(element).focus();
+ },
+
+ present: function() {
+ for (var i = 0; i < arguments.length; i++)
+ if ($(arguments[i]).value == '') return false;
+ return true;
+ },
+
+ select: function(element) {
+ $(element).select();
+ },
+
+ activate: function(element) {
+ element = $(element);
+ element.focus();
+ if (element.select)
+ element.select();
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Form = {
+ serialize: function(form) {
+ var elements = Form.getElements($(form));
+ var queryComponents = new Array();
+
+ for (var i = 0; i < elements.length; i++) {
+ var queryComponent = Form.Element.serialize(elements[i]);
+ if (queryComponent)
+ queryComponents.push(queryComponent);
+ }
+
+ return queryComponents.join('&');
+ },
+
+ getElements: function(form) {
+ form = $(form);
+ var elements = new Array();
+
+ for (tagName in Form.Element.Serializers) {
+ var tagElements = form.getElementsByTagName(tagName);
+ for (var j = 0; j < tagElements.length; j++)
+ elements.push(tagElements[j]);
+ }
+ return elements;
+ },
+
+ getInputs: function(form, typeName, name) {
+ form = $(form);
+ var inputs = form.getElementsByTagName('input');
+
+ if (!typeName && !name)
+ return inputs;
+
+ var matchingInputs = new Array();
+ for (var i = 0; i < inputs.length; i++) {
+ var input = inputs[i];
+ if ((typeName && input.type != typeName) ||
+ (name && input.name != name))
+ continue;
+ matchingInputs.push(input);
+ }
+
+ return matchingInputs;
+ },
+
+ disable: function(form) {
+ var elements = Form.getElements(form);
+ for (var i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ element.blur();
+ element.disabled = 'true';
+ }
+ },
+
+ enable: function(form) {
+ var elements = Form.getElements(form);
+ for (var i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ element.disabled = '';
+ }
+ },
+
+ findFirstElement: function(form) {
+ return Form.getElements(form).find(function(element) {
+ return element.type != 'hidden' && !element.disabled &&
+ ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+ });
+ },
+
+ focusFirstElement: function(form) {
+ Field.activate(Form.findFirstElement(form));
+ },
+
+ reset: function(form) {
+ $(form).reset();
+ }
+}
+
+Form.Element = {
+ serialize: function(element) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ var parameter = Form.Element.Serializers[method](element);
+
+ if (parameter) {
+ var key = encodeURIComponent(parameter[0]);
+ if (key.length == 0) return;
+
+ if (parameter[1].constructor != Array)
+ parameter[1] = [parameter[1]];
+
+ return parameter[1].map(function(value) {
+ return key + '=' + encodeURIComponent(value);
+ }).join('&');
+ }
+ },
+
+ getValue: function(element) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ var parameter = Form.Element.Serializers[method](element);
+
+ if (parameter)
+ return parameter[1];
+ }
+}
+
+Form.Element.Serializers = {
+ input: function(element) {
+ switch (element.type.toLowerCase()) {
+ case 'submit':
+ case 'hidden':
+ case 'password':
+ case 'text':
+ return Form.Element.Serializers.textarea(element);
+ case 'checkbox':
+ case 'radio':
+ return Form.Element.Serializers.inputSelector(element);
+ }
+ return false;
+ },
+
+ inputSelector: function(element) {
+ if (element.checked)
+ return [element.name, element.value];
+ },
+
+ textarea: function(element) {
+ return [element.name, element.value];
+ },
+
+ select: function(element) {
+ return Form.Element.Serializers[element.type == 'select-one' ?
+ 'selectOne' : 'selectMany'](element);
+ },
+
+ selectOne: function(element) {
+ var value = '', opt, index = element.selectedIndex;
+ if (index >= 0) {
+ opt = element.options[index];
+ value = opt.value;
+ if (!value && !('value' in opt))
+ value = opt.text;
+ }
+ return [element.name, value];
+ },
+
+ selectMany: function(element) {
+ var value = new Array();
+ for (var i = 0; i < element.length; i++) {
+ var opt = element.options[i];
+ if (opt.selected) {
+ var optValue = opt.value;
+ if (!optValue && !('value' in opt))
+ optValue = opt.text;
+ value.push(optValue);
+ }
+ }
+ return [element.name, value];
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var $F = Form.Element.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = function() {}
+Abstract.TimedObserver.prototype = {
+ initialize: function(element, frequency, callback) {
+ this.frequency = frequency;
+ this.element = $(element);
+ this.callback = callback;
+
+ this.lastValue = this.getValue();
+ this.registerCallback();
+ },
+
+ registerCallback: function() {
+ setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+ },
+
+ onTimerEvent: function() {
+ var value = this.getValue();
+ if (this.lastValue != value) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ }
+}
+
+Form.Element.Observer = Class.create();
+Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.Observer = Class.create();
+Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = function() {}
+Abstract.EventObserver.prototype = {
+ initialize: function(element, callback) {
+ this.element = $(element);
+ this.callback = callback;
+
+ this.lastValue = this.getValue();
+ if (this.element.tagName.toLowerCase() == 'form')
+ this.registerFormCallbacks();
+ else
+ this.registerCallback(this.element);
+ },
+
+ onElementEvent: function() {
+ var value = this.getValue();
+ if (this.lastValue != value) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ },
+
+ registerFormCallbacks: function() {
+ var elements = Form.getElements(this.element);
+ for (var i = 0; i < elements.length; i++)
+ this.registerCallback(elements[i]);
+ },
+
+ registerCallback: function(element) {
+ if (element.type) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ Event.observe(element, 'click', this.onElementEvent.bind(this));
+ break;
+ case 'password':
+ case 'text':
+ case 'textarea':
+ case 'select-one':
+ case 'select-multiple':
+ Event.observe(element, 'change', this.onElementEvent.bind(this));
+ break;
+ }
+ }
+ }
+}
+
+Form.Element.EventObserver = Class.create();
+Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.EventObserver = Class.create();
+Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+if (!window.Event) {
+ var Event = new Object();
+}
+
+Object.extend(Event, {
+ KEY_BACKSPACE: 8,
+ KEY_TAB: 9,
+ KEY_RETURN: 13,
+ KEY_ESC: 27,
+ KEY_LEFT: 37,
+ KEY_UP: 38,
+ KEY_RIGHT: 39,
+ KEY_DOWN: 40,
+ KEY_DELETE: 46,
+
+ element: function(event) {
+ return event.target || event.srcElement;
+ },
+
+ isLeftClick: function(event) {
+ return (((event.which) && (event.which == 1)) ||
+ ((event.button) && (event.button == 1)));
+ },
+
+ pointerX: function(event) {
+ return event.pageX || (event.clientX +
+ (document.documentElement.scrollLeft || document.body.scrollLeft));
+ },
+
+ pointerY: function(event) {
+ return event.pageY || (event.clientY +
+ (document.documentElement.scrollTop || document.body.scrollTop));
+ },
+
+ stop: function(event) {
+ if (event.preventDefault) {
+ event.preventDefault();
+ event.stopPropagation();
+ } else {
+ event.returnValue = false;
+ event.cancelBubble = true;
+ }
+ },
+
+ // find the first node with the given tagName, starting from the
+ // node the event was triggered on; traverses the DOM upwards
+ findElement: function(event, tagName) {
+ var element = Event.element(event);
+ while (element.parentNode && (!element.tagName ||
+ (element.tagName.toUpperCase() != tagName.toUpperCase())))
+ element = element.parentNode;
+ return element;
+ },
+
+ observers: false,
+
+ _observeAndCache: function(element, name, observer, useCapture) {
+ if (!this.observers) this.observers = [];
+ if (element.addEventListener) {
+ this.observers.push([element, name, observer, useCapture]);
+ element.addEventListener(name, observer, useCapture);
+ } else if (element.attachEvent) {
+ this.observers.push([element, name, observer, useCapture]);
+ element.attachEvent('on' + name, observer);
+ }
+ },
+
+ unloadCache: function() {
+ if (!Event.observers) return;
+ for (var i = 0; i < Event.observers.length; i++) {
+ Event.stopObserving.apply(this, Event.observers[i]);
+ Event.observers[i][0] = null;
+ }
+ Event.observers = false;
+ },
+
+ observe: function(element, name, observer, useCapture) {
+ var element = $(element);
+ useCapture = useCapture || false;
+
+ if (name == 'keypress' &&
+ (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+ || element.attachEvent))
+ name = 'keydown';
+
+ this._observeAndCache(element, name, observer, useCapture);
+ },
+
+ stopObserving: function(element, name, observer, useCapture) {
+ var element = $(element);
+ useCapture = useCapture || false;
+
+ if (name == 'keypress' &&
+ (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+ || element.detachEvent))
+ name = 'keydown';
+
+ if (element.removeEventListener) {
+ element.removeEventListener(name, observer, useCapture);
+ } else if (element.detachEvent) {
+ element.detachEvent('on' + name, observer);
+ }
+ }
+});
+
+/* prevent memory leaks in IE */
+Event.observe(window, 'unload', Event.unloadCache, false);
+var Position = {
+ // set to true if needed, warning: firefox performance problems
+ // NOT neeeded for page scrolling, only if draggable contained in
+ // scrollable elements
+ includeScrollOffsets: false,
+
+ // must be called before calling withinIncludingScrolloffset, every time the
+ // page is scrolled
+ prepare: function() {
+ this.deltaX = window.pageXOffset
+ || document.documentElement.scrollLeft
+ || document.body.scrollLeft
+ || 0;
+ this.deltaY = window.pageYOffset
+ || document.documentElement.scrollTop
+ || document.body.scrollTop
+ || 0;
+ },
+
+ realOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.scrollTop || 0;
+ valueL += element.scrollLeft || 0;
+ element = element.parentNode;
+ } while (element);
+ return [valueL, valueT];
+ },
+
+ cumulativeOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ } while (element);
+ return [valueL, valueT];
+ },
+
+ positionedOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ if (element) {
+ p = Element.getStyle(element, 'position');
+ if (p == 'relative' || p == 'absolute') break;
+ }
+ } while (element);
+ return [valueL, valueT];
+ },
+
+ offsetParent: function(element) {
+ if (element.offsetParent) return element.offsetParent;
+ if (element == document.body) return element;
+
+ while ((element = element.parentNode) && element != document.body)
+ if (Element.getStyle(element, 'position') != 'static')
+ return element;
+
+ return document.body;
+ },
+
+ // caches x/y coordinate pair to use with overlap
+ within: function(element, x, y) {
+ if (this.includeScrollOffsets)
+ return this.withinIncludingScrolloffsets(element, x, y);
+ this.xcomp = x;
+ this.ycomp = y;
+ this.offset = this.cumulativeOffset(element);
+
+ return (y >= this.offset[1] &&
+ y < this.offset[1] + element.offsetHeight &&
+ x >= this.offset[0] &&
+ x < this.offset[0] + element.offsetWidth);
+ },
+
+ withinIncludingScrolloffsets: function(element, x, y) {
+ var offsetcache = this.realOffset(element);
+
+ this.xcomp = x + offsetcache[0] - this.deltaX;
+ this.ycomp = y + offsetcache[1] - this.deltaY;
+ this.offset = this.cumulativeOffset(element);
+
+ return (this.ycomp >= this.offset[1] &&
+ this.ycomp < this.offset[1] + element.offsetHeight &&
+ this.xcomp >= this.offset[0] &&
+ this.xcomp < this.offset[0] + element.offsetWidth);
+ },
+
+ // within must be called directly before
+ overlap: function(mode, element) {
+ if (!mode) return 0;
+ if (mode == 'vertical')
+ return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+ element.offsetHeight;
+ if (mode == 'horizontal')
+ return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+ element.offsetWidth;
+ },
+
+ clone: function(source, target) {
+ source = $(source);
+ target = $(target);
+ target.style.position = 'absolute';
+ var offsets = this.cumulativeOffset(source);
+ target.style.top = offsets[1] + 'px';
+ target.style.left = offsets[0] + 'px';
+ target.style.width = source.offsetWidth + 'px';
+ target.style.height = source.offsetHeight + 'px';
+ },
+
+ page: function(forElement) {
+ var valueT = 0, valueL = 0;
+
+ var element = forElement;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+
+ // Safari fix
+ if (element.offsetParent==document.body)
+ if (Element.getStyle(element,'position')=='absolute') break;
+
+ } while (element = element.offsetParent);
+
+ element = forElement;
+ do {
+ valueT -= element.scrollTop || 0;
+ valueL -= element.scrollLeft || 0;
+ } while (element = element.parentNode);
+
+ return [valueL, valueT];
+ },
+
+ clone: function(source, target) {
+ var options = Object.extend({
+ setLeft: true,
+ setTop: true,
+ setWidth: true,
+ setHeight: true,
+ offsetTop: 0,
+ offsetLeft: 0
+ }, arguments[2] || {})
+
+ // find page position of source
+ source = $(source);
+ var p = Position.page(source);
+
+ // find coordinate system to use
+ target = $(target);
+ var delta = [0, 0];
+ var parent = null;
+ // delta [0,0] will do fine with position: fixed elements,
+ // position:absolute needs offsetParent deltas
+ if (Element.getStyle(target,'position') == 'absolute') {
+ parent = Position.offsetParent(target);
+ delta = Position.page(parent);
+ }
+
+ // correct by body offsets (fixes Safari)
+ if (parent == document.body) {
+ delta[0] -= document.body.offsetLeft;
+ delta[1] -= document.body.offsetTop;
+ }
+
+ // set position
+ if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
+ if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
+ if(options.setWidth) target.style.width = source.offsetWidth + 'px';
+ if(options.setHeight) target.style.height = source.offsetHeight + 'px';
+ },
+
+ absolutize: function(element) {
+ element = $(element);
+ if (element.style.position == 'absolute') return;
+ Position.prepare();
+
+ var offsets = Position.positionedOffset(element);
+ var top = offsets[1];
+ var left = offsets[0];
+ var width = element.clientWidth;
+ var height = element.clientHeight;
+
+ element._originalLeft = left - parseFloat(element.style.left || 0);
+ element._originalTop = top - parseFloat(element.style.top || 0);
+ element._originalWidth = element.style.width;
+ element._originalHeight = element.style.height;
+
+ element.style.position = 'absolute';
+ element.style.top = top + 'px';;
+ element.style.left = left + 'px';;
+ element.style.width = width + 'px';;
+ element.style.height = height + 'px';;
+ },
+
+ relativize: function(element) {
+ element = $(element);
+ if (element.style.position == 'relative') return;
+ Position.prepare();
+
+ element.style.position = 'relative';
+ var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
+ var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+ element.style.top = top + 'px';
+ element.style.left = left + 'px';
+ element.style.height = element._originalHeight;
+ element.style.width = element._originalWidth;
+ }
+}
+
+// Safari returns margins on body which is incorrect if the child is absolutely
+// positioned. For performance reasons, redefine Position.cumulativeOffset for
+// KHTML/WebKit only.
+if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
+ Position.cumulativeOffset = function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ if (element.offsetParent == document.body)
+ if (Element.getStyle(element, 'position') == 'absolute') break;
+
+ element = element.offsetParent;
+ } while (element);
+
+ return [valueL, valueT];
+ }
+} \ No newline at end of file
diff --git a/o3d/samples/trends/assets/clouds.jpg b/o3d/samples/trends/assets/clouds.jpg
new file mode 100644
index 0000000..8767681
--- /dev/null
+++ b/o3d/samples/trends/assets/clouds.jpg
Binary files differ
diff --git a/o3d/samples/trends/assets/earth-large-with-ocean-mask.png b/o3d/samples/trends/assets/earth-large-with-ocean-mask.png
new file mode 100644
index 0000000..850d0ff
--- /dev/null
+++ b/o3d/samples/trends/assets/earth-large-with-ocean-mask.png
Binary files differ
diff --git a/o3d/samples/trends/assets/earth-large.jpg b/o3d/samples/trends/assets/earth-large.jpg
new file mode 100644
index 0000000..ce797dc
--- /dev/null
+++ b/o3d/samples/trends/assets/earth-large.jpg
Binary files differ
diff --git a/o3d/samples/trends/assets/earth.jpg b/o3d/samples/trends/assets/earth.jpg
new file mode 100644
index 0000000..f04b09d
--- /dev/null
+++ b/o3d/samples/trends/assets/earth.jpg
Binary files differ
diff --git a/o3d/samples/trends/assets/energy.png b/o3d/samples/trends/assets/energy.png
new file mode 100644
index 0000000..576ddbb
--- /dev/null
+++ b/o3d/samples/trends/assets/energy.png
Binary files differ
diff --git a/o3d/samples/trends/assets/moon.jpg b/o3d/samples/trends/assets/moon.jpg
new file mode 100644
index 0000000..f916c70
--- /dev/null
+++ b/o3d/samples/trends/assets/moon.jpg
Binary files differ
diff --git a/o3d/samples/trends/assets/night-large.jpg b/o3d/samples/trends/assets/night-large.jpg
new file mode 100644
index 0000000..4979d6e
--- /dev/null
+++ b/o3d/samples/trends/assets/night-large.jpg
Binary files differ
diff --git a/o3d/samples/trends/assets/night.jpg b/o3d/samples/trends/assets/night.jpg
new file mode 100644
index 0000000..2bfb99e
--- /dev/null
+++ b/o3d/samples/trends/assets/night.jpg
Binary files differ
diff --git a/o3d/samples/trends/trends.html b/o3d/samples/trends/trends.html
new file mode 100644
index 0000000..d7ca2b2
--- /dev/null
+++ b/o3d/samples/trends/trends.html
@@ -0,0 +1,866 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+TODO:
+ O Set Sun to correct location for time.
+ O Put in moon
+ O Put in Google satellite
+ O Put in Star Shader that uses star data from a texture
+ O Add Halo
+ O Add Sun Model
+ O with glowing rays.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<title>
+Google Trends Visualizer
+</title>
+<style>
+ html, body {
+ border: 0;
+ margin: 0;
+ height: 100%;
+ height: 100%;
+ text-align: center;
+ }
+</style>
+</head>
+<body onload="init();" onunload="uninit();">
+<script type="text/javascript" src="../o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.quaternions');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.arcball');
+o3djs.require('o3djs.io');
+
+var g = {
+ EARTH_RADIUS: 25,
+ ENERGY_WIDTH: 0.5,
+ ENERGY_HEIGHT: 10
+};
+
+g.camera = {
+ eye: [0, 0, 75],
+ target: [0, 0, 0]
+};
+
+var g_finished = false; // for selenium.
+var dragging = false;
+
+function startDragging(e) {
+ g.lastRot = g.thisRot;
+ g.aball.click([e.x, e.y]);
+ dragging = true;
+}
+
+function drag(e) {
+ if (dragging) {
+ var rotationQuat = g.aball.drag([e.x, e.y]);
+ var rot_mat = g.quaternions.quaternionToRotation(rotationQuat);
+ g.thisRot = g.math.matrix4.mul(g.lastRot, rot_mat);
+
+ var m = g.root.localMatrix;
+ g.math.matrix4.setUpper3x3(m, g.thisRot);
+ g.root.localMatrix = m;
+ }
+}
+
+function stopDragging(e) {
+ dragging = false;
+}
+
+function updateViewFromCamera() {
+ var target = g.camera.target;
+ var eye = g.camera.eye;
+ var up = [0, 1, 0];
+ g.viewInfo.drawContext.view = g.math.matrix4.lookAt(eye, target, up);
+ g.eyePosParam.value = eye;
+}
+
+function scrollMe(e) {
+ if (e.deltaY > 0) {
+ g.camera.eye[0] *= 11 / 12;
+ g.camera.eye[1] *= 11 / 12;
+ g.camera.eye[2] *= 11 / 12;
+
+ } else {
+ g.camera.eye[0] *= (1 + 1 / 12);
+ g.camera.eye[1] *= (1 + 1 / 12);
+ g.camera.eye[2] *= (1 + 1 / 12);
+ }
+ updateViewFromCamera();
+}
+
+function getURL(path) {
+ var base = window.location.href;
+ var index = base.lastIndexOf('/');
+ base = base.substring(0, index + 1);
+ return base + path;
+}
+
+function setClientSize() {
+ var newWidth = g.client.width;
+ var newHeight = g.client.height;
+
+ if (newWidth != g.o3dWidth || newHeight != g.o3dHeight) {
+ g.o3dWidth = newWidth;
+ g.o3dHeight = newHeight;
+
+ // Create a perspective projection matrix
+ g.viewInfo.drawContext.projection = g.math.matrix4.perspective(
+ g.math.degToRad(45), g.o3dWidth / g.o3dHeight, 0.1, 5000);
+
+ // Sets a new area size for arcball.
+ g.aball.setAreaSize(g.o3dWidth, g.o3dHeight);
+ }
+}
+
+function onRender() {
+ setClientSize();
+}
+
+function createEnergyShape(pack, material, width, height) {
+ var vertexInfo = o3djs.primitives.createVertexInfo();
+ var positionStream = vertexInfo.addStream(
+ 3, g.o3d.Stream.POSITION);
+ var normalStream = vertexInfo.addStream(
+ 3, g.o3d.Stream.NORMAL);
+ var colorStream = vertexInfo.addStream(
+ 4, g.o3d.Stream.COLOR);
+ var texCoordStream = vertexInfo.addStream(
+ 2, g.o3d.Stream.TEXCOORD, 0);
+
+ var vScale = 1;
+ positionStream.addElement(width * -0.5, height, 0);
+ normalStream.addElement(0, 0, 1);
+ colorStream.addElement(1, 1, 1, 0);
+ texCoordStream.addElement(0, 0);
+ positionStream.addElement(width * 0.5, height, 0);
+ normalStream.addElement(0, 0, 1);
+ colorStream.addElement(1, 1, 1, 0);
+ texCoordStream.addElement(1, 0);
+ positionStream.addElement(width * -0.5, 0, 0);
+ normalStream.addElement(0, 0, 1);
+ colorStream.addElement(1, 1, 1, 1);
+ texCoordStream.addElement(0, vScale);
+ positionStream.addElement(width * 0.5, 0, 0);
+ normalStream.addElement(0, 0, 1);
+ colorStream.addElement(1, 1, 1, 1);
+ texCoordStream.addElement(1, vScale);
+ positionStream.addElement(0, height, width * -0.5);
+ normalStream.addElement(1, 0, 0);
+ colorStream.addElement(1, 1, 1, 0);
+ texCoordStream.addElement(0, 0);
+ positionStream.addElement(0, height, width * 0.5);
+ normalStream.addElement(1, 0, 0);
+ colorStream.addElement(1, 1, 1, 0);
+ texCoordStream.addElement(1, 0);
+ positionStream.addElement(0, 0, width * -0.5);
+ normalStream.addElement(1, 0, 0);
+ colorStream.addElement(1, 1, 1, 1);
+ texCoordStream.addElement(0, vScale);
+ positionStream.addElement(0, 0, width * 0.5);
+ normalStream.addElement(1, 0, 0);
+ colorStream.addElement(1, 1, 1, 1);
+ texCoordStream.addElement(1, vScale);
+
+ vertexInfo.addTriangle(0, 1, 2);
+ vertexInfo.addTriangle(1, 2, 3);
+ vertexInfo.addTriangle(4, 5, 6);
+ vertexInfo.addTriangle(5, 6, 7);
+
+ return vertexInfo.createShape(pack, material);
+}
+
+// A geo is a float where the integer part is in degrees and the fractional
+// part is in 60ths
+function geoToRad(geo) {
+ var sign = geo >= 0 ? 1 : -1;
+ geo = Math.abs(geo);
+ var integerPart = Math.floor(geo);
+ var fractionalPart = (geo % 1) * 100;
+ fractionalPart = fractionalPart / 60;
+ return g.math.degToRad(integerPart + fractionalPart);
+}
+
+function addEnergyShard(latitude, longitude, energy, height, color) {
+ var transform = g.pack.createObject('Transform');
+ transform.rotateZ(geoToRad(latitude));
+ transform.rotateY(geoToRad(-longitude));
+ transform.rotateZ(g.math.degToRad(90));
+ transform.translate(0, g.EARTH_RADIUS, 0);
+ transform.parent = g.root;
+ transform.addShape(g.energyShape);
+ transform.createParam('colorMult', 'ParamFloat4').value = color;
+ transform.createParam('offset', 'ParamFloat').value = Math.random();
+ return transform;
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+/**
+ * Initializes o3d
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ var path = window.location.href;
+ var index = path.lastIndexOf('/');
+
+ g.o3dElement = clientElements[0];
+ g.o3d = g.o3dElement.o3d;
+ g.math = o3djs.math;
+ g.quaternions = o3djs.quaternions;
+ g.client = g.o3dElement.client;
+
+ g.pack = g.client.createPack();
+
+ // Create the render graph for a view.
+ g.viewInfo = o3djs.rendergraph.createBasicView(
+ g.pack,
+ g.client.root,
+ g.client.renderGraphRoot);
+
+ // Set the background color to black.
+ g.viewInfo.clearBuffer.clearColor = [0, 0, 0, 0];
+
+ // Set states for shards.
+ g.viewInfo.zOrderedState.getStateParam('CullMode').value =
+ g.o3d.State.CULL_NONE;
+ g.viewInfo.zOrderedState.getStateParam('DestinationBlendFunction').value =
+ g.o3d.State.BLENDFUNC_ONE;
+ g.viewInfo.zOrderedState.getStateParam('ZWriteEnable').value = false;
+
+ g.viewInfo.performanceDrawPass.sortMethod = g.o3d.DrawList.BY_PRIORITY;
+
+ g.lastRot = g.math.matrix4.identity();
+ g.thisRot = g.math.matrix4.identity();
+
+ var root = g.client.root;
+
+ // Create a param for the sun and eye positions that we can bind
+ // to auto update a bunch of materials.
+ g.globalParams = g.pack.createObject('ParamObject');
+ g.sunPosParam = g.globalParams.createParam('sunPos', 'ParamFloat3');
+ g.sunPosParam.value = [1000, 200, 100];
+ g.eyePosParam = g.globalParams.createParam('eyePos', 'ParamFloat3');
+
+ updateViewFromCamera();
+
+ g.aball = o3djs.arcball.create(100, 100);
+ setClientSize();
+
+ g.client.setRenderCallback(onRender);
+
+
+ // Create Materials.
+ var effectNames = [
+ "noTexture",
+ "dayOnly",
+ "nightAndDay",
+ "mask",
+ "energy",
+ "atmosphere"
+ ];
+ g.materials = [];
+ for (var ii = 0; ii < effectNames.length; ++ii) {
+ var effectName = effectNames[ii];
+ var effect = g.pack.createObject('Effect');
+ effect.loadFromFXString(document.getElementById(effectName).value);
+
+ // Create a Material for the effect.
+ var material = g.pack.createObject('Material');
+
+ // Apply our effect to this material. The effect tells the 3D hardware
+ // which shader to use.
+ material.effect = effect;
+
+ // Set the material's drawList
+ material.drawList = g.viewInfo.performanceDrawList;
+
+ // This will create the effects's params on the material.
+ effect.createUniformParameters(material);
+
+ // Bind the sun position to a global value so we can easily change it
+ // globally.
+ var sunParam = material.getParam('sunPos');
+ if (sunParam) {
+ sunParam.bind(g.sunPosParam);
+ }
+
+ // Save off the material.
+ g.materials.push(material);
+ }
+ g.noTextureMaterial = g.materials[0];
+ g.dayOnlyMaterial = g.materials[1];
+ g.nightAndDayMaterial = g.materials[2];
+ g.maskMaterial = g.materials[3];
+ g.energyMaterial = g.materials[4];
+ g.energyMaterial.drawList = g.viewInfo.zOrderedDrawList;
+ g.atmosphereMaterial = g.materials[5];
+
+ // create samplers
+ g.samplers = [];
+ for (var ii = 0; ii < 4; ++ii) {
+ var sampler = g.pack.createObject('Sampler');
+ g.samplers[ii] = sampler;
+ }
+
+ g.daySampler = g.samplers[0];
+ g.nightSampler = g.samplers[1];
+ g.maskSampler = g.samplers[2];
+ g.energySampler = g.samplers[3];
+
+ // set the material samplers.
+ g.dayOnlyMaterial.getParam('daySampler').value = g.daySampler;
+ g.nightAndDayMaterial.getParam('daySampler').value = g.daySampler;
+ g.nightAndDayMaterial.getParam('nightSampler').value = g.nightSampler;
+ g.maskMaterial.getParam('daySampler').value = g.daySampler;
+ g.maskMaterial.getParam('maskSampler').value = g.maskSampler;
+ g.maskMaterial.getParam('nightSampler').value = g.nightSampler;
+ g.energyMaterial.getParam('energySampler').value = g.energySampler;
+
+ // Create energy texture(s)
+ {
+ var dots = [ 0, 1, 0, 1, 0, 0, 1, 0,
+ 1, 0, 0, 1, 0, 1, 0, 0,
+ 1, 0, 1, 0, 0, 0, 1, 0,
+ 0, 1, 0, 1, 0, 0, 1, 0 ];
+ var texture = g.pack.createTexture2D(3,
+ dots.length,
+ g.o3d.Texture.XRGB8,
+ 1,
+ false);
+ var pixels = [];
+ for (var yy = 0; yy < dots.length; ++yy) {
+ for (var xx = 0; xx < 3; ++xx) {
+ var pixelOffset = (yy * 3 + xx) * 3;
+ var color = (xx == 1) ? dots[yy] : 0;
+ for (var cc = 0; cc < 3; ++cc) {
+ pixels[pixelOffset + cc] = color;
+ }
+ }
+ }
+ texture.set(0, pixels);
+ g.energySampler.texture = texture;
+ }
+
+ // Setup counters for shard animation.
+ g.shardCounter = g.pack.createObject('SecondCounter');
+ g.shardCounter.multiplier = 0.1;
+ g.energyMaterial.getParam('time').bind(
+ g.shardCounter.getParam('count'));
+
+ // Setup counters to fade in textures.
+ g.flatToDayCounter = g.pack.createObject('SecondCounter');
+ g.flatToDayCounter.end = 1;
+ g.flatToDayCounter.multiplier = 0.5;
+ g.flatToDayCounter.countMode = g.o3d.Counter.ONCE;
+ g.flatToDayCounter.running = false;
+ g.flatToDayCounter.addCallback(1, loadNightTexture);
+ g.dayOnlyMaterial.getParam('mix').bind(
+ g.flatToDayCounter.getParam('count'));
+
+ g.dayOnlyToNightCounter = g.pack.createObject('SecondCounter');
+ g.dayOnlyToNightCounter.end = 1;
+ g.dayOnlyToNightCounter.multiplier = 0.5;
+ g.dayOnlyToNightCounter.countMode = g.o3d.Counter.ONCE;
+ g.dayOnlyToNightCounter.running = false;
+ g.dayOnlyToNightCounter.addCallback(1, loadMaskTexture);
+ g.nightAndDayMaterial.getParam('mix').bind(
+ g.dayOnlyToNightCounter.getParam('count'));
+
+ g.noMaskToMaskCounter = g.pack.createObject('SecondCounter');
+ g.noMaskToMaskCounter.end = 1;
+ g.noMaskToMaskCounter.multiplier = 0.5;
+ g.noMaskToMaskCounter.countMode = g.o3d.Counter.ONCE;
+ g.noMaskToMaskCounter.running = false;
+ g.maskMaterial.getParam('mix').bind(
+ g.noMaskToMaskCounter.getParam('count'));
+
+ // Create a sphere at the origin for the earth.
+ var earth = o3djs.primitives.createSphere(g.pack,
+ g.noTextureMaterial,
+ 25,
+ 50,
+ 50);
+
+ // Get a the element so we can set its material later.
+ g.earthPrimitive = earth.elements[0];
+ g.atmosphereState = g.pack.createObject('State');
+ g.atmosphereState.getStateParam('AlphaBlendEnable').value = true;
+ g.atmosphereState.getStateParam('SourceBlendFunction').value =
+ g.o3d.State.BLENDFUNC_SOURCE_ALPHA;
+ g.atmosphereState.getStateParam('DestinationBlendFunction').value =
+ g.o3d.State.BLENDFUNC_INVERSE_SOURCE_ALPHA;
+ g.atmosphereState.getStateParam('ZWriteEnable').value = false;
+ g.atmosphereMaterial.state = g.atmosphereState;
+
+ g.root = g.pack.createObject('Transform');
+ g.root.parent = g.client.root;
+ g.earth = g.pack.createObject('Transform');
+ g.earth.addShape(earth);
+ g.earth.parent = g.root;
+
+ // Create a sphere at the origin for the atmosphere.
+ var atmosphere = o3djs.primitives.createSphere(g.pack,
+ g.atmosphereMaterial,
+ 26,
+ 50,
+ 50);
+ g.atmospherePrimitive = atmosphere.elements[0];
+ g.atmospherePrimitive.priority = 1;
+ g.atmosphere = g.pack.createObject('Transform');
+ g.atmosphere.addShape(atmosphere);
+ g.atmosphere.parent = g.root;
+
+ g.energyShape = createEnergyShape(g.pack,
+ g.energyMaterial,
+ g.ENERGY_WIDTH,
+ g.ENERGY_HEIGHT);
+
+ addEnergyShard(0, 0, 1, 1, [1, 1, 1, 1]);
+
+ // Honolulu, Hawaii, 21, 18, 157, 50
+ addEnergyShard(21.18, 157.50, 1, 1, [0, 1, 0, 1]);
+ // San Francisco, Calif. 37 47 122 26
+ addEnergyShard(37.47, 122.26, 1, 1, [1, 0.5, 0.5, 1]);
+
+ for (var ii = 0; ii < 24; ++ii) {
+ var longitude = Math.random() * 360;
+ var latitude = Math.random() * 360 - 180;
+ var color = [ Math.random() * 0.5 + 0.2,
+ Math.random() * 0.5 + 0.2,
+ Math.random() * 0.5 + 0.2,
+ 1 ];
+ for (var jj = 0; jj < 24; ++jj) {
+ addEnergyShard(latitude + (Math.random() - 0.5) * 10,
+ longitude + (Math.random() - 0.5) * 10,
+ 1,
+ 1,
+ color);
+ }
+ }
+
+ o3djs.event.addEventListener(g.o3dElement, 'mousedown', startDragging);
+ o3djs.event.addEventListener(g.o3dElement, 'mousemove', drag);
+ o3djs.event.addEventListener(g.o3dElement, 'mouseup', stopDragging);
+ o3djs.event.addEventListener(g.o3dElement, 'wheel', scrollMe);
+
+ loadDayTexture();
+}
+
+function loadTexture(path, callback) {
+ var url = getURL(path);
+ o3djs.io.loadTexture(g.pack, url, function(texture, exception) {
+ if (exception) {
+ alert(exception);
+ } else {
+ callback(texture);
+ }
+ });
+}
+
+function loadDayTexture() {
+ loadTexture('assets/earth.jpg', function(texture) {
+ g.daySampler.texture = texture;
+ g.earthPrimitive.material = g.dayOnlyMaterial;
+ g.flatToDayCounter.running = true;
+ });
+}
+
+function loadNightTexture() {
+ loadTexture('assets/night.jpg', function(texture) {
+ g.nightSampler.texture = texture;
+ g.earthPrimitive.material = g.nightAndDayMaterial;
+ g.dayOnlyToNightCounter.running = true;
+ });
+}
+
+function loadMaskTexture() {
+ loadTexture('assets/earth-large-with-ocean-mask.png', function(texture) {
+ g.maskSampler.texture = texture;
+ g.earthPrimitive.material = g.maskMaterial;
+ g.noMaskToMaskCounter.running = true;
+ g_finished = true; // for selenium
+ });
+}
+
+function uninit() {
+ // TODO: We should clean up any counters that have callbacks here.
+ if (g.client) {
+ g.client.cleanup();
+ }
+}
+
+</script>
+<!--<h1>
+Google Trends Visualizer.
+</h1>-->
+<div id="o3d" style="width:100%; height: 100%;"></div>
+<div style="display:none">
+<textarea id="noTexture" name="fx" cols="80" rows="20">
+float4x4 worldViewProjection : WorldViewProjection;
+float4x4 world : World;
+float4x4 view : View;
+
+float3 sunPos;
+
+struct a2v {
+ float4 pos : POSITION;
+ float3 normal : NORMAL;
+};
+
+struct v2f {
+ float4 pos : POSITION;
+ float3 normal : TEXCOORD0;
+ float3 sun : TEXCOORD1;
+ float3 view : TEXCOORD2;
+};
+
+v2f vsMain(a2v IN) {
+ v2f OUT;
+
+ OUT.pos = mul(IN.pos, worldViewProjection);
+ OUT.normal = mul(float4(IN.normal, 0), world).xyz;
+ float3 worldPos = mul(IN.pos, world).xyz;
+ OUT.sun = sunPos - worldPos;
+ OUT.view = (view[3] - worldPos);
+
+ return OUT;
+}
+
+float4 psMain(v2f IN): COLOR {
+ float3 norm = normalize(IN.normal);
+ float3 sun = normalize(IN.sun);
+ float light = dot(norm, sun);
+ float lightSign = sign(light);
+ float dayNight = 1 - sqrt(light);
+ dayNight = dayNight * dayNight;
+ dayNight = (1 - dayNight) * lightSign;
+ dayNight = clamp(dayNight, 0, 1);
+ float3 view = normalize(IN.view);
+ float3 r = normalize(reflect(norm, sun));
+ float4 litR = lit(light, dot(r, view), 0.0).y;
+ float3 day = float3(0.5, 0.5, 1.0) * litR.y + float3(1,1,1) * litR.z;
+ float3 night = float3(0.2, 0.2, 0.5);
+ return float4(lerp(night, day, dayNight),1);
+}
+
+// #o3d VertexShaderEntryPoint vsMain
+// #o3d PixelShaderEntryPoint psMain
+// #o3d MatrixLoadOrder RowMajor
+</textarea>
+<textarea id="dayOnly" name="fx" cols="80" rows="20">
+float4x4 worldViewProjection : WorldViewProjection;
+float4x4 world : World;
+float4x4 view : View;
+
+float3 sunPos;
+float mix;
+
+sampler2D daySampler;
+
+struct a2v {
+ float4 pos : POSITION;
+ float3 normal : NORMAL;
+ float2 uv : TEXCOORD0;
+};
+
+struct v2f {
+ float4 pos : POSITION;
+ float2 uv : TEXCOORD0;
+ float3 normal : TEXCOORD1;
+ float3 sun : TEXCOORD2;
+ float3 view : TEXCOORD3;
+};
+
+v2f vsMain(a2v IN) {
+ v2f OUT;
+
+ OUT.pos = mul(IN.pos, worldViewProjection);
+ OUT.uv = IN.uv;
+ OUT.normal = mul(float4(IN.normal, 0), world).xyz;
+ float3 worldPos = mul(IN.pos, world).xyz;
+ OUT.sun = sunPos - worldPos;
+ OUT.view = (view[3] - worldPos);
+
+ return OUT;
+}
+
+float4 psMain(v2f IN): COLOR {
+ float3 norm = normalize(IN.normal);
+ float3 sun = normalize(IN.sun);
+ float light = dot(norm, sun);
+ float lightSign = sign(light);
+ float dayNight = 1 - sqrt(light);
+ dayNight = dayNight * dayNight;
+ dayNight = (1 - dayNight) * lightSign;
+ dayNight = clamp(dayNight, 0, 1);
+ float3 view = normalize(IN.view);
+ float3 r = normalize(reflect(norm, sun));
+ float4 litR = lit(light, dot(r, view), 0.0).y;
+ float3 earth = tex2D(daySampler, IN.uv).xyz;
+ float3 day = lerp(float3(0.5, 0.5, 1.0), earth, mix);
+ day = day * litR.y + float3(1,1,1) * litR.z;
+ float3 night = lerp(float3(0.2, 0.2, 0.5), earth * 0.3, mix);
+ return float4(lerp(night, day, dayNight),1);
+}
+
+// #o3d VertexShaderEntryPoint vsMain
+// #o3d PixelShaderEntryPoint psMain
+// #o3d MatrixLoadOrder RowMajor
+</textarea>
+<textarea id="nightAndDay" name="fx" cols="80" rows="20">
+float4x4 worldViewProjection : WorldViewProjection;
+float4x4 world : World;
+float4x4 view : View;
+
+float3 sunPos;
+float mix;
+
+sampler2D daySampler;
+sampler2D nightSampler;
+
+struct a2v {
+ float4 pos : POSITION;
+ float3 normal : NORMAL;
+ float2 uv : TEXCOORD0;
+};
+
+struct v2f {
+ float4 pos : POSITION;
+ float2 uv : TEXCOORD0;
+ float3 normal : TEXCOORD1;
+ float3 sun : TEXCOORD2;
+ float3 view : TEXCOORD3;
+};
+
+v2f vsMain(a2v IN) {
+ v2f OUT;
+
+ OUT.pos = mul(IN.pos, worldViewProjection);
+ OUT.uv = IN.uv;
+ OUT.normal = mul(float4(IN.normal, 0), world).xyz;
+ float3 worldPos = mul(IN.pos, world).xyz;
+ OUT.sun = sunPos - worldPos;
+ OUT.view = (view[3] - worldPos);
+
+ return OUT;
+}
+
+float4 psMain(v2f IN): COLOR {
+ float3 norm = normalize(IN.normal);
+ float3 sun = normalize(IN.sun);
+ float light = dot(norm, sun);
+ float lightSign = sign(light);
+ float dayNight = 1 - sqrt(light);
+ dayNight = dayNight * dayNight;
+ dayNight = (1 - dayNight) * lightSign;
+ dayNight = clamp(dayNight, 0, 1);
+ float3 view = normalize(IN.view);
+ float3 r = normalize(reflect(norm, sun));
+ float4 litR = lit(light, dot(r, view), 0.0).y;
+ float3 earth = tex2D(daySampler, IN.uv).xyz;
+ float3 day = tex2D(daySampler, IN.uv).xyz;
+ float3 night = lerp(day * 0.3, tex2D(nightSampler, IN.uv).xyz, mix);
+ day = day * litR.y + float3(1,1,1) * litR.z;
+ return float4(lerp(night, day, dayNight),1);
+}
+
+// #o3d VertexShaderEntryPoint vsMain
+// #o3d PixelShaderEntryPoint psMain
+// #o3d MatrixLoadOrder RowMajor
+</textarea>
+<!--
+This shader renders the ocean different then the non-ocean using a mask
+stored in the alpha channel of the maskSampler
+-->
+<textarea id="mask" name="fx" cols="80" rows="20">
+float4x4 worldViewProjection : WorldViewProjection;
+float4x4 world : World;
+float4x4 view : View;
+
+float3 sunPos;
+float mix;
+
+sampler2D daySampler;
+sampler2D nightSampler;
+sampler2D maskSampler;
+
+struct a2v {
+ float4 pos : POSITION;
+ float3 normal : NORMAL;
+ float2 uv : TEXCOORD0;
+};
+
+struct v2f {
+ float4 pos : POSITION;
+ float2 uv : TEXCOORD0;
+ float3 normal : TEXCOORD1;
+ float3 sun : TEXCOORD2;
+ float3 view : TEXCOORD3;
+};
+
+v2f vsMain(a2v IN) {
+ v2f OUT;
+
+ OUT.pos = mul(IN.pos, worldViewProjection);
+ OUT.uv = IN.uv;
+ OUT.normal = mul(float4(IN.normal, 0), world).xyz;
+ float3 worldPos = mul(IN.pos, world).xyz;
+ OUT.sun = sunPos - worldPos;
+ OUT.view = (view[3] - worldPos);
+
+ return OUT;
+}
+
+float4 psMain(v2f IN): COLOR {
+ float3 norm = normalize(IN.normal);
+ float3 sun = normalize(IN.sun);
+ float light = dot(norm, sun);
+ float lightSign = sign(light);
+ float dayNight = 1 - sqrt(light);
+ dayNight = dayNight * dayNight;
+ dayNight = (1 - dayNight) * lightSign;
+ dayNight = clamp(dayNight, 0, 1);
+ float3 view = normalize(IN.view);
+ float3 r = normalize(reflect(norm, sun));
+ float4 litR = lit(light, dot(r, view), 0.0).y;
+ float3 earth = tex2D(daySampler, IN.uv).xyz;
+ float4 mask = tex2D(maskSampler, IN.uv);
+ float3 day = lerp(tex2D(daySampler, IN.uv).xyz,
+ mask.xyz, mix);
+ float3 night = tex2D(nightSampler, IN.uv).xyz;
+ day = day * litR.y + float3(1,1,1) * litR.z * (1 - mask.w * mix);
+
+ float3 color = lerp(night, day, dayNight);
+ return float4(color,1);
+}
+
+// #o3d VertexShaderEntryPoint vsMain
+// #o3d PixelShaderEntryPoint psMain
+// #o3d MatrixLoadOrder RowMajor
+</textarea>
+<textarea id="energy" name="fx" cols="80" rows="20">
+float4x4 worldViewProjection : WorldViewProjection;
+
+// time is used to scroll the UV coords
+float time;
+
+// offset is used to allow each shard to scroll over a different part of the
+// texture.
+float offset;
+
+// Sets the color of the shard.
+float4 colorMult;
+
+// Provides the dots on the shard.
+sampler2D energySampler;
+
+struct a2v {
+ float4 pos : POSITION;
+ float3 norm : NORMAL;
+ float4 color : COLOR;
+ float2 uv : TEXCOORD0;
+};
+
+struct v2f {
+ float4 pos : POSITION;
+ float2 uv : TEXCOORD0;
+ float4 color : TEXCOORD1;
+};
+
+v2f vsMain(a2v IN) {
+ v2f OUT;
+
+ OUT.pos = mul(IN.pos, worldViewProjection);
+ OUT.uv = float2(IN.uv.x, IN.uv.y + time + offset);
+ OUT.color = IN.color;
+
+ return OUT;
+}
+
+float4 psMain(v2f IN): COLOR {
+ return float4(tex2D(energySampler, IN.uv)) * IN.color * colorMult;
+}
+
+// #o3d VertexShaderEntryPoint vsMain
+// #o3d PixelShaderEntryPoint psMain
+// #o3d MatrixLoadOrder RowMajor
+</textarea>
+<textarea id="atmosphere" name="fx" cols="80" rows="20">
+float4x4 worldViewProjection : WorldViewProjection;
+float4x4 worldView : WorldView;
+float3 sunPos;
+float3 eyePos;
+
+struct a2v {
+ float4 pos : POSITION;
+ float3 norm : NORMAL;
+};
+
+struct v2f {
+ float4 pos : POSITION;
+ float3 normal : TEXCOORD0;
+};
+
+v2f vsMain(a2v IN) {
+ v2f OUT;
+
+ OUT.pos = mul(IN.pos, worldViewProjection);
+ OUT.normal = normalize(mul(float4(IN.norm,0), worldView).xyz);
+ return OUT;
+}
+
+float4 psMain(v2f IN): COLOR {
+ float n = 1 - log(2 * normalize(IN.normal).z);
+ return float4(0.3, 0.3, 1, n * n * n * n);
+}
+
+// #o3d VertexShaderEntryPoint vsMain
+// #o3d PixelShaderEntryPoint psMain
+// #o3d MatrixLoadOrder RowMajor
+</textarea>
+</div>
+</body>
+</html>
+
+
diff --git a/o3d/samples/tutorial-primitive.html b/o3d/samples/tutorial-primitive.html
new file mode 100644
index 0000000..ef41aeb
--- /dev/null
+++ b/o3d/samples/tutorial-primitive.html
@@ -0,0 +1,120 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+This sample shows how to display something without having to manually setup a
+shader.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Tutorial: Primitive.
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.camera');
+o3djs.require('o3djs.material');
+
+// Events
+// init() once the page has finished loading.
+window.onload = init;
+
+// global variables
+var g_finished = false; // for selenium testing
+var g_client;
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D, create a primitive and apply a basic material.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initializes global variables and libraries.
+ var o3d = clientElements[0];
+ g_client = o3d.client;
+
+ // Creates a pack to manage our resources/assets
+ var pack = g_client.createPack();
+
+ // Create a view.
+ var viewInfo = o3djs.rendergraph.createBasicView(
+ pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Create a material.
+ var material = o3djs.material.createBasicMaterial(
+ pack,
+ viewInfo,
+ [1, 0, 0, 1]); // red
+
+ // Create a cube.
+ var shape = o3djs.primitives.createCube(pack, material, 10);
+
+ // Make a transform for the cube, attach the cube and parent it.
+ var transform = pack.createObject('Transform');
+ transform.parent = g_client.root;
+ transform.addShape(shape);
+
+ // Make our camera show the scene.
+ o3djs.camera.fitContextToScene(g_client.root,
+ g_client.width,
+ g_client.height,
+ viewInfo.drawContext);
+
+ g_finished = true; // for selenium
+}
+
+</script>
+</head>
+<body>
+<h1>Tutorial: Primitive.</h1>
+<p>This sample shows how to display something without having to manually setup a
+shader.</p>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 600px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+</body>
+
+</html>
diff --git a/o3d/samples/vertex-shader-animation.html b/o3d/samples/vertex-shader-animation.html
new file mode 100644
index 0000000..b0e5c7c
--- /dev/null
+++ b/o3d/samples/vertex-shader-animation.html
@@ -0,0 +1,210 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+O3D Vertex Shader Animation Example.
+
+Demonstrates using a vertex shader for simple animation. The shader moves the
+vertices in a sin wave based on the parameter "time" and the world position
+of the vertices.
+-->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Vertex Shader Animation
+</title>
+<style type="text/css">
+ html, body {
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ border: none;
+ }
+</style>
+<script type="text/javascript" src="o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.effect');
+
+// global variables
+var g_o3d;
+var g_o3dElement;
+var g_math;
+var g_client;
+var g_pack;
+var g_viewInfo;
+var g_timeParam;
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ // These are here so that they are visible to both the browser (so
+ // selenium sees them) and the embedded V8 engine.
+ window.g_clock = 0;
+ window.g_timeMult = 1;
+ window.g_finished = false; // for selenium testing.
+
+ // Comment out the line below to run the sample in the browser
+ // JavaScript engine. This may be helpful for debugging.
+ o3djs.util.setMainEngine(o3djs.util.Engine.V8);
+
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D, loads the effect, and creates the quads.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initialize global variables and libraries.
+ g_o3dElement = clientElements[0];
+ g_o3d = g_o3dElement.o3d;
+ g_math = o3djs.math;
+
+ // Set window.g_client as well. Otherwise when the sample runs in
+ // V8, selenium won't be able to find this variable (it can only see
+ // the browser environment).
+ window.g_client = g_client = g_o3dElement.client;
+
+ // Create a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Create and load the effect.
+ var effect = g_pack.createObject('Effect');
+ o3djs.effect.loadEffect(effect, 'shaders/phong-vertex-anim.shader');
+
+ // Create a new Material for the quad.
+ var material = g_pack.createObject('Material');
+
+ // Set the material's drawList
+ material.drawList = g_viewInfo.performanceDrawList;
+ material.effect = effect;
+
+ // Create Params on the material for all the uniform parameters needed by
+ // the effect.
+ effect.createUniformParameters(material);
+
+ // Set the material parameters.
+ material.getParam('lightWorldPos').value = [-40, 100, 0];
+ material.getParam('lightIntensity').value = [1, 1, 1, 1];
+ material.getParam('ambientIntensity').value = [0.1, 0.1, 0.1, 1];
+ material.getParam('ambient').value = [1, 1, 1, 1];
+ material.getParam('diffuse').value = [1, 1, 1, 1];
+ material.getParam('specular').value = [1, 1, 1, 1];
+ material.getParam('shininess').value = 50;
+
+ g_timeParam = material.getParam('time');
+
+ // Create the view matrix which tells the camera which way to point to.
+ var eye = [10, 50, 20];
+ var target = [2, 0, -2];
+ var up = [0, 0, -1];
+ var view_matrix = g_math.matrix4.lookAt(eye, target, up);
+
+ g_viewInfo.drawContext.view = view_matrix;
+
+ var shape = o3djs.primitives.createPlane(g_pack, material,
+ 10, 10, 100, 100);
+ for (var xx = 0; xx < 5; xx++) {
+ for (var yy = 0; yy < 4; yy++) {
+ var index = yy * 3 + xx;
+
+ // Make a transform for each quad.
+ var transform = g_pack.createObject('Transform');
+ transform.translate((xx - 2) * 12, 0, (2 - yy) * -12);
+ transform.addShape(shape);
+ transform.parent = g_client.root;
+ transform.createParam('diffuse', 'ParamFloat4').value = [xx * 0.2,
+ yy * 0.25,
+ 0.5,
+ 1];
+ }
+ }
+
+ // Setup an onrender callback for animation.
+ g_client.setRenderCallback(onrender);
+
+ window.g_finished = true; // for selenium testing.
+}
+
+function updateProjectionMatrix() {
+ // Create our projection matrix, with a vertical field of view of 45 degrees
+ // a near clipping plane of 0.1 and far clipping plane of 100.
+ g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(45),
+ g_client.width / g_client.height,
+ 0.1,
+ 100);
+}
+
+// spin the camera.
+function onrender(render_event) {
+ // Get the number of seconds since the last render.
+ var elapsedTime = render_event.elapsedTime;
+ // Update g_clock in the browser and cache a V8 copy that can be
+ // accessed efficiently. g_clock must be in the browser for selenium.
+ var clock = window.g_clock + elapsedTime * window.g_timeMult;
+ window.g_clock = clock;
+
+ updateProjectionMatrix();
+
+ g_timeParam.value = clock * 4;
+}
+
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+
+</script>
+</head>
+<body onload="init()" onunload="unload()">
+<h1>Vertex Shader Animation Example</h1>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 100%; height: 80%"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/samples/vertex-shader.html b/o3d/samples/vertex-shader.html
new file mode 100644
index 0000000..42edf73
--- /dev/null
+++ b/o3d/samples/vertex-shader.html
@@ -0,0 +1,337 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+O3D Vertex Shader Demo
+
+This sample uses a custom vertex shader to quickly adjust the positions and
+normals of many vertices in a plane to achieve a ripple effect without iterating
+through the vertices in javascript.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Vertex Shader
+</title>
+<script type="text/javascript" src="o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.effect');
+
+// global variables
+var g_o3dElement;
+var g_client;
+var g_o3d;
+var g_math;
+var g_pack;
+var g_viewInfo;
+var g_clockParam;
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ // These are here so that they are visible to both the browser (so
+ // selenium sees them) and the embedded V8 engine.
+ window.g_clock = 0;
+ window.g_timeMult = 1;
+ window.g_finished = false; // for selenium testing.
+
+ // Comment out the line below to run the sample in the browser
+ // JavaScript engine. This may be helpful for debugging.
+ o3djs.util.setMainEngine(o3djs.util.Engine.V8);
+
+ o3djs.util.makeClients(initStep2, 'LargeGeometry');
+}
+
+
+/**
+ * Initializes global variables, positions camera, creates the material, and
+ * draws the plane.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Init global variables.
+ initGlobals(clientElements);
+
+ // Set up the view and projection transformations.
+ initContext();
+
+ // Add the shapes to the transform heirarchy.
+ createPlane();
+
+ // Setup render callback.
+ g_client.setRenderCallback(onRender);
+
+ window.g_finished = true; // for selenium testing.
+}
+
+
+/**
+ * Initializes global variables and libraries.
+ * @param {Array} clientElements An array of o3d object elements assumed
+ * to have one entry.
+ */
+function initGlobals(clientElements) {
+ g_o3dElement = clientElements[0];
+ g_o3d = g_o3dElement.o3d;
+ g_math = o3djs.math;
+
+ // Set window.g_client as well. Otherwise when the sample runs in
+ // V8, selenium won't be able to find this variable (it can only see
+ // the browser environment).
+ window.g_client = g_client = g_o3dElement.client;
+
+ // Create a pack to manage the objects created.
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+}
+
+
+/**
+ * Sets up reasonable view and projection matrices.
+ */
+function initContext() {
+ // Set up a perspective transformation for the projection.
+ g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(30), // 30 degree frustum.
+ g_client.width / g_client.height, // Aspect ratio.
+ 1, // Near plane.
+ 5000); // Far plane.
+
+ // Set up our view transformation to look towards the world origin where the
+ // cube is located.
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [4, 4, 4], // eye
+ [0, 0, 0], // target
+ [0, 1, 0]); // up
+}
+
+
+/**
+ * Creates an effect using the shaders in the textarea in the document, applies
+ * the effect to a new material, binds the uniform parameters of the shader
+ * to parameters of the material, and sets certain parameters: the light and
+ * camera position.
+ * @return {Material} The material.
+ */
+function createMaterial() {
+ // Create a new, empty Material and Effect object.
+ var material = g_pack.createObject('Material');
+ var effect = g_pack.createObject('Effect');
+
+ // Load shader string from document.
+ var shaderString = o3djs.util.getElementContentById('effect');
+ effect.loadFromFXString(shaderString);
+
+ // Apply the effect to this material.
+ material.effect = effect;
+
+ // Bind uniform parameters declared in shader to parameters of material.
+ effect.createUniformParameters(material);
+
+ // Set the material's drawList.
+ material.drawList = g_viewInfo.performanceDrawList;
+
+ // Set light and camera positions for the pixel shader.
+ material.getParam('lightWorldPos').value = [3, 10, 0];
+ material.getParam('cameraWorldPos').value = [1, 3, 12];
+
+ // Look up clock param.
+ g_clockParam = material.getParam('clock');
+
+ return material;
+}
+
+
+/**
+ * Creates the plane using the primitives utility library, and adds it to the
+ * transform graph at the root node.
+ */
+function createPlane() {
+ // This will create a plane subdivided into 180,000 triangles.
+ var plane = o3djs.primitives.createPlane(
+ g_pack, createMaterial(), 4, 4, 300, 300);
+
+ // Add the shape to the transform heirarchy.
+ g_client.root.addShape(plane);
+}
+
+
+/**
+ * Updates the clock for the animation.
+ * @param {!o3d.RenderEvent} renderEvent Rendering Information.
+ */
+function onRender(renderEvent) {
+ var elapsedTime = renderEvent.elapsedTime;
+
+ // Update g_clock in the browser and cache a V8 copy that can be
+ // accessed efficiently. g_clock must be in the browser for selenium.
+ var clock = window.g_clock + elapsedTime * window.g_timeMult;
+ window.g_clock = clock;
+
+ g_clockParam.value = clock;
+}
+
+
+/**
+ * Cleanup before exiting.
+ */
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+</script>
+</head>
+<body onload="init()" onunload="unload()">
+<h1>Vertex Shader</h1>
+This sample uses a custom vertex shader to quickly adjust the positions and
+normals of many vertices in a plane to achieve a ripple effect without iterating
+through the vertices in javascript.
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 600px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+
+<!-- Text area to hold the shaders -->
+<textarea id="effect" name="effect" cols="80" rows="20"
+ style="display: none;">
+uniform float4x4 world : WORLD;
+uniform float4x4 worldViewProjection : WORLDVIEWPROJECTION;
+uniform float4x4 worldInverseTranspose : WORLDINVERSETRANSPOSE;
+uniform float clock;
+uniform float3 lightWorldPos;
+uniform float3 cameraWorldPos;
+
+
+// Input parameters for the vertex shader.
+struct VertexShaderInput {
+ float4 position : POSITION;
+ float3 normal : NORMAL;
+ float4 color : COLOR;
+};
+
+
+// Input parameters for the pixel shader (also the output parameters for the
+// vertex shader.)
+struct PixelShaderInput {
+ float4 position : POSITION; // the position in clip space
+ float3 objectPosition : TEXCOORD0; // the position in world space
+ float3 normal : TEXCOORD1; // the normal in world space
+ float4 color : COLOR;
+};
+
+
+/**
+ * A function defining the shape of the wave. Takes a single float2 as an
+ * argument the entries of which are the x and z components of a point in the
+ * plane. Returns the height of that point.
+ *
+ * @param {float2} v The x and z components of the point in a single float2.
+ */
+float wave(float2 v) {
+ float d = length(v);
+ return 0.15 * sin(15 * d - 5 * clock) / (1 + d * d);
+}
+
+
+/**
+ * vertexShaderFunction - The vertex shader perturbs the vertices of the plane
+ * to achieve the ripples. Then it applies the worldViewProjection matrix.
+ *
+ * @param input.position Position vector of vertex in object coordinates.
+ * @param input.normal Normal of vertex in object coordinates.
+ * @param input.color Color of vertex.
+ */
+PixelShaderInput vertexShaderFunction(VertexShaderInput input) {
+ PixelShaderInput output;
+
+ float4 p = input.position;
+
+ // The height of the point p is adjusted according to the wave function.
+ p.y = wave(p.xz);
+
+ // Step size used to approximate the partial derivatives of the wave function.
+ float h = 0.001;
+
+ // We take the derivative numerically so that the wave function can be
+ // modified and the normal will still be correct.
+ float3 n = normalize(float3(
+ wave(float2(p.x - h, p.z)) - wave(float2(p.x + h, p.z)), 2 * h,
+ wave(float2(p.x, p.z - h)) - wave(float2(p.x, p.z + h))));
+
+ output.position = mul(p, worldViewProjection);
+ output.objectPosition = mul(p, world).xyz;
+ output.normal = mul(float4(n, 1), worldInverseTranspose).xyz;
+ output.color = input.color;
+
+ return output;
+}
+
+
+/**
+ * This pixel shader is meant to be minimal since the vertex shader is
+ * the focus of the sample.
+ */
+float4 pixelShaderFunction(PixelShaderInput input) : COLOR {
+ float3 p = input.objectPosition; // The point in question.
+ float3 l = normalize(lightWorldPos - p); // Unit-length vector toward light.
+ float3 n = normalize(input.normal); // Unit-length normal vector.
+ float3 v = normalize(cameraWorldPos - p); // Unit-length vector toward camera.
+ float3 r = normalize(-reflect(v, n)); // Reflection of v across n.
+
+ float3 q = (lightWorldPos - p);
+ float ldotr = dot(r, l);
+ float specular = clamp(ldotr, 0, 1) /
+ (1 + length(q - length(q) * ldotr * r));
+
+ return float4(0, 0.6, 0.7, 1) * dot(n, l) + specular;
+}
+
+
+// #o3d VertexShaderEntryPoint vertexShaderFunction
+// #o3d PixelShaderEntryPoint pixelShaderFunction
+// #o3d MatrixLoadOrder RowMajor
+</textarea>
+</body>
+</html>
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.0.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.0.png
new file mode 100644
index 0000000..64df9b0
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.0.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.1.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.1.png
new file mode 100644
index 0000000..05fc42a
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.1.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.10.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.10.png
new file mode 100644
index 0000000..4e907fa
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.10.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.11.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.11.png
new file mode 100644
index 0000000..a799e97
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.11.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.12.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.12.png
new file mode 100644
index 0000000..15ec38e
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.12.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.13.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.13.png
new file mode 100644
index 0000000..1bc24f2
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.13.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.14.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.14.png
new file mode 100644
index 0000000..3e0ce6f
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.14.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.15.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.15.png
new file mode 100644
index 0000000..39421f2
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.15.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.16.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.16.png
new file mode 100644
index 0000000..63315b8
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.16.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.17.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.17.png
new file mode 100644
index 0000000..2786a2a
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.17.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.18.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.18.png
new file mode 100644
index 0000000..bbdf045
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.18.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.19.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.19.png
new file mode 100644
index 0000000..5568e4a
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.19.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.2.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.2.png
new file mode 100644
index 0000000..195fc1f
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.2.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.20.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.20.png
new file mode 100644
index 0000000..3278701
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.20.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.21.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.21.png
new file mode 100644
index 0000000..6197751
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.21.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.22.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.22.png
new file mode 100644
index 0000000..22e0812
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.22.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.23.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.23.png
new file mode 100644
index 0000000..0457d70
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.23.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.24.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.24.png
new file mode 100644
index 0000000..134aa07
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.24.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.25.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.25.png
new file mode 100644
index 0000000..59eee69
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.25.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.26.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.26.png
new file mode 100644
index 0000000..e7f1290
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.26.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.27.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.27.png
new file mode 100644
index 0000000..45fffa9
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.27.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.28.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.28.png
new file mode 100644
index 0000000..731f324
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.28.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.29.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.29.png
new file mode 100644
index 0000000..ec694c1
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.29.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.3.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.3.png
new file mode 100644
index 0000000..8cb83a9
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.3.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.4.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.4.png
new file mode 100644
index 0000000..08b1c5d
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.4.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.5.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.5.png
new file mode 100644
index 0000000..456e287
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.5.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.6.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.6.png
new file mode 100644
index 0000000..74be504
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.6.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.7.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.7.png
new file mode 100644
index 0000000..b06faef
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.7.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.8.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.8.png
new file mode 100644
index 0000000..b7bda4c
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.8.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/deepwater/deepwater.9.png b/o3d/samples/waterdemo/assets/deepwater/deepwater.9.png
new file mode 100644
index 0000000..4540265
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/deepwater/deepwater.9.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/empty.txt b/o3d/samples/waterdemo/assets/empty.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/empty.txt
diff --git a/o3d/samples/waterdemo/assets/horizon_ramp.png b/o3d/samples/waterdemo/assets/horizon_ramp.png
new file mode 100644
index 0000000..087fac1
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/horizon_ramp.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/horizon_ramp_1.png b/o3d/samples/waterdemo/assets/horizon_ramp_1.png
new file mode 100644
index 0000000..087fac1
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/horizon_ramp_1.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/main_rock_normal.dds b/o3d/samples/waterdemo/assets/main_rock_normal.dds
new file mode 100644
index 0000000..9410b3a
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/main_rock_normal.dds
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/noise.png b/o3d/samples/waterdemo/assets/noise.png
new file mode 100644
index 0000000..863857d
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/noise.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/reflectivity_map.png b/o3d/samples/waterdemo/assets/reflectivity_map.png
new file mode 100644
index 0000000..c048d63
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/reflectivity_map.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/rock_reflection_new.png b/o3d/samples/waterdemo/assets/rock_reflection_new.png
new file mode 100644
index 0000000..f1cc39a
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/rock_reflection_new.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/rock_tile_normal_x.jpg b/o3d/samples/waterdemo/assets/rock_tile_normal_x.jpg
new file mode 100644
index 0000000..368a805
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/rock_tile_normal_x.jpg
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/rock_tile_rgb.jpg b/o3d/samples/waterdemo/assets/rock_tile_rgb.jpg
new file mode 100644
index 0000000..efec13b
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/rock_tile_rgb.jpg
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/sky_compat.png b/o3d/samples/waterdemo/assets/sky_compat.png
new file mode 100644
index 0000000..0fe4134
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/sky_compat.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/sun_compat.png b/o3d/samples/waterdemo/assets/sun_compat.png
new file mode 100644
index 0000000..969d848
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/sun_compat.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/sun_ramp.png b/o3d/samples/waterdemo/assets/sun_ramp.png
new file mode 100644
index 0000000..d2dbc53
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/sun_ramp.png
Binary files differ
diff --git a/o3d/samples/waterdemo/assets/sun_ramp_1.png b/o3d/samples/waterdemo/assets/sun_ramp_1.png
new file mode 100644
index 0000000..d2dbc53
--- /dev/null
+++ b/o3d/samples/waterdemo/assets/sun_ramp_1.png
Binary files differ
diff --git a/o3d/samples/waterdemo/cameracontrol.js b/o3d/samples/waterdemo/cameracontrol.js
new file mode 100644
index 0000000..271c863
--- /dev/null
+++ b/o3d/samples/waterdemo/cameracontrol.js
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains functions used controlling the camera.
+ *
+ */
+
+ /**
+ * Clamps a value between bounds.
+ *
+ * @param {number} inputValue Value to be clamped.
+ * @param {number} lowerBound Lower limit.
+ * @param {number} upperBound Upper limit.
+ * @return {number} The clamped value.
+ */
+function clamp(inputValue, lowerBound, upperBound) {
+ var resultValue = inputValue;
+ if (inputValue < lowerBound) resultValue = lowerBound;
+ if (inputValue > upperBound) resultValue = upperBound;
+ return resultValue;
+}
+
+/**
+ * Functions for control based on the mouse
+ */
+var dragging = false;
+var g_rotX = 2.85;
+var g_rotY = 0;
+var g_thisClick;
+var g_lastClick;
+var g_cameraZoom = 1.0;
+
+function startDragging(e) {
+ g_thisClick = e;
+
+ dragging = true;
+}
+
+/**
+ * Scroll wheel handler. Adjusts g_cameraZoom global according to wheel
+ * movement then causes a recalculation of the projection matrix.
+ * The goal is to simulate a zoom lens rather than to move the viewpoint.
+ * @param {Event} e Wheel event to handle.
+ */
+function scrollMe(e) {
+ if (e.deltaY) {
+ g_cameraZoom = clamp(g_cameraZoom * ((e.deltaY < 0 ? 13 : 11) / 12),
+ 0.1,
+ 4.0);
+ resize();
+ }
+}
+
+function drag(e) {
+ if (dragging) {
+ var scale = -.001 * g_cameraZoom;
+
+ g_lastClick = g_thisClick;
+ g_thisClick = e;
+
+ var xLowerLimit = 2.3;
+ var xUpperLimit = 4.2;
+ var yLowerLimit = -0.6;
+ var yUpperLimit = 0.25;
+
+ g_rotX = clamp(g_rotX - scale * (g_thisClick.x - g_lastClick.x),
+ xLowerLimit, xUpperLimit);
+ g_rotY = clamp(g_rotY + scale * (g_thisClick.y - g_lastClick.y),
+ yLowerLimit, yUpperLimit);
+
+ setViewFromRotation();
+ }
+}
+
+function setViewFromRotation() {
+ var rotMatX = g_math.matrix4.rotationY(g_rotX);
+ var right = g_math.matrix4.transformDirection(rotMatX, [0, 0, 1]);
+ var camera_X = g_math.normalize(g_math.cross([0, 1, 0], right));
+ var rotMatY = g_math.matrix4.axisRotation(camera_X, g_rotY);
+ var rot_mat = g_math.matrix4.mul(rotMatX, rotMatY);
+ g_root.identity();
+
+ // TODO: eye position should be a variable
+ var eye = [-2.751, 3.529, -8.563];
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ eye,
+ g_math.addVector(eye, g_math.mulVectorMatrix([0, 0, 1, 0], rot_mat)),
+ g_math.mulVectorMatrix([0, 1, 0, 0], rot_mat));
+}
+
+function stopDragging(e) {
+ dragging = false;
+}
diff --git a/o3d/samples/waterdemo/uicomponents.js b/o3d/samples/waterdemo/uicomponents.js
new file mode 100644
index 0000000..121c646
--- /dev/null
+++ b/o3d/samples/waterdemo/uicomponents.js
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains functions used for creating UI components
+ * on the web page. It also has functions used for altering the parameters
+ * that those UI components control.
+ *
+ */
+
+/*****************************************************************************
+ * Helper functions for the html
+ *****************************************************************************/
+
+/**
+ * Creates the html for the select pop down menu to select the time.
+ */
+function buildTimeSelect() {
+ document.write('Time: <select id="time" name="time"');
+ document.write(' onChange="changeColor()">');
+ for (var t = 0; t < 24; t++) {
+ document.write('<option value="' + t * 60 + '">');
+ if (t % 12 == 0) {
+ document.write('12');
+ } else {
+ document.write(t % 12);
+ }
+ if (t < 12) {
+ document.write(' AM</option>');
+ } else {
+ document.write(' PM</option>');
+ }
+ }
+ document.write('</select>');
+}
+
+
+/**
+ * Creates the html for the select pop down menu to select the speed of the sun.
+ */
+function buildTimeSpeedSelect() {
+ document.write('Speed: <select id="speed" name="speed" ');
+ document.write('onChange="changeSpeed()">');
+ document.write('<option value="1">1X(real time)</option>');
+ for (var t = 10; t <= 100; t += 10) {
+ document.write('<option value="' + t + '">' + t + 'X</option>');
+ }
+ document.write('</select>');
+}
+
+
+/*****************************************************************************
+ * Functions for using the html input.
+ *****************************************************************************/
+
+/**
+ * Caluclates the current position of the sun based on time.
+ * TODO: This is currently not being used--a direction vector is.
+ * However, this function shoudl change that direction vector.
+ * @param {number} opt_inputMinutes Optional paramter of the time in minutes
+ * to return the sun position for.
+ * @return {!Array.<number>} A vector indicating the position of the sun.
+ */
+function getCurrentSunPosition(opt_inputMinutes) {
+ userDate = new Date();
+ var totalMinutes = opt_inputMinutes;
+ if (!opt_inputMinutes) {
+ totalMinutes = userDate.getHours() * 60 + userDate.getMinutes();
+ g_currentTime = totalMinutes;
+ }
+ var minuteRadians = ((totalMinutes / 720 * Math.PI) + 1.5 * Math.PI) %
+ (2 * Math.PI);
+ return [150 * Math.cos(minuteRadians), 150 * Math.sin(minuteRadians), 0];
+}
+
+
+/**
+ * Changes the speed of the sun.
+ */
+function changeSpeed() {
+ g_sunMultiplier = document.getElementById('speed').value;
+ o3djs.dump.dump('-------------speed: ' + g_sunMltiplier + '\n');
+}
diff --git a/o3d/samples/waterdemo/waterdemo.html b/o3d/samples/waterdemo/waterdemo.html
new file mode 100644
index 0000000..7fcf611
--- /dev/null
+++ b/o3d/samples/waterdemo/waterdemo.html
@@ -0,0 +1,439 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+ Water Demo
+</title>
+<style type="text/css">
+ html, body {
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ border: none;
+ }
+</style>
+<script type="text/javascript" src="../o3djs/base.js"></script>
+
+<script type="text/javascript" src="waterdemo.js"></script>
+<script type="text/javascript" src="uicomponents.js"></script>
+<script type="text/javascript" src="cameracontrol.js"></script>
+</head>
+<body>
+ <!-- Start of o3d plugin -->
+ <div id="o3d" style="width: 100%; height: 100%;"></div>
+ <!-- End of o3d plugin -->
+<!-- Don't render the textarea -->
+<div style="display:none">
+
+<!-- *********************************************************************
+ Shader for the sky dome
+ ********************************************************************* -->
+<textarea id="dome" name="dome" cols="80" rows="20">
+ // Constant float4x4 matrices
+ float4x4 WVP : WORLDVIEWPROJECTION;
+ float4x4 world : WORLD;
+
+ // Positions of the light and camera
+ float3 lightDirection;
+ float3 cameraEye;
+
+ // Samplers for textures
+ sampler2D DiffuseSampler;
+ sampler2D HorizonRamp;
+ sampler2D SunRamp;
+
+ // Input to our vertex shader
+ struct a2v {
+ float3 pos : POSITION;
+ float4 col : COLOR;
+ float3 normal : NORMAL;
+ float2 texcoord0 : TEXCOORD0;
+ };
+
+ // Input to our pixel shader and output of our vertex shader
+ struct v2f {
+ float4 pos : POSITION;
+ float3 worldPos : TEXCOORD0;
+ float3 eye : TEXCOORD1;
+ };
+
+ v2f vsMain(a2v IN) {
+ v2f OUT;
+ float4 objectPos = float4(IN.pos, 1);
+ OUT.pos = mul(objectPos, WVP);
+ OUT.worldPos = mul(objectPos,world).xyz;
+ OUT.eye = OUT.worldPos - cameraEye;
+ return OUT;
+ }
+
+ float4 psMain(v2f IN): COLOR {
+ // TODO: lightDirection is a constant--no need to normalize each vert
+ float3 L = normalize(lightDirection);
+ float LdotEye=dot(L, normalize(IN.eye));
+
+ float4 sun = tex2D(SunRamp, float2((1-LdotEye)*40, (1-LdotEye)*40));
+ float4 horizon = tex2D(HorizonRamp, IN.worldPos.yy/80);
+ return sun.w*sun*.7+(1-sun.w*.7)*horizon;
+ }
+
+ // Here we tell our effect file *which* functions are
+ // our vertex and pixel shaders.
+
+ // #o3d VertexShaderEntryPoint vsMain
+ // #o3d PixelShaderEntryPoint psMain
+ // #o3d MatrixLoadOrder RowMajor
+</textarea>
+
+<!-- *********************************************************************
+ Shader for the water
+ Based on Tessendorf's "Simulating Ocean Water" paper
+ ********************************************************************* -->
+<textarea id="water" name="water" cols="80" rows="20">
+ float4x4 WVP : WorldViewProjection;
+ float4x4 worldInverseTranspose : WORLDINVERSETRANSPOSE;
+ float4x4 world : WORLD;
+
+ // Positions of the light and camera
+ float3 lightDirection;
+ float3 cameraEye;
+
+ // Time from the javascript program
+ float inputTime;
+
+ float reflectivity;
+ float3 swizzle1;
+
+ // Textures
+ sampler2D HeightSampler;
+ sampler2D Reflectivity;
+ sampler2D ReflectionSampler;
+ sampler2D HorizonRamp;
+ sampler2D SunRamp;
+ sampler2D RockTexture;
+
+ struct a2v{
+ float3 Position : POSITION;
+ float3 Normal : NORMAL;
+ float2 texcoord0 : TEXCOORD0;
+ };
+
+ struct v2f{
+ float4 pos : POSITION;
+ float2 texcoord0 : TEXCOORD0;
+ float3 lightVec : TEXCOORD1;
+ float4 diffuse : TEXCOORD2;
+ float3 eyeVec : TEXCOORD3;
+ float3 normal : TEXCOORD4;
+ float3 worldPos : TEXCOORD5;
+ float2 worldTexCoord : TEXCOORD6;
+ };
+
+
+ float displacement(float2 vec,float time) {
+ // These values could be passed in
+ float frequency = 15;
+ float scaleFactor = .075;
+
+ float displacement=0;
+ displacement+=scaleFactor*sin((vec.x*-.4+vec.y*-.9)*frequency+time);
+ displacement+=scaleFactor*.5*sin((vec.x*.1+vec.y*-.9)*frequency*3+time);
+ displacement+=scaleFactor*.25*sin((vec.x*-.2+vec.y*-.8)*frequency*9+time);
+ return displacement;
+ }
+
+ v2f vsMain (a2v IN){
+ v2f OUT;
+
+ float4 PosWorld = mul(float4(IN.Position, 1.0),world);
+
+ float displacementC = displacement(PosWorld.xz/10, inputTime);
+ float4 newPosition = float4(IN.Position,1) + displacementC*float4(0,1,0,0);
+
+ OUT.normal = mul(float4(IN.Normal,1),worldInverseTranspose).xyz;
+ OUT.pos = mul(newPosition, WVP);
+ OUT.worldPos = PosWorld.xyz;
+ OUT.diffuse = float4(displacementC,displacementC,displacementC,1);
+ // TODO: lightDirection is a constant--no need to normalize each vert
+ OUT.lightVec = normalize(lightDirection);
+ OUT.eyeVec = normalize(PosWorld.xyz - cameraEye);
+ OUT.worldTexCoord = PosWorld.xz/40;
+ OUT.texcoord0 = IN.texcoord0;
+ return OUT;
+ }
+
+ float4 psMain (v2f IN) : COLOR{
+ // Use central differencing to get the normal from the bump maps
+ // The swizzle says which channel we're using
+ float r = dot(tex2D(HeightSampler, IN.worldTexCoord.xy+float2(.005,0)).xyz,
+ swizzle1);
+ float l = dot(tex2D(HeightSampler, IN.worldTexCoord.xy+float2(-.005,0)).xyz,
+ swizzle1);
+ float b = dot(tex2D(HeightSampler, IN.worldTexCoord.xy+float2(0,.005)).xyz,
+ swizzle1);
+ float f = dot(tex2D(HeightSampler, IN.worldTexCoord.xy+float2(0,-.005)).xyz,
+ swizzle1);
+ float norm = .01*40;
+ float3 bump = float3((r-l)/norm, 1, (b-f)/norm);
+ float3 n = normalize(bump + IN.normal);
+
+ // Precalculate the calculation of reflectivity basead on costhetai
+ // and store as a texture for lookup
+ float costhetai = abs(dot(IN.eyeVec, n));
+ float reflectivity = tex2D(Reflectivity, float2(costhetai,0)).x;
+
+ float3 reflectV = normalize(reflect(IN.eyeVec, n));
+
+ float3 dPE = IN.worldPos-IN.eyeVec;
+ float dist = length(dPE) * .61;
+ dist = exp(-dist);
+
+ float3 L = normalize(IN.lightVec);
+ float LdotReflectV = dot(L, reflectV);
+
+ // Calculate the sun ramp for its reflection on the water
+ float4 sun = tex2D(SunRamp, float2((1-LdotReflectV)*40,0));
+ sun.xyz = sun.w*sun.xyz;
+ float4 horizon = tex2D(HorizonRamp, reflectV.yy*2);
+
+ // Add in the refelction of the rock
+ float2 vPos = float2((IN.texcoord0.x-.7668)*11.7,
+ ((1-IN.texcoord0.y)-.888)*11);
+ vPos += bump.xz/7;
+ float4 islandReflection = tex2D(ReflectionSampler, vPos);
+ reflectivity = reflectivity*(1-islandReflection.w*
+ (.75-.75*islandReflection.x));
+
+ // Get the reflection of the environment (sun, sky)
+ float4 environmentColor = sun*.7+(1-sun.w*.7)*horizon;
+
+ // The color of the water when we look straight into it, not at a glancing
+ // angle
+ float4 internalColor = float4(0, .1, .15, 1);
+
+ // First interpolate water color with reflection from environment
+ float4 firstlerp = lerp(internalColor, environmentColor, reflectivity);
+ // Then interpolate with the air to give atmosphere and some haze
+ return lerp(firstlerp, float4(.1, .1, .1, 1), dist) +
+ pow(LdotReflectV,200)*float4(.9,.6,.4,0);
+ }
+
+ // #o3d VertexShaderEntryPoint vsMain
+ // #o3d PixelShaderEntryPoint psMain
+ // #o3d MatrixLoadOrder RowMajor
+</textarea>
+
+
+<!-- *********************************************************************
+ Shader for the rocks
+ ********************************************************************* -->
+<textarea id="rocks" name="rocks" cols="80" rows="20">
+ // The 4x4 world view projection matrix.
+ float4x4 WVP : WORLDVIEWPROJECTION;
+
+ // positions of the light and camera
+ float3 lightDirection;
+ float3 cameraEye;
+
+ // Varying time input
+ float inputTime;
+
+ float4 ambient_color;
+ float4 diffuse_Color;
+
+ sampler2D DiffuseSampler;
+ sampler2D BumpSampler;
+
+ // Input to our vertex shader
+ struct a2v {
+ float3 pos : POSITION;
+ float3 normal : NORMAL;
+ float2 texcoord0 : TEXCOORD0;
+ };
+
+ // Input to our pixel shader and output of our vertex shader
+ struct v2f {
+ float4 pos : POSITION;
+ float2 texcoord0 : TEXCOORD0;
+ float3 lightVec : TEXCOORD1;
+ float3 normal : TEXCOORD2;
+ };
+
+ v2f vsMain(a2v IN) {
+ v2f OUT;
+
+ OUT.pos = mul(float4(IN.pos, 1.0), WVP);
+ // TODO: lightDirection is a constant--no need to normalize each vert
+ float3 l = normalize(lightDirection);
+ float3 n = IN.normal;
+
+ ambient_color = float4(0.4, 0.4, 0.4, 1);
+
+ OUT.texcoord0 = IN.texcoord0;
+ OUT.lightVec = l;
+ OUT.normal = n;
+ return OUT;
+ }
+
+ float4 psMain(v2f IN): COLOR {
+ ambient_color = float4(0.2, 0.2, 0.2, 1);
+ float3 n = normalize(IN.normal);
+ float4 diffuse = tex2D(DiffuseSampler, IN.texcoord0.xy);
+ float4 diffuseCol = diffuse * saturate(dot(n,normalize(IN.lightVec)));
+ return diffuseCol + ambient_color*diffuse;
+ }
+
+ // Here we tell our effect file *which* functions are
+ // our vertex and pixel shaders.
+
+ // #o3d VertexShaderEntryPoint vsMain
+ // #o3d PixelShaderEntryPoint psMain
+ // #o3d MatrixLoadOrder RowMajor
+</textarea>
+
+<!-- *********************************************************************
+ Shader for the sun
+ ********************************************************************* -->
+<textarea id="sun" name="sun" cols="80" rows="20">
+ // The 4x4 world view projection matrix.
+ float4x4 WVP : WORLDVIEWPROJECTION;
+
+ sampler2D DiffuseSampler;
+
+ // Input parameters for our vertex shader
+ struct a2v {
+ float3 pos : POSITION;
+ float3 normal : NORMAL;
+ float2 texcoord0 : TEXCOORD0;
+ };
+
+ // Input parameters for our pixel shader
+ struct v2f {
+ float4 pos : POSITION;
+ float2 tex : TEXCOORD0;
+ };
+
+ v2f vsMain(a2v IN) {
+ v2f OUT;
+ OUT.pos = mul(float4(IN.pos,1), WVP);
+ OUT.tex = IN.texcoord0;
+ return OUT;
+ }
+
+ float4 psMain(v2f IN): COLOR {
+ return tex2D(DiffuseSampler, IN.tex);
+ }
+
+ // Here we tell our effect file *which* functions are
+ // our vertex and pixel shaders.
+
+ // #o3d VertexShaderEntryPoint vsMain
+ // #o3d PixelShaderEntryPoint psMain
+ // #o3d MatrixLoadOrder RowMajor
+</textarea>
+
+<!-- *********************************************************************
+ Shader for the bridge
+ ********************************************************************* -->
+<textarea id="bridge" name="bridge" cols="80" rows="20">
+ // The 4x4 world view projection matrix.
+ float4x4 WVP : WORLDVIEWPROJECTION;
+
+ // Positions of the light and camera
+ float3 lightPos;
+ float3 cameraEye;
+
+ // Changing time variable
+ float inputTime;
+
+ // Color for the bridge
+ float4 lightDiffuse;
+
+ sampler2D DiffuseSampler;
+
+ float3 bridgeEnd1;
+ float3 bridgeEnd2;
+
+ // Input parameters for our vertex shader
+ struct a2v {
+ float3 pos : POSITION;
+ float3 col : COLOR;
+ float3 normal : NORMAL;
+ };
+
+ // Input parameters for our pixel shader
+ struct v2f {
+ float4 pos : POSITION;
+ };
+
+ v2f vsMain(a2v IN) {
+ v2f OUT;
+
+ // These two could possible be passed in--different wind values
+ float d_frequency = .5;
+ float d_scale = .1;
+
+ bridgeEnd1 = float3(21,11.3,-50.5);
+ bridgeEnd2 = float3(23.3,11,-37);
+ float halfBridgeLength = .5*distance(bridgeEnd1,bridgeEnd2);
+ float3 displacementVector = float3(0,1,0);
+ float displacement = sin(d_frequency*inputTime);
+ float percentFromEnd1 = distance(IN.pos,bridgeEnd1)/halfBridgeLength;
+ float percentFromEnd2 = distance(IN.pos,bridgeEnd2)/halfBridgeLength;
+ float d_weight = percentFromEnd1*percentFromEnd2;
+ float3 newPosition = IN.pos +
+ d_weight*d_scale*displacement*displacementVector;
+ OUT.pos = mul(float4(newPosition,1), WVP);
+ return OUT;
+ }
+
+ float4 psMain(v2f IN): COLOR {
+ return lightDiffuse;
+ }
+
+ // Here we tell our effect file *which* functions are
+ // our vertex and pixel shaders.
+
+ // #o3d VertexShaderEntryPoint vsMain
+ // #o3d PixelShaderEntryPoint psMain
+ // #o3d MatrixLoadOrder RowMajor
+</textarea>
+</div>
+</body>
+</html>
+
+
diff --git a/o3d/samples/waterdemo/waterdemo.js b/o3d/samples/waterdemo/waterdemo.js
new file mode 100644
index 0000000..48282f9
--- /dev/null
+++ b/o3d/samples/waterdemo/waterdemo.js
@@ -0,0 +1,646 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview This file contains functions used for creating a beach scene.
+ *
+ */
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.pack');
+o3djs.require('o3djs.effect');
+o3djs.require('o3djs.dump');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.loader');
+
+// Events
+
+/**
+ * Run the init() function once the page has finished loading.
+ */
+window.onload = init;
+
+/**
+ * Run the unload() function when the page is unloaded.
+ */
+window.onunload = unload;
+
+// O3D variables
+var g_root;
+var g_o3dElement;
+var g_o3d;
+var g_math;
+var g_client;
+var g_pack = null;
+var g_viewInfo;
+var g_clientWidth;
+var g_clientHeight;
+var g_loader;
+var g_finished = false; // for selenium
+var g_globalParams;
+
+// Model variables
+var g_modelCounter = 0;
+var g_modelPaths = [];
+var g_modelPacks = [];
+var g_modelRoots = [];
+var g_modelIndices = [];
+
+// Pack variables
+var g_rockPack;
+var g_waterPack;
+var g_bridgePack;
+var g_woodPack;
+var g_domePack;
+var g_treePack;
+var g_sunPack;
+
+// Time variables
+var g_frame;
+var g_clock = 0.0;
+var g_clockParam;
+var g_timeMult = 1.0;
+
+// General shader variables
+var g_lightDirectionParam;
+
+// Water variables
+var g_samplerParam;
+var g_swizzle1Param;
+var g_deepWaterTextures = [];
+
+// Cameras
+var g_cameraOriginal = {
+ eye: { x: -2.751, y: 3.529, z: -8.563 },
+ target: { x: 0, y: 4, z: -10 },
+ up: { x: 0, y: 1, z: 0 }
+};
+var g_cameraOverhead = {
+ eye: { x: 0, y: 120, z: -20 },
+ target: { x: 0, y: 2, z: -20 },
+ up: { x: 0, y: 0, z: -1 }
+};
+var g_camera = {
+ eye: { x: 0, y: 0, z: 0 },
+ target: { x: 0, y: 0, z: 0 },
+ up: { x: 0, y: 0, z: 0 }
+};
+
+var g_clientZoom = 1.0;
+
+// Initialize the models we'll be using
+g_modelPaths[0] = 'assets/rocks.9.o3dtgz';
+g_modelPaths[1] = 'assets/lazy_bridge.o3dtgz';
+
+g_modelIndices[0] = 0;
+g_modelIndices[1] = 2;
+
+/**
+ * Performs the initial creation of the packs and their roots.
+ */
+function initializePacks() {
+ var packNames = [
+ 'Rock pack', // 0
+ 'Tree pack', // 1
+ 'Brdige pack', // 2
+ 'Wood pack', // 3
+ 'Dome pack', // 4
+ 'Water pack', // 5
+ 'Sun pack']; // 6
+
+ for (var mp = 0; mp < packNames.length; mp++) {
+ g_modelPacks[mp] = g_client.createPack();
+ g_modelRoots[mp] = g_modelPacks[mp].createObject('Transform');
+ g_modelRoots[mp].parent = g_root;
+ }
+
+ // Set some useful variables
+ g_rockPack = g_modelPacks[0];
+ g_treePack = g_modelPacks[1];
+ g_bridgePack = g_modelPacks[2];
+ g_woodPack = g_modelPacks[3];
+ g_domePack = g_modelPacks[4];
+ g_waterPack = g_modelPacks[5];
+ g_sunPack = g_modelPacks[6];
+
+}
+
+/**
+ * Swaps the properties of two cameras.
+ * @param {!Object} oldCamera The old camera.
+ * @param {!Object} newCamera The new camera to switch to.
+ */
+function switchCamera(oldCamera, newCamera) {
+ oldCamera.eye.x = newCamera.eye.x;
+ oldCamera.eye.y = newCamera.eye.y;
+ oldCamera.eye.z = newCamera.eye.z;
+
+ oldCamera.target.x = newCamera.target.x;
+ oldCamera.target.y = newCamera.target.y;
+ oldCamera.target.z = newCamera.target.z;
+
+ oldCamera.up.x = newCamera.up.x;
+ oldCamera.up.y = newCamera.up.y;
+ oldCamera.up.z = newCamera.up.z;
+}
+
+
+/**
+ * Generates the projection matrix based on the size of the o3d plugin and the
+ * camera zoom.
+ */
+function resize() {
+ var newWidth = g_client.width;
+ var newHeight = g_client.height;
+
+ if (g_clientZoom != g_cameraZoom ||
+ newWidth != g_clientWidth ||
+ newHeight != g_clientHeight) {
+ g_clientZoom = g_cameraZoom;
+ g_clientWidth = newWidth;
+ g_clientHeight = newHeight;
+
+ // Set the projection matrix, with a vertical field of view of 30 degrees
+ // a near clipping plane of 0.1 and far clipping plane of 10000.
+ g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(g_clientZoom * 30),
+ g_clientWidth / g_clientHeight,
+ 0.1,
+ 10000);
+ }
+}
+/**
+ * Performs functions (usually related to animation) every time the frame is
+ * rendered. Also updates properities such as time.
+ * @param {!o3d.RenderEvent} renderEvent Render event.
+ */
+function onrender(renderEvent) {
+
+ var elapsedTime = renderEvent.elapsedTime;
+ g_clock += g_timeMult * elapsedTime;
+
+ g_clockParam.value = g_clock;
+
+ g_frame = Math.floor(g_clock * 18);
+ g_frame = g_frame % 90;
+
+ var frame = g_frame - 22.5;
+ var twoPiOver90 = 2.0 * Math.PI / 90.0;
+ var s1 = Math.sin(frame * twoPiOver90) + 1;
+ var s2 = Math.sin((frame - 30) * twoPiOver90) + 1;
+ var s3 = Math.sin((frame - 60) * twoPiOver90) + 1;
+
+ var denominator = s1 + s2 + s3;
+ s1 /= denominator;
+ s2 /= denominator;
+ s3 /= denominator;
+
+ if (g_frame >= 0 && g_frame < 30) {
+ g_swizzle1Param.value = new Array(s1, s3, s2);
+ } else if (g_frame >= 30 && g_frame < 60) {
+ g_swizzle1Param.value = new Array(s2, s1, s3);
+ } else {
+ g_swizzle1Param.value = new Array(s3, s2, s1);
+ }
+
+ var i = g_frame % 30;
+ var m = Math.floor(g_frame / 3);
+ if (g_deepWaterTextures[i]){
+ g_samplerParam.value.texture = g_deepWaterTextures[i];
+ }
+ resize();
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes the scene.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ g_o3dElement = clientElements[0];
+ g_o3d = g_o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = g_o3dElement.client;
+
+ // Currently hardcoding the size.
+ g_clientWidth = 700;
+ g_clientHeight = 500;
+
+ g_loader = o3djs.loader.createLoader(function() {
+ g_finished = true; // for selenuim
+ });
+
+ g_pack = g_client.createPack();
+ g_root = g_pack.createObject('Transform');
+ g_root.parent = g_client.root;
+
+ // Initialize the camera
+ switchCamera(g_camera, g_cameraOriginal);
+
+ // Initialize all the other packs
+ initializePacks();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Initialize camera
+ resize();
+ setViewFromRotation();
+
+ // Create global params.
+ g_globalParams = g_pack.createObject('ParamObject');
+ g_lightDirectionParam = g_globalParams.createParam('lightWorldPos',
+ 'ParamFloat3');
+ g_clockParam = g_globalParams.createParam('timeParam',
+ 'ParamFloat');
+ g_swizzle1Param = g_globalParams.createParam('swizzle1Param',
+ 'ParamFloat3');
+
+ g_lightDirectionParam.value = [0, .15, -1];
+
+ // Add our models
+ water(g_waterPack, g_modelRoots[5]);
+ dome(g_domePack, g_modelRoots[4]);
+ sun(g_sunPack, g_modelRoots[6]);
+ rocks(g_rockPack, g_modelRoots[0]);
+ bridge(g_bridgePack, g_modelRoots[2]);
+
+ // Add mouse control
+ o3djs.event.addEventListener(g_o3dElement, 'mousedown', startDragging);
+ o3djs.event.addEventListener(g_o3dElement, 'mousemove', drag);
+ o3djs.event.addEventListener(g_o3dElement, 'mouseup', stopDragging);
+ o3djs.event.addEventListener(g_o3dElement, 'wheel', scrollMe);
+
+ g_client.setRenderCallback(onrender);
+
+ g_loader.finish();
+}
+
+/**
+ * Returns the path of where the file is located
+ * with the trailing slash
+ * @return {string} The current path.
+ */
+function getCurrentPath() {
+ var path = window.location.href;
+ var index = path.lastIndexOf('/');
+ return path.substring(0, index + 1);
+}
+
+/**
+ * Loads a texture.
+ *
+ * @param {!o3djs.loader.Loader} loader Loader to use to load texture.
+ * @param {!o3d.Pack} pack Pack to load texture in.
+ * @param {!o3d.Material} material Material to attach sampler to.
+ * @param {string} samplerName Name of sampler.
+ * @param {string} textureName filename of texture.
+ * @return {!o3d.Sampler} Sampler attached to material.
+ */
+function loadTexture(loader, pack, material, samplerName, textureName) {
+ var samplerParam = material.getParam(samplerName);
+ var sampler = pack.createObject('Sampler');
+ samplerParam.value = sampler;
+
+ var url = getCurrentPath() + 'assets/' + textureName;
+ loader.loadTexture(pack, url, function(texture, exception) {
+ if (exception) {
+ alert(exception);
+ } else {
+ sampler.texture = texture;
+ }
+ });
+
+ return sampler;
+}
+
+/**
+ * Creates the rocks and its shaders.
+ * @param {!o3d.Pack} pack Pack to load the rocks into.
+ * @param {o3d.Transform} root Parent transform for the rocks shape.
+ */
+function rocks(pack, root) {
+ var loader = g_loader.createLoader(function() {
+ o3djs.pack.preparePack(pack, g_viewInfo);});
+ loader.loadScene(g_client, pack, root, getCurrentPath() + g_modelPaths[0],
+ rockCallback);
+
+ function rockCallback(pack, root, exception) {
+ var rockTextures = ['rock_tile_rgb.jpg', 'main_rock_normal.dds'];
+ var bumpSamplers = ['DiffuseSampler', 'BumpSampler'];
+
+ var materials = pack.getObjects('lambert2', 'o3d.Material');
+ for (var m = 0; m < materials.length; m++) {
+ var material = materials[m];
+ var fxString = document.getElementById('rocks').value;
+ var effect = pack.createObject('Effect');
+ effect.loadFromFXString(fxString);
+
+ effect.createUniformParameters(material);
+ material.effect = effect;
+
+ var lightDiffuseParam = material.getParam('diffuse_Color');
+ if (lightDiffuseParam) {
+ lightDiffuseParam.value = [.5, .5, .5, 1];
+ }
+ for (var t = 0; t < rockTextures.length; t++) {
+ loadTexture(loader, pack, material, bumpSamplers[t], rockTextures[t]);
+ }
+ }
+ connectParams(pack);
+ }
+ loader.finish();
+}
+
+/**
+ * Creates the bridge and its shaders.
+ * @param {!o3d.Pack} pack Pack to load the bridge into.
+ * @param {!o3d.Transform} root Parent transform for the bridge shape.
+ */
+function bridge(pack, root) {
+ var loader = g_loader.createLoader(function() {
+ o3djs.pack.preparePack(pack, g_viewInfo);});
+ loader.loadScene(
+ g_client, pack, root, getCurrentPath() + g_modelPaths[1], callback);
+
+ function callback(pack, root, exception) {
+ var materials = pack.getObjects('bridgeMaterial', 'o3d.Material');
+ for (var m = 0; m < materials.length; m++) {
+ var material = materials[m];
+
+ var fxString = document.getElementById('bridge').value;
+ var effect = pack.createObject('Effect');
+ effect.loadFromFXString(fxString);
+
+ effect.createUniformParameters(material);
+ material.effect = effect;
+
+ // White ambient light
+ var lightAmbientParam = material.getParam('lightAmbient');
+ if (lightAmbientParam) {
+ lightAmbientParam.value = [0.04, 0.04, 0.04, 1];
+ }
+ // Reddish diffuse light
+ var lightDiffuseParam = material.getParam('lightDiffuse');
+ if (lightDiffuseParam) {
+ lightDiffuseParam.value = [.45, .27, .07, 1];
+ }
+ }
+ connectParams(pack);
+ }
+
+ loader.finish();
+}
+
+/**
+ * Creates the water shapes, applies correct shaders and connects any special
+ * water params.
+ * @param {!o3d.Pack} pack Pack to load the water into.
+ * @param {!o3d.Transform} root Parent transform for the water shape.
+ */
+function water(pack, root) {
+ // Create the material
+ o3djs.dump.dump('-- start water\n');
+
+ var material = pack.createObject('Material');
+ o3djs.dump.dump('-- water: created material\n');
+ material.drawList = g_viewInfo.performanceDrawList;
+ var effect = pack.createObject('Effect');
+ var effectString = document.getElementById('water').value;
+ effect.loadFromFXString(effectString);
+ effect.createUniformParameters(material);
+ material.effect = effect;
+
+ o3djs.dump.dump('-- water: created material & effect\n');
+ var swizzle1Param = material.getParam('swizzle1');
+ swizzle1Param.bind(g_swizzle1Param);
+ o3djs.dump.dump('-- water: bound swizzle param\n');
+
+ var water = o3djs.primitives.createDisc(pack, material,
+ 155, 300, 500, 300, 10)
+ var loader = g_loader.createLoader(
+ function() {
+ root.addShape(water);
+ });
+
+ // The texture that this sampler references changes every 3 frames
+ g_samplerParam = material.getParam('HeightSampler');
+ if (g_samplerParam) {
+ o3djs.dump.dump('bind sampler param\n');
+ g_samplerParam.value = pack.createObject('Sampler');
+ }
+
+ // This is so "index" is unique.
+ // @param {number} index Index of texture to register.
+ // @return {!function(!o3d.Texture): void} A function that takes a
+ // texture.
+ function registerDeepWaterTexture(index) {
+ return function(texture) {
+ g_deepWaterTextures[index] = texture;
+ }
+ }
+
+ o3djs.dump.dump('-- water: start loading deep water textures\n');
+ for (var i = 0; i < 30; i++) {
+ var textureName = 'deepwater.' + i + '.png';
+ var url = getCurrentPath() + 'assets/deepwater/' + textureName;
+ loader.loadTexture(pack, url, registerDeepWaterTexture(i));
+ }
+
+ var sampler = loadTexture(loader, pack, material, 'Reflectivity',
+ 'reflectivity_map.png');
+ sampler.addressModeU = g_o3d.Sampler.CLAMP;
+ sampler.addressModeV = g_o3d.Sampler.CLAMP;
+ sampler.magFilter = g_o3d.Sampler.LINEAR;
+ sampler.minFilter = g_o3d.Sampler.LINEAR;
+
+ sampler = loadTexture(loader, pack, material, 'ReflectionSampler',
+ 'rock_reflection_new.png');
+ sampler.addressModeU = g_o3d.Sampler.CLAMP;
+ sampler.addressModeV = g_o3d.Sampler.CLAMP;
+
+ sampler = loadTexture(loader, pack, material, 'HorizonRamp',
+ 'horizon_ramp_1.png');
+ sampler.addressModeU = g_o3d.Sampler.CLAMP;
+ sampler.addressModeV = g_o3d.Sampler.CLAMP;
+
+ sampler = loadTexture(loader, pack, material, 'SunRamp', 'sun_ramp_1.png');
+ sampler.addressModeU = g_o3d.Sampler.CLAMP;
+ sampler.addressModeV = g_o3d.Sampler.CLAMP;
+
+ sampler = loadTexture(loader, pack, material, 'RockTexture',
+ 'rock_tile_rgb.jpg');
+
+ connectParams(pack);
+ o3djs.dump.dump('Done applying Shader\n');
+
+ root.localMatrix = g_math.matrix4.translation([-2.751, 0, -8.563]);
+ o3djs.dump.dump('-- Finished water\n');
+ loader.finish();
+}
+
+
+/**
+ * Creates the sun shape, applies correct shaders and connects any special
+ * sun params.
+ * @param {!o3d.Pack} pack Pack to load the sun into.
+ * @param {!o3d.Transform} root Parent transform for the sun shape.
+ */
+function sun(pack, root) {
+ // Create the material
+ o3djs.dump.dump('-- start sun\n');
+
+ var material = pack.createObject('Material');
+ var effect = pack.createObject('Effect');
+ var fxString = document.getElementById('sun').value;
+ effect.loadFromFXString(fxString);
+ material.drawList = g_viewInfo.zOrderedDrawList;
+ material.effect = effect;
+ effect.createUniformParameters(material);
+ o3djs.dump.dump('-- created sun material\n');
+
+ // Probably not a necessary state, but available for the future
+ var state = pack.createObject('State');
+ state.getStateParam('CullMode').value = g_o3d.State.CULL_NONE;
+ material.state = state;
+
+ var sun = o3djs.primitives.createPlane(pack, material, 20, 20, 2, 2);
+ var loader = g_loader.createLoader(
+ function() {
+ root.addShape(sun);
+ });
+
+ var sampler = loadTexture(loader, pack, material, 'DiffuseSampler',
+ 'sun_compat.png');
+ sampler.addressModeU = g_o3d.Sampler.CLAMP;
+ sampler.addressModeV = g_o3d.Sampler.CLAMP;
+
+ connectParams(pack);
+ root.localMatrix = g_math.matrix4.mul(
+ g_math.matrix4.rotationX(90 * 0.0174),
+ g_math.matrix4.translation([0, 15, 140]));
+ loader.finish();
+ o3djs.dump.dump('-- Finished sun\n');
+}
+
+/**
+ * Creates the dume sphere shape, applies correct shaders and connects any
+ * special dome params.
+ * @param {!o3d.Pack} pack Pack to load the dome into.
+ * @param {!o3d.Transform} root Parent transform for the dome sphere shape.
+ */
+function dome(pack, root) {
+ // Create the material
+ o3djs.dump.dump('-- start dome\n');
+
+ var material = pack.createObject('Material');
+ var effect = pack.createObject('Effect');
+ var fxString = document.getElementById('dome').value;
+ effect.loadFromFXString(fxString);
+ material.effect = effect;
+ material.drawList = g_viewInfo.performanceDrawList;
+ effect.createUniformParameters(material);
+ o3djs.dump.dump('-- created dome material\n');
+
+ // Turn off culling since we are inside the sphere
+ var state = pack.createObject('State');
+ state.getStateParam('CullMode').value = g_o3d.State.CULL_NONE;
+ material.state = state;
+
+ var dome = o3djs.primitives.createSphere(pack, material, 150, 25, 25);
+
+ var loader = g_loader.createLoader(
+ function() {
+ root.addShape(dome);
+ });
+
+ var sampler = loadTexture(loader, pack, material, 'DiffuseSampler',
+ 'sky_compat.png');
+
+ sampler = loadTexture(loader, pack, material, 'HorizonRamp',
+ 'horizon_ramp.png');
+ sampler.addressModeU = g_o3d.Sampler.CLAMP;
+ sampler.addressModeV = g_o3d.Sampler.CLAMP;
+
+ sampler = loadTexture(loader, pack, material, 'SunRamp', 'sun_ramp.png');
+ sampler.addressModeU = g_o3d.Sampler.CLAMP;
+ sampler.addressModeV = g_o3d.Sampler.CLAMP;
+
+ connectParams(pack);
+ loader.finish();
+ o3djs.dump.dump('-- Finished dome\n');
+}
+
+/**
+ * Binds params to the global params so they update automatically.
+ * @param {!o3d.Pack} pack The pack containing the materials with params.
+ */
+function connectParams(pack) {
+ // Manually connect all the materials' lightWorldPos params to the global
+ // params.
+ var materials = pack.getObjectsByClassName('o3d.Material');
+ for (var m = 0; m < materials.length; ++m) {
+ var material = materials[m];
+ var param = material.getParam('lightDirection');
+ if (param) {
+ param.bind(g_lightDirectionParam);
+ }
+ param = material.getParam('inputTime');
+ if (param) {
+ param.bind(g_clockParam);
+ }
+ param = material.getParam('cameraEye');
+ if (param) {
+ param.value = [-2.751, 3.529, -8.563];
+ }
+ }
+}
+
+
+/**
+ * Remove any callbacks so they don't get called after the page has unloaded.
+ */
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
diff --git a/o3d/samples/yuv2rgb.html b/o3d/samples/yuv2rgb.html
new file mode 100644
index 0000000..d69d288
--- /dev/null
+++ b/o3d/samples/yuv2rgb.html
@@ -0,0 +1,206 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+
+O3D YUV to RGB conversion Tutorial
+
+In this sample we convert from a Y'UV420p encoded video frame to an
+RGB texture using a shader to do the conversion on the video hardware.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>YUV to RGB Conversion in a shader.</title>
+<script type="text/javascript" src="o3djs/base.js"></script>
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.effect');
+o3djs.require('o3djs.io');
+
+// Events
+// Run the init() once the page has finished loading.
+window.onload = init;
+
+// global variables
+var g_o3d;
+var g_math;
+var g_client;
+var g_pack;
+var g_viewInfo;
+var g_finished = false; // for selenium testing
+
+/**
+ * Creates the client area and sets up the secondary init entry point.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D, loads the effect, and draws the quad that
+ * will display the texture.
+ *
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initialize global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = o3dElement.client;
+
+ // Create a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Setup an orthographic projection
+ // Offset by half a pixel to avoid roundoff errors.
+ g_viewInfo.drawContext.projection = g_math.matrix4.orthographic(
+ -g_client.width * 0.5 + 0.5,
+ g_client.width * 0.5 + 0.5,
+ -g_client.height * 0.5 - 0.5,
+ g_client.height * 0.5 - 0.5,
+ 0.1,
+ 1000);
+
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [0, 500, 0],
+ [0, 0, 0],
+ [0, 0, -1]);
+
+ // Create and load the effect and the shader.
+ var effect = g_pack.createObject('Effect');
+ o3djs.effect.loadEffect(effect, 'shaders/yuv2rgb.shader');
+
+ // Create a Material for the effect.
+ var myMaterial = g_pack.createObject('Material');
+
+ // Set the material's drawList
+ myMaterial.drawList = g_viewInfo.performanceDrawList;
+
+ // Apply our effect to this material.
+ myMaterial.effect = effect;
+
+ // Create the params the effect needs on the material.
+ effect.createUniformParameters(myMaterial);
+
+ // Creates a quad using the effect that has a 1:1 aspect ratio.
+ var myShape = o3djs.primitives.createPlane(g_pack,
+ myMaterial,
+ 720, // width
+ 720, // height
+ 1, // quads across
+ 1); // quads down
+
+ // Get the material's sampler parameter
+ var sampler_param = myMaterial.getParam('textureSampler');
+ var sampler = g_pack.createObject('Sampler');
+ sampler.addressModeU = g_o3d.Sampler.WRAP;
+ sampler.addressModeV = g_o3d.Sampler.WRAP;
+ sampler.magFilter = g_o3d.Sampler.POINT;
+ sampler.minFilter = g_o3d.Sampler.POINT;
+
+ // Be sure to turn off MIP-mapping, since that causes problems when
+ // jumping around the image the way we are.
+ sampler.mipFilter = g_o3d.Sampler.NONE;
+ sampler_param.value = sampler;
+
+ // Set the source image width
+ var width_param = myMaterial.getParam('imageWidth');
+ width_param.value = 720.0;
+
+ // Set the source image height
+ var height_param = myMaterial.getParam('imageHeight');
+ height_param.value = 486.0;
+
+ // Load our Y'UV420p texture, encoded as a greyscale PNG image. This
+ // happens asynchronously.
+ var url = o3djs.util.getCurrentURI() + 'assets/shaving_cream.png';
+ o3djs.io.loadTexture(g_pack, url, function(texture, exception) {
+ if (exception) {
+ alert(exception);
+ return;
+ }
+ sampler.texture = texture;
+ // adjust the scale of our transform to match the aspect ratio of
+ // the texture. Of course we could also have waited until now to build
+ // our quad and set its width and height to match instead of scaling
+ // here.
+ var textureWidth = texture.width;
+
+ // Account for additional data on bottom of image.
+ var textureHeight = texture.height * 2.0 / 3.0;
+ var hScale = 1;
+ var vScale = 1;
+ if (textureWidth > textureHeight) {
+ vScale = textureHeight / textureWidth;
+ } else if (textureHeight > textureWidth) {
+ hScale = textureWidth / textureHeight;
+ }
+ // We now attach our quad to the root of the transform graph. We do
+ // this after the texture has loaded, otherwise we'd be attempting
+ // to display something invalid.
+ var root = g_client.root;
+ root.addShape(myShape);
+ root.scale(hScale, 1, vScale);
+
+ g_finished = true; // for selenium testing.
+ });
+}
+</script>
+</head>
+<body>
+<h1>YUV to RGB Conversion</h1>
+<p>This how to do image colorspace and format conversion in a shader.</p>
+<p>Here is the YUV encoded image (treated as an 8-bit greyscale image):</p>
+<p><img src="assets/shaving_cream.png" style="width: 720px; height: 729px"
+ alt="YUV encoded image"/></p>
+<p>Here is the original image, encoded as a JPEG image:</p>
+<p><img src="assets/shaving_cream.jpg" style="width: 720px; height: 486px"
+ alt="Original JPG encoded image"/></p>
+<p>Here is the O3D plugin loading the YUV image, and converting it
+to RGB in the shader on the fly.</p>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 720px; height: 486px"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/samples/zsorting.html b/o3d/samples/zsorting.html
new file mode 100644
index 0000000..1a0a942
--- /dev/null
+++ b/o3d/samples/zsorting.html
@@ -0,0 +1,221 @@
+<!--
+Copyright 2009, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+O3D ZSorting.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+ZSorting.
+</title>
+<!-- Include sample javascript library functions-->
+<script type="text/javascript" src="o3djs/base.js"></script>
+
+<!-- Our javascript code -->
+<script type="text/javascript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.effect');
+
+// Events
+// init() once the page has finished loading.
+// unload() when the page is unloaded.
+window.onload = init;
+window.onunload = unload;
+
+// global variables
+var g_timeMult = 1.0;
+var g_framesRendered = 0;
+var g_o3d;
+var g_math;
+var g_client;
+var g_viewInfo;
+var g_pack;
+var g_clock = 0;
+var g_finished = false; // for selenium testing
+var g_groupTransforms = [];
+var GROUPS_ACROSS = 2;
+var UNITS_ACROSS_GROUP = 2;
+var TOTAL_ACROSS = GROUPS_ACROSS * UNITS_ACROSS_GROUP;
+var HALF_WIDTH = TOTAL_ACROSS * 0.0;
+var UNIT_SPACING = 100;
+
+/**
+ * Make a grid of transforms and put a teapot instance on each one
+ * @param {!o3d.Pack} pack Pack to create transforms in.
+ * @param {!o3d.Shape} shape Shape to instance.
+ */
+function createInstances(pack, shape) {
+ for (var g = 0; g < GROUPS_ACROSS; g++) {
+ for (var h = 0; h < GROUPS_ACROSS; h++) {
+ for (var i = 0; i < GROUPS_ACROSS; i++) {
+ var groupTransform = pack.createObject('Transform');
+ g_groupTransforms[g_groupTransforms.length] = groupTransform;
+ groupTransform.parent = g_client.root;
+ groupTransform.translate(
+ (g * UNITS_ACROSS_GROUP - HALF_WIDTH) * UNIT_SPACING,
+ (h * UNITS_ACROSS_GROUP - HALF_WIDTH) * UNIT_SPACING,
+ (i * UNITS_ACROSS_GROUP - HALF_WIDTH) * UNIT_SPACING);
+ for (var x = 0; x < UNITS_ACROSS_GROUP; x++) {
+ for (var y = 0; y < UNITS_ACROSS_GROUP; y++) {
+ for (var z = 0; z < UNITS_ACROSS_GROUP; z++) {
+ var transform = pack.createObject('Transform');
+ transform.parent = groupTransform;
+ transform.addShape(shape);
+ transform.translate(
+ (x - UNITS_ACROSS_GROUP * 0.5) * UNIT_SPACING,
+ (y - UNITS_ACROSS_GROUP * 0.5) * UNIT_SPACING,
+ (z - UNITS_ACROSS_GROUP * 0.5) * UNIT_SPACING);
+ transform.createParam('colorMult', 'ParamFloat4').value = [
+ (g * UNITS_ACROSS_GROUP + x) * (1 / TOTAL_ACROSS),
+ (h * UNITS_ACROSS_GROUP + y) * (1 / TOTAL_ACROSS),
+ (i * UNITS_ACROSS_GROUP + z) * (1 / TOTAL_ACROSS),
+ 0.5];
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+/**
+ * Initializes O3D and creates one shape.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ // Initialize global variables and libraries.
+ var o3dElement = clientElements[0];
+ g_o3d = o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = o3dElement.client;
+
+ // Creates a pack to manage our resources/assets
+ g_pack = g_client.createPack();
+
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+
+ // Create our projection matrix, with a vertical field of view of 45
+ // degrees a near clipping plane of 0.1 and far clipping plane of 10000.
+ g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(45),
+ g_client.width / g_client.height,
+ 0.1,
+ 10000);
+
+ // Create and load the effect.
+ var effect = g_pack.createObject('Effect');
+ o3djs.effect.loadEffect(effect, 'shaders/phong-with-colormult.shader');
+
+ // Create a Material for the effect.
+ var material = g_pack.createObject('Material');
+
+ // Apply our effect to this material.
+ material.effect = effect;
+
+ // Set the material's drawList
+ material.drawList = g_viewInfo.zOrderedDrawList;
+
+ // Create the params the effect needs on the material.
+ effect.createUniformParameters(material);
+
+ // Set the material parameters.
+ material.getParam('lightWorldPos').value = [1000, 1000, 0];
+ material.getParam('lightIntensity').value = [1, 1, 1, 1];
+ material.getParam('ambientIntensity').value = [0.1, 0.1, 0.1, 1];
+ material.getParam('ambient').value = [1, 1, 1, 1];
+ material.getParam('diffuse').value = [1, 1, 1, 1];
+ material.getParam('specular').value = [0.5, 0.5, 0.5, 1];
+ material.getParam('shininess').value = 50;
+
+ // Create a sphere.
+ var shape = o3djs.primitives.createSphere(g_pack, material, 30, 20, 20);
+
+ createInstances(g_pack, shape);
+
+ // Setup an onrender callback for animation.
+ g_client.setRenderCallback(onrender);
+
+ g_finished = true; // for selenium testing.
+}
+
+// spin the camera.
+function onrender(renderEvent) {
+ g_framesRendered++;
+ // Get the number of seconds since the last render.
+ var elapsedTime = renderEvent.elapsedTime;
+ g_clock += elapsedTime * g_timeMult;
+
+ var x = Math.sin(g_clock * 0.1) * 400;
+ var z = Math.cos(g_clock * 0.1) * 400;
+ var y = Math.sin(g_clock * 0.2) * 400;
+
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [x, y, z], // eye
+ [0, 0, 0], // target
+ [0, 1, 0]); // up
+}
+
+/**
+ * Removes any callbacks so they don't get called after the page has unloaded.
+ */
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+</script>
+</head>
+<body>
+<h1>ZSorting</h1>
+Objects get zsorted for transparency.
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 800px; height: 600px"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>