From 05b47f7a8c5451f858dc220df0e3a97542edace6 Mon Sep 17 00:00:00 2001 From: "gspencer@google.com" Date: Wed, 27 May 2009 23:15:42 +0000 Subject: 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 --- o3d/samples/2d.html | 577 +++ o3d/samples/MANIFEST | 270 ++ o3d/samples/animated-scene.html | 227 ++ o3d/samples/animation.html | 411 ++ o3d/samples/archive-textures.html | 297 ++ o3d/samples/assets/android.png | Bin 0 -> 11280 bytes o3d/samples/assets/archive_textures.o3dtgz | Bin 0 -> 1019281 bytes o3d/samples/assets/block.png | Bin 0 -> 4461 bytes o3d/samples/assets/brush.png | Bin 0 -> 435 bytes o3d/samples/assets/egg.png | Bin 0 -> 29942 bytes o3d/samples/assets/empty.txt | 0 o3d/samples/assets/fullscreen.png | Bin 0 -> 865 bytes o3d/samples/assets/gauge.png | Bin 0 -> 174 bytes o3d/samples/assets/gaugeback.png | Bin 0 -> 2355 bytes o3d/samples/assets/gears_init.js | 86 + o3d/samples/assets/google-square.png | Bin 0 -> 12051 bytes o3d/samples/assets/iconback.png | Bin 0 -> 2316 bytes o3d/samples/assets/normalmap.dds | Bin 0 -> 43832 bytes o3d/samples/assets/one-pixel-white.tga | Bin 0 -> 48 bytes o3d/samples/assets/orange-flower.png | Bin 0 -> 11702 bytes o3d/samples/assets/particle-anim.png | Bin 0 -> 10050 bytes o3d/samples/assets/pillar.png | Bin 0 -> 7867 bytes o3d/samples/assets/purple-flower.png | Bin 0 -> 26558 bytes o3d/samples/assets/radar.png | Bin 0 -> 11122 bytes o3d/samples/assets/ripple.png | Bin 0 -> 3221 bytes o3d/samples/assets/rock_bumps.jpg | Bin 0 -> 1951243 bytes o3d/samples/assets/rock_texture.jpg | Bin 0 -> 714283 bytes o3d/samples/assets/shaving_cream.jpg | Bin 0 -> 322717 bytes o3d/samples/assets/shaving_cream.png | Bin 0 -> 496645 bytes o3d/samples/assets/square.png | Bin 0 -> 14491 bytes o3d/samples/assets/teapot_vertices.js | 32 + o3d/samples/assets/texture_b3.jpg | Bin 0 -> 38230 bytes o3d/samples/beachdemo/assets/pe_fire.jpg | Bin 0 -> 10629 bytes o3d/samples/beachdemo/assets/pe_mist.png | Bin 0 -> 54013 bytes o3d/samples/beachdemo/assets/sky-cubemap.dds | Bin 0 -> 786560 bytes o3d/samples/beachdemo/beachdemo.html | 1228 ++++++ o3d/samples/beachdemo/beachdemo.js | 2663 ++++++++++++ o3d/samples/box2d-3d/box2d-3d.html | 201 + o3d/samples/box2d-3d/demos/LICENSE.txt | 14 + o3d/samples/box2d-3d/demos/README.o3d | 5 + o3d/samples/box2d-3d/demos/compound.js | 71 + o3d/samples/box2d-3d/demos/crank.js | 79 + o3d/samples/box2d-3d/demos/demo_base.js | 59 + o3d/samples/box2d-3d/demos/demos.js | 271 ++ o3d/samples/box2d-3d/demos/draw_world.js | 90 + o3d/samples/box2d-3d/demos/manager.js | 210 + o3d/samples/box2d-3d/demos/pendulum.js | 25 + o3d/samples/box2d-3d/demos/stack.js | 37 + o3d/samples/box2d-3d/demos/top.js | 53 + o3d/samples/box2d-3d/third_party/box2d/LICENSE.txt | 14 + o3d/samples/box2d-3d/third_party/box2d/README.o3d | 5 + .../third_party/box2d/collision/ClipVertex.js | 35 + .../third_party/box2d/collision/Features.js | 61 + .../box2d-3d/third_party/box2d/collision/b2AABB.js | 45 + .../third_party/box2d/collision/b2Bound.js | 43 + .../third_party/box2d/collision/b2BoundValues.js | 31 + .../third_party/box2d/collision/b2BroadPhase.js | 898 +++++ .../third_party/box2d/collision/b2BufferedPair.js | 26 + .../third_party/box2d/collision/b2Collision.js | 738 ++++ .../third_party/box2d/collision/b2ContactID.js | 52 + .../third_party/box2d/collision/b2ContactPoint.js | 35 + .../third_party/box2d/collision/b2Distance.js | 333 ++ .../third_party/box2d/collision/b2Manifold.js | 34 + .../box2d-3d/third_party/box2d/collision/b2OBB.js | 34 + .../box2d-3d/third_party/box2d/collision/b2Pair.js | 60 + .../third_party/box2d/collision/b2PairCallback.js | 34 + .../third_party/box2d/collision/b2PairManager.js | 386 ++ .../third_party/box2d/collision/b2Proxy.js | 40 + .../third_party/box2d/collision/shapes/b2BoxDef.js | 49 + .../box2d/collision/shapes/b2CircleDef.js | 49 + .../box2d/collision/shapes/b2CircleShape.js | 198 + .../box2d/collision/shapes/b2MassData.js | 36 + .../box2d/collision/shapes/b2PolyDef.js | 58 + .../box2d/collision/shapes/b2PolyShape.js | 492 +++ .../third_party/box2d/collision/shapes/b2Shape.js | 339 ++ .../box2d/collision/shapes/b2ShapeDef.js | 109 + .../third_party/box2d/common/b2Settings.js | 72 + .../third_party/box2d/common/math/b2Mat22.js | 130 + .../third_party/box2d/common/math/b2Math.js | 218 + .../third_party/box2d/common/math/b2Vec2.js | 131 + .../box2d-3d/third_party/box2d/dynamics/b2Body.js | 469 +++ .../third_party/box2d/dynamics/b2BodyDef.js | 69 + .../box2d/dynamics/b2CollisionFilter.js | 42 + .../third_party/box2d/dynamics/b2ContactManager.js | 337 ++ .../third_party/box2d/dynamics/b2Island.js | 331 ++ .../third_party/box2d/dynamics/b2TimeStep.js | 27 + .../box2d-3d/third_party/box2d/dynamics/b2World.js | 522 +++ .../third_party/box2d/dynamics/b2WorldListener.js | 52 + .../box2d/dynamics/contacts/b2CircleContact.js | 102 + .../box2d/dynamics/contacts/b2Conservative.js | 228 ++ .../box2d/dynamics/contacts/b2Contact.js | 201 + .../box2d/dynamics/contacts/b2ContactConstraint.js | 45 + .../dynamics/contacts/b2ContactConstraintPoint.js | 40 + .../box2d/dynamics/contacts/b2ContactNode.js | 33 + .../box2d/dynamics/contacts/b2ContactRegister.js | 30 + .../box2d/dynamics/contacts/b2ContactSolver.js | 537 +++ .../box2d/dynamics/contacts/b2NullContact.js | 65 + .../dynamics/contacts/b2PolyAndCircleContact.js | 103 + .../box2d/dynamics/contacts/b2PolyContact.js | 163 + .../box2d/dynamics/joints/b2DistanceJoint.js | 264 ++ .../box2d/dynamics/joints/b2DistanceJointDef.js | 49 + .../box2d/dynamics/joints/b2GearJoint.js | 307 ++ .../box2d/dynamics/joints/b2GearJointDef.js | 50 + .../box2d/dynamics/joints/b2Jacobian.js | 49 + .../third_party/box2d/dynamics/joints/b2Joint.js | 200 + .../box2d/dynamics/joints/b2JointDef.js | 40 + .../box2d/dynamics/joints/b2JointNode.js | 33 + .../box2d/dynamics/joints/b2MouseJoint.js | 234 ++ .../box2d/dynamics/joints/b2MouseJointDef.js | 53 + .../box2d/dynamics/joints/b2PrismaticJoint.js | 676 ++++ .../box2d/dynamics/joints/b2PrismaticJointDef.js | 56 + .../box2d/dynamics/joints/b2PulleyJoint.js | 618 +++ .../box2d/dynamics/joints/b2PulleyJointDef.js | 70 + .../box2d/dynamics/joints/b2RevoluteJoint.js | 491 +++ .../box2d/dynamics/joints/b2RevoluteJointDef.js | 55 + .../box2d-3d/third_party/prototype-1.6.0.2.js | 4221 ++++++++++++++++++++ o3d/samples/build.scons | 348 ++ o3d/samples/canvas-fonts.html | 194 + o3d/samples/canvas-texturedraw.html | 268 ++ o3d/samples/canvas.html | 341 ++ o3d/samples/checkers.html | 895 +++++ o3d/samples/convolution.html | 397 ++ o3d/samples/culling.html | 340 ++ o3d/samples/customcamera.html | 404 ++ o3d/samples/debugging.html | 246 ++ o3d/samples/displayfps.html | 219 + o3d/samples/error-texture.html | 263 ++ o3d/samples/fullscreen.html | 538 +++ o3d/samples/gadgets/readme.txt | 11 + o3d/samples/gadgets/scatter-chart.xml | 434 ++ o3d/samples/generate-texture.html | 286 ++ o3d/samples/hellocube-colors.html | 352 ++ o3d/samples/hellocube-textures.html | 445 +++ o3d/samples/hellocube.html | 312 ++ o3d/samples/helloworld.html | 160 + o3d/samples/home-configurators/assets/empty.txt | 0 .../cb_images/cb_item_thumbnails.jpg | Bin 0 -> 42895 bytes .../home-configurators/cb_images/toolselector.gif | Bin 0 -> 344 bytes .../home-configurators/cb_images/unbranded_bg.png | Bin 0 -> 26868 bytes o3d/samples/home-configurators/cbassets/empty.txt | 0 .../craftsmanassets/craftsman_item_thumbnails.jpg | Bin 0 -> 34143 bytes o3d/samples/home-configurators/deletetool.js | 88 + o3d/samples/home-configurators/homedesigner.html | 171 + o3d/samples/home-configurators/movetool.js | 161 + o3d/samples/home-configurators/orbittool.js | 94 + o3d/samples/home-configurators/pantool.js | 77 + o3d/samples/home-configurators/rotatetool.js | 113 + .../home-configurators/searsassets/sears_bg.png | Bin 0 -> 65087 bytes .../searsassets/sears_item_thumbnails.jpg | Bin 0 -> 45942 bytes o3d/samples/home-configurators/viewer.js | 578 +++ o3d/samples/home-configurators/zoomtool.js | 76 + o3d/samples/hud-2d-overlay.html | 503 +++ o3d/samples/iframeit.html | 133 + o3d/samples/instance-override.html | 199 + o3d/samples/instancing.html | 208 + o3d/samples/interactive_logic.js | 576 +++ .../interactive_sampler_assets/images/bl.gif | Bin 0 -> 157 bytes .../interactive_sampler_assets/images/br.gif | Bin 0 -> 158 bytes .../interactive_sampler_assets/images/cleardot.gif | Bin 0 -> 43 bytes .../interactive_sampler_assets/images/corner.png | Bin 0 -> 1140 bytes .../interactive_sampler_assets/images/db_tl.png | Bin 0 -> 126 bytes .../interactive_sampler_assets/images/db_tr.png | Bin 0 -> 124 bytes .../images/google-small.png | Bin 0 -> 2445 bytes .../interactive_sampler_assets/images/lb_tl.png | Bin 0 -> 123 bytes .../interactive_sampler_assets/images/lb_tr.png | Bin 0 -> 124 bytes .../interactive_sampler_assets/images/sprites.gif | Bin 0 -> 6229 bytes .../images/sprites08132008.png | Bin 0 -> 4179 bytes .../interactive_sampler_assets/images/sprites2.jpg | Bin 0 -> 1087 bytes .../interactive_sampler_assets/images/tl.gif | Bin 0 -> 1185 bytes .../interactive_sampler_assets/images/tr.gif | Bin 0 -> 158 bytes o3d/samples/interactive_sampler_assets/styles.css | 282 ++ .../interactive_sampler_assets/styles_ie.css | 301 ++ o3d/samples/interactive_sampler_assets/utils.js | 200 + o3d/samples/interactive_samples.js | 183 + o3d/samples/io/README.txt | 29 + o3d/samples/io/actors/actor.js | 144 + o3d/samples/io/actors/arrow.js | 94 + o3d/samples/io/actors/avatar.js | 67 + o3d/samples/io/actors/coin.js | 68 + o3d/samples/io/actors/horizontalpad.js | 85 + o3d/samples/io/actors/mover.js | 48 + o3d/samples/io/actors/spikem.js | 126 + o3d/samples/io/actors/verticalpad.js | 84 + o3d/samples/io/autoincludes.js | 49 + o3d/samples/io/cutscenes.js | 148 + o3d/samples/io/dynamic_lights.js | 123 + o3d/samples/io/editor.html | 126 + o3d/samples/io/gamelogic.js | 578 +++ o3d/samples/io/init.js | 395 ++ o3d/samples/io/io.html | 173 + o3d/samples/io/levels/all_actors.js | 383 ++ o3d/samples/io/levels/all_actors.skp | Bin 0 -> 1514934 bytes o3d/samples/io/levels/map1.js | 156 + o3d/samples/io/levels/map1.skp | Bin 0 -> 663342 bytes o3d/samples/io/levels/starter_level.skp | Bin 0 -> 557136 bytes o3d/samples/io/sound/_MISS.mp3 | Bin 0 -> 627 bytes o3d/samples/io/sound/_PUNCH.mp3 | Bin 0 -> 940 bytes o3d/samples/io/sound/_SMASH.mp3 | Bin 0 -> 40333 bytes o3d/samples/io/sound/_woosh.mp3 | Bin 0 -> 6653 bytes o3d/samples/io/sound/ah.mp3 | Bin 0 -> 7549 bytes o3d/samples/io/sound/arrow.mp3 | Bin 0 -> 5459 bytes o3d/samples/io/sound/coin_3.mp3 | Bin 0 -> 12564 bytes o3d/samples/io/sound/music.mp3 | Bin 0 -> 3276800 bytes o3d/samples/io/sound/page.mp3 | Bin 0 -> 12146 bytes o3d/samples/io/sound/soundplayer.js | 147 + o3d/samples/io/sound/soundplayer.swf | Bin 0 -> 3869 bytes o3d/samples/io/sound/step1.mp3 | Bin 0 -> 4623 bytes o3d/samples/io/sound/step2.mp3 | Bin 0 -> 5041 bytes o3d/samples/io/sound/step3.mp3 | Bin 0 -> 4205 bytes o3d/samples/io/sound/ug.mp3 | Bin 0 -> 2951 bytes o3d/samples/io/ui/Thumbs.db | Bin 0 -> 27648 bytes o3d/samples/io/ui/bgtile.jpg | Bin 0 -> 2495 bytes o3d/samples/io/ui/book_capbottom.jpg | Bin 0 -> 18693 bytes o3d/samples/io/ui/book_capleft.jpg | Bin 0 -> 11719 bytes o3d/samples/io/ui/book_capright.jpg | Bin 0 -> 14239 bytes o3d/samples/io/ui/book_captop.jpg | Bin 0 -> 16493 bytes o3d/samples/io/ui/book_cover.jpg | Bin 0 -> 86247 bytes o3d/samples/io/ui/book_innercover.jpg | Bin 0 -> 25296 bytes o3d/samples/io/ui/book_page1.jpg | Bin 0 -> 97294 bytes o3d/samples/io/ui/book_page2.jpg | Bin 0 -> 95923 bytes o3d/samples/io/ui/book_page3.jpg | Bin 0 -> 75059 bytes o3d/samples/io/ui/book_pageblank.jpg | Bin 0 -> 44635 bytes o3d/samples/io/ui/covershadow.png | Bin 0 -> 6490 bytes o3d/samples/io/ui/io.css | 181 + o3d/samples/io/ui/logo.gif | Bin 0 -> 1577 bytes o3d/samples/io/ui/scrollwork.gif | Bin 0 -> 26553 bytes o3d/samples/juggler.html | 431 ++ o3d/samples/julia.html | 295 ++ o3d/samples/multiple-clients.html | 243 ++ o3d/samples/multiple-views.html | 220 + o3d/samples/o3djs/arcball.js | 139 + o3d/samples/o3djs/base.js | 779 ++++ o3d/samples/o3djs/camera.js | 364 ++ o3d/samples/o3djs/canvas.js | 387 ++ o3d/samples/o3djs/debug.js | 1257 ++++++ o3d/samples/o3djs/dump.js | 597 +++ o3d/samples/o3djs/effect.js | 737 ++++ o3d/samples/o3djs/element.js | 144 + o3d/samples/o3djs/error.js | 133 + o3d/samples/o3djs/event.js | 360 ++ o3d/samples/o3djs/fps.js | 469 +++ o3d/samples/o3djs/io.js | 577 +++ o3d/samples/o3djs/js_list.scons | 61 + o3d/samples/o3djs/loader.js | 194 + o3d/samples/o3djs/material.js | 461 +++ o3d/samples/o3djs/math.js | 2493 ++++++++++++ o3d/samples/o3djs/pack.js | 70 + o3d/samples/o3djs/particles.js | 1051 +++++ o3d/samples/o3djs/picking.js | 560 +++ o3d/samples/o3djs/primitives.js | 1698 ++++++++ o3d/samples/o3djs/quaternions.js | 479 +++ o3d/samples/o3djs/rendergraph.js | 420 ++ o3d/samples/o3djs/scene.js | 91 + o3d/samples/o3djs/serialization.js | 744 ++++ o3d/samples/o3djs/shape.js | 140 + o3d/samples/o3djs/simple.js | 556 +++ o3d/samples/o3djs/test.js | 342 ++ o3d/samples/o3djs/util.js | 928 +++++ o3d/samples/particles.html | 497 +++ o3d/samples/phongshading.html | 367 ++ o3d/samples/picking.html | 288 ++ o3d/samples/pingpong/instructions.gif | Bin 0 -> 3439 bytes o3d/samples/pingpong/logo.gif | Bin 0 -> 6958 bytes o3d/samples/pingpong/o3dPingPong.html | 862 ++++ o3d/samples/primitives.html | 245 ++ o3d/samples/procedural-texture.html | 258 ++ o3d/samples/render-mode.html | 283 ++ o3d/samples/render-targets.html | 340 ++ o3d/samples/rotatemodel.html | 247 ++ o3d/samples/sampler_index.html | 187 + o3d/samples/scatter-chart.html | 426 ++ o3d/samples/shader-test.html | 403 ++ o3d/samples/shaders/README | 16 + o3d/samples/shaders/binormal.shader | 64 + o3d/samples/shaders/bump.shader | 137 + o3d/samples/shaders/checker.shader | 117 + o3d/samples/shaders/diffuse.shader | 96 + o3d/samples/shaders/normal.shader | 64 + o3d/samples/shaders/one-channel-texture.shader | 94 + o3d/samples/shaders/phong-vertex-anim.shader | 95 + o3d/samples/shaders/phong-with-colormult.shader | 83 + o3d/samples/shaders/solid-color.shader | 74 + o3d/samples/shaders/tangent.shader | 64 + o3d/samples/shaders/texture-colormult.shader | 74 + o3d/samples/shaders/texture-only.shader | 71 + o3d/samples/shaders/vertex-color.shader | 75 + o3d/samples/shaders/yuv2rgb.shader | 235 ++ o3d/samples/simple.html | 108 + o3d/samples/simpletexture.html | 179 + o3d/samples/simpleviewer/assets/empty.txt | 0 o3d/samples/simpleviewer/simpleviewer.html | 365 ++ o3d/samples/skinning.html | 276 ++ o3d/samples/sobel.html | 342 ++ o3d/samples/stencil_example.html | 439 ++ o3d/samples/texturesamplers.html | 249 ++ o3d/samples/third_party/codemirror/LICENSE | 23 + o3d/samples/third_party/codemirror/README.o3d | 5 + .../third_party/codemirror/css/csscolors.css | 47 + o3d/samples/third_party/codemirror/css/docs.css | 38 + .../third_party/codemirror/css/jscolors.css | 47 + .../third_party/codemirror/css/xmlcolors.css | 51 + .../third_party/codemirror/js/codemirror.js | 189 + o3d/samples/third_party/codemirror/js/editor.js | 1052 +++++ .../third_party/codemirror/js/mirrorframe.js | 83 + o3d/samples/third_party/codemirror/js/parsecss.js | 155 + .../third_party/codemirror/js/parsehtmlmixed.js | 66 + .../third_party/codemirror/js/parsejavascript.js | 322 ++ .../third_party/codemirror/js/parsesparql.js | 162 + o3d/samples/third_party/codemirror/js/parsexml.js | 292 ++ o3d/samples/third_party/codemirror/js/select.js | 500 +++ .../third_party/codemirror/js/stringstream.js | 111 + o3d/samples/third_party/codemirror/js/tokenize.js | 57 + .../codemirror/js/tokenizejavascript.js | 168 + o3d/samples/third_party/codemirror/js/undo.js | 386 ++ o3d/samples/third_party/codemirror/js/util.js | 134 + .../lightbox/LICENSE-lightbox-iframe.txt | 66 + .../third_party/lightbox/LICENSE-prototype.txt | 16 + .../third_party/lightbox/lightbox-iframe.js | 199 + o3d/samples/third_party/lightbox/lightbox.css | 74 + o3d/samples/third_party/lightbox/prototype.js | 1785 +++++++++ o3d/samples/trends/assets/clouds.jpg | Bin 0 -> 107460 bytes .../trends/assets/earth-large-with-ocean-mask.png | Bin 0 -> 2367738 bytes o3d/samples/trends/assets/earth-large.jpg | Bin 0 -> 586564 bytes o3d/samples/trends/assets/earth.jpg | Bin 0 -> 46696 bytes o3d/samples/trends/assets/energy.png | Bin 0 -> 136 bytes o3d/samples/trends/assets/moon.jpg | Bin 0 -> 67152 bytes o3d/samples/trends/assets/night-large.jpg | Bin 0 -> 313629 bytes o3d/samples/trends/assets/night.jpg | Bin 0 -> 86315 bytes o3d/samples/trends/trends.html | 866 ++++ o3d/samples/tutorial-primitive.html | 120 + o3d/samples/vertex-shader-animation.html | 210 + o3d/samples/vertex-shader.html | 337 ++ .../waterdemo/assets/deepwater/deepwater.0.png | Bin 0 -> 211905 bytes .../waterdemo/assets/deepwater/deepwater.1.png | Bin 0 -> 212138 bytes .../waterdemo/assets/deepwater/deepwater.10.png | Bin 0 -> 211866 bytes .../waterdemo/assets/deepwater/deepwater.11.png | Bin 0 -> 212161 bytes .../waterdemo/assets/deepwater/deepwater.12.png | Bin 0 -> 212358 bytes .../waterdemo/assets/deepwater/deepwater.13.png | Bin 0 -> 212488 bytes .../waterdemo/assets/deepwater/deepwater.14.png | Bin 0 -> 212369 bytes .../waterdemo/assets/deepwater/deepwater.15.png | Bin 0 -> 212315 bytes .../waterdemo/assets/deepwater/deepwater.16.png | Bin 0 -> 211997 bytes .../waterdemo/assets/deepwater/deepwater.17.png | Bin 0 -> 212105 bytes .../waterdemo/assets/deepwater/deepwater.18.png | Bin 0 -> 211728 bytes .../waterdemo/assets/deepwater/deepwater.19.png | Bin 0 -> 211885 bytes .../waterdemo/assets/deepwater/deepwater.2.png | Bin 0 -> 211780 bytes .../waterdemo/assets/deepwater/deepwater.20.png | Bin 0 -> 211708 bytes .../waterdemo/assets/deepwater/deepwater.21.png | Bin 0 -> 211877 bytes .../waterdemo/assets/deepwater/deepwater.22.png | Bin 0 -> 211941 bytes .../waterdemo/assets/deepwater/deepwater.23.png | Bin 0 -> 211997 bytes .../waterdemo/assets/deepwater/deepwater.24.png | Bin 0 -> 211822 bytes .../waterdemo/assets/deepwater/deepwater.25.png | Bin 0 -> 211683 bytes .../waterdemo/assets/deepwater/deepwater.26.png | Bin 0 -> 211858 bytes .../waterdemo/assets/deepwater/deepwater.27.png | Bin 0 -> 211979 bytes .../waterdemo/assets/deepwater/deepwater.28.png | Bin 0 -> 212254 bytes .../waterdemo/assets/deepwater/deepwater.29.png | Bin 0 -> 212175 bytes .../waterdemo/assets/deepwater/deepwater.3.png | Bin 0 -> 212017 bytes .../waterdemo/assets/deepwater/deepwater.4.png | Bin 0 -> 212107 bytes .../waterdemo/assets/deepwater/deepwater.5.png | Bin 0 -> 212184 bytes .../waterdemo/assets/deepwater/deepwater.6.png | Bin 0 -> 212345 bytes .../waterdemo/assets/deepwater/deepwater.7.png | Bin 0 -> 212017 bytes .../waterdemo/assets/deepwater/deepwater.8.png | Bin 0 -> 211892 bytes .../waterdemo/assets/deepwater/deepwater.9.png | Bin 0 -> 211785 bytes o3d/samples/waterdemo/assets/empty.txt | 0 o3d/samples/waterdemo/assets/horizon_ramp.png | Bin 0 -> 2961 bytes o3d/samples/waterdemo/assets/horizon_ramp_1.png | Bin 0 -> 2961 bytes o3d/samples/waterdemo/assets/main_rock_normal.dds | Bin 0 -> 524416 bytes o3d/samples/waterdemo/assets/noise.png | Bin 0 -> 467149 bytes o3d/samples/waterdemo/assets/reflectivity_map.png | Bin 0 -> 170 bytes .../waterdemo/assets/rock_reflection_new.png | Bin 0 -> 24018 bytes .../waterdemo/assets/rock_tile_normal_x.jpg | Bin 0 -> 5102958 bytes o3d/samples/waterdemo/assets/rock_tile_rgb.jpg | Bin 0 -> 3558437 bytes o3d/samples/waterdemo/assets/sky_compat.png | Bin 0 -> 147484 bytes o3d/samples/waterdemo/assets/sun_compat.png | Bin 0 -> 55036 bytes o3d/samples/waterdemo/assets/sun_ramp.png | Bin 0 -> 2950 bytes o3d/samples/waterdemo/assets/sun_ramp_1.png | Bin 0 -> 2950 bytes o3d/samples/waterdemo/cameracontrol.js | 123 + o3d/samples/waterdemo/uicomponents.js | 112 + o3d/samples/waterdemo/waterdemo.html | 439 ++ o3d/samples/waterdemo/waterdemo.js | 646 +++ o3d/samples/yuv2rgb.html | 206 + o3d/samples/zsorting.html | 221 + 381 files changed, 70601 insertions(+) create mode 100644 o3d/samples/2d.html create mode 100644 o3d/samples/MANIFEST create mode 100644 o3d/samples/animated-scene.html create mode 100644 o3d/samples/animation.html create mode 100644 o3d/samples/archive-textures.html create mode 100644 o3d/samples/assets/android.png create mode 100644 o3d/samples/assets/archive_textures.o3dtgz create mode 100644 o3d/samples/assets/block.png create mode 100644 o3d/samples/assets/brush.png create mode 100644 o3d/samples/assets/egg.png create mode 100644 o3d/samples/assets/empty.txt create mode 100644 o3d/samples/assets/fullscreen.png create mode 100644 o3d/samples/assets/gauge.png create mode 100644 o3d/samples/assets/gaugeback.png create mode 100644 o3d/samples/assets/gears_init.js create mode 100644 o3d/samples/assets/google-square.png create mode 100644 o3d/samples/assets/iconback.png create mode 100644 o3d/samples/assets/normalmap.dds create mode 100644 o3d/samples/assets/one-pixel-white.tga create mode 100644 o3d/samples/assets/orange-flower.png create mode 100644 o3d/samples/assets/particle-anim.png create mode 100644 o3d/samples/assets/pillar.png create mode 100644 o3d/samples/assets/purple-flower.png create mode 100644 o3d/samples/assets/radar.png create mode 100644 o3d/samples/assets/ripple.png create mode 100644 o3d/samples/assets/rock_bumps.jpg create mode 100644 o3d/samples/assets/rock_texture.jpg create mode 100644 o3d/samples/assets/shaving_cream.jpg create mode 100644 o3d/samples/assets/shaving_cream.png create mode 100644 o3d/samples/assets/square.png create mode 100644 o3d/samples/assets/teapot_vertices.js create mode 100644 o3d/samples/assets/texture_b3.jpg create mode 100644 o3d/samples/beachdemo/assets/pe_fire.jpg create mode 100644 o3d/samples/beachdemo/assets/pe_mist.png create mode 100644 o3d/samples/beachdemo/assets/sky-cubemap.dds create mode 100644 o3d/samples/beachdemo/beachdemo.html create mode 100644 o3d/samples/beachdemo/beachdemo.js create mode 100644 o3d/samples/box2d-3d/box2d-3d.html create mode 100644 o3d/samples/box2d-3d/demos/LICENSE.txt create mode 100644 o3d/samples/box2d-3d/demos/README.o3d create mode 100644 o3d/samples/box2d-3d/demos/compound.js create mode 100644 o3d/samples/box2d-3d/demos/crank.js create mode 100644 o3d/samples/box2d-3d/demos/demo_base.js create mode 100644 o3d/samples/box2d-3d/demos/demos.js create mode 100644 o3d/samples/box2d-3d/demos/draw_world.js create mode 100644 o3d/samples/box2d-3d/demos/manager.js create mode 100644 o3d/samples/box2d-3d/demos/pendulum.js create mode 100644 o3d/samples/box2d-3d/demos/stack.js create mode 100644 o3d/samples/box2d-3d/demos/top.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/LICENSE.txt create mode 100644 o3d/samples/box2d-3d/third_party/box2d/README.o3d create mode 100644 o3d/samples/box2d-3d/third_party/box2d/collision/ClipVertex.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/collision/Features.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/collision/b2AABB.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/collision/b2Bound.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/collision/b2BoundValues.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/collision/b2BroadPhase.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/collision/b2BufferedPair.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/collision/b2Collision.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/collision/b2ContactID.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/collision/b2ContactPoint.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/collision/b2Distance.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/collision/b2Manifold.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/collision/b2OBB.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/collision/b2Pair.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/collision/b2PairCallback.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/collision/b2PairManager.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/collision/b2Proxy.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2BoxDef.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2CircleDef.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2CircleShape.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2MassData.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2PolyDef.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2PolyShape.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2Shape.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2ShapeDef.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/common/b2Settings.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/common/math/b2Mat22.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/common/math/b2Math.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/common/math/b2Vec2.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/b2Body.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/b2BodyDef.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/b2CollisionFilter.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/b2ContactManager.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/b2Island.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/b2TimeStep.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/b2World.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/b2WorldListener.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2CircleContact.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2Conservative.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2Contact.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactConstraint.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactConstraintPoint.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactNode.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactRegister.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactSolver.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2NullContact.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2PolyAndCircleContact.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2PolyContact.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2DistanceJoint.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2DistanceJointDef.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2GearJoint.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2GearJointDef.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2Jacobian.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2Joint.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2JointDef.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2JointNode.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2MouseJoint.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2MouseJointDef.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PrismaticJoint.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PrismaticJointDef.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PulleyJoint.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PulleyJointDef.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2RevoluteJoint.js create mode 100644 o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2RevoluteJointDef.js create mode 100644 o3d/samples/box2d-3d/third_party/prototype-1.6.0.2.js create mode 100644 o3d/samples/build.scons create mode 100644 o3d/samples/canvas-fonts.html create mode 100644 o3d/samples/canvas-texturedraw.html create mode 100644 o3d/samples/canvas.html create mode 100644 o3d/samples/checkers.html create mode 100644 o3d/samples/convolution.html create mode 100644 o3d/samples/culling.html create mode 100644 o3d/samples/customcamera.html create mode 100644 o3d/samples/debugging.html create mode 100644 o3d/samples/displayfps.html create mode 100644 o3d/samples/error-texture.html create mode 100644 o3d/samples/fullscreen.html create mode 100644 o3d/samples/gadgets/readme.txt create mode 100644 o3d/samples/gadgets/scatter-chart.xml create mode 100644 o3d/samples/generate-texture.html create mode 100644 o3d/samples/hellocube-colors.html create mode 100644 o3d/samples/hellocube-textures.html create mode 100644 o3d/samples/hellocube.html create mode 100644 o3d/samples/helloworld.html create mode 100644 o3d/samples/home-configurators/assets/empty.txt create mode 100644 o3d/samples/home-configurators/cb_images/cb_item_thumbnails.jpg create mode 100644 o3d/samples/home-configurators/cb_images/toolselector.gif create mode 100644 o3d/samples/home-configurators/cb_images/unbranded_bg.png create mode 100644 o3d/samples/home-configurators/cbassets/empty.txt create mode 100644 o3d/samples/home-configurators/craftsmanassets/craftsman_item_thumbnails.jpg create mode 100644 o3d/samples/home-configurators/deletetool.js create mode 100644 o3d/samples/home-configurators/homedesigner.html create mode 100644 o3d/samples/home-configurators/movetool.js create mode 100644 o3d/samples/home-configurators/orbittool.js create mode 100644 o3d/samples/home-configurators/pantool.js create mode 100644 o3d/samples/home-configurators/rotatetool.js create mode 100644 o3d/samples/home-configurators/searsassets/sears_bg.png create mode 100644 o3d/samples/home-configurators/searsassets/sears_item_thumbnails.jpg create mode 100644 o3d/samples/home-configurators/viewer.js create mode 100644 o3d/samples/home-configurators/zoomtool.js create mode 100644 o3d/samples/hud-2d-overlay.html create mode 100644 o3d/samples/iframeit.html create mode 100644 o3d/samples/instance-override.html create mode 100644 o3d/samples/instancing.html create mode 100644 o3d/samples/interactive_logic.js create mode 100644 o3d/samples/interactive_sampler_assets/images/bl.gif create mode 100644 o3d/samples/interactive_sampler_assets/images/br.gif create mode 100644 o3d/samples/interactive_sampler_assets/images/cleardot.gif create mode 100644 o3d/samples/interactive_sampler_assets/images/corner.png create mode 100644 o3d/samples/interactive_sampler_assets/images/db_tl.png create mode 100644 o3d/samples/interactive_sampler_assets/images/db_tr.png create mode 100644 o3d/samples/interactive_sampler_assets/images/google-small.png create mode 100644 o3d/samples/interactive_sampler_assets/images/lb_tl.png create mode 100644 o3d/samples/interactive_sampler_assets/images/lb_tr.png create mode 100644 o3d/samples/interactive_sampler_assets/images/sprites.gif create mode 100644 o3d/samples/interactive_sampler_assets/images/sprites08132008.png create mode 100644 o3d/samples/interactive_sampler_assets/images/sprites2.jpg create mode 100644 o3d/samples/interactive_sampler_assets/images/tl.gif create mode 100644 o3d/samples/interactive_sampler_assets/images/tr.gif create mode 100644 o3d/samples/interactive_sampler_assets/styles.css create mode 100644 o3d/samples/interactive_sampler_assets/styles_ie.css create mode 100644 o3d/samples/interactive_sampler_assets/utils.js create mode 100644 o3d/samples/interactive_samples.js create mode 100644 o3d/samples/io/README.txt create mode 100644 o3d/samples/io/actors/actor.js create mode 100644 o3d/samples/io/actors/arrow.js create mode 100644 o3d/samples/io/actors/avatar.js create mode 100644 o3d/samples/io/actors/coin.js create mode 100644 o3d/samples/io/actors/horizontalpad.js create mode 100644 o3d/samples/io/actors/mover.js create mode 100644 o3d/samples/io/actors/spikem.js create mode 100644 o3d/samples/io/actors/verticalpad.js create mode 100644 o3d/samples/io/autoincludes.js create mode 100644 o3d/samples/io/cutscenes.js create mode 100644 o3d/samples/io/dynamic_lights.js create mode 100644 o3d/samples/io/editor.html create mode 100644 o3d/samples/io/gamelogic.js create mode 100644 o3d/samples/io/init.js create mode 100644 o3d/samples/io/io.html create mode 100644 o3d/samples/io/levels/all_actors.js create mode 100644 o3d/samples/io/levels/all_actors.skp create mode 100644 o3d/samples/io/levels/map1.js create mode 100644 o3d/samples/io/levels/map1.skp create mode 100644 o3d/samples/io/levels/starter_level.skp create mode 100644 o3d/samples/io/sound/_MISS.mp3 create mode 100644 o3d/samples/io/sound/_PUNCH.mp3 create mode 100644 o3d/samples/io/sound/_SMASH.mp3 create mode 100644 o3d/samples/io/sound/_woosh.mp3 create mode 100644 o3d/samples/io/sound/ah.mp3 create mode 100644 o3d/samples/io/sound/arrow.mp3 create mode 100644 o3d/samples/io/sound/coin_3.mp3 create mode 100644 o3d/samples/io/sound/music.mp3 create mode 100644 o3d/samples/io/sound/page.mp3 create mode 100644 o3d/samples/io/sound/soundplayer.js create mode 100644 o3d/samples/io/sound/soundplayer.swf create mode 100644 o3d/samples/io/sound/step1.mp3 create mode 100644 o3d/samples/io/sound/step2.mp3 create mode 100644 o3d/samples/io/sound/step3.mp3 create mode 100644 o3d/samples/io/sound/ug.mp3 create mode 100644 o3d/samples/io/ui/Thumbs.db create mode 100644 o3d/samples/io/ui/bgtile.jpg create mode 100644 o3d/samples/io/ui/book_capbottom.jpg create mode 100644 o3d/samples/io/ui/book_capleft.jpg create mode 100644 o3d/samples/io/ui/book_capright.jpg create mode 100644 o3d/samples/io/ui/book_captop.jpg create mode 100644 o3d/samples/io/ui/book_cover.jpg create mode 100644 o3d/samples/io/ui/book_innercover.jpg create mode 100644 o3d/samples/io/ui/book_page1.jpg create mode 100644 o3d/samples/io/ui/book_page2.jpg create mode 100644 o3d/samples/io/ui/book_page3.jpg create mode 100644 o3d/samples/io/ui/book_pageblank.jpg create mode 100644 o3d/samples/io/ui/covershadow.png create mode 100644 o3d/samples/io/ui/io.css create mode 100644 o3d/samples/io/ui/logo.gif create mode 100644 o3d/samples/io/ui/scrollwork.gif create mode 100644 o3d/samples/juggler.html create mode 100644 o3d/samples/julia.html create mode 100644 o3d/samples/multiple-clients.html create mode 100644 o3d/samples/multiple-views.html create mode 100644 o3d/samples/o3djs/arcball.js create mode 100644 o3d/samples/o3djs/base.js create mode 100644 o3d/samples/o3djs/camera.js create mode 100644 o3d/samples/o3djs/canvas.js create mode 100644 o3d/samples/o3djs/debug.js create mode 100644 o3d/samples/o3djs/dump.js create mode 100644 o3d/samples/o3djs/effect.js create mode 100644 o3d/samples/o3djs/element.js create mode 100644 o3d/samples/o3djs/error.js create mode 100644 o3d/samples/o3djs/event.js create mode 100644 o3d/samples/o3djs/fps.js create mode 100644 o3d/samples/o3djs/io.js create mode 100644 o3d/samples/o3djs/js_list.scons create mode 100644 o3d/samples/o3djs/loader.js create mode 100644 o3d/samples/o3djs/material.js create mode 100644 o3d/samples/o3djs/math.js create mode 100644 o3d/samples/o3djs/pack.js create mode 100644 o3d/samples/o3djs/particles.js create mode 100644 o3d/samples/o3djs/picking.js create mode 100644 o3d/samples/o3djs/primitives.js create mode 100644 o3d/samples/o3djs/quaternions.js create mode 100644 o3d/samples/o3djs/rendergraph.js create mode 100644 o3d/samples/o3djs/scene.js create mode 100644 o3d/samples/o3djs/serialization.js create mode 100644 o3d/samples/o3djs/shape.js create mode 100644 o3d/samples/o3djs/simple.js create mode 100644 o3d/samples/o3djs/test.js create mode 100644 o3d/samples/o3djs/util.js create mode 100644 o3d/samples/particles.html create mode 100644 o3d/samples/phongshading.html create mode 100644 o3d/samples/picking.html create mode 100644 o3d/samples/pingpong/instructions.gif create mode 100644 o3d/samples/pingpong/logo.gif create mode 100644 o3d/samples/pingpong/o3dPingPong.html create mode 100644 o3d/samples/primitives.html create mode 100644 o3d/samples/procedural-texture.html create mode 100644 o3d/samples/render-mode.html create mode 100644 o3d/samples/render-targets.html create mode 100644 o3d/samples/rotatemodel.html create mode 100644 o3d/samples/sampler_index.html create mode 100644 o3d/samples/scatter-chart.html create mode 100644 o3d/samples/shader-test.html create mode 100644 o3d/samples/shaders/README create mode 100644 o3d/samples/shaders/binormal.shader create mode 100644 o3d/samples/shaders/bump.shader create mode 100644 o3d/samples/shaders/checker.shader create mode 100644 o3d/samples/shaders/diffuse.shader create mode 100644 o3d/samples/shaders/normal.shader create mode 100644 o3d/samples/shaders/one-channel-texture.shader create mode 100644 o3d/samples/shaders/phong-vertex-anim.shader create mode 100644 o3d/samples/shaders/phong-with-colormult.shader create mode 100644 o3d/samples/shaders/solid-color.shader create mode 100644 o3d/samples/shaders/tangent.shader create mode 100644 o3d/samples/shaders/texture-colormult.shader create mode 100644 o3d/samples/shaders/texture-only.shader create mode 100644 o3d/samples/shaders/vertex-color.shader create mode 100644 o3d/samples/shaders/yuv2rgb.shader create mode 100644 o3d/samples/simple.html create mode 100644 o3d/samples/simpletexture.html create mode 100644 o3d/samples/simpleviewer/assets/empty.txt create mode 100644 o3d/samples/simpleviewer/simpleviewer.html create mode 100644 o3d/samples/skinning.html create mode 100644 o3d/samples/sobel.html create mode 100644 o3d/samples/stencil_example.html create mode 100644 o3d/samples/texturesamplers.html create mode 100644 o3d/samples/third_party/codemirror/LICENSE create mode 100644 o3d/samples/third_party/codemirror/README.o3d create mode 100644 o3d/samples/third_party/codemirror/css/csscolors.css create mode 100644 o3d/samples/third_party/codemirror/css/docs.css create mode 100644 o3d/samples/third_party/codemirror/css/jscolors.css create mode 100644 o3d/samples/third_party/codemirror/css/xmlcolors.css create mode 100644 o3d/samples/third_party/codemirror/js/codemirror.js create mode 100644 o3d/samples/third_party/codemirror/js/editor.js create mode 100644 o3d/samples/third_party/codemirror/js/mirrorframe.js create mode 100644 o3d/samples/third_party/codemirror/js/parsecss.js create mode 100644 o3d/samples/third_party/codemirror/js/parsehtmlmixed.js create mode 100644 o3d/samples/third_party/codemirror/js/parsejavascript.js create mode 100644 o3d/samples/third_party/codemirror/js/parsesparql.js create mode 100644 o3d/samples/third_party/codemirror/js/parsexml.js create mode 100644 o3d/samples/third_party/codemirror/js/select.js create mode 100644 o3d/samples/third_party/codemirror/js/stringstream.js create mode 100644 o3d/samples/third_party/codemirror/js/tokenize.js create mode 100644 o3d/samples/third_party/codemirror/js/tokenizejavascript.js create mode 100644 o3d/samples/third_party/codemirror/js/undo.js create mode 100644 o3d/samples/third_party/codemirror/js/util.js create mode 100644 o3d/samples/third_party/lightbox/LICENSE-lightbox-iframe.txt create mode 100644 o3d/samples/third_party/lightbox/LICENSE-prototype.txt create mode 100644 o3d/samples/third_party/lightbox/lightbox-iframe.js create mode 100644 o3d/samples/third_party/lightbox/lightbox.css create mode 100644 o3d/samples/third_party/lightbox/prototype.js create mode 100644 o3d/samples/trends/assets/clouds.jpg create mode 100644 o3d/samples/trends/assets/earth-large-with-ocean-mask.png create mode 100644 o3d/samples/trends/assets/earth-large.jpg create mode 100644 o3d/samples/trends/assets/earth.jpg create mode 100644 o3d/samples/trends/assets/energy.png create mode 100644 o3d/samples/trends/assets/moon.jpg create mode 100644 o3d/samples/trends/assets/night-large.jpg create mode 100644 o3d/samples/trends/assets/night.jpg create mode 100644 o3d/samples/trends/trends.html create mode 100644 o3d/samples/tutorial-primitive.html create mode 100644 o3d/samples/vertex-shader-animation.html create mode 100644 o3d/samples/vertex-shader.html create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.0.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.1.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.10.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.11.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.12.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.13.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.14.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.15.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.16.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.17.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.18.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.19.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.2.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.20.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.21.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.22.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.23.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.24.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.25.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.26.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.27.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.28.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.29.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.3.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.4.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.5.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.6.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.7.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.8.png create mode 100644 o3d/samples/waterdemo/assets/deepwater/deepwater.9.png create mode 100644 o3d/samples/waterdemo/assets/empty.txt create mode 100644 o3d/samples/waterdemo/assets/horizon_ramp.png create mode 100644 o3d/samples/waterdemo/assets/horizon_ramp_1.png create mode 100644 o3d/samples/waterdemo/assets/main_rock_normal.dds create mode 100644 o3d/samples/waterdemo/assets/noise.png create mode 100644 o3d/samples/waterdemo/assets/reflectivity_map.png create mode 100644 o3d/samples/waterdemo/assets/rock_reflection_new.png create mode 100644 o3d/samples/waterdemo/assets/rock_tile_normal_x.jpg create mode 100644 o3d/samples/waterdemo/assets/rock_tile_rgb.jpg create mode 100644 o3d/samples/waterdemo/assets/sky_compat.png create mode 100644 o3d/samples/waterdemo/assets/sun_compat.png create mode 100644 o3d/samples/waterdemo/assets/sun_ramp.png create mode 100644 o3d/samples/waterdemo/assets/sun_ramp_1.png create mode 100644 o3d/samples/waterdemo/cameracontrol.js create mode 100644 o3d/samples/waterdemo/uicomponents.js create mode 100644 o3d/samples/waterdemo/waterdemo.html create mode 100644 o3d/samples/waterdemo/waterdemo.js create mode 100644 o3d/samples/yuv2rgb.html create mode 100644 o3d/samples/zsorting.html (limited to 'o3d/samples') 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 @@ + + + + + + + + +2D in O3D. + + + + + + + + +

2D In O3D

+Doing 2D in O3D. +
+ +
+ + + 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 @@ + + + + + + + + +Animated Scene. + + + + + + + + + +

Animated scene.

+Loads an scene with animation and plays it back. +
Loading...
+
+ +
+ + + 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 @@ + + + + + + + + +Animation. + + + + + + + + +

Animation

+Once the scene is setup no Javascript is running. +
+ +
+ + +
+ +
+ + 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 @@ + + + + + + + + +Progressive Texture Loading + + + + + +

Progressive archive loading

+This tutorial shows how to load textures progressively from a compressed archive. +

+ + +
+ + +

+ + +
+ +
+
+

File Loading Progress

+
+ + + + diff --git a/o3d/samples/assets/android.png b/o3d/samples/assets/android.png new file mode 100644 index 0000000..8ffbc61 Binary files /dev/null and b/o3d/samples/assets/android.png differ diff --git a/o3d/samples/assets/archive_textures.o3dtgz b/o3d/samples/assets/archive_textures.o3dtgz new file mode 100644 index 0000000..1d1cf35 Binary files /dev/null and b/o3d/samples/assets/archive_textures.o3dtgz differ diff --git a/o3d/samples/assets/block.png b/o3d/samples/assets/block.png new file mode 100644 index 0000000..b0e45c6 Binary files /dev/null and b/o3d/samples/assets/block.png differ diff --git a/o3d/samples/assets/brush.png b/o3d/samples/assets/brush.png new file mode 100644 index 0000000..6e9079f Binary files /dev/null and b/o3d/samples/assets/brush.png differ diff --git a/o3d/samples/assets/egg.png b/o3d/samples/assets/egg.png new file mode 100644 index 0000000..caa2718 Binary files /dev/null and b/o3d/samples/assets/egg.png differ diff --git a/o3d/samples/assets/empty.txt b/o3d/samples/assets/empty.txt new file mode 100644 index 0000000..e69de29 diff --git a/o3d/samples/assets/fullscreen.png b/o3d/samples/assets/fullscreen.png new file mode 100644 index 0000000..27a20c2 Binary files /dev/null and b/o3d/samples/assets/fullscreen.png differ diff --git a/o3d/samples/assets/gauge.png b/o3d/samples/assets/gauge.png new file mode 100644 index 0000000..4813779 Binary files /dev/null and b/o3d/samples/assets/gauge.png differ diff --git a/o3d/samples/assets/gaugeback.png b/o3d/samples/assets/gaugeback.png new file mode 100644 index 0000000..5d713f0 Binary files /dev/null and b/o3d/samples/assets/gaugeback.png 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 Binary files /dev/null and b/o3d/samples/assets/google-square.png differ diff --git a/o3d/samples/assets/iconback.png b/o3d/samples/assets/iconback.png new file mode 100644 index 0000000..28ccf80 Binary files /dev/null and b/o3d/samples/assets/iconback.png differ diff --git a/o3d/samples/assets/normalmap.dds b/o3d/samples/assets/normalmap.dds new file mode 100644 index 0000000..ed1fdd7 Binary files /dev/null and b/o3d/samples/assets/normalmap.dds 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 Binary files /dev/null and b/o3d/samples/assets/one-pixel-white.tga differ diff --git a/o3d/samples/assets/orange-flower.png b/o3d/samples/assets/orange-flower.png new file mode 100644 index 0000000..6b68390 Binary files /dev/null and b/o3d/samples/assets/orange-flower.png differ diff --git a/o3d/samples/assets/particle-anim.png b/o3d/samples/assets/particle-anim.png new file mode 100644 index 0000000..e78cfe7 Binary files /dev/null and b/o3d/samples/assets/particle-anim.png differ diff --git a/o3d/samples/assets/pillar.png b/o3d/samples/assets/pillar.png new file mode 100644 index 0000000..11fe2a4 Binary files /dev/null and b/o3d/samples/assets/pillar.png differ diff --git a/o3d/samples/assets/purple-flower.png b/o3d/samples/assets/purple-flower.png new file mode 100644 index 0000000..1c1c24c Binary files /dev/null and b/o3d/samples/assets/purple-flower.png differ diff --git a/o3d/samples/assets/radar.png b/o3d/samples/assets/radar.png new file mode 100644 index 0000000..113ca84 Binary files /dev/null and b/o3d/samples/assets/radar.png differ diff --git a/o3d/samples/assets/ripple.png b/o3d/samples/assets/ripple.png new file mode 100644 index 0000000..78c2d76 Binary files /dev/null and b/o3d/samples/assets/ripple.png differ diff --git a/o3d/samples/assets/rock_bumps.jpg b/o3d/samples/assets/rock_bumps.jpg new file mode 100644 index 0000000..0744fe4 Binary files /dev/null and b/o3d/samples/assets/rock_bumps.jpg differ diff --git a/o3d/samples/assets/rock_texture.jpg b/o3d/samples/assets/rock_texture.jpg new file mode 100644 index 0000000..d298941 Binary files /dev/null and b/o3d/samples/assets/rock_texture.jpg differ diff --git a/o3d/samples/assets/shaving_cream.jpg b/o3d/samples/assets/shaving_cream.jpg new file mode 100644 index 0000000..0d2bd09 Binary files /dev/null and b/o3d/samples/assets/shaving_cream.jpg differ diff --git a/o3d/samples/assets/shaving_cream.png b/o3d/samples/assets/shaving_cream.png new file mode 100644 index 0000000..9d9b4b0 Binary files /dev/null and b/o3d/samples/assets/shaving_cream.png differ diff --git a/o3d/samples/assets/square.png b/o3d/samples/assets/square.png new file mode 100644 index 0000000..fe6ca68 Binary files /dev/null and b/o3d/samples/assets/square.png 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 Binary files /dev/null and b/o3d/samples/assets/texture_b3.jpg 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 Binary files /dev/null and b/o3d/samples/beachdemo/assets/pe_fire.jpg 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 Binary files /dev/null and b/o3d/samples/beachdemo/assets/pe_mist.png 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 Binary files /dev/null and b/o3d/samples/beachdemo/assets/sky-cubemap.dds 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 @@ + + + + + + + Beach Demo + + + + + + +
+
+
+
+
+
+
+
+ + +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + 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 = ''; + 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 = ''; + var count = 0; + for (var prefix in propPrefixes) { + html += '' + + ''; + ++count; + } + g_propPanelElement.innerHTML = html + '
' + + '' + + prefix + + '
'; + 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 < + * @param {string} str to escape. + * @return {string} escaped string. + */ +function escapeHTML(str) { + return str.replace(/' + + '' + escapeHTML(paramObject.name) + '' + + ''; + 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 += '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + ''; + } + } + 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 = ''; + 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 + '
'; + + 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 @@ + + + + + + + Box2DJS in 3D + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Box2DJS in 3D

+

Based on box2d-js.
+Left Click to create a new object.
+Right Click (shift-click on OSX) to switch to the next demo. +

+
+
+
+
+ +
+ + 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: ']*>([\\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,'&').replace(//g,'>'); + }, + unescapeHTML: function() { + return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/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 '#'; + } +}; + +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 '#'; + }, + + 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: ['', '
', 1], + TBODY: ['', '
', 2], + TR: ['', '
', 3], + TD: ['
', '
', 4], + 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 "#"; + } +}); + +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("(.*)', 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%(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 @@ + + + + + + + + + +O3D Canvas + + + + + + + + + +

O3D Canvas Sample: Fonts

+
+ +
+ + + + 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 @@ + + + + + + + + + +O3D Canvas + + + + + + + + + +

O3D Canvas Sample: Drawing with bitmaps

+
+ +
+ +

+Brush URL: +
+ +

Click and drag to draw onto the red canvas surface. + + + 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 @@ + + + + + + + + + +O3D Canvas + + + + + + + + + +

O3D Canvas Sample

+
+ +
+ + + + 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 @@ + + + + + + + + +3D Checkers Game + + + + + + + + +

3D Checkers Game

+
+ +
+ + + + 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 @@ + + + + + + + + +O3D: Convolution Shader Sample + + + + + + +

Convolution Shader Example

+

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.

+
+ +
+ + + +
+ +
+ + + 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 @@ + + + + + + + + +Culling. + + + + + + + + +

Culling

+Objects off screen should get culled. +
+ +
+ + + + + + + + + + + +
Total Drawable Things:-
Total Transforms:-
Transforms Processed:-
Transforms Culled:-
Total DrawElements:-
DrawElements Processed:-
DrawElements Culled:-
DrawElements Rendered:-
Primitives Rendered:-
+ + 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 @@ + + + + + + + + +Tutorial B4: Cameras and events + + + + + + + +
+
+

Cameras and events

+

+This tutorial shows how we generate a simple cube mesh +and view it using a dynamically generated camera which can be animated. +

+

+Press Animate to animate the camera and use the textboxes to manually +set the position of the camera. +

+ +
+ +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
xyz
Eye + +
Target
Up vector + +
+ +
+
+
+
+ + 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 @@ + + + + + + + + +Debugging. + + + + + + + + +

Debugging.

+

This sample shows examples of using the debug.js utilities. + +

+ + + 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 @@ + + + + + + + + +Compute and Display FPS. + + + + + + + + + +
+

Show FPS

+

+This example shows computing and displaying FPS (Frames Per Second) +

+
+
+ + 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 @@ + + + + + +Error Texture + + + + +

Error Texture.

+
+Demonstrates how missing textures are handled. +
+ +
+ +
+
+ + + + +
Error: -
+ 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 @@ + + + + + + + + +HUD 2D Overlay with fullscreen toggle. + + + + + + + + +

HUD 2D Overlay

+HUD = Heads Up Display. +
+ +
+ + + 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 @@ + + + + + + + + + + + + + + +
+ + +
+
+Rotate: (W, S), (A, D), (K, L)       Zoom: (I, O)      + +
+
+]]> +
+
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 @@ + + + + + + + + +Generate Texture. + + + + + +

Generate Texture

+Shows how to create textures in Javascript. +
+ + +
+ + + 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 @@ + + + + + + + + +Hello Square Colors: Getting started with O3D, take 2. + + + + + +

Hello Square: Colors

+This example shows how to use parameters to the color output of a shader. +
+ + + +
+ + +
+

+ Change color: + + + + + + + +

+
+ + +
+ + + +
+ + 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 @@ + + + + + + + + +Hello Square Textures: Getting started with O3D, take 3. + + + + + +

Hello Cube: Textures

+This example shows how texture map a cube using an image fetched from a URL. +
+ + + +
+ +
+Image URL: +
+ + +
+ + + +
+ + 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 @@ + + + + + + + + +Hello Cube: Getting started with O3D + + + + + +

Hello Cube

+This example shows how to display a spinning red cube in O3D. +
+ + + +
+ + + +
+ + + +
+ + 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 @@ + + + + + + + + +Tutorial A1: Loading a scene + + + + + + + + +

Loading a scene.

+This tutorial shows how we load and display a scene in O3D. +
+ +
+ + + 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 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 Binary files /dev/null and b/o3d/samples/home-configurators/cb_images/cb_item_thumbnails.jpg 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 Binary files /dev/null and b/o3d/samples/home-configurators/cb_images/toolselector.gif 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 Binary files /dev/null and b/o3d/samples/home-configurators/cb_images/unbranded_bg.png 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 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 Binary files /dev/null and b/o3d/samples/home-configurators/craftsmanassets/craftsman_item_thumbnails.jpg 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 @@ + + + + + O3D Home Designer Demo + + + + + + + + + + + + + + +
+ + + + + +
+ +
+
+
+
+
+
+
+ Most Popular Items +

Drag and drop objects into the +
rooms on the left. +

+
+
+ + +

+
+
+ + +
+ +
+
+
+
+ +
+
+ + + + + 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 Binary files /dev/null and b/o3d/samples/home-configurators/searsassets/sears_bg.png 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 Binary files /dev/null and b/o3d/samples/home-configurators/searsassets/sears_item_thumbnails.jpg 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('
'); + } + $('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 @@ + + + + + + + + +HUD 2D Overlay. + + + + + + + + +

HUD 2D Overlay

+HUD = Heads Up Display. +
+ +
+ + + 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 @@ + + + + + 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 @@ + + + + + + + + +Instancing with Overrides. + + + + + + + + +

Instancing with overrides

+1000 Instances of the same sphere. +
+ +
+ + + 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 @@ + + + + + + + + +Instancing. + + + + + + + + +

Instancing

+1000 Teapots +
+ +
+ + + 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 tag will actually hide the tag + // unless we muck with the zIndex after the 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 = '
'; + html += '
'; + html += file; + html += '
'; + + 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 = /', scriptSrcEnd) + 9; + var found = false; + for (var z in this.currentCode) { + if (z == script) { + var data = this.replaceV8Call(this.currentCode[z].code); + 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 Binary files /dev/null and b/o3d/samples/interactive_sampler_assets/images/bl.gif 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 Binary files /dev/null and b/o3d/samples/interactive_sampler_assets/images/br.gif 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 Binary files /dev/null and b/o3d/samples/interactive_sampler_assets/images/cleardot.gif 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 Binary files /dev/null and b/o3d/samples/interactive_sampler_assets/images/corner.png 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 Binary files /dev/null and b/o3d/samples/interactive_sampler_assets/images/db_tl.png 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 Binary files /dev/null and b/o3d/samples/interactive_sampler_assets/images/db_tr.png 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 Binary files /dev/null and b/o3d/samples/interactive_sampler_assets/images/google-small.png 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 Binary files /dev/null and b/o3d/samples/interactive_sampler_assets/images/lb_tl.png 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 Binary files /dev/null and b/o3d/samples/interactive_sampler_assets/images/lb_tr.png 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 Binary files /dev/null and b/o3d/samples/interactive_sampler_assets/images/sprites.gif 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 Binary files /dev/null and b/o3d/samples/interactive_sampler_assets/images/sprites08132008.png 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 Binary files /dev/null and b/o3d/samples/interactive_sampler_assets/images/sprites2.jpg 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 Binary files /dev/null and b/o3d/samples/interactive_sampler_assets/images/tl.gif 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 Binary files /dev/null and b/o3d/samples/interactive_sampler_assets/images/tr.gif 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(''); +document.write(''); +document.write(''); +document.write(''); +document.write(''); +document.write(''); +document.write(''); +document.write(''); + +document.write(''); +document.write(''); 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 @@ + + + + + Level Editor - Don't open this is a browser. + It's only good inside a SketchUp WebDialog + + + + + + + + + +
Select an action...
+ + + + + +
Or select an existing level to load...
+ + List... + + + + + 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...
'; + for (var i = 0; i < levels.length; i++) { + var level = levels[i]; + var str = '' + + level.name + '
' + $('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 @@ + + + + The Journies of Prince IO: An O3D Adventure + + + + + + + + + + + + +
+ + + + + + + +

+ + Choose a level... + +
+
+

 
+ +
+ +
+ +
+ + + + 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 Binary files /dev/null and b/o3d/samples/io/levels/all_actors.skp 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 Binary files /dev/null and b/o3d/samples/io/levels/map1.skp 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 Binary files /dev/null and b/o3d/samples/io/levels/starter_level.skp differ diff --git a/o3d/samples/io/sound/_MISS.mp3 b/o3d/samples/io/sound/_MISS.mp3 new file mode 100644 index 0000000..7acfc80 Binary files /dev/null and b/o3d/samples/io/sound/_MISS.mp3 differ diff --git a/o3d/samples/io/sound/_PUNCH.mp3 b/o3d/samples/io/sound/_PUNCH.mp3 new file mode 100644 index 0000000..abb7277 Binary files /dev/null and b/o3d/samples/io/sound/_PUNCH.mp3 differ diff --git a/o3d/samples/io/sound/_SMASH.mp3 b/o3d/samples/io/sound/_SMASH.mp3 new file mode 100644 index 0000000..c655a11 Binary files /dev/null and b/o3d/samples/io/sound/_SMASH.mp3 differ diff --git a/o3d/samples/io/sound/_woosh.mp3 b/o3d/samples/io/sound/_woosh.mp3 new file mode 100644 index 0000000..5687be2 Binary files /dev/null and b/o3d/samples/io/sound/_woosh.mp3 differ diff --git a/o3d/samples/io/sound/ah.mp3 b/o3d/samples/io/sound/ah.mp3 new file mode 100644 index 0000000..ff40207 Binary files /dev/null and b/o3d/samples/io/sound/ah.mp3 differ diff --git a/o3d/samples/io/sound/arrow.mp3 b/o3d/samples/io/sound/arrow.mp3 new file mode 100644 index 0000000..e106d668 Binary files /dev/null and b/o3d/samples/io/sound/arrow.mp3 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 Binary files /dev/null and b/o3d/samples/io/sound/coin_3.mp3 differ diff --git a/o3d/samples/io/sound/music.mp3 b/o3d/samples/io/sound/music.mp3 new file mode 100644 index 0000000..653d191 Binary files /dev/null and b/o3d/samples/io/sound/music.mp3 differ diff --git a/o3d/samples/io/sound/page.mp3 b/o3d/samples/io/sound/page.mp3 new file mode 100644 index 0000000..f2c9a08 Binary files /dev/null and b/o3d/samples/io/sound/page.mp3 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 = '
' + html = ' + + + + + + + + Juggler Shader + + + + + + + + + + + +
+

Juggler

+

+ This sample displays a juggling pattern computed entirely in a shader. +

+ 3 Balls + 5 Balls + 7 Balls + 9 Balls + Animate +
+

+ + + + +
+ +
+ +
+ + +
+ + 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 @@ + + + + + + + + +Julia Set Pixel Shader + + + + + + + + + + + +
+

Julia Set

+

+ This sample draws an animated julia set in real time using + the pixel shader for the computation. +

+ + + + +
+ +
+ +
+ + +
+ + + + 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 @@ + + + + + + + + +Multiple Clients + + + + + + +

Multiple Clients

+ + + +
+ + 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 @@ + + + + + + + + +Multiple Views + + + + + + + + +

Multiple Views

+
+ +
+ + + 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.} + */ +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(' + // 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 = + '
' + + '

' + subMessage + '
' + + 'Click here to download.' + + '
' + 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 = '' + + '

More Info:
' + error + '
'; + } + return html; + }; + + // TODO: This needs to be localized OR we could insert a html like + // + // 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 = + '
' + + '

' + subMessage + + '

Click Here to go the O3D website' + + moreInfo(error) + + '
'; + 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 = + '
' + + '

' + subMessage + + moreInfo(error) + + '
'; + } else { + subMessage = + 'A unknown error has prevented O3D from starting. Try downloading ' + + 'new drivers or checking for OS updates.'; + message = + '
' + + '

' + subMessage + + moreInfo(error) + + '
'; + } + 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.} + */ +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 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: + *
  • FloatingPointTextures: Includes the formats R32F, ABGR16F and + * ABGR32F
  • + * 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 = + '' + + '' + + '' + + ''; + 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 -- + * <div id="o3d" style="width:800px; height:600px"></div> + * + * -- A div that fills its containing element -- + * <div id="o3d" style="width:100%; height:100%"></div> + * + * In both cases, a DOCTYPE is probably required. + * + * You can also request certain features by adding the attribute + * 'o3d_features' as in + * + * <div id="o3d" o3d_features="FloatingPointTextures"></div> + * + * This allows you to specify different features per area. Otherwise you can + * request features as an argument to this function. + * + * @param {!function(Array.): 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: + * + *
  • FloatingPointTextures: Includes the formats R32F, ABGR16F and + * ABGR32F
  • + *
  • LargeGeometry: Allows buffers to have more than 65534 elements.
  • + *
  • NotAntiAliased: Turns off anti-aliasing
  • + *
  • InitStatus=X: Where X is a number. Allows simulatation of the plugin + * failing
  • + * + * 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 @@ + + + + + + + + +Particles. + + + + + + + + +

    Particles

    +
    + +
    + + + 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 @@ + + + + + + + + +Tutorial B5: Phong Shading + + + + + + + +
    +

    Phong shading

    +

    +This tutorial shows how we generate a custom mesh and +perform Phong illumination using a shader. +

    +

    +This sample displays a Phong shaded white sphere lit by a single red light. +

    +
    + +
    + +
    + + +
    + + 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 @@ + + + + + + + + +O3D Picking Example. + + + + + + + +

    Picking

    +Click on an object +
    + +
    + +
    PICKED:
    + + diff --git a/o3d/samples/pingpong/instructions.gif b/o3d/samples/pingpong/instructions.gif new file mode 100644 index 0000000..d142083 Binary files /dev/null and b/o3d/samples/pingpong/instructions.gif differ diff --git a/o3d/samples/pingpong/logo.gif b/o3d/samples/pingpong/logo.gif new file mode 100644 index 0000000..379d196 Binary files /dev/null and b/o3d/samples/pingpong/logo.gif 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 @@ + + +o3dPingPong + + + + + + +
    +
    + +
    +
    + +
    + +
    + + + + + + 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 @@ + + + + + + + + +Primitives + + + + + +

    Primitives

    +This example shows how to use the primitives utility library to make various +shapes. +
    + +
    + + + 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 @@ + + + + + + + + +Applying a Procedural Texture to a scene + + + + + + + + +

    Applying a Procedural Texture to a scene

    +This tutorial shows how to apply a procedural texture to a scene. +
    + +
    + + +
    + +
    + + 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 @@ + + + + + + + Render Mode Example. + + + + + + + +
    +

    +Render Mode Example. +

    +

    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.

    +

    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.

    + + +
    +
    +
    +
    +
    +

    +Drag The Mouse To Rotate
    +Scrollwheel To Zoom
    +Resize The Window To Resize The View +

    +
    + + + + 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 @@ + + + + + + + + +O3D: Render-Target Sample + + + + + + +

    Basic Render-Target Example

    +
    + +
    + + + +
    + +
    + + + 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 @@ + + + + + + + + +Tutorial A2: Transformations + + + + + + + +

    Transformations

    +This tutorial shows how to perform transformations on a scene that +has been loaded. +
    + +
    + +

    Use W, S, A, D to rotate.

    +
    + + + + + +
    + + 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 @@ + + + + + + + + + + + + + + + + + + + + + O3D Interactive Sampler + + + + + + + + + +
    +
    + + + + + +
    +

    Client 3D Interactive Samples

    +
    + +
    + +
    + +
    + + + + + + + + + + + + + + + + + + +
    +

    Pick

    +
    +
    + +
    +

    Edit

    + +
    +
    + +
    +   + + +
    + + +
    +
    +

    Run

    +
    + + +
    +
    +
    +
    + + + + + + + + + + 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 @@ + + + + + + + + +3D Scatter Chart + + + + + + + + +

    Scatter Chart - rotate & zoom with mouse or keyboard

    + +
    + +
    +
    +Rotate: (W, S), (A, D), (K, L)       +Zoom: (I, O)      + +
    +
    + + + 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 @@ + + + + + + + + +Shader Test + + + + + + + + + +

    Shader Test

    +This example is useful for testing a shader or checking a scene. Clicking on the scene will temporarily stop rotation. +
    + +
    + +

    + + + 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 @@ + + + + + + + + +Simple + + + + + + + + +

    Simple

    +Using the simple library.
    +View the source of this sample to see the point.
    + +
    + + + 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 @@ + + + + + + + + +Tutorial B3: Textures + + + + + +

    Simple texturing

    +This tutorial shows how we use textures in O3D. +
    + + +
    + + + diff --git a/o3d/samples/simpleviewer/assets/empty.txt b/o3d/samples/simpleviewer/assets/empty.txt new file mode 100644 index 0000000..e69de29 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 @@ + + + + + + + Simple Scene Viewer + + + + + + + +
    +
    +

    +Simple Scene Viewer +

    +
    +
    +
    +

    +

    + + +
    + +Drag The Mouse To Rotate
    +Scrollwheel To Zoom
    +Resize The Window To Resize The View +
    +
    +
    + + + + 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 @@ + + + + + + + + +Skinning. + + + + + + + + +

    Skinning

    + +
    + + + 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 @@ + + + + + + + + +O3D: Sobel Shader Sample + + + + + + +

    Sobel Edge Detection Shader Example

    +
    + +
    + + + +
    + +
    + + + 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 @@ + + + + + + + + +Stencil Example + + + + + + + + +

    Stencil Example

    +Shows 4 "Views" using 4 stencils, and 9 scene files. +4 shapes, 4 skydomes, 1 frame drawn with an orthographic projection. +
    + + +
    + + + 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 @@ + + + + + + + + + +Texture Samplers + + + + + +

    Texture Sampler Example

    +This tutorial demonstrates various texture sampler settings. +
    + +
    +

    Scrollwheel To Zoom


    + + + 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 = [""]; + forEach(options.stylesheet, function(file) { + html.push(""); + }); + forEach(options.basefiles.concat(options.parserfile), function(file) { + html.push(""); + }); + html.push(""); + + 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 and
    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
    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 + // and
    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 , 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 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
    , or null for the first + // line. If given a
    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 ( 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
    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, "= 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 == "")); + else if (content == "")); + 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(""); + 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, "
    "); + }; + + 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 = "
    close (x)