diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..5cea5ae2b --- /dev/null +++ b/.eslintignore @@ -0,0 +1,6 @@ +node_modules + +dist +build +lib + diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 000000000..048d2ec7c --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,488 @@ +{ + "env": { + "es6": true, + "browser": true, + "node": true, + "mocha": true + }, + "globals": { + "global": false + }, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 8, + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint" + ], + "rules": { + // Possible Errors + "comma-dangle": [ + 1, + "only-multiline" + ], + "no-cond-assign": [ + 1, + "except-parens" + ], + "no-constant-condition": 1, + "no-control-regex": 1, + "no-debugger": 1, + "no-dupe-args": 1, + "no-dupe-keys": 1, + "no-duplicate-case": 1, + "no-empty": 1, + "no-empty-character-class": 1, + "no-ex-assign": 1, + "no-extra-boolean-cast": 1, + "no-extra-parens": 0, + "no-extra-semi": 1, + "no-func-assign": 1, + "no-inner-declarations": 1, + "no-invalid-regexp": 1, + "no-irregular-whitespace": 1, + "no-negated-in-lhs": 1, + "no-obj-calls": 1, + "no-regex-spaces": 1, + "no-sparse-arrays": 1, + "no-unexpected-multiline": 1, + "no-unreachable": 1, + "no-unsafe-finally": 1, + "use-isnan": 1, + "valid-jsdoc": [ + 0, + { + "requireReturn": false, + "requireReturnType": true, + "requireParamDescription": true, + "requireReturnDescription": true + } + ], + "valid-typeof": 1, + // Best Practices + "accessor-pairs": 1, + "array-callback-return": 1, + "block-scoped-var": 1, + "complexity": 0, + "curly": [ + 1, + "multi-line" + ], + "default-case": 0, + "dot-location": [ + 1, + "property" + ], + "dot-notation": 0, + "eqeqeq": 1, + "guard-for-in": 0, + "no-alert": 1, + "no-caller": 1, + "no-case-declarations": 0, + "no-div-regex": 1, + "no-else-return": 1, + "no-empty-function": 0, + "no-empty-pattern": 1, + "no-eq-null": 1, + "no-eval": 1, + "no-extend-native": 1, + "no-extra-bind": 1, + "no-extra-label": 1, + "no-fallthrough": 1, + "no-floating-decimal": 1, + "no-implicit-coercion": [ + 1, + { + "boolean": false, + "number": true, + "string": true, + "allow": [] + } + ], + "no-implicit-globals": 1, + "no-implied-eval": 1, + "no-invalid-this": 0, + "no-iterator": 1, + "no-labels": 1, + "no-lone-blocks": 1, + "no-loop-func": 1, + "no-magic-numbers": [ + 0 + ], + "no-multi-spaces": [ + 1, + { + "exceptions": { + "VariableDeclarator": true, + "ImportDeclaration": true, + "Property": true + } + } + ], + "no-multi-str": 1, + "no-native-reassign": 1, + "no-new": 1, + "no-new-func": 1, + "no-new-wrappers": 1, + "no-octal": 1, + "no-octal-escape": 1, + "no-param-reassign": 0, + "no-redeclare": 1, + "no-return-assign": 1, + "no-script-url": 1, + "no-self-assign": 1, + "no-self-compare": 1, + "no-sequences": 1, + "no-unmodified-loop-condition": 1, + "no-unused-expressions": "off", + "no-unused-labels": 1, + "no-useless-call": 1, + "no-useless-concat": 1, + "no-useless-escape": 0, + "no-void": 1, + "no-warning-comments": 0, + "no-with": 1, + "radix": 1, + "vars-on-top": 0, + "wrap-iife": [ + 1, + "inside" + ], + "yoda": 0, + // Strict Mode + "strict": [ + 1, + "global" + ], + // Variables + "init-declarations": 0, + "no-catch-shadow": 1, + "no-delete-var": 1, + "no-label-var": 1, + "no-restricted-globals": [ + "error", + "event", + "fdescribe" + ], + "no-shadow": 0, + "no-shadow-restricted-names": 1, + "no-undef": 1, + "no-undef-init": 1, + "no-undefined": 0, + "no-unused-vars": "off", + // Node.js and CommonJS + "callback-return": 0, + "global-require": 1, + "handle-callback-err": 1, + "no-mixed-requires": 1, + "no-new-require": 1, + "no-path-concat": 1, + "no-process-env": 0, + "no-process-exit": 0, + "no-restricted-modules": 0, + "no-sync": 0, + // Stylistic Issues + "array-bracket-spacing": [ + 1, + "never" + ], + "block-spacing": [ + 1, + "always" + ], + "brace-style": [ + 1, + "allman", + { + "allowSingleLine": true + } + ], + "camelcase": [ + 1, + { + "properties": "never" + } + ], + "comma-spacing": [ + 1, + { + "before": false, + "after": true + } + ], + "comma-style": [ + 1, + "last" + ], + "computed-property-spacing": [ + 1, + "never" + ], + "consistent-this": 0, + "eol-last": [ + 1, + "unix" + ], + "func-style": [ + 1, + "declaration", + { + "allowArrowFunctions": true + } + ], + "id-blacklist": 0, + "id-length": 0, + "id-match": 0, + "jsx-quotes": [ + 1, + "prefer-double" + ], + "key-spacing": [ + 1, + { + "beforeColon": false, + "afterColon": true, + "mode": "minimum" + } + ], + "keyword-spacing": [ + 1, + { + "before": true, + "after": true + } + ], + "lines-around-comment": 0, + "max-depth": [ + 1, + 6 + ], + "max-lines": 0, + "max-nested-callbacks": [ + 1, + { + "max": 5 + } + ], + "max-params": [ + 1, + { + "max": 10 + } + ], + "max-statements": 0, + "new-cap": [ + 1, + { + "newIsCap": true, + "capIsNew": false, + "properties": false + } + ], + "new-parens": 1, + "newline-before-return": 1, + "newline-per-chained-call": [ + 1, + { + "ignoreChainWithDepth": 4 + } + ], + "no-array-constructor": 1, + "no-bitwise": 0, + "no-continue": 0, + "no-inline-comments": 0, + "no-lonely-if": 1, + "no-mixed-spaces-and-tabs": 1, + "no-multiple-empty-lines": [ + 1, + { + "max": 1, + "maxEOF": 1, + "maxBOF": 0 + } + ], + "no-negated-condition": 0, + "no-new-object": 1, + "no-plusplus": 0, + "no-restricted-syntax": [ + 1, + "DebuggerStatement", + "EmptyStatement", + "LabeledStatement", + "WithStatement" + ], + "no-spaced-func": 1, + "no-ternary": 0, + "no-trailing-spaces": 1, + "no-underscore-dangle": 0, + "no-unneeded-ternary": 1, + "no-whitespace-before-property": 1, + "object-curly-newline": 0, + "object-curly-spacing": [ + 1, + "always" + ], + "object-property-newline": 0, + "one-var": [ + 1, + "never" + ], + "one-var-declaration-per-line": [ + 1, + "always" + ], + "operator-assignment": 0, + "operator-linebreak": [ + 1, + "before" + ], + "padded-blocks": [ + 1, + "never" + ], + "quote-props": [ + 1, + "as-needed" + ], + "quotes": [ + 1, + "single", + { + "allowTemplateLiterals": true + } + ], + "require-jsdoc": [ + 0, + { + "require": { + "FunctionDeclaration": false, + "ClassDeclaration": true, + "MethodDefinition": true + } + } + ], + "semi": [ + 1, + "always" + ], + "semi-spacing": [ + 1, + { + "before": false, + "after": true + } + ], + "sort-vars": 0, + "space-before-blocks": [ + 1, + "always" + ], + "space-in-parens": [ + 1, + "never" + ], + "space-infix-ops": 1, + "space-unary-ops": [ + 1, + { + "words": true, + "nonwords": false + } + ], + "spaced-comment": [ + 1, + "always" + ], + "unicode-bom": [ + 1, + "never" + ], + "wrap-regex": 1, + // ECMAScript 6 + "arrow-body-style": [ + 1, + "as-needed" + ], + "arrow-parens": 1, + "arrow-spacing": [ + 1, + { + "before": true, + "after": true + } + ], + "constructor-super": 1, + "generator-star-spacing": [ + 1, + { + "before": true, + "after": false + } + ], + "no-class-assign": 1, + "no-confusing-arrow": [ + 1, + { + "allowParens": true + } + ], + "no-const-assign": 1, + "no-dupe-class-members": 0, + "no-duplicate-imports": 0, + "no-new-symbol": 1, + "no-restricted-imports": 0, + "no-this-before-super": 1, + "no-useless-computed-key": 1, + "no-useless-constructor": 0, + "@typescript-eslint/no-useless-constructor": 0, + "no-useless-rename": 1, + "no-var": 1, + "object-shorthand": 1, + "prefer-arrow-callback": 0, + "prefer-const": 1, + "prefer-reflect": 0, + "prefer-rest-params": 0, + "prefer-spread": 0, + "prefer-template": 1, + "require-yield": 1, + "rest-spread-spacing": [ + 1, + "never" + ], + "sort-imports": 0, + "template-curly-spacing": [ + 1, + "never" + ], + "yield-star-spacing": [ + 1, + "before" + ] + }, + "overrides": [ + { + "files": [ + "*.ts" + ], + "extends": [ + "plugin:@typescript-eslint/recommended" + ], + "rules": { + "@typescript-eslint/ban-types": 0, + "@typescript-eslint/semi": 1, + "@typescript-eslint/no-explicit-any": 0, + "@typescript-eslint/interface-name-prefix": 0, + "@typescript-eslint/no-unused-vars": [ + "error", + { + "argsIgnorePattern": "^_" + } + ], + "@typescript-eslint/no-empty-interface": 0, + "@typescript-eslint/explicit-module-boundary-types": 0, + "@typescript-eslint/no-empty-function": 0 + } + } + ] +} \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..aea24e546 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +//把js文件识别为ts文件 +*.js linguist-language=TypeScript \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 000000000..569d76a1a --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,36 @@ +name: tests + +on: + push: + pull_request: + branches: [ $default-branch ] + +jobs: + tests: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - uses: pnpm/action-setup@v2.2.2 + with: + version: latest + + - name: Install Node + uses: actions/setup-node@v3 + with: + node-version: 14 + cache: 'pnpm' + + - name: Install dependencies + run: pnpm i + + - name: Lint + run: pnpm run lint + + - name: Build + run: pnpm run build + + - name: Test + run: pnpm run test diff --git a/.gitignore b/.gitignore index ea5284eda..92e9decdf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,7 @@ -# Python compiled files -*.pyc - -# Temp files -*~ -*# - -# Node Modules -node_modules/* - -# File system -.DS_Store \ No newline at end of file +node_modules +yarn.lock +dist +lib +public +out +package-lock.json \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 000000000..0c08b0382 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,26 @@ +# To contribute improvements to CI/CD templates, please follow the Development guide at: +# https://docs.gitlab.com/ee/development/cicd/templates.html +# This specific template is located at: +# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/npm.gitlab-ci.yml + +image: node:latest + +cache: + paths: + - node_modules/ + +test: + stage: test + script: + - npm install + - npm run test + +pages: + stage: deploy + script: + - npm run docs + artifacts: + paths: + - public + only: + - master diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 000000000..48f39c82f --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,3 @@ +tasks: + - init: npm install && npm run build + command: npm run watch diff --git a/.jshintignore b/.jshintignore deleted file mode 100644 index a169d0be7..000000000 --- a/.jshintignore +++ /dev/null @@ -1,11 +0,0 @@ -src/wrapper -src/objects/sph.js -src/demo -build -examples -demos -libs -node_modules -test -utils -Gruntfile.js \ No newline at end of file diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index 2bb4c6d60..000000000 --- a/.jshintrc +++ /dev/null @@ -1,33 +0,0 @@ -{ - // environments - "node" : true, - "browser" : true, - - // options - "boss" : false, - "curly": true, - "debug": false, - "devel": false, - "eqeqeq": true, - "eqnull": true, - "evil": false, - "forin": false, - "immed": true, - "laxbreak": false, - "newcap": true, - "noarg": true, - "noempty": false, - "nonew": false, - "plusplus": false, - "regexp": false, - "smarttabs": true, - "sub": true, - "strict": false, - "trailing" : true, - "undef": true, - "indent":4, - "shadow" : true, - "globals": { - "CANNON": true - } -} diff --git a/.mocharc.json b/.mocharc.json new file mode 100644 index 000000000..1cd4e9f8c --- /dev/null +++ b/.mocharc.json @@ -0,0 +1,7 @@ +{ + "extension": [ + "ts" + ], + "spec": "test/**/*.spec.ts", + "require": "ts-node/register" +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..4745c7f89 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +// 将设置放入此文件中以覆盖默认值和用户设置。 +{ + // 定义函数的左大括号是否放置在新的一行。 + "typescript.format.placeOpenBraceOnNewLineForFunctions": true, + // 定义控制块的左括号是否放置在新的一行。 + "typescript.format.placeOpenBraceOnNewLineForControlBlocks": true, + "javascript.format.placeOpenBraceOnNewLineForFunctions": true, + "javascript.format.placeOpenBraceOnNewLineForControlBlocks": true, +} \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js deleted file mode 100644 index d2a46d76d..000000000 --- a/Gruntfile.js +++ /dev/null @@ -1,94 +0,0 @@ -var fs = require('fs') - -module.exports = function(grunt) { - - var bundlePath = "build/cannon.js", - minifiedBundlePath = "build/cannon.min.js"; - - grunt.initConfig({ - pkg: grunt.file.readJSON('package.json'), - concat: { - options: { - separator: '\n\n' - }, - - demo : { - src: ['src/demo/Demo.js'], - dest: 'build/cannon.demo.js' - }, - }, - - browserify : { - cannon : { - src : ["src/Cannon.js"], - dest : bundlePath, - options : { - bundleOptions: { - standalone : "CANNON" - } - } - } - }, - - uglify : { - build : { - src : [bundlePath], - dest : minifiedBundlePath - } - }, - - yuidoc: { - compile: { - name: '<%= pkg.name %>', - description: '<%= pkg.description %>', - version: '<%= pkg.version %>', - url: '<%= pkg.homepage %>', - options: { - outdir : "docs", - paths : ["./src/"], - }, - } - }, - - nodeunit: { - all: ['test/**/*.js'], - }, - }); - - grunt.loadNpmTasks('grunt-contrib-uglify'); - grunt.loadNpmTasks('grunt-contrib-concat'); - grunt.loadNpmTasks('grunt-contrib-yuidoc'); - grunt.loadNpmTasks('grunt-contrib-nodeunit'); - grunt.loadNpmTasks('grunt-browserify'); - grunt.registerTask('default', ['test', 'concat', 'browserify', 'uglify', 'addLicense', 'addDate', 'requireJsFix']); - grunt.registerTask('test', ['nodeunit']); - - grunt.registerTask('addDate','Adds the current date to the top of the built files',function(){ - var text = '// ' + new Date().toUTCString() + '\n'; - - var dev = fs.readFileSync(bundlePath).toString(); - var min = fs.readFileSync(minifiedBundlePath).toString(); - - fs.writeFileSync(bundlePath,text+"\n"+dev); - fs.writeFileSync(minifiedBundlePath,text+"\n"+min); - }); - - grunt.registerTask('addLicense','Adds the LICENSE to the top of the built files',function(){ - var text = fs.readFileSync("LICENSE").toString(); - - var dev = fs.readFileSync(bundlePath).toString(); - var min = fs.readFileSync(minifiedBundlePath).toString(); - - fs.writeFileSync(bundlePath,text+"\n"+dev); - fs.writeFileSync(minifiedBundlePath,text+"\n"+min); - }); - - // Not sure what flag Browserify needs to do this. Fixing it manually for now. - grunt.registerTask('requireJsFix','Modifies the browserify bundle so it works with RequireJS',function(){ - [bundlePath, minifiedBundlePath].forEach(function(path){ - var text = fs.readFileSync(path).toString(); - text = text.replace('define.amd', 'false'); // This makes the bundle skip using define() from RequireJS - fs.writeFileSync(path, text); - }); - }); -}; diff --git a/README.markdown b/README.markdown index 326223d8e..ce839359c 100644 --- a/README.markdown +++ b/README.markdown @@ -41,7 +41,7 @@ world.gravity.set(0, 0, -9.82); // m/s² var radius = 1; // m var sphereBody = new CANNON.Body({ mass: 5, // kg - position: new CANNON.Vec3(0, 0, 10), // m + position: new CANNON.Vector3(0, 0, 10), // m shape: new CANNON.Sphere(radius) }); world.addBody(sphereBody); diff --git a/build/cannon.demo.js b/build/cannon.demo.js index 16a8f7e3e..6bc1eeb99 100644 --- a/build/cannon.demo.js +++ b/build/cannon.demo.js @@ -1045,9 +1045,9 @@ CANNON.Demo.prototype.shape2mesh = function(body){ case CANNON.Shape.types.HEIGHTFIELD: var geometry = new THREE.Geometry(); - var v0 = new CANNON.Vec3(); - var v1 = new CANNON.Vec3(); - var v2 = new CANNON.Vec3(); + var v0 = new CANNON.Vector3(); + var v1 = new CANNON.Vector3(); + var v2 = new CANNON.Vector3(); for (var xi = 0; xi < shape.data.length - 1; xi++) { for (var yi = 0; yi < shape.data[xi].length - 1; yi++) { for (var k = 0; k < 2; k++) { @@ -1076,9 +1076,9 @@ CANNON.Demo.prototype.shape2mesh = function(body){ case CANNON.Shape.types.TRIMESH: var geometry = new THREE.Geometry(); - var v0 = new CANNON.Vec3(); - var v1 = new CANNON.Vec3(); - var v2 = new CANNON.Vec3(); + var v0 = new CANNON.Vector3(); + var v1 = new CANNON.Vector3(); + var v2 = new CANNON.Vector3(); for (var i = 0; i < shape.indices.length / 3; i++) { shape.getTriangleVertices(i, v0, v1, v2); geometry.vertices.push( diff --git a/build/cannon.js b/build/cannon.js deleted file mode 100644 index 331ecc5fe..000000000 --- a/build/cannon.js +++ /dev/null @@ -1,13687 +0,0 @@ -/* - * Copyright (c) 2015 cannon.js Authors - * - * 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 above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. - */ - -!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&false)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.CANNON=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o (http://steffe.se)", - "keywords": [ - "cannon.js", - "cannon", - "physics", - "engine", - "3d" - ], - "main": "./build/cannon.js", - "engines": { - "node": "*" - }, - "repository": { - "type": "git", - "url": "https://github.com/schteppe/cannon.js.git" - }, - "bugs": { - "url": "https://github.com/schteppe/cannon.js/issues" - }, - "licenses": [ - { - "type": "MIT" - } - ], - "devDependencies": { - "jshint": "latest", - "uglify-js": "latest", - "nodeunit": "^0.9.0", - "grunt": "~0.4.0", - "grunt-contrib-jshint": "~0.1.1", - "grunt-contrib-nodeunit": "^0.4.1", - "grunt-contrib-concat": "~0.1.3", - "grunt-contrib-uglify": "^0.5.1", - "grunt-browserify": "^2.1.4", - "grunt-contrib-yuidoc": "^0.5.2", - "browserify": "*" - }, - "dependencies": {} -} - -},{}],2:[function(_dereq_,module,exports){ -// Export classes -module.exports = { - version : _dereq_('../package.json').version, - - AABB : _dereq_('./collision/AABB'), - ArrayCollisionMatrix : _dereq_('./collision/ArrayCollisionMatrix'), - Body : _dereq_('./objects/Body'), - Box : _dereq_('./shapes/Box'), - Broadphase : _dereq_('./collision/Broadphase'), - Constraint : _dereq_('./constraints/Constraint'), - ContactEquation : _dereq_('./equations/ContactEquation'), - Narrowphase : _dereq_('./world/Narrowphase'), - ConeTwistConstraint : _dereq_('./constraints/ConeTwistConstraint'), - ContactMaterial : _dereq_('./material/ContactMaterial'), - ConvexPolyhedron : _dereq_('./shapes/ConvexPolyhedron'), - Cylinder : _dereq_('./shapes/Cylinder'), - DistanceConstraint : _dereq_('./constraints/DistanceConstraint'), - Equation : _dereq_('./equations/Equation'), - EventTarget : _dereq_('./utils/EventTarget'), - FrictionEquation : _dereq_('./equations/FrictionEquation'), - GSSolver : _dereq_('./solver/GSSolver'), - GridBroadphase : _dereq_('./collision/GridBroadphase'), - Heightfield : _dereq_('./shapes/Heightfield'), - HingeConstraint : _dereq_('./constraints/HingeConstraint'), - LockConstraint : _dereq_('./constraints/LockConstraint'), - Mat3 : _dereq_('./math/Mat3'), - Material : _dereq_('./material/Material'), - NaiveBroadphase : _dereq_('./collision/NaiveBroadphase'), - ObjectCollisionMatrix : _dereq_('./collision/ObjectCollisionMatrix'), - Pool : _dereq_('./utils/Pool'), - Particle : _dereq_('./shapes/Particle'), - Plane : _dereq_('./shapes/Plane'), - PointToPointConstraint : _dereq_('./constraints/PointToPointConstraint'), - Quaternion : _dereq_('./math/Quaternion'), - Ray : _dereq_('./collision/Ray'), - RaycastVehicle : _dereq_('./objects/RaycastVehicle'), - RaycastResult : _dereq_('./collision/RaycastResult'), - RigidVehicle : _dereq_('./objects/RigidVehicle'), - RotationalEquation : _dereq_('./equations/RotationalEquation'), - RotationalMotorEquation : _dereq_('./equations/RotationalMotorEquation'), - SAPBroadphase : _dereq_('./collision/SAPBroadphase'), - SPHSystem : _dereq_('./objects/SPHSystem'), - Shape : _dereq_('./shapes/Shape'), - Solver : _dereq_('./solver/Solver'), - Sphere : _dereq_('./shapes/Sphere'), - SplitSolver : _dereq_('./solver/SplitSolver'), - Spring : _dereq_('./objects/Spring'), - Trimesh : _dereq_('./shapes/Trimesh'), - Vec3 : _dereq_('./math/Vec3'), - Vec3Pool : _dereq_('./utils/Vec3Pool'), - World : _dereq_('./world/World'), -}; - -},{"../package.json":1,"./collision/AABB":3,"./collision/ArrayCollisionMatrix":4,"./collision/Broadphase":5,"./collision/GridBroadphase":6,"./collision/NaiveBroadphase":7,"./collision/ObjectCollisionMatrix":8,"./collision/Ray":9,"./collision/RaycastResult":10,"./collision/SAPBroadphase":11,"./constraints/ConeTwistConstraint":12,"./constraints/Constraint":13,"./constraints/DistanceConstraint":14,"./constraints/HingeConstraint":15,"./constraints/LockConstraint":16,"./constraints/PointToPointConstraint":17,"./equations/ContactEquation":19,"./equations/Equation":20,"./equations/FrictionEquation":21,"./equations/RotationalEquation":22,"./equations/RotationalMotorEquation":23,"./material/ContactMaterial":24,"./material/Material":25,"./math/Mat3":27,"./math/Quaternion":28,"./math/Vec3":30,"./objects/Body":31,"./objects/RaycastVehicle":32,"./objects/RigidVehicle":33,"./objects/SPHSystem":34,"./objects/Spring":35,"./shapes/Box":37,"./shapes/ConvexPolyhedron":38,"./shapes/Cylinder":39,"./shapes/Heightfield":40,"./shapes/Particle":41,"./shapes/Plane":42,"./shapes/Shape":43,"./shapes/Sphere":44,"./shapes/Trimesh":45,"./solver/GSSolver":46,"./solver/Solver":47,"./solver/SplitSolver":48,"./utils/EventTarget":49,"./utils/Pool":51,"./utils/Vec3Pool":54,"./world/Narrowphase":55,"./world/World":56}],3:[function(_dereq_,module,exports){ -var Vec3 = _dereq_('../math/Vec3'); -var Utils = _dereq_('../utils/Utils'); - -module.exports = AABB; - -/** - * Axis aligned bounding box class. - * @class AABB - * @constructor - * @param {Object} [options] - * @param {Vec3} [options.upperBound] - * @param {Vec3} [options.lowerBound] - */ -function AABB(options){ - options = options || {}; - - /** - * The lower bound of the bounding box. - * @property lowerBound - * @type {Vec3} - */ - this.lowerBound = new Vec3(); - if(options.lowerBound){ - this.lowerBound.copy(options.lowerBound); - } - - /** - * The upper bound of the bounding box. - * @property upperBound - * @type {Vec3} - */ - this.upperBound = new Vec3(); - if(options.upperBound){ - this.upperBound.copy(options.upperBound); - } -} - -var tmp = new Vec3(); - -/** - * Set the AABB bounds from a set of points. - * @method setFromPoints - * @param {Array} points An array of Vec3's. - * @param {Vec3} position - * @param {Quaternion} quaternion - * @param {number} skinSize - * @return {AABB} The self object - */ -AABB.prototype.setFromPoints = function(points, position, quaternion, skinSize){ - var l = this.lowerBound, - u = this.upperBound, - q = quaternion; - - // Set to the first point - l.copy(points[0]); - if(q){ - q.vmult(l, l); - } - u.copy(l); - - for(var i = 1; i u.x){ u.x = p.x; } - if(p.x < l.x){ l.x = p.x; } - if(p.y > u.y){ u.y = p.y; } - if(p.y < l.y){ l.y = p.y; } - if(p.z > u.z){ u.z = p.z; } - if(p.z < l.z){ l.z = p.z; } - } - - // Add offset - if (position) { - position.vadd(l, l); - position.vadd(u, u); - } - - if(skinSize){ - l.x -= skinSize; - l.y -= skinSize; - l.z -= skinSize; - u.x += skinSize; - u.y += skinSize; - u.z += skinSize; - } - - return this; -}; - -/** - * Copy bounds from an AABB to this AABB - * @method copy - * @param {AABB} aabb Source to copy from - * @return {AABB} The this object, for chainability - */ -AABB.prototype.copy = function(aabb){ - this.lowerBound.copy(aabb.lowerBound); - this.upperBound.copy(aabb.upperBound); - return this; -}; - -/** - * Clone an AABB - * @method clone - */ -AABB.prototype.clone = function(){ - return new AABB().copy(this); -}; - -/** - * Extend this AABB so that it covers the given AABB too. - * @method extend - * @param {AABB} aabb - */ -AABB.prototype.extend = function(aabb){ - // Extend lower bound - var l = aabb.lowerBound.x; - if(this.lowerBound.x > l){ - this.lowerBound.x = l; - } - - // Upper - var u = aabb.upperBound.x; - if(this.upperBound.x < u){ - this.upperBound.x = u; - } - - // Extend lower bound - var l = aabb.lowerBound.y; - if(this.lowerBound.y > l){ - this.lowerBound.y = l; - } - - // Upper - var u = aabb.upperBound.y; - if(this.upperBound.y < u){ - this.upperBound.y = u; - } - - // Extend lower bound - var l = aabb.lowerBound.z; - if(this.lowerBound.z > l){ - this.lowerBound.z = l; - } - - // Upper - var u = aabb.upperBound.z; - if(this.upperBound.z < u){ - this.upperBound.z = u; - } -}; - -/** - * Returns true if the given AABB overlaps this AABB. - * @method overlaps - * @param {AABB} aabb - * @return {Boolean} - */ -AABB.prototype.overlaps = function(aabb){ - var l1 = this.lowerBound, - u1 = this.upperBound, - l2 = aabb.lowerBound, - u2 = aabb.upperBound; - - // l2 u2 - // |---------| - // |--------| - // l1 u1 - - return ((l2.x <= u1.x && u1.x <= u2.x) || (l1.x <= u2.x && u2.x <= u1.x)) && - ((l2.y <= u1.y && u1.y <= u2.y) || (l1.y <= u2.y && u2.y <= u1.y)) && - ((l2.z <= u1.z && u1.z <= u2.z) || (l1.z <= u2.z && u2.z <= u1.z)); -}; - -/** - * Returns true if the given AABB is fully contained in this AABB. - * @method contains - * @param {AABB} aabb - * @return {Boolean} - */ -AABB.prototype.contains = function(aabb){ - var l1 = this.lowerBound, - u1 = this.upperBound, - l2 = aabb.lowerBound, - u2 = aabb.upperBound; - - // l2 u2 - // |---------| - // |---------------| - // l1 u1 - - return ( - (l1.x <= l2.x && u1.x >= u2.x) && - (l1.y <= l2.y && u1.y >= u2.y) && - (l1.z <= l2.z && u1.z >= u2.z) - ); -}; - -/** - * @method getCorners - * @param {Vec3} a - * @param {Vec3} b - * @param {Vec3} c - * @param {Vec3} d - * @param {Vec3} e - * @param {Vec3} f - * @param {Vec3} g - * @param {Vec3} h - */ -AABB.prototype.getCorners = function(a, b, c, d, e, f, g, h){ - var l = this.lowerBound, - u = this.upperBound; - - a.copy(l); - b.set( u.x, l.y, l.z ); - c.set( u.x, u.y, l.z ); - d.set( l.x, u.y, u.z ); - e.set( u.x, l.y, l.z ); - f.set( l.x, u.y, l.z ); - g.set( l.x, l.y, u.z ); - h.copy(u); -}; - -var transformIntoFrame_corners = [ - new Vec3(), - new Vec3(), - new Vec3(), - new Vec3(), - new Vec3(), - new Vec3(), - new Vec3(), - new Vec3() -]; - -/** - * Get the representation of an AABB in another frame. - * @method toLocalFrame - * @param {Transform} frame - * @param {AABB} target - * @return {AABB} The "target" AABB object. - */ -AABB.prototype.toLocalFrame = function(frame, target){ - - var corners = transformIntoFrame_corners; - var a = corners[0]; - var b = corners[1]; - var c = corners[2]; - var d = corners[3]; - var e = corners[4]; - var f = corners[5]; - var g = corners[6]; - var h = corners[7]; - - // Get corners in current frame - this.getCorners(a, b, c, d, e, f, g, h); - - // Transform them to new local frame - for(var i=0; i !== 8; i++){ - var corner = corners[i]; - frame.pointToLocal(corner, corner); - } - - return target.setFromPoints(corners); -}; - -/** - * Get the representation of an AABB in the global frame. - * @method toWorldFrame - * @param {Transform} frame - * @param {AABB} target - * @return {AABB} The "target" AABB object. - */ -AABB.prototype.toWorldFrame = function(frame, target){ - - var corners = transformIntoFrame_corners; - var a = corners[0]; - var b = corners[1]; - var c = corners[2]; - var d = corners[3]; - var e = corners[4]; - var f = corners[5]; - var g = corners[6]; - var h = corners[7]; - - // Get corners in current frame - this.getCorners(a, b, c, d, e, f, g, h); - - // Transform them to new local frame - for(var i=0; i !== 8; i++){ - var corner = corners[i]; - frame.pointToWorld(corner, corner); - } - - return target.setFromPoints(corners); -}; - -},{"../math/Vec3":30,"../utils/Utils":53}],4:[function(_dereq_,module,exports){ -module.exports = ArrayCollisionMatrix; - -/** - * Collision "matrix". It's actually a triangular-shaped array of whether two bodies are touching this step, for reference next step - * @class ArrayCollisionMatrix - * @constructor - */ -function ArrayCollisionMatrix() { - - /** - * The matrix storage - * @property matrix - * @type {Array} - */ - this.matrix = []; -} - -/** - * Get an element - * @method get - * @param {Number} i - * @param {Number} j - * @return {Number} - */ -ArrayCollisionMatrix.prototype.get = function(i, j) { - i = i.index; - j = j.index; - if (j > i) { - var temp = j; - j = i; - i = temp; - } - return this.matrix[(i*(i + 1)>>1) + j-1]; -}; - -/** - * Set an element - * @method set - * @param {Number} i - * @param {Number} j - * @param {Number} value - */ -ArrayCollisionMatrix.prototype.set = function(i, j, value) { - i = i.index; - j = j.index; - if (j > i) { - var temp = j; - j = i; - i = temp; - } - this.matrix[(i*(i + 1)>>1) + j-1] = value ? 1 : 0; -}; - -/** - * Sets all elements to zero - * @method reset - */ -ArrayCollisionMatrix.prototype.reset = function() { - for (var i=0, l=this.matrix.length; i!==l; i++) { - this.matrix[i]=0; - } -}; - -/** - * Sets the max number of objects - * @method setNumObjects - * @param {Number} n - */ -ArrayCollisionMatrix.prototype.setNumObjects = function(n) { - this.matrix.length = n*(n-1)>>1; -}; - -},{}],5:[function(_dereq_,module,exports){ -var Body = _dereq_('../objects/Body'); -var Vec3 = _dereq_('../math/Vec3'); -var Quaternion = _dereq_('../math/Quaternion'); -var Shape = _dereq_('../shapes/Shape'); -var Plane = _dereq_('../shapes/Plane'); - -module.exports = Broadphase; - -/** - * Base class for broadphase implementations - * @class Broadphase - * @constructor - * @author schteppe - */ -function Broadphase(){ - /** - * The world to search for collisions in. - * @property world - * @type {World} - */ - this.world = null; - - /** - * If set to true, the broadphase uses bounding boxes for intersection test, else it uses bounding spheres. - * @property useBoundingBoxes - * @type {Boolean} - */ - this.useBoundingBoxes = false; - - /** - * Set to true if the objects in the world moved. - * @property {Boolean} dirty - */ - this.dirty = true; -} - -/** - * Get the collision pairs from the world - * @method collisionPairs - * @param {World} world The world to search in - * @param {Array} p1 Empty array to be filled with body objects - * @param {Array} p2 Empty array to be filled with body objects - */ -Broadphase.prototype.collisionPairs = function(world,p1,p2){ - throw new Error("collisionPairs not implemented for this BroadPhase class!"); -}; - -/** - * Check if a body pair needs to be intersection tested at all. - * @method needBroadphaseCollision - * @param {Body} bodyA - * @param {Body} bodyB - * @return {bool} - */ -var Broadphase_needBroadphaseCollision_STATIC_OR_KINEMATIC = Body.STATIC | Body.KINEMATIC; -Broadphase.prototype.needBroadphaseCollision = function(bodyA,bodyB){ - - // Check collision filter masks - if( (bodyA.collisionFilterGroup & bodyB.collisionFilterMask)===0 || (bodyB.collisionFilterGroup & bodyA.collisionFilterMask)===0){ - return false; - } - - // Check types - if(((bodyA.type & Broadphase_needBroadphaseCollision_STATIC_OR_KINEMATIC)!==0 || bodyA.sleepState === Body.SLEEPING) && - ((bodyB.type & Broadphase_needBroadphaseCollision_STATIC_OR_KINEMATIC)!==0 || bodyB.sleepState === Body.SLEEPING)) { - // Both bodies are static, kinematic or sleeping. Skip. - return false; - } - - return true; -}; - -/** - * Check if the bounding volumes of two bodies intersect. - * @method intersectionTest - * @param {Body} bodyA - * @param {Body} bodyB - * @param {array} pairs1 - * @param {array} pairs2 - */ -Broadphase.prototype.intersectionTest = function(bodyA, bodyB, pairs1, pairs2){ - if(this.useBoundingBoxes){ - this.doBoundingBoxBroadphase(bodyA,bodyB,pairs1,pairs2); - } else { - this.doBoundingSphereBroadphase(bodyA,bodyB,pairs1,pairs2); - } -}; - -/** - * Check if the bounding spheres of two bodies are intersecting. - * @method doBoundingSphereBroadphase - * @param {Body} bodyA - * @param {Body} bodyB - * @param {Array} pairs1 bodyA is appended to this array if intersection - * @param {Array} pairs2 bodyB is appended to this array if intersection - */ -var Broadphase_collisionPairs_r = new Vec3(), // Temp objects - Broadphase_collisionPairs_normal = new Vec3(), - Broadphase_collisionPairs_quat = new Quaternion(), - Broadphase_collisionPairs_relpos = new Vec3(); -Broadphase.prototype.doBoundingSphereBroadphase = function(bodyA,bodyB,pairs1,pairs2){ - var r = Broadphase_collisionPairs_r; - bodyB.position.vsub(bodyA.position,r); - var boundingRadiusSum2 = Math.pow(bodyA.boundingRadius + bodyB.boundingRadius, 2); - var norm2 = r.norm2(); - if(norm2 < boundingRadiusSum2){ - pairs1.push(bodyA); - pairs2.push(bodyB); - } -}; - -/** - * Check if the bounding boxes of two bodies are intersecting. - * @method doBoundingBoxBroadphase - * @param {Body} bodyA - * @param {Body} bodyB - * @param {Array} pairs1 - * @param {Array} pairs2 - */ -Broadphase.prototype.doBoundingBoxBroadphase = function(bodyA,bodyB,pairs1,pairs2){ - if(bodyA.aabbNeedsUpdate){ - bodyA.computeAABB(); - } - if(bodyB.aabbNeedsUpdate){ - bodyB.computeAABB(); - } - - // Check AABB / AABB - if(bodyA.aabb.overlaps(bodyB.aabb)){ - pairs1.push(bodyA); - pairs2.push(bodyB); - } -}; - -/** - * Removes duplicate pairs from the pair arrays. - * @method makePairsUnique - * @param {Array} pairs1 - * @param {Array} pairs2 - */ -var Broadphase_makePairsUnique_temp = { keys:[] }, - Broadphase_makePairsUnique_p1 = [], - Broadphase_makePairsUnique_p2 = []; -Broadphase.prototype.makePairsUnique = function(pairs1,pairs2){ - var t = Broadphase_makePairsUnique_temp, - p1 = Broadphase_makePairsUnique_p1, - p2 = Broadphase_makePairsUnique_p2, - N = pairs1.length; - - for(var i=0; i!==N; i++){ - p1[i] = pairs1[i]; - p2[i] = pairs2[i]; - } - - pairs1.length = 0; - pairs2.length = 0; - - for(var i=0; i!==N; i++){ - var id1 = p1[i].id, - id2 = p2[i].id; - var key = id1 < id2 ? id1+","+id2 : id2+","+id1; - t[key] = i; - t.keys.push(key); - } - - for(var i=0; i!==t.keys.length; i++){ - var key = t.keys.pop(), - pairIndex = t[key]; - pairs1.push(p1[pairIndex]); - pairs2.push(p2[pairIndex]); - delete t[key]; - } -}; - -/** - * To be implemented by subcasses - * @method setWorld - * @param {World} world - */ -Broadphase.prototype.setWorld = function(world){ -}; - -/** - * Check if the bounding spheres of two bodies overlap. - * @method boundingSphereCheck - * @param {Body} bodyA - * @param {Body} bodyB - * @return {boolean} - */ -var bsc_dist = new Vec3(); -Broadphase.boundingSphereCheck = function(bodyA,bodyB){ - var dist = bsc_dist; - bodyA.position.vsub(bodyB.position,dist); - return Math.pow(bodyA.shape.boundingSphereRadius + bodyB.shape.boundingSphereRadius,2) > dist.norm2(); -}; - -/** - * Returns all the bodies within the AABB. - * @method aabbQuery - * @param {World} world - * @param {AABB} aabb - * @param {array} result An array to store resulting bodies in. - * @return {array} - */ -Broadphase.prototype.aabbQuery = function(world, aabb, result){ - console.warn('.aabbQuery is not implemented in this Broadphase subclass.'); - return []; -}; -},{"../math/Quaternion":28,"../math/Vec3":30,"../objects/Body":31,"../shapes/Plane":42,"../shapes/Shape":43}],6:[function(_dereq_,module,exports){ -module.exports = GridBroadphase; - -var Broadphase = _dereq_('./Broadphase'); -var Vec3 = _dereq_('../math/Vec3'); -var Shape = _dereq_('../shapes/Shape'); - -/** - * Axis aligned uniform grid broadphase. - * @class GridBroadphase - * @constructor - * @extends Broadphase - * @todo Needs support for more than just planes and spheres. - * @param {Vec3} aabbMin - * @param {Vec3} aabbMax - * @param {Number} nx Number of boxes along x - * @param {Number} ny Number of boxes along y - * @param {Number} nz Number of boxes along z - */ -function GridBroadphase(aabbMin,aabbMax,nx,ny,nz){ - Broadphase.apply(this); - this.nx = nx || 10; - this.ny = ny || 10; - this.nz = nz || 10; - this.aabbMin = aabbMin || new Vec3(100,100,100); - this.aabbMax = aabbMax || new Vec3(-100,-100,-100); - var nbins = this.nx * this.ny * this.nz; - if (nbins <= 0) { - throw "GridBroadphase: Each dimension's n must be >0"; - } - this.bins = []; - this.binLengths = []; //Rather than continually resizing arrays (thrashing the memory), just record length and allow them to grow - this.bins.length = nbins; - this.binLengths.length = nbins; - for (var i=0;i= nx) { xoff0 = nx - 1; } - if (yoff0 < 0) { yoff0 = 0; } else if (yoff0 >= ny) { yoff0 = ny - 1; } - if (zoff0 < 0) { zoff0 = 0; } else if (zoff0 >= nz) { zoff0 = nz - 1; } - if (xoff1 < 0) { xoff1 = 0; } else if (xoff1 >= nx) { xoff1 = nx - 1; } - if (yoff1 < 0) { yoff1 = 0; } else if (yoff1 >= ny) { yoff1 = ny - 1; } - if (zoff1 < 0) { zoff1 = 0; } else if (zoff1 >= nz) { zoff1 = nz - 1; } - - xoff0 *= xstep; - yoff0 *= ystep; - zoff0 *= zstep; - xoff1 *= xstep; - yoff1 *= ystep; - zoff1 *= zstep; - - for (var xoff = xoff0; xoff <= xoff1; xoff += xstep) { - for (var yoff = yoff0; yoff <= yoff1; yoff += ystep) { - for (var zoff = zoff0; zoff <= zoff1; zoff += zstep) { - var idx = xoff+yoff+zoff; - bins[idx][binLengths[idx]++] = bi; - } - } - } - } - - // Put all bodies into the bins - for(var i=0; i!==N; i++){ - var bi = bodies[i]; - var si = bi.shape; - - switch(si.type){ - case SPHERE: - // Put in bin - // check if overlap with other bins - var x = bi.position.x, - y = bi.position.y, - z = bi.position.z; - var r = si.radius; - - addBoxToBins(x-r, y-r, z-r, x+r, y+r, z+r, bi); - break; - - case PLANE: - if(si.worldNormalNeedsUpdate){ - si.computeWorldNormal(bi.quaternion); - } - var planeNormal = si.worldNormal; - - //Relative position from origin of plane object to the first bin - //Incremented as we iterate through the bins - var xreset = xmin + binsizeX*0.5 - bi.position.x, - yreset = ymin + binsizeY*0.5 - bi.position.y, - zreset = zmin + binsizeZ*0.5 - bi.position.z; - - var d = GridBroadphase_collisionPairs_d; - d.set(xreset, yreset, zreset); - - for (var xi = 0, xoff = 0; xi !== nx; xi++, xoff += xstep, d.y = yreset, d.x += binsizeX) { - for (var yi = 0, yoff = 0; yi !== ny; yi++, yoff += ystep, d.z = zreset, d.y += binsizeY) { - for (var zi = 0, zoff = 0; zi !== nz; zi++, zoff += zstep, d.z += binsizeZ) { - if (d.dot(planeNormal) < binRadius) { - var idx = xoff + yoff + zoff; - bins[idx][binLengths[idx]++] = bi; - } - } - } - } - break; - - default: - if (bi.aabbNeedsUpdate) { - bi.computeAABB(); - } - - addBoxToBins( - bi.aabb.lowerBound.x, - bi.aabb.lowerBound.y, - bi.aabb.lowerBound.z, - bi.aabb.upperBound.x, - bi.aabb.upperBound.y, - bi.aabb.upperBound.z, - bi); - break; - } - } - - // Check each bin - for(var i=0; i!==Nbins; i++){ - var binLength = binLengths[i]; - //Skip bins with no potential collisions - if (binLength > 1) { - var bin = bins[i]; - - // Do N^2 broadphase inside - for(var xi=0; xi!==binLength; xi++){ - var bi = bin[xi]; - for(var yi=0; yi!==xi; yi++){ - var bj = bin[yi]; - if(this.needBroadphaseCollision(bi,bj)){ - this.intersectionTest(bi,bj,pairs1,pairs2); - } - } - } - } - } - -// for (var zi = 0, zoff=0; zi < nz; zi++, zoff+= zstep) { -// console.log("layer "+zi); -// for (var yi = 0, yoff=0; yi < ny; yi++, yoff += ystep) { -// var row = ''; -// for (var xi = 0, xoff=0; xi < nx; xi++, xoff += xstep) { -// var idx = xoff + yoff + zoff; -// row += ' ' + binLengths[idx]; -// } -// console.log(row); -// } -// } - - this.makePairsUnique(pairs1,pairs2); -}; - -},{"../math/Vec3":30,"../shapes/Shape":43,"./Broadphase":5}],7:[function(_dereq_,module,exports){ -module.exports = NaiveBroadphase; - -var Broadphase = _dereq_('./Broadphase'); -var AABB = _dereq_('./AABB'); - -/** - * Naive broadphase implementation, used in lack of better ones. - * @class NaiveBroadphase - * @constructor - * @description The naive broadphase looks at all possible pairs without restriction, therefore it has complexity N^2 (which is bad) - * @extends Broadphase - */ -function NaiveBroadphase(){ - Broadphase.apply(this); -} -NaiveBroadphase.prototype = new Broadphase(); -NaiveBroadphase.prototype.constructor = NaiveBroadphase; - -/** - * Get all the collision pairs in the physics world - * @method collisionPairs - * @param {World} world - * @param {Array} pairs1 - * @param {Array} pairs2 - */ -NaiveBroadphase.prototype.collisionPairs = function(world,pairs1,pairs2){ - var bodies = world.bodies, - n = bodies.length, - i,j,bi,bj; - - // Naive N^2 ftw! - for(i=0; i!==n; i++){ - for(j=0; j!==i; j++){ - - bi = bodies[i]; - bj = bodies[j]; - - if(!this.needBroadphaseCollision(bi,bj)){ - continue; - } - - this.intersectionTest(bi,bj,pairs1,pairs2); - } - } -}; - -var tmpAABB = new AABB(); - -/** - * Returns all the bodies within an AABB. - * @method aabbQuery - * @param {World} world - * @param {AABB} aabb - * @param {array} result An array to store resulting bodies in. - * @return {array} - */ -NaiveBroadphase.prototype.aabbQuery = function(world, aabb, result){ - result = result || []; - - for(var i = 0; i < world.bodies.length; i++){ - var b = world.bodies[i]; - - if(b.aabbNeedsUpdate){ - b.computeAABB(); - } - - // Ugly hack until Body gets aabb - if(b.aabb.overlaps(aabb)){ - result.push(b); - } - } - - return result; -}; -},{"./AABB":3,"./Broadphase":5}],8:[function(_dereq_,module,exports){ -module.exports = ObjectCollisionMatrix; - -/** - * Records what objects are colliding with each other - * @class ObjectCollisionMatrix - * @constructor - */ -function ObjectCollisionMatrix() { - - /** - * The matrix storage - * @property matrix - * @type {Object} - */ - this.matrix = {}; -} - -/** - * @method get - * @param {Number} i - * @param {Number} j - * @return {Number} - */ -ObjectCollisionMatrix.prototype.get = function(i, j) { - i = i.id; - j = j.id; - if (j > i) { - var temp = j; - j = i; - i = temp; - } - return i+'-'+j in this.matrix; -}; - -/** - * @method set - * @param {Number} i - * @param {Number} j - * @param {Number} value - */ -ObjectCollisionMatrix.prototype.set = function(i, j, value) { - i = i.id; - j = j.id; - if (j > i) { - var temp = j; - j = i; - i = temp; - } - if (value) { - this.matrix[i+'-'+j] = true; - } - else { - delete this.matrix[i+'-'+j]; - } -}; - -/** - * Empty the matrix - * @method reset - */ -ObjectCollisionMatrix.prototype.reset = function() { - this.matrix = {}; -}; - -/** - * Set max number of objects - * @method setNumObjects - * @param {Number} n - */ -ObjectCollisionMatrix.prototype.setNumObjects = function(n) { -}; - -},{}],9:[function(_dereq_,module,exports){ -module.exports = Ray; - -var Vec3 = _dereq_('../math/Vec3'); -var Quaternion = _dereq_('../math/Quaternion'); -var Transform = _dereq_('../math/Transform'); -var ConvexPolyhedron = _dereq_('../shapes/ConvexPolyhedron'); -var Box = _dereq_('../shapes/Box'); -var RaycastResult = _dereq_('../collision/RaycastResult'); -var Shape = _dereq_('../shapes/Shape'); -var AABB = _dereq_('../collision/AABB'); - -/** - * A line in 3D space that intersects bodies and return points. - * @class Ray - * @constructor - * @param {Vec3} from - * @param {Vec3} to - */ -function Ray(from, to){ - /** - * @property {Vec3} from - */ - this.from = from ? from.clone() : new Vec3(); - - /** - * @property {Vec3} to - */ - this.to = to ? to.clone() : new Vec3(); - - /** - * @private - * @property {Vec3} _direction - */ - this._direction = new Vec3(); - - /** - * The precision of the ray. Used when checking parallelity etc. - * @property {Number} precision - */ - this.precision = 0.0001; - - /** - * Set to true if you want the Ray to take .collisionResponse flags into account on bodies and shapes. - * @property {Boolean} checkCollisionResponse - */ - this.checkCollisionResponse = true; - - /** - * If set to true, the ray skips any hits with normal.dot(rayDirection) < 0. - * @property {Boolean} skipBackfaces - */ - this.skipBackfaces = false; - - /** - * @property {number} collisionFilterMask - * @default -1 - */ - this.collisionFilterMask = -1; - - /** - * @property {number} collisionFilterGroup - * @default -1 - */ - this.collisionFilterGroup = -1; - - /** - * The intersection mode. Should be Ray.ANY, Ray.ALL or Ray.CLOSEST. - * @property {number} mode - */ - this.mode = Ray.ANY; - - /** - * Current result object. - * @property {RaycastResult} result - */ - this.result = new RaycastResult(); - - /** - * Will be set to true during intersectWorld() if the ray hit anything. - * @property {Boolean} hasHit - */ - this.hasHit = false; - - /** - * Current, user-provided result callback. Will be used if mode is Ray.ALL. - * @property {Function} callback - */ - this.callback = function(result){}; -} -Ray.prototype.constructor = Ray; - -Ray.CLOSEST = 1; -Ray.ANY = 2; -Ray.ALL = 4; - -var tmpAABB = new AABB(); -var tmpArray = []; - -/** - * Do itersection against all bodies in the given World. - * @method intersectWorld - * @param {World} world - * @param {object} options - * @return {Boolean} True if the ray hit anything, otherwise false. - */ -Ray.prototype.intersectWorld = function (world, options) { - this.mode = options.mode || Ray.ANY; - this.result = options.result || new RaycastResult(); - this.skipBackfaces = !!options.skipBackfaces; - this.collisionFilterMask = typeof(options.collisionFilterMask) !== 'undefined' ? options.collisionFilterMask : -1; - this.collisionFilterGroup = typeof(options.collisionFilterGroup) !== 'undefined' ? options.collisionFilterGroup : -1; - if(options.from){ - this.from.copy(options.from); - } - if(options.to){ - this.to.copy(options.to); - } - this.callback = options.callback || function(){}; - this.hasHit = false; - - this.result.reset(); - this._updateDirection(); - - this.getAABB(tmpAABB); - tmpArray.length = 0; - world.broadphase.aabbQuery(world, tmpAABB, tmpArray); - this.intersectBodies(tmpArray); - - return this.hasHit; -}; - -var v1 = new Vec3(), - v2 = new Vec3(); - -/* - * As per "Barycentric Technique" as named here http://www.blackpawn.com/texts/pointinpoly/default.html But without the division - */ -Ray.pointInTriangle = pointInTriangle; -function pointInTriangle(p, a, b, c) { - c.vsub(a,v0); - b.vsub(a,v1); - p.vsub(a,v2); - - var dot00 = v0.dot( v0 ); - var dot01 = v0.dot( v1 ); - var dot02 = v0.dot( v2 ); - var dot11 = v1.dot( v1 ); - var dot12 = v1.dot( v2 ); - - var u,v; - - return ( (u = dot11 * dot02 - dot01 * dot12) >= 0 ) && - ( (v = dot00 * dot12 - dot01 * dot02) >= 0 ) && - ( u + v < ( dot00 * dot11 - dot01 * dot01 ) ); -} - -/** - * Shoot a ray at a body, get back information about the hit. - * @method intersectBody - * @private - * @param {Body} body - * @param {RaycastResult} [result] Deprecated - set the result property of the Ray instead. - */ -var intersectBody_xi = new Vec3(); -var intersectBody_qi = new Quaternion(); -Ray.prototype.intersectBody = function (body, result) { - if(result){ - this.result = result; - this._updateDirection(); - } - var checkCollisionResponse = this.checkCollisionResponse; - - if(checkCollisionResponse && !body.collisionResponse){ - return; - } - - if((this.collisionFilterGroup & body.collisionFilterMask)===0 || (body.collisionFilterGroup & this.collisionFilterMask)===0){ - return; - } - - var xi = intersectBody_xi; - var qi = intersectBody_qi; - - for (var i = 0, N = body.shapes.length; i < N; i++) { - var shape = body.shapes[i]; - - if(checkCollisionResponse && !shape.collisionResponse){ - continue; // Skip - } - - body.quaternion.mult(body.shapeOrientations[i], qi); - body.quaternion.vmult(body.shapeOffsets[i], xi); - xi.vadd(body.position, xi); - - this.intersectShape( - shape, - qi, - xi, - body - ); - - if(this.result._shouldStop){ - break; - } - } -}; - -/** - * @method intersectBodies - * @param {Array} bodies An array of Body objects. - * @param {RaycastResult} [result] Deprecated - */ -Ray.prototype.intersectBodies = function (bodies, result) { - if(result){ - this.result = result; - this._updateDirection(); - } - - for ( var i = 0, l = bodies.length; !this.result._shouldStop && i < l; i ++ ) { - this.intersectBody(bodies[i]); - } -}; - -/** - * Updates the _direction vector. - * @private - * @method _updateDirection - */ -Ray.prototype._updateDirection = function(){ - this.to.vsub(this.from, this._direction); - this._direction.normalize(); -}; - -/** - * @method intersectShape - * @private - * @param {Shape} shape - * @param {Quaternion} quat - * @param {Vec3} position - * @param {Body} body - */ -Ray.prototype.intersectShape = function(shape, quat, position, body){ - var from = this.from; - - - // Checking boundingSphere - var distance = distanceFromIntersection(from, this._direction, position); - if ( distance > shape.boundingSphereRadius ) { - return; - } - - var intersectMethod = this[shape.type]; - if(intersectMethod){ - intersectMethod.call(this, shape, quat, position, body); - } -}; - -var vector = new Vec3(); -var normal = new Vec3(); -var intersectPoint = new Vec3(); - -var a = new Vec3(); -var b = new Vec3(); -var c = new Vec3(); -var d = new Vec3(); - -var tmpRaycastResult = new RaycastResult(); - -/** - * @method intersectBox - * @private - * @param {Shape} shape - * @param {Quaternion} quat - * @param {Vec3} position - * @param {Body} body - */ -Ray.prototype.intersectBox = function(shape, quat, position, body){ - return this.intersectConvex(shape.convexPolyhedronRepresentation, quat, position, body); -}; -Ray.prototype[Shape.types.BOX] = Ray.prototype.intersectBox; - -/** - * @method intersectPlane - * @private - * @param {Shape} shape - * @param {Quaternion} quat - * @param {Vec3} position - * @param {Body} body - */ -Ray.prototype.intersectPlane = function(shape, quat, position, body){ - var from = this.from; - var to = this.to; - var direction = this._direction; - - // Get plane normal - var worldNormal = new Vec3(0, 0, 1); - quat.vmult(worldNormal, worldNormal); - - var len = new Vec3(); - from.vsub(position, len); - var planeToFrom = len.dot(worldNormal); - to.vsub(position, len); - var planeToTo = len.dot(worldNormal); - - if(planeToFrom * planeToTo > 0){ - // "from" and "to" are on the same side of the plane... bail out - return; - } - - if(from.distanceTo(to) < planeToFrom){ - return; - } - - var n_dot_dir = worldNormal.dot(direction); - - if (Math.abs(n_dot_dir) < this.precision) { - // No intersection - return; - } - - var planePointToFrom = new Vec3(); - var dir_scaled_with_t = new Vec3(); - var hitPointWorld = new Vec3(); - - from.vsub(position, planePointToFrom); - var t = -worldNormal.dot(planePointToFrom) / n_dot_dir; - direction.scale(t, dir_scaled_with_t); - from.vadd(dir_scaled_with_t, hitPointWorld); - - this.reportIntersection(worldNormal, hitPointWorld, shape, body, -1); -}; -Ray.prototype[Shape.types.PLANE] = Ray.prototype.intersectPlane; - -/** - * Get the world AABB of the ray. - * @method getAABB - * @param {AABB} aabb - */ -Ray.prototype.getAABB = function(result){ - var to = this.to; - var from = this.from; - result.lowerBound.x = Math.min(to.x, from.x); - result.lowerBound.y = Math.min(to.y, from.y); - result.lowerBound.z = Math.min(to.z, from.z); - result.upperBound.x = Math.max(to.x, from.x); - result.upperBound.y = Math.max(to.y, from.y); - result.upperBound.z = Math.max(to.z, from.z); -}; - -var intersectConvexOptions = { - faceList: [0] -}; - -/** - * @method intersectHeightfield - * @private - * @param {Shape} shape - * @param {Quaternion} quat - * @param {Vec3} position - * @param {Body} body - */ -Ray.prototype.intersectHeightfield = function(shape, quat, position, body){ - var data = shape.data, - w = shape.elementSize, - worldPillarOffset = new Vec3(); - - // Convert the ray to local heightfield coordinates - var localRay = new Ray(this.from, this.to); - Transform.pointToLocalFrame(position, quat, localRay.from, localRay.from); - Transform.pointToLocalFrame(position, quat, localRay.to, localRay.to); - - // Get the index of the data points to test against - var index = []; - var iMinX = null; - var iMinY = null; - var iMaxX = null; - var iMaxY = null; - - var inside = shape.getIndexOfPosition(localRay.from.x, localRay.from.y, index, false); - if(inside){ - iMinX = index[0]; - iMinY = index[1]; - iMaxX = index[0]; - iMaxY = index[1]; - } - inside = shape.getIndexOfPosition(localRay.to.x, localRay.to.y, index, false); - if(inside){ - if (iMinX === null || index[0] < iMinX) { iMinX = index[0]; } - if (iMaxX === null || index[0] > iMaxX) { iMaxX = index[0]; } - if (iMinY === null || index[1] < iMinY) { iMinY = index[1]; } - if (iMaxY === null || index[1] > iMaxY) { iMaxY = index[1]; } - } - - if(iMinX === null){ - return; - } - - var minMax = []; - shape.getRectMinMax(iMinX, iMinY, iMaxX, iMaxY, minMax); - var min = minMax[0]; - var max = minMax[1]; - - // // Bail out if the ray can't touch the bounding box - // // TODO - // var aabb = new AABB(); - // this.getAABB(aabb); - // if(aabb.intersects()){ - // return; - // } - - for(var i = iMinX; i <= iMaxX; i++){ - for(var j = iMinY; j <= iMaxY; j++){ - - if(this.result._shouldStop){ - return; - } - - // Lower triangle - shape.getConvexTrianglePillar(i, j, false); - Transform.pointToWorldFrame(position, quat, shape.pillarOffset, worldPillarOffset); - this.intersectConvex(shape.pillarConvex, quat, worldPillarOffset, body, intersectConvexOptions); - - if(this.result._shouldStop){ - return; - } - - // Upper triangle - shape.getConvexTrianglePillar(i, j, true); - Transform.pointToWorldFrame(position, quat, shape.pillarOffset, worldPillarOffset); - this.intersectConvex(shape.pillarConvex, quat, worldPillarOffset, body, intersectConvexOptions); - } - } -}; -Ray.prototype[Shape.types.HEIGHTFIELD] = Ray.prototype.intersectHeightfield; - -var Ray_intersectSphere_intersectionPoint = new Vec3(); -var Ray_intersectSphere_normal = new Vec3(); - -/** - * @method intersectSphere - * @private - * @param {Shape} shape - * @param {Quaternion} quat - * @param {Vec3} position - * @param {Body} body - */ -Ray.prototype.intersectSphere = function(shape, quat, position, body){ - var from = this.from, - to = this.to, - r = shape.radius; - - var a = Math.pow(to.x - from.x, 2) + Math.pow(to.y - from.y, 2) + Math.pow(to.z - from.z, 2); - var b = 2 * ((to.x - from.x) * (from.x - position.x) + (to.y - from.y) * (from.y - position.y) + (to.z - from.z) * (from.z - position.z)); - var c = Math.pow(from.x - position.x, 2) + Math.pow(from.y - position.y, 2) + Math.pow(from.z - position.z, 2) - Math.pow(r, 2); - - var delta = Math.pow(b, 2) - 4 * a * c; - - var intersectionPoint = Ray_intersectSphere_intersectionPoint; - var normal = Ray_intersectSphere_normal; - - if(delta < 0){ - // No intersection - return; - - } else if(delta === 0){ - // single intersection point - from.lerp(to, delta, intersectionPoint); - - intersectionPoint.vsub(position, normal); - normal.normalize(); - - this.reportIntersection(normal, intersectionPoint, shape, body, -1); - - } else { - var d1 = (- b - Math.sqrt(delta)) / (2 * a); - var d2 = (- b + Math.sqrt(delta)) / (2 * a); - - if(d1 >= 0 && d1 <= 1){ - from.lerp(to, d1, intersectionPoint); - intersectionPoint.vsub(position, normal); - normal.normalize(); - this.reportIntersection(normal, intersectionPoint, shape, body, -1); - } - - if(this.result._shouldStop){ - return; - } - - if(d2 >= 0 && d2 <= 1){ - from.lerp(to, d2, intersectionPoint); - intersectionPoint.vsub(position, normal); - normal.normalize(); - this.reportIntersection(normal, intersectionPoint, shape, body, -1); - } - } -}; -Ray.prototype[Shape.types.SPHERE] = Ray.prototype.intersectSphere; - - -var intersectConvex_normal = new Vec3(); -var intersectConvex_minDistNormal = new Vec3(); -var intersectConvex_minDistIntersect = new Vec3(); -var intersectConvex_vector = new Vec3(); - -/** - * @method intersectConvex - * @private - * @param {Shape} shape - * @param {Quaternion} quat - * @param {Vec3} position - * @param {Body} body - * @param {object} [options] - * @param {array} [options.faceList] - */ -Ray.prototype.intersectConvex = function intersectConvex( - shape, - quat, - position, - body, - options -){ - var minDistNormal = intersectConvex_minDistNormal; - var normal = intersectConvex_normal; - var vector = intersectConvex_vector; - var minDistIntersect = intersectConvex_minDistIntersect; - var faceList = (options && options.faceList) || null; - - // Checking faces - var faces = shape.faces, - vertices = shape.vertices, - normals = shape.faceNormals; - var direction = this._direction; - - var from = this.from; - var to = this.to; - var fromToDistance = from.distanceTo(to); - - var minDist = -1; - var Nfaces = faceList ? faceList.length : faces.length; - var result = this.result; - - for (var j = 0; !result._shouldStop && j < Nfaces; j++) { - var fi = faceList ? faceList[j] : j; - - var face = faces[fi]; - var faceNormal = normals[fi]; - var q = quat; - var x = position; - - // determine if ray intersects the plane of the face - // note: this works regardless of the direction of the face normal - - // Get plane point in world coordinates... - vector.copy(vertices[face[0]]); - q.vmult(vector,vector); - vector.vadd(x,vector); - - // ...but make it relative to the ray from. We'll fix this later. - vector.vsub(from,vector); - - // Get plane normal - q.vmult(faceNormal,normal); - - // If this dot product is negative, we have something interesting - var dot = direction.dot(normal); - - // Bail out if ray and plane are parallel - if ( Math.abs( dot ) < this.precision ){ - continue; - } - - // calc distance to plane - var scalar = normal.dot(vector) / dot; - - // if negative distance, then plane is behind ray - if (scalar < 0){ - continue; - } - - // if (dot < 0) { - - // Intersection point is from + direction * scalar - direction.mult(scalar,intersectPoint); - intersectPoint.vadd(from,intersectPoint); - - // a is the point we compare points b and c with. - a.copy(vertices[face[0]]); - q.vmult(a,a); - x.vadd(a,a); - - for(var i = 1; !result._shouldStop && i < face.length - 1; i++){ - // Transform 3 vertices to world coords - b.copy(vertices[face[i]]); - c.copy(vertices[face[i+1]]); - q.vmult(b,b); - q.vmult(c,c); - x.vadd(b,b); - x.vadd(c,c); - - var distance = intersectPoint.distanceTo(from); - - if(!(pointInTriangle(intersectPoint, a, b, c) || pointInTriangle(intersectPoint, b, a, c)) || distance > fromToDistance){ - continue; - } - - this.reportIntersection(normal, intersectPoint, shape, body, fi); - } - // } - } -}; -Ray.prototype[Shape.types.CONVEXPOLYHEDRON] = Ray.prototype.intersectConvex; - -var intersectTrimesh_normal = new Vec3(); -var intersectTrimesh_localDirection = new Vec3(); -var intersectTrimesh_localFrom = new Vec3(); -var intersectTrimesh_localTo = new Vec3(); -var intersectTrimesh_worldNormal = new Vec3(); -var intersectTrimesh_worldIntersectPoint = new Vec3(); -var intersectTrimesh_localAABB = new AABB(); -var intersectTrimesh_triangles = []; -var intersectTrimesh_treeTransform = new Transform(); - -/** - * @method intersectTrimesh - * @private - * @param {Shape} shape - * @param {Quaternion} quat - * @param {Vec3} position - * @param {Body} body - * @param {object} [options] - * @todo Optimize by transforming the world to local space first. - * @todo Use Octree lookup - */ -Ray.prototype.intersectTrimesh = function intersectTrimesh( - mesh, - quat, - position, - body, - options -){ - var normal = intersectTrimesh_normal; - var triangles = intersectTrimesh_triangles; - var treeTransform = intersectTrimesh_treeTransform; - var minDistNormal = intersectConvex_minDistNormal; - var vector = intersectConvex_vector; - var minDistIntersect = intersectConvex_minDistIntersect; - var localAABB = intersectTrimesh_localAABB; - var localDirection = intersectTrimesh_localDirection; - var localFrom = intersectTrimesh_localFrom; - var localTo = intersectTrimesh_localTo; - var worldIntersectPoint = intersectTrimesh_worldIntersectPoint; - var worldNormal = intersectTrimesh_worldNormal; - var faceList = (options && options.faceList) || null; - - // Checking faces - var indices = mesh.indices, - vertices = mesh.vertices, - normals = mesh.faceNormals; - - var from = this.from; - var to = this.to; - var direction = this._direction; - - var minDist = -1; - treeTransform.position.copy(position); - treeTransform.quaternion.copy(quat); - - // Transform ray to local space! - Transform.vectorToLocalFrame(position, quat, direction, localDirection); - //body.vectorToLocalFrame(direction, localDirection); - Transform.pointToLocalFrame(position, quat, from, localFrom); - //body.pointToLocalFrame(from, localFrom); - Transform.pointToLocalFrame(position, quat, to, localTo); - //body.pointToLocalFrame(to, localTo); - var fromToDistanceSquared = localFrom.distanceSquared(localTo); - - mesh.tree.rayQuery(this, treeTransform, triangles); - - for (var i = 0, N = triangles.length; !this.result._shouldStop && i !== N; i++) { - var trianglesIndex = triangles[i]; - - mesh.getNormal(trianglesIndex, normal); - - // determine if ray intersects the plane of the face - // note: this works regardless of the direction of the face normal - - // Get plane point in world coordinates... - mesh.getVertex(indices[trianglesIndex * 3], a); - - // ...but make it relative to the ray from. We'll fix this later. - a.vsub(localFrom,vector); - - // Get plane normal - // quat.vmult(normal, normal); - - // If this dot product is negative, we have something interesting - var dot = localDirection.dot(normal); - - // Bail out if ray and plane are parallel - // if (Math.abs( dot ) < this.precision){ - // continue; - // } - - // calc distance to plane - var scalar = normal.dot(vector) / dot; - - // if negative distance, then plane is behind ray - if (scalar < 0){ - continue; - } - - // Intersection point is from + direction * scalar - localDirection.scale(scalar,intersectPoint); - intersectPoint.vadd(localFrom,intersectPoint); - - // Get triangle vertices - mesh.getVertex(indices[trianglesIndex * 3 + 1], b); - mesh.getVertex(indices[trianglesIndex * 3 + 2], c); - - var squaredDistance = intersectPoint.distanceSquared(localFrom); - - if(!(pointInTriangle(intersectPoint, b, a, c) || pointInTriangle(intersectPoint, a, b, c)) || squaredDistance > fromToDistanceSquared){ - continue; - } - - // transform intersectpoint and normal to world - Transform.vectorToWorldFrame(quat, normal, worldNormal); - //body.vectorToWorldFrame(normal, worldNormal); - Transform.pointToWorldFrame(position, quat, intersectPoint, worldIntersectPoint); - //body.pointToWorldFrame(intersectPoint, worldIntersectPoint); - this.reportIntersection(worldNormal, worldIntersectPoint, mesh, body, trianglesIndex); - } - triangles.length = 0; -}; -Ray.prototype[Shape.types.TRIMESH] = Ray.prototype.intersectTrimesh; - - -/** - * @method reportIntersection - * @private - * @param {Vec3} normal - * @param {Vec3} hitPointWorld - * @param {Shape} shape - * @param {Body} body - * @return {boolean} True if the intersections should continue - */ -Ray.prototype.reportIntersection = function(normal, hitPointWorld, shape, body, hitFaceIndex){ - var from = this.from; - var to = this.to; - var distance = from.distanceTo(hitPointWorld); - var result = this.result; - - // Skip back faces? - if(this.skipBackfaces && normal.dot(this._direction) > 0){ - return; - } - - result.hitFaceIndex = typeof(hitFaceIndex) !== 'undefined' ? hitFaceIndex : -1; - - switch(this.mode){ - case Ray.ALL: - this.hasHit = true; - result.set( - from, - to, - normal, - hitPointWorld, - shape, - body, - distance - ); - result.hasHit = true; - this.callback(result); - break; - - case Ray.CLOSEST: - - // Store if closer than current closest - if(distance < result.distance || !result.hasHit){ - this.hasHit = true; - result.hasHit = true; - result.set( - from, - to, - normal, - hitPointWorld, - shape, - body, - distance - ); - } - break; - - case Ray.ANY: - - // Report and stop. - this.hasHit = true; - result.hasHit = true; - result.set( - from, - to, - normal, - hitPointWorld, - shape, - body, - distance - ); - result._shouldStop = true; - break; - } -}; - -var v0 = new Vec3(), - intersect = new Vec3(); -function distanceFromIntersection(from, direction, position) { - - // v0 is vector from from to position - position.vsub(from,v0); - var dot = v0.dot(direction); - - // intersect = direction*dot + from - direction.mult(dot,intersect); - intersect.vadd(from,intersect); - - var distance = position.distanceTo(intersect); - - return distance; -} - - -},{"../collision/AABB":3,"../collision/RaycastResult":10,"../math/Quaternion":28,"../math/Transform":29,"../math/Vec3":30,"../shapes/Box":37,"../shapes/ConvexPolyhedron":38,"../shapes/Shape":43}],10:[function(_dereq_,module,exports){ -var Vec3 = _dereq_('../math/Vec3'); - -module.exports = RaycastResult; - -/** - * Storage for Ray casting data. - * @class RaycastResult - * @constructor - */ -function RaycastResult(){ - - /** - * @property {Vec3} rayFromWorld - */ - this.rayFromWorld = new Vec3(); - - /** - * @property {Vec3} rayToWorld - */ - this.rayToWorld = new Vec3(); - - /** - * @property {Vec3} hitNormalWorld - */ - this.hitNormalWorld = new Vec3(); - - /** - * @property {Vec3} hitPointWorld - */ - this.hitPointWorld = new Vec3(); - - /** - * @property {boolean} hasHit - */ - this.hasHit = false; - - /** - * The hit shape, or null. - * @property {Shape} shape - */ - this.shape = null; - - /** - * The hit body, or null. - * @property {Body} body - */ - this.body = null; - - /** - * The index of the hit triangle, if the hit shape was a trimesh. - * @property {number} hitFaceIndex - * @default -1 - */ - this.hitFaceIndex = -1; - - /** - * Distance to the hit. Will be set to -1 if there was no hit. - * @property {number} distance - * @default -1 - */ - this.distance = -1; - - /** - * If the ray should stop traversing the bodies. - * @private - * @property {Boolean} _shouldStop - * @default false - */ - this._shouldStop = false; -} - -/** - * Reset all result data. - * @method reset - */ -RaycastResult.prototype.reset = function () { - this.rayFromWorld.setZero(); - this.rayToWorld.setZero(); - this.hitNormalWorld.setZero(); - this.hitPointWorld.setZero(); - this.hasHit = false; - this.shape = null; - this.body = null; - this.hitFaceIndex = -1; - this.distance = -1; - this._shouldStop = false; -}; - -/** - * @method abort - */ -RaycastResult.prototype.abort = function(){ - this._shouldStop = true; -}; - -/** - * @method set - * @param {Vec3} rayFromWorld - * @param {Vec3} rayToWorld - * @param {Vec3} hitNormalWorld - * @param {Vec3} hitPointWorld - * @param {Shape} shape - * @param {Body} body - * @param {number} distance - */ -RaycastResult.prototype.set = function( - rayFromWorld, - rayToWorld, - hitNormalWorld, - hitPointWorld, - shape, - body, - distance -){ - this.rayFromWorld.copy(rayFromWorld); - this.rayToWorld.copy(rayToWorld); - this.hitNormalWorld.copy(hitNormalWorld); - this.hitPointWorld.copy(hitPointWorld); - this.shape = shape; - this.body = body; - this.distance = distance; -}; -},{"../math/Vec3":30}],11:[function(_dereq_,module,exports){ -var Shape = _dereq_('../shapes/Shape'); -var Broadphase = _dereq_('../collision/Broadphase'); - -module.exports = SAPBroadphase; - -/** - * Sweep and prune broadphase along one axis. - * - * @class SAPBroadphase - * @constructor - * @param {World} [world] - * @extends Broadphase - */ -function SAPBroadphase(world){ - Broadphase.apply(this); - - /** - * List of bodies currently in the broadphase. - * @property axisList - * @type {Array} - */ - this.axisList = []; - - /** - * The world to search in. - * @property world - * @type {World} - */ - this.world = null; - - /** - * Axis to sort the bodies along. Set to 0 for x axis, and 1 for y axis. For best performance, choose an axis that the bodies are spread out more on. - * @property axisIndex - * @type {Number} - */ - this.axisIndex = 0; - - var axisList = this.axisList; - - this._addBodyHandler = function(e){ - axisList.push(e.body); - }; - - this._removeBodyHandler = function(e){ - var idx = axisList.indexOf(e.body); - if(idx !== -1){ - axisList.splice(idx,1); - } - }; - - if(world){ - this.setWorld(world); - } -} -SAPBroadphase.prototype = new Broadphase(); - -/** - * Change the world - * @method setWorld - * @param {World} world - */ -SAPBroadphase.prototype.setWorld = function(world){ - // Clear the old axis array - this.axisList.length = 0; - - // Add all bodies from the new world - for(var i=0; i=0;j--) { - if(a[j].aabb.lowerBound.x <= v.aabb.lowerBound.x){ - break; - } - a[j+1] = a[j]; - } - a[j+1] = v; - } - return a; -}; - -/** - * @static - * @method insertionSortY - * @param {Array} a - * @return {Array} - */ -SAPBroadphase.insertionSortY = function(a) { - for(var i=1,l=a.length;i=0;j--) { - if(a[j].aabb.lowerBound.y <= v.aabb.lowerBound.y){ - break; - } - a[j+1] = a[j]; - } - a[j+1] = v; - } - return a; -}; - -/** - * @static - * @method insertionSortZ - * @param {Array} a - * @return {Array} - */ -SAPBroadphase.insertionSortZ = function(a) { - for(var i=1,l=a.length;i=0;j--) { - if(a[j].aabb.lowerBound.z <= v.aabb.lowerBound.z){ - break; - } - a[j+1] = a[j]; - } - a[j+1] = v; - } - return a; -}; - -/** - * Collect all collision pairs - * @method collisionPairs - * @param {World} world - * @param {Array} p1 - * @param {Array} p2 - */ -SAPBroadphase.prototype.collisionPairs = function(world,p1,p2){ - var bodies = this.axisList, - N = bodies.length, - axisIndex = this.axisIndex, - i, j; - - if(this.dirty){ - this.sortList(); - this.dirty = false; - } - - // Look through the list - for(i=0; i !== N; i++){ - var bi = bodies[i]; - - for(j=i+1; j < N; j++){ - var bj = bodies[j]; - - if(!this.needBroadphaseCollision(bi,bj)){ - continue; - } - - if(!SAPBroadphase.checkBounds(bi,bj,axisIndex)){ - break; - } - - this.intersectionTest(bi,bj,p1,p2); - } - } -}; - -SAPBroadphase.prototype.sortList = function(){ - var axisList = this.axisList; - var axisIndex = this.axisIndex; - var N = axisList.length; - - // Update AABBs - for(var i = 0; i!==N; i++){ - var bi = axisList[i]; - if(bi.aabbNeedsUpdate){ - bi.computeAABB(); - } - } - - // Sort the list - if(axisIndex === 0){ - SAPBroadphase.insertionSortX(axisList); - } else if(axisIndex === 1){ - SAPBroadphase.insertionSortY(axisList); - } else if(axisIndex === 2){ - SAPBroadphase.insertionSortZ(axisList); - } -}; - -/** - * Check if the bounds of two bodies overlap, along the given SAP axis. - * @static - * @method checkBounds - * @param {Body} bi - * @param {Body} bj - * @param {Number} axisIndex - * @return {Boolean} - */ -SAPBroadphase.checkBounds = function(bi, bj, axisIndex){ - var biPos; - var bjPos; - - if(axisIndex === 0){ - biPos = bi.position.x; - bjPos = bj.position.x; - } else if(axisIndex === 1){ - biPos = bi.position.y; - bjPos = bj.position.y; - } else if(axisIndex === 2){ - biPos = bi.position.z; - bjPos = bj.position.z; - } - - var ri = bi.boundingRadius, - rj = bj.boundingRadius, - boundA1 = biPos - ri, - boundA2 = biPos + ri, - boundB1 = bjPos - rj, - boundB2 = bjPos + rj; - - return boundB1 < boundA2; -}; - -/** - * Computes the variance of the body positions and estimates the best - * axis to use. Will automatically set property .axisIndex. - * @method autoDetectAxis - */ -SAPBroadphase.prototype.autoDetectAxis = function(){ - var sumX=0, - sumX2=0, - sumY=0, - sumY2=0, - sumZ=0, - sumZ2=0, - bodies = this.axisList, - N = bodies.length, - invN=1/N; - - for(var i=0; i!==N; i++){ - var b = bodies[i]; - - var centerX = b.position.x; - sumX += centerX; - sumX2 += centerX*centerX; - - var centerY = b.position.y; - sumY += centerY; - sumY2 += centerY*centerY; - - var centerZ = b.position.z; - sumZ += centerZ; - sumZ2 += centerZ*centerZ; - } - - var varianceX = sumX2 - sumX*sumX*invN, - varianceY = sumY2 - sumY*sumY*invN, - varianceZ = sumZ2 - sumZ*sumZ*invN; - - if(varianceX > varianceY){ - if(varianceX > varianceZ){ - this.axisIndex = 0; - } else{ - this.axisIndex = 2; - } - } else if(varianceY > varianceZ){ - this.axisIndex = 1; - } else{ - this.axisIndex = 2; - } -}; - -/** - * Returns all the bodies within an AABB. - * @method aabbQuery - * @param {World} world - * @param {AABB} aabb - * @param {array} result An array to store resulting bodies in. - * @return {array} - */ -SAPBroadphase.prototype.aabbQuery = function(world, aabb, result){ - result = result || []; - - if(this.dirty){ - this.sortList(); - this.dirty = false; - } - - var axisIndex = this.axisIndex, axis = 'x'; - if(axisIndex === 1){ axis = 'y'; } - if(axisIndex === 2){ axis = 'z'; } - - var axisList = this.axisList; - var lower = aabb.lowerBound[axis]; - var upper = aabb.upperBound[axis]; - for(var i = 0; i < axisList.length; i++){ - var b = axisList[i]; - - if(b.aabbNeedsUpdate){ - b.computeAABB(); - } - - if(b.aabb.overlaps(aabb)){ - result.push(b); - } - } - - return result; -}; -},{"../collision/Broadphase":5,"../shapes/Shape":43}],12:[function(_dereq_,module,exports){ -module.exports = ConeTwistConstraint; - -var Constraint = _dereq_('./Constraint'); -var PointToPointConstraint = _dereq_('./PointToPointConstraint'); -var ConeEquation = _dereq_('../equations/ConeEquation'); -var RotationalEquation = _dereq_('../equations/RotationalEquation'); -var ContactEquation = _dereq_('../equations/ContactEquation'); -var Vec3 = _dereq_('../math/Vec3'); - -/** - * @class ConeTwistConstraint - * @constructor - * @author schteppe - * @param {Body} bodyA - * @param {Body} bodyB - * @param {object} [options] - * @param {Vec3} [options.pivotA] - * @param {Vec3} [options.pivotB] - * @param {Vec3} [options.axisA] - * @param {Vec3} [options.axisB] - * @param {Number} [options.maxForce=1e6] - * @extends PointToPointConstraint - */ -function ConeTwistConstraint(bodyA, bodyB, options){ - options = options || {}; - var maxForce = typeof(options.maxForce) !== 'undefined' ? options.maxForce : 1e6; - - // Set pivot point in between - var pivotA = options.pivotA ? options.pivotA.clone() : new Vec3(); - var pivotB = options.pivotB ? options.pivotB.clone() : new Vec3(); - this.axisA = options.axisA ? options.axisA.clone() : new Vec3(); - this.axisB = options.axisB ? options.axisB.clone() : new Vec3(); - - PointToPointConstraint.call(this, bodyA, pivotA, bodyB, pivotB, maxForce); - - this.collideConnected = !!options.collideConnected; - - this.angle = typeof(options.angle) !== 'undefined' ? options.angle : 0; - - /** - * @property {ConeEquation} coneEquation - */ - var c = this.coneEquation = new ConeEquation(bodyA,bodyB,options); - - /** - * @property {RotationalEquation} twistEquation - */ - var t = this.twistEquation = new RotationalEquation(bodyA,bodyB,options); - this.twistAngle = typeof(options.twistAngle) !== 'undefined' ? options.twistAngle : 0; - - // Make the cone equation push the bodies toward the cone axis, not outward - c.maxForce = 0; - c.minForce = -maxForce; - - // Make the twist equation add torque toward the initial position - t.maxForce = 0; - t.minForce = -maxForce; - - this.equations.push(c, t); -} -ConeTwistConstraint.prototype = new PointToPointConstraint(); -ConeTwistConstraint.constructor = ConeTwistConstraint; - -var ConeTwistConstraint_update_tmpVec1 = new Vec3(); -var ConeTwistConstraint_update_tmpVec2 = new Vec3(); - -ConeTwistConstraint.prototype.update = function(){ - var bodyA = this.bodyA, - bodyB = this.bodyB, - cone = this.coneEquation, - twist = this.twistEquation; - - PointToPointConstraint.prototype.update.call(this); - - // Update the axes to the cone constraint - bodyA.vectorToWorldFrame(this.axisA, cone.axisA); - bodyB.vectorToWorldFrame(this.axisB, cone.axisB); - - // Update the world axes in the twist constraint - this.axisA.tangents(twist.axisA, twist.axisA); - bodyA.vectorToWorldFrame(twist.axisA, twist.axisA); - - this.axisB.tangents(twist.axisB, twist.axisB); - bodyB.vectorToWorldFrame(twist.axisB, twist.axisB); - - cone.angle = this.angle; - twist.maxAngle = this.twistAngle; -}; - - -},{"../equations/ConeEquation":18,"../equations/ContactEquation":19,"../equations/RotationalEquation":22,"../math/Vec3":30,"./Constraint":13,"./PointToPointConstraint":17}],13:[function(_dereq_,module,exports){ -module.exports = Constraint; - -var Utils = _dereq_('../utils/Utils'); - -/** - * Constraint base class - * @class Constraint - * @author schteppe - * @constructor - * @param {Body} bodyA - * @param {Body} bodyB - * @param {object} [options] - * @param {boolean} [options.collideConnected=true] - * @param {boolean} [options.wakeUpBodies=true] - */ -function Constraint(bodyA, bodyB, options){ - options = Utils.defaults(options,{ - collideConnected : true, - wakeUpBodies : true, - }); - - /** - * Equations to be solved in this constraint - * @property equations - * @type {Array} - */ - this.equations = []; - - /** - * @property {Body} bodyA - */ - this.bodyA = bodyA; - - /** - * @property {Body} bodyB - */ - this.bodyB = bodyB; - - /** - * @property {Number} id - */ - this.id = Constraint.idCounter++; - - /** - * Set to true if you want the bodies to collide when they are connected. - * @property collideConnected - * @type {boolean} - */ - this.collideConnected = options.collideConnected; - - if(options.wakeUpBodies){ - if(bodyA){ - bodyA.wakeUp(); - } - if(bodyB){ - bodyB.wakeUp(); - } - } -} - -/** - * Update all the equations with data. - * @method update - */ -Constraint.prototype.update = function(){ - throw new Error("method update() not implmemented in this Constraint subclass!"); -}; - -/** - * Enables all equations in the constraint. - * @method enable - */ -Constraint.prototype.enable = function(){ - var eqs = this.equations; - for(var i=0; i - // G = [0 axisA 0 -axisB] - - GA.rotational.copy(axisA); - axisB.negate(GB.rotational); - - var GW = this.computeGW() - this.targetVelocity, - GiMf = this.computeGiMf(); - - var B = - GW * b - h * GiMf; - - return B; -}; - -},{"../math/Mat3":27,"../math/Vec3":30,"./Equation":20}],24:[function(_dereq_,module,exports){ -var Utils = _dereq_('../utils/Utils'); - -module.exports = ContactMaterial; - -/** - * Defines what happens when two materials meet. - * @class ContactMaterial - * @constructor - * @param {Material} m1 - * @param {Material} m2 - * @param {object} [options] - * @param {Number} [options.friction=0.3] - * @param {Number} [options.restitution=0.3] - * @param {number} [options.contactEquationStiffness=1e7] - * @param {number} [options.contactEquationRelaxation=3] - * @param {number} [options.frictionEquationStiffness=1e7] - * @param {Number} [options.frictionEquationRelaxation=3] - */ -function ContactMaterial(m1, m2, options){ - options = Utils.defaults(options, { - friction: 0.3, - restitution: 0.3, - contactEquationStiffness: 1e7, - contactEquationRelaxation: 3, - frictionEquationStiffness: 1e7, - frictionEquationRelaxation: 3 - }); - - /** - * Identifier of this material - * @property {Number} id - */ - this.id = ContactMaterial.idCounter++; - - /** - * Participating materials - * @property {Array} materials - * @todo Should be .materialA and .materialB instead - */ - this.materials = [m1, m2]; - - /** - * Friction coefficient - * @property {Number} friction - */ - this.friction = options.friction; - - /** - * Restitution coefficient - * @property {Number} restitution - */ - this.restitution = options.restitution; - - /** - * Stiffness of the produced contact equations - * @property {Number} contactEquationStiffness - */ - this.contactEquationStiffness = options.contactEquationStiffness; - - /** - * Relaxation time of the produced contact equations - * @property {Number} contactEquationRelaxation - */ - this.contactEquationRelaxation = options.contactEquationRelaxation; - - /** - * Stiffness of the produced friction equations - * @property {Number} frictionEquationStiffness - */ - this.frictionEquationStiffness = options.frictionEquationStiffness; - - /** - * Relaxation time of the produced friction equations - * @property {Number} frictionEquationRelaxation - */ - this.frictionEquationRelaxation = options.frictionEquationRelaxation; -} - -ContactMaterial.idCounter = 0; - -},{"../utils/Utils":53}],25:[function(_dereq_,module,exports){ -module.exports = Material; - -/** - * Defines a physics material. - * @class Material - * @constructor - * @param {object} [options] - * @author schteppe - */ -function Material(options){ - var name = ''; - options = options || {}; - - // Backwards compatibility fix - if(typeof(options) === 'string'){ - name = options; - options = {}; - } else if(typeof(options) === 'object') { - name = ''; - } - - /** - * @property name - * @type {String} - */ - this.name = name; - - /** - * material id. - * @property id - * @type {number} - */ - this.id = Material.idCounter++; - - /** - * Friction for this material. If non-negative, it will be used instead of the friction given by ContactMaterials. If there's no matching ContactMaterial, the value from .defaultContactMaterial in the World will be used. - * @property {number} friction - */ - this.friction = typeof(options.friction) !== 'undefined' ? options.friction : -1; - - /** - * Restitution for this material. If non-negative, it will be used instead of the restitution given by ContactMaterials. If there's no matching ContactMaterial, the value from .defaultContactMaterial in the World will be used. - * @property {number} restitution - */ - this.restitution = typeof(options.restitution) !== 'undefined' ? options.restitution : -1; -} - -Material.idCounter = 0; - -},{}],26:[function(_dereq_,module,exports){ -module.exports = JacobianElement; - -var Vec3 = _dereq_('./Vec3'); - -/** - * An element containing 6 entries, 3 spatial and 3 rotational degrees of freedom. - * @class JacobianElement - * @constructor - */ -function JacobianElement(){ - - /** - * @property {Vec3} spatial - */ - this.spatial = new Vec3(); - - /** - * @property {Vec3} rotational - */ - this.rotational = new Vec3(); -} - -/** - * Multiply with other JacobianElement - * @method multiplyElement - * @param {JacobianElement} element - * @return {Number} - */ -JacobianElement.prototype.multiplyElement = function(element){ - return element.spatial.dot(this.spatial) + element.rotational.dot(this.rotational); -}; - -/** - * Multiply with two vectors - * @method multiplyVectors - * @param {Vec3} spatial - * @param {Vec3} rotational - * @return {Number} - */ -JacobianElement.prototype.multiplyVectors = function(spatial,rotational){ - return spatial.dot(this.spatial) + rotational.dot(this.rotational); -}; - -},{"./Vec3":30}],27:[function(_dereq_,module,exports){ -module.exports = Mat3; - -var Vec3 = _dereq_('./Vec3'); - -/** - * A 3x3 matrix. - * @class Mat3 - * @constructor - * @param array elements Array of nine elements. Optional. - * @author schteppe / http://github.com/schteppe - */ -function Mat3(elements){ - /** - * A vector of length 9, containing all matrix elements - * @property {Array} elements - */ - if(elements){ - this.elements = elements; - } else { - this.elements = [0,0,0,0,0,0,0,0,0]; - } -} - -/** - * Sets the matrix to identity - * @method identity - * @todo Should perhaps be renamed to setIdentity() to be more clear. - * @todo Create another function that immediately creates an identity matrix eg. eye() - */ -Mat3.prototype.identity = function(){ - var e = this.elements; - e[0] = 1; - e[1] = 0; - e[2] = 0; - - e[3] = 0; - e[4] = 1; - e[5] = 0; - - e[6] = 0; - e[7] = 0; - e[8] = 1; -}; - -/** - * Set all elements to zero - * @method setZero - */ -Mat3.prototype.setZero = function(){ - var e = this.elements; - e[0] = 0; - e[1] = 0; - e[2] = 0; - e[3] = 0; - e[4] = 0; - e[5] = 0; - e[6] = 0; - e[7] = 0; - e[8] = 0; -}; - -/** - * Sets the matrix diagonal elements from a Vec3 - * @method setTrace - * @param {Vec3} vec3 - */ -Mat3.prototype.setTrace = function(vec3){ - var e = this.elements; - e[0] = vec3.x; - e[4] = vec3.y; - e[8] = vec3.z; -}; - -/** - * Gets the matrix diagonal elements - * @method getTrace - * @return {Vec3} - */ -Mat3.prototype.getTrace = function(target){ - var target = target || new Vec3(); - var e = this.elements; - target.x = e[0]; - target.y = e[4]; - target.z = e[8]; -}; - -/** - * Matrix-Vector multiplication - * @method vmult - * @param {Vec3} v The vector to multiply with - * @param {Vec3} target Optional, target to save the result in. - */ -Mat3.prototype.vmult = function(v,target){ - target = target || new Vec3(); - - var e = this.elements, - x = v.x, - y = v.y, - z = v.z; - target.x = e[0]*x + e[1]*y + e[2]*z; - target.y = e[3]*x + e[4]*y + e[5]*z; - target.z = e[6]*x + e[7]*y + e[8]*z; - - return target; -}; - -/** - * Matrix-scalar multiplication - * @method smult - * @param {Number} s - */ -Mat3.prototype.smult = function(s){ - for(var i=0; i1 acos and sqrt will produce errors, this cant happen if quaternion is normalised - var angle = 2 * Math.acos(this.w); - var s = Math.sqrt(1-this.w*this.w); // assuming quaternion normalised then w is less than 1, so term always positive. - if (s < 0.001) { // test to avoid divide by zero, s is always positive due to sqrt - // if s close to zero then direction of axis not important - targetAxis.x = this.x; // if it is important that axis is normalised then replace with x=1; y=z=0; - targetAxis.y = this.y; - targetAxis.z = this.z; - } else { - targetAxis.x = this.x / s; // normalise axis - targetAxis.y = this.y / s; - targetAxis.z = this.z / s; - } - return [targetAxis,angle]; -}; - -var sfv_t1 = new Vec3(), - sfv_t2 = new Vec3(); - -/** - * Set the quaternion value given two vectors. The resulting rotation will be the needed rotation to rotate u to v. - * @method setFromVectors - * @param {Vec3} u - * @param {Vec3} v - */ -Quaternion.prototype.setFromVectors = function(u,v){ - if(u.isAntiparallelTo(v)){ - var t1 = sfv_t1; - var t2 = sfv_t2; - - u.tangents(t1,t2); - this.setFromAxisAngle(t1,Math.PI); - } else { - var a = u.cross(v); - this.x = a.x; - this.y = a.y; - this.z = a.z; - this.w = Math.sqrt(Math.pow(u.norm(),2) * Math.pow(v.norm(),2)) + u.dot(v); - this.normalize(); - } -}; - -/** - * Quaternion multiplication - * @method mult - * @param {Quaternion} q - * @param {Quaternion} target Optional. - * @return {Quaternion} - */ -var Quaternion_mult_va = new Vec3(); -var Quaternion_mult_vb = new Vec3(); -var Quaternion_mult_vaxvb = new Vec3(); -Quaternion.prototype.mult = function(q,target){ - target = target || new Quaternion(); - var w = this.w, - va = Quaternion_mult_va, - vb = Quaternion_mult_vb, - vaxvb = Quaternion_mult_vaxvb; - - va.set(this.x,this.y,this.z); - vb.set(q.x,q.y,q.z); - target.w = w*q.w - va.dot(vb); - va.cross(vb,vaxvb); - - target.x = w * vb.x + q.w*va.x + vaxvb.x; - target.y = w * vb.y + q.w*va.y + vaxvb.y; - target.z = w * vb.z + q.w*va.z + vaxvb.z; - - return target; -}; - -/** - * Get the inverse quaternion rotation. - * @method inverse - * @param {Quaternion} target - * @return {Quaternion} - */ -Quaternion.prototype.inverse = function(target){ - var x = this.x, y = this.y, z = this.z, w = this.w; - target = target || new Quaternion(); - - this.conjugate(target); - var inorm2 = 1/(x*x + y*y + z*z + w*w); - target.x *= inorm2; - target.y *= inorm2; - target.z *= inorm2; - target.w *= inorm2; - - return target; -}; - -/** - * Get the quaternion conjugate - * @method conjugate - * @param {Quaternion} target - * @return {Quaternion} - */ -Quaternion.prototype.conjugate = function(target){ - target = target || new Quaternion(); - - target.x = -this.x; - target.y = -this.y; - target.z = -this.z; - target.w = this.w; - - return target; -}; - -/** - * Normalize the quaternion. Note that this changes the values of the quaternion. - * @method normalize - */ -Quaternion.prototype.normalize = function(){ - var l = Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w); - if ( l === 0 ) { - this.x = 0; - this.y = 0; - this.z = 0; - this.w = 0; - } else { - l = 1 / l; - this.x *= l; - this.y *= l; - this.z *= l; - this.w *= l; - } -}; - -/** - * Approximation of quaternion normalization. Works best when quat is already almost-normalized. - * @method normalizeFast - * @see http://jsperf.com/fast-quaternion-normalization - * @author unphased, https://github.com/unphased - */ -Quaternion.prototype.normalizeFast = function () { - var f = (3.0-(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w))/2.0; - if ( f === 0 ) { - this.x = 0; - this.y = 0; - this.z = 0; - this.w = 0; - } else { - this.x *= f; - this.y *= f; - this.z *= f; - this.w *= f; - } -}; - -/** - * Multiply the quaternion by a vector - * @method vmult - * @param {Vec3} v - * @param {Vec3} target Optional - * @return {Vec3} - */ -Quaternion.prototype.vmult = function(v,target){ - target = target || new Vec3(); - - var x = v.x, - y = v.y, - z = v.z; - - var qx = this.x, - qy = this.y, - qz = this.z, - qw = this.w; - - // q*v - var ix = qw * x + qy * z - qz * y, - iy = qw * y + qz * x - qx * z, - iz = qw * z + qx * y - qy * x, - iw = -qx * x - qy * y - qz * z; - - target.x = ix * qw + iw * -qx + iy * -qz - iz * -qy; - target.y = iy * qw + iw * -qy + iz * -qx - ix * -qz; - target.z = iz * qw + iw * -qz + ix * -qy - iy * -qx; - - return target; -}; - -/** - * Copies value of source to this quaternion. - * @method copy - * @param {Quaternion} source - * @return {Quaternion} this - */ -Quaternion.prototype.copy = function(source){ - this.x = source.x; - this.y = source.y; - this.z = source.z; - this.w = source.w; - return this; -}; - -/** - * Convert the quaternion to euler angle representation. Order: YZX, as this page describes: http://www.euclideanspace.com/maths/standards/index.htm - * @method toEuler - * @param {Vec3} target - * @param string order Three-character string e.g. "YZX", which also is default. - */ -Quaternion.prototype.toEuler = function(target,order){ - order = order || "YZX"; - - var heading, attitude, bank; - var x = this.x, y = this.y, z = this.z, w = this.w; - - switch(order){ - case "YZX": - var test = x*y + z*w; - if (test > 0.499) { // singularity at north pole - heading = 2 * Math.atan2(x,w); - attitude = Math.PI/2; - bank = 0; - } - if (test < -0.499) { // singularity at south pole - heading = -2 * Math.atan2(x,w); - attitude = - Math.PI/2; - bank = 0; - } - if(isNaN(heading)){ - var sqx = x*x; - var sqy = y*y; - var sqz = z*z; - heading = Math.atan2(2*y*w - 2*x*z , 1 - 2*sqy - 2*sqz); // Heading - attitude = Math.asin(2*test); // attitude - bank = Math.atan2(2*x*w - 2*y*z , 1 - 2*sqx - 2*sqz); // bank - } - break; - default: - throw new Error("Euler order "+order+" not supported yet."); - } - - target.y = heading; - target.z = attitude; - target.x = bank; -}; - -/** - * See http://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/content/SpinCalc.m - * @method setFromEuler - * @param {Number} x - * @param {Number} y - * @param {Number} z - * @param {String} order The order to apply angles: 'XYZ' or 'YXZ' or any other combination - */ -Quaternion.prototype.setFromEuler = function ( x, y, z, order ) { - order = order || "XYZ"; - - var c1 = Math.cos( x / 2 ); - var c2 = Math.cos( y / 2 ); - var c3 = Math.cos( z / 2 ); - var s1 = Math.sin( x / 2 ); - var s2 = Math.sin( y / 2 ); - var s3 = Math.sin( z / 2 ); - - if ( order === 'XYZ' ) { - - this.x = s1 * c2 * c3 + c1 * s2 * s3; - this.y = c1 * s2 * c3 - s1 * c2 * s3; - this.z = c1 * c2 * s3 + s1 * s2 * c3; - this.w = c1 * c2 * c3 - s1 * s2 * s3; - - } else if ( order === 'YXZ' ) { - - this.x = s1 * c2 * c3 + c1 * s2 * s3; - this.y = c1 * s2 * c3 - s1 * c2 * s3; - this.z = c1 * c2 * s3 - s1 * s2 * c3; - this.w = c1 * c2 * c3 + s1 * s2 * s3; - - } else if ( order === 'ZXY' ) { - - this.x = s1 * c2 * c3 - c1 * s2 * s3; - this.y = c1 * s2 * c3 + s1 * c2 * s3; - this.z = c1 * c2 * s3 + s1 * s2 * c3; - this.w = c1 * c2 * c3 - s1 * s2 * s3; - - } else if ( order === 'ZYX' ) { - - this.x = s1 * c2 * c3 - c1 * s2 * s3; - this.y = c1 * s2 * c3 + s1 * c2 * s3; - this.z = c1 * c2 * s3 - s1 * s2 * c3; - this.w = c1 * c2 * c3 + s1 * s2 * s3; - - } else if ( order === 'YZX' ) { - - this.x = s1 * c2 * c3 + c1 * s2 * s3; - this.y = c1 * s2 * c3 + s1 * c2 * s3; - this.z = c1 * c2 * s3 - s1 * s2 * c3; - this.w = c1 * c2 * c3 - s1 * s2 * s3; - - } else if ( order === 'XZY' ) { - - this.x = s1 * c2 * c3 - c1 * s2 * s3; - this.y = c1 * s2 * c3 - s1 * c2 * s3; - this.z = c1 * c2 * s3 + s1 * s2 * c3; - this.w = c1 * c2 * c3 + s1 * s2 * s3; - - } - - return this; - -}; - -Quaternion.prototype.clone = function(){ - return new Quaternion(this.x, this.y, this.z, this.w); -}; -},{"./Vec3":30}],29:[function(_dereq_,module,exports){ -var Vec3 = _dereq_('./Vec3'); -var Quaternion = _dereq_('./Quaternion'); - -module.exports = Transform; - -/** - * @class Transform - * @constructor - */ -function Transform(options) { - options = options || {}; - - /** - * @property {Vec3} position - */ - this.position = new Vec3(); - if(options.position){ - this.position.copy(options.position); - } - - /** - * @property {Quaternion} quaternion - */ - this.quaternion = new Quaternion(); - if(options.quaternion){ - this.quaternion.copy(options.quaternion); - } -} - -var tmpQuat = new Quaternion(); - -/** - * @static - * @method pointToLocaFrame - * @param {Vec3} position - * @param {Quaternion} quaternion - * @param {Vec3} worldPoint - * @param {Vec3} result - */ -Transform.pointToLocalFrame = function(position, quaternion, worldPoint, result){ - var result = result || new Vec3(); - worldPoint.vsub(position, result); - quaternion.conjugate(tmpQuat); - tmpQuat.vmult(result, result); - return result; -}; - -/** - * Get a global point in local transform coordinates. - * @method pointToLocal - * @param {Vec3} point - * @param {Vec3} result - * @return {Vec3} The "result" vector object - */ -Transform.prototype.pointToLocal = function(worldPoint, result){ - return Transform.pointToLocalFrame(this.position, this.quaternion, worldPoint, result); -}; - -/** - * @static - * @method pointToWorldFrame - * @param {Vec3} position - * @param {Vec3} quaternion - * @param {Vec3} localPoint - * @param {Vec3} result - */ -Transform.pointToWorldFrame = function(position, quaternion, localPoint, result){ - var result = result || new Vec3(); - quaternion.vmult(localPoint, result); - result.vadd(position, result); - return result; -}; - -/** - * Get a local point in global transform coordinates. - * @method pointToWorld - * @param {Vec3} point - * @param {Vec3} result - * @return {Vec3} The "result" vector object - */ -Transform.prototype.pointToWorld = function(localPoint, result){ - return Transform.pointToWorldFrame(this.position, this.quaternion, localPoint, result); -}; - - -Transform.prototype.vectorToWorldFrame = function(localVector, result){ - var result = result || new Vec3(); - this.quaternion.vmult(localVector, result); - return result; -}; - -Transform.vectorToWorldFrame = function(quaternion, localVector, result){ - quaternion.vmult(localVector, result); - return result; -}; - -Transform.vectorToLocalFrame = function(position, quaternion, worldVector, result){ - var result = result || new Vec3(); - quaternion.w *= -1; - quaternion.vmult(worldVector, result); - quaternion.w *= -1; - return result; -}; - -},{"./Quaternion":28,"./Vec3":30}],30:[function(_dereq_,module,exports){ -module.exports = Vec3; - -var Mat3 = _dereq_('./Mat3'); - -/** - * 3-dimensional vector - * @class Vec3 - * @constructor - * @param {Number} x - * @param {Number} y - * @param {Number} z - * @author schteppe - * @example - * var v = new Vec3(1, 2, 3); - * console.log('x=' + v.x); // x=1 - */ -function Vec3(x,y,z){ - /** - * @property x - * @type {Number} - */ - this.x = x||0.0; - - /** - * @property y - * @type {Number} - */ - this.y = y||0.0; - - /** - * @property z - * @type {Number} - */ - this.z = z||0.0; -} - -/** - * @static - * @property {Vec3} ZERO - */ -Vec3.ZERO = new Vec3(0, 0, 0); - -/** - * @static - * @property {Vec3} UNIT_X - */ -Vec3.UNIT_X = new Vec3(1, 0, 0); - -/** - * @static - * @property {Vec3} UNIT_Y - */ -Vec3.UNIT_Y = new Vec3(0, 1, 0); - -/** - * @static - * @property {Vec3} UNIT_Z - */ -Vec3.UNIT_Z = new Vec3(0, 0, 1); - -/** - * Vector cross product - * @method cross - * @param {Vec3} v - * @param {Vec3} target Optional. Target to save in. - * @return {Vec3} - */ -Vec3.prototype.cross = function(v,target){ - var vx=v.x, vy=v.y, vz=v.z, x=this.x, y=this.y, z=this.z; - target = target || new Vec3(); - - target.x = (y * vz) - (z * vy); - target.y = (z * vx) - (x * vz); - target.z = (x * vy) - (y * vx); - - return target; -}; - -/** - * Set the vectors' 3 elements - * @method set - * @param {Number} x - * @param {Number} y - * @param {Number} z - * @return Vec3 - */ -Vec3.prototype.set = function(x,y,z){ - this.x = x; - this.y = y; - this.z = z; - return this; -}; - -/** - * Set all components of the vector to zero. - * @method setZero - */ -Vec3.prototype.setZero = function(){ - this.x = this.y = this.z = 0; -}; - -/** - * Vector addition - * @method vadd - * @param {Vec3} v - * @param {Vec3} target Optional. - * @return {Vec3} - */ -Vec3.prototype.vadd = function(v,target){ - if(target){ - target.x = v.x + this.x; - target.y = v.y + this.y; - target.z = v.z + this.z; - } else { - return new Vec3(this.x + v.x, - this.y + v.y, - this.z + v.z); - } -}; - -/** - * Vector subtraction - * @method vsub - * @param {Vec3} v - * @param {Vec3} target Optional. Target to save in. - * @return {Vec3} - */ -Vec3.prototype.vsub = function(v,target){ - if(target){ - target.x = this.x - v.x; - target.y = this.y - v.y; - target.z = this.z - v.z; - } else { - return new Vec3(this.x-v.x, - this.y-v.y, - this.z-v.z); - } -}; - -/** - * Get the cross product matrix a_cross from a vector, such that a x b = a_cross * b = c - * @method crossmat - * @see http://www8.cs.umu.se/kurser/TDBD24/VT06/lectures/Lecture6.pdf - * @return {Mat3} - */ -Vec3.prototype.crossmat = function(){ - return new Mat3([ 0, -this.z, this.y, - this.z, 0, -this.x, - -this.y, this.x, 0]); -}; - -/** - * Normalize the vector. Note that this changes the values in the vector. - * @method normalize - * @return {Number} Returns the norm of the vector - */ -Vec3.prototype.normalize = function(){ - var x=this.x, y=this.y, z=this.z; - var n = Math.sqrt(x*x + y*y + z*z); - if(n>0.0){ - var invN = 1/n; - this.x *= invN; - this.y *= invN; - this.z *= invN; - } else { - // Make something up - this.x = 0; - this.y = 0; - this.z = 0; - } - return n; -}; - -/** - * Get the version of this vector that is of length 1. - * @method unit - * @param {Vec3} target Optional target to save in - * @return {Vec3} Returns the unit vector - */ -Vec3.prototype.unit = function(target){ - target = target || new Vec3(); - var x=this.x, y=this.y, z=this.z; - var ninv = Math.sqrt(x*x + y*y + z*z); - if(ninv>0.0){ - ninv = 1.0/ninv; - target.x = x * ninv; - target.y = y * ninv; - target.z = z * ninv; - } else { - target.x = 1; - target.y = 0; - target.z = 0; - } - return target; -}; - -/** - * Get the length of the vector - * @method norm - * @return {Number} - * @deprecated Use .length() instead - */ -Vec3.prototype.norm = function(){ - var x=this.x, y=this.y, z=this.z; - return Math.sqrt(x*x + y*y + z*z); -}; - -/** - * Get the length of the vector - * @method length - * @return {Number} - */ -Vec3.prototype.length = Vec3.prototype.norm; - -/** - * Get the squared length of the vector - * @method norm2 - * @return {Number} - * @deprecated Use .lengthSquared() instead. - */ -Vec3.prototype.norm2 = function(){ - return this.dot(this); -}; - -/** - * Get the squared length of the vector. - * @method lengthSquared - * @return {Number} - */ -Vec3.prototype.lengthSquared = Vec3.prototype.norm2; - -/** - * Get distance from this point to another point - * @method distanceTo - * @param {Vec3} p - * @return {Number} - */ -Vec3.prototype.distanceTo = function(p){ - var x=this.x, y=this.y, z=this.z; - var px=p.x, py=p.y, pz=p.z; - return Math.sqrt((px-x)*(px-x)+ - (py-y)*(py-y)+ - (pz-z)*(pz-z)); -}; - -/** - * Get squared distance from this point to another point - * @method distanceSquared - * @param {Vec3} p - * @return {Number} - */ -Vec3.prototype.distanceSquared = function(p){ - var x=this.x, y=this.y, z=this.z; - var px=p.x, py=p.y, pz=p.z; - return (px-x)*(px-x) + (py-y)*(py-y) + (pz-z)*(pz-z); -}; - -/** - * Multiply all the components of the vector with a scalar. - * @deprecated Use .scale instead - * @method mult - * @param {Number} scalar - * @param {Vec3} target The vector to save the result in. - * @return {Vec3} - * @deprecated Use .scale() instead - */ -Vec3.prototype.mult = function(scalar,target){ - target = target || new Vec3(); - var x = this.x, - y = this.y, - z = this.z; - target.x = scalar * x; - target.y = scalar * y; - target.z = scalar * z; - return target; -}; - -/** - * Multiply the vector with a scalar. - * @method scale - * @param {Number} scalar - * @param {Vec3} target - * @return {Vec3} - */ -Vec3.prototype.scale = Vec3.prototype.mult; - -/** - * Calculate dot product - * @method dot - * @param {Vec3} v - * @return {Number} - */ -Vec3.prototype.dot = function(v){ - return this.x * v.x + this.y * v.y + this.z * v.z; -}; - -/** - * @method isZero - * @return bool - */ -Vec3.prototype.isZero = function(){ - return this.x===0 && this.y===0 && this.z===0; -}; - -/** - * Make the vector point in the opposite direction. - * @method negate - * @param {Vec3} target Optional target to save in - * @return {Vec3} - */ -Vec3.prototype.negate = function(target){ - target = target || new Vec3(); - target.x = -this.x; - target.y = -this.y; - target.z = -this.z; - return target; -}; - -/** - * Compute two artificial tangents to the vector - * @method tangents - * @param {Vec3} t1 Vector object to save the first tangent in - * @param {Vec3} t2 Vector object to save the second tangent in - */ -var Vec3_tangents_n = new Vec3(); -var Vec3_tangents_randVec = new Vec3(); -Vec3.prototype.tangents = function(t1,t2){ - var norm = this.norm(); - if(norm>0.0){ - var n = Vec3_tangents_n; - var inorm = 1/norm; - n.set(this.x*inorm,this.y*inorm,this.z*inorm); - var randVec = Vec3_tangents_randVec; - if(Math.abs(n.x) < 0.9){ - randVec.set(1,0,0); - n.cross(randVec,t1); - } else { - randVec.set(0,1,0); - n.cross(randVec,t1); - } - n.cross(t1,t2); - } else { - // The normal length is zero, make something up - t1.set(1, 0, 0); - t2.set(0, 1, 0); - } -}; - -/** - * Converts to a more readable format - * @method toString - * @return string - */ -Vec3.prototype.toString = function(){ - return this.x+","+this.y+","+this.z; -}; - -/** - * Converts to an array - * @method toArray - * @return Array - */ -Vec3.prototype.toArray = function(){ - return [this.x, this.y, this.z]; -}; - -/** - * Copies value of source to this vector. - * @method copy - * @param {Vec3} source - * @return {Vec3} this - */ -Vec3.prototype.copy = function(source){ - this.x = source.x; - this.y = source.y; - this.z = source.z; - return this; -}; - - -/** - * Do a linear interpolation between two vectors - * @method lerp - * @param {Vec3} v - * @param {Number} t A number between 0 and 1. 0 will make this function return u, and 1 will make it return v. Numbers in between will generate a vector in between them. - * @param {Vec3} target - */ -Vec3.prototype.lerp = function(v,t,target){ - var x=this.x, y=this.y, z=this.z; - target.x = x + (v.x-x)*t; - target.y = y + (v.y-y)*t; - target.z = z + (v.z-z)*t; -}; - -/** - * Check if a vector equals is almost equal to another one. - * @method almostEquals - * @param {Vec3} v - * @param {Number} precision - * @return bool - */ -Vec3.prototype.almostEquals = function(v,precision){ - if(precision===undefined){ - precision = 1e-6; - } - if( Math.abs(this.x-v.x)>precision || - Math.abs(this.y-v.y)>precision || - Math.abs(this.z-v.z)>precision){ - return false; - } - return true; -}; - -/** - * Check if a vector is almost zero - * @method almostZero - * @param {Number} precision - */ -Vec3.prototype.almostZero = function(precision){ - if(precision===undefined){ - precision = 1e-6; - } - if( Math.abs(this.x)>precision || - Math.abs(this.y)>precision || - Math.abs(this.z)>precision){ - return false; - } - return true; -}; - -var antip_neg = new Vec3(); - -/** - * Check if the vector is anti-parallel to another vector. - * @method isAntiparallelTo - * @param {Vec3} v - * @param {Number} precision Set to zero for exact comparisons - * @return {Boolean} - */ -Vec3.prototype.isAntiparallelTo = function(v,precision){ - this.negate(antip_neg); - return antip_neg.almostEquals(v,precision); -}; - -/** - * Clone the vector - * @method clone - * @return {Vec3} - */ -Vec3.prototype.clone = function(){ - return new Vec3(this.x, this.y, this.z); -}; -},{"./Mat3":27}],31:[function(_dereq_,module,exports){ -module.exports = Body; - -var EventTarget = _dereq_('../utils/EventTarget'); -var Shape = _dereq_('../shapes/Shape'); -var Vec3 = _dereq_('../math/Vec3'); -var Mat3 = _dereq_('../math/Mat3'); -var Quaternion = _dereq_('../math/Quaternion'); -var Material = _dereq_('../material/Material'); -var AABB = _dereq_('../collision/AABB'); -var Box = _dereq_('../shapes/Box'); - -/** - * Base class for all body types. - * @class Body - * @constructor - * @extends EventTarget - * @param {object} [options] - * @param {Vec3} [options.position] - * @param {Vec3} [options.velocity] - * @param {Vec3} [options.angularVelocity] - * @param {Quaternion} [options.quaternion] - * @param {number} [options.mass] - * @param {Material} [options.material] - * @param {number} [options.type] - * @param {number} [options.linearDamping=0.01] - * @param {number} [options.angularDamping=0.01] - * @param {boolean} [options.allowSleep=true] - * @param {number} [options.sleepSpeedLimit=0.1] - * @param {number} [options.sleepTimeLimit=1] - * @param {number} [options.collisionFilterGroup=1] - * @param {number} [options.collisionFilterMask=1] - * @param {boolean} [options.fixedRotation=false] - * @param {Body} [options.shape] - * @example - * var body = new Body({ - * mass: 1 - * }); - * var shape = new Sphere(1); - * body.addShape(shape); - * world.add(body); - */ -function Body(options){ - options = options || {}; - - EventTarget.apply(this); - - this.id = Body.idCounter++; - - /** - * Reference to the world the body is living in - * @property world - * @type {World} - */ - this.world = null; - - /** - * Callback function that is used BEFORE stepping the system. Use it to apply forces, for example. Inside the function, "this" will refer to this Body object. - * @property preStep - * @type {Function} - * @deprecated Use World events instead - */ - this.preStep = null; - - /** - * Callback function that is used AFTER stepping the system. Inside the function, "this" will refer to this Body object. - * @property postStep - * @type {Function} - * @deprecated Use World events instead - */ - this.postStep = null; - - this.vlambda = new Vec3(); - - /** - * @property {Number} collisionFilterGroup - */ - this.collisionFilterGroup = typeof(options.collisionFilterGroup) === 'number' ? options.collisionFilterGroup : 1; - - /** - * @property {Number} collisionFilterMask - */ - this.collisionFilterMask = typeof(options.collisionFilterMask) === 'number' ? options.collisionFilterMask : 1; - - /** - * Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled. - * @property {Number} collisionResponse - */ - this.collisionResponse = true; - - /** - * @property position - * @type {Vec3} - */ - this.position = new Vec3(); - - if(options.position){ - this.position.copy(options.position); - } - - /** - * @property {Vec3} previousPosition - */ - this.previousPosition = new Vec3(); - - /** - * Initial position of the body - * @property initPosition - * @type {Vec3} - */ - this.initPosition = new Vec3(); - - /** - * @property velocity - * @type {Vec3} - */ - this.velocity = new Vec3(); - - if(options.velocity){ - this.velocity.copy(options.velocity); - } - - /** - * @property initVelocity - * @type {Vec3} - */ - this.initVelocity = new Vec3(); - - /** - * Linear force on the body - * @property force - * @type {Vec3} - */ - this.force = new Vec3(); - - var mass = typeof(options.mass) === 'number' ? options.mass : 0; - - /** - * @property mass - * @type {Number} - * @default 0 - */ - this.mass = mass; - - /** - * @property invMass - * @type {Number} - */ - this.invMass = mass > 0 ? 1.0 / mass : 0; - - /** - * @property material - * @type {Material} - */ - this.material = options.material || null; - - /** - * @property linearDamping - * @type {Number} - */ - this.linearDamping = typeof(options.linearDamping) === 'number' ? options.linearDamping : 0.01; - - /** - * One of: Body.DYNAMIC, Body.STATIC and Body.KINEMATIC. - * @property type - * @type {Number} - */ - this.type = (mass <= 0.0 ? Body.STATIC : Body.DYNAMIC); - if(typeof(options.type) === typeof(Body.STATIC)){ - this.type = options.type; - } - - /** - * If true, the body will automatically fall to sleep. - * @property allowSleep - * @type {Boolean} - * @default true - */ - this.allowSleep = typeof(options.allowSleep) !== 'undefined' ? options.allowSleep : true; - - /** - * Current sleep state. - * @property sleepState - * @type {Number} - */ - this.sleepState = 0; - - /** - * If the speed (the norm of the velocity) is smaller than this value, the body is considered sleepy. - * @property sleepSpeedLimit - * @type {Number} - * @default 0.1 - */ - this.sleepSpeedLimit = typeof(options.sleepSpeedLimit) !== 'undefined' ? options.sleepSpeedLimit : 0.1; - - /** - * If the body has been sleepy for this sleepTimeLimit seconds, it is considered sleeping. - * @property sleepTimeLimit - * @type {Number} - * @default 1 - */ - this.sleepTimeLimit = typeof(options.sleepTimeLimit) !== 'undefined' ? options.sleepTimeLimit : 1; - - this.timeLastSleepy = 0; - - this._wakeUpAfterNarrowphase = false; - - - /** - * Rotational force on the body, around center of mass - * @property {Vec3} torque - */ - this.torque = new Vec3(); - - /** - * Orientation of the body - * @property quaternion - * @type {Quaternion} - */ - this.quaternion = new Quaternion(); - - if(options.quaternion){ - this.quaternion.copy(options.quaternion); - } - - /** - * @property initQuaternion - * @type {Quaternion} - */ - this.initQuaternion = new Quaternion(); - - /** - * @property angularVelocity - * @type {Vec3} - */ - this.angularVelocity = new Vec3(); - - if(options.angularVelocity){ - this.angularVelocity.copy(options.angularVelocity); - } - - /** - * @property initAngularVelocity - * @type {Vec3} - */ - this.initAngularVelocity = new Vec3(); - - this.interpolatedPosition = new Vec3(); - this.interpolatedQuaternion = new Quaternion(); - - /** - * @property shapes - * @type {array} - */ - this.shapes = []; - - /** - * @property shapeOffsets - * @type {array} - */ - this.shapeOffsets = []; - - /** - * @property shapeOrientations - * @type {array} - */ - this.shapeOrientations = []; - - /** - * @property inertia - * @type {Vec3} - */ - this.inertia = new Vec3(); - - /** - * @property {Vec3} invInertia - */ - this.invInertia = new Vec3(); - - /** - * @property {Mat3} invInertiaWorld - */ - this.invInertiaWorld = new Mat3(); - - this.invMassSolve = 0; - - /** - * @property {Vec3} invInertiaSolve - */ - this.invInertiaSolve = new Vec3(); - - /** - * @property {Mat3} invInertiaWorldSolve - */ - this.invInertiaWorldSolve = new Mat3(); - - /** - * Set to true if you don't want the body to rotate. Make sure to run .updateMassProperties() after changing this. - * @property {Boolean} fixedRotation - * @default false - */ - this.fixedRotation = typeof(options.fixedRotation) !== "undefined" ? options.fixedRotation : false; - - /** - * @property {Number} angularDamping - */ - this.angularDamping = typeof(options.angularDamping) !== 'undefined' ? options.angularDamping : 0.01; - - /** - * @property aabb - * @type {AABB} - */ - this.aabb = new AABB(); - - /** - * Indicates if the AABB needs to be updated before use. - * @property aabbNeedsUpdate - * @type {Boolean} - */ - this.aabbNeedsUpdate = true; - - this.wlambda = new Vec3(); - - if(options.shape){ - this.addShape(options.shape); - } - - this.updateMassProperties(); -} -Body.prototype = new EventTarget(); -Body.prototype.constructor = Body; - -/** - * A dynamic body is fully simulated. Can be moved manually by the user, but normally they move according to forces. A dynamic body can collide with all body types. A dynamic body always has finite, non-zero mass. - * @static - * @property DYNAMIC - * @type {Number} - */ -Body.DYNAMIC = 1; - -/** - * A static body does not move during simulation and behaves as if it has infinite mass. Static bodies can be moved manually by setting the position of the body. The velocity of a static body is always zero. Static bodies do not collide with other static or kinematic bodies. - * @static - * @property STATIC - * @type {Number} - */ -Body.STATIC = 2; - -/** - * A kinematic body moves under simulation according to its velocity. They do not respond to forces. They can be moved manually, but normally a kinematic body is moved by setting its velocity. A kinematic body behaves as if it has infinite mass. Kinematic bodies do not collide with other static or kinematic bodies. - * @static - * @property KINEMATIC - * @type {Number} - */ -Body.KINEMATIC = 4; - - - -/** - * @static - * @property AWAKE - * @type {number} - */ -Body.AWAKE = 0; - -/** - * @static - * @property SLEEPY - * @type {number} - */ -Body.SLEEPY = 1; - -/** - * @static - * @property SLEEPING - * @type {number} - */ -Body.SLEEPING = 2; - -Body.idCounter = 0; - -/** - * Wake the body up. - * @method wakeUp - */ -Body.prototype.wakeUp = function(){ - var s = this.sleepState; - this.sleepState = 0; - if(s === Body.SLEEPING){ - this.dispatchEvent({type:"wakeup"}); - } -}; - -/** - * Force body sleep - * @method sleep - */ -Body.prototype.sleep = function(){ - this.sleepState = Body.SLEEPING; - this.velocity.set(0,0,0); - this.angularVelocity.set(0,0,0); -}; - -Body.sleepyEvent = { - type: "sleepy" -}; - -Body.sleepEvent = { - type: "sleep" -}; - -/** - * Called every timestep to update internal sleep timer and change sleep state if needed. - * @method sleepTick - * @param {Number} time The world time in seconds - */ -Body.prototype.sleepTick = function(time){ - if(this.allowSleep){ - var sleepState = this.sleepState; - var speedSquared = this.velocity.norm2() + this.angularVelocity.norm2(); - var speedLimitSquared = Math.pow(this.sleepSpeedLimit,2); - if(sleepState===Body.AWAKE && speedSquared < speedLimitSquared){ - this.sleepState = Body.SLEEPY; // Sleepy - this.timeLastSleepy = time; - this.dispatchEvent(Body.sleepyEvent); - } else if(sleepState===Body.SLEEPY && speedSquared > speedLimitSquared){ - this.wakeUp(); // Wake up - } else if(sleepState===Body.SLEEPY && (time - this.timeLastSleepy ) > this.sleepTimeLimit){ - this.sleep(); // Sleeping - this.dispatchEvent(Body.sleepEvent); - } - } -}; - -/** - * If the body is sleeping, it should be immovable / have infinite mass during solve. We solve it by having a separate "solve mass". - * @method updateSolveMassProperties - */ -Body.prototype.updateSolveMassProperties = function(){ - if(this.sleepState === Body.SLEEPING || this.type === Body.KINEMATIC){ - this.invMassSolve = 0; - this.invInertiaSolve.setZero(); - this.invInertiaWorldSolve.setZero(); - } else { - this.invMassSolve = this.invMass; - this.invInertiaSolve.copy(this.invInertia); - this.invInertiaWorldSolve.copy(this.invInertiaWorld); - } -}; - -/** - * Convert a world point to local body frame. - * @method pointToLocalFrame - * @param {Vec3} worldPoint - * @param {Vec3} result - * @return {Vec3} - */ -Body.prototype.pointToLocalFrame = function(worldPoint,result){ - var result = result || new Vec3(); - worldPoint.vsub(this.position,result); - this.quaternion.conjugate().vmult(result,result); - return result; -}; - -/** - * Convert a world vector to local body frame. - * @method vectorToLocalFrame - * @param {Vec3} worldPoint - * @param {Vec3} result - * @return {Vec3} - */ -Body.prototype.vectorToLocalFrame = function(worldVector, result){ - var result = result || new Vec3(); - this.quaternion.conjugate().vmult(worldVector,result); - return result; -}; - -/** - * Convert a local body point to world frame. - * @method pointToWorldFrame - * @param {Vec3} localPoint - * @param {Vec3} result - * @return {Vec3} - */ -Body.prototype.pointToWorldFrame = function(localPoint,result){ - var result = result || new Vec3(); - this.quaternion.vmult(localPoint,result); - result.vadd(this.position,result); - return result; -}; - -/** - * Convert a local body point to world frame. - * @method vectorToWorldFrame - * @param {Vec3} localVector - * @param {Vec3} result - * @return {Vec3} - */ -Body.prototype.vectorToWorldFrame = function(localVector, result){ - var result = result || new Vec3(); - this.quaternion.vmult(localVector, result); - return result; -}; - -var tmpVec = new Vec3(); -var tmpQuat = new Quaternion(); - -/** - * Add a shape to the body with a local offset and orientation. - * @method addShape - * @param {Shape} shape - * @param {Vec3} offset - * @param {Quaternion} quaternion - * @return {Body} The body object, for chainability. - */ -Body.prototype.addShape = function(shape, _offset, _orientation){ - var offset = new Vec3(); - var orientation = new Quaternion(); - - if(_offset){ - offset.copy(_offset); - } - if(_orientation){ - orientation.copy(_orientation); - } - - this.shapes.push(shape); - this.shapeOffsets.push(offset); - this.shapeOrientations.push(orientation); - this.updateMassProperties(); - this.updateBoundingRadius(); - - this.aabbNeedsUpdate = true; - - return this; -}; - -/** - * Update the bounding radius of the body. Should be done if any of the shapes are changed. - * @method updateBoundingRadius - */ -Body.prototype.updateBoundingRadius = function(){ - var shapes = this.shapes, - shapeOffsets = this.shapeOffsets, - N = shapes.length, - radius = 0; - - for(var i=0; i!==N; i++){ - var shape = shapes[i]; - shape.updateBoundingSphereRadius(); - var offset = shapeOffsets[i].norm(), - r = shape.boundingSphereRadius; - if(offset + r > radius){ - radius = offset + r; - } - } - - this.boundingRadius = radius; -}; - -var computeAABB_shapeAABB = new AABB(); - -/** - * Updates the .aabb - * @method computeAABB - * @todo rename to updateAABB() - */ -Body.prototype.computeAABB = function(){ - var shapes = this.shapes, - shapeOffsets = this.shapeOffsets, - shapeOrientations = this.shapeOrientations, - N = shapes.length, - offset = tmpVec, - orientation = tmpQuat, - bodyQuat = this.quaternion, - aabb = this.aabb, - shapeAABB = computeAABB_shapeAABB; - - for(var i=0; i!==N; i++){ - var shape = shapes[i]; - - // Get shape world quaternion - shapeOrientations[i].mult(bodyQuat, orientation); - - // Get shape world position - orientation.vmult(shapeOffsets[i], offset); - offset.vadd(this.position, offset); - - // vec2.rotate(offset, shapeOffsets[i], bodyAngle); - // vec2.add(offset, offset, this.position); - - // Get shape AABB - shape.calculateWorldAABB(offset, orientation, shapeAABB.lowerBound, shapeAABB.upperBound); - - if(i === 0){ - aabb.copy(shapeAABB); - } else { - aabb.extend(shapeAABB); - } - } - - this.aabbNeedsUpdate = false; -}; - -var uiw_m1 = new Mat3(), - uiw_m2 = new Mat3(), - uiw_m3 = new Mat3(); - -/** - * Update .inertiaWorld and .invInertiaWorld - * @method updateInertiaWorld - */ -Body.prototype.updateInertiaWorld = function(force){ - var I = this.invInertia; - if (I.x === I.y && I.y === I.z && !force) { - // If inertia M = s*I, where I is identity and s a scalar, then - // R*M*R' = R*(s*I)*R' = s*R*I*R' = s*R*R' = s*I = M - // where R is the rotation matrix. - // In other words, we don't have to transform the inertia if all - // inertia diagonal entries are equal. - } else { - var m1 = uiw_m1, - m2 = uiw_m2, - m3 = uiw_m3; - m1.setRotationFromQuaternion(this.quaternion); - m1.transpose(m2); - m1.scale(I,m1); - m1.mmult(m2,this.invInertiaWorld); - //m3.getTrace(this.invInertiaWorld); - } - - /* - this.quaternion.vmult(this.inertia,this.inertiaWorld); - this.quaternion.vmult(this.invInertia,this.invInertiaWorld); - */ -}; - -/** - * Apply force to a world point. This could for example be a point on the Body surface. Applying force this way will add to Body.force and Body.torque. - * @method applyForce - * @param {Vec3} force The amount of force to add. - * @param {Vec3} worldPoint A world point to apply the force on. - */ -var Body_applyForce_r = new Vec3(); -var Body_applyForce_rotForce = new Vec3(); -Body.prototype.applyForce = function(force,worldPoint){ - if(this.type !== Body.DYNAMIC){ - return; - } - - // Compute point position relative to the body center - var r = Body_applyForce_r; - worldPoint.vsub(this.position,r); - - // Compute produced rotational force - var rotForce = Body_applyForce_rotForce; - r.cross(force,rotForce); - - // Add linear force - this.force.vadd(force,this.force); - - // Add rotational force - this.torque.vadd(rotForce,this.torque); -}; - -/** - * Apply force to a local point in the body. - * @method applyLocalForce - * @param {Vec3} force The force vector to apply, defined locally in the body frame. - * @param {Vec3} localPoint A local point in the body to apply the force on. - */ -var Body_applyLocalForce_worldForce = new Vec3(); -var Body_applyLocalForce_worldPoint = new Vec3(); -Body.prototype.applyLocalForce = function(localForce, localPoint){ - if(this.type !== Body.DYNAMIC){ - return; - } - - var worldForce = Body_applyLocalForce_worldForce; - var worldPoint = Body_applyLocalForce_worldPoint; - - // Transform the force vector to world space - this.vectorToWorldFrame(localForce, worldForce); - this.pointToWorldFrame(localPoint, worldPoint); - - this.applyForce(worldForce, worldPoint); -}; - -/** - * Apply impulse to a world point. This could for example be a point on the Body surface. An impulse is a force added to a body during a short period of time (impulse = force * time). Impulses will be added to Body.velocity and Body.angularVelocity. - * @method applyImpulse - * @param {Vec3} impulse The amount of impulse to add. - * @param {Vec3} worldPoint A world point to apply the force on. - */ -var Body_applyImpulse_r = new Vec3(); -var Body_applyImpulse_velo = new Vec3(); -var Body_applyImpulse_rotVelo = new Vec3(); -Body.prototype.applyImpulse = function(impulse, worldPoint){ - if(this.type !== Body.DYNAMIC){ - return; - } - - // Compute point position relative to the body center - var r = Body_applyImpulse_r; - worldPoint.vsub(this.position,r); - - // Compute produced central impulse velocity - var velo = Body_applyImpulse_velo; - velo.copy(impulse); - velo.mult(this.invMass,velo); - - // Add linear impulse - this.velocity.vadd(velo, this.velocity); - - // Compute produced rotational impulse velocity - var rotVelo = Body_applyImpulse_rotVelo; - r.cross(impulse,rotVelo); - - /* - rotVelo.x *= this.invInertia.x; - rotVelo.y *= this.invInertia.y; - rotVelo.z *= this.invInertia.z; - */ - this.invInertiaWorld.vmult(rotVelo,rotVelo); - - // Add rotational Impulse - this.angularVelocity.vadd(rotVelo, this.angularVelocity); -}; - -/** - * Apply locally-defined impulse to a local point in the body. - * @method applyLocalImpulse - * @param {Vec3} force The force vector to apply, defined locally in the body frame. - * @param {Vec3} localPoint A local point in the body to apply the force on. - */ -var Body_applyLocalImpulse_worldImpulse = new Vec3(); -var Body_applyLocalImpulse_worldPoint = new Vec3(); -Body.prototype.applyLocalImpulse = function(localImpulse, localPoint){ - if(this.type !== Body.DYNAMIC){ - return; - } - - var worldImpulse = Body_applyLocalImpulse_worldImpulse; - var worldPoint = Body_applyLocalImpulse_worldPoint; - - // Transform the force vector to world space - this.vectorToWorldFrame(localImpulse, worldImpulse); - this.pointToWorldFrame(localPoint, worldPoint); - - this.applyImpulse(worldImpulse, worldPoint); -}; - -var Body_updateMassProperties_halfExtents = new Vec3(); - -/** - * Should be called whenever you change the body shape or mass. - * @method updateMassProperties - */ -Body.prototype.updateMassProperties = function(){ - var halfExtents = Body_updateMassProperties_halfExtents; - - this.invMass = this.mass > 0 ? 1.0 / this.mass : 0; - var I = this.inertia; - var fixed = this.fixedRotation; - - // Approximate with AABB box - this.computeAABB(); - halfExtents.set( - (this.aabb.upperBound.x-this.aabb.lowerBound.x) / 2, - (this.aabb.upperBound.y-this.aabb.lowerBound.y) / 2, - (this.aabb.upperBound.z-this.aabb.lowerBound.z) / 2 - ); - Box.calculateInertia(halfExtents, this.mass, I); - - this.invInertia.set( - I.x > 0 && !fixed ? 1.0 / I.x : 0, - I.y > 0 && !fixed ? 1.0 / I.y : 0, - I.z > 0 && !fixed ? 1.0 / I.z : 0 - ); - this.updateInertiaWorld(true); -}; - -/** - * Get world velocity of a point in the body. - * @method getVelocityAtWorldPoint - * @param {Vec3} worldPoint - * @param {Vec3} result - * @return {Vec3} The result vector. - */ -Body.prototype.getVelocityAtWorldPoint = function(worldPoint, result){ - var r = new Vec3(); - worldPoint.vsub(this.position, r); - this.angularVelocity.cross(r, result); - this.velocity.vadd(result, result); - return result; -}; - -},{"../collision/AABB":3,"../material/Material":25,"../math/Mat3":27,"../math/Quaternion":28,"../math/Vec3":30,"../shapes/Box":37,"../shapes/Shape":43,"../utils/EventTarget":49}],32:[function(_dereq_,module,exports){ -var Body = _dereq_('./Body'); -var Vec3 = _dereq_('../math/Vec3'); -var Quaternion = _dereq_('../math/Quaternion'); -var RaycastResult = _dereq_('../collision/RaycastResult'); -var Ray = _dereq_('../collision/Ray'); -var WheelInfo = _dereq_('../objects/WheelInfo'); - -module.exports = RaycastVehicle; - -/** - * Vehicle helper class that casts rays from the wheel positions towards the ground and applies forces. - * @class RaycastVehicle - * @constructor - * @param {object} [options] - * @param {Body} [options.chassisBody] The car chassis body. - * @param {integer} [options.indexRightAxis] Axis to use for right. x=0, y=1, z=2 - * @param {integer} [options.indexLeftAxis] - * @param {integer} [options.indexUpAxis] - */ -function RaycastVehicle(options){ - - /** - * @property {Body} chassisBody - */ - this.chassisBody = options.chassisBody; - - /** - * An array of WheelInfo objects. - * @property {array} wheelInfos - */ - this.wheelInfos = []; - - /** - * Will be set to true if the car is sliding. - * @property {boolean} sliding - */ - this.sliding = false; - - /** - * @property {World} world - */ - this.world = null; - - /** - * Index of the right axis, 0=x, 1=y, 2=z - * @property {integer} indexRightAxis - * @default 1 - */ - this.indexRightAxis = typeof(options.indexRightAxis) !== 'undefined' ? options.indexRightAxis : 1; - - /** - * Index of the forward axis, 0=x, 1=y, 2=z - * @property {integer} indexForwardAxis - * @default 0 - */ - this.indexForwardAxis = typeof(options.indexForwardAxis) !== 'undefined' ? options.indexForwardAxis : 0; - - /** - * Index of the up axis, 0=x, 1=y, 2=z - * @property {integer} indexUpAxis - * @default 2 - */ - this.indexUpAxis = typeof(options.indexUpAxis) !== 'undefined' ? options.indexUpAxis : 2; -} - -var tmpVec1 = new Vec3(); -var tmpVec2 = new Vec3(); -var tmpVec3 = new Vec3(); -var tmpVec4 = new Vec3(); -var tmpVec5 = new Vec3(); -var tmpVec6 = new Vec3(); -var tmpRay = new Ray(); - -/** - * Add a wheel. For information about the options, see WheelInfo. - * @method addWheel - * @param {object} [options] - */ -RaycastVehicle.prototype.addWheel = function(options){ - options = options || {}; - - var info = new WheelInfo(options); - var index = this.wheelInfos.length; - this.wheelInfos.push(info); - - return index; -}; - -/** - * Set the steering value of a wheel. - * @method setSteeringValue - * @param {number} value - * @param {integer} wheelIndex - */ -RaycastVehicle.prototype.setSteeringValue = function(value, wheelIndex){ - var wheel = this.wheelInfos[wheelIndex]; - wheel.steering = value; -}; - -var torque = new Vec3(); - -/** - * Set the wheel force to apply on one of the wheels each time step - * @method applyEngineForce - * @param {number} value - * @param {integer} wheelIndex - */ -RaycastVehicle.prototype.applyEngineForce = function(value, wheelIndex){ - this.wheelInfos[wheelIndex].engineForce = value; -}; - -/** - * Set the braking force of a wheel - * @method setBrake - * @param {number} brake - * @param {integer} wheelIndex - */ -RaycastVehicle.prototype.setBrake = function(brake, wheelIndex){ - this.wheelInfos[wheelIndex].brake = brake; -}; - -/** - * Add the vehicle including its constraints to the world. - * @method addToWorld - * @param {World} world - */ -RaycastVehicle.prototype.addToWorld = function(world){ - var constraints = this.constraints; - world.add(this.chassisBody); - var that = this; - this.preStepCallback = function(){ - that.updateVehicle(world.dt); - }; - world.addEventListener('preStep', this.preStepCallback); - this.world = world; -}; - -/** - * Get one of the wheel axles, world-oriented. - * @private - * @method getVehicleAxisWorld - * @param {integer} axisIndex - * @param {Vec3} result - */ -RaycastVehicle.prototype.getVehicleAxisWorld = function(axisIndex, result){ - result.set( - axisIndex === 0 ? 1 : 0, - axisIndex === 1 ? 1 : 0, - axisIndex === 2 ? 1 : 0 - ); - this.chassisBody.vectorToWorldFrame(result, result); -}; - -RaycastVehicle.prototype.updateVehicle = function(timeStep){ - var wheelInfos = this.wheelInfos; - var numWheels = wheelInfos.length; - var chassisBody = this.chassisBody; - - for (var i = 0; i < numWheels; i++) { - this.updateWheelTransform(i); - } - - this.currentVehicleSpeedKmHour = 3.6 * chassisBody.velocity.norm(); - - var forwardWorld = new Vec3(); - this.getVehicleAxisWorld(this.indexForwardAxis, forwardWorld); - - if (forwardWorld.dot(chassisBody.velocity) < 0){ - this.currentVehicleSpeedKmHour *= -1; - } - - // simulate suspension - for (var i = 0; i < numWheels; i++) { - this.castRay(wheelInfos[i]); - } - - this.updateSuspension(timeStep); - - var impulse = new Vec3(); - var relpos = new Vec3(); - for (var i = 0; i < numWheels; i++) { - //apply suspension force - var wheel = wheelInfos[i]; - var suspensionForce = wheel.suspensionForce; - if (suspensionForce > wheel.maxSuspensionForce) { - suspensionForce = wheel.maxSuspensionForce; - } - wheel.raycastResult.hitNormalWorld.scale(suspensionForce * timeStep, impulse); - - wheel.raycastResult.hitPointWorld.vsub(chassisBody.position, relpos); - chassisBody.applyImpulse(impulse, wheel.raycastResult.hitPointWorld/*relpos*/); - } - - this.updateFriction(timeStep); - - var hitNormalWorldScaledWithProj = new Vec3(); - var fwd = new Vec3(); - var vel = new Vec3(); - for (i = 0; i < numWheels; i++) { - var wheel = wheelInfos[i]; - //var relpos = new Vec3(); - //wheel.chassisConnectionPointWorld.vsub(chassisBody.position, relpos); - chassisBody.getVelocityAtWorldPoint(wheel.chassisConnectionPointWorld, vel); - - // Hack to get the rotation in the correct direction - var m = 1; - switch(this.indexUpAxis){ - case 1: - m = -1; - break; - } - - if (wheel.isInContact) { - - this.getVehicleAxisWorld(this.indexForwardAxis, fwd); - var proj = fwd.dot(wheel.raycastResult.hitNormalWorld); - wheel.raycastResult.hitNormalWorld.scale(proj, hitNormalWorldScaledWithProj); - - fwd.vsub(hitNormalWorldScaledWithProj, fwd); - - var proj2 = fwd.dot(vel); - wheel.deltaRotation = m * proj2 * timeStep / wheel.radius; - } - - if((wheel.sliding || !wheel.isInContact) && wheel.engineForce !== 0 && wheel.useCustomSlidingRotationalSpeed){ - // Apply custom rotation when accelerating and sliding - wheel.deltaRotation = (wheel.engineForce > 0 ? 1 : -1) * wheel.customSlidingRotationalSpeed * timeStep; - } - - // Lock wheels - if(Math.abs(wheel.brake) > Math.abs(wheel.engineForce)){ - wheel.deltaRotation = 0; - } - - wheel.rotation += wheel.deltaRotation; // Use the old value - wheel.deltaRotation *= 0.99; // damping of rotation when not in contact - } -}; - -RaycastVehicle.prototype.updateSuspension = function(deltaTime) { - var chassisBody = this.chassisBody; - var chassisMass = chassisBody.mass; - var wheelInfos = this.wheelInfos; - var numWheels = wheelInfos.length; - - for (var w_it = 0; w_it < numWheels; w_it++){ - var wheel = wheelInfos[w_it]; - - if (wheel.isInContact){ - var force; - - // Spring - var susp_length = wheel.suspensionRestLength; - var current_length = wheel.suspensionLength; - var length_diff = (susp_length - current_length); - - force = wheel.suspensionStiffness * length_diff * wheel.clippedInvContactDotSuspension; - - // Damper - var projected_rel_vel = wheel.suspensionRelativeVelocity; - var susp_damping; - if (projected_rel_vel < 0) { - susp_damping = wheel.dampingCompression; - } else { - susp_damping = wheel.dampingRelaxation; - } - force -= susp_damping * projected_rel_vel; - - wheel.suspensionForce = force * chassisMass; - if (wheel.suspensionForce < 0) { - wheel.suspensionForce = 0; - } - } else { - wheel.suspensionForce = 0; - } - } -}; - -/** - * Remove the vehicle including its constraints from the world. - * @method removeFromWorld - * @param {World} world - */ -RaycastVehicle.prototype.removeFromWorld = function(world){ - var constraints = this.constraints; - world.remove(this.chassisBody); - world.removeEventListener('preStep', this.preStepCallback); - this.world = null; -}; - -var castRay_rayvector = new Vec3(); -var castRay_target = new Vec3(); -RaycastVehicle.prototype.castRay = function(wheel) { - var rayvector = castRay_rayvector; - var target = castRay_target; - - this.updateWheelTransformWorld(wheel); - var chassisBody = this.chassisBody; - - var depth = -1; - - var raylen = wheel.suspensionRestLength + wheel.radius; - - wheel.directionWorld.scale(raylen, rayvector); - var source = wheel.chassisConnectionPointWorld; - source.vadd(rayvector, target); - var raycastResult = wheel.raycastResult; - - var param = 0; - - raycastResult.reset(); - // Turn off ray collision with the chassis temporarily - var oldState = chassisBody.collisionResponse; - chassisBody.collisionResponse = false; - - // Cast ray against world - this.world.rayTest(source, target, raycastResult); - chassisBody.collisionResponse = oldState; - - var object = raycastResult.body; - - wheel.raycastResult.groundObject = 0; - - if (object) { - depth = raycastResult.distance; - wheel.raycastResult.hitNormalWorld = raycastResult.hitNormalWorld; - wheel.isInContact = true; - - var hitDistance = raycastResult.distance; - wheel.suspensionLength = hitDistance - wheel.radius; - - // clamp on max suspension travel - var minSuspensionLength = wheel.suspensionRestLength - wheel.maxSuspensionTravel; - var maxSuspensionLength = wheel.suspensionRestLength + wheel.maxSuspensionTravel; - if (wheel.suspensionLength < minSuspensionLength) { - wheel.suspensionLength = minSuspensionLength; - } - if (wheel.suspensionLength > maxSuspensionLength) { - wheel.suspensionLength = maxSuspensionLength; - wheel.raycastResult.reset(); - } - - var denominator = wheel.raycastResult.hitNormalWorld.dot(wheel.directionWorld); - - var chassis_velocity_at_contactPoint = new Vec3(); - chassisBody.getVelocityAtWorldPoint(wheel.raycastResult.hitPointWorld, chassis_velocity_at_contactPoint); - - var projVel = wheel.raycastResult.hitNormalWorld.dot( chassis_velocity_at_contactPoint ); - - if (denominator >= -0.1) { - wheel.suspensionRelativeVelocity = 0; - wheel.clippedInvContactDotSuspension = 1 / 0.1; - } else { - var inv = -1 / denominator; - wheel.suspensionRelativeVelocity = projVel * inv; - wheel.clippedInvContactDotSuspension = inv; - } - - } else { - - //put wheel info as in rest position - wheel.suspensionLength = wheel.suspensionRestLength + 0 * wheel.maxSuspensionTravel; - wheel.suspensionRelativeVelocity = 0.0; - wheel.directionWorld.scale(-1, wheel.raycastResult.hitNormalWorld); - wheel.clippedInvContactDotSuspension = 1.0; - } - - return depth; -}; - -RaycastVehicle.prototype.updateWheelTransformWorld = function(wheel){ - wheel.isInContact = false; - var chassisBody = this.chassisBody; - chassisBody.pointToWorldFrame(wheel.chassisConnectionPointLocal, wheel.chassisConnectionPointWorld); - chassisBody.vectorToWorldFrame(wheel.directionLocal, wheel.directionWorld); - chassisBody.vectorToWorldFrame(wheel.axleLocal, wheel.axleWorld); -}; - - -/** - * Update one of the wheel transform. - * Note when rendering wheels: during each step, wheel transforms are updated BEFORE the chassis; ie. their position becomes invalid after the step. Thus when you render wheels, you must update wheel transforms before rendering them. See raycastVehicle demo for an example. - * @method updateWheelTransform - * @param {integer} wheelIndex The wheel index to update. - */ -RaycastVehicle.prototype.updateWheelTransform = function(wheelIndex){ - var up = tmpVec4; - var right = tmpVec5; - var fwd = tmpVec6; - - var wheel = this.wheelInfos[wheelIndex]; - this.updateWheelTransformWorld(wheel); - - wheel.directionLocal.scale(-1, up); - right.copy(wheel.axleLocal); - up.cross(right, fwd); - fwd.normalize(); - right.normalize(); - - // Rotate around steering over the wheelAxle - var steering = wheel.steering; - var steeringOrn = new Quaternion(); - steeringOrn.setFromAxisAngle(up, steering); - - var rotatingOrn = new Quaternion(); - rotatingOrn.setFromAxisAngle(right, wheel.rotation); - - // World rotation of the wheel - var q = wheel.worldTransform.quaternion; - this.chassisBody.quaternion.mult(steeringOrn, q); - q.mult(rotatingOrn, q); - - q.normalize(); - - // world position of the wheel - var p = wheel.worldTransform.position; - p.copy(wheel.directionWorld); - p.scale(wheel.suspensionLength, p); - p.vadd(wheel.chassisConnectionPointWorld, p); -}; - -var directions = [ - new Vec3(1, 0, 0), - new Vec3(0, 1, 0), - new Vec3(0, 0, 1) -]; - -/** - * Get the world transform of one of the wheels - * @method getWheelTransformWorld - * @param {integer} wheelIndex - * @return {Transform} - */ -RaycastVehicle.prototype.getWheelTransformWorld = function(wheelIndex) { - return this.wheelInfos[wheelIndex].worldTransform; -}; - - -var updateFriction_surfNormalWS_scaled_proj = new Vec3(); -var updateFriction_axle = []; -var updateFriction_forwardWS = []; -var sideFrictionStiffness2 = 1; -RaycastVehicle.prototype.updateFriction = function(timeStep) { - var surfNormalWS_scaled_proj = updateFriction_surfNormalWS_scaled_proj; - - //calculate the impulse, so that the wheels don't move sidewards - var wheelInfos = this.wheelInfos; - var numWheels = wheelInfos.length; - var chassisBody = this.chassisBody; - var forwardWS = updateFriction_forwardWS; - var axle = updateFriction_axle; - - var numWheelsOnGround = 0; - - for (var i = 0; i < numWheels; i++) { - var wheel = wheelInfos[i]; - - var groundObject = wheel.raycastResult.body; - if (groundObject){ - numWheelsOnGround++; - } - - wheel.sideImpulse = 0; - wheel.forwardImpulse = 0; - if(!forwardWS[i]){ - forwardWS[i] = new Vec3(); - } - if(!axle[i]){ - axle[i] = new Vec3(); - } - } - - for (var i = 0; i < numWheels; i++){ - var wheel = wheelInfos[i]; - - var groundObject = wheel.raycastResult.body; - - if (groundObject) { - var axlei = axle[i]; - var wheelTrans = this.getWheelTransformWorld(i); - - // Get world axle - wheelTrans.vectorToWorldFrame(directions[this.indexRightAxis], axlei); - - var surfNormalWS = wheel.raycastResult.hitNormalWorld; - var proj = axlei.dot(surfNormalWS); - surfNormalWS.scale(proj, surfNormalWS_scaled_proj); - axlei.vsub(surfNormalWS_scaled_proj, axlei); - axlei.normalize(); - - surfNormalWS.cross(axlei, forwardWS[i]); - forwardWS[i].normalize(); - - wheel.sideImpulse = resolveSingleBilateral( - chassisBody, - wheel.raycastResult.hitPointWorld, - groundObject, - wheel.raycastResult.hitPointWorld, - axlei - ); - - wheel.sideImpulse *= sideFrictionStiffness2; - } - } - - var sideFactor = 1; - var fwdFactor = 0.5; - - this.sliding = false; - for (var i = 0; i < numWheels; i++) { - var wheel = wheelInfos[i]; - var groundObject = wheel.raycastResult.body; - - var rollingFriction = 0; - - wheel.slipInfo = 1; - if (groundObject) { - var defaultRollingFrictionImpulse = 0; - var maxImpulse = wheel.brake ? wheel.brake : defaultRollingFrictionImpulse; - - // btWheelContactPoint contactPt(chassisBody,groundObject,wheelInfraycastInfo.hitPointWorld,forwardWS[wheel],maxImpulse); - // rollingFriction = calcRollingFriction(contactPt); - rollingFriction = calcRollingFriction(chassisBody, groundObject, wheel.raycastResult.hitPointWorld, forwardWS[i], maxImpulse); - - rollingFriction += wheel.engineForce * timeStep; - - // rollingFriction = 0; - var factor = maxImpulse / rollingFriction; - wheel.slipInfo *= factor; - } - - //switch between active rolling (throttle), braking and non-active rolling friction (nthrottle/break) - - wheel.forwardImpulse = 0; - wheel.skidInfo = 1; - - if (groundObject) { - wheel.skidInfo = 1; - - var maximp = wheel.suspensionForce * timeStep * wheel.frictionSlip; - var maximpSide = maximp; - - var maximpSquared = maximp * maximpSide; - - wheel.forwardImpulse = rollingFriction;//wheelInfo.engineForce* timeStep; - - var x = wheel.forwardImpulse * fwdFactor; - var y = wheel.sideImpulse * sideFactor; - - var impulseSquared = x * x + y * y; - - wheel.sliding = false; - if (impulseSquared > maximpSquared) { - this.sliding = true; - wheel.sliding = true; - - var factor = maximp / Math.sqrt(impulseSquared); - - wheel.skidInfo *= factor; - } - } - } - - if (this.sliding) { - for (var i = 0; i < numWheels; i++) { - var wheel = wheelInfos[i]; - if (wheel.sideImpulse !== 0) { - if (wheel.skidInfo < 1){ - wheel.forwardImpulse *= wheel.skidInfo; - wheel.sideImpulse *= wheel.skidInfo; - } - } - } - } - - // apply the impulses - for (var i = 0; i < numWheels; i++) { - var wheel = wheelInfos[i]; - - var rel_pos = new Vec3(); - //wheel.raycastResult.hitPointWorld.vsub(chassisBody.position, rel_pos); - // cannons applyimpulse is using world coord for the position - rel_pos.copy(wheel.raycastResult.hitPointWorld); - - if (wheel.forwardImpulse !== 0) { - var impulse = new Vec3(); - forwardWS[i].scale(wheel.forwardImpulse, impulse); - chassisBody.applyImpulse(impulse, rel_pos); - } - - if (wheel.sideImpulse !== 0){ - var groundObject = wheel.raycastResult.body; - - var rel_pos2 = new Vec3(); - //wheel.raycastResult.hitPointWorld.vsub(groundObject.position, rel_pos2); - rel_pos2.copy(wheel.raycastResult.hitPointWorld); - var sideImp = new Vec3(); - axle[i].scale(wheel.sideImpulse, sideImp); - - // Scale the relative position in the up direction with rollInfluence. - // If rollInfluence is 1, the impulse will be applied on the hitPoint (easy to roll over), if it is zero it will be applied in the same plane as the center of mass (not easy to roll over). - chassisBody.pointToLocalFrame(rel_pos, rel_pos); - rel_pos['xyz'[this.indexUpAxis]] *= wheel.rollInfluence; - chassisBody.pointToWorldFrame(rel_pos, rel_pos); - chassisBody.applyImpulse(sideImp, rel_pos); - - //apply friction impulse on the ground - sideImp.scale(-1, sideImp); - groundObject.applyImpulse(sideImp, rel_pos2); - } - } -}; - -var calcRollingFriction_vel1 = new Vec3(); -var calcRollingFriction_vel2 = new Vec3(); -var calcRollingFriction_vel = new Vec3(); - -function calcRollingFriction(body0, body1, frictionPosWorld, frictionDirectionWorld, maxImpulse) { - var j1 = 0; - var contactPosWorld = frictionPosWorld; - - // var rel_pos1 = new Vec3(); - // var rel_pos2 = new Vec3(); - var vel1 = calcRollingFriction_vel1; - var vel2 = calcRollingFriction_vel2; - var vel = calcRollingFriction_vel; - // contactPosWorld.vsub(body0.position, rel_pos1); - // contactPosWorld.vsub(body1.position, rel_pos2); - - body0.getVelocityAtWorldPoint(contactPosWorld, vel1); - body1.getVelocityAtWorldPoint(contactPosWorld, vel2); - vel1.vsub(vel2, vel); - - var vrel = frictionDirectionWorld.dot(vel); - - var denom0 = computeImpulseDenominator(body0, frictionPosWorld, frictionDirectionWorld); - var denom1 = computeImpulseDenominator(body1, frictionPosWorld, frictionDirectionWorld); - var relaxation = 1; - var jacDiagABInv = relaxation / (denom0 + denom1); - - // calculate j that moves us to zero relative velocity - j1 = -vrel * jacDiagABInv; - - if (maxImpulse < j1) { - j1 = maxImpulse; - } - if (j1 < -maxImpulse) { - j1 = -maxImpulse; - } - - return j1; -} - -var computeImpulseDenominator_r0 = new Vec3(); -var computeImpulseDenominator_c0 = new Vec3(); -var computeImpulseDenominator_vec = new Vec3(); -var computeImpulseDenominator_m = new Vec3(); -function computeImpulseDenominator(body, pos, normal) { - var r0 = computeImpulseDenominator_r0; - var c0 = computeImpulseDenominator_c0; - var vec = computeImpulseDenominator_vec; - var m = computeImpulseDenominator_m; - - pos.vsub(body.position, r0); - r0.cross(normal, c0); - body.invInertiaWorld.vmult(c0, m); - m.cross(r0, vec); - - return body.invMass + normal.dot(vec); -} - - -var resolveSingleBilateral_vel1 = new Vec3(); -var resolveSingleBilateral_vel2 = new Vec3(); -var resolveSingleBilateral_vel = new Vec3(); - -//bilateral constraint between two dynamic objects -function resolveSingleBilateral(body1, pos1, body2, pos2, normal, impulse){ - var normalLenSqr = normal.norm2(); - if (normalLenSqr > 1.1){ - return 0; // no impulse - } - // var rel_pos1 = new Vec3(); - // var rel_pos2 = new Vec3(); - // pos1.vsub(body1.position, rel_pos1); - // pos2.vsub(body2.position, rel_pos2); - - var vel1 = resolveSingleBilateral_vel1; - var vel2 = resolveSingleBilateral_vel2; - var vel = resolveSingleBilateral_vel; - body1.getVelocityAtWorldPoint(pos1, vel1); - body2.getVelocityAtWorldPoint(pos2, vel2); - - vel1.vsub(vel2, vel); - - var rel_vel = normal.dot(vel); - - var contactDamping = 0.2; - var massTerm = 1 / (body1.invMass + body2.invMass); - var impulse = - contactDamping * rel_vel * massTerm; - - return impulse; -} -},{"../collision/Ray":9,"../collision/RaycastResult":10,"../math/Quaternion":28,"../math/Vec3":30,"../objects/WheelInfo":36,"./Body":31}],33:[function(_dereq_,module,exports){ -var Body = _dereq_('./Body'); -var Sphere = _dereq_('../shapes/Sphere'); -var Box = _dereq_('../shapes/Box'); -var Vec3 = _dereq_('../math/Vec3'); -var HingeConstraint = _dereq_('../constraints/HingeConstraint'); - -module.exports = RigidVehicle; - -/** - * Simple vehicle helper class with spherical rigid body wheels. - * @class RigidVehicle - * @constructor - * @param {Body} [options.chassisBody] - */ -function RigidVehicle(options){ - this.wheelBodies = []; - - /** - * @property coordinateSystem - * @type {Vec3} - */ - this.coordinateSystem = typeof(options.coordinateSystem)==='undefined' ? new Vec3(1, 2, 3) : options.coordinateSystem.clone(); - - /** - * @property {Body} chassisBody - */ - this.chassisBody = options.chassisBody; - - if(!this.chassisBody){ - // No chassis body given. Create it! - var chassisShape = new Box(new Vec3(5, 2, 0.5)); - this.chassisBody = new Body(1, chassisShape); - } - - /** - * @property constraints - * @type {Array} - */ - this.constraints = []; - - this.wheelAxes = []; - this.wheelForces = []; -} - -/** - * Add a wheel - * @method addWheel - * @param {object} options - * @param {boolean} [options.isFrontWheel] - * @param {Vec3} [options.position] Position of the wheel, locally in the chassis body. - * @param {Vec3} [options.direction] Slide direction of the wheel along the suspension. - * @param {Vec3} [options.axis] Axis of rotation of the wheel, locally defined in the chassis. - * @param {Body} [options.body] The wheel body. - */ -RigidVehicle.prototype.addWheel = function(options){ - options = options || {}; - var wheelBody = options.body; - if(!wheelBody){ - wheelBody = new Body(1, new Sphere(1.2)); - } - this.wheelBodies.push(wheelBody); - this.wheelForces.push(0); - - // Position constrain wheels - var zero = new Vec3(); - var position = typeof(options.position) !== 'undefined' ? options.position.clone() : new Vec3(); - - // Set position locally to the chassis - var worldPosition = new Vec3(); - this.chassisBody.pointToWorldFrame(position, worldPosition); - wheelBody.position.set(worldPosition.x, worldPosition.y, worldPosition.z); - - // Constrain wheel - var axis = typeof(options.axis) !== 'undefined' ? options.axis.clone() : new Vec3(0, 1, 0); - this.wheelAxes.push(axis); - - var hingeConstraint = new HingeConstraint(this.chassisBody, wheelBody, { - pivotA: position, - axisA: axis, - pivotB: Vec3.ZERO, - axisB: axis, - collideConnected: false - }); - this.constraints.push(hingeConstraint); - - return this.wheelBodies.length - 1; -}; - -/** - * Set the steering value of a wheel. - * @method setSteeringValue - * @param {number} value - * @param {integer} wheelIndex - * @todo check coordinateSystem - */ -RigidVehicle.prototype.setSteeringValue = function(value, wheelIndex){ - // Set angle of the hinge axis - var axis = this.wheelAxes[wheelIndex]; - - var c = Math.cos(value), - s = Math.sin(value), - x = axis.x, - y = axis.y; - this.constraints[wheelIndex].axisA.set( - c*x -s*y, - s*x +c*y, - 0 - ); -}; - -/** - * Set the target rotational speed of the hinge constraint. - * @method setMotorSpeed - * @param {number} value - * @param {integer} wheelIndex - */ -RigidVehicle.prototype.setMotorSpeed = function(value, wheelIndex){ - var hingeConstraint = this.constraints[wheelIndex]; - hingeConstraint.enableMotor(); - hingeConstraint.motorTargetVelocity = value; -}; - -/** - * Set the target rotational speed of the hinge constraint. - * @method disableMotor - * @param {number} value - * @param {integer} wheelIndex - */ -RigidVehicle.prototype.disableMotor = function(wheelIndex){ - var hingeConstraint = this.constraints[wheelIndex]; - hingeConstraint.disableMotor(); -}; - -var torque = new Vec3(); - -/** - * Set the wheel force to apply on one of the wheels each time step - * @method setWheelForce - * @param {number} value - * @param {integer} wheelIndex - */ -RigidVehicle.prototype.setWheelForce = function(value, wheelIndex){ - this.wheelForces[wheelIndex] = value; -}; - -/** - * Apply a torque on one of the wheels. - * @method applyWheelForce - * @param {number} value - * @param {integer} wheelIndex - */ -RigidVehicle.prototype.applyWheelForce = function(value, wheelIndex){ - var axis = this.wheelAxes[wheelIndex]; - var wheelBody = this.wheelBodies[wheelIndex]; - var bodyTorque = wheelBody.torque; - - axis.scale(value, torque); - wheelBody.vectorToWorldFrame(torque, torque); - bodyTorque.vadd(torque, bodyTorque); -}; - -/** - * Add the vehicle including its constraints to the world. - * @method addToWorld - * @param {World} world - */ -RigidVehicle.prototype.addToWorld = function(world){ - var constraints = this.constraints; - var bodies = this.wheelBodies.concat([this.chassisBody]); - - for (var i = 0; i < bodies.length; i++) { - world.add(bodies[i]); - } - - for (var i = 0; i < constraints.length; i++) { - world.addConstraint(constraints[i]); - } - - world.addEventListener('preStep', this._update.bind(this)); -}; - -RigidVehicle.prototype._update = function(){ - var wheelForces = this.wheelForces; - for (var i = 0; i < wheelForces.length; i++) { - this.applyWheelForce(wheelForces[i], i); - } -}; - -/** - * Remove the vehicle including its constraints from the world. - * @method removeFromWorld - * @param {World} world - */ -RigidVehicle.prototype.removeFromWorld = function(world){ - var constraints = this.constraints; - var bodies = this.wheelBodies.concat([this.chassisBody]); - - for (var i = 0; i < bodies.length; i++) { - world.remove(bodies[i]); - } - - for (var i = 0; i < constraints.length; i++) { - world.removeConstraint(constraints[i]); - } -}; - -var worldAxis = new Vec3(); - -/** - * Get current rotational velocity of a wheel - * @method getWheelSpeed - * @param {integer} wheelIndex - */ -RigidVehicle.prototype.getWheelSpeed = function(wheelIndex){ - var axis = this.wheelAxes[wheelIndex]; - var wheelBody = this.wheelBodies[wheelIndex]; - var w = wheelBody.angularVelocity; - this.chassisBody.vectorToWorldFrame(axis, worldAxis); - return w.dot(worldAxis); -}; - -},{"../constraints/HingeConstraint":15,"../math/Vec3":30,"../shapes/Box":37,"../shapes/Sphere":44,"./Body":31}],34:[function(_dereq_,module,exports){ -module.exports = SPHSystem; - -var Shape = _dereq_('../shapes/Shape'); -var Vec3 = _dereq_('../math/Vec3'); -var Quaternion = _dereq_('../math/Quaternion'); -var Particle = _dereq_('../shapes/Particle'); -var Body = _dereq_('../objects/Body'); -var Material = _dereq_('../material/Material'); - -/** - * Smoothed-particle hydrodynamics system - * @class SPHSystem - * @constructor - */ -function SPHSystem(){ - this.particles = []; - - /** - * Density of the system (kg/m3). - * @property {number} density - */ - this.density = 1; - - /** - * Distance below which two particles are considered to be neighbors. - * It should be adjusted so there are about 15-20 neighbor particles within this radius. - * @property {number} smoothingRadius - */ - this.smoothingRadius = 1; - this.speedOfSound = 1; - - /** - * Viscosity of the system. - * @property {number} viscosity - */ - this.viscosity = 0.01; - this.eps = 0.000001; - - // Stuff Computed per particle - this.pressures = []; - this.densities = []; - this.neighbors = []; -} - -/** - * Add a particle to the system. - * @method add - * @param {Body} particle - */ -SPHSystem.prototype.add = function(particle){ - this.particles.push(particle); - if(this.neighbors.length < this.particles.length){ - this.neighbors.push([]); - } -}; - -/** - * Remove a particle from the system. - * @method remove - * @param {Body} particle - */ -SPHSystem.prototype.remove = function(particle){ - var idx = this.particles.indexOf(particle); - if(idx !== -1){ - this.particles.splice(idx,1); - if(this.neighbors.length > this.particles.length){ - this.neighbors.pop(); - } - } -}; - -/** - * Get neighbors within smoothing volume, save in the array neighbors - * @method getNeighbors - * @param {Body} particle - * @param {Array} neighbors - */ -var SPHSystem_getNeighbors_dist = new Vec3(); -SPHSystem.prototype.getNeighbors = function(particle,neighbors){ - var N = this.particles.length, - id = particle.id, - R2 = this.smoothingRadius * this.smoothingRadius, - dist = SPHSystem_getNeighbors_dist; - for(var i=0; i!==N; i++){ - var p = this.particles[i]; - p.position.vsub(particle.position,dist); - if(id!==p.id && dist.norm2() < R2){ - neighbors.push(p); - } - } -}; - -// Temp vectors for calculation -var SPHSystem_update_dist = new Vec3(), - SPHSystem_update_a_pressure = new Vec3(), - SPHSystem_update_a_visc = new Vec3(), - SPHSystem_update_gradW = new Vec3(), - SPHSystem_update_r_vec = new Vec3(), - SPHSystem_update_u = new Vec3(); // Relative velocity -SPHSystem.prototype.update = function(){ - var N = this.particles.length, - dist = SPHSystem_update_dist, - cs = this.speedOfSound, - eps = this.eps; - - for(var i=0; i!==N; i++){ - var p = this.particles[i]; // Current particle - var neighbors = this.neighbors[i]; - - // Get neighbors - neighbors.length = 0; - this.getNeighbors(p,neighbors); - neighbors.push(this.particles[i]); // Add current too - var numNeighbors = neighbors.length; - - // Accumulate density for the particle - var sum = 0.0; - for(var j=0; j!==numNeighbors; j++){ - - //printf("Current particle has position %f %f %f\n",objects[id].pos.x(),objects[id].pos.y(),objects[id].pos.z()); - p.position.vsub(neighbors[j].position, dist); - var len = dist.norm(); - - var weight = this.w(len); - sum += neighbors[j].mass * weight; - } - - // Save - this.densities[i] = sum; - this.pressures[i] = cs * cs * (this.densities[i] - this.density); - } - - // Add forces - - // Sum to these accelerations - var a_pressure= SPHSystem_update_a_pressure; - var a_visc = SPHSystem_update_a_visc; - var gradW = SPHSystem_update_gradW; - var r_vec = SPHSystem_update_r_vec; - var u = SPHSystem_update_u; - - for(var i=0; i!==N; i++){ - - var particle = this.particles[i]; - - a_pressure.set(0,0,0); - a_visc.set(0,0,0); - - // Init vars - var Pij; - var nabla; - var Vij; - - // Sum up for all other neighbors - var neighbors = this.neighbors[i]; - var numNeighbors = neighbors.length; - - //printf("Neighbors: "); - for(var j=0; j!==numNeighbors; j++){ - - var neighbor = neighbors[j]; - //printf("%d ",nj); - - // Get r once for all.. - particle.position.vsub(neighbor.position,r_vec); - var r = r_vec.norm(); - - // Pressure contribution - Pij = -neighbor.mass * (this.pressures[i] / (this.densities[i]*this.densities[i] + eps) + this.pressures[j] / (this.densities[j]*this.densities[j] + eps)); - this.gradw(r_vec, gradW); - // Add to pressure acceleration - gradW.mult(Pij , gradW); - a_pressure.vadd(gradW, a_pressure); - - // Viscosity contribution - neighbor.velocity.vsub(particle.velocity, u); - u.mult( 1.0 / (0.0001+this.densities[i] * this.densities[j]) * this.viscosity * neighbor.mass , u ); - nabla = this.nablaw(r); - u.mult(nabla,u); - // Add to viscosity acceleration - a_visc.vadd( u, a_visc ); - } - - // Calculate force - a_visc.mult(particle.mass, a_visc); - a_pressure.mult(particle.mass, a_pressure); - - // Add force to particles - particle.force.vadd(a_visc, particle.force); - particle.force.vadd(a_pressure, particle.force); - } -}; - -// Calculate the weight using the W(r) weightfunction -SPHSystem.prototype.w = function(r){ - // 315 - var h = this.smoothingRadius; - return 315.0/(64.0*Math.PI*Math.pow(h,9)) * Math.pow(h*h-r*r,3); -}; - -// calculate gradient of the weight function -SPHSystem.prototype.gradw = function(rVec,resultVec){ - var r = rVec.norm(), - h = this.smoothingRadius; - rVec.mult(945.0/(32.0*Math.PI*Math.pow(h,9)) * Math.pow((h*h-r*r),2) , resultVec); -}; - -// Calculate nabla(W) -SPHSystem.prototype.nablaw = function(r){ - var h = this.smoothingRadius; - var nabla = 945.0/(32.0*Math.PI*Math.pow(h,9)) * (h*h-r*r)*(7*r*r - 3*h*h); - return nabla; -}; - -},{"../material/Material":25,"../math/Quaternion":28,"../math/Vec3":30,"../objects/Body":31,"../shapes/Particle":41,"../shapes/Shape":43}],35:[function(_dereq_,module,exports){ -var Vec3 = _dereq_('../math/Vec3'); - -module.exports = Spring; - -/** - * A spring, connecting two bodies. - * - * @class Spring - * @constructor - * @param {Body} bodyA - * @param {Body} bodyB - * @param {Object} [options] - * @param {number} [options.restLength] A number > 0. Default: 1 - * @param {number} [options.stiffness] A number >= 0. Default: 100 - * @param {number} [options.damping] A number >= 0. Default: 1 - * @param {Vec3} [options.worldAnchorA] Where to hook the spring to body A, in world coordinates. - * @param {Vec3} [options.worldAnchorB] - * @param {Vec3} [options.localAnchorA] Where to hook the spring to body A, in local body coordinates. - * @param {Vec3} [options.localAnchorB] - */ -function Spring(bodyA,bodyB,options){ - options = options || {}; - - /** - * Rest length of the spring. - * @property restLength - * @type {number} - */ - this.restLength = typeof(options.restLength) === "number" ? options.restLength : 1; - - /** - * Stiffness of the spring. - * @property stiffness - * @type {number} - */ - this.stiffness = options.stiffness || 100; - - /** - * Damping of the spring. - * @property damping - * @type {number} - */ - this.damping = options.damping || 1; - - /** - * First connected body. - * @property bodyA - * @type {Body} - */ - this.bodyA = bodyA; - - /** - * Second connected body. - * @property bodyB - * @type {Body} - */ - this.bodyB = bodyB; - - /** - * Anchor for bodyA in local bodyA coordinates. - * @property localAnchorA - * @type {Vec3} - */ - this.localAnchorA = new Vec3(); - - /** - * Anchor for bodyB in local bodyB coordinates. - * @property localAnchorB - * @type {Vec3} - */ - this.localAnchorB = new Vec3(); - - if(options.localAnchorA){ - this.localAnchorA.copy(options.localAnchorA); - } - if(options.localAnchorB){ - this.localAnchorB.copy(options.localAnchorB); - } - if(options.worldAnchorA){ - this.setWorldAnchorA(options.worldAnchorA); - } - if(options.worldAnchorB){ - this.setWorldAnchorB(options.worldAnchorB); - } -} - -/** - * Set the anchor point on body A, using world coordinates. - * @method setWorldAnchorA - * @param {Vec3} worldAnchorA - */ -Spring.prototype.setWorldAnchorA = function(worldAnchorA){ - this.bodyA.pointToLocalFrame(worldAnchorA,this.localAnchorA); -}; - -/** - * Set the anchor point on body B, using world coordinates. - * @method setWorldAnchorB - * @param {Vec3} worldAnchorB - */ -Spring.prototype.setWorldAnchorB = function(worldAnchorB){ - this.bodyB.pointToLocalFrame(worldAnchorB,this.localAnchorB); -}; - -/** - * Get the anchor point on body A, in world coordinates. - * @method getWorldAnchorA - * @param {Vec3} result The vector to store the result in. - */ -Spring.prototype.getWorldAnchorA = function(result){ - this.bodyA.pointToWorldFrame(this.localAnchorA,result); -}; - -/** - * Get the anchor point on body B, in world coordinates. - * @method getWorldAnchorB - * @param {Vec3} result The vector to store the result in. - */ -Spring.prototype.getWorldAnchorB = function(result){ - this.bodyB.pointToWorldFrame(this.localAnchorB,result); -}; - -var applyForce_r = new Vec3(), - applyForce_r_unit = new Vec3(), - applyForce_u = new Vec3(), - applyForce_f = new Vec3(), - applyForce_worldAnchorA = new Vec3(), - applyForce_worldAnchorB = new Vec3(), - applyForce_ri = new Vec3(), - applyForce_rj = new Vec3(), - applyForce_ri_x_f = new Vec3(), - applyForce_rj_x_f = new Vec3(), - applyForce_tmp = new Vec3(); - -/** - * Apply the spring force to the connected bodies. - * @method applyForce - */ -Spring.prototype.applyForce = function(){ - var k = this.stiffness, - d = this.damping, - l = this.restLength, - bodyA = this.bodyA, - bodyB = this.bodyB, - r = applyForce_r, - r_unit = applyForce_r_unit, - u = applyForce_u, - f = applyForce_f, - tmp = applyForce_tmp; - - var worldAnchorA = applyForce_worldAnchorA, - worldAnchorB = applyForce_worldAnchorB, - ri = applyForce_ri, - rj = applyForce_rj, - ri_x_f = applyForce_ri_x_f, - rj_x_f = applyForce_rj_x_f; - - // Get world anchors - this.getWorldAnchorA(worldAnchorA); - this.getWorldAnchorB(worldAnchorB); - - // Get offset points - worldAnchorA.vsub(bodyA.position,ri); - worldAnchorB.vsub(bodyB.position,rj); - - // Compute distance vector between world anchor points - worldAnchorB.vsub(worldAnchorA,r); - var rlen = r.norm(); - r_unit.copy(r); - r_unit.normalize(); - - // Compute relative velocity of the anchor points, u - bodyB.velocity.vsub(bodyA.velocity,u); - // Add rotational velocity - - bodyB.angularVelocity.cross(rj,tmp); - u.vadd(tmp,u); - bodyA.angularVelocity.cross(ri,tmp); - u.vsub(tmp,u); - - // F = - k * ( x - L ) - D * ( u ) - r_unit.mult(-k*(rlen-l) - d*u.dot(r_unit), f); - - // Add forces to bodies - bodyA.force.vsub(f,bodyA.force); - bodyB.force.vadd(f,bodyB.force); - - // Angular force - ri.cross(f,ri_x_f); - rj.cross(f,rj_x_f); - bodyA.torque.vsub(ri_x_f,bodyA.torque); - bodyB.torque.vadd(rj_x_f,bodyB.torque); -}; - -},{"../math/Vec3":30}],36:[function(_dereq_,module,exports){ -var Vec3 = _dereq_('../math/Vec3'); -var Transform = _dereq_('../math/Transform'); -var RaycastResult = _dereq_('../collision/RaycastResult'); -var Utils = _dereq_('../utils/Utils'); - -module.exports = WheelInfo; - -/** - * @class WheelInfo - * @constructor - * @param {Object} [options] - * - * @param {Vec3} [options.chassisConnectionPointLocal] - * @param {Vec3} [options.chassisConnectionPointWorld] - * @param {Vec3} [options.directionLocal] - * @param {Vec3} [options.directionWorld] - * @param {Vec3} [options.axleLocal] - * @param {Vec3} [options.axleWorld] - * @param {number} [options.suspensionRestLength=1] - * @param {number} [options.suspensionMaxLength=2] - * @param {number} [options.radius=1] - * @param {number} [options.suspensionStiffness=100] - * @param {number} [options.dampingCompression=10] - * @param {number} [options.dampingRelaxation=10] - * @param {number} [options.frictionSlip=10000] - * @param {number} [options.steering=0] - * @param {number} [options.rotation=0] - * @param {number} [options.deltaRotation=0] - * @param {number} [options.rollInfluence=0.01] - * @param {number} [options.maxSuspensionForce] - * @param {boolean} [options.isFrontWheel=true] - * @param {number} [options.clippedInvContactDotSuspension=1] - * @param {number} [options.suspensionRelativeVelocity=0] - * @param {number} [options.suspensionForce=0] - * @param {number} [options.skidInfo=0] - * @param {number} [options.suspensionLength=0] - * @param {number} [options.maxSuspensionTravel=1] - * @param {boolean} [options.useCustomSlidingRotationalSpeed=false] - * @param {number} [options.customSlidingRotationalSpeed=-0.1] - */ -function WheelInfo(options){ - options = Utils.defaults(options, { - chassisConnectionPointLocal: new Vec3(), - chassisConnectionPointWorld: new Vec3(), - directionLocal: new Vec3(), - directionWorld: new Vec3(), - axleLocal: new Vec3(), - axleWorld: new Vec3(), - suspensionRestLength: 1, - suspensionMaxLength: 2, - radius: 1, - suspensionStiffness: 100, - dampingCompression: 10, - dampingRelaxation: 10, - frictionSlip: 10000, - steering: 0, - rotation: 0, - deltaRotation: 0, - rollInfluence: 0.01, - maxSuspensionForce: Number.MAX_VALUE, - isFrontWheel: true, - clippedInvContactDotSuspension: 1, - suspensionRelativeVelocity: 0, - suspensionForce: 0, - skidInfo: 0, - suspensionLength: 0, - maxSuspensionTravel: 1, - useCustomSlidingRotationalSpeed: false, - customSlidingRotationalSpeed: -0.1 - }); - - /** - * Max travel distance of the suspension, in meters. - * @property {number} maxSuspensionTravel - */ - this.maxSuspensionTravel = options.maxSuspensionTravel; - - /** - * Speed to apply to the wheel rotation when the wheel is sliding. - * @property {number} customSlidingRotationalSpeed - */ - this.customSlidingRotationalSpeed = options.customSlidingRotationalSpeed; - - /** - * If the customSlidingRotationalSpeed should be used. - * @property {Boolean} useCustomSlidingRotationalSpeed - */ - this.useCustomSlidingRotationalSpeed = options.useCustomSlidingRotationalSpeed; - - /** - * @property {Boolean} sliding - */ - this.sliding = false; - - /** - * Connection point, defined locally in the chassis body frame. - * @property {Vec3} chassisConnectionPointLocal - */ - this.chassisConnectionPointLocal = options.chassisConnectionPointLocal.clone(); - - /** - * @property {Vec3} chassisConnectionPointWorld - */ - this.chassisConnectionPointWorld = options.chassisConnectionPointWorld.clone(); - - /** - * @property {Vec3} directionLocal - */ - this.directionLocal = options.directionLocal.clone(); - - /** - * @property {Vec3} directionWorld - */ - this.directionWorld = options.directionWorld.clone(); - - /** - * @property {Vec3} axleLocal - */ - this.axleLocal = options.axleLocal.clone(); - - /** - * @property {Vec3} axleWorld - */ - this.axleWorld = options.axleWorld.clone(); - - /** - * @property {number} suspensionRestLength - */ - this.suspensionRestLength = options.suspensionRestLength; - - /** - * @property {number} suspensionMaxLength - */ - this.suspensionMaxLength = options.suspensionMaxLength; - - /** - * @property {number} radius - */ - this.radius = options.radius; - - /** - * @property {number} suspensionStiffness - */ - this.suspensionStiffness = options.suspensionStiffness; - - /** - * @property {number} dampingCompression - */ - this.dampingCompression = options.dampingCompression; - - /** - * @property {number} dampingRelaxation - */ - this.dampingRelaxation = options.dampingRelaxation; - - /** - * @property {number} frictionSlip - */ - this.frictionSlip = options.frictionSlip; - - /** - * @property {number} steering - */ - this.steering = 0; - - /** - * Rotation value, in radians. - * @property {number} rotation - */ - this.rotation = 0; - - /** - * @property {number} deltaRotation - */ - this.deltaRotation = 0; - - /** - * @property {number} rollInfluence - */ - this.rollInfluence = options.rollInfluence; - - /** - * @property {number} maxSuspensionForce - */ - this.maxSuspensionForce = options.maxSuspensionForce; - - /** - * @property {number} engineForce - */ - this.engineForce = 0; - - /** - * @property {number} brake - */ - this.brake = 0; - - /** - * @property {number} isFrontWheel - */ - this.isFrontWheel = options.isFrontWheel; - - /** - * @property {number} clippedInvContactDotSuspension - */ - this.clippedInvContactDotSuspension = 1; - - /** - * @property {number} suspensionRelativeVelocity - */ - this.suspensionRelativeVelocity = 0; - - /** - * @property {number} suspensionForce - */ - this.suspensionForce = 0; - - /** - * @property {number} skidInfo - */ - this.skidInfo = 0; - - /** - * @property {number} suspensionLength - */ - this.suspensionLength = 0; - - /** - * @property {number} sideImpulse - */ - this.sideImpulse = 0; - - /** - * @property {number} forwardImpulse - */ - this.forwardImpulse = 0; - - /** - * The result from raycasting - * @property {RaycastResult} raycastResult - */ - this.raycastResult = new RaycastResult(); - - /** - * Wheel world transform - * @property {Transform} worldTransform - */ - this.worldTransform = new Transform(); - - /** - * @property {boolean} isInContact - */ - this.isInContact = false; -} - -var chassis_velocity_at_contactPoint = new Vec3(); -var relpos = new Vec3(); -var chassis_velocity_at_contactPoint = new Vec3(); -WheelInfo.prototype.updateWheel = function(chassis){ - var raycastResult = this.raycastResult; - - if (this.isInContact){ - var project= raycastResult.hitNormalWorld.dot(raycastResult.directionWorld); - raycastResult.hitPointWorld.vsub(chassis.position, relpos); - chassis.getVelocityAtWorldPoint(relpos, chassis_velocity_at_contactPoint); - var projVel = raycastResult.hitNormalWorld.dot( chassis_velocity_at_contactPoint ); - if (project >= -0.1) { - this.suspensionRelativeVelocity = 0.0; - this.clippedInvContactDotSuspension = 1.0 / 0.1; - } else { - var inv = -1 / project; - this.suspensionRelativeVelocity = projVel * inv; - this.clippedInvContactDotSuspension = inv; - } - - } else { - // Not in contact : position wheel in a nice (rest length) position - raycastResult.suspensionLength = this.suspensionRestLength; - this.suspensionRelativeVelocity = 0.0; - raycastResult.directionWorld.scale(-1, raycastResult.hitNormalWorld); - this.clippedInvContactDotSuspension = 1.0; - } -}; -},{"../collision/RaycastResult":10,"../math/Transform":29,"../math/Vec3":30,"../utils/Utils":53}],37:[function(_dereq_,module,exports){ -module.exports = Box; - -var Shape = _dereq_('./Shape'); -var Vec3 = _dereq_('../math/Vec3'); -var ConvexPolyhedron = _dereq_('./ConvexPolyhedron'); - -/** - * A 3d box shape. - * @class Box - * @constructor - * @param {Vec3} halfExtents - * @author schteppe - * @extends Shape - */ -function Box(halfExtents){ - Shape.call(this); - - this.type = Shape.types.BOX; - - /** - * @property halfExtents - * @type {Vec3} - */ - this.halfExtents = halfExtents; - - /** - * Used by the contact generator to make contacts with other convex polyhedra for example - * @property convexPolyhedronRepresentation - * @type {ConvexPolyhedron} - */ - this.convexPolyhedronRepresentation = null; - - this.updateConvexPolyhedronRepresentation(); - this.updateBoundingSphereRadius(); -} -Box.prototype = new Shape(); -Box.prototype.constructor = Box; - -/** - * Updates the local convex polyhedron representation used for some collisions. - * @method updateConvexPolyhedronRepresentation - */ -Box.prototype.updateConvexPolyhedronRepresentation = function(){ - var sx = this.halfExtents.x; - var sy = this.halfExtents.y; - var sz = this.halfExtents.z; - var V = Vec3; - - var vertices = [ - new V(-sx,-sy,-sz), - new V( sx,-sy,-sz), - new V( sx, sy,-sz), - new V(-sx, sy,-sz), - new V(-sx,-sy, sz), - new V( sx,-sy, sz), - new V( sx, sy, sz), - new V(-sx, sy, sz) - ]; - - var indices = [ - [3,2,1,0], // -z - [4,5,6,7], // +z - [5,4,0,1], // -y - [2,3,7,6], // +y - [0,4,7,3], // -x - [1,2,6,5], // +x - ]; - - var axes = [ - new V(0, 0, 1), - new V(0, 1, 0), - new V(1, 0, 0) - ]; - - var h = new ConvexPolyhedron(vertices, indices); - this.convexPolyhedronRepresentation = h; - h.material = this.material; -}; - -/** - * @method calculateLocalInertia - * @param {Number} mass - * @param {Vec3} target - * @return {Vec3} - */ -Box.prototype.calculateLocalInertia = function(mass,target){ - target = target || new Vec3(); - Box.calculateInertia(this.halfExtents, mass, target); - return target; -}; - -Box.calculateInertia = function(halfExtents,mass,target){ - var e = halfExtents; - target.x = 1.0 / 12.0 * mass * ( 2*e.y*2*e.y + 2*e.z*2*e.z ); - target.y = 1.0 / 12.0 * mass * ( 2*e.x*2*e.x + 2*e.z*2*e.z ); - target.z = 1.0 / 12.0 * mass * ( 2*e.y*2*e.y + 2*e.x*2*e.x ); -}; - -/** - * Get the box 6 side normals - * @method getSideNormals - * @param {array} sixTargetVectors An array of 6 vectors, to store the resulting side normals in. - * @param {Quaternion} quat Orientation to apply to the normal vectors. If not provided, the vectors will be in respect to the local frame. - * @return {array} - */ -Box.prototype.getSideNormals = function(sixTargetVectors,quat){ - var sides = sixTargetVectors; - var ex = this.halfExtents; - sides[0].set( ex.x, 0, 0); - sides[1].set( 0, ex.y, 0); - sides[2].set( 0, 0, ex.z); - sides[3].set( -ex.x, 0, 0); - sides[4].set( 0, -ex.y, 0); - sides[5].set( 0, 0, -ex.z); - - if(quat!==undefined){ - for(var i=0; i!==sides.length; i++){ - quat.vmult(sides[i],sides[i]); - } - } - - return sides; -}; - -Box.prototype.volume = function(){ - return 8.0 * this.halfExtents.x * this.halfExtents.y * this.halfExtents.z; -}; - -Box.prototype.updateBoundingSphereRadius = function(){ - this.boundingSphereRadius = this.halfExtents.norm(); -}; - -var worldCornerTempPos = new Vec3(); -var worldCornerTempNeg = new Vec3(); -Box.prototype.forEachWorldCorner = function(pos,quat,callback){ - - var e = this.halfExtents; - var corners = [[ e.x, e.y, e.z], - [ -e.x, e.y, e.z], - [ -e.x, -e.y, e.z], - [ -e.x, -e.y, -e.z], - [ e.x, -e.y, -e.z], - [ e.x, e.y, -e.z], - [ -e.x, e.y, -e.z], - [ e.x, -e.y, e.z]]; - for(var i=0; i max.x){ - max.x = x; - } - if(y > max.y){ - max.y = y; - } - if(z > max.z){ - max.z = z; - } - - if(x < min.x){ - min.x = x; - } - if(y < min.y){ - min.y = y; - } - if(z < min.z){ - min.z = z; - } - } - - // Get each axis max - // min.set(Infinity,Infinity,Infinity); - // max.set(-Infinity,-Infinity,-Infinity); - // this.forEachWorldCorner(pos,quat,function(x,y,z){ - // if(x > max.x){ - // max.x = x; - // } - // if(y > max.y){ - // max.y = y; - // } - // if(z > max.z){ - // max.z = z; - // } - - // if(x < min.x){ - // min.x = x; - // } - // if(y < min.y){ - // min.y = y; - // } - // if(z < min.z){ - // min.z = z; - // } - // }); -}; - -},{"../math/Vec3":30,"./ConvexPolyhedron":38,"./Shape":43}],38:[function(_dereq_,module,exports){ -module.exports = ConvexPolyhedron; - -var Shape = _dereq_('./Shape'); -var Vec3 = _dereq_('../math/Vec3'); -var Quaternion = _dereq_('../math/Quaternion'); -var Transform = _dereq_('../math/Transform'); - -/** - * A set of polygons describing a convex shape. - * @class ConvexPolyhedron - * @constructor - * @extends Shape - * @description The shape MUST be convex for the code to work properly. No polygons may be coplanar (contained - * in the same 3D plane), instead these should be merged into one polygon. - * - * @param {array} points An array of Vec3's - * @param {array} faces Array of integer arrays, describing which vertices that is included in each face. - * - * @author qiao / https://github.com/qiao (original author, see https://github.com/qiao/three.js/commit/85026f0c769e4000148a67d45a9e9b9c5108836f) - * @author schteppe / https://github.com/schteppe - * @see http://www.altdevblogaday.com/2011/05/13/contact-generation-between-3d-convex-meshes/ - * @see http://bullet.googlecode.com/svn/trunk/src/BulletCollision/NarrowPhaseCollision/btPolyhedralContactClipping.cpp - * - * @todo Move the clipping functions to ContactGenerator? - * @todo Automatically merge coplanar polygons in constructor. - */ -function ConvexPolyhedron(points, faces, uniqueAxes) { - var that = this; - Shape.call(this); - this.type = Shape.types.CONVEXPOLYHEDRON; - - /** - * Array of Vec3 - * @property vertices - * @type {Array} - */ - this.vertices = points||[]; - - this.worldVertices = []; // World transformed version of .vertices - this.worldVerticesNeedsUpdate = true; - - /** - * Array of integer arrays, indicating which vertices each face consists of - * @property faces - * @type {Array} - */ - this.faces = faces||[]; - - /** - * Array of Vec3 - * @property faceNormals - * @type {Array} - */ - this.faceNormals = []; - this.computeNormals(); - - this.worldFaceNormalsNeedsUpdate = true; - this.worldFaceNormals = []; // World transformed version of .faceNormals - - /** - * Array of Vec3 - * @property uniqueEdges - * @type {Array} - */ - this.uniqueEdges = []; - - /** - * If given, these locally defined, normalized axes are the only ones being checked when doing separating axis check. - * @property {Array} uniqueAxes - */ - this.uniqueAxes = uniqueAxes ? uniqueAxes.slice() : null; - - this.computeEdges(); - this.updateBoundingSphereRadius(); -} -ConvexPolyhedron.prototype = new Shape(); -ConvexPolyhedron.prototype.constructor = ConvexPolyhedron; - -var computeEdges_tmpEdge = new Vec3(); -/** - * Computes uniqueEdges - * @method computeEdges - */ -ConvexPolyhedron.prototype.computeEdges = function(){ - var faces = this.faces; - var vertices = this.vertices; - var nv = vertices.length; - var edges = this.uniqueEdges; - - edges.length = 0; - - var edge = computeEdges_tmpEdge; - - for(var i=0; i !== faces.length; i++){ - var face = faces[i]; - var numVertices = face.length; - for(var j = 0; j !== numVertices; j++){ - var k = ( j+1 ) % numVertices; - vertices[face[j]].vsub(vertices[face[k]], edge); - edge.normalize(); - var found = false; - for(var p=0; p !== edges.length; p++){ - if (edges[p].almostEquals(edge) || edges[p].almostEquals(edge)){ - found = true; - break; - } - } - - if (!found){ - edges.push(edge.clone()); - } - } - } -}; - -/** - * Compute the normals of the faces. Will reuse existing Vec3 objects in the .faceNormals array if they exist. - * @method computeNormals - */ -ConvexPolyhedron.prototype.computeNormals = function(){ - this.faceNormals.length = this.faces.length; - - // Generate normals - for(var i=0; i dmax){ - dmax = d; - closestFaceB = face; - } - } - var worldVertsB1 = []; - var polyB = hullB.faces[closestFaceB]; - var numVertices = polyB.length; - for(var e0=0; e0=0){ - this.clipFaceAgainstHull(separatingNormal, - posA, - quatA, - worldVertsB1, - minDist, - maxDist, - result); - } -}; - -/** - * Find the separating axis between this hull and another - * @method findSeparatingAxis - * @param {ConvexPolyhedron} hullB - * @param {Vec3} posA - * @param {Quaternion} quatA - * @param {Vec3} posB - * @param {Quaternion} quatB - * @param {Vec3} target The target vector to save the axis in - * @return {bool} Returns false if a separation is found, else true - */ -var fsa_faceANormalWS3 = new Vec3(), - fsa_Worldnormal1 = new Vec3(), - fsa_deltaC = new Vec3(), - fsa_worldEdge0 = new Vec3(), - fsa_worldEdge1 = new Vec3(), - fsa_Cross = new Vec3(); -ConvexPolyhedron.prototype.findSeparatingAxis = function(hullB,posA,quatA,posB,quatB,target, faceListA, faceListB){ - var faceANormalWS3 = fsa_faceANormalWS3, - Worldnormal1 = fsa_Worldnormal1, - deltaC = fsa_deltaC, - worldEdge0 = fsa_worldEdge0, - worldEdge1 = fsa_worldEdge1, - Cross = fsa_Cross; - - var dmin = Number.MAX_VALUE; - var hullA = this; - var curPlaneTests=0; - - if(!hullA.uniqueAxes){ - - var numFacesA = faceListA ? faceListA.length : hullA.faces.length; - - // Test face normals from hullA - for(var i=0; i0.0){ - target.negate(target); - } - - return true; -}; - -var maxminA=[], maxminB=[]; - -/** - * Test separating axis against two hulls. Both hulls are projected onto the axis and the overlap size is returned if there is one. - * @method testSepAxis - * @param {Vec3} axis - * @param {ConvexPolyhedron} hullB - * @param {Vec3} posA - * @param {Quaternion} quatA - * @param {Vec3} posB - * @param {Quaternion} quatB - * @return {number} The overlap depth, or FALSE if no penetration. - */ -ConvexPolyhedron.prototype.testSepAxis = function(axis, hullB, posA, quatA, posB, quatB){ - var hullA=this; - ConvexPolyhedron.project(hullA, axis, posA, quatA, maxminA); - ConvexPolyhedron.project(hullB, axis, posB, quatB, maxminB); - var maxA = maxminA[0]; - var minA = maxminA[1]; - var maxB = maxminB[0]; - var minB = maxminB[1]; - if(maxA= 0, so output intersection - var newv = new Vec3(); - firstVertex.lerp(lastVertex, - n_dot_first / (n_dot_first - n_dot_last), - newv); - outVertices.push(newv); - } - } else { - if(n_dot_last<0){ - // Start >= 0, end < 0 so output intersection and end - var newv = new Vec3(); - firstVertex.lerp(lastVertex, - n_dot_first / (n_dot_first - n_dot_last), - newv); - outVertices.push(newv); - outVertices.push(lastVertex); - } - } - firstVertex = lastVertex; - n_dot_first = n_dot_last; - } - return outVertices; -}; - -// Updates .worldVertices and sets .worldVerticesNeedsUpdate to false. -ConvexPolyhedron.prototype.computeWorldVertices = function(position,quat){ - var N = this.vertices.length; - while(this.worldVertices.length < N){ - this.worldVertices.push( new Vec3() ); - } - - var verts = this.vertices, - worldVerts = this.worldVertices; - for(var i=0; i!==N; i++){ - quat.vmult( verts[i] , worldVerts[i] ); - position.vadd( worldVerts[i] , worldVerts[i] ); - } - - this.worldVerticesNeedsUpdate = false; -}; - -var computeLocalAABB_worldVert = new Vec3(); -ConvexPolyhedron.prototype.computeLocalAABB = function(aabbmin,aabbmax){ - var n = this.vertices.length, - vertices = this.vertices, - worldVert = computeLocalAABB_worldVert; - - aabbmin.set(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE); - aabbmax.set(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE); - - for(var i=0; i aabbmax.x){ - aabbmax.x = v.x; - } - if (v.y < aabbmin.y){ - aabbmin.y = v.y; - } else if(v.y > aabbmax.y){ - aabbmax.y = v.y; - } - if (v.z < aabbmin.z){ - aabbmin.z = v.z; - } else if(v.z > aabbmax.z){ - aabbmax.z = v.z; - } - } -}; - -/** - * Updates .worldVertices and sets .worldVerticesNeedsUpdate to false. - * @method computeWorldFaceNormals - * @param {Quaternion} quat - */ -ConvexPolyhedron.prototype.computeWorldFaceNormals = function(quat){ - var N = this.faceNormals.length; - while(this.worldFaceNormals.length < N){ - this.worldFaceNormals.push( new Vec3() ); - } - - var normals = this.faceNormals, - worldNormals = this.worldFaceNormals; - for(var i=0; i!==N; i++){ - quat.vmult( normals[i] , worldNormals[i] ); - } - - this.worldFaceNormalsNeedsUpdate = false; -}; - -/** - * @method updateBoundingSphereRadius - */ -ConvexPolyhedron.prototype.updateBoundingSphereRadius = function(){ - // Assume points are distributed with local (0,0,0) as center - var max2 = 0; - var verts = this.vertices; - for(var i=0, N=verts.length; i!==N; i++) { - var norm2 = verts[i].norm2(); - if(norm2 > max2){ - max2 = norm2; - } - } - this.boundingSphereRadius = Math.sqrt(max2); -}; - -var tempWorldVertex = new Vec3(); - -/** - * @method calculateWorldAABB - * @param {Vec3} pos - * @param {Quaternion} quat - * @param {Vec3} min - * @param {Vec3} max - */ -ConvexPolyhedron.prototype.calculateWorldAABB = function(pos,quat,min,max){ - var n = this.vertices.length, verts = this.vertices; - var minx,miny,minz,maxx,maxy,maxz; - for(var i=0; i maxx || maxx===undefined){ - maxx = v.x; - } - - if (v.y < miny || miny===undefined){ - miny = v.y; - } else if(v.y > maxy || maxy===undefined){ - maxy = v.y; - } - - if (v.z < minz || minz===undefined){ - minz = v.z; - } else if(v.z > maxz || maxz===undefined){ - maxz = v.z; - } - } - min.set(minx,miny,minz); - max.set(maxx,maxy,maxz); -}; - -/** - * Get approximate convex volume - * @method volume - * @return {Number} - */ -ConvexPolyhedron.prototype.volume = function(){ - return 4.0 * Math.PI * this.boundingSphereRadius / 3.0; -}; - -/** - * Get an average of all the vertices positions - * @method getAveragePointLocal - * @param {Vec3} target - * @return {Vec3} - */ -ConvexPolyhedron.prototype.getAveragePointLocal = function(target){ - target = target || new Vec3(); - var n = this.vertices.length, - verts = this.vertices; - for(var i=0; i0) || (r1>0 && r2<0)){ - return false; // Encountered some other sign. Exit. - } else { - } - } - - // If we got here, all dot products were of the same sign. - return positiveResult ? 1 : -1; -}; - -/** - * Get max and min dot product of a convex hull at position (pos,quat) projected onto an axis. Results are saved in the array maxmin. - * @static - * @method project - * @param {ConvexPolyhedron} hull - * @param {Vec3} axis - * @param {Vec3} pos - * @param {Quaternion} quat - * @param {array} result result[0] and result[1] will be set to maximum and minimum, respectively. - */ -var project_worldVertex = new Vec3(); -var project_localAxis = new Vec3(); -var project_localOrigin = new Vec3(); -ConvexPolyhedron.project = function(hull, axis, pos, quat, result){ - var n = hull.vertices.length, - worldVertex = project_worldVertex, - localAxis = project_localAxis, - max = 0, - min = 0, - localOrigin = project_localOrigin, - vs = hull.vertices; - - localOrigin.setZero(); - - // Transform the axis to local - Transform.vectorToLocalFrame(pos, quat, axis, localAxis); - Transform.pointToLocalFrame(pos, quat, localOrigin, localOrigin); - var add = localOrigin.dot(localAxis); - - min = max = vs[0].dot(localAxis); - - for(var i = 1; i < n; i++){ - var val = vs[i].dot(localAxis); - - if(val > max){ - max = val; - } - - if(val < min){ - min = val; - } - } - - min -= add; - max -= add; - - if(min > max){ - // Inconsistent - swap - var temp = min; - min = max; - max = temp; - } - // Output - result[0] = max; - result[1] = min; -}; - -},{"../math/Quaternion":28,"../math/Transform":29,"../math/Vec3":30,"./Shape":43}],39:[function(_dereq_,module,exports){ -module.exports = Cylinder; - -var Shape = _dereq_('./Shape'); -var Vec3 = _dereq_('../math/Vec3'); -var Quaternion = _dereq_('../math/Quaternion'); -var ConvexPolyhedron = _dereq_('./ConvexPolyhedron'); - -/** - * @class Cylinder - * @constructor - * @extends ConvexPolyhedron - * @author schteppe / https://github.com/schteppe - * @param {Number} radiusTop - * @param {Number} radiusBottom - * @param {Number} height - * @param {Number} numSegments The number of segments to build the cylinder out of - */ -function Cylinder( radiusTop, radiusBottom, height , numSegments ) { - var N = numSegments, - verts = [], - axes = [], - faces = [], - bottomface = [], - topface = [], - cos = Math.cos, - sin = Math.sin; - - // First bottom point - verts.push(new Vec3(radiusBottom*cos(0), - radiusBottom*sin(0), - -height*0.5)); - bottomface.push(0); - - // First top point - verts.push(new Vec3(radiusTop*cos(0), - radiusTop*sin(0), - height*0.5)); - topface.push(1); - - for(var i=0; i { convex: ..., offset: ... } - // for example: - // _cachedPillars["0_2_1"] - this._cachedPillars = {}; -} -Heightfield.prototype = new Shape(); - -/** - * Call whenever you change the data array. - * @method update - */ -Heightfield.prototype.update = function(){ - this._cachedPillars = {}; -}; - -/** - * Update the .minValue property - * @method updateMinValue - */ -Heightfield.prototype.updateMinValue = function(){ - var data = this.data; - var minValue = data[0][0]; - for(var i=0; i !== data.length; i++){ - for(var j=0; j !== data[i].length; j++){ - var v = data[i][j]; - if(v < minValue){ - minValue = v; - } - } - } - this.minValue = minValue; -}; - -/** - * Update the .maxValue property - * @method updateMaxValue - */ -Heightfield.prototype.updateMaxValue = function(){ - var data = this.data; - var maxValue = data[0][0]; - for(var i=0; i !== data.length; i++){ - for(var j=0; j !== data[i].length; j++){ - var v = data[i][j]; - if(v > maxValue){ - maxValue = v; - } - } - } - this.maxValue = maxValue; -}; - -/** - * Set the height value at an index. Don't forget to update maxValue and minValue after you're done. - * @method setHeightValueAtIndex - * @param {integer} xi - * @param {integer} yi - * @param {number} value - */ -Heightfield.prototype.setHeightValueAtIndex = function(xi, yi, value){ - var data = this.data; - data[xi][yi] = value; - - // Invalidate cache - this.clearCachedConvexTrianglePillar(xi, yi, false); - if(xi > 0){ - this.clearCachedConvexTrianglePillar(xi - 1, yi, true); - this.clearCachedConvexTrianglePillar(xi - 1, yi, false); - } - if(yi > 0){ - this.clearCachedConvexTrianglePillar(xi, yi - 1, true); - this.clearCachedConvexTrianglePillar(xi, yi - 1, false); - } - if(yi > 0 && xi > 0){ - this.clearCachedConvexTrianglePillar(xi - 1, yi - 1, true); - } -}; - -/** - * Get max/min in a rectangle in the matrix data - * @method getRectMinMax - * @param {integer} iMinX - * @param {integer} iMinY - * @param {integer} iMaxX - * @param {integer} iMaxY - * @param {array} [result] An array to store the results in. - * @return {array} The result array, if it was passed in. Minimum will be at position 0 and max at 1. - */ -Heightfield.prototype.getRectMinMax = function (iMinX, iMinY, iMaxX, iMaxY, result) { - result = result || []; - - // Get max and min of the data - var data = this.data, - max = this.minValue; // Set first value - for(var i = iMinX; i <= iMaxX; i++){ - for(var j = iMinY; j <= iMaxY; j++){ - var height = data[i][j]; - if(height > max){ - max = height; - } - } - } - - result[0] = this.minValue; - result[1] = max; -}; - -/** - * Get the index of a local position on the heightfield. The indexes indicate the rectangles, so if your terrain is made of N x N height data points, you will have rectangle indexes ranging from 0 to N-1. - * @method getIndexOfPosition - * @param {number} x - * @param {number} y - * @param {array} result Two-element array - * @param {boolean} clamp If the position should be clamped to the heightfield edge. - * @return {boolean} - */ -Heightfield.prototype.getIndexOfPosition = function (x, y, result, clamp) { - - // Get the index of the data points to test against - var w = this.elementSize; - var data = this.data; - var xi = Math.floor(x / w); - var yi = Math.floor(y / w); - - result[0] = xi; - result[1] = yi; - - if(clamp){ - // Clamp index to edges - if(xi < 0){ xi = 0; } - if(yi < 0){ yi = 0; } - if(xi >= data.length - 1){ xi = data.length - 1; } - if(yi >= data[0].length - 1){ yi = data[0].length - 1; } - } - - // Bail out if we are out of the terrain - if(xi < 0 || yi < 0 || xi >= data.length-1 || yi >= data[0].length-1){ - return false; - } - - return true; -}; - -Heightfield.prototype.getHeightAt = function(x, y, edgeClamp){ - var idx = []; - this.getIndexOfPosition(x, y, idx, edgeClamp); - - // TODO: get upper or lower triangle, then use barycentric interpolation to get the height in the triangle. - var minmax = []; - this.getRectMinMax(idx[0], idx[1] + 1, idx[0], idx[1] + 1, minmax); - - return (minmax[0] + minmax[1]) / 2; // average -}; - -Heightfield.prototype.getCacheConvexTrianglePillarKey = function(xi, yi, getUpperTriangle){ - return xi + '_' + yi + '_' + (getUpperTriangle ? 1 : 0); -}; - -Heightfield.prototype.getCachedConvexTrianglePillar = function(xi, yi, getUpperTriangle){ - return this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi, yi, getUpperTriangle)]; -}; - -Heightfield.prototype.setCachedConvexTrianglePillar = function(xi, yi, getUpperTriangle, convex, offset){ - this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi, yi, getUpperTriangle)] = { - convex: convex, - offset: offset - }; -}; - -Heightfield.prototype.clearCachedConvexTrianglePillar = function(xi, yi, getUpperTriangle){ - delete this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi, yi, getUpperTriangle)]; -}; - -/** - * Get a triangle in the terrain in the form of a triangular convex shape. - * @method getConvexTrianglePillar - * @param {integer} i - * @param {integer} j - * @param {boolean} getUpperTriangle - */ -Heightfield.prototype.getConvexTrianglePillar = function(xi, yi, getUpperTriangle){ - var result = this.pillarConvex; - var offsetResult = this.pillarOffset; - - if(this.cacheEnabled){ - var data = this.getCachedConvexTrianglePillar(xi, yi, getUpperTriangle); - if(data){ - this.pillarConvex = data.convex; - this.pillarOffset = data.offset; - return; - } - - result = new ConvexPolyhedron(); - offsetResult = new Vec3(); - - this.pillarConvex = result; - this.pillarOffset = offsetResult; - } - - var data = this.data; - var elementSize = this.elementSize; - var faces = result.faces; - - // Reuse verts if possible - result.vertices.length = 6; - for (var i = 0; i < 6; i++) { - if(!result.vertices[i]){ - result.vertices[i] = new Vec3(); - } - } - - // Reuse faces if possible - faces.length = 5; - for (var i = 0; i < 5; i++) { - if(!faces[i]){ - faces[i] = []; - } - } - - var verts = result.vertices; - - var h = (Math.min( - data[xi][yi], - data[xi+1][yi], - data[xi][yi+1], - data[xi+1][yi+1] - ) - this.minValue ) / 2 + this.minValue; - - if (!getUpperTriangle) { - - // Center of the triangle pillar - all polygons are given relative to this one - offsetResult.set( - (xi + 0.25) * elementSize, // sort of center of a triangle - (yi + 0.25) * elementSize, - h // vertical center - ); - - // Top triangle verts - verts[0].set( - -0.25 * elementSize, - -0.25 * elementSize, - data[xi][yi] - h - ); - verts[1].set( - 0.75 * elementSize, - -0.25 * elementSize, - data[xi + 1][yi] - h - ); - verts[2].set( - -0.25 * elementSize, - 0.75 * elementSize, - data[xi][yi + 1] - h - ); - - // bottom triangle verts - verts[3].set( - -0.25 * elementSize, - -0.25 * elementSize, - -h-1 - ); - verts[4].set( - 0.75 * elementSize, - -0.25 * elementSize, - -h-1 - ); - verts[5].set( - -0.25 * elementSize, - 0.75 * elementSize, - -h-1 - ); - - // top triangle - faces[0][0] = 0; - faces[0][1] = 1; - faces[0][2] = 2; - - // bottom triangle - faces[1][0] = 5; - faces[1][1] = 4; - faces[1][2] = 3; - - // -x facing quad - faces[2][0] = 0; - faces[2][1] = 2; - faces[2][2] = 5; - faces[2][3] = 3; - - // -y facing quad - faces[3][0] = 1; - faces[3][1] = 0; - faces[3][2] = 3; - faces[3][3] = 4; - - // +xy facing quad - faces[4][0] = 4; - faces[4][1] = 5; - faces[4][2] = 2; - faces[4][3] = 1; - - - } else { - - // Center of the triangle pillar - all polygons are given relative to this one - offsetResult.set( - (xi + 0.75) * elementSize, // sort of center of a triangle - (yi + 0.75) * elementSize, - h // vertical center - ); - - // Top triangle verts - verts[0].set( - 0.25 * elementSize, - 0.25 * elementSize, - data[xi + 1][yi + 1] - h - ); - verts[1].set( - -0.75 * elementSize, - 0.25 * elementSize, - data[xi][yi + 1] - h - ); - verts[2].set( - 0.25 * elementSize, - -0.75 * elementSize, - data[xi + 1][yi] - h - ); - - // bottom triangle verts - verts[3].set( - 0.25 * elementSize, - 0.25 * elementSize, - - h-1 - ); - verts[4].set( - -0.75 * elementSize, - 0.25 * elementSize, - - h-1 - ); - verts[5].set( - 0.25 * elementSize, - -0.75 * elementSize, - - h-1 - ); - - // Top triangle - faces[0][0] = 0; - faces[0][1] = 1; - faces[0][2] = 2; - - // bottom triangle - faces[1][0] = 5; - faces[1][1] = 4; - faces[1][2] = 3; - - // +x facing quad - faces[2][0] = 2; - faces[2][1] = 5; - faces[2][2] = 3; - faces[2][3] = 0; - - // +y facing quad - faces[3][0] = 3; - faces[3][1] = 4; - faces[3][2] = 1; - faces[3][3] = 0; - - // -xy facing quad - faces[4][0] = 1; - faces[4][1] = 4; - faces[4][2] = 5; - faces[4][3] = 2; - } - - result.computeNormals(); - result.computeEdges(); - result.updateBoundingSphereRadius(); - - this.setCachedConvexTrianglePillar(xi, yi, getUpperTriangle, result, offsetResult); -}; - -Heightfield.prototype.calculateLocalInertia = function(mass, target){ - target = target || new Vec3(); - target.set(0, 0, 0); - return target; -}; - -Heightfield.prototype.volume = function(){ - return Number.MAX_VALUE; // The terrain is infinite -}; - -Heightfield.prototype.calculateWorldAABB = function(pos, quat, min, max){ - // TODO: do it properly - min.set(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE); - max.set(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE); -}; - -Heightfield.prototype.updateBoundingSphereRadius = function(){ - // Use the bounding box of the min/max values - var data = this.data, - s = this.elementSize; - this.boundingSphereRadius = new Vec3(data.length * s, data[0].length * s, Math.max(Math.abs(this.maxValue), Math.abs(this.minValue))).norm(); -}; - -},{"../math/Vec3":30,"../utils/Utils":53,"./ConvexPolyhedron":38,"./Shape":43}],41:[function(_dereq_,module,exports){ -module.exports = Particle; - -var Shape = _dereq_('./Shape'); -var Vec3 = _dereq_('../math/Vec3'); - -/** - * Particle shape. - * @class Particle - * @constructor - * @author schteppe - * @extends Shape - */ -function Particle(){ - Shape.call(this); - - this.type = Shape.types.PARTICLE; -} -Particle.prototype = new Shape(); -Particle.prototype.constructor = Particle; - -/** - * @method calculateLocalInertia - * @param {Number} mass - * @param {Vec3} target - * @return {Vec3} - */ -Particle.prototype.calculateLocalInertia = function(mass,target){ - target = target || new Vec3(); - target.set(0, 0, 0); - return target; -}; - -Particle.prototype.volume = function(){ - return 0; -}; - -Particle.prototype.updateBoundingSphereRadius = function(){ - this.boundingSphereRadius = 0; -}; - -Particle.prototype.calculateWorldAABB = function(pos,quat,min,max){ - // Get each axis max - min.copy(pos); - max.copy(pos); -}; - -},{"../math/Vec3":30,"./Shape":43}],42:[function(_dereq_,module,exports){ -module.exports = Plane; - -var Shape = _dereq_('./Shape'); -var Vec3 = _dereq_('../math/Vec3'); - -/** - * A plane, facing in the Z direction. The plane has its surface at z=0 and everything below z=0 is assumed to be solid plane. To make the plane face in some other direction than z, you must put it inside a RigidBody and rotate that body. See the demos. - * @class Plane - * @constructor - * @extends Shape - * @author schteppe - */ -function Plane(){ - Shape.call(this); - this.type = Shape.types.PLANE; - - // World oriented normal - this.worldNormal = new Vec3(); - this.worldNormalNeedsUpdate = true; - - this.boundingSphereRadius = Number.MAX_VALUE; -} -Plane.prototype = new Shape(); -Plane.prototype.constructor = Plane; - -Plane.prototype.computeWorldNormal = function(quat){ - var n = this.worldNormal; - n.set(0,0,1); - quat.vmult(n,n); - this.worldNormalNeedsUpdate = false; -}; - -Plane.prototype.calculateLocalInertia = function(mass,target){ - target = target || new Vec3(); - return target; -}; - -Plane.prototype.volume = function(){ - return Number.MAX_VALUE; // The plane is infinite... -}; - -var tempNormal = new Vec3(); -Plane.prototype.calculateWorldAABB = function(pos, quat, min, max){ - // The plane AABB is infinite, except if the normal is pointing along any axis - tempNormal.set(0,0,1); // Default plane normal is z - quat.vmult(tempNormal,tempNormal); - var maxVal = Number.MAX_VALUE; - min.set(-maxVal, -maxVal, -maxVal); - max.set(maxVal, maxVal, maxVal); - - if(tempNormal.x === 1){ max.x = pos.x; } - if(tempNormal.y === 1){ max.y = pos.y; } - if(tempNormal.z === 1){ max.z = pos.z; } - - if(tempNormal.x === -1){ min.x = pos.x; } - if(tempNormal.y === -1){ min.y = pos.y; } - if(tempNormal.z === -1){ min.z = pos.z; } -}; - -Plane.prototype.updateBoundingSphereRadius = function(){ - this.boundingSphereRadius = Number.MAX_VALUE; -}; -},{"../math/Vec3":30,"./Shape":43}],43:[function(_dereq_,module,exports){ -module.exports = Shape; - -var Shape = _dereq_('./Shape'); -var Vec3 = _dereq_('../math/Vec3'); -var Quaternion = _dereq_('../math/Quaternion'); -var Material = _dereq_('../material/Material'); - -/** - * Base class for shapes - * @class Shape - * @constructor - * @author schteppe - * @todo Should have a mechanism for caching bounding sphere radius instead of calculating it each time - */ -function Shape(){ - - /** - * Identifyer of the Shape. - * @property {number} id - */ - this.id = Shape.idCounter++; - - /** - * The type of this shape. Must be set to an int > 0 by subclasses. - * @property type - * @type {Number} - * @see Shape.types - */ - this.type = 0; - - /** - * The local bounding sphere radius of this shape. - * @property {Number} boundingSphereRadius - */ - this.boundingSphereRadius = 0; - - /** - * Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled. - * @property {boolean} collisionResponse - */ - this.collisionResponse = true; - - /** - * @property {Material} material - */ - this.material = null; -} -Shape.prototype.constructor = Shape; - -/** - * Computes the bounding sphere radius. The result is stored in the property .boundingSphereRadius - * @method updateBoundingSphereRadius - * @return {Number} - */ -Shape.prototype.updateBoundingSphereRadius = function(){ - throw "computeBoundingSphereRadius() not implemented for shape type "+this.type; -}; - -/** - * Get the volume of this shape - * @method volume - * @return {Number} - */ -Shape.prototype.volume = function(){ - throw "volume() not implemented for shape type "+this.type; -}; - -/** - * Calculates the inertia in the local frame for this shape. - * @method calculateLocalInertia - * @return {Vec3} - * @see http://en.wikipedia.org/wiki/List_of_moments_of_inertia - */ -Shape.prototype.calculateLocalInertia = function(mass,target){ - throw "calculateLocalInertia() not implemented for shape type "+this.type; -}; - -Shape.idCounter = 0; - -/** - * The available shape types. - * @static - * @property types - * @type {Object} - */ -Shape.types = { - SPHERE:1, - PLANE:2, - BOX:4, - COMPOUND:8, - CONVEXPOLYHEDRON:16, - HEIGHTFIELD:32, - PARTICLE:64, - CYLINDER:128, - TRIMESH:256 -}; - - -},{"../material/Material":25,"../math/Quaternion":28,"../math/Vec3":30,"./Shape":43}],44:[function(_dereq_,module,exports){ -module.exports = Sphere; - -var Shape = _dereq_('./Shape'); -var Vec3 = _dereq_('../math/Vec3'); - -/** - * Spherical shape - * @class Sphere - * @constructor - * @extends Shape - * @param {Number} radius The radius of the sphere, a non-negative number. - * @author schteppe / http://github.com/schteppe - */ -function Sphere(radius){ - Shape.call(this); - - /** - * @property {Number} radius - */ - this.radius = radius!==undefined ? Number(radius) : 1.0; - this.type = Shape.types.SPHERE; - - if(this.radius < 0){ - throw new Error('The sphere radius cannot be negative.'); - } - - this.updateBoundingSphereRadius(); -} -Sphere.prototype = new Shape(); -Sphere.prototype.constructor = Sphere; - -Sphere.prototype.calculateLocalInertia = function(mass,target){ - target = target || new Vec3(); - var I = 2.0*mass*this.radius*this.radius/5.0; - target.x = I; - target.y = I; - target.z = I; - return target; -}; - -Sphere.prototype.volume = function(){ - return 4.0 * Math.PI * this.radius / 3.0; -}; - -Sphere.prototype.updateBoundingSphereRadius = function(){ - this.boundingSphereRadius = this.radius; -}; - -Sphere.prototype.calculateWorldAABB = function(pos,quat,min,max){ - var r = this.radius; - var axes = ['x','y','z']; - for(var i=0; i u.x){ - u.x = v.x; - } - - if(v.y < l.y){ - l.y = v.y; - } else if(v.y > u.y){ - u.y = v.y; - } - - if(v.z < l.z){ - l.z = v.z; - } else if(v.z > u.z){ - u.z = v.z; - } - } -}; - - -/** - * Update the .aabb property - * @method updateAABB - */ -Trimesh.prototype.updateAABB = function(){ - this.computeLocalAABB(this.aabb); -}; - -/** - * Will update the .boundingSphereRadius property - * @method updateBoundingSphereRadius - */ -Trimesh.prototype.updateBoundingSphereRadius = function(){ - // Assume points are distributed with local (0,0,0) as center - var max2 = 0; - var vertices = this.vertices; - var v = new Vec3(); - for(var i=0, N=vertices.length / 3; i !== N; i++) { - this.getVertex(i, v); - var norm2 = v.norm2(); - if(norm2 > max2){ - max2 = norm2; - } - } - this.boundingSphereRadius = Math.sqrt(max2); -}; - -var tempWorldVertex = new Vec3(); -var calculateWorldAABB_frame = new Transform(); -var calculateWorldAABB_aabb = new AABB(); - -/** - * @method calculateWorldAABB - * @param {Vec3} pos - * @param {Quaternion} quat - * @param {Vec3} min - * @param {Vec3} max - */ -Trimesh.prototype.calculateWorldAABB = function(pos,quat,min,max){ - /* - var n = this.vertices.length / 3, - verts = this.vertices; - var minx,miny,minz,maxx,maxy,maxz; - - var v = tempWorldVertex; - for(var i=0; i maxx || maxx===undefined){ - maxx = v.x; - } - - if (v.y < miny || miny===undefined){ - miny = v.y; - } else if(v.y > maxy || maxy===undefined){ - maxy = v.y; - } - - if (v.z < minz || minz===undefined){ - minz = v.z; - } else if(v.z > maxz || maxz===undefined){ - maxz = v.z; - } - } - min.set(minx,miny,minz); - max.set(maxx,maxy,maxz); - */ - - // Faster approximation using local AABB - var frame = calculateWorldAABB_frame; - var result = calculateWorldAABB_aabb; - frame.position = pos; - frame.quaternion = quat; - this.aabb.toWorldFrame(frame, result); - min.copy(result.lowerBound); - max.copy(result.upperBound); -}; - -/** - * Get approximate volume - * @method volume - * @return {Number} - */ -Trimesh.prototype.volume = function(){ - return 4.0 * Math.PI * this.boundingSphereRadius / 3.0; -}; - -/** - * Create a Trimesh instance, shaped as a torus. - * @static - * @method createTorus - * @param {number} [radius=1] - * @param {number} [tube=0.5] - * @param {number} [radialSegments=8] - * @param {number} [tubularSegments=6] - * @param {number} [arc=6.283185307179586] - * @return {Trimesh} A torus - */ -Trimesh.createTorus = function (radius, tube, radialSegments, tubularSegments, arc) { - radius = radius || 1; - tube = tube || 0.5; - radialSegments = radialSegments || 8; - tubularSegments = tubularSegments || 6; - arc = arc || Math.PI * 2; - - var vertices = []; - var indices = []; - - for ( var j = 0; j <= radialSegments; j ++ ) { - for ( var i = 0; i <= tubularSegments; i ++ ) { - var u = i / tubularSegments * arc; - var v = j / radialSegments * Math.PI * 2; - - var x = ( radius + tube * Math.cos( v ) ) * Math.cos( u ); - var y = ( radius + tube * Math.cos( v ) ) * Math.sin( u ); - var z = tube * Math.sin( v ); - - vertices.push( x, y, z ); - } - } - - for ( var j = 1; j <= radialSegments; j ++ ) { - for ( var i = 1; i <= tubularSegments; i ++ ) { - var a = ( tubularSegments + 1 ) * j + i - 1; - var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1; - var c = ( tubularSegments + 1 ) * ( j - 1 ) + i; - var d = ( tubularSegments + 1 ) * j + i; - - indices.push(a, b, d); - indices.push(b, c, d); - } - } - - return new Trimesh(vertices, indices); -}; - -},{"../collision/AABB":3,"../math/Quaternion":28,"../math/Transform":29,"../math/Vec3":30,"../utils/Octree":50,"./Shape":43}],46:[function(_dereq_,module,exports){ -module.exports = GSSolver; - -var Vec3 = _dereq_('../math/Vec3'); -var Quaternion = _dereq_('../math/Quaternion'); -var Solver = _dereq_('./Solver'); - -/** - * Constraint equation Gauss-Seidel solver. - * @class GSSolver - * @constructor - * @todo The spook parameters should be specified for each constraint, not globally. - * @author schteppe / https://github.com/schteppe - * @see https://www8.cs.umu.se/kurser/5DV058/VT09/lectures/spooknotes.pdf - * @extends Solver - */ -function GSSolver(){ - Solver.call(this); - - /** - * The number of solver iterations determines quality of the constraints in the world. The more iterations, the more correct simulation. More iterations need more computations though. If you have a large gravity force in your world, you will need more iterations. - * @property iterations - * @type {Number} - * @todo write more about solver and iterations in the wiki - */ - this.iterations = 10; - - /** - * When tolerance is reached, the system is assumed to be converged. - * @property tolerance - * @type {Number} - */ - this.tolerance = 1e-7; -} -GSSolver.prototype = new Solver(); - -var GSSolver_solve_lambda = []; // Just temporary number holders that we want to reuse each solve. -var GSSolver_solve_invCs = []; -var GSSolver_solve_Bs = []; -GSSolver.prototype.solve = function(dt,world){ - var iter = 0, - maxIter = this.iterations, - tolSquared = this.tolerance*this.tolerance, - equations = this.equations, - Neq = equations.length, - bodies = world.bodies, - Nbodies = bodies.length, - h = dt, - q, B, invC, deltalambda, deltalambdaTot, GWlambda, lambdaj; - - // Update solve mass - if(Neq !== 0){ - for(var i=0; i!==Nbodies; i++){ - bodies[i].updateSolveMassProperties(); - } - } - - // Things that does not change during iteration can be computed once - var invCs = GSSolver_solve_invCs, - Bs = GSSolver_solve_Bs, - lambda = GSSolver_solve_lambda; - invCs.length = Neq; - Bs.length = Neq; - lambda.length = Neq; - for(var i=0; i!==Neq; i++){ - var c = equations[i]; - lambda[i] = 0.0; - Bs[i] = c.computeB(h); - invCs[i] = 1.0 / c.computeC(); - } - - if(Neq !== 0){ - - // Reset vlambda - for(var i=0; i!==Nbodies; i++){ - var b=bodies[i], - vlambda=b.vlambda, - wlambda=b.wlambda; - vlambda.set(0,0,0); - if(wlambda){ - wlambda.set(0,0,0); - } - } - - // Iterate over equations - for(iter=0; iter!==maxIter; iter++){ - - // Accumulate the total error for each iteration. - deltalambdaTot = 0.0; - - for(var j=0; j!==Neq; j++){ - - var c = equations[j]; - - // Compute iteration - B = Bs[j]; - invC = invCs[j]; - lambdaj = lambda[j]; - GWlambda = c.computeGWlambda(); - deltalambda = invC * ( B - GWlambda - c.eps * lambdaj ); - - // Clamp if we are not within the min/max interval - if(lambdaj + deltalambda < c.minForce){ - deltalambda = c.minForce - lambdaj; - } else if(lambdaj + deltalambda > c.maxForce){ - deltalambda = c.maxForce - lambdaj; - } - lambda[j] += deltalambda; - - deltalambdaTot += deltalambda > 0.0 ? deltalambda : -deltalambda; // abs(deltalambda) - - c.addToWlambda(deltalambda); - } - - // If the total error is small enough - stop iterate - if(deltalambdaTot*deltalambdaTot < tolSquared){ - break; - } - } - - // Add result to velocity - for(var i=0; i!==Nbodies; i++){ - var b=bodies[i], - v=b.velocity, - w=b.angularVelocity; - v.vadd(b.vlambda, v); - if(w){ - w.vadd(b.wlambda, w); - } - } - } - - return iter; -}; - -},{"../math/Quaternion":28,"../math/Vec3":30,"./Solver":47}],47:[function(_dereq_,module,exports){ -module.exports = Solver; - -/** - * Constraint equation solver base class. - * @class Solver - * @constructor - * @author schteppe / https://github.com/schteppe - */ -function Solver(){ - /** - * All equations to be solved - * @property {Array} equations - */ - this.equations = []; -} - -/** - * Should be implemented in subclasses! - * @method solve - * @param {Number} dt - * @param {World} world - */ -Solver.prototype.solve = function(dt,world){ - // Should return the number of iterations done! - return 0; -}; - -/** - * Add an equation - * @method addEquation - * @param {Equation} eq - */ -Solver.prototype.addEquation = function(eq){ - if (eq.enabled) { - this.equations.push(eq); - } -}; - -/** - * Remove an equation - * @method removeEquation - * @param {Equation} eq - */ -Solver.prototype.removeEquation = function(eq){ - var eqs = this.equations; - var i = eqs.indexOf(eq); - if(i !== -1){ - eqs.splice(i,1); - } -}; - -/** - * Add all equations - * @method removeAllEquations - */ -Solver.prototype.removeAllEquations = function(){ - this.equations.length = 0; -}; - - -},{}],48:[function(_dereq_,module,exports){ -module.exports = SplitSolver; - -var Vec3 = _dereq_('../math/Vec3'); -var Quaternion = _dereq_('../math/Quaternion'); -var Solver = _dereq_('./Solver'); -var Body = _dereq_('../objects/Body'); - -/** - * Splits the equations into islands and solves them independently. Can improve performance. - * @class SplitSolver - * @constructor - * @extends Solver - * @param {Solver} subsolver - */ -function SplitSolver(subsolver){ - Solver.call(this); - this.iterations = 10; - this.tolerance = 1e-7; - this.subsolver = subsolver; - this.nodes = []; - this.nodePool = []; - - // Create needed nodes, reuse if possible - while(this.nodePool.length < 128){ - this.nodePool.push(this.createNode()); - } -} -SplitSolver.prototype = new Solver(); - -// Returns the number of subsystems -var SplitSolver_solve_nodes = []; // All allocated node objects -var SplitSolver_solve_nodePool = []; // All allocated node objects -var SplitSolver_solve_eqs = []; // Temp array -var SplitSolver_solve_bds = []; // Temp array -var SplitSolver_solve_dummyWorld = {bodies:[]}; // Temp object - -var STATIC = Body.STATIC; -function getUnvisitedNode(nodes){ - var Nnodes = nodes.length; - for(var i=0; i!==Nnodes; i++){ - var node = nodes[i]; - if(!node.visited && !(node.body.type & STATIC)){ - return node; - } - } - return false; -} - -var queue = []; -function bfs(root,visitFunc,bds,eqs){ - queue.push(root); - root.visited = true; - visitFunc(root,bds,eqs); - while(queue.length) { - var node = queue.pop(); - // Loop over unvisited child nodes - var child; - while((child = getUnvisitedNode(node.children))) { - child.visited = true; - visitFunc(child,bds,eqs); - queue.push(child); - } - } -} - -function visitFunc(node,bds,eqs){ - bds.push(node.body); - var Neqs = node.eqs.length; - for(var i=0; i!==Neqs; i++){ - var eq = node.eqs[i]; - if(eqs.indexOf(eq) === -1){ - eqs.push(eq); - } - } -} - -SplitSolver.prototype.createNode = function(){ - return { body:null, children:[], eqs:[], visited:false }; -}; - -/** - * Solve the subsystems - * @method solve - * @param {Number} dt - * @param {World} world - */ -SplitSolver.prototype.solve = function(dt,world){ - var nodes=SplitSolver_solve_nodes, - nodePool=this.nodePool, - bodies=world.bodies, - equations=this.equations, - Neq=equations.length, - Nbodies=bodies.length, - subsolver=this.subsolver; - - // Create needed nodes, reuse if possible - while(nodePool.length < Nbodies){ - nodePool.push(this.createNode()); - } - nodes.length = Nbodies; - for (var i = 0; i < Nbodies; i++) { - nodes[i] = nodePool[i]; - } - - // Reset node values - for(var i=0; i!==Nbodies; i++){ - var node = nodes[i]; - node.body = bodies[i]; - node.children.length = 0; - node.eqs.length = 0; - node.visited = false; - } - for(var k=0; k!==Neq; k++){ - var eq=equations[k], - i=bodies.indexOf(eq.bi), - j=bodies.indexOf(eq.bj), - ni=nodes[i], - nj=nodes[j]; - ni.children.push(nj); - ni.eqs.push(eq); - nj.children.push(ni); - nj.eqs.push(eq); - } - - var child, n=0, eqs=SplitSolver_solve_eqs; - - subsolver.tolerance = this.tolerance; - subsolver.iterations = this.iterations; - - var dummyWorld = SplitSolver_solve_dummyWorld; - while((child = getUnvisitedNode(nodes))){ - eqs.length = 0; - dummyWorld.bodies.length = 0; - bfs(child, visitFunc, dummyWorld.bodies, eqs); - - var Neqs = eqs.length; - - eqs = eqs.sort(sortById); - - for(var i=0; i!==Neqs; i++){ - subsolver.addEquation(eqs[i]); - } - - var iter = subsolver.solve(dt,dummyWorld); - subsolver.removeAllEquations(); - n++; - } - - return n; -}; - -function sortById(a, b){ - return b.id - a.id; -} -},{"../math/Quaternion":28,"../math/Vec3":30,"../objects/Body":31,"./Solver":47}],49:[function(_dereq_,module,exports){ -/** - * Base class for objects that dispatches events. - * @class EventTarget - * @constructor - */ -var EventTarget = function () { - -}; - -module.exports = EventTarget; - -EventTarget.prototype = { - constructor: EventTarget, - - /** - * Add an event listener - * @method addEventListener - * @param {String} type - * @param {Function} listener - * @return {EventTarget} The self object, for chainability. - */ - addEventListener: function ( type, listener ) { - if ( this._listeners === undefined ){ this._listeners = {}; } - var listeners = this._listeners; - if ( listeners[ type ] === undefined ) { - listeners[ type ] = []; - } - if ( listeners[ type ].indexOf( listener ) === - 1 ) { - listeners[ type ].push( listener ); - } - return this; - }, - - /** - * Check if an event listener is added - * @method hasEventListener - * @param {String} type - * @param {Function} listener - * @return {Boolean} - */ - hasEventListener: function ( type, listener ) { - if ( this._listeners === undefined ){ return false; } - var listeners = this._listeners; - if ( listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1 ) { - return true; - } - return false; - }, - - /** - * Remove an event listener - * @method removeEventListener - * @param {String} type - * @param {Function} listener - * @return {EventTarget} The self object, for chainability. - */ - removeEventListener: function ( type, listener ) { - if ( this._listeners === undefined ){ return this; } - var listeners = this._listeners; - if ( listeners[type] === undefined ){ return this; } - var index = listeners[ type ].indexOf( listener ); - if ( index !== - 1 ) { - listeners[ type ].splice( index, 1 ); - } - return this; - }, - - /** - * Emit an event. - * @method dispatchEvent - * @param {Object} event - * @param {String} event.type - * @return {EventTarget} The self object, for chainability. - */ - dispatchEvent: function ( event ) { - if ( this._listeners === undefined ){ return this; } - var listeners = this._listeners; - var listenerArray = listeners[ event.type ]; - if ( listenerArray !== undefined ) { - event.target = this; - for ( var i = 0, l = listenerArray.length; i < l; i ++ ) { - listenerArray[ i ].call( this, event ); - } - } - return this; - } -}; - -},{}],50:[function(_dereq_,module,exports){ -var AABB = _dereq_('../collision/AABB'); -var Vec3 = _dereq_('../math/Vec3'); - -module.exports = Octree; - -/** - * @class OctreeNode - * @param {object} [options] - * @param {Octree} [options.root] - * @param {AABB} [options.aabb] - */ -function OctreeNode(options){ - options = options || {}; - - /** - * The root node - * @property {OctreeNode} root - */ - this.root = options.root || null; - - /** - * Boundary of this node - * @property {AABB} aabb - */ - this.aabb = options.aabb ? options.aabb.clone() : new AABB(); - - /** - * Contained data at the current node level. - * @property {Array} data - */ - this.data = []; - - /** - * Children to this node - * @property {Array} children - */ - this.children = []; -} - -/** - * @class Octree - * @param {AABB} aabb The total AABB of the tree - * @param {object} [options] - * @param {number} [options.maxDepth=8] - * @extends OctreeNode - */ -function Octree(aabb, options){ - options = options || {}; - options.root = null; - options.aabb = aabb; - OctreeNode.call(this, options); - - /** - * Maximum subdivision depth - * @property {number} maxDepth - */ - this.maxDepth = typeof(options.maxDepth) !== 'undefined' ? options.maxDepth : 8; -} -Octree.prototype = new OctreeNode(); - -OctreeNode.prototype.reset = function(aabb, options){ - this.children.length = this.data.length = 0; -}; - -/** - * Insert data into this node - * @method insert - * @param {AABB} aabb - * @param {object} elementData - * @return {boolean} True if successful, otherwise false - */ -OctreeNode.prototype.insert = function(aabb, elementData, level){ - var nodeData = this.data; - level = level || 0; - - // Ignore objects that do not belong in this node - if (!this.aabb.contains(aabb)){ - return false; // object cannot be added - } - - var children = this.children; - - if(level < (this.maxDepth || this.root.maxDepth)){ - // Subdivide if there are no children yet - var subdivided = false; - if (!children.length){ - this.subdivide(); - subdivided = true; - } - - // add to whichever node will accept it - for (var i = 0; i !== 8; i++) { - if (children[i].insert(aabb, elementData, level + 1)){ - return true; - } - } - - if(subdivided){ - // No children accepted! Might as well just remove em since they contain none - children.length = 0; - } - } - - // Too deep, or children didnt want it. add it in current node - nodeData.push(elementData); - - return true; -}; - -var halfDiagonal = new Vec3(); - -/** - * Create 8 equally sized children nodes and put them in the .children array. - * @method subdivide - */ -OctreeNode.prototype.subdivide = function() { - var aabb = this.aabb; - var l = aabb.lowerBound; - var u = aabb.upperBound; - - var children = this.children; - - children.push( - new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(0,0,0) }) }), - new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(1,0,0) }) }), - new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(1,1,0) }) }), - new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(1,1,1) }) }), - new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(0,1,1) }) }), - new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(0,0,1) }) }), - new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(1,0,1) }) }), - new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(0,1,0) }) }) - ); - - u.vsub(l, halfDiagonal); - halfDiagonal.scale(0.5, halfDiagonal); - - var root = this.root || this; - - for (var i = 0; i !== 8; i++) { - var child = children[i]; - - // Set current node as root - child.root = root; - - // Compute bounds - var lowerBound = child.aabb.lowerBound; - lowerBound.x *= halfDiagonal.x; - lowerBound.y *= halfDiagonal.y; - lowerBound.z *= halfDiagonal.z; - - lowerBound.vadd(l, lowerBound); - - // Upper bound is always lower bound + halfDiagonal - lowerBound.vadd(halfDiagonal, child.aabb.upperBound); - } -}; - -/** - * Get all data, potentially within an AABB - * @method aabbQuery - * @param {AABB} aabb - * @param {array} result - * @return {array} The "result" object - */ -OctreeNode.prototype.aabbQuery = function(aabb, result) { - - var nodeData = this.data; - - // abort if the range does not intersect this node - // if (!this.aabb.overlaps(aabb)){ - // return result; - // } - - // Add objects at this level - // Array.prototype.push.apply(result, nodeData); - - // Add child data - // @todo unwrap recursion into a queue / loop, that's faster in JS - var children = this.children; - - - // for (var i = 0, N = this.children.length; i !== N; i++) { - // children[i].aabbQuery(aabb, result); - // } - - var queue = [this]; - while (queue.length) { - var node = queue.pop(); - if (node.aabb.overlaps(aabb)){ - Array.prototype.push.apply(result, node.data); - } - Array.prototype.push.apply(queue, node.children); - } - - return result; -}; - -var tmpAABB = new AABB(); - -/** - * Get all data, potentially intersected by a ray. - * @method rayQuery - * @param {Ray} ray - * @param {Transform} treeTransform - * @param {array} result - * @return {array} The "result" object - */ -OctreeNode.prototype.rayQuery = function(ray, treeTransform, result) { - - // Use aabb query for now. - // @todo implement real ray query which needs less lookups - ray.getAABB(tmpAABB); - tmpAABB.toLocalFrame(treeTransform, tmpAABB); - this.aabbQuery(tmpAABB, result); - - return result; -}; - -/** - * @method removeEmptyNodes - */ -OctreeNode.prototype.removeEmptyNodes = function() { - var queue = [this]; - while (queue.length) { - var node = queue.pop(); - for (var i = node.children.length - 1; i >= 0; i--) { - if(!node.children[i].data.length){ - node.children.splice(i, 1); - } - } - Array.prototype.push.apply(queue, node.children); - } -}; - -},{"../collision/AABB":3,"../math/Vec3":30}],51:[function(_dereq_,module,exports){ -module.exports = Pool; - -/** - * For pooling objects that can be reused. - * @class Pool - * @constructor - */ -function Pool(){ - /** - * The pooled objects - * @property {Array} objects - */ - this.objects = []; - - /** - * Constructor of the objects - * @property {mixed} type - */ - this.type = Object; -} - -/** - * Release an object after use - * @method release - * @param {Object} obj - */ -Pool.prototype.release = function(){ - var Nargs = arguments.length; - for(var i=0; i!==Nargs; i++){ - this.objects.push(arguments[i]); - } -}; - -/** - * Get an object - * @method get - * @return {mixed} - */ -Pool.prototype.get = function(){ - if(this.objects.length===0){ - return this.constructObject(); - } else { - return this.objects.pop(); - } -}; - -/** - * Construct an object. Should be implmented in each subclass. - * @method constructObject - * @return {mixed} - */ -Pool.prototype.constructObject = function(){ - throw new Error("constructObject() not implemented in this Pool subclass yet!"); -}; - -},{}],52:[function(_dereq_,module,exports){ -module.exports = TupleDictionary; - -/** - * @class TupleDictionary - * @constructor - */ -function TupleDictionary() { - - /** - * The data storage - * @property data - * @type {Object} - */ - this.data = { keys:[] }; -} - -/** - * @method get - * @param {Number} i - * @param {Number} j - * @return {Number} - */ -TupleDictionary.prototype.get = function(i, j) { - if (i > j) { - // swap - var temp = j; - j = i; - i = temp; - } - return this.data[i+'-'+j]; -}; - -/** - * @method set - * @param {Number} i - * @param {Number} j - * @param {Number} value - */ -TupleDictionary.prototype.set = function(i, j, value) { - if (i > j) { - var temp = j; - j = i; - i = temp; - } - var key = i+'-'+j; - - // Check if key already exists - if(!this.get(i,j)){ - this.data.keys.push(key); - } - - this.data[key] = value; -}; - -/** - * @method reset - */ -TupleDictionary.prototype.reset = function() { - var data = this.data, - keys = data.keys; - while(keys.length > 0){ - var key = keys.pop(); - delete data[key]; - } -}; - -},{}],53:[function(_dereq_,module,exports){ -function Utils(){} - -module.exports = Utils; - -/** - * Extend an options object with default values. - * @static - * @method defaults - * @param {object} options The options object. May be falsy: in this case, a new object is created and returned. - * @param {object} defaults An object containing default values. - * @return {object} The modified options object. - */ -Utils.defaults = function(options, defaults){ - options = options || {}; - - for(var key in defaults){ - if(!(key in options)){ - options[key] = defaults[key]; - } - } - - return options; -}; - -},{}],54:[function(_dereq_,module,exports){ -module.exports = Vec3Pool; - -var Vec3 = _dereq_('../math/Vec3'); -var Pool = _dereq_('./Pool'); - -/** - * @class Vec3Pool - * @constructor - * @extends Pool - */ -function Vec3Pool(){ - Pool.call(this); - this.type = Vec3; -} -Vec3Pool.prototype = new Pool(); - -/** - * Construct a vector - * @method constructObject - * @return {Vec3} - */ -Vec3Pool.prototype.constructObject = function(){ - return new Vec3(); -}; - -},{"../math/Vec3":30,"./Pool":51}],55:[function(_dereq_,module,exports){ -module.exports = Narrowphase; - -var AABB = _dereq_('../collision/AABB'); -var Shape = _dereq_('../shapes/Shape'); -var Ray = _dereq_('../collision/Ray'); -var Vec3 = _dereq_('../math/Vec3'); -var Transform = _dereq_('../math/Transform'); -var ConvexPolyhedron = _dereq_('../shapes/ConvexPolyhedron'); -var Quaternion = _dereq_('../math/Quaternion'); -var Solver = _dereq_('../solver/Solver'); -var Vec3Pool = _dereq_('../utils/Vec3Pool'); -var ContactEquation = _dereq_('../equations/ContactEquation'); -var FrictionEquation = _dereq_('../equations/FrictionEquation'); - -/** - * Helper class for the World. Generates ContactEquations. - * @class Narrowphase - * @constructor - * @todo Sphere-ConvexPolyhedron contacts - * @todo Contact reduction - * @todo should move methods to prototype - */ -function Narrowphase(world){ - - /** - * Internal storage of pooled contact points. - * @property {Array} contactPointPool - */ - this.contactPointPool = []; - - this.frictionEquationPool = []; - - this.result = []; - this.frictionResult = []; - - /** - * Pooled vectors. - * @property {Vec3Pool} v3pool - */ - this.v3pool = new Vec3Pool(); - - this.world = world; - this.currentContactMaterial = null; - - /** - * @property {Boolean} enableFrictionReduction - */ - this.enableFrictionReduction = false; -} - -/** - * Make a contact object, by using the internal pool or creating a new one. - * @method createContactEquation - * @return {ContactEquation} - */ -Narrowphase.prototype.createContactEquation = function(bi, bj, si, sj, rsi, rsj){ - var c; - if(this.contactPointPool.length){ - c = this.contactPointPool.pop(); - c.bi = bi; - c.bj = bj; - } else { - c = new ContactEquation(bi, bj); - } - - c.enabled = bi.collisionResponse && bj.collisionResponse && si.collisionResponse && sj.collisionResponse; - - var cm = this.currentContactMaterial; - - c.restitution = cm.restitution; - - c.setSpookParams( - cm.contactEquationStiffness, - cm.contactEquationRelaxation, - this.world.dt - ); - - var matA = si.material || bi.material; - var matB = sj.material || bj.material; - if(matA && matB && matA.restitution >= 0 && matB.restitution >= 0){ - c.restitution = matA.restitution * matB.restitution; - } - - c.si = rsi || si; - c.sj = rsj || sj; - - return c; -}; - -Narrowphase.prototype.createFrictionEquationsFromContact = function(contactEquation, outArray){ - var bodyA = contactEquation.bi; - var bodyB = contactEquation.bj; - var shapeA = contactEquation.si; - var shapeB = contactEquation.sj; - - var world = this.world; - var cm = this.currentContactMaterial; - - // If friction or restitution were specified in the material, use them - var friction = cm.friction; - var matA = shapeA.material || bodyA.material; - var matB = shapeB.material || bodyB.material; - if(matA && matB && matA.friction >= 0 && matB.friction >= 0){ - friction = matA.friction * matB.friction; - } - - if(friction > 0){ - - // Create 2 tangent equations - var mug = friction * world.gravity.length(); - var reducedMass = (bodyA.invMass + bodyB.invMass); - if(reducedMass > 0){ - reducedMass = 1/reducedMass; - } - var pool = this.frictionEquationPool; - var c1 = pool.length ? pool.pop() : new FrictionEquation(bodyA,bodyB,mug*reducedMass); - var c2 = pool.length ? pool.pop() : new FrictionEquation(bodyA,bodyB,mug*reducedMass); - - c1.bi = c2.bi = bodyA; - c1.bj = c2.bj = bodyB; - c1.minForce = c2.minForce = -mug*reducedMass; - c1.maxForce = c2.maxForce = mug*reducedMass; - - // Copy over the relative vectors - c1.ri.copy(contactEquation.ri); - c1.rj.copy(contactEquation.rj); - c2.ri.copy(contactEquation.ri); - c2.rj.copy(contactEquation.rj); - - // Construct tangents - contactEquation.ni.tangents(c1.t, c2.t); - - // Set spook params - c1.setSpookParams(cm.frictionEquationStiffness, cm.frictionEquationRelaxation, world.dt); - c2.setSpookParams(cm.frictionEquationStiffness, cm.frictionEquationRelaxation, world.dt); - - c1.enabled = c2.enabled = contactEquation.enabled; - - outArray.push(c1, c2); - - return true; - } - - return false; -}; - -var averageNormal = new Vec3(); -var averageContactPointA = new Vec3(); -var averageContactPointB = new Vec3(); - -// Take the average N latest contact point on the plane. -Narrowphase.prototype.createFrictionFromAverage = function(numContacts){ - // The last contactEquation - var c = this.result[this.result.length - 1]; - - // Create the result: two "average" friction equations - if (!this.createFrictionEquationsFromContact(c, this.frictionResult) || numContacts === 1) { - return; - } - - var f1 = this.frictionResult[this.frictionResult.length - 2]; - var f2 = this.frictionResult[this.frictionResult.length - 1]; - - averageNormal.setZero(); - averageContactPointA.setZero(); - averageContactPointB.setZero(); - - var bodyA = c.bi; - var bodyB = c.bj; - for(var i=0; i!==numContacts; i++){ - c = this.result[this.result.length - 1 - i]; - if(c.bodyA !== bodyA){ - averageNormal.vadd(c.ni, averageNormal); // vec2.add(eq.t, eq.t, c.normalA); - averageContactPointA.vadd(c.ri, averageContactPointA); // vec2.add(eq.contactPointA, eq.contactPointA, c.contactPointA); - averageContactPointB.vadd(c.rj, averageContactPointB); - } else { - averageNormal.vsub(c.ni, averageNormal); // vec2.sub(eq.t, eq.t, c.normalA); - averageContactPointA.vadd(c.rj, averageContactPointA); // vec2.add(eq.contactPointA, eq.contactPointA, c.contactPointA); - averageContactPointB.vadd(c.ri, averageContactPointB); - } - } - - var invNumContacts = 1 / numContacts; - averageContactPointA.scale(invNumContacts, f1.ri); // vec2.scale(eq.contactPointA, eq.contactPointA, invNumContacts); - averageContactPointB.scale(invNumContacts, f1.rj); // vec2.scale(eq.contactPointB, eq.contactPointB, invNumContacts); - f2.ri.copy(f1.ri); // Should be the same - f2.rj.copy(f1.rj); - averageNormal.normalize(); - averageNormal.tangents(f1.t, f2.t); - // return eq; -}; - - -var tmpVec1 = new Vec3(); -var tmpVec2 = new Vec3(); -var tmpQuat1 = new Quaternion(); -var tmpQuat2 = new Quaternion(); - -/** - * Generate all contacts between a list of body pairs - * @method getContacts - * @param {array} p1 Array of body indices - * @param {array} p2 Array of body indices - * @param {World} world - * @param {array} result Array to store generated contacts - * @param {array} oldcontacts Optional. Array of reusable contact objects - */ -Narrowphase.prototype.getContacts = function(p1, p2, world, result, oldcontacts, frictionResult, frictionPool){ - // Save old contact objects - this.contactPointPool = oldcontacts; - this.frictionEquationPool = frictionPool; - this.result = result; - this.frictionResult = frictionResult; - - var qi = tmpQuat1; - var qj = tmpQuat2; - var xi = tmpVec1; - var xj = tmpVec2; - - for(var k=0, N=p1.length; k!==N; k++){ - - // Get current collision bodies - var bi = p1[k], - bj = p2[k]; - - // Get contact material - var bodyContactMaterial = null; - if(bi.material && bj.material){ - bodyContactMaterial = world.getContactMaterial(bi.material,bj.material) || null; - } - - for (var i = 0; i < bi.shapes.length; i++) { - bi.quaternion.mult(bi.shapeOrientations[i], qi); - bi.quaternion.vmult(bi.shapeOffsets[i], xi); - xi.vadd(bi.position, xi); - var si = bi.shapes[i]; - - for (var j = 0; j < bj.shapes.length; j++) { - - // Compute world transform of shapes - bj.quaternion.mult(bj.shapeOrientations[j], qj); - bj.quaternion.vmult(bj.shapeOffsets[j], xj); - xj.vadd(bj.position, xj); - var sj = bj.shapes[j]; - - if(xi.distanceTo(xj) > si.boundingSphereRadius + sj.boundingSphereRadius){ - continue; - } - - // Get collision material - var shapeContactMaterial = null; - if(si.material && sj.material){ - shapeContactMaterial = world.getContactMaterial(si.material,sj.material) || null; - } - - this.currentContactMaterial = shapeContactMaterial || bodyContactMaterial || world.defaultContactMaterial; - - // Get contacts - var resolver = this[si.type | sj.type]; - if(resolver){ - if (si.type < sj.type) { - resolver.call(this, si, sj, xi, xj, qi, qj, bi, bj, si, sj); - } else { - resolver.call(this, sj, si, xj, xi, qj, qi, bj, bi, si, sj); - } - } - } - } - } -}; - -var numWarnings = 0; -var maxWarnings = 10; - -function warn(msg){ - if(numWarnings > maxWarnings){ - return; - } - - numWarnings++; - - console.warn(msg); -} - -Narrowphase.prototype[Shape.types.BOX | Shape.types.BOX] = -Narrowphase.prototype.boxBox = function(si,sj,xi,xj,qi,qj,bi,bj){ - si.convexPolyhedronRepresentation.material = si.material; - sj.convexPolyhedronRepresentation.material = sj.material; - si.convexPolyhedronRepresentation.collisionResponse = si.collisionResponse; - sj.convexPolyhedronRepresentation.collisionResponse = sj.collisionResponse; - this.convexConvex(si.convexPolyhedronRepresentation,sj.convexPolyhedronRepresentation,xi,xj,qi,qj,bi,bj,si,sj); -}; - -Narrowphase.prototype[Shape.types.BOX | Shape.types.CONVEXPOLYHEDRON] = -Narrowphase.prototype.boxConvex = function(si,sj,xi,xj,qi,qj,bi,bj){ - si.convexPolyhedronRepresentation.material = si.material; - si.convexPolyhedronRepresentation.collisionResponse = si.collisionResponse; - this.convexConvex(si.convexPolyhedronRepresentation,sj,xi,xj,qi,qj,bi,bj,si,sj); -}; - -Narrowphase.prototype[Shape.types.BOX | Shape.types.PARTICLE] = -Narrowphase.prototype.boxParticle = function(si,sj,xi,xj,qi,qj,bi,bj){ - si.convexPolyhedronRepresentation.material = si.material; - si.convexPolyhedronRepresentation.collisionResponse = si.collisionResponse; - this.convexParticle(si.convexPolyhedronRepresentation,sj,xi,xj,qi,qj,bi,bj,si,sj); -}; - -/** - * @method sphereSphere - * @param {Shape} si - * @param {Shape} sj - * @param {Vec3} xi - * @param {Vec3} xj - * @param {Quaternion} qi - * @param {Quaternion} qj - * @param {Body} bi - * @param {Body} bj - */ -Narrowphase.prototype[Shape.types.SPHERE] = -Narrowphase.prototype.sphereSphere = function(si,sj,xi,xj,qi,qj,bi,bj){ - // We will have only one contact in this case - var r = this.createContactEquation(bi,bj,si,sj); - - // Contact normal - xj.vsub(xi, r.ni); - r.ni.normalize(); - - // Contact point locations - r.ri.copy(r.ni); - r.rj.copy(r.ni); - r.ri.mult(si.radius, r.ri); - r.rj.mult(-sj.radius, r.rj); - - r.ri.vadd(xi, r.ri); - r.ri.vsub(bi.position, r.ri); - - r.rj.vadd(xj, r.rj); - r.rj.vsub(bj.position, r.rj); - - this.result.push(r); - - this.createFrictionEquationsFromContact(r, this.frictionResult); -}; - -/** - * @method planeTrimesh - * @param {Shape} si - * @param {Shape} sj - * @param {Vec3} xi - * @param {Vec3} xj - * @param {Quaternion} qi - * @param {Quaternion} qj - * @param {Body} bi - * @param {Body} bj - */ -var planeTrimesh_normal = new Vec3(); -var planeTrimesh_relpos = new Vec3(); -var planeTrimesh_projected = new Vec3(); -Narrowphase.prototype[Shape.types.PLANE | Shape.types.TRIMESH] = -Narrowphase.prototype.planeTrimesh = function( - planeShape, - trimeshShape, - planePos, - trimeshPos, - planeQuat, - trimeshQuat, - planeBody, - trimeshBody -){ - // Make contacts! - var v = new Vec3(); - - var normal = planeTrimesh_normal; - normal.set(0,0,1); - planeQuat.vmult(normal,normal); // Turn normal according to plane - - for(var i=0; i 0 && positionAlongEdgeB < 0){ - - // Now check the orthogonal distance from edge to sphere center - localSpherePos.vsub(edgeVertexA, tmp); - - edgeVectorUnit.copy(edgeVector); - edgeVectorUnit.normalize(); - positionAlongEdgeA = tmp.dot(edgeVectorUnit); - - edgeVectorUnit.scale(positionAlongEdgeA, tmp); - tmp.vadd(edgeVertexA, tmp); - - // tmp is now the sphere center position projected to the edge, defined locally in the trimesh frame - var dist = tmp.distanceTo(localSpherePos); - if(dist < sphereShape.radius){ - var r = this.createContactEquation(sphereBody, trimeshBody, sphereShape, trimeshShape); - - tmp.vsub(localSpherePos, r.ni); - r.ni.normalize(); - r.ni.scale(sphereShape.radius, r.ri); - - Transform.pointToWorldFrame(trimeshPos, trimeshQuat, tmp, tmp); - tmp.vsub(trimeshBody.position, r.rj); - - Transform.vectorToWorldFrame(trimeshQuat, r.ni, r.ni); - Transform.vectorToWorldFrame(trimeshQuat, r.ri, r.ri); - - this.result.push(r); - this.createFrictionEquationsFromContact(r, this.frictionResult); - } - } - } - } - - // Triangle faces - var va = sphereTrimesh_va; - var vb = sphereTrimesh_vb; - var vc = sphereTrimesh_vc; - var normal = sphereTrimesh_normal; - for(var i=0, N = triangles.length; i !== N; i++){ - trimeshShape.getTriangleVertices(triangles[i], va, vb, vc); - trimeshShape.getNormal(triangles[i], normal); - localSpherePos.vsub(va, tmp); - var dist = tmp.dot(normal); - normal.scale(dist, tmp); - localSpherePos.vsub(tmp, tmp); - - // tmp is now the sphere position projected to the triangle plane - dist = tmp.distanceTo(localSpherePos); - if(Ray.pointInTriangle(tmp, va, vb, vc) && dist < sphereShape.radius){ - var r = this.createContactEquation(sphereBody, trimeshBody, sphereShape, trimeshShape); - - tmp.vsub(localSpherePos, r.ni); - r.ni.normalize(); - r.ni.scale(sphereShape.radius, r.ri); - - Transform.pointToWorldFrame(trimeshPos, trimeshQuat, tmp, tmp); - tmp.vsub(trimeshBody.position, r.rj); - - Transform.vectorToWorldFrame(trimeshQuat, r.ni, r.ni); - Transform.vectorToWorldFrame(trimeshQuat, r.ri, r.ri); - - this.result.push(r); - this.createFrictionEquationsFromContact(r, this.frictionResult); - } - } - - triangles.length = 0; -}; - -var point_on_plane_to_sphere = new Vec3(); -var plane_to_sphere_ortho = new Vec3(); - -/** - * @method spherePlane - * @param {Shape} si - * @param {Shape} sj - * @param {Vec3} xi - * @param {Vec3} xj - * @param {Quaternion} qi - * @param {Quaternion} qj - * @param {Body} bi - * @param {Body} bj - */ -Narrowphase.prototype[Shape.types.SPHERE | Shape.types.PLANE] = -Narrowphase.prototype.spherePlane = function(si,sj,xi,xj,qi,qj,bi,bj){ - // We will have one contact in this case - var r = this.createContactEquation(bi,bj,si,sj); - - // Contact normal - r.ni.set(0,0,1); - qj.vmult(r.ni, r.ni); - r.ni.negate(r.ni); // body i is the sphere, flip normal - r.ni.normalize(); // Needed? - - // Vector from sphere center to contact point - r.ni.mult(si.radius, r.ri); - - // Project down sphere on plane - xi.vsub(xj, point_on_plane_to_sphere); - r.ni.mult(r.ni.dot(point_on_plane_to_sphere), plane_to_sphere_ortho); - point_on_plane_to_sphere.vsub(plane_to_sphere_ortho,r.rj); // The sphere position projected to plane - - if(-point_on_plane_to_sphere.dot(r.ni) <= si.radius){ - - // Make it relative to the body - var ri = r.ri; - var rj = r.rj; - ri.vadd(xi, ri); - ri.vsub(bi.position, ri); - rj.vadd(xj, rj); - rj.vsub(bj.position, rj); - - this.result.push(r); - this.createFrictionEquationsFromContact(r, this.frictionResult); - } -}; - -// See http://bulletphysics.com/Bullet/BulletFull/SphereTriangleDetector_8cpp_source.html -var pointInPolygon_edge = new Vec3(); -var pointInPolygon_edge_x_normal = new Vec3(); -var pointInPolygon_vtp = new Vec3(); -function pointInPolygon(verts, normal, p){ - var positiveResult = null; - var N = verts.length; - for(var i=0; i!==N; i++){ - var v = verts[i]; - - // Get edge to the next vertex - var edge = pointInPolygon_edge; - verts[(i+1) % (N)].vsub(v,edge); - - // Get cross product between polygon normal and the edge - var edge_x_normal = pointInPolygon_edge_x_normal; - //var edge_x_normal = new Vec3(); - edge.cross(normal,edge_x_normal); - - // Get vector between point and current vertex - var vertex_to_p = pointInPolygon_vtp; - p.vsub(v,vertex_to_p); - - // This dot product determines which side of the edge the point is - var r = edge_x_normal.dot(vertex_to_p); - - // If all such dot products have same sign, we are inside the polygon. - if(positiveResult===null || (r>0 && positiveResult===true) || (r<=0 && positiveResult===false)){ - if(positiveResult===null){ - positiveResult = r>0; - } - continue; - } else { - return false; // Encountered some other sign. Exit. - } - } - - // If we got here, all dot products were of the same sign. - return true; -} - -var box_to_sphere = new Vec3(); -var sphereBox_ns = new Vec3(); -var sphereBox_ns1 = new Vec3(); -var sphereBox_ns2 = new Vec3(); -var sphereBox_sides = [new Vec3(),new Vec3(),new Vec3(),new Vec3(),new Vec3(),new Vec3()]; -var sphereBox_sphere_to_corner = new Vec3(); -var sphereBox_side_ns = new Vec3(); -var sphereBox_side_ns1 = new Vec3(); -var sphereBox_side_ns2 = new Vec3(); - -/** - * @method sphereBox - * @param {Shape} si - * @param {Shape} sj - * @param {Vec3} xi - * @param {Vec3} xj - * @param {Quaternion} qi - * @param {Quaternion} qj - * @param {Body} bi - * @param {Body} bj - */ -Narrowphase.prototype[Shape.types.SPHERE | Shape.types.BOX] = -Narrowphase.prototype.sphereBox = function(si,sj,xi,xj,qi,qj,bi,bj){ - var v3pool = this.v3pool; - - // we refer to the box as body j - var sides = sphereBox_sides; - xi.vsub(xj,box_to_sphere); - sj.getSideNormals(sides,qj); - var R = si.radius; - var penetrating_sides = []; - - // Check side (plane) intersections - var found = false; - - // Store the resulting side penetration info - var side_ns = sphereBox_side_ns; - var side_ns1 = sphereBox_side_ns1; - var side_ns2 = sphereBox_side_ns2; - var side_h = null; - var side_penetrations = 0; - var side_dot1 = 0; - var side_dot2 = 0; - var side_distance = null; - for(var idx=0,nsides=sides.length; idx!==nsides && found===false; idx++){ - // Get the plane side normal (ns) - var ns = sphereBox_ns; - ns.copy(sides[idx]); - - var h = ns.norm(); - ns.normalize(); - - // The normal/distance dot product tells which side of the plane we are - var dot = box_to_sphere.dot(ns); - - if(dot0){ - // Intersects plane. Now check the other two dimensions - var ns1 = sphereBox_ns1; - var ns2 = sphereBox_ns2; - ns1.copy(sides[(idx+1)%3]); - ns2.copy(sides[(idx+2)%3]); - var h1 = ns1.norm(); - var h2 = ns2.norm(); - ns1.normalize(); - ns2.normalize(); - var dot1 = box_to_sphere.dot(ns1); - var dot2 = box_to_sphere.dot(ns2); - if(dot1

-h1 && dot2

-h2){ - var dist = Math.abs(dot-h-R); - if(side_distance===null || dist < side_distance){ - side_distance = dist; - side_dot1 = dot1; - side_dot2 = dot2; - side_h = h; - side_ns.copy(ns); - side_ns1.copy(ns1); - side_ns2.copy(ns2); - side_penetrations++; - } - } - } - } - if(side_penetrations){ - found = true; - var r = this.createContactEquation(bi,bj,si,sj); - side_ns.mult(-R,r.ri); // Sphere r - r.ni.copy(side_ns); - r.ni.negate(r.ni); // Normal should be out of sphere - side_ns.mult(side_h,side_ns); - side_ns1.mult(side_dot1,side_ns1); - side_ns.vadd(side_ns1,side_ns); - side_ns2.mult(side_dot2,side_ns2); - side_ns.vadd(side_ns2,r.rj); - - // Make relative to bodies - r.ri.vadd(xi, r.ri); - r.ri.vsub(bi.position, r.ri); - r.rj.vadd(xj, r.rj); - r.rj.vsub(bj.position, r.rj); - - this.result.push(r); - this.createFrictionEquationsFromContact(r, this.frictionResult); - } - - // Check corners - var rj = v3pool.get(); - var sphere_to_corner = sphereBox_sphere_to_corner; - for(var j=0; j!==2 && !found; j++){ - for(var k=0; k!==2 && !found; k++){ - for(var l=0; l!==2 && !found; l++){ - rj.set(0,0,0); - if(j){ - rj.vadd(sides[0],rj); - } else { - rj.vsub(sides[0],rj); - } - if(k){ - rj.vadd(sides[1],rj); - } else { - rj.vsub(sides[1],rj); - } - if(l){ - rj.vadd(sides[2],rj); - } else { - rj.vsub(sides[2],rj); - } - - // World position of corner - xj.vadd(rj,sphere_to_corner); - sphere_to_corner.vsub(xi,sphere_to_corner); - - if(sphere_to_corner.norm2() < R*R){ - found = true; - var r = this.createContactEquation(bi,bj,si,sj); - r.ri.copy(sphere_to_corner); - r.ri.normalize(); - r.ni.copy(r.ri); - r.ri.mult(R,r.ri); - r.rj.copy(rj); - - // Make relative to bodies - r.ri.vadd(xi, r.ri); - r.ri.vsub(bi.position, r.ri); - r.rj.vadd(xj, r.rj); - r.rj.vsub(bj.position, r.rj); - - this.result.push(r); - this.createFrictionEquationsFromContact(r, this.frictionResult); - } - } - } - } - v3pool.release(rj); - rj = null; - - // Check edges - var edgeTangent = v3pool.get(); - var edgeCenter = v3pool.get(); - var r = v3pool.get(); // r = edge center to sphere center - var orthogonal = v3pool.get(); - var dist = v3pool.get(); - var Nsides = sides.length; - for(var j=0; j!==Nsides && !found; j++){ - for(var k=0; k!==Nsides && !found; k++){ - if(j%3 !== k%3){ - // Get edge tangent - sides[k].cross(sides[j],edgeTangent); - edgeTangent.normalize(); - sides[j].vadd(sides[k], edgeCenter); - r.copy(xi); - r.vsub(edgeCenter,r); - r.vsub(xj,r); - var orthonorm = r.dot(edgeTangent); // distance from edge center to sphere center in the tangent direction - edgeTangent.mult(orthonorm,orthogonal); // Vector from edge center to sphere center in the tangent direction - - // Find the third side orthogonal to this one - var l = 0; - while(l===j%3 || l===k%3){ - l++; - } - - // vec from edge center to sphere projected to the plane orthogonal to the edge tangent - dist.copy(xi); - dist.vsub(orthogonal,dist); - dist.vsub(edgeCenter,dist); - dist.vsub(xj,dist); - - // Distances in tangent direction and distance in the plane orthogonal to it - var tdist = Math.abs(orthonorm); - var ndist = dist.norm(); - - if(tdist < sides[l].norm() && ndist si.boundingSphereRadius + sj.boundingSphereRadius){ - // return; - // } - - // Check corners - for(var i=0; i!==verts.length; i++){ - var v = verts[i]; - - // World position of corner - var worldCorner = sphereConvex_worldCorner; - qj.vmult(v,worldCorner); - xj.vadd(worldCorner,worldCorner); - var sphere_to_corner = sphereConvex_sphereToCorner; - worldCorner.vsub(xi, sphere_to_corner); - if(sphere_to_corner.norm2() < R * R){ - found = true; - var r = this.createContactEquation(bi,bj,si,sj); - r.ri.copy(sphere_to_corner); - r.ri.normalize(); - r.ni.copy(r.ri); - r.ri.mult(R,r.ri); - worldCorner.vsub(xj,r.rj); - - // Should be relative to the body. - r.ri.vadd(xi, r.ri); - r.ri.vsub(bi.position, r.ri); - - // Should be relative to the body. - r.rj.vadd(xj, r.rj); - r.rj.vsub(bj.position, r.rj); - - this.result.push(r); - this.createFrictionEquationsFromContact(r, this.frictionResult); - return; - } - } - - // Check side (plane) intersections - var found = false; - for(var i=0, nfaces=faces.length; i!==nfaces && found===false; i++){ - var normal = normals[i]; - var face = faces[i]; - - // Get world-transformed normal of the face - var worldNormal = sphereConvex_worldNormal; - qj.vmult(normal,worldNormal); - - // Get a world vertex from the face - var worldPoint = sphereConvex_worldPoint; - qj.vmult(verts[face[0]],worldPoint); - worldPoint.vadd(xj,worldPoint); - - // Get a point on the sphere, closest to the face normal - var worldSpherePointClosestToPlane = sphereConvex_worldSpherePointClosestToPlane; - worldNormal.mult(-R, worldSpherePointClosestToPlane); - xi.vadd(worldSpherePointClosestToPlane, worldSpherePointClosestToPlane); - - // Vector from a face point to the closest point on the sphere - var penetrationVec = sphereConvex_penetrationVec; - worldSpherePointClosestToPlane.vsub(worldPoint,penetrationVec); - - // The penetration. Negative value means overlap. - var penetration = penetrationVec.dot(worldNormal); - - var worldPointToSphere = sphereConvex_sphereToWorldPoint; - xi.vsub(worldPoint, worldPointToSphere); - - if(penetration < 0 && worldPointToSphere.dot(worldNormal)>0){ - // Intersects plane. Now check if the sphere is inside the face polygon - var faceVerts = []; // Face vertices, in world coords - for(var j=0, Nverts=face.length; j!==Nverts; j++){ - var worldVertex = v3pool.get(); - qj.vmult(verts[face[j]], worldVertex); - xj.vadd(worldVertex,worldVertex); - faceVerts.push(worldVertex); - } - - if(pointInPolygon(faceVerts,worldNormal,xi)){ // Is the sphere center in the face polygon? - found = true; - var r = this.createContactEquation(bi,bj,si,sj); - - worldNormal.mult(-R, r.ri); // Contact offset, from sphere center to contact - worldNormal.negate(r.ni); // Normal pointing out of sphere - - var penetrationVec2 = v3pool.get(); - worldNormal.mult(-penetration, penetrationVec2); - var penetrationSpherePoint = v3pool.get(); - worldNormal.mult(-R, penetrationSpherePoint); - - //xi.vsub(xj).vadd(penetrationSpherePoint).vadd(penetrationVec2 , r.rj); - xi.vsub(xj,r.rj); - r.rj.vadd(penetrationSpherePoint,r.rj); - r.rj.vadd(penetrationVec2 , r.rj); - - // Should be relative to the body. - r.rj.vadd(xj, r.rj); - r.rj.vsub(bj.position, r.rj); - - // Should be relative to the body. - r.ri.vadd(xi, r.ri); - r.ri.vsub(bi.position, r.ri); - - v3pool.release(penetrationVec2); - v3pool.release(penetrationSpherePoint); - - this.result.push(r); - this.createFrictionEquationsFromContact(r, this.frictionResult); - - // Release world vertices - for(var j=0, Nfaceverts=faceVerts.length; j!==Nfaceverts; j++){ - v3pool.release(faceVerts[j]); - } - - return; // We only expect *one* face contact - } else { - // Edge? - for(var j=0; j!==face.length; j++){ - - // Get two world transformed vertices - var v1 = v3pool.get(); - var v2 = v3pool.get(); - qj.vmult(verts[face[(j+1)%face.length]], v1); - qj.vmult(verts[face[(j+2)%face.length]], v2); - xj.vadd(v1, v1); - xj.vadd(v2, v2); - - // Construct edge vector - var edge = sphereConvex_edge; - v2.vsub(v1,edge); - - // Construct the same vector, but normalized - var edgeUnit = sphereConvex_edgeUnit; - edge.unit(edgeUnit); - - // p is xi projected onto the edge - var p = v3pool.get(); - var v1_to_xi = v3pool.get(); - xi.vsub(v1, v1_to_xi); - var dot = v1_to_xi.dot(edgeUnit); - edgeUnit.mult(dot, p); - p.vadd(v1, p); - - // Compute a vector from p to the center of the sphere - var xi_to_p = v3pool.get(); - p.vsub(xi, xi_to_p); - - // Collision if the edge-sphere distance is less than the radius - // AND if p is in between v1 and v2 - if(dot > 0 && dot*dot si.boundingSphereRadius + sj.boundingSphereRadius){ - return; - } - - if(si.findSeparatingAxis(sj,xi,qi,xj,qj,sepAxis,faceListA,faceListB)){ - var res = []; - var q = convexConvex_q; - si.clipAgainstHull(xi,qi,sj,xj,qj,sepAxis,-100,100,res); - var numContacts = 0; - for(var j = 0; j !== res.length; j++){ - var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj), - ri = r.ri, - rj = r.rj; - sepAxis.negate(r.ni); - res[j].normal.negate(q); - q.mult(res[j].depth, q); - res[j].point.vadd(q, ri); - rj.copy(res[j].point); - - // Contact points are in world coordinates. Transform back to relative - ri.vsub(xi,ri); - rj.vsub(xj,rj); - - // Make relative to bodies - ri.vadd(xi, ri); - ri.vsub(bi.position, ri); - rj.vadd(xj, rj); - rj.vsub(bj.position, rj); - - this.result.push(r); - numContacts++; - if(!this.enableFrictionReduction){ - this.createFrictionEquationsFromContact(r, this.frictionResult); - } - } - if(this.enableFrictionReduction && numContacts){ - this.createFrictionFromAverage(numContacts); - } - } -}; - - -/** - * @method convexTrimesh - * @param {Array} result - * @param {Shape} si - * @param {Shape} sj - * @param {Vec3} xi - * @param {Vec3} xj - * @param {Quaternion} qi - * @param {Quaternion} qj - * @param {Body} bi - * @param {Body} bj - */ -// Narrowphase.prototype[Shape.types.CONVEXPOLYHEDRON | Shape.types.TRIMESH] = -// Narrowphase.prototype.convexTrimesh = function(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,faceListA,faceListB){ -// var sepAxis = convexConvex_sepAxis; - -// if(xi.distanceTo(xj) > si.boundingSphereRadius + sj.boundingSphereRadius){ -// return; -// } - -// // Construct a temp hull for each triangle -// var hullB = new ConvexPolyhedron(); - -// hullB.faces = [[0,1,2]]; -// var va = new Vec3(); -// var vb = new Vec3(); -// var vc = new Vec3(); -// hullB.vertices = [ -// va, -// vb, -// vc -// ]; - -// for (var i = 0; i < sj.indices.length / 3; i++) { - -// var triangleNormal = new Vec3(); -// sj.getNormal(i, triangleNormal); -// hullB.faceNormals = [triangleNormal]; - -// sj.getTriangleVertices(i, va, vb, vc); - -// var d = si.testSepAxis(triangleNormal, hullB, xi, qi, xj, qj); -// if(!d){ -// triangleNormal.scale(-1, triangleNormal); -// d = si.testSepAxis(triangleNormal, hullB, xi, qi, xj, qj); - -// if(!d){ -// continue; -// } -// } - -// var res = []; -// var q = convexConvex_q; -// si.clipAgainstHull(xi,qi,hullB,xj,qj,triangleNormal,-100,100,res); -// for(var j = 0; j !== res.length; j++){ -// var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj), -// ri = r.ri, -// rj = r.rj; -// r.ni.copy(triangleNormal); -// r.ni.negate(r.ni); -// res[j].normal.negate(q); -// q.mult(res[j].depth, q); -// res[j].point.vadd(q, ri); -// rj.copy(res[j].point); - -// // Contact points are in world coordinates. Transform back to relative -// ri.vsub(xi,ri); -// rj.vsub(xj,rj); - -// // Make relative to bodies -// ri.vadd(xi, ri); -// ri.vsub(bi.position, ri); -// rj.vadd(xj, rj); -// rj.vsub(bj.position, rj); - -// result.push(r); -// } -// } -// }; - -var particlePlane_normal = new Vec3(); -var particlePlane_relpos = new Vec3(); -var particlePlane_projected = new Vec3(); - -/** - * @method particlePlane - * @param {Array} result - * @param {Shape} si - * @param {Shape} sj - * @param {Vec3} xi - * @param {Vec3} xj - * @param {Quaternion} qi - * @param {Quaternion} qj - * @param {Body} bi - * @param {Body} bj - */ -Narrowphase.prototype[Shape.types.PLANE | Shape.types.PARTICLE] = -Narrowphase.prototype.planeParticle = function(sj,si,xj,xi,qj,qi,bj,bi){ - var normal = particlePlane_normal; - normal.set(0,0,1); - bj.quaternion.vmult(normal,normal); // Turn normal according to plane orientation - var relpos = particlePlane_relpos; - xi.vsub(bj.position,relpos); - var dot = normal.dot(relpos); - if(dot <= 0.0){ - var r = this.createContactEquation(bi,bj,si,sj); - r.ni.copy(normal); // Contact normal is the plane normal - r.ni.negate(r.ni); - r.ri.set(0,0,0); // Center of particle - - // Get particle position projected on plane - var projected = particlePlane_projected; - normal.mult(normal.dot(xi),projected); - xi.vsub(projected,projected); - //projected.vadd(bj.position,projected); - - // rj is now the projected world position minus plane position - r.rj.copy(projected); - this.result.push(r); - this.createFrictionEquationsFromContact(r, this.frictionResult); - } -}; - -var particleSphere_normal = new Vec3(); - -/** - * @method particleSphere - * @param {Array} result - * @param {Shape} si - * @param {Shape} sj - * @param {Vec3} xi - * @param {Vec3} xj - * @param {Quaternion} qi - * @param {Quaternion} qj - * @param {Body} bi - * @param {Body} bj - */ -Narrowphase.prototype[Shape.types.PARTICLE | Shape.types.SPHERE] = -Narrowphase.prototype.sphereParticle = function(sj,si,xj,xi,qj,qi,bj,bi){ - // The normal is the unit vector from sphere center to particle center - var normal = particleSphere_normal; - normal.set(0,0,1); - xi.vsub(xj,normal); - var lengthSquared = normal.norm2(); - - if(lengthSquared <= sj.radius * sj.radius){ - var r = this.createContactEquation(bi,bj,si,sj); - normal.normalize(); - r.rj.copy(normal); - r.rj.mult(sj.radius,r.rj); - r.ni.copy(normal); // Contact normal - r.ni.negate(r.ni); - r.ri.set(0,0,0); // Center of particle - this.result.push(r); - this.createFrictionEquationsFromContact(r, this.frictionResult); - } -}; - -// WIP -var cqj = new Quaternion(); -var convexParticle_local = new Vec3(); -var convexParticle_normal = new Vec3(); -var convexParticle_penetratedFaceNormal = new Vec3(); -var convexParticle_vertexToParticle = new Vec3(); -var convexParticle_worldPenetrationVec = new Vec3(); - -/** - * @method convexParticle - * @param {Array} result - * @param {Shape} si - * @param {Shape} sj - * @param {Vec3} xi - * @param {Vec3} xj - * @param {Quaternion} qi - * @param {Quaternion} qj - * @param {Body} bi - * @param {Body} bj - */ -Narrowphase.prototype[Shape.types.PARTICLE | Shape.types.CONVEXPOLYHEDRON] = -Narrowphase.prototype.convexParticle = function(sj,si,xj,xi,qj,qi,bj,bi){ - var penetratedFaceIndex = -1; - var penetratedFaceNormal = convexParticle_penetratedFaceNormal; - var worldPenetrationVec = convexParticle_worldPenetrationVec; - var minPenetration = null; - var numDetectedFaces = 0; - - // Convert particle position xi to local coords in the convex - var local = convexParticle_local; - local.copy(xi); - local.vsub(xj,local); // Convert position to relative the convex origin - qj.conjugate(cqj); - cqj.vmult(local,local); - - if(sj.pointIsInside(local)){ - - if(sj.worldVerticesNeedsUpdate){ - sj.computeWorldVertices(xj,qj); - } - if(sj.worldFaceNormalsNeedsUpdate){ - sj.computeWorldFaceNormals(qj); - } - - // For each world polygon in the polyhedra - for(var i=0,nfaces=sj.faces.length; i!==nfaces; i++){ - - // Construct world face vertices - var verts = [ sj.worldVertices[ sj.faces[i][0] ] ]; - var normal = sj.worldFaceNormals[i]; - - // Check how much the particle penetrates the polygon plane. - xi.vsub(verts[0],convexParticle_vertexToParticle); - var penetration = -normal.dot(convexParticle_vertexToParticle); - if(minPenetration===null || Math.abs(penetration) data.length || iMinY > data[0].length){ - return; - } - - // Clamp index to edges - if(iMinX < 0){ iMinX = 0; } - if(iMaxX < 0){ iMaxX = 0; } - if(iMinY < 0){ iMinY = 0; } - if(iMaxY < 0){ iMaxY = 0; } - if(iMinX >= data.length){ iMinX = data.length - 1; } - if(iMaxX >= data.length){ iMaxX = data.length - 1; } - if(iMaxY >= data[0].length){ iMaxY = data[0].length - 1; } - if(iMinY >= data[0].length){ iMinY = data[0].length - 1; } - - var minMax = []; - hfShape.getRectMinMax(iMinX, iMinY, iMaxX, iMaxY, minMax); - var min = minMax[0]; - var max = minMax[1]; - - // Bail out if we're cant touch the bounding height box - if(localConvexPos.z - radius > max || localConvexPos.z + radius < min){ - return; - } - - for(var i = iMinX; i < iMaxX; i++){ - for(var j = iMinY; j < iMaxY; j++){ - - // Lower triangle - hfShape.getConvexTrianglePillar(i, j, false); - Transform.pointToWorldFrame(hfPos, hfQuat, hfShape.pillarOffset, worldPillarOffset); - if (convexPos.distanceTo(worldPillarOffset) < hfShape.pillarConvex.boundingSphereRadius + convexShape.boundingSphereRadius) { - this.convexConvex(convexShape, hfShape.pillarConvex, convexPos, worldPillarOffset, convexQuat, hfQuat, convexBody, hfBody, null, null, faceList, null); - } - - // Upper triangle - hfShape.getConvexTrianglePillar(i, j, true); - Transform.pointToWorldFrame(hfPos, hfQuat, hfShape.pillarOffset, worldPillarOffset); - if (convexPos.distanceTo(worldPillarOffset) < hfShape.pillarConvex.boundingSphereRadius + convexShape.boundingSphereRadius) { - this.convexConvex(convexShape, hfShape.pillarConvex, convexPos, worldPillarOffset, convexQuat, hfQuat, convexBody, hfBody, null, null, faceList, null); - } - } - } -}; - -var sphereHeightfield_tmp1 = new Vec3(); -var sphereHeightfield_tmp2 = new Vec3(); - -/** - * @method sphereHeightfield - */ -Narrowphase.prototype[Shape.types.SPHERE | Shape.types.HEIGHTFIELD] = -Narrowphase.prototype.sphereHeightfield = function ( - sphereShape, - hfShape, - spherePos, - hfPos, - sphereQuat, - hfQuat, - sphereBody, - hfBody -){ - var data = hfShape.data, - radius = sphereShape.radius, - w = hfShape.elementSize, - worldPillarOffset = sphereHeightfield_tmp2; - - // Get sphere position to heightfield local! - var localSpherePos = sphereHeightfield_tmp1; - Transform.pointToLocalFrame(hfPos, hfQuat, spherePos, localSpherePos); - - // Get the index of the data points to test against - var iMinX = Math.floor((localSpherePos.x - radius) / w) - 1, - iMaxX = Math.ceil((localSpherePos.x + radius) / w) + 1, - iMinY = Math.floor((localSpherePos.y - radius) / w) - 1, - iMaxY = Math.ceil((localSpherePos.y + radius) / w) + 1; - - // Bail out if we are out of the terrain - if(iMaxX < 0 || iMaxY < 0 || iMinX > data.length || iMaxY > data[0].length){ - return; - } - - // Clamp index to edges - if(iMinX < 0){ iMinX = 0; } - if(iMaxX < 0){ iMaxX = 0; } - if(iMinY < 0){ iMinY = 0; } - if(iMaxY < 0){ iMaxY = 0; } - if(iMinX >= data.length){ iMinX = data.length - 1; } - if(iMaxX >= data.length){ iMaxX = data.length - 1; } - if(iMaxY >= data[0].length){ iMaxY = data[0].length - 1; } - if(iMinY >= data[0].length){ iMinY = data[0].length - 1; } - - var minMax = []; - hfShape.getRectMinMax(iMinX, iMinY, iMaxX, iMaxY, minMax); - var min = minMax[0]; - var max = minMax[1]; - - // Bail out if we're cant touch the bounding height box - if(localSpherePos.z - radius > max || localSpherePos.z + radius < min){ - return; - } - - var result = this.result; - for(var i = iMinX; i < iMaxX; i++){ - for(var j = iMinY; j < iMaxY; j++){ - - var numContactsBefore = result.length; - - // Lower triangle - hfShape.getConvexTrianglePillar(i, j, false); - Transform.pointToWorldFrame(hfPos, hfQuat, hfShape.pillarOffset, worldPillarOffset); - if (spherePos.distanceTo(worldPillarOffset) < hfShape.pillarConvex.boundingSphereRadius + sphereShape.boundingSphereRadius) { - this.sphereConvex(sphereShape, hfShape.pillarConvex, spherePos, worldPillarOffset, sphereQuat, hfQuat, sphereBody, hfBody); - } - - // Upper triangle - hfShape.getConvexTrianglePillar(i, j, true); - Transform.pointToWorldFrame(hfPos, hfQuat, hfShape.pillarOffset, worldPillarOffset); - if (spherePos.distanceTo(worldPillarOffset) < hfShape.pillarConvex.boundingSphereRadius + sphereShape.boundingSphereRadius) { - this.sphereConvex(sphereShape, hfShape.pillarConvex, spherePos, worldPillarOffset, sphereQuat, hfQuat, sphereBody, hfBody); - } - - var numContacts = result.length - numContactsBefore; - - if(numContacts > 2){ - return; - } - /* - // Skip all but 1 - for (var k = 0; k < numContacts - 1; k++) { - result.pop(); - } - */ - } - } -}; - -},{"../collision/AABB":3,"../collision/Ray":9,"../equations/ContactEquation":19,"../equations/FrictionEquation":21,"../math/Quaternion":28,"../math/Transform":29,"../math/Vec3":30,"../shapes/ConvexPolyhedron":38,"../shapes/Shape":43,"../solver/Solver":47,"../utils/Vec3Pool":54}],56:[function(_dereq_,module,exports){ -/* global performance */ - -module.exports = World; - -var Shape = _dereq_('../shapes/Shape'); -var Vec3 = _dereq_('../math/Vec3'); -var Quaternion = _dereq_('../math/Quaternion'); -var GSSolver = _dereq_('../solver/GSSolver'); -var Vec3Pool = _dereq_('../utils/Vec3Pool'); -var ContactEquation = _dereq_('../equations/ContactEquation'); -var FrictionEquation = _dereq_('../equations/FrictionEquation'); -var Narrowphase = _dereq_('./Narrowphase'); -var EventTarget = _dereq_('../utils/EventTarget'); -var ArrayCollisionMatrix = _dereq_('../collision/ArrayCollisionMatrix'); -var Material = _dereq_('../material/Material'); -var ContactMaterial = _dereq_('../material/ContactMaterial'); -var Body = _dereq_('../objects/Body'); -var TupleDictionary = _dereq_('../utils/TupleDictionary'); -var RaycastResult = _dereq_('../collision/RaycastResult'); -var AABB = _dereq_('../collision/AABB'); -var Ray = _dereq_('../collision/Ray'); -var NaiveBroadphase = _dereq_('../collision/NaiveBroadphase'); - -/** - * The physics world - * @class World - * @constructor - * @extends EventTarget - */ -function World(){ - EventTarget.apply(this); - - /** - * Currently / last used timestep. Is set to -1 if not available. This value is updated before each internal step, which means that it is "fresh" inside event callbacks. - * @property {Number} dt - */ - this.dt = -1; - - /** - * Makes bodies go to sleep when they've been inactive - * @property allowSleep - * @type {Boolean} - */ - this.allowSleep = false; - - /** - * All the current contacts (instances of ContactEquation) in the world. - * @property contacts - * @type {Array} - */ - this.contacts = []; - this.frictionEquations = []; - - /** - * How often to normalize quaternions. Set to 0 for every step, 1 for every second etc.. A larger value increases performance. If bodies tend to explode, set to a smaller value (zero to be sure nothing can go wrong). - * @property quatNormalizeSkip - * @type {Number} - */ - this.quatNormalizeSkip = 0; - - /** - * Set to true to use fast quaternion normalization. It is often enough accurate to use. If bodies tend to explode, set to false. - * @property quatNormalizeFast - * @type {Boolean} - * @see Quaternion.normalizeFast - * @see Quaternion.normalize - */ - this.quatNormalizeFast = false; - - /** - * The wall-clock time since simulation start - * @property time - * @type {Number} - */ - this.time = 0.0; - - /** - * Number of timesteps taken since start - * @property stepnumber - * @type {Number} - */ - this.stepnumber = 0; - - /// Default and last timestep sizes - this.default_dt = 1/60; - - this.nextId = 0; - /** - * @property gravity - * @type {Vec3} - */ - this.gravity = new Vec3(); - - /** - * @property broadphase - * @type {Broadphase} - */ - this.broadphase = new NaiveBroadphase(); - - /** - * @property bodies - * @type {Array} - */ - this.bodies = []; - - /** - * @property solver - * @type {Solver} - */ - this.solver = new GSSolver(); - - /** - * @property constraints - * @type {Array} - */ - this.constraints = []; - - /** - * @property narrowphase - * @type {Narrowphase} - */ - this.narrowphase = new Narrowphase(this); - - /** - * @property {ArrayCollisionMatrix} collisionMatrix - * @type {ArrayCollisionMatrix} - */ - this.collisionMatrix = new ArrayCollisionMatrix(); - - /** - * CollisionMatrix from the previous step. - * @property {ArrayCollisionMatrix} collisionMatrixPrevious - * @type {ArrayCollisionMatrix} - */ - this.collisionMatrixPrevious = new ArrayCollisionMatrix(); - - /** - * All added materials - * @property materials - * @type {Array} - */ - this.materials = []; - - /** - * @property contactmaterials - * @type {Array} - */ - this.contactmaterials = []; - - /** - * Used to look up a ContactMaterial given two instances of Material. - * @property {TupleDictionary} contactMaterialTable - */ - this.contactMaterialTable = new TupleDictionary(); - - this.defaultMaterial = new Material("default"); - - /** - * This contact material is used if no suitable contactmaterial is found for a contact. - * @property defaultContactMaterial - * @type {ContactMaterial} - */ - this.defaultContactMaterial = new ContactMaterial(this.defaultMaterial, this.defaultMaterial, { friction: 0.3, restitution: 0.0 }); - - /** - * @property doProfiling - * @type {Boolean} - */ - this.doProfiling = false; - - /** - * @property profile - * @type {Object} - */ - this.profile = { - solve:0, - makeContactConstraints:0, - broadphase:0, - integrate:0, - narrowphase:0, - }; - - /** - * @property subsystems - * @type {Array} - */ - this.subsystems = []; - - this.addBodyEvent = { - type:"addBody", - body : null, - }; - - this.removeBodyEvent = { - type:"removeBody", - body : null, - }; -} -World.prototype = new EventTarget(); - -// Temp stuff -var tmpAABB1 = new AABB(); -var tmpArray1 = []; -var tmpRay = new Ray(); - -/** - * Get the contact material between materials m1 and m2 - * @method getContactMaterial - * @param {Material} m1 - * @param {Material} m2 - * @return {ContactMaterial} The contact material if it was found. - */ -World.prototype.getContactMaterial = function(m1,m2){ - return this.contactMaterialTable.get(m1.id,m2.id); //this.contactmaterials[this.mats2cmat[i+j*this.materials.length]]; -}; - -/** - * Get number of objects in the world. - * @method numObjects - * @return {Number} - * @deprecated - */ -World.prototype.numObjects = function(){ - return this.bodies.length; -}; - -/** - * Store old collision state info - * @method collisionMatrixTick - */ -World.prototype.collisionMatrixTick = function(){ - var temp = this.collisionMatrixPrevious; - this.collisionMatrixPrevious = this.collisionMatrix; - this.collisionMatrix = temp; - this.collisionMatrix.reset(); -}; - -/** - * Add a rigid body to the simulation. - * @method add - * @param {Body} body - * @todo If the simulation has not yet started, why recrete and copy arrays for each body? Accumulate in dynamic arrays in this case. - * @todo Adding an array of bodies should be possible. This would save some loops too - * @deprecated Use .addBody instead - */ -World.prototype.add = World.prototype.addBody = function(body){ - if(this.bodies.indexOf(body) !== -1){ - return; - } - body.index = this.bodies.length; - this.bodies.push(body); - body.world = this; - body.initPosition.copy(body.position); - body.initVelocity.copy(body.velocity); - body.timeLastSleepy = this.time; - if(body instanceof Body){ - body.initAngularVelocity.copy(body.angularVelocity); - body.initQuaternion.copy(body.quaternion); - } - this.collisionMatrix.setNumObjects(this.bodies.length); - this.addBodyEvent.body = body; - this.dispatchEvent(this.addBodyEvent); -}; - -/** - * Add a constraint to the simulation. - * @method addConstraint - * @param {Constraint} c - */ -World.prototype.addConstraint = function(c){ - this.constraints.push(c); -}; - -/** - * Removes a constraint - * @method removeConstraint - * @param {Constraint} c - */ -World.prototype.removeConstraint = function(c){ - var idx = this.constraints.indexOf(c); - if(idx!==-1){ - this.constraints.splice(idx,1); - } -}; - -/** - * Raycast test - * @method rayTest - * @param {Vec3} from - * @param {Vec3} to - * @param {Function|RaycastResult} result - * @deprecated Use .raycastAll, .raycastClosest or .raycastAny instead. - */ -World.prototype.rayTest = function(from, to, result){ - if(result instanceof RaycastResult){ - // Do raycastclosest - this.raycastClosest(from, to, { - skipBackfaces: true - }, result); - } else { - // Do raycastAll - this.raycastAll(from, to, { - skipBackfaces: true - }, result); - } -}; - -/** - * Ray cast against all bodies. The provided callback will be executed for each hit with a RaycastResult as single argument. - * @method raycastAll - * @param {Vec3} from - * @param {Vec3} to - * @param {Object} options - * @param {number} [options.collisionFilterMask=-1] - * @param {number} [options.collisionFilterGroup=-1] - * @param {boolean} [options.skipBackfaces=false] - * @param {boolean} [options.checkCollisionResponse=true] - * @param {Function} callback - * @return {boolean} True if any body was hit. - */ -World.prototype.raycastAll = function(from, to, options, callback){ - options.mode = Ray.ALL; - options.from = from; - options.to = to; - options.callback = callback; - return tmpRay.intersectWorld(this, options); -}; - -/** - * Ray cast, and stop at the first result. Note that the order is random - but the method is fast. - * @method raycastAny - * @param {Vec3} from - * @param {Vec3} to - * @param {Object} options - * @param {number} [options.collisionFilterMask=-1] - * @param {number} [options.collisionFilterGroup=-1] - * @param {boolean} [options.skipBackfaces=false] - * @param {boolean} [options.checkCollisionResponse=true] - * @param {RaycastResult} result - * @return {boolean} True if any body was hit. - */ -World.prototype.raycastAny = function(from, to, options, result){ - options.mode = Ray.ANY; - options.from = from; - options.to = to; - options.result = result; - return tmpRay.intersectWorld(this, options); -}; - -/** - * Ray cast, and return information of the closest hit. - * @method raycastClosest - * @param {Vec3} from - * @param {Vec3} to - * @param {Object} options - * @param {number} [options.collisionFilterMask=-1] - * @param {number} [options.collisionFilterGroup=-1] - * @param {boolean} [options.skipBackfaces=false] - * @param {boolean} [options.checkCollisionResponse=true] - * @param {RaycastResult} result - * @return {boolean} True if any body was hit. - */ -World.prototype.raycastClosest = function(from, to, options, result){ - options.mode = Ray.CLOSEST; - options.from = from; - options.to = to; - options.result = result; - return tmpRay.intersectWorld(this, options); -}; - -/** - * Remove a rigid body from the simulation. - * @method remove - * @param {Body} body - * @deprecated Use .removeBody instead - */ -World.prototype.remove = function(body){ - body.world = null; - var n = this.bodies.length-1, - bodies = this.bodies, - idx = bodies.indexOf(body); - if(idx !== -1){ - bodies.splice(idx, 1); // Todo: should use a garbage free method - - // Recompute index - for(var i=0; i!==bodies.length; i++){ - bodies[i].index = i; - } - - this.collisionMatrix.setNumObjects(n); - this.removeBodyEvent.body = body; - this.dispatchEvent(this.removeBodyEvent); - } -}; - -/** - * Remove a rigid body from the simulation. - * @method removeBody - * @param {Body} body - */ -World.prototype.removeBody = World.prototype.remove; - -/** - * Adds a material to the World. - * @method addMaterial - * @param {Material} m - * @todo Necessary? - */ -World.prototype.addMaterial = function(m){ - this.materials.push(m); -}; - -/** - * Adds a contact material to the World - * @method addContactMaterial - * @param {ContactMaterial} cmat - */ -World.prototype.addContactMaterial = function(cmat) { - - // Add contact material - this.contactmaterials.push(cmat); - - // Add current contact material to the material table - this.contactMaterialTable.set(cmat.materials[0].id,cmat.materials[1].id,cmat); -}; - -// performance.now() -if(typeof performance === 'undefined'){ - performance = {}; -} -if(!performance.now){ - var nowOffset = Date.now(); - if (performance.timing && performance.timing.navigationStart){ - nowOffset = performance.timing.navigationStart; - } - performance.now = function(){ - return Date.now() - nowOffset; - }; -} - -var step_tmp1 = new Vec3(); - -/** - * Step the physics world forward in time. - * - * There are two modes. The simple mode is fixed timestepping without interpolation. In this case you only use the first argument. The second case uses interpolation. In that you also provide the time since the function was last used, as well as the maximum fixed timesteps to take. - * - * @method step - * @param {Number} dt The fixed time step size to use. - * @param {Number} [timeSinceLastCalled] The time elapsed since the function was last called. - * @param {Number} [maxSubSteps=10] Maximum number of fixed steps to take per function call. - * - * @example - * // fixed timestepping without interpolation - * world.step(1/60); - * - * @see http://bulletphysics.org/mediawiki-1.5.8/index.php/Stepping_The_World - */ -World.prototype.step = function(dt, timeSinceLastCalled, maxSubSteps){ - maxSubSteps = maxSubSteps || 10; - timeSinceLastCalled = timeSinceLastCalled || 0; - - if(timeSinceLastCalled === 0){ // Fixed, simple stepping - - this.internalStep(dt); - - // Increment time - this.time += dt; - - } else { - - // Compute the number of fixed steps we should have taken since the last step - var internalSteps = Math.floor((this.time + timeSinceLastCalled) / dt) - Math.floor(this.time / dt); - internalSteps = Math.min(internalSteps,maxSubSteps); - - // Do some fixed steps to catch up - var t0 = performance.now(); - for(var i=0; i!==internalSteps; i++){ - this.internalStep(dt); - if(performance.now() - t0 > dt * 1000){ - // We are slower than real-time. Better bail out. - break; - } - } - - // Increment internal clock - this.time += timeSinceLastCalled; - - // Compute "Left over" time step - var h = this.time % dt; - var h_div_dt = h / dt; - var interpvelo = step_tmp1; - var bodies = this.bodies; - - for(var j=0; j !== bodies.length; j++){ - var b = bodies[j]; - if(b.type !== Body.STATIC && b.sleepState !== Body.SLEEPING){ - - // Interpolate - b.position.vsub(b.previousPosition, interpvelo); - interpvelo.scale(h_div_dt, interpvelo); - b.position.vadd(interpvelo, b.interpolatedPosition); - - // TODO: interpolate quaternion - // b.interpolatedAngle = b.angle + (b.angle - b.previousAngle) * h_div_dt; - - } else { - - // For static bodies, just copy. Who else will do it? - b.interpolatedPosition.copy(b.position); - b.interpolatedQuaternion.copy(b.quaternion); - } - } - } -}; - -/** - * Step the simulation - * @method step - * @param {Number} dt - */ -var World_step_postStepEvent = {type:"postStep"}, // Reusable event objects to save memory - World_step_preStepEvent = {type:"preStep"}, - World_step_collideEvent = {type:"collide", body:null, contact:null }, - World_step_oldContacts = [], // Pools for unused objects - World_step_frictionEquationPool = [], - World_step_p1 = [], // Reusable arrays for collision pairs - World_step_p2 = [], - World_step_gvec = new Vec3(), // Temporary vectors and quats - World_step_vi = new Vec3(), - World_step_vj = new Vec3(), - World_step_wi = new Vec3(), - World_step_wj = new Vec3(), - World_step_t1 = new Vec3(), - World_step_t2 = new Vec3(), - World_step_rixn = new Vec3(), - World_step_rjxn = new Vec3(), - World_step_step_q = new Quaternion(), - World_step_step_w = new Quaternion(), - World_step_step_wq = new Quaternion(), - invI_tau_dt = new Vec3(); -World.prototype.internalStep = function(dt){ - this.dt = dt; - - var world = this, - that = this, - contacts = this.contacts, - p1 = World_step_p1, - p2 = World_step_p2, - N = this.numObjects(), - bodies = this.bodies, - solver = this.solver, - gravity = this.gravity, - doProfiling = this.doProfiling, - profile = this.profile, - DYNAMIC = Body.DYNAMIC, - profilingStart, - constraints = this.constraints, - frictionEquationPool = World_step_frictionEquationPool, - gnorm = gravity.norm(), - gx = gravity.x, - gy = gravity.y, - gz = gravity.z, - i=0; - - if(doProfiling){ - profilingStart = performance.now(); - } - - // Add gravity to all objects - for(i=0; i!==N; i++){ - var bi = bodies[i]; - if(bi.type & DYNAMIC){ // Only for dynamic bodies - var f = bi.force, m = bi.mass; - f.x += m*gx; - f.y += m*gy; - f.z += m*gz; - } - } - - // Update subsystems - for(var i=0, Nsubsystems=this.subsystems.length; i!==Nsubsystems; i++){ - this.subsystems[i].update(); - } - - // Collision detection - if(doProfiling){ profilingStart = performance.now(); } - p1.length = 0; // Clean up pair arrays from last step - p2.length = 0; - this.broadphase.collisionPairs(this,p1,p2); - if(doProfiling){ profile.broadphase = performance.now() - profilingStart; } - - // Remove constrained pairs with collideConnected == false - var Nconstraints = constraints.length; - for(i=0; i!==Nconstraints; i++){ - var c = constraints[i]; - if(!c.collideConnected){ - for(var j = p1.length-1; j>=0; j-=1){ - if( (c.bodyA === p1[j] && c.bodyB === p2[j]) || - (c.bodyB === p1[j] && c.bodyA === p2[j])){ - p1.splice(j, 1); - p2.splice(j, 1); - } - } - } - } - - this.collisionMatrixTick(); - - // Generate contacts - if(doProfiling){ profilingStart = performance.now(); } - var oldcontacts = World_step_oldContacts; - var NoldContacts = contacts.length; - - for(i=0; i!==NoldContacts; i++){ - oldcontacts.push(contacts[i]); - } - contacts.length = 0; - - // Transfer FrictionEquation from current list to the pool for reuse - var NoldFrictionEquations = this.frictionEquations.length; - for(i=0; i!==NoldFrictionEquations; i++){ - frictionEquationPool.push(this.frictionEquations[i]); - } - this.frictionEquations.length = 0; - - this.narrowphase.getContacts( - p1, - p2, - this, - contacts, - oldcontacts, // To be reused - this.frictionEquations, - frictionEquationPool - ); - - if(doProfiling){ - profile.narrowphase = performance.now() - profilingStart; - } - - // Loop over all collisions - if(doProfiling){ - profilingStart = performance.now(); - } - - // Add all friction eqs - for (var i = 0; i < this.frictionEquations.length; i++) { - solver.addEquation(this.frictionEquations[i]); - } - - var ncontacts = contacts.length; - for(var k=0; k!==ncontacts; k++){ - - // Current contact - var c = contacts[k]; - - // Get current collision indeces - var bi = c.bi, - bj = c.bj, - si = c.si, - sj = c.sj; - - // Get collision properties - var cm; - if(bi.material && bj.material){ - cm = this.getContactMaterial(bi.material,bj.material) || this.defaultContactMaterial; - } else { - cm = this.defaultContactMaterial; - } - - // c.enabled = bi.collisionResponse && bj.collisionResponse && si.collisionResponse && sj.collisionResponse; - - var mu = cm.friction; - // c.restitution = cm.restitution; - - // If friction or restitution were specified in the material, use them - if(bi.material && bj.material){ - if(bi.material.friction >= 0 && bj.material.friction >= 0){ - mu = bi.material.friction * bj.material.friction; - } - - if(bi.material.restitution >= 0 && bj.material.restitution >= 0){ - c.restitution = bi.material.restitution * bj.material.restitution; - } - } - - // c.setSpookParams( - // cm.contactEquationStiffness, - // cm.contactEquationRelaxation, - // dt - // ); - - solver.addEquation(c); - - // // Add friction constraint equation - // if(mu > 0){ - - // // Create 2 tangent equations - // var mug = mu * gnorm; - // var reducedMass = (bi.invMass + bj.invMass); - // if(reducedMass > 0){ - // reducedMass = 1/reducedMass; - // } - // var pool = frictionEquationPool; - // var c1 = pool.length ? pool.pop() : new FrictionEquation(bi,bj,mug*reducedMass); - // var c2 = pool.length ? pool.pop() : new FrictionEquation(bi,bj,mug*reducedMass); - // this.frictionEquations.push(c1, c2); - - // c1.bi = c2.bi = bi; - // c1.bj = c2.bj = bj; - // c1.minForce = c2.minForce = -mug*reducedMass; - // c1.maxForce = c2.maxForce = mug*reducedMass; - - // // Copy over the relative vectors - // c1.ri.copy(c.ri); - // c1.rj.copy(c.rj); - // c2.ri.copy(c.ri); - // c2.rj.copy(c.rj); - - // // Construct tangents - // c.ni.tangents(c1.t, c2.t); - - // // Set spook params - // c1.setSpookParams(cm.frictionEquationStiffness, cm.frictionEquationRelaxation, dt); - // c2.setSpookParams(cm.frictionEquationStiffness, cm.frictionEquationRelaxation, dt); - - // c1.enabled = c2.enabled = c.enabled; - - // // Add equations to solver - // solver.addEquation(c1); - // solver.addEquation(c2); - // } - - if( bi.allowSleep && - bi.type === Body.DYNAMIC && - bi.sleepState === Body.SLEEPING && - bj.sleepState === Body.AWAKE && - bj.type !== Body.STATIC - ){ - var speedSquaredB = bj.velocity.norm2() + bj.angularVelocity.norm2(); - var speedLimitSquaredB = Math.pow(bj.sleepSpeedLimit,2); - if(speedSquaredB >= speedLimitSquaredB*2){ - bi._wakeUpAfterNarrowphase = true; - } - } - - if( bj.allowSleep && - bj.type === Body.DYNAMIC && - bj.sleepState === Body.SLEEPING && - bi.sleepState === Body.AWAKE && - bi.type !== Body.STATIC - ){ - var speedSquaredA = bi.velocity.norm2() + bi.angularVelocity.norm2(); - var speedLimitSquaredA = Math.pow(bi.sleepSpeedLimit,2); - if(speedSquaredA >= speedLimitSquaredA*2){ - bj._wakeUpAfterNarrowphase = true; - } - } - - // Now we know that i and j are in contact. Set collision matrix state - this.collisionMatrix.set(bi, bj, true); - - if (!this.collisionMatrixPrevious.get(bi, bj)) { - // First contact! - // We reuse the collideEvent object, otherwise we will end up creating new objects for each new contact, even if there's no event listener attached. - World_step_collideEvent.body = bj; - World_step_collideEvent.contact = c; - bi.dispatchEvent(World_step_collideEvent); - - World_step_collideEvent.body = bi; - bj.dispatchEvent(World_step_collideEvent); - } - } - if(doProfiling){ - profile.makeContactConstraints = performance.now() - profilingStart; - profilingStart = performance.now(); - } - - // Wake up bodies - for(i=0; i!==N; i++){ - var bi = bodies[i]; - if(bi._wakeUpAfterNarrowphase){ - bi.wakeUp(); - bi._wakeUpAfterNarrowphase = false; - } - } - - // Add user-added constraints - var Nconstraints = constraints.length; - for(i=0; i!==Nconstraints; i++){ - var c = constraints[i]; - c.update(); - for(var j=0, Neq=c.equations.length; j!==Neq; j++){ - var eq = c.equations[j]; - solver.addEquation(eq); - } - } - - // Solve the constrained system - solver.solve(dt,this); - - if(doProfiling){ - profile.solve = performance.now() - profilingStart; - } - - // Remove all contacts from solver - solver.removeAllEquations(); - - // Apply damping, see http://code.google.com/p/bullet/issues/detail?id=74 for details - var pow = Math.pow; - for(i=0; i!==N; i++){ - var bi = bodies[i]; - if(bi.type & DYNAMIC){ // Only for dynamic bodies - var ld = pow(1.0 - bi.linearDamping,dt); - var v = bi.velocity; - v.mult(ld,v); - var av = bi.angularVelocity; - if(av){ - var ad = pow(1.0 - bi.angularDamping,dt); - av.mult(ad,av); - } - } - } - - this.dispatchEvent(World_step_preStepEvent); - - // Invoke pre-step callbacks - for(i=0; i!==N; i++){ - var bi = bodies[i]; - if(bi.preStep){ - bi.preStep.call(bi); - } - } - - // Leap frog - // vnew = v + h*f/m - // xnew = x + h*vnew - if(doProfiling){ - profilingStart = performance.now(); - } - var q = World_step_step_q; - var w = World_step_step_w; - var wq = World_step_step_wq; - var stepnumber = this.stepnumber; - var DYNAMIC_OR_KINEMATIC = Body.DYNAMIC | Body.KINEMATIC; - var quatNormalize = stepnumber % (this.quatNormalizeSkip+1) === 0; - var quatNormalizeFast = this.quatNormalizeFast; - var half_dt = dt * 0.5; - var PLANE = Shape.types.PLANE, - CONVEX = Shape.types.CONVEXPOLYHEDRON; - - for(i=0; i!==N; i++){ - var b = bodies[i], - force = b.force, - tau = b.torque; - if((b.type & DYNAMIC_OR_KINEMATIC) && b.sleepState !== Body.SLEEPING){ // Only for dynamic - var velo = b.velocity, - angularVelo = b.angularVelocity, - pos = b.position, - quat = b.quaternion, - invMass = b.invMass, - invInertia = b.invInertiaWorld; - - velo.x += force.x * invMass * dt; - velo.y += force.y * invMass * dt; - velo.z += force.z * invMass * dt; - - if(b.angularVelocity){ - invInertia.vmult(tau,invI_tau_dt); - invI_tau_dt.mult(dt,invI_tau_dt); - invI_tau_dt.vadd(angularVelo,angularVelo); - } - - // Use new velocity - leap frog - pos.x += velo.x * dt; - pos.y += velo.y * dt; - pos.z += velo.z * dt; - - if(b.angularVelocity){ - w.set(angularVelo.x, angularVelo.y, angularVelo.z, 0); - w.mult(quat,wq); - quat.x += half_dt * wq.x; - quat.y += half_dt * wq.y; - quat.z += half_dt * wq.z; - quat.w += half_dt * wq.w; - if(quatNormalize){ - if(quatNormalizeFast){ - quat.normalizeFast(); - } else { - quat.normalize(); - } - } - } - - if(b.aabb){ - b.aabbNeedsUpdate = true; - } - - // Update world inertia - if(b.updateInertiaWorld){ - b.updateInertiaWorld(); - } - } - } - this.clearForces(); - - this.broadphase.dirty = true; - - if(doProfiling){ - profile.integrate = performance.now() - profilingStart; - } - - // Update world time - this.time += dt; - this.stepnumber += 1; - - this.dispatchEvent(World_step_postStepEvent); - - // Invoke post-step callbacks - for(i=0; i!==N; i++){ - var bi = bodies[i]; - var postStep = bi.postStep; - if(postStep){ - postStep.call(bi); - } - } - - // Sleeping update - if(this.allowSleep){ - for(i=0; i!==N; i++){ - bodies[i].sleepTick(this.time); - } - } -}; - -/** - * Sets all body forces in the world to zero. - * @method clearForces - */ -World.prototype.clearForces = function(){ - var bodies = this.bodies; - var N = bodies.length; - for(var i=0; i !== N; i++){ - var b = bodies[i], - force = b.force, - tau = b.torque; - - b.force.set(0,0,0); - b.torque.set(0,0,0); - } -}; - -},{"../collision/AABB":3,"../collision/ArrayCollisionMatrix":4,"../collision/NaiveBroadphase":7,"../collision/Ray":9,"../collision/RaycastResult":10,"../equations/ContactEquation":19,"../equations/FrictionEquation":21,"../material/ContactMaterial":24,"../material/Material":25,"../math/Quaternion":28,"../math/Vec3":30,"../objects/Body":31,"../shapes/Shape":43,"../solver/GSSolver":46,"../utils/EventTarget":49,"../utils/TupleDictionary":52,"../utils/Vec3Pool":54,"./Narrowphase":55}]},{},[2]) -(2) -}); \ No newline at end of file diff --git a/build/cannon.min.js b/build/cannon.min.js deleted file mode 100644 index 01a53bc6d..000000000 --- a/build/cannon.min.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2015 cannon.js Authors - * - * 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 above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 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. - */ - -!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&false)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.CANNON=e()}}(function(){return function e(f,n,o){function d(t,l){if(!n[t]){if(!f[t]){var u="function"==typeof require&&require;if(!l&&u)return u(t,!0);if(i)return i(t,!0);throw new Error("Cannot find module '"+t+"'")}var p=n[t]={exports:{}};f[t][0].call(p.exports,function(e){var n=f[t][1][e];return d(n?n:e)},p,p.exports,e,f,n,o)}return n[t].exports}for(var i="function"==typeof require&&require,t=0;t (http://steffe.se)",keywords:["cannon.js","cannon","physics","engine","3d"],main:"./build/cannon.js",engines:{node:"*"},repository:{type:"git",url:"https://github.com/schteppe/cannon.js.git"},bugs:{url:"https://github.com/schteppe/cannon.js/issues"},licenses:[{type:"MIT"}],devDependencies:{jshint:"latest","uglify-js":"latest",nodeunit:"^0.9.0",grunt:"~0.4.0","grunt-contrib-jshint":"~0.1.1","grunt-contrib-nodeunit":"^0.4.1","grunt-contrib-concat":"~0.1.3","grunt-contrib-uglify":"^0.5.1","grunt-browserify":"^2.1.4","grunt-contrib-yuidoc":"^0.5.2",browserify:"*"},dependencies:{}}},{}],2:[function(e,f){f.exports={version:e("../package.json").version,AABB:e("./collision/AABB"),ArrayCollisionMatrix:e("./collision/ArrayCollisionMatrix"),Body:e("./objects/Body"),Box:e("./shapes/Box"),Broadphase:e("./collision/Broadphase"),Constraint:e("./constraints/Constraint"),ContactEquation:e("./equations/ContactEquation"),Narrowphase:e("./world/Narrowphase"),ConeTwistConstraint:e("./constraints/ConeTwistConstraint"),ContactMaterial:e("./material/ContactMaterial"),ConvexPolyhedron:e("./shapes/ConvexPolyhedron"),Cylinder:e("./shapes/Cylinder"),DistanceConstraint:e("./constraints/DistanceConstraint"),Equation:e("./equations/Equation"),EventTarget:e("./utils/EventTarget"),FrictionEquation:e("./equations/FrictionEquation"),GSSolver:e("./solver/GSSolver"),GridBroadphase:e("./collision/GridBroadphase"),Heightfield:e("./shapes/Heightfield"),HingeConstraint:e("./constraints/HingeConstraint"),LockConstraint:e("./constraints/LockConstraint"),Mat3:e("./math/Mat3"),Material:e("./material/Material"),NaiveBroadphase:e("./collision/NaiveBroadphase"),ObjectCollisionMatrix:e("./collision/ObjectCollisionMatrix"),Pool:e("./utils/Pool"),Particle:e("./shapes/Particle"),Plane:e("./shapes/Plane"),PointToPointConstraint:e("./constraints/PointToPointConstraint"),Quaternion:e("./math/Quaternion"),Ray:e("./collision/Ray"),RaycastVehicle:e("./objects/RaycastVehicle"),RaycastResult:e("./collision/RaycastResult"),RigidVehicle:e("./objects/RigidVehicle"),RotationalEquation:e("./equations/RotationalEquation"),RotationalMotorEquation:e("./equations/RotationalMotorEquation"),SAPBroadphase:e("./collision/SAPBroadphase"),SPHSystem:e("./objects/SPHSystem"),Shape:e("./shapes/Shape"),Solver:e("./solver/Solver"),Sphere:e("./shapes/Sphere"),SplitSolver:e("./solver/SplitSolver"),Spring:e("./objects/Spring"),Trimesh:e("./shapes/Trimesh"),Vec3:e("./math/Vec3"),Vec3Pool:e("./utils/Vec3Pool"),World:e("./world/World")}},{"../package.json":1,"./collision/AABB":3,"./collision/ArrayCollisionMatrix":4,"./collision/Broadphase":5,"./collision/GridBroadphase":6,"./collision/NaiveBroadphase":7,"./collision/ObjectCollisionMatrix":8,"./collision/Ray":9,"./collision/RaycastResult":10,"./collision/SAPBroadphase":11,"./constraints/ConeTwistConstraint":12,"./constraints/Constraint":13,"./constraints/DistanceConstraint":14,"./constraints/HingeConstraint":15,"./constraints/LockConstraint":16,"./constraints/PointToPointConstraint":17,"./equations/ContactEquation":19,"./equations/Equation":20,"./equations/FrictionEquation":21,"./equations/RotationalEquation":22,"./equations/RotationalMotorEquation":23,"./material/ContactMaterial":24,"./material/Material":25,"./math/Mat3":27,"./math/Quaternion":28,"./math/Vec3":30,"./objects/Body":31,"./objects/RaycastVehicle":32,"./objects/RigidVehicle":33,"./objects/SPHSystem":34,"./objects/Spring":35,"./shapes/Box":37,"./shapes/ConvexPolyhedron":38,"./shapes/Cylinder":39,"./shapes/Heightfield":40,"./shapes/Particle":41,"./shapes/Plane":42,"./shapes/Shape":43,"./shapes/Sphere":44,"./shapes/Trimesh":45,"./solver/GSSolver":46,"./solver/Solver":47,"./solver/SplitSolver":48,"./utils/EventTarget":49,"./utils/Pool":51,"./utils/Vec3Pool":54,"./world/Narrowphase":55,"./world/World":56}],3:[function(e,f){function n(e){e=e||{},this.lowerBound=new o,e.lowerBound&&this.lowerBound.copy(e.lowerBound),this.upperBound=new o,e.upperBound&&this.upperBound.copy(e.upperBound)}{var o=e("../math/Vec3");e("../utils/Utils")}f.exports=n;var d=new o;n.prototype.setFromPoints=function(e,f,n,o){var i=this.lowerBound,t=this.upperBound,l=n;i.copy(e[0]),l&&l.vmult(i,i),t.copy(i);for(var u=1;ut.x&&(t.x=p.x),p.xt.y&&(t.y=p.y),p.yt.z&&(t.z=p.z),p.zf&&(this.lowerBound.x=f);var n=e.upperBound.x;this.upperBound.xf&&(this.lowerBound.y=f);var n=e.upperBound.y;this.upperBound.yf&&(this.lowerBound.z=f);var n=e.upperBound.z;this.upperBound.z=d.x&&f.y<=o.y&&n.y>=d.y&&f.z<=o.z&&n.z>=d.z},n.prototype.getCorners=function(e,f,n,o,d,i,t,l){var u=this.lowerBound,p=this.upperBound;e.copy(u),f.set(p.x,u.y,u.z),n.set(p.x,p.y,u.z),o.set(u.x,p.y,p.z),d.set(p.x,u.y,u.z),i.set(u.x,p.y,u.z),t.set(u.x,u.y,p.z),l.copy(p)};var i=[new o,new o,new o,new o,new o,new o,new o,new o];n.prototype.toLocalFrame=function(e,f){var n=i,o=n[0],d=n[1],t=n[2],l=n[3],u=n[4],p=n[5],s=n[6],y=n[7];this.getCorners(o,d,t,l,u,p,s,y);for(var c=0;8!==c;c++){var a=n[c];e.pointToLocal(a,a)}return f.setFromPoints(n)},n.prototype.toWorldFrame=function(e,f){var n=i,o=n[0],d=n[1],t=n[2],l=n[3],u=n[4],p=n[5],s=n[6],y=n[7];this.getCorners(o,d,t,l,u,p,s,y);for(var c=0;8!==c;c++){var a=n[c];e.pointToWorld(a,a)}return f.setFromPoints(n)}},{"../math/Vec3":30,"../utils/Utils":53}],4:[function(e,f){function n(){this.matrix=[]}f.exports=n,n.prototype.get=function(e,f){if(e=e.index,f=f.index,f>e){var n=f;f=e,e=n}return this.matrix[(e*(e+1)>>1)+f-1]},n.prototype.set=function(e,f,n){if(e=e.index,f=f.index,f>e){var o=f;f=e,e=o}this.matrix[(e*(e+1)>>1)+f-1]=n?1:0},n.prototype.reset=function(){for(var e=0,f=this.matrix.length;e!==f;e++)this.matrix[e]=0},n.prototype.setNumObjects=function(e){this.matrix.length=e*(e-1)>>1}},{}],5:[function(e,f){function n(){this.world=null,this.useBoundingBoxes=!1,this.dirty=!0}{var o=e("../objects/Body"),d=e("../math/Vec3"),i=e("../math/Quaternion");e("../shapes/Shape"),e("../shapes/Plane")}f.exports=n,n.prototype.collisionPairs=function(){throw new Error("collisionPairs not implemented for this BroadPhase class!")};var t=o.STATIC|o.KINEMATIC;n.prototype.needBroadphaseCollision=function(e,f){return 0===(e.collisionFilterGroup&f.collisionFilterMask)||0===(f.collisionFilterGroup&e.collisionFilterMask)?!1:0===(e.type&t)&&e.sleepState!==o.SLEEPING||0===(f.type&t)&&f.sleepState!==o.SLEEPING?!0:!1},n.prototype.intersectionTest=function(e,f,n,o){this.useBoundingBoxes?this.doBoundingBoxBroadphase(e,f,n,o):this.doBoundingSphereBroadphase(e,f,n,o)};{var l=new d;new d,new i,new d}n.prototype.doBoundingSphereBroadphase=function(e,f,n,o){var d=l;f.position.vsub(e.position,d);var i=Math.pow(e.boundingRadius+f.boundingRadius,2),t=d.norm2();i>t&&(n.push(e),o.push(f))},n.prototype.doBoundingBoxBroadphase=function(e,f,n,o){e.aabbNeedsUpdate&&e.computeAABB(),f.aabbNeedsUpdate&&f.computeAABB(),e.aabb.overlaps(f.aabb)&&(n.push(e),o.push(f))};var u={keys:[]},p=[],s=[];n.prototype.makePairsUnique=function(e,f){for(var n=u,o=p,d=s,i=e.length,t=0;t!==i;t++)o[t]=e[t],d[t]=f[t];e.length=0,f.length=0;for(var t=0;t!==i;t++){var l=o[t].id,y=d[t].id,c=y>l?l+","+y:y+","+l;n[c]=t,n.keys.push(c)}for(var t=0;t!==n.keys.length;t++){var c=n.keys.pop(),a=n[c];e.push(o[a]),f.push(d[a]),delete n[c]}},n.prototype.setWorld=function(){};var y=new d;n.boundingSphereCheck=function(e,f){var n=y;return e.position.vsub(f.position,n),Math.pow(e.shape.boundingSphereRadius+f.shape.boundingSphereRadius,2)>n.norm2()},n.prototype.aabbQuery=function(){return console.warn(".aabbQuery is not implemented in this Broadphase subclass."),[]}},{"../math/Quaternion":28,"../math/Vec3":30,"../objects/Body":31,"../shapes/Plane":42,"../shapes/Shape":43}],6:[function(e,f){function n(e,f,n,i,t){o.apply(this),this.nx=n||10,this.ny=i||10,this.nz=t||10,this.aabbMin=e||new d(100,100,100),this.aabbMax=f||new d(-100,-100,-100);var l=this.nx*this.ny*this.nz;if(0>=l)throw"GridBroadphase: Each dimension's n must be >0";this.bins=[],this.binLengths=[],this.bins.length=l,this.binLengths.length=l;for(var u=0;l>u;u++)this.bins[u]=[],this.binLengths[u]=0}f.exports=n;var o=e("./Broadphase"),d=e("../math/Vec3"),i=e("../shapes/Shape");n.prototype=new o,n.prototype.constructor=n;{var t=new d;new d}n.prototype.collisionPairs=function(e,f,n){function o(e,f,n,o,d,i,t){var l=(e-g)*v|0,u=(f-x)*A|0,p=(n-j)*C|0,b=I((o-g)*v),m=I((d-x)*A),N=I((i-j)*C);0>l?l=0:l>=s&&(l=s-1),0>u?u=0:u>=y&&(u=y-1),0>p?p=0:p>=c&&(p=c-1),0>b?b=0:b>=s&&(b=s-1),0>m?m=0:m>=y&&(m=y-1),0>N?N=0:N>=c&&(N=c-1),l*=a,u*=r,p*=w,b*=a,m*=r,N*=w;for(var O=l;b>=O;O+=a)for(var h=u;m>=h;h+=r)for(var k=p;N>=k;k+=w){var q=O+h+k;E[q][F[q]++]=t}}for(var d=e.numObjects(),l=e.bodies,u=this.aabbMax,p=this.aabbMin,s=this.nx,y=this.ny,c=this.nz,a=y*c,r=c,w=1,b=u.x,m=u.y,N=u.z,g=p.x,x=p.y,j=p.z,v=s/(b-g),A=y/(m-x),C=c/(N-j),O=(b-g)/s,h=(m-x)/y,k=(N-j)/c,q=.5*Math.sqrt(O*O+h*h+k*k),z=i.types,B=z.SPHERE,D=z.PLANE,E=(z.BOX,z.COMPOUND,z.CONVEXPOLYHEDRON,this.bins),F=this.binLengths,G=this.bins.length,H=0;H!==G;H++)F[H]=0;for(var I=Math.ceil,p=Math.min,u=Math.max,H=0;H!==d;H++){var J=l[H],K=J.shape;switch(K.type){case B:var L=J.position.x,M=J.position.y,P=J.position.z,Q=K.radius;o(L-Q,M-Q,P-Q,L+Q,M+Q,P+Q,J);break;case D:K.worldNormalNeedsUpdate&&K.computeWorldNormal(J.quaternion);var R=K.worldNormal,S=g+.5*O-J.position.x,T=x+.5*h-J.position.y,U=j+.5*k-J.position.z,V=t;V.set(S,T,U);for(var W=0,X=0;W!==s;W++,X+=a,V.y=T,V.x+=O)for(var Y=0,Z=0;Y!==y;Y++,Z+=r,V.z=U,V.y+=h)for(var $=0,_=0;$!==c;$++,_+=w,V.z+=k)if(V.dot(R)1)for(var nf=E[H],W=0;W!==ff;W++)for(var J=nf[W],Y=0;Y!==W;Y++){var of=nf[Y];this.needBroadphaseCollision(J,of)&&this.intersectionTest(J,of,f,n)}}this.makePairsUnique(f,n)}},{"../math/Vec3":30,"../shapes/Shape":43,"./Broadphase":5}],7:[function(e,f){function n(){o.apply(this)}f.exports=n;var o=e("./Broadphase"),d=e("./AABB");n.prototype=new o,n.prototype.constructor=n,n.prototype.collisionPairs=function(e,f,n){var o,d,i,t,l=e.bodies,u=l.length;for(o=0;o!==u;o++)for(d=0;d!==o;d++)i=l[o],t=l[d],this.needBroadphaseCollision(i,t)&&this.intersectionTest(i,t,f,n)};new d;n.prototype.aabbQuery=function(e,f,n){n=n||[];for(var o=0;oe){var n=f;f=e,e=n}return e+"-"+f in this.matrix},n.prototype.set=function(e,f,n){if(e=e.id,f=f.id,f>e){var o=f;f=e,e=o}n?this.matrix[e+"-"+f]=!0:delete this.matrix[e+"-"+f]},n.prototype.reset=function(){this.matrix={}},n.prototype.setNumObjects=function(){}},{}],9:[function(e,f){function n(e,f){this.from=e?e.clone():new i,this.to=f?f.clone():new i,this._direction=new i,this.precision=1e-4,this.checkCollisionResponse=!0,this.skipBackfaces=!1,this.collisionFilterMask=-1,this.collisionFilterGroup=-1,this.mode=n.ANY,this.result=new u,this.hasHit=!1,this.callback=function(){}}function o(e,f,n,o){o.vsub(f,G),n.vsub(f,a),e.vsub(f,r);var d,i,t=G.dot(G),l=G.dot(a),u=G.dot(r),p=a.dot(a),s=a.dot(r);return(d=p*u-l*s)>=0&&(i=t*s-l*u)>=0&&t*p-l*l>d+i}function d(e,f,n){n.vsub(e,G);var o=G.dot(f);f.mult(o,H),H.vadd(e,H);var d=n.distanceTo(H);return d}f.exports=n;var i=e("../math/Vec3"),t=e("../math/Quaternion"),l=e("../math/Transform"),u=(e("../shapes/ConvexPolyhedron"),e("../shapes/Box"),e("../collision/RaycastResult")),p=e("../shapes/Shape"),s=e("../collision/AABB");n.prototype.constructor=n,n.CLOSEST=1,n.ANY=2,n.ALL=4;var y=new s,c=[];n.prototype.intersectWorld=function(e,f){return this.mode=f.mode||n.ANY,this.result=f.result||new u,this.skipBackfaces=!!f.skipBackfaces,this.collisionFilterMask="undefined"!=typeof f.collisionFilterMask?f.collisionFilterMask:-1,this.collisionFilterGroup="undefined"!=typeof f.collisionFilterGroup?f.collisionFilterGroup:-1,f.from&&this.from.copy(f.from),f.to&&this.to.copy(f.to),this.callback=f.callback||function(){},this.hasHit=!1,this.result.reset(),this._updateDirection(),this.getAABB(y),c.length=0,e.broadphase.aabbQuery(e,y,c),this.intersectBodies(c),this.hasHit};var a=new i,r=new i;n.pointInTriangle=o;var w=new i,b=new t;n.prototype.intersectBody=function(e,f){f&&(this.result=f,this._updateDirection());var n=this.checkCollisionResponse;if((!n||e.collisionResponse)&&0!==(this.collisionFilterGroup&e.collisionFilterMask)&&0!==(e.collisionFilterGroup&this.collisionFilterMask))for(var o=w,d=b,i=0,t=e.shapes.length;t>i;i++){var l=e.shapes[i];if((!n||l.collisionResponse)&&(e.quaternion.mult(e.shapeOrientations[i],d),e.quaternion.vmult(e.shapeOffsets[i],o),o.vadd(e.position,o),this.intersectShape(l,d,o,e),this.result._shouldStop))break}},n.prototype.intersectBodies=function(e,f){f&&(this.result=f,this._updateDirection());for(var n=0,o=e.length;!this.result._shouldStop&&o>n;n++)this.intersectBody(e[n])},n.prototype._updateDirection=function(){this.to.vsub(this.from,this._direction),this._direction.normalize()},n.prototype.intersectShape=function(e,f,n,o){var i=this.from,t=d(i,this._direction,n);if(!(t>e.boundingSphereRadius)){var l=this[e.type];l&&l.call(this,e,f,n,o)}};{var m=(new i,new i,new i),N=new i,g=new i,x=new i;new i,new u}n.prototype.intersectBox=function(e,f,n,o){return this.intersectConvex(e.convexPolyhedronRepresentation,f,n,o)},n.prototype[p.types.BOX]=n.prototype.intersectBox,n.prototype.intersectPlane=function(e,f,n,o){var d=this.from,t=this.to,l=this._direction,u=new i(0,0,1);f.vmult(u,u);var p=new i;d.vsub(n,p);var s=p.dot(u);t.vsub(n,p);var y=p.dot(u);if(!(s*y>0||d.distanceTo(t)c)&&(c=p[0]),(null===y||p[1]a)&&(a=p[1])),null!==s){var w=[];e.getRectMinMax(s,y,c,a,w);for(var b=(w[0],w[1],s);c>=b;b++)for(var m=y;a>=m;m++){if(this.result._shouldStop)return;if(e.getConvexTrianglePillar(b,m,!1),l.pointToWorldFrame(o,f,e.pillarOffset,t),this.intersectConvex(e.pillarConvex,f,t,d,j),this.result._shouldStop)return;e.getConvexTrianglePillar(b,m,!0),l.pointToWorldFrame(o,f,e.pillarOffset,t),this.intersectConvex(e.pillarConvex,f,t,d,j)}}},n.prototype[p.types.HEIGHTFIELD]=n.prototype.intersectHeightfield;var v=new i,A=new i;n.prototype.intersectSphere=function(e,f,n,o){var d=this.from,i=this.to,t=e.radius,l=Math.pow(i.x-d.x,2)+Math.pow(i.y-d.y,2)+Math.pow(i.z-d.z,2),u=2*((i.x-d.x)*(d.x-n.x)+(i.y-d.y)*(d.y-n.y)+(i.z-d.z)*(d.z-n.z)),p=Math.pow(d.x-n.x,2)+Math.pow(d.y-n.y,2)+Math.pow(d.z-n.z,2)-Math.pow(t,2),s=Math.pow(u,2)-4*l*p,y=v,c=A;if(!(0>s))if(0===s)d.lerp(i,s,y),y.vsub(n,c),c.normalize(),this.reportIntersection(c,y,e,o,-1);else{var a=(-u-Math.sqrt(s))/(2*l),r=(-u+Math.sqrt(s))/(2*l);if(a>=0&&1>=a&&(d.lerp(i,a,y),y.vsub(n,c),c.normalize(),this.reportIntersection(c,y,e,o,-1)),this.result._shouldStop)return;r>=0&&1>=r&&(d.lerp(i,r,y),y.vsub(n,c),c.normalize(),this.reportIntersection(c,y,e,o,-1))}},n.prototype[p.types.SPHERE]=n.prototype.intersectSphere;var C=new i,O=(new i,new i,new i);n.prototype.intersectConvex=function(e,f,n,d,i){for(var t=C,l=O,u=i&&i.faceList||null,p=e.faces,s=e.vertices,y=e.faceNormals,c=this._direction,a=this.from,r=this.to,w=a.distanceTo(r),b=u?u.length:p.length,j=this.result,v=0;!j._shouldStop&&b>v;v++){var A=u?u[v]:v,h=p[A],k=y[A],q=f,z=n;l.copy(s[h[0]]),q.vmult(l,l),l.vadd(z,l),l.vsub(a,l),q.vmult(k,t);var B=c.dot(t);if(!(Math.abs(B)D)){c.mult(D,m),m.vadd(a,m),N.copy(s[h[0]]),q.vmult(N,N),z.vadd(N,N);for(var E=1;!j._shouldStop&&Ew||this.reportIntersection(t,m,e,d,A)}}}}},n.prototype[p.types.CONVEXPOLYHEDRON]=n.prototype.intersectConvex;var h=new i,k=new i,q=new i,z=new i,B=new i,D=new i,E=(new s,[]),F=new l;n.prototype.intersectTrimesh=function(e,f,n,d,i){var t=h,u=E,p=F,s=O,y=k,c=q,a=z,r=D,w=B,b=(i&&i.faceList||null,e.indices),j=(e.vertices,e.faceNormals,this.from),v=this.to,A=this._direction;p.position.copy(n),p.quaternion.copy(f),l.vectorToLocalFrame(n,f,A,y),l.pointToLocalFrame(n,f,j,c),l.pointToLocalFrame(n,f,v,a);var C=c.distanceSquared(a);e.tree.rayQuery(this,p,u);for(var G=0,H=u.length;!this.result._shouldStop&&G!==H;G++){var I=u[G];e.getNormal(I,t),e.getVertex(b[3*I],N),N.vsub(c,s);var J=y.dot(t),K=t.dot(s)/J;if(!(0>K)){y.scale(K,m),m.vadd(c,m),e.getVertex(b[3*I+1],g),e.getVertex(b[3*I+2],x);var L=m.distanceSquared(c);!o(m,g,N,x)&&!o(m,N,g,x)||L>C||(l.vectorToWorldFrame(f,t,w),l.pointToWorldFrame(n,f,m,r),this.reportIntersection(w,r,e,d,I))}}u.length=0},n.prototype[p.types.TRIMESH]=n.prototype.intersectTrimesh,n.prototype.reportIntersection=function(e,f,o,d,i){var t=this.from,l=this.to,u=t.distanceTo(f),p=this.result;if(!(this.skipBackfaces&&e.dot(this._direction)>0))switch(p.hitFaceIndex="undefined"!=typeof i?i:-1,this.mode){case n.ALL:this.hasHit=!0,p.set(t,l,e,f,o,d,u),p.hasHit=!0,this.callback(p);break;case n.CLOSEST:(uf;f++){for(var o=e[f],d=f-1;d>=0&&!(e[d].aabb.lowerBound.x<=o.aabb.lowerBound.x);d--)e[d+1]=e[d];e[d+1]=o}return e},n.insertionSortY=function(e){for(var f=1,n=e.length;n>f;f++){for(var o=e[f],d=f-1;d>=0&&!(e[d].aabb.lowerBound.y<=o.aabb.lowerBound.y);d--)e[d+1]=e[d];e[d+1]=o}return e},n.insertionSortZ=function(e){for(var f=1,n=e.length;n>f;f++){for(var o=e[f],d=f-1;d>=0&&!(e[d].aabb.lowerBound.z<=o.aabb.lowerBound.z);d--)e[d+1]=e[d];e[d+1]=o}return e},n.prototype.collisionPairs=function(e,f,o){var d,i,t=this.axisList,l=t.length,u=this.axisIndex;for(this.dirty&&(this.sortList(),this.dirty=!1),d=0;d!==l;d++){var p=t[d];for(i=d+1;l>i;i++){var s=t[i];if(this.needBroadphaseCollision(p,s)){if(!n.checkBounds(p,s,u))break;this.intersectionTest(p,s,f,o)}}}},n.prototype.sortList=function(){for(var e=this.axisList,f=this.axisIndex,o=e.length,d=0;d!==o;d++){var i=e[d];i.aabbNeedsUpdate&&i.computeAABB()}0===f?n.insertionSortX(e):1===f?n.insertionSortY(e):2===f&&n.insertionSortZ(e)},n.checkBounds=function(e,f,n){var o,d;0===n?(o=e.position.x,d=f.position.x):1===n?(o=e.position.y,d=f.position.y):2===n&&(o=e.position.z,d=f.position.z);var i=e.boundingRadius,t=f.boundingRadius,l=o+i,u=d-t;return l>u},n.prototype.autoDetectAxis=function(){for(var e=0,f=0,n=0,o=0,d=0,i=0,t=this.axisList,l=t.length,u=1/l,p=0;p!==l;p++){var s=t[p],y=s.position.x;e+=y,f+=y*y;var c=s.position.y;n+=c,o+=c*c;var a=s.position.z;d+=a,i+=a*a}var r=f-e*e*u,w=o-n*n*u,b=i-d*d*u;this.axisIndex=r>w?r>b?0:2:w>b?1:2},n.prototype.aabbQuery=function(e,f,n){n=n||[],this.dirty&&(this.sortList(),this.dirty=!1);var o=this.axisIndex,d="x";1===o&&(d="y"),2===o&&(d="z");for(var i=this.axisList,t=(f.lowerBound[d],f.upperBound[d],0);td;d++)for(var i=0;3>i;i++){for(var t=0,l=0;3>l;l++)t+=e.elements[d+3*l]*this.elements[l+3*i];o.elements[d+3*i]=t}return o},n.prototype.scale=function(e,f){f=f||new n;for(var o=this.elements,d=f.elements,i=0;3!==i;i++)d[3*i+0]=e.x*o[3*i+0],d[3*i+1]=e.y*o[3*i+1],d[3*i+2]=e.z*o[3*i+2];return f},n.prototype.solve=function(e,f){f=f||new o;for(var n=3,d=4,i=[],t=0;n*d>t;t++)i.push(0);var t,l;for(t=0;3>t;t++)for(l=0;3>l;l++)i[t+d*l]=this.elements[t+3*l];i[3]=e.x,i[7]=e.y,i[11]=e.z;var u,p,s=3,y=s,c=4;do{if(t=y-s,0===i[t+d*t])for(l=t+1;y>l;l++)if(0!==i[t+d*l]){u=c;do p=c-u,i[p+d*t]+=i[p+d*l];while(--u);break}if(0!==i[t+d*t])for(l=t+1;y>l;l++){var a=i[t+d*l]/i[t+d*t];u=c;do p=c-u,i[p+d*l]=t>=p?0:i[p+d*l]-i[p+d*t]*a;while(--u)}}while(--s);if(f.z=i[2*d+3]/i[2*d+2],f.y=(i[1*d+3]-i[1*d+2]*f.z)/i[1*d+1],f.x=(i[0*d+3]-i[0*d+2]*f.z-i[0*d+1]*f.y)/i[0*d+0],isNaN(f.x)||isNaN(f.y)||isNaN(f.z)||1/0===f.x||1/0===f.y||1/0===f.z)throw"Could not solve equation! Got x=["+f.toString()+"], b=["+e.toString()+"], A=["+this.toString()+"]";return f},n.prototype.e=function(e,f,n){return void 0===n?this.elements[f+3*e]:void(this.elements[f+3*e]=n)},n.prototype.copy=function(e){for(var f=0;fn;n++)e+=this.elements[n]+f;return e},n.prototype.reverse=function(e){e=e||new n;for(var f=3,o=6,d=[],i=0;f*o>i;i++)d.push(0);var i,t;for(i=0;3>i;i++)for(t=0;3>t;t++)d[i+o*t]=this.elements[i+3*t];d[3]=1,d[9]=0,d[15]=0,d[4]=0,d[10]=1,d[16]=0,d[5]=0,d[11]=0,d[17]=1;var l,u,p=3,s=p,y=o;do{if(i=s-p,0===d[i+o*i])for(t=i+1;s>t;t++)if(0!==d[i+o*t]){l=y;do u=y-l,d[u+o*i]+=d[u+o*t];while(--l);break}if(0!==d[i+o*i])for(t=i+1;s>t;t++){var c=d[i+o*t]/d[i+o*i];l=y;do u=y-l,d[u+o*t]=i>=u?0:d[u+o*t]-d[u+o*i]*c;while(--l)}}while(--p);i=2;do{t=i-1;do{var c=d[i+o*t]/d[i+o*i];l=o;do u=o-l,d[u+o*t]=d[u+o*t]-d[u+o*i]*c;while(--l)}while(t--)}while(--i);i=2;do{var c=1/d[i+o*i];l=o;do u=o-l,d[u+o*i]=d[u+o*i]*c;while(--l)}while(i--);i=2;do{t=2;do{if(u=d[f+t+o*i],isNaN(u)||1/0===u)throw"Could not reverse! A=["+this.toString()+"]";e.e(i,t,u)}while(t--)}while(i--);return e},n.prototype.setRotationFromQuaternion=function(e){var f=e.x,n=e.y,o=e.z,d=e.w,i=f+f,t=n+n,l=o+o,u=f*i,p=f*t,s=f*l,y=n*t,c=n*l,a=o*l,r=d*i,w=d*t,b=d*l,m=this.elements;return m[0]=1-(y+a),m[1]=p-b,m[2]=s+w,m[3]=p+b,m[4]=1-(u+a),m[5]=c-r,m[6]=s-w,m[7]=c+r,m[8]=1-(u+y),this},n.prototype.transpose=function(e){e=e||new n;for(var f=e.elements,o=this.elements,d=0;3!==d;d++)for(var i=0;3!==i;i++)f[3*d+i]=o[3*i+d];return e}},{"./Vec3":30}],28:[function(e,f){function n(e,f,n,o){this.x=void 0!==e?e:0,this.y=void 0!==f?f:0,this.z=void 0!==n?n:0,this.w=void 0!==o?o:1}f.exports=n;var o=e("./Vec3");n.prototype.set=function(e,f,n,o){this.x=e,this.y=f,this.z=n,this.w=o},n.prototype.toString=function(){return this.x+","+this.y+","+this.z+","+this.w},n.prototype.toArray=function(){return[this.x,this.y,this.z,this.w]},n.prototype.setFromAxisAngle=function(e,f){var n=Math.sin(.5*f);this.x=e.x*n,this.y=e.y*n,this.z=e.z*n,this.w=Math.cos(.5*f)},n.prototype.toAxisAngle=function(e){e=e||new o,this.normalize();var f=2*Math.acos(this.w),n=Math.sqrt(1-this.w*this.w);return.001>n?(e.x=this.x,e.y=this.y,e.z=this.z):(e.x=this.x/n,e.y=this.y/n,e.z=this.z/n),[e,f]};var d=new o,i=new o;n.prototype.setFromVectors=function(e,f){if(e.isAntiparallelTo(f)){var n=d,o=i;e.tangents(n,o),this.setFromAxisAngle(n,Math.PI)}else{var t=e.cross(f);this.x=t.x,this.y=t.y,this.z=t.z,this.w=Math.sqrt(Math.pow(e.norm(),2)*Math.pow(f.norm(),2))+e.dot(f),this.normalize()}};var t=new o,l=new o,u=new o;n.prototype.mult=function(e,f){f=f||new n;var o=this.w,d=t,i=l,p=u;return d.set(this.x,this.y,this.z),i.set(e.x,e.y,e.z),f.w=o*e.w-d.dot(i),d.cross(i,p),f.x=o*i.x+e.w*d.x+p.x,f.y=o*i.y+e.w*d.y+p.y,f.z=o*i.z+e.w*d.z+p.z,f},n.prototype.inverse=function(e){var f=this.x,o=this.y,d=this.z,i=this.w;e=e||new n,this.conjugate(e);var t=1/(f*f+o*o+d*d+i*i);return e.x*=t,e.y*=t,e.z*=t,e.w*=t,e},n.prototype.conjugate=function(e){return e=e||new n,e.x=-this.x,e.y=-this.y,e.z=-this.z,e.w=this.w,e},n.prototype.normalize=function(){var e=Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w);0===e?(this.x=0,this.y=0,this.z=0,this.w=0):(e=1/e,this.x*=e,this.y*=e,this.z*=e,this.w*=e)},n.prototype.normalizeFast=function(){var e=(3-(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w))/2;0===e?(this.x=0,this.y=0,this.z=0,this.w=0):(this.x*=e,this.y*=e,this.z*=e,this.w*=e)},n.prototype.vmult=function(e,f){f=f||new o;var n=e.x,d=e.y,i=e.z,t=this.x,l=this.y,u=this.z,p=this.w,s=p*n+l*i-u*d,y=p*d+u*n-t*i,c=p*i+t*d-l*n,a=-t*n-l*d-u*i;return f.x=s*p+a*-t+y*-u-c*-l,f.y=y*p+a*-l+c*-t-s*-u,f.z=c*p+a*-u+s*-l-y*-t,f},n.prototype.copy=function(e){return this.x=e.x,this.y=e.y,this.z=e.z,this.w=e.w,this},n.prototype.toEuler=function(e,f){f=f||"YZX";var n,o,d,i=this.x,t=this.y,l=this.z,u=this.w;switch(f){case"YZX":var p=i*t+l*u;if(p>.499&&(n=2*Math.atan2(i,u),o=Math.PI/2,d=0),-.499>p&&(n=-2*Math.atan2(i,u),o=-Math.PI/2,d=0),isNaN(n)){var s=i*i,y=t*t,c=l*l;n=Math.atan2(2*t*u-2*i*l,1-2*y-2*c),o=Math.asin(2*p),d=Math.atan2(2*i*u-2*t*l,1-2*s-2*c)}break;default:throw new Error("Euler order "+f+" not supported yet.")}e.y=n,e.z=o,e.x=d},n.prototype.setFromEuler=function(e,f,n,o){o=o||"XYZ";var d=Math.cos(e/2),i=Math.cos(f/2),t=Math.cos(n/2),l=Math.sin(e/2),u=Math.sin(f/2),p=Math.sin(n/2);return"XYZ"===o?(this.x=l*i*t+d*u*p,this.y=d*u*t-l*i*p,this.z=d*i*p+l*u*t,this.w=d*i*t-l*u*p):"YXZ"===o?(this.x=l*i*t+d*u*p,this.y=d*u*t-l*i*p,this.z=d*i*p-l*u*t,this.w=d*i*t+l*u*p):"ZXY"===o?(this.x=l*i*t-d*u*p,this.y=d*u*t+l*i*p,this.z=d*i*p+l*u*t,this.w=d*i*t-l*u*p):"ZYX"===o?(this.x=l*i*t-d*u*p,this.y=d*u*t+l*i*p,this.z=d*i*p-l*u*t,this.w=d*i*t+l*u*p):"YZX"===o?(this.x=l*i*t+d*u*p,this.y=d*u*t+l*i*p,this.z=d*i*p-l*u*t,this.w=d*i*t-l*u*p):"XZY"===o&&(this.x=l*i*t-d*u*p,this.y=d*u*t-l*i*p,this.z=d*i*p+l*u*t,this.w=d*i*t+l*u*p),this},n.prototype.clone=function(){return new n(this.x,this.y,this.z,this.w)}},{"./Vec3":30}],29:[function(e,f){function n(e){e=e||{},this.position=new o,e.position&&this.position.copy(e.position),this.quaternion=new d,e.quaternion&&this.quaternion.copy(e.quaternion)}var o=e("./Vec3"),d=e("./Quaternion");f.exports=n;var i=new d;n.pointToLocalFrame=function(e,f,n,d){var d=d||new o;return n.vsub(e,d),f.conjugate(i),i.vmult(d,d),d},n.prototype.pointToLocal=function(e,f){return n.pointToLocalFrame(this.position,this.quaternion,e,f)},n.pointToWorldFrame=function(e,f,n,d){var d=d||new o;return f.vmult(n,d),d.vadd(e,d),d},n.prototype.pointToWorld=function(e,f){return n.pointToWorldFrame(this.position,this.quaternion,e,f)},n.prototype.vectorToWorldFrame=function(e,f){var f=f||new o;return this.quaternion.vmult(e,f),f},n.vectorToWorldFrame=function(e,f,n){return e.vmult(f,n),n},n.vectorToLocalFrame=function(e,f,n,d){var d=d||new o;return f.w*=-1,f.vmult(n,d),f.w*=-1,d}},{"./Quaternion":28,"./Vec3":30}],30:[function(e,f){function n(e,f,n){this.x=e||0,this.y=f||0,this.z=n||0}f.exports=n;var o=e("./Mat3");n.ZERO=new n(0,0,0),n.UNIT_X=new n(1,0,0),n.UNIT_Y=new n(0,1,0),n.UNIT_Z=new n(0,0,1),n.prototype.cross=function(e,f){var o=e.x,d=e.y,i=e.z,t=this.x,l=this.y,u=this.z;return f=f||new n,f.x=l*i-u*d,f.y=u*o-t*i,f.z=t*d-l*o,f},n.prototype.set=function(e,f,n){return this.x=e,this.y=f,this.z=n,this},n.prototype.setZero=function(){this.x=this.y=this.z=0},n.prototype.vadd=function(e,f){return f?(f.x=e.x+this.x,f.y=e.y+this.y,f.z=e.z+this.z,void 0):new n(this.x+e.x,this.y+e.y,this.z+e.z)},n.prototype.vsub=function(e,f){return f?(f.x=this.x-e.x,f.y=this.y-e.y,f.z=this.z-e.z,void 0):new n(this.x-e.x,this.y-e.y,this.z-e.z)},n.prototype.crossmat=function(){return new o([0,-this.z,this.y,this.z,0,-this.x,-this.y,this.x,0])},n.prototype.normalize=function(){var e=this.x,f=this.y,n=this.z,o=Math.sqrt(e*e+f*f+n*n);if(o>0){var d=1/o;this.x*=d,this.y*=d,this.z*=d}else this.x=0,this.y=0,this.z=0;return o},n.prototype.unit=function(e){e=e||new n;var f=this.x,o=this.y,d=this.z,i=Math.sqrt(f*f+o*o+d*d);return i>0?(i=1/i,e.x=f*i,e.y=o*i,e.z=d*i):(e.x=1,e.y=0,e.z=0),e},n.prototype.norm=function(){var e=this.x,f=this.y,n=this.z;return Math.sqrt(e*e+f*f+n*n)},n.prototype.length=n.prototype.norm,n.prototype.norm2=function(){return this.dot(this)},n.prototype.lengthSquared=n.prototype.norm2,n.prototype.distanceTo=function(e){var f=this.x,n=this.y,o=this.z,d=e.x,i=e.y,t=e.z;return Math.sqrt((d-f)*(d-f)+(i-n)*(i-n)+(t-o)*(t-o))},n.prototype.distanceSquared=function(e){var f=this.x,n=this.y,o=this.z,d=e.x,i=e.y,t=e.z;return(d-f)*(d-f)+(i-n)*(i-n)+(t-o)*(t-o)},n.prototype.mult=function(e,f){f=f||new n;var o=this.x,d=this.y,i=this.z;return f.x=e*o,f.y=e*d,f.z=e*i,f},n.prototype.scale=n.prototype.mult,n.prototype.dot=function(e){return this.x*e.x+this.y*e.y+this.z*e.z},n.prototype.isZero=function(){return 0===this.x&&0===this.y&&0===this.z},n.prototype.negate=function(e){return e=e||new n,e.x=-this.x,e.y=-this.y,e.z=-this.z,e};var d=new n,i=new n;n.prototype.tangents=function(e,f){var n=this.norm();if(n>0){var o=d,t=1/n;o.set(this.x*t,this.y*t,this.z*t);var l=i;Math.abs(o.x)<.9?(l.set(1,0,0),o.cross(l,e)):(l.set(0,1,0),o.cross(l,e)),o.cross(e,f)}else e.set(1,0,0),f.set(0,1,0)},n.prototype.toString=function(){return this.x+","+this.y+","+this.z},n.prototype.toArray=function(){return[this.x,this.y,this.z]},n.prototype.copy=function(e){return this.x=e.x,this.y=e.y,this.z=e.z,this},n.prototype.lerp=function(e,f,n){var o=this.x,d=this.y,i=this.z;n.x=o+(e.x-o)*f,n.y=d+(e.y-d)*f,n.z=i+(e.z-i)*f},n.prototype.almostEquals=function(e,f){return void 0===f&&(f=1e-6),Math.abs(this.x-e.x)>f||Math.abs(this.y-e.y)>f||Math.abs(this.z-e.z)>f?!1:!0},n.prototype.almostZero=function(e){return void 0===e&&(e=1e-6),Math.abs(this.x)>e||Math.abs(this.y)>e||Math.abs(this.z)>e?!1:!0};var t=new n;n.prototype.isAntiparallelTo=function(e,f){return this.negate(t),t.almostEquals(e,f)},n.prototype.clone=function(){return new n(this.x,this.y,this.z)}},{"./Mat3":27}],31:[function(e,f){function n(e){e=e||{},o.apply(this),this.id=n.idCounter++,this.world=null,this.preStep=null,this.postStep=null,this.vlambda=new d,this.collisionFilterGroup="number"==typeof e.collisionFilterGroup?e.collisionFilterGroup:1,this.collisionFilterMask="number"==typeof e.collisionFilterMask?e.collisionFilterMask:1,this.collisionResponse=!0,this.position=new d,e.position&&this.position.copy(e.position),this.previousPosition=new d,this.initPosition=new d,this.velocity=new d,e.velocity&&this.velocity.copy(e.velocity),this.initVelocity=new d,this.force=new d;var f="number"==typeof e.mass?e.mass:0;this.mass=f,this.invMass=f>0?1/f:0,this.material=e.material||null,this.linearDamping="number"==typeof e.linearDamping?e.linearDamping:.01,this.type=0>=f?n.STATIC:n.DYNAMIC,typeof e.type==typeof n.STATIC&&(this.type=e.type),this.allowSleep="undefined"!=typeof e.allowSleep?e.allowSleep:!0,this.sleepState=0,this.sleepSpeedLimit="undefined"!=typeof e.sleepSpeedLimit?e.sleepSpeedLimit:.1,this.sleepTimeLimit="undefined"!=typeof e.sleepTimeLimit?e.sleepTimeLimit:1,this.timeLastSleepy=0,this._wakeUpAfterNarrowphase=!1,this.torque=new d,this.quaternion=new t,e.quaternion&&this.quaternion.copy(e.quaternion),this.initQuaternion=new t,this.angularVelocity=new d,e.angularVelocity&&this.angularVelocity.copy(e.angularVelocity),this.initAngularVelocity=new d,this.interpolatedPosition=new d,this.interpolatedQuaternion=new t,this.shapes=[],this.shapeOffsets=[],this.shapeOrientations=[],this.inertia=new d,this.invInertia=new d,this.invInertiaWorld=new i,this.invMassSolve=0,this.invInertiaSolve=new d,this.invInertiaWorldSolve=new i,this.fixedRotation="undefined"!=typeof e.fixedRotation?e.fixedRotation:!1,this.angularDamping="undefined"!=typeof e.angularDamping?e.angularDamping:.01,this.aabb=new l,this.aabbNeedsUpdate=!0,this.wlambda=new d,e.shape&&this.addShape(e.shape),this.updateMassProperties()}f.exports=n;var o=e("../utils/EventTarget"),d=(e("../shapes/Shape"),e("../math/Vec3")),i=e("../math/Mat3"),t=e("../math/Quaternion"),l=(e("../material/Material"),e("../collision/AABB")),u=e("../shapes/Box");n.prototype=new o,n.prototype.constructor=n,n.DYNAMIC=1,n.STATIC=2,n.KINEMATIC=4,n.AWAKE=0,n.SLEEPY=1,n.SLEEPING=2,n.idCounter=0,n.prototype.wakeUp=function(){var e=this.sleepState;this.sleepState=0,e===n.SLEEPING&&this.dispatchEvent({type:"wakeup"})},n.prototype.sleep=function(){this.sleepState=n.SLEEPING,this.velocity.set(0,0,0),this.angularVelocity.set(0,0,0)},n.sleepyEvent={type:"sleepy"},n.sleepEvent={type:"sleep"},n.prototype.sleepTick=function(e){if(this.allowSleep){var f=this.sleepState,o=this.velocity.norm2()+this.angularVelocity.norm2(),d=Math.pow(this.sleepSpeedLimit,2);f===n.AWAKE&&d>o?(this.sleepState=n.SLEEPY,this.timeLastSleepy=e,this.dispatchEvent(n.sleepyEvent)):f===n.SLEEPY&&o>d?this.wakeUp():f===n.SLEEPY&&e-this.timeLastSleepy>this.sleepTimeLimit&&(this.sleep(),this.dispatchEvent(n.sleepEvent))}},n.prototype.updateSolveMassProperties=function(){this.sleepState===n.SLEEPING||this.type===n.KINEMATIC?(this.invMassSolve=0,this.invInertiaSolve.setZero(),this.invInertiaWorldSolve.setZero()):(this.invMassSolve=this.invMass,this.invInertiaSolve.copy(this.invInertia),this.invInertiaWorldSolve.copy(this.invInertiaWorld))},n.prototype.pointToLocalFrame=function(e,f){var f=f||new d;return e.vsub(this.position,f),this.quaternion.conjugate().vmult(f,f),f},n.prototype.vectorToLocalFrame=function(e,f){var f=f||new d;return this.quaternion.conjugate().vmult(e,f),f},n.prototype.pointToWorldFrame=function(e,f){var f=f||new d;return this.quaternion.vmult(e,f),f.vadd(this.position,f),f},n.prototype.vectorToWorldFrame=function(e,f){var f=f||new d;return this.quaternion.vmult(e,f),f};var p=new d,s=new t;n.prototype.addShape=function(e,f,n){var o=new d,i=new t;return f&&o.copy(f),n&&i.copy(n),this.shapes.push(e),this.shapeOffsets.push(o),this.shapeOrientations.push(i),this.updateMassProperties(),this.updateBoundingRadius(),this.aabbNeedsUpdate=!0,this},n.prototype.updateBoundingRadius=function(){for(var e=this.shapes,f=this.shapeOffsets,n=e.length,o=0,d=0;d!==n;d++){var i=e[d];i.updateBoundingSphereRadius();var t=f[d].norm(),l=i.boundingSphereRadius;t+l>o&&(o=t+l)}this.boundingRadius=o};var y=new l;n.prototype.computeAABB=function(){for(var e=this.shapes,f=this.shapeOffsets,n=this.shapeOrientations,o=e.length,d=p,i=s,t=this.quaternion,l=this.aabb,u=y,c=0;c!==o;c++){var a=e[c];n[c].mult(t,i),i.vmult(f[c],d),d.vadd(this.position,d),a.calculateWorldAABB(d,i,u.lowerBound,u.upperBound),0===c?l.copy(u):l.extend(u)}this.aabbNeedsUpdate=!1};{var c=new i,a=new i;new i}n.prototype.updateInertiaWorld=function(e){var f=this.invInertia;if(f.x!==f.y||f.y!==f.z||e){var n=c,o=a;n.setRotationFromQuaternion(this.quaternion),n.transpose(o),n.scale(f,n),n.mmult(o,this.invInertiaWorld)}else;};var r=new d,w=new d;n.prototype.applyForce=function(e,f){if(this.type===n.DYNAMIC){var o=r;f.vsub(this.position,o);var d=w;o.cross(e,d),this.force.vadd(e,this.force),this.torque.vadd(d,this.torque)}};var b=new d,m=new d;n.prototype.applyLocalForce=function(e,f){if(this.type===n.DYNAMIC){var o=b,d=m;this.vectorToWorldFrame(e,o),this.pointToWorldFrame(f,d),this.applyForce(o,d)}};var N=new d,g=new d,x=new d;n.prototype.applyImpulse=function(e,f){if(this.type===n.DYNAMIC){var o=N;f.vsub(this.position,o);var d=g;d.copy(e),d.mult(this.invMass,d),this.velocity.vadd(d,this.velocity);var i=x;o.cross(e,i),this.invInertiaWorld.vmult(i,i),this.angularVelocity.vadd(i,this.angularVelocity)}};var j=new d,v=new d;n.prototype.applyLocalImpulse=function(e,f){if(this.type===n.DYNAMIC){var o=j,d=v;this.vectorToWorldFrame(e,o),this.pointToWorldFrame(f,d),this.applyImpulse(o,d)}};var A=new d;n.prototype.updateMassProperties=function(){var e=A;this.invMass=this.mass>0?1/this.mass:0;var f=this.inertia,n=this.fixedRotation;this.computeAABB(),e.set((this.aabb.upperBound.x-this.aabb.lowerBound.x)/2,(this.aabb.upperBound.y-this.aabb.lowerBound.y)/2,(this.aabb.upperBound.z-this.aabb.lowerBound.z)/2),u.calculateInertia(e,this.mass,f),this.invInertia.set(f.x>0&&!n?1/f.x:0,f.y>0&&!n?1/f.y:0,f.z>0&&!n?1/f.z:0),this.updateInertiaWorld(!0)},n.prototype.getVelocityAtWorldPoint=function(e,f){var n=new d;return e.vsub(this.position,n),this.angularVelocity.cross(n,f),this.velocity.vadd(f,f),f}},{"../collision/AABB":3,"../material/Material":25,"../math/Mat3":27,"../math/Quaternion":28,"../math/Vec3":30,"../shapes/Box":37,"../shapes/Shape":43,"../utils/EventTarget":49}],32:[function(e,f){function n(e){this.chassisBody=e.chassisBody,this.wheelInfos=[],this.sliding=!1,this.world=null,this.indexRightAxis="undefined"!=typeof e.indexRightAxis?e.indexRightAxis:1,this.indexForwardAxis="undefined"!=typeof e.indexForwardAxis?e.indexForwardAxis:0,this.indexUpAxis="undefined"!=typeof e.indexUpAxis?e.indexUpAxis:2}function o(e,f,n,o,i){var t=0,l=n,u=x,p=j,s=v;e.getVelocityAtWorldPoint(l,u),f.getVelocityAtWorldPoint(l,p),u.vsub(p,s);var y=o.dot(s),c=d(e,n,o),a=d(f,n,o),r=1,w=r/(c+a);return t=-y*w,t>i&&(t=i),-i>t&&(t=-i),t}function d(e,f,n){var o=A,d=C,i=O,t=h;return f.vsub(e.position,o),o.cross(n,d),e.invInertiaWorld.vmult(d,t),t.cross(o,i),e.invMass+n.dot(i)}function i(e,f,n,o,d,i){var t=d.norm2();if(t>1.1)return 0;var l=k,u=q,p=z;e.getVelocityAtWorldPoint(f,l),n.getVelocityAtWorldPoint(o,u),l.vsub(u,p);var s=d.dot(p),y=.2,c=1/(e.invMass+n.invMass),i=-y*s*c;return i}var t=(e("./Body"),e("../math/Vec3")),l=e("../math/Quaternion"),u=(e("../collision/RaycastResult"),e("../collision/Ray")),p=e("../objects/WheelInfo");f.exports=n;{var s=(new t,new t,new t,new t),y=new t,c=new t;new u}n.prototype.addWheel=function(e){e=e||{};var f=new p(e),n=this.wheelInfos.length;return this.wheelInfos.push(f),n},n.prototype.setSteeringValue=function(e,f){var n=this.wheelInfos[f];n.steering=e};new t;n.prototype.applyEngineForce=function(e,f){this.wheelInfos[f].engineForce=e},n.prototype.setBrake=function(e,f){this.wheelInfos[f].brake=e},n.prototype.addToWorld=function(e){this.constraints;e.add(this.chassisBody);var f=this;this.preStepCallback=function(){f.updateVehicle(e.dt)},e.addEventListener("preStep",this.preStepCallback),this.world=e},n.prototype.getVehicleAxisWorld=function(e,f){f.set(0===e?1:0,1===e?1:0,2===e?1:0),this.chassisBody.vectorToWorldFrame(f,f)},n.prototype.updateVehicle=function(e){for(var f=this.wheelInfos,n=f.length,o=this.chassisBody,d=0;n>d;d++)this.updateWheelTransform(d);this.currentVehicleSpeedKmHour=3.6*o.velocity.norm();var i=new t;this.getVehicleAxisWorld(this.indexForwardAxis,i),i.dot(o.velocity)<0&&(this.currentVehicleSpeedKmHour*=-1);for(var d=0;n>d;d++)this.castRay(f[d]);this.updateSuspension(e);for(var l=new t,u=new t,d=0;n>d;d++){var p=f[d],s=p.suspensionForce;s>p.maxSuspensionForce&&(s=p.maxSuspensionForce),p.raycastResult.hitNormalWorld.scale(s*e,l),p.raycastResult.hitPointWorld.vsub(o.position,u),o.applyImpulse(l,p.raycastResult.hitPointWorld)}this.updateFriction(e);var y=new t,c=new t,a=new t;for(d=0;n>d;d++){var p=f[d];o.getVelocityAtWorldPoint(p.chassisConnectionPointWorld,a);var r=1;switch(this.indexUpAxis){case 1:r=-1}if(p.isInContact){this.getVehicleAxisWorld(this.indexForwardAxis,c);var w=c.dot(p.raycastResult.hitNormalWorld);p.raycastResult.hitNormalWorld.scale(w,y),c.vsub(y,c);var b=c.dot(a);p.deltaRotation=r*b*e/p.radius}!p.sliding&&p.isInContact||0===p.engineForce||!p.useCustomSlidingRotationalSpeed||(p.deltaRotation=(p.engineForce>0?1:-1)*p.customSlidingRotationalSpeed*e),Math.abs(p.brake)>Math.abs(p.engineForce)&&(p.deltaRotation=0),p.rotation+=p.deltaRotation,p.deltaRotation*=.99}},n.prototype.updateSuspension=function(){for(var e=this.chassisBody,f=e.mass,n=this.wheelInfos,o=n.length,d=0;o>d;d++){var i=n[d];if(i.isInContact){var t,l=i.suspensionRestLength,u=i.suspensionLength,p=l-u;t=i.suspensionStiffness*p*i.clippedInvContactDotSuspension;var s,y=i.suspensionRelativeVelocity;s=0>y?i.dampingCompression:i.dampingRelaxation,t-=s*y,i.suspensionForce=t*f,i.suspensionForce<0&&(i.suspensionForce=0)}else i.suspensionForce=0}},n.prototype.removeFromWorld=function(e){this.constraints;e.remove(this.chassisBody),e.removeEventListener("preStep",this.preStepCallback),this.world=null};var a=new t,r=new t;n.prototype.castRay=function(e){var f=a,n=r;this.updateWheelTransformWorld(e);var o=this.chassisBody,d=-1,i=e.suspensionRestLength+e.radius;e.directionWorld.scale(i,f);var l=e.chassisConnectionPointWorld;l.vadd(f,n);var u=e.raycastResult;u.reset();var p=o.collisionResponse;o.collisionResponse=!1,this.world.rayTest(l,n,u),o.collisionResponse=p;var s=u.body;if(e.raycastResult.groundObject=0,s){d=u.distance,e.raycastResult.hitNormalWorld=u.hitNormalWorld,e.isInContact=!0;var y=u.distance;e.suspensionLength=y-e.radius;var c=e.suspensionRestLength-e.maxSuspensionTravel,w=e.suspensionRestLength+e.maxSuspensionTravel;e.suspensionLengthw&&(e.suspensionLength=w,e.raycastResult.reset());var b=e.raycastResult.hitNormalWorld.dot(e.directionWorld),m=new t;o.getVelocityAtWorldPoint(e.raycastResult.hitPointWorld,m);var N=e.raycastResult.hitNormalWorld.dot(m);if(b>=-.1)e.suspensionRelativeVelocity=0,e.clippedInvContactDotSuspension=10;else{var g=-1/b;e.suspensionRelativeVelocity=N*g,e.clippedInvContactDotSuspension=g}}else e.suspensionLength=e.suspensionRestLength+0*e.maxSuspensionTravel,e.suspensionRelativeVelocity=0,e.directionWorld.scale(-1,e.raycastResult.hitNormalWorld),e.clippedInvContactDotSuspension=1;return d},n.prototype.updateWheelTransformWorld=function(e){e.isInContact=!1;var f=this.chassisBody;f.pointToWorldFrame(e.chassisConnectionPointLocal,e.chassisConnectionPointWorld),f.vectorToWorldFrame(e.directionLocal,e.directionWorld),f.vectorToWorldFrame(e.axleLocal,e.axleWorld)},n.prototype.updateWheelTransform=function(e){var f=s,n=y,o=c,d=this.wheelInfos[e];this.updateWheelTransformWorld(d),d.directionLocal.scale(-1,f),n.copy(d.axleLocal),f.cross(n,o),o.normalize(),n.normalize();var i=d.steering,t=new l;t.setFromAxisAngle(f,i);var u=new l;u.setFromAxisAngle(n,d.rotation);var p=d.worldTransform.quaternion;this.chassisBody.quaternion.mult(t,p),p.mult(u,p),p.normalize();var a=d.worldTransform.position;a.copy(d.directionWorld),a.scale(d.suspensionLength,a),a.vadd(d.chassisConnectionPointWorld,a)};var w=[new t(1,0,0),new t(0,1,0),new t(0,0,1)];n.prototype.getWheelTransformWorld=function(e){return this.wheelInfos[e].worldTransform};var b=new t,m=[],N=[],g=1;n.prototype.updateFriction=function(e){for(var f=b,n=this.wheelInfos,d=n.length,l=this.chassisBody,u=N,p=m,s=0,y=0;d>y;y++){var c=n[y],a=c.raycastResult.body;a&&s++,c.sideImpulse=0,c.forwardImpulse=0,u[y]||(u[y]=new t),p[y]||(p[y]=new t)}for(var y=0;d>y;y++){var c=n[y],a=c.raycastResult.body;if(a){var r=p[y],x=this.getWheelTransformWorld(y);x.vectorToWorldFrame(w[this.indexRightAxis],r);var j=c.raycastResult.hitNormalWorld,v=r.dot(j);j.scale(v,f),r.vsub(f,r),r.normalize(),j.cross(r,u[y]),u[y].normalize(),c.sideImpulse=i(l,c.raycastResult.hitPointWorld,a,c.raycastResult.hitPointWorld,r),c.sideImpulse*=g}}var A=1,C=.5;this.sliding=!1;for(var y=0;d>y;y++){var c=n[y],a=c.raycastResult.body,O=0;if(c.slipInfo=1,a){var h=0,k=c.brake?c.brake:h;O=o(l,a,c.raycastResult.hitPointWorld,u[y],k),O+=c.engineForce*e;var q=k/O;c.slipInfo*=q}if(c.forwardImpulse=0,c.skidInfo=1,a){c.skidInfo=1;var z=c.suspensionForce*e*c.frictionSlip,B=z,D=z*B;c.forwardImpulse=O;var E=c.forwardImpulse*C,F=c.sideImpulse*A,G=E*E+F*F;if(c.sliding=!1,G>D){this.sliding=!0,c.sliding=!0;var q=z/Math.sqrt(G);c.skidInfo*=q}}}if(this.sliding)for(var y=0;d>y;y++){var c=n[y];0!==c.sideImpulse&&c.skidInfo<1&&(c.forwardImpulse*=c.skidInfo,c.sideImpulse*=c.skidInfo)}for(var y=0;d>y;y++){var c=n[y],H=new t;if(H.copy(c.raycastResult.hitPointWorld),0!==c.forwardImpulse){var I=new t;u[y].scale(c.forwardImpulse,I),l.applyImpulse(I,H)}if(0!==c.sideImpulse){var a=c.raycastResult.body,J=new t;J.copy(c.raycastResult.hitPointWorld);var K=new t;p[y].scale(c.sideImpulse,K),l.pointToLocalFrame(H,H),H["xyz"[this.indexUpAxis]]*=c.rollInfluence,l.pointToWorldFrame(H,H),l.applyImpulse(K,H),K.scale(-1,K),a.applyImpulse(K,J)}}};var x=new t,j=new t,v=new t,A=new t,C=new t,O=new t,h=new t,k=new t,q=new t,z=new t},{"../collision/Ray":9,"../collision/RaycastResult":10,"../math/Quaternion":28,"../math/Vec3":30,"../objects/WheelInfo":36,"./Body":31}],33:[function(e,f){function n(e){if(this.wheelBodies=[],this.coordinateSystem="undefined"==typeof e.coordinateSystem?new t(1,2,3):e.coordinateSystem.clone(),this.chassisBody=e.chassisBody,!this.chassisBody){var f=new i(new t(5,2,.5));this.chassisBody=new o(1,f)}this.constraints=[],this.wheelAxes=[],this.wheelForces=[]}var o=e("./Body"),d=e("../shapes/Sphere"),i=e("../shapes/Box"),t=e("../math/Vec3"),l=e("../constraints/HingeConstraint");f.exports=n,n.prototype.addWheel=function(e){e=e||{};var f=e.body;f||(f=new o(1,new d(1.2))),this.wheelBodies.push(f),this.wheelForces.push(0);var n=(new t,"undefined"!=typeof e.position?e.position.clone():new t),i=new t;this.chassisBody.pointToWorldFrame(n,i),f.position.set(i.x,i.y,i.z);var u="undefined"!=typeof e.axis?e.axis.clone():new t(0,1,0);this.wheelAxes.push(u);var p=new l(this.chassisBody,f,{pivotA:n,axisA:u,pivotB:t.ZERO,axisB:u,collideConnected:!1});return this.constraints.push(p),this.wheelBodies.length-1},n.prototype.setSteeringValue=function(e,f){var n=this.wheelAxes[f],o=Math.cos(e),d=Math.sin(e),i=n.x,t=n.y;this.constraints[f].axisA.set(o*i-d*t,d*i+o*t,0)},n.prototype.setMotorSpeed=function(e,f){var n=this.constraints[f];n.enableMotor(),n.motorTargetVelocity=e},n.prototype.disableMotor=function(e){var f=this.constraints[e]; -f.disableMotor()};var u=new t;n.prototype.setWheelForce=function(e,f){this.wheelForces[f]=e},n.prototype.applyWheelForce=function(e,f){var n=this.wheelAxes[f],o=this.wheelBodies[f],d=o.torque;n.scale(e,u),o.vectorToWorldFrame(u,u),d.vadd(u,d)},n.prototype.addToWorld=function(e){for(var f=this.constraints,n=this.wheelBodies.concat([this.chassisBody]),o=0;othis.particles.length&&this.neighbors.pop())};var d=new o;n.prototype.getNeighbors=function(e,f){for(var n=this.particles.length,o=e.id,i=this.smoothingRadius*this.smoothingRadius,t=d,l=0;l!==n;l++){var u=this.particles[l];u.position.vsub(e.position,t),o!==u.id&&t.norm2()=-.1)this.suspensionRelativeVelocity=0,this.clippedInvContactDotSuspension=10;else{var d=-1/n;this.suspensionRelativeVelocity=o*d,this.clippedInvContactDotSuspension=d}}else f.suspensionLength=this.suspensionRestLength,this.suspensionRelativeVelocity=0,f.directionWorld.scale(-1,f.hitNormalWorld),this.clippedInvContactDotSuspension=1}},{"../collision/RaycastResult":10,"../math/Transform":29,"../math/Vec3":30,"../utils/Utils":53}],37:[function(e,f){function n(e){o.call(this),this.type=o.types.BOX,this.halfExtents=e,this.convexPolyhedronRepresentation=null,this.updateConvexPolyhedronRepresentation(),this.updateBoundingSphereRadius()}f.exports=n;var o=e("./Shape"),d=e("../math/Vec3"),i=e("./ConvexPolyhedron");n.prototype=new o,n.prototype.constructor=n,n.prototype.updateConvexPolyhedronRepresentation=function(){var e=this.halfExtents.x,f=this.halfExtents.y,n=this.halfExtents.z,o=d,t=[new o(-e,-f,-n),new o(e,-f,-n),new o(e,f,-n),new o(-e,f,-n),new o(-e,-f,n),new o(e,-f,n),new o(e,f,n),new o(-e,f,n)],l=[[3,2,1,0],[4,5,6,7],[5,4,0,1],[2,3,7,6],[0,4,7,3],[1,2,6,5]],u=([new o(0,0,1),new o(0,1,0),new o(1,0,0)],new i(t,l));this.convexPolyhedronRepresentation=u,u.material=this.material},n.prototype.calculateLocalInertia=function(e,f){return f=f||new d,n.calculateInertia(this.halfExtents,e,f),f},n.calculateInertia=function(e,f,n){var o=e;n.x=1/12*f*(2*o.y*2*o.y+2*o.z*2*o.z),n.y=1/12*f*(2*o.x*2*o.x+2*o.z*2*o.z),n.z=1/12*f*(2*o.y*2*o.y+2*o.x*2*o.x)},n.prototype.getSideNormals=function(e,f){var n=e,o=this.halfExtents;if(n[0].set(o.x,0,0),n[1].set(0,o.y,0),n[2].set(0,0,o.z),n[3].set(-o.x,0,0),n[4].set(0,-o.y,0),n[5].set(0,0,-o.z),void 0!==f)for(var d=0;d!==n.length;d++)f.vmult(n[d],n[d]);return n},n.prototype.volume=function(){return 8*this.halfExtents.x*this.halfExtents.y*this.halfExtents.z},n.prototype.updateBoundingSphereRadius=function(){this.boundingSphereRadius=this.halfExtents.norm()};{var t=new d;new d}n.prototype.forEachWorldCorner=function(e,f,n){for(var o=this.halfExtents,d=[[o.x,o.y,o.z],[-o.x,o.y,o.z],[-o.x,-o.y,o.z],[-o.x,-o.y,-o.z],[o.x,-o.y,-o.z],[o.x,o.y,-o.z],[-o.x,o.y,-o.z],[o.x,-o.y,o.z]],i=0;it;t++){var i=l[t];f.vmult(i,i),e.vadd(i,i);var u=i.x,p=i.y,s=i.z;u>o.x&&(o.x=u),p>o.y&&(o.y=p),s>o.z&&(o.z=s),ua&&(a=w,c=r)}for(var b=[],m=n.faces[c],N=m.length,g=0;N>g;g++){var x=n.vertices[m[g]],j=new d;j.copy(x),i.vmult(j,j),o.vadd(j,j),b.push(j)}c>=0&&this.clipFaceAgainstHull(t,e,f,b,l,u,s)};var s=new d,y=new d,c=new d,a=new d,r=new d,w=new d;n.prototype.findSeparatingAxis=function(e,f,n,o,d,i,t,l){var u=s,p=y,b=c,m=a,N=r,g=w,x=Number.MAX_VALUE,j=this,v=0;if(j.uniqueAxes)for(var A=0;A!==j.uniqueAxes.length;A++){n.vmult(j.uniqueAxes[A],u);var C=j.testSepAxis(u,e,f,n,o,d);if(C===!1)return!1;x>C&&(x=C,i.copy(u))}else for(var O=t?t.length:j.faces.length,A=0;O>A;A++){var h=t?t[A]:A;u.copy(j.faceNormals[h]),n.vmult(u,u);var C=j.testSepAxis(u,e,f,n,o,d);if(C===!1)return!1;x>C&&(x=C,i.copy(u))}if(e.uniqueAxes)for(var A=0;A!==e.uniqueAxes.length;A++){d.vmult(e.uniqueAxes[A],p),v++;var C=j.testSepAxis(p,e,f,n,o,d);if(C===!1)return!1;x>C&&(x=C,i.copy(p))}else for(var k=l?l.length:e.faces.length,A=0;k>A;A++){var h=l?l[A]:A;p.copy(e.faceNormals[h]),d.vmult(p,p),v++;var C=j.testSepAxis(p,e,f,n,o,d);if(C===!1)return!1;x>C&&(x=C,i.copy(p))}for(var q=0;q!==j.uniqueEdges.length;q++){n.vmult(j.uniqueEdges[q],m);for(var z=0;z!==e.uniqueEdges.length;z++)if(d.vmult(e.uniqueEdges[z],N),m.cross(N,g),!g.almostZero()){g.normalize();var B=j.testSepAxis(g,e,f,n,o,d);if(B===!1)return!1;x>B&&(x=B,i.copy(g))}}return o.vsub(f,b),b.dot(i)>0&&i.negate(i),!0};var b=[],m=[];n.prototype.testSepAxis=function(e,f,o,d,i,t){var l=this;n.project(l,e,o,d,b),n.project(f,e,i,t,m);var u=b[0],p=b[1],s=m[0],y=m[1];if(y>u||p>s)return!1;var c=u-y,a=s-p,r=a>c?c:a;return r};var N=new d,g=new d;n.prototype.calculateLocalInertia=function(e,f){this.computeLocalAABB(N,g);var n=g.x-N.x,o=g.y-N.y,d=g.z-N.z;f.x=1/12*e*(2*o*2*o+2*d*2*d),f.y=1/12*e*(2*n*2*n+2*d*2*d),f.z=1/12*e*(2*o*2*o+2*n*2*n)},n.prototype.getPlaneConstantOfFace=function(e){var f=this.faces[e],n=this.faceNormals[e],o=this.vertices[f[0]],d=-n.dot(o);return d};var x=new d,j=new d,v=new d,A=new d,C=new d,O=new d,h=new d,k=new d;n.prototype.clipFaceAgainstHull=function(e,f,n,o,d,i,t){for(var l=x,u=j,p=v,s=A,y=C,c=O,a=h,r=k,w=this,b=[],m=o,N=b,g=-1,q=Number.MAX_VALUE,z=0;zB&&(q=B,g=z)}if(!(0>g)){var D=w.faces[g];D.connectedFaces=[];for(var E=0;EH;H++){var I=w.vertices[D[H]],J=w.vertices[D[(H+1)%G]];I.vsub(J,u),p.copy(u),n.vmult(p,p),f.vadd(p,p),s.copy(this.faceNormals[g]),n.vmult(s,s),f.vadd(s,s),p.cross(s,y),y.negate(y),c.copy(I),n.vmult(c,c),f.vadd(c,c);var K,L=(-c.dot(y),D.connectedFaces[H]);a.copy(this.faceNormals[L]);var M=this.getPlaneConstantOfFace(L);r.copy(a),n.vmult(r,r);var K=M-r.dot(f);for(this.clipFaceAgainstPlane(m,N,r,K);m.length;)m.shift();for(;N.length;)m.push(N.shift())}a.copy(this.faceNormals[g]);var M=this.getPlaneConstantOfFace(g);r.copy(a),n.vmult(r,r);for(var K=M-r.dot(f),E=0;E=P&&(console.log("clamped: depth="+P+" to minDist="+(d+"")),P=d),i>=P){var Q=m[E];if(0>=P){var R={point:Q,normal:r,depth:P};t.push(R)}}}}},n.prototype.clipFaceAgainstPlane=function(e,f,n,o){var i,t,l=e.length;if(2>l)return f;var u=e[e.length-1],p=e[0];i=n.dot(u)+o;for(var s=0;l>s;s++){if(p=e[s],t=n.dot(p)+o,0>i)if(0>t){var y=new d;y.copy(p),f.push(y)}else{var y=new d;u.lerp(p,i/(i-t),y),f.push(y)}else if(0>t){var y=new d;u.lerp(p,i/(i-t),y),f.push(y),f.push(p)}u=p,i=t}return f},n.prototype.computeWorldVertices=function(e,f){for(var n=this.vertices.length;this.worldVertices.lengthd;d++){var i=o[d];i.xf.x&&(f.x=i.x),i.yf.y&&(f.y=i.y),i.zf.z&&(f.z=i.z)}},n.prototype.computeWorldFaceNormals=function(e){for(var f=this.faceNormals.length;this.worldFaceNormals.lengthe&&(e=d)}this.boundingSphereRadius=Math.sqrt(e)};var q=new d;n.prototype.calculateWorldAABB=function(e,f,n,o){for(var d,i,t,l,u,p,s=this.vertices.length,y=this.vertices,c=0;s>c;c++){q.copy(y[c]),f.vmult(q,q),e.vadd(q,q);var a=q;a.xl||void 0===l)&&(l=a.x),a.yu||void 0===u)&&(u=a.y),a.zp||void 0===p)&&(p=a.z)}n.set(d,i,t),o.set(l,u,p)},n.prototype.volume=function(){return 4*Math.PI*this.boundingSphereRadius/3},n.prototype.getAveragePointLocal=function(e){e=e||new d;for(var f=this.vertices.length,n=this.vertices,o=0;f>o;o++)e.vadd(n[o],e);return e.mult(1/f,e),e},n.prototype.transformAllPoints=function(e,f){var n=this.vertices.length,o=this.vertices;if(f){for(var d=0;n>d;d++){var i=o[d];f.vmult(i,i)}for(var d=0;dd;d++){var i=o[d];i.vadd(e,i)}};var z=new d,B=new d,D=new d;n.prototype.pointIsInside=function(e){var f=this.vertices.length,n=this.vertices,o=this.faces,d=this.faceNormals,i=null,t=this.faces.length,l=z;this.getAveragePointLocal(l);for(var u=0;t>u;u++){var f=(this.faces[u].length,d[u]),p=n[o[u][0]],s=B;e.vsub(p,s);var y=f.dot(s),c=D;l.vsub(p,c);var a=f.dot(c);if(0>y&&a>0||y>0&&0>a)return!1}return i?1:-1};var E=(new d,new d),F=new d;n.project=function(e,f,n,o,d){var t=e.vertices.length,l=E,u=0,p=0,s=F,y=e.vertices;s.setZero(),i.vectorToLocalFrame(n,o,f,l),i.pointToLocalFrame(n,o,s,s);var c=s.dot(l);p=u=y[0].dot(l);for(var a=1;t>a;a++){var r=y[a].dot(l);r>u&&(u=r),p>r&&(p=r)}if(p-=c,u-=c,p>u){var w=p;p=u,u=w}d[0]=u,d[1]=p}},{"../math/Quaternion":28,"../math/Transform":29,"../math/Vec3":30,"./Shape":43}],39:[function(e,f){function n(e,f,n,t){var l=t,u=[],p=[],s=[],y=[],c=[],a=Math.cos,r=Math.sin;u.push(new d(f*a(0),f*r(0),.5*-n)),y.push(0),u.push(new d(e*a(0),e*r(0),.5*n)),c.push(1);for(var w=0;l>w;w++){var b=2*Math.PI/l*(w+1),m=2*Math.PI/l*(w+.5);l-1>w?(u.push(new d(f*a(b),f*r(b),.5*-n)),y.push(2*w+2),u.push(new d(e*a(b),e*r(b),.5*n)),c.push(2*w+3),s.push([2*w+2,2*w+3,2*w+1,2*w])):s.push([0,1,2*w+1,2*w]),(l%2===1||l/2>w)&&p.push(new d(a(m),r(m),0))}s.push(c),p.push(new d(0,0,1));for(var N=[],w=0;wd&&(f=d)}this.minValue=f},n.prototype.updateMaxValue=function(){for(var e=this.data,f=e[0][0],n=0;n!==e.length;n++)for(var o=0;o!==e[n].length;o++){var d=e[n][o];d>f&&(f=d)}this.maxValue=f},n.prototype.setHeightValueAtIndex=function(e,f,n){var o=this.data;o[e][f]=n,this.clearCachedConvexTrianglePillar(e,f,!1),e>0&&(this.clearCachedConvexTrianglePillar(e-1,f,!0),this.clearCachedConvexTrianglePillar(e-1,f,!1)),f>0&&(this.clearCachedConvexTrianglePillar(e,f-1,!0),this.clearCachedConvexTrianglePillar(e,f-1,!1)),f>0&&e>0&&this.clearCachedConvexTrianglePillar(e-1,f-1,!0)},n.prototype.getRectMinMax=function(e,f,n,o,d){d=d||[];for(var i=this.data,t=this.minValue,l=e;n>=l;l++)for(var u=f;o>=u;u++){var p=i[l][u];p>t&&(t=p)}d[0]=this.minValue,d[1]=t},n.prototype.getIndexOfPosition=function(e,f,n,o){var d=this.elementSize,i=this.data,t=Math.floor(e/d),l=Math.floor(f/d);return n[0]=t,n[1]=l,o&&(0>t&&(t=0),0>l&&(l=0),t>=i.length-1&&(t=i.length-1),l>=i[0].length-1&&(l=i[0].length-1)),0>t||0>l||t>=i.length-1||l>=i[0].length-1?!1:!0},n.prototype.getHeightAt=function(e,f,n){var o=[];this.getIndexOfPosition(e,f,o,n);var d=[];return this.getRectMinMax(o[0],o[1]+1,o[0],o[1]+1,d),(d[0]+d[1])/2},n.prototype.getCacheConvexTrianglePillarKey=function(e,f,n){return e+"_"+f+"_"+(n?1:0)},n.prototype.getCachedConvexTrianglePillar=function(e,f,n){return this._cachedPillars[this.getCacheConvexTrianglePillarKey(e,f,n)]},n.prototype.setCachedConvexTrianglePillar=function(e,f,n,o,d){this._cachedPillars[this.getCacheConvexTrianglePillarKey(e,f,n)]={convex:o,offset:d}},n.prototype.clearCachedConvexTrianglePillar=function(e,f,n){delete this._cachedPillars[this.getCacheConvexTrianglePillarKey(e,f,n)]},n.prototype.getConvexTrianglePillar=function(e,f,n){var o=this.pillarConvex,t=this.pillarOffset;if(this.cacheEnabled){var l=this.getCachedConvexTrianglePillar(e,f,n);if(l)return this.pillarConvex=l.convex,void(this.pillarOffset=l.offset);o=new d,t=new i,this.pillarConvex=o,this.pillarOffset=t}var l=this.data,u=this.elementSize,p=o.faces;o.vertices.length=6;for(var s=0;6>s;s++)o.vertices[s]||(o.vertices[s]=new i);p.length=5;for(var s=0;5>s;s++)p[s]||(p[s]=[]);var y=o.vertices,c=(Math.min(l[e][f],l[e+1][f],l[e][f+1],l[e+1][f+1])-this.minValue)/2+this.minValue;n?(t.set((e+.75)*u,(f+.75)*u,c),y[0].set(.25*u,.25*u,l[e+1][f+1]-c),y[1].set(-.75*u,.25*u,l[e][f+1]-c),y[2].set(.25*u,-.75*u,l[e+1][f]-c),y[3].set(.25*u,.25*u,-c-1),y[4].set(-.75*u,.25*u,-c-1),y[5].set(.25*u,-.75*u,-c-1),p[0][0]=0,p[0][1]=1,p[0][2]=2,p[1][0]=5,p[1][1]=4,p[1][2]=3,p[2][0]=2,p[2][1]=5,p[2][2]=3,p[2][3]=0,p[3][0]=3,p[3][1]=4,p[3][2]=1,p[3][3]=0,p[4][0]=1,p[4][1]=4,p[4][2]=5,p[4][3]=2):(t.set((e+.25)*u,(f+.25)*u,c),y[0].set(-.25*u,-.25*u,l[e][f]-c),y[1].set(.75*u,-.25*u,l[e+1][f]-c),y[2].set(-.25*u,.75*u,l[e][f+1]-c),y[3].set(-.25*u,-.25*u,-c-1),y[4].set(.75*u,-.25*u,-c-1),y[5].set(-.25*u,.75*u,-c-1),p[0][0]=0,p[0][1]=1,p[0][2]=2,p[1][0]=5,p[1][1]=4,p[1][2]=3,p[2][0]=0,p[2][1]=2,p[2][2]=5,p[2][3]=3,p[3][0]=1,p[3][1]=0,p[3][2]=3,p[3][3]=4,p[4][0]=4,p[4][1]=5,p[4][2]=2,p[4][3]=1),o.computeNormals(),o.computeEdges(),o.updateBoundingSphereRadius(),this.setCachedConvexTrianglePillar(e,f,n,o,t)},n.prototype.calculateLocalInertia=function(e,f){return f=f||new i,f.set(0,0,0),f},n.prototype.volume=function(){return Number.MAX_VALUE},n.prototype.calculateWorldAABB=function(e,f,n,o){n.set(-Number.MAX_VALUE,-Number.MAX_VALUE,-Number.MAX_VALUE),o.set(Number.MAX_VALUE,Number.MAX_VALUE,Number.MAX_VALUE)},n.prototype.updateBoundingSphereRadius=function(){var e=this.data,f=this.elementSize;this.boundingSphereRadius=new i(e.length*f,e[0].length*f,Math.max(Math.abs(this.maxValue),Math.abs(this.minValue))).norm()}},{"../math/Vec3":30,"../utils/Utils":53,"./ConvexPolyhedron":38,"./Shape":43}],41:[function(e,f){function n(){o.call(this),this.type=o.types.PARTICLE}f.exports=n;var o=e("./Shape"),d=e("../math/Vec3");n.prototype=new o,n.prototype.constructor=n,n.prototype.calculateLocalInertia=function(e,f){return f=f||new d,f.set(0,0,0),f},n.prototype.volume=function(){return 0},n.prototype.updateBoundingSphereRadius=function(){this.boundingSphereRadius=0},n.prototype.calculateWorldAABB=function(e,f,n,o){n.copy(e),o.copy(e)}},{"../math/Vec3":30,"./Shape":43}],42:[function(e,f){function n(){o.call(this),this.type=o.types.PLANE,this.worldNormal=new d,this.worldNormalNeedsUpdate=!0,this.boundingSphereRadius=Number.MAX_VALUE}f.exports=n;var o=e("./Shape"),d=e("../math/Vec3");n.prototype=new o,n.prototype.constructor=n,n.prototype.computeWorldNormal=function(e){var f=this.worldNormal;f.set(0,0,1),e.vmult(f,f),this.worldNormalNeedsUpdate=!1},n.prototype.calculateLocalInertia=function(e,f){return f=f||new d},n.prototype.volume=function(){return Number.MAX_VALUE};var i=new d;n.prototype.calculateWorldAABB=function(e,f,n,o){i.set(0,0,1),f.vmult(i,i);var d=Number.MAX_VALUE;n.set(-d,-d,-d),o.set(d,d,d),1===i.x&&(o.x=e.x),1===i.y&&(o.y=e.y),1===i.z&&(o.z=e.z),-1===i.x&&(n.x=e.x),-1===i.y&&(n.y=e.y),-1===i.z&&(n.z=e.z)},n.prototype.updateBoundingSphereRadius=function(){this.boundingSphereRadius=Number.MAX_VALUE}},{"../math/Vec3":30,"./Shape":43}],43:[function(e,f){function n(){this.id=n.idCounter++,this.type=0,this.boundingSphereRadius=0,this.collisionResponse=!0,this.material=null}f.exports=n;{var n=e("./Shape");e("../math/Vec3"),e("../math/Quaternion"),e("../material/Material")}n.prototype.constructor=n,n.prototype.updateBoundingSphereRadius=function(){throw"computeBoundingSphereRadius() not implemented for shape type "+this.type},n.prototype.volume=function(){throw"volume() not implemented for shape type "+this.type},n.prototype.calculateLocalInertia=function(){throw"calculateLocalInertia() not implemented for shape type "+this.type},n.idCounter=0,n.types={SPHERE:1,PLANE:2,BOX:4,COMPOUND:8,CONVEXPOLYHEDRON:16,HEIGHTFIELD:32,PARTICLE:64,CYLINDER:128,TRIMESH:256}},{"../material/Material":25,"../math/Quaternion":28,"../math/Vec3":30,"./Shape":43}],44:[function(e,f){function n(e){if(o.call(this),this.radius=void 0!==e?Number(e):1,this.type=o.types.SPHERE,this.radius<0)throw new Error("The sphere radius cannot be negative.");this.updateBoundingSphereRadius()}f.exports=n;var o=e("./Shape"),d=e("../math/Vec3");n.prototype=new o,n.prototype.constructor=n,n.prototype.calculateLocalInertia=function(e,f){f=f||new d;var n=2*e*this.radius*this.radius/5;return f.x=n,f.y=n,f.z=n,f},n.prototype.volume=function(){return 4*Math.PI*this.radius/3},n.prototype.updateBoundingSphereRadius=function(){this.boundingSphereRadius=this.radius},n.prototype.calculateWorldAABB=function(e,f,n,o){for(var d=this.radius,i=["x","y","z"],t=0;td?d+"_"+i:i+"_"+d;e[f]=!0},n=0;nn.x&&(n.x=d.x),d.yn.y&&(n.y=d.y),d.zn.z&&(n.z=d.z)},n.prototype.updateAABB=function(){this.computeLocalAABB(this.aabb)},n.prototype.updateBoundingSphereRadius=function(){for(var e=0,f=this.vertices,n=new d,o=0,i=f.length/3;o!==i;o++){this.getVertex(o,n);var t=n.norm2();t>e&&(e=t)}this.boundingSphereRadius=Math.sqrt(e)};var g=(new d,new i),x=new t;n.prototype.calculateWorldAABB=function(e,f,n,o){var d=g,i=x;d.position=e,d.quaternion=f,this.aabb.toWorldFrame(d,i),n.copy(i.lowerBound),o.copy(i.upperBound)},n.prototype.volume=function(){return 4*Math.PI*this.boundingSphereRadius/3},n.createTorus=function(e,f,o,d,i){e=e||1,f=f||.5,o=o||8,d=d||6,i=i||2*Math.PI;for(var t=[],l=[],u=0;o>=u;u++)for(var p=0;d>=p;p++){var s=p/d*i,y=u/o*Math.PI*2,c=(e+f*Math.cos(y))*Math.cos(s),a=(e+f*Math.cos(y))*Math.sin(s),r=f*Math.sin(y);t.push(c,a,r)}for(var u=1;o>=u;u++)for(var p=1;d>=p;p++){var w=(d+1)*u+p-1,b=(d+1)*(u-1)+p-1,m=(d+1)*(u-1)+p,N=(d+1)*u+p;l.push(w,b,N),l.push(b,m,N)}return new n(t,l)}},{"../collision/AABB":3,"../math/Quaternion":28,"../math/Transform":29,"../math/Vec3":30,"../utils/Octree":50,"./Shape":43}],46:[function(e,f){function n(){o.call(this),this.iterations=10,this.tolerance=1e-7}f.exports=n;var o=(e("../math/Vec3"),e("../math/Quaternion"),e("./Solver"));n.prototype=new o;var d=[],i=[],t=[];n.prototype.solve=function(e,f){var n,o,l,u,p,s,y=0,c=this.iterations,a=this.tolerance*this.tolerance,r=this.equations,w=r.length,b=f.bodies,m=b.length,N=e;if(0!==w)for(var g=0;g!==m;g++)b[g].updateSolveMassProperties();var x=i,j=t,v=d; -x.length=w,j.length=w,v.length=w;for(var g=0;g!==w;g++){var A=r[g];v[g]=0,j[g]=A.computeB(N),x[g]=1/A.computeC()}if(0!==w){for(var g=0;g!==m;g++){var C=b[g],O=C.vlambda,h=C.wlambda;O.set(0,0,0),h&&h.set(0,0,0)}for(y=0;y!==c;y++){u=0;for(var k=0;k!==w;k++){var A=r[k];n=j[k],o=x[k],s=v[k],p=A.computeGWlambda(),l=o*(n-p-A.eps*s),s+lA.maxForce&&(l=A.maxForce-s),v[k]+=l,u+=l>0?l:-l,A.addToWlambda(l)}if(a>u*u)break}for(var g=0;g!==m;g++){var C=b[g],q=C.velocity,z=C.angularVelocity;q.vadd(C.vlambda,q),z&&z.vadd(C.wlambda,z)}}return y}},{"../math/Quaternion":28,"../math/Vec3":30,"./Solver":47}],47:[function(e,f){function n(){this.equations=[]}f.exports=n,n.prototype.solve=function(){return 0},n.prototype.addEquation=function(e){e.enabled&&this.equations.push(e)},n.prototype.removeEquation=function(e){var f=this.equations,n=f.indexOf(e);-1!==n&&f.splice(n,1)},n.prototype.removeAllEquations=function(){this.equations.length=0}},{}],48:[function(e,f){function n(e){for(l.call(this),this.iterations=10,this.tolerance=1e-7,this.subsolver=e,this.nodes=[],this.nodePool=[];this.nodePool.length<128;)this.nodePool.push(this.createNode())}function o(e){for(var f=e.length,n=0;n!==f;n++){var o=e[n];if(!(o.visited||o.body.type&c))return o}return!1}function d(e,f,n,d){for(a.push(e),e.visited=!0,f(e,n,d);a.length;)for(var i,t=a.pop();i=o(t.children);)i.visited=!0,f(i,n,d),a.push(i)}function i(e,f,n){f.push(e.body);for(var o=e.eqs.length,d=0;d!==o;d++){var i=e.eqs[d];-1===n.indexOf(i)&&n.push(i)}}function t(e,f){return f.id-e.id}f.exports=n;var l=(e("../math/Vec3"),e("../math/Quaternion"),e("./Solver")),u=e("../objects/Body");n.prototype=new l;var p=[],s=[],y={bodies:[]},c=u.STATIC,a=[];n.prototype.createNode=function(){return{body:null,children:[],eqs:[],visited:!1}},n.prototype.solve=function(e,f){for(var n=p,l=this.nodePool,u=f.bodies,c=this.equations,a=c.length,r=u.length,w=this.subsolver;l.lengthb;b++)n[b]=l[b];for(var b=0;b!==r;b++){var m=n[b];m.body=u[b],m.children.length=0,m.eqs.length=0,m.visited=!1}for(var N=0;N!==a;N++){var g=c[N],b=u.indexOf(g.bi),x=u.indexOf(g.bj),j=n[b],v=n[x];j.children.push(v),j.eqs.push(g),v.children.push(j),v.eqs.push(g)}var A,C=0,O=s;w.tolerance=this.tolerance,w.iterations=this.iterations;for(var h=y;A=o(n);){O.length=0,h.bodies.length=0,d(A,i,h.bodies,O);var k=O.length;O=O.sort(t);for(var b=0;b!==k;b++)w.addEquation(O[b]);{w.solve(e,h)}w.removeAllEquations(),C++}return C}},{"../math/Quaternion":28,"../math/Vec3":30,"../objects/Body":31,"./Solver":47}],49:[function(e,f){var n=function(){};f.exports=n,n.prototype={constructor:n,addEventListener:function(e,f){void 0===this._listeners&&(this._listeners={});var n=this._listeners;return void 0===n[e]&&(n[e]=[]),-1===n[e].indexOf(f)&&n[e].push(f),this},hasEventListener:function(e,f){if(void 0===this._listeners)return!1;var n=this._listeners;return void 0!==n[e]&&-1!==n[e].indexOf(f)?!0:!1},removeEventListener:function(e,f){if(void 0===this._listeners)return this;var n=this._listeners;if(void 0===n[e])return this;var o=n[e].indexOf(f);return-1!==o&&n[e].splice(o,1),this},dispatchEvent:function(e){if(void 0===this._listeners)return this;var f=this._listeners,n=f[e.type];if(void 0!==n){e.target=this;for(var o=0,d=n.length;d>o;o++)n[o].call(this,e)}return this}}},{}],50:[function(e,f){function n(e){e=e||{},this.root=e.root||null,this.aabb=e.aabb?e.aabb.clone():new d,this.data=[],this.children=[]}function o(e,f){f=f||{},f.root=null,f.aabb=e,n.call(this,f),this.maxDepth="undefined"!=typeof f.maxDepth?f.maxDepth:8}var d=e("../collision/AABB"),i=e("../math/Vec3");f.exports=o,o.prototype=new n,n.prototype.reset=function(){this.children.length=this.data.length=0},n.prototype.insert=function(e,f,n){var o=this.data;if(n=n||0,!this.aabb.contains(e))return!1;var d=this.children;if(n<(this.maxDepth||this.root.maxDepth)){var i=!1;d.length||(this.subdivide(),i=!0);for(var t=0;8!==t;t++)if(d[t].insert(e,f,n+1))return!0;i&&(d.length=0)}return o.push(f),!0};var t=new i;n.prototype.subdivide=function(){var e=this.aabb,f=e.lowerBound,o=e.upperBound,l=this.children;l.push(new n({aabb:new d({lowerBound:new i(0,0,0)})}),new n({aabb:new d({lowerBound:new i(1,0,0)})}),new n({aabb:new d({lowerBound:new i(1,1,0)})}),new n({aabb:new d({lowerBound:new i(1,1,1)})}),new n({aabb:new d({lowerBound:new i(0,1,1)})}),new n({aabb:new d({lowerBound:new i(0,0,1)})}),new n({aabb:new d({lowerBound:new i(1,0,1)})}),new n({aabb:new d({lowerBound:new i(0,1,0)})})),o.vsub(f,t),t.scale(.5,t);for(var u=this.root||this,p=0;8!==p;p++){var s=l[p];s.root=u;var y=s.aabb.lowerBound;y.x*=t.x,y.y*=t.y,y.z*=t.z,y.vadd(f,y),y.vadd(t,s.aabb.upperBound)}},n.prototype.aabbQuery=function(e,f){for(var n=(this.data,this.children,[this]);n.length;){var o=n.pop();o.aabb.overlaps(e)&&Array.prototype.push.apply(f,o.data),Array.prototype.push.apply(n,o.children)}return f};var l=new d;n.prototype.rayQuery=function(e,f,n){return e.getAABB(l),l.toLocalFrame(f,l),this.aabbQuery(l,n),n},n.prototype.removeEmptyNodes=function(){for(var e=[this];e.length;){for(var f=e.pop(),n=f.children.length-1;n>=0;n--)f.children[n].data.length||f.children.splice(n,1);Array.prototype.push.apply(e,f.children)}}},{"../collision/AABB":3,"../math/Vec3":30}],51:[function(e,f){function n(){this.objects=[],this.type=Object}f.exports=n,n.prototype.release=function(){for(var e=arguments.length,f=0;f!==e;f++)this.objects.push(arguments[f])},n.prototype.get=function(){return 0===this.objects.length?this.constructObject():this.objects.pop()},n.prototype.constructObject=function(){throw new Error("constructObject() not implemented in this Pool subclass yet!")}},{}],52:[function(e,f){function n(){this.data={keys:[]}}f.exports=n,n.prototype.get=function(e,f){if(e>f){var n=f;f=e,e=n}return this.data[e+"-"+f]},n.prototype.set=function(e,f,n){if(e>f){var o=f;f=e,e=o}var d=e+"-"+f;this.get(e,f)||this.data.keys.push(d),this.data[d]=n},n.prototype.reset=function(){for(var e=this.data,f=e.keys;f.length>0;){var n=f.pop();delete e[n]}}},{}],53:[function(e,f){function n(){}f.exports=n,n.defaults=function(e,f){e=e||{};for(var n in f)n in e||(e[n]=f[n]);return e}},{}],54:[function(e,f){function n(){d.call(this),this.type=o}f.exports=n;var o=e("../math/Vec3"),d=e("./Pool");n.prototype=new d,n.prototype.constructObject=function(){return new o}},{"../math/Vec3":30,"./Pool":51}],55:[function(e,f){function n(e){this.contactPointPool=[],this.frictionEquationPool=[],this.result=[],this.frictionResult=[],this.v3pool=new s,this.world=e,this.currentContactMaterial=null,this.enableFrictionReduction=!1}function o(e,f,n){for(var o=null,d=e.length,i=0;i!==d;i++){var t=e[i],l=M;e[(i+1)%d].vsub(t,l);var u=P;l.cross(f,u);var p=Q;n.vsub(t,p);var s=u.dot(p);if(!(null===o||s>0&&o===!0||0>=s&&o===!1))return!1;null===o&&(o=s>0)}return!0}f.exports=n;var d=e("../collision/AABB"),i=e("../shapes/Shape"),t=e("../collision/Ray"),l=e("../math/Vec3"),u=e("../math/Transform"),p=(e("../shapes/ConvexPolyhedron"),e("../math/Quaternion")),s=(e("../solver/Solver"),e("../utils/Vec3Pool")),y=e("../equations/ContactEquation"),c=e("../equations/FrictionEquation");n.prototype.createContactEquation=function(e,f,n,o,d,i){var t;this.contactPointPool.length?(t=this.contactPointPool.pop(),t.bi=e,t.bj=f):t=new y(e,f),t.enabled=e.collisionResponse&&f.collisionResponse&&n.collisionResponse&&o.collisionResponse;var l=this.currentContactMaterial;t.restitution=l.restitution,t.setSpookParams(l.contactEquationStiffness,l.contactEquationRelaxation,this.world.dt);var u=n.material||e.material,p=o.material||f.material;return u&&p&&u.restitution>=0&&p.restitution>=0&&(t.restitution=u.restitution*p.restitution),t.si=d||n,t.sj=i||o,t},n.prototype.createFrictionEquationsFromContact=function(e,f){var n=e.bi,o=e.bj,d=e.si,i=e.sj,t=this.world,l=this.currentContactMaterial,u=l.friction,p=d.material||n.material,s=i.material||o.material;if(p&&s&&p.friction>=0&&s.friction>=0&&(u=p.friction*s.friction),u>0){var y=u*t.gravity.length(),a=n.invMass+o.invMass;a>0&&(a=1/a);var r=this.frictionEquationPool,w=r.length?r.pop():new c(n,o,y*a),b=r.length?r.pop():new c(n,o,y*a);return w.bi=b.bi=n,w.bj=b.bj=o,w.minForce=b.minForce=-y*a,w.maxForce=b.maxForce=y*a,w.ri.copy(e.ri),w.rj.copy(e.rj),b.ri.copy(e.ri),b.rj.copy(e.rj),e.ni.tangents(w.t,b.t),w.setSpookParams(l.frictionEquationStiffness,l.frictionEquationRelaxation,t.dt),b.setSpookParams(l.frictionEquationStiffness,l.frictionEquationRelaxation,t.dt),w.enabled=b.enabled=e.enabled,f.push(w,b),!0}return!1};var a=new l,r=new l,w=new l;n.prototype.createFrictionFromAverage=function(e){var f=this.result[this.result.length-1];if(this.createFrictionEquationsFromContact(f,this.frictionResult)&&1!==e){var n=this.frictionResult[this.frictionResult.length-2],o=this.frictionResult[this.frictionResult.length-1];a.setZero(),r.setZero(),w.setZero();for(var d=f.bi,i=(f.bj,0);i!==e;i++)f=this.result[this.result.length-1-i],f.bodyA!==d?(a.vadd(f.ni,a),r.vadd(f.ri,r),w.vadd(f.rj,w)):(a.vsub(f.ni,a),r.vadd(f.rj,r),w.vadd(f.ri,w));var t=1/e;r.scale(t,n.ri),w.scale(t,n.rj),o.ri.copy(n.ri),o.rj.copy(n.rj),a.normalize(),a.tangents(n.t,o.t)}};var b=new l,m=new l,N=new p,g=new p;n.prototype.getContacts=function(e,f,n,o,d,i,t){this.contactPointPool=d,this.frictionEquationPool=t,this.result=o,this.frictionResult=i;for(var l=N,u=g,p=b,s=m,y=0,c=e.length;y!==c;y++){var a=e[y],r=f[y],w=null;a.material&&r.material&&(w=n.getContactMaterial(a.material,r.material)||null);for(var x=0;xj.boundingSphereRadius+A.boundingSphereRadius)){var C=null;j.material&&A.material&&(C=n.getContactMaterial(j.material,A.material)||null),this.currentContactMaterial=C||w||n.defaultContactMaterial;var O=this[j.type|A.type];O&&(j.type=w){var b=this.createContactEquation(t,p,e,f);b.ni.copy(y);var m=v;y.scale(r.dot(y),m),s.vsub(m,m),b.ri.copy(m),b.ri.vsub(t.position,b.ri),b.rj.copy(s),b.rj.vsub(p.position,b.rj),this.result.push(b),this.createFrictionEquationsFromContact(b,this.frictionResult)}}};var A=new l,C=new l,O=(new l,new l),h=new l,k=new l,q=new l,z=new l,B=new l,D=new l,E=new l,F=new l,G=new l,H=new l,I=new d,J=[];n.prototype[i.types.SPHERE|i.types.TRIMESH]=n.prototype.sphereTrimesh=function(e,f,n,o,d,i,l,p){var s=k,y=q,c=z,a=B,r=D,w=E,b=I,m=h,N=C,g=J;u.pointToLocalFrame(o,i,n,r);var x=e.radius;b.lowerBound.set(r.x-x,r.y-x,r.z-x),b.upperBound.set(r.x+x,r.y+x,r.z+x),f.getTrianglesInAABB(b,g);for(var j=O,v=e.radius*e.radius,K=0;KL;L++)if(f.getVertex(f.indices[3*g[K]+L],j),j.vsub(r,N),N.norm2()<=v){m.copy(j),u.pointToWorldFrame(o,i,m,j),j.vsub(n,N);var M=this.createContactEquation(l,p,e,f);M.ni.copy(N),M.ni.normalize(),M.ri.copy(M.ni),M.ri.scale(e.radius,M.ri),M.ri.vadd(n,M.ri),M.ri.vsub(l.position,M.ri),M.rj.copy(j),M.rj.vsub(p.position,M.rj),this.result.push(M),this.createFrictionEquationsFromContact(M,this.frictionResult)}for(var K=0;KL;L++){f.getVertex(f.indices[3*g[K]+L],s),f.getVertex(f.indices[3*g[K]+(L+1)%3],y),y.vsub(s,c),r.vsub(y,w);var P=w.dot(c);r.vsub(s,w);var Q=w.dot(c);if(Q>0&&0>P){r.vsub(s,w),a.copy(c),a.normalize(),Q=w.dot(a),a.scale(Q,w),w.vadd(s,w);var R=w.distanceTo(r);if(RC&&C>0){var O=T,h=U;O.copy(p[(x+1)%3]),h.copy(p[(x+2)%3]);var k=O.norm(),q=h.norm();O.normalize(),h.normalize();var z=R.dot(O),B=R.dot(h);if(k>z&&z>-k&&q>B&&B>-q){var D=Math.abs(C-A-s);(null===g||g>D)&&(g=D,m=z,N=B,w=A,c.copy(v),a.copy(O),r.copy(h),b++)}}}if(b){y=!0;var E=this.createContactEquation(t,l,e,f);c.mult(-s,E.ri),E.ni.copy(c),E.ni.negate(E.ni),c.mult(w,c),a.mult(m,a),c.vadd(a,c),r.mult(N,r),c.vadd(r,E.rj),E.ri.vadd(n,E.ri),E.ri.vsub(t.position,E.ri),E.rj.vadd(o,E.rj),E.rj.vsub(l.position,E.rj),this.result.push(E),this.createFrictionEquationsFromContact(E,this.frictionResult)}for(var F=u.get(),G=W,H=0;2!==H&&!y;H++)for(var I=0;2!==I&&!y;I++)for(var J=0;2!==J&&!y;J++)if(F.set(0,0,0),H?F.vadd(p[0],F):F.vsub(p[0],F),I?F.vadd(p[1],F):F.vsub(p[1],F),J?F.vadd(p[2],F):F.vsub(p[2],F),o.vadd(F,G),G.vsub(n,G),G.norm2()_){y=!0;var ef=this.createContactEquation(t,l,e,f);L.vadd(M,ef.rj),ef.rj.copy(ef.rj),D.negate(ef.ni),ef.ni.normalize(),ef.ri.copy(ef.rj),ef.ri.vadd(o,ef.ri),ef.ri.vsub(n,ef.ri),ef.ri.normalize(),ef.ri.mult(s,ef.ri),ef.ri.vadd(n,ef.ri),ef.ri.vsub(t.position,ef.ri),ef.rj.vadd(o,ef.rj),ef.rj.vsub(l.position,ef.rj),this.result.push(ef),this.createFrictionEquationsFromContact(ef,this.frictionResult)}}u.release(K,L,E,M,D)};var $=new l,_=new l,ef=new l,ff=new l,nf=new l,of=new l,df=new l,tf=new l,lf=new l,uf=new l;n.prototype[i.types.SPHERE|i.types.CONVEXPOLYHEDRON]=n.prototype.sphereConvex=function(e,f,n,d,i,t,l,u){var p=this.v3pool;n.vsub(d,$);for(var s=f.faceNormals,y=f.faces,c=f.vertices,a=e.radius,r=0;r!==c.length;r++){var w=c[r],b=nf;t.vmult(w,b),d.vadd(b,b);var m=ff;if(b.vsub(n,m),m.norm2()k&&q.dot(A)>0){for(var z=[],B=0,D=v.length;B!==D;B++){var E=p.get();t.vmult(c[v[B]],E),d.vadd(E,E),z.push(E)}if(o(z,A,n)){g=!0;var N=this.createContactEquation(l,u,e,f);A.mult(-a,N.ri),A.negate(N.ni);var F=p.get();A.mult(-k,F);var G=p.get();A.mult(-a,G),n.vsub(d,N.rj),N.rj.vadd(G,N.rj),N.rj.vadd(F,N.rj),N.rj.vadd(d,N.rj),N.rj.vsub(u.position,N.rj),N.ri.vadd(n,N.ri),N.ri.vsub(l.position,N.ri),p.release(F),p.release(G),this.result.push(N),this.createFrictionEquationsFromContact(N,this.frictionResult);for(var B=0,H=z.length;B!==H;B++)p.release(z[B]);return}for(var B=0;B!==v.length;B++){var I=p.get(),J=p.get();t.vmult(c[v[(B+1)%v.length]],I),t.vmult(c[v[(B+2)%v.length]],J),d.vadd(I,I),d.vadd(J,J);var K=_;J.vsub(I,K);var L=ef;K.unit(L);var M=p.get(),P=p.get();n.vsub(I,P);var Q=P.dot(L);L.mult(Q,M),M.vadd(I,M);var R=p.get();if(M.vsub(n,R),Q>0&&Q*Q=a){var r=this.createContactEquation(t,l,e,f),w=cf;p.mult(p.dot(y),w),u.vsub(w,w),w.vsub(n,r.ri),r.ni.copy(p),u.vsub(o,r.rj),r.ri.vadd(n,r.ri),r.ri.vsub(t.position,r.ri),r.rj.vadd(o,r.rj),r.rj.vsub(l.position,r.rj),this.result.push(r),s++,this.enableFrictionReduction||this.createFrictionEquationsFromContact(r,this.frictionResult)}}this.enableFrictionReduction&&s&&this.createFrictionFromAverage(s)};var af=new l,rf=new l;n.prototype[i.types.CONVEXPOLYHEDRON]=n.prototype.convexConvex=function(e,f,n,o,d,i,t,l,u,p,s,y){var c=af;if(!(n.distanceTo(o)>e.boundingSphereRadius+f.boundingSphereRadius)&&e.findSeparatingAxis(f,n,d,o,i,c,s,y)){var a=[],r=rf;e.clipAgainstHull(n,d,f,o,i,c,-100,100,a);for(var w=0,b=0;b!==a.length;b++){var m=this.createContactEquation(t,l,e,f,u,p),N=m.ri,g=m.rj;c.negate(m.ni),a[b].normal.negate(r),r.mult(a[b].depth,r),a[b].point.vadd(r,N),g.copy(a[b].point),N.vsub(n,N),g.vsub(o,g),N.vadd(n,N),N.vsub(t.position,N),g.vadd(o,g),g.vsub(l.position,g),this.result.push(m),w++,this.enableFrictionReduction||this.createFrictionEquationsFromContact(m,this.frictionResult)}this.enableFrictionReduction&&w&&this.createFrictionFromAverage(w)}};var wf=new l,bf=new l,mf=new l;n.prototype[i.types.PLANE|i.types.PARTICLE]=n.prototype.planeParticle=function(e,f,n,o,d,i,t,l){var u=wf;u.set(0,0,1),t.quaternion.vmult(u,u);var p=bf;o.vsub(t.position,p);var s=u.dot(p);if(0>=s){var y=this.createContactEquation(l,t,f,e);y.ni.copy(u),y.ni.negate(y.ni),y.ri.set(0,0,0);var c=mf;u.mult(u.dot(o),c),o.vsub(c,c),y.rj.copy(c),this.result.push(y),this.createFrictionEquationsFromContact(y,this.frictionResult)}};var Nf=new l;n.prototype[i.types.PARTICLE|i.types.SPHERE]=n.prototype.sphereParticle=function(e,f,n,o,d,i,t,l){var u=Nf;u.set(0,0,1),o.vsub(n,u);var p=u.norm2();if(p<=e.radius*e.radius){var s=this.createContactEquation(l,t,f,e);u.normalize(),s.rj.copy(u),s.rj.mult(e.radius,s.rj),s.ni.copy(u),s.ni.negate(s.ni),s.ri.set(0,0,0),this.result.push(s),this.createFrictionEquationsFromContact(s,this.frictionResult)}};var gf=new p,xf=new l,jf=(new l,new l),vf=new l,Af=new l;n.prototype[i.types.PARTICLE|i.types.CONVEXPOLYHEDRON]=n.prototype.convexParticle=function(e,f,n,o,d,i,t,l){var u=-1,p=jf,s=Af,y=null,c=0,a=xf;if(a.copy(o),a.vsub(n,a),d.conjugate(gf),gf.vmult(a,a),e.pointIsInside(a)){e.worldVerticesNeedsUpdate&&e.computeWorldVertices(n,d),e.worldFaceNormalsNeedsUpdate&&e.computeWorldFaceNormals(d);for(var r=0,w=e.faces.length;r!==w;r++){var b=[e.worldVertices[e.faces[r][0]]],m=e.worldFaceNormals[r];o.vsub(b[0],vf);var N=-m.dot(vf);(null===y||Math.abs(N)b||0>N||w>p.length||m>p[0].length)){0>w&&(w=0),0>b&&(b=0),0>m&&(m=0),0>N&&(N=0),w>=p.length&&(w=p.length-1),b>=p.length&&(b=p.length-1),N>=p[0].length&&(N=p[0].length-1),m>=p[0].length&&(m=p[0].length-1);var g=[];f.getRectMinMax(w,m,b,N,g);var x=g[0],j=g[1];if(!(r.z-y>j||r.z+yv;v++)for(var A=m;N>A;A++)f.getConvexTrianglePillar(v,A,!1),u.pointToWorldFrame(o,i,f.pillarOffset,c),n.distanceTo(c)w||0>m||r>p.length||m>p[0].length)){0>r&&(r=0),0>w&&(w=0),0>b&&(b=0),0>m&&(m=0),r>=p.length&&(r=p.length-1),w>=p.length&&(w=p.length-1),m>=p[0].length&&(m=p[0].length-1),b>=p[0].length&&(b=p[0].length-1);var N=[];f.getRectMinMax(r,b,w,m,N);var g=N[0],x=N[1];if(!(a.z-s>x||a.z+sv;v++)for(var A=b;m>A;A++){var C=j.length;f.getConvexTrianglePillar(v,A,!1),u.pointToWorldFrame(o,i,f.pillarOffset,c),n.distanceTo(c)2)return}}}},{"../collision/AABB":3,"../collision/Ray":9,"../equations/ContactEquation":19,"../equations/FrictionEquation":21,"../math/Quaternion":28,"../math/Transform":29,"../math/Vec3":30,"../shapes/ConvexPolyhedron":38,"../shapes/Shape":43,"../solver/Solver":47,"../utils/Vec3Pool":54}],56:[function(e,f){function n(){u.apply(this),this.dt=-1,this.allowSleep=!1,this.contacts=[],this.frictionEquations=[],this.quatNormalizeSkip=0,this.quatNormalizeFast=!1,this.time=0,this.stepnumber=0,this.default_dt=1/60,this.nextId=0,this.gravity=new d,this.broadphase=new m,this.bodies=[],this.solver=new t,this.constraints=[],this.narrowphase=new l(this),this.collisionMatrix=new p,this.collisionMatrixPrevious=new p,this.materials=[],this.contactmaterials=[],this.contactMaterialTable=new a,this.defaultMaterial=new s("default"),this.defaultContactMaterial=new y(this.defaultMaterial,this.defaultMaterial,{friction:.3,restitution:0}),this.doProfiling=!1,this.profile={solve:0,makeContactConstraints:0,broadphase:0,integrate:0,narrowphase:0},this.subsystems=[],this.addBodyEvent={type:"addBody",body:null},this.removeBodyEvent={type:"removeBody",body:null}}f.exports=n;var o=e("../shapes/Shape"),d=e("../math/Vec3"),i=e("../math/Quaternion"),t=e("../solver/GSSolver"),l=(e("../utils/Vec3Pool"),e("../equations/ContactEquation"),e("../equations/FrictionEquation"),e("./Narrowphase")),u=e("../utils/EventTarget"),p=e("../collision/ArrayCollisionMatrix"),s=e("../material/Material"),y=e("../material/ContactMaterial"),c=e("../objects/Body"),a=e("../utils/TupleDictionary"),r=e("../collision/RaycastResult"),w=e("../collision/AABB"),b=e("../collision/Ray"),m=e("../collision/NaiveBroadphase");n.prototype=new u;var N=(new w,new b);if(n.prototype.getContactMaterial=function(e,f){return this.contactMaterialTable.get(e.id,f.id)},n.prototype.numObjects=function(){return this.bodies.length},n.prototype.collisionMatrixTick=function(){var e=this.collisionMatrixPrevious;this.collisionMatrixPrevious=this.collisionMatrix,this.collisionMatrix=e,this.collisionMatrix.reset()},n.prototype.add=n.prototype.addBody=function(e){-1===this.bodies.indexOf(e)&&(e.index=this.bodies.length,this.bodies.push(e),e.world=this,e.initPosition.copy(e.position),e.initVelocity.copy(e.velocity),e.timeLastSleepy=this.time,e instanceof c&&(e.initAngularVelocity.copy(e.angularVelocity),e.initQuaternion.copy(e.quaternion)),this.collisionMatrix.setNumObjects(this.bodies.length),this.addBodyEvent.body=e,this.dispatchEvent(this.addBodyEvent))},n.prototype.addConstraint=function(e){this.constraints.push(e)},n.prototype.removeConstraint=function(e){var f=this.constraints.indexOf(e);-1!==f&&this.constraints.splice(f,1)},n.prototype.rayTest=function(e,f,n){n instanceof r?this.raycastClosest(e,f,{skipBackfaces:!0},n):this.raycastAll(e,f,{skipBackfaces:!0},n)},n.prototype.raycastAll=function(e,f,n,o){return n.mode=b.ALL,n.from=e,n.to=f,n.callback=o,N.intersectWorld(this,n)},n.prototype.raycastAny=function(e,f,n,o){return n.mode=b.ANY,n.from=e,n.to=f,n.result=o,N.intersectWorld(this,n)},n.prototype.raycastClosest=function(e,f,n,o){return n.mode=b.CLOSEST,n.from=e,n.to=f,n.result=o,N.intersectWorld(this,n)},n.prototype.remove=function(e){e.world=null;var f=this.bodies.length-1,n=this.bodies,o=n.indexOf(e);if(-1!==o){n.splice(o,1);for(var d=0;d!==n.length;d++)n[d].index=d;this.collisionMatrix.setNumObjects(f),this.removeBodyEvent.body=e,this.dispatchEvent(this.removeBodyEvent)}},n.prototype.removeBody=n.prototype.remove,n.prototype.addMaterial=function(e){this.materials.push(e)},n.prototype.addContactMaterial=function(e){this.contactmaterials.push(e),this.contactMaterialTable.set(e.materials[0].id,e.materials[1].id,e)},"undefined"==typeof performance&&(performance={}),!performance.now){var g=Date.now();performance.timing&&performance.timing.navigationStart&&(g=performance.timing.navigationStart),performance.now=function(){return Date.now()-g}}var x=new d;n.prototype.step=function(e,f,n){if(n=n||10,f=f||0,0===f)this.internalStep(e),this.time+=e;else{var o=Math.floor((this.time+f)/e)-Math.floor(this.time/e);o=Math.min(o,n);for(var d=performance.now(),i=0;i!==o&&(this.internalStep(e),!(performance.now()-d>1e3*e));i++);this.time+=f;for(var t=this.time%e,l=t/e,u=x,p=this.bodies,s=0;s!==p.length;s++){var y=p[s];y.type!==c.STATIC&&y.sleepState!==c.SLEEPING?(y.position.vsub(y.previousPosition,u),u.scale(l,u),y.position.vadd(u,y.interpolatedPosition)):(y.interpolatedPosition.copy(y.position),y.interpolatedQuaternion.copy(y.quaternion))}}};var j={type:"postStep"},v={type:"preStep"},A={type:"collide",body:null,contact:null},C=[],O=[],h=[],k=[],q=(new d,new d,new d,new d,new d,new d,new d,new d,new d,new i,new i),z=new i,B=new d;n.prototype.internalStep=function(e){this.dt=e;var f,n=this.contacts,d=h,i=k,t=this.numObjects(),l=this.bodies,u=this.solver,p=this.gravity,s=this.doProfiling,y=this.profile,a=c.DYNAMIC,r=this.constraints,w=O,b=(p.norm(),p.x),m=p.y,N=p.z,g=0;for(s&&(f=performance.now()),g=0;g!==t;g++){var x=l[g];if(x.type&a){var D=x.force,E=x.mass;D.x+=E*b,D.y+=E*m,D.z+=E*N}}for(var g=0,F=this.subsystems.length;g!==F;g++)this.subsystems[g].update();s&&(f=performance.now()),d.length=0,i.length=0,this.broadphase.collisionPairs(this,d,i),s&&(y.broadphase=performance.now()-f);var G=r.length;for(g=0;g!==G;g++){var H=r[g];if(!H.collideConnected)for(var I=d.length-1;I>=0;I-=1)(H.bodyA===d[I]&&H.bodyB===i[I]||H.bodyB===d[I]&&H.bodyA===i[I])&&(d.splice(I,1),i.splice(I,1))}this.collisionMatrixTick(),s&&(f=performance.now());var J=C,K=n.length;for(g=0;g!==K;g++)J.push(n[g]);n.length=0;var L=this.frictionEquations.length;for(g=0;g!==L;g++)w.push(this.frictionEquations[g]);this.frictionEquations.length=0,this.narrowphase.getContacts(d,i,this,n,J,this.frictionEquations,w),s&&(y.narrowphase=performance.now()-f),s&&(f=performance.now());for(var g=0;g=0&&R.material.friction>=0&&(S=x.material.friction*R.material.friction),x.material.restitution>=0&&R.material.restitution>=0&&(H.restitution=x.material.restitution*R.material.restitution)),u.addEquation(H),x.allowSleep&&x.type===c.DYNAMIC&&x.sleepState===c.SLEEPING&&R.sleepState===c.AWAKE&&R.type!==c.STATIC){var T=R.velocity.norm2()+R.angularVelocity.norm2(),U=Math.pow(R.sleepSpeedLimit,2); -T>=2*U&&(x._wakeUpAfterNarrowphase=!0)}if(R.allowSleep&&R.type===c.DYNAMIC&&R.sleepState===c.SLEEPING&&x.sleepState===c.AWAKE&&x.type!==c.STATIC){var V=x.velocity.norm2()+x.angularVelocity.norm2(),W=Math.pow(x.sleepSpeedLimit,2);V>=2*W&&(R._wakeUpAfterNarrowphase=!0)}this.collisionMatrix.set(x,R,!0),this.collisionMatrixPrevious.get(x,R)||(A.body=R,A.contact=H,x.dispatchEvent(A),A.body=x,R.dispatchEvent(A))}for(s&&(y.makeContactConstraints=performance.now()-f,f=performance.now()),g=0;g!==t;g++){var x=l[g];x._wakeUpAfterNarrowphase&&(x.wakeUp(),x._wakeUpAfterNarrowphase=!1)}var G=r.length;for(g=0;g!==G;g++){var H=r[g];H.update();for(var I=0,X=H.equations.length;I!==X;I++){var Y=H.equations[I];u.addEquation(Y)}}u.solve(e,this),s&&(y.solve=performance.now()-f),u.removeAllEquations();var Z=Math.pow;for(g=0;g!==t;g++){var x=l[g];if(x.type&a){var $=Z(1-x.linearDamping,e),_=x.velocity;_.mult($,_);var ef=x.angularVelocity;if(ef){var ff=Z(1-x.angularDamping,e);ef.mult(ff,ef)}}}for(this.dispatchEvent(v),g=0;g!==t;g++){var x=l[g];x.preStep&&x.preStep.call(x)}s&&(f=performance.now());{var nf=q,of=z,df=this.stepnumber,tf=c.DYNAMIC|c.KINEMATIC,lf=df%(this.quatNormalizeSkip+1)===0,uf=this.quatNormalizeFast,pf=.5*e;o.types.PLANE,o.types.CONVEXPOLYHEDRON}for(g=0;g!==t;g++){var sf=l[g],yf=sf.force,cf=sf.torque;if(sf.type&tf&&sf.sleepState!==c.SLEEPING){var af=sf.velocity,rf=sf.angularVelocity,wf=sf.position,bf=sf.quaternion,mf=sf.invMass,Nf=sf.invInertiaWorld;af.x+=yf.x*mf*e,af.y+=yf.y*mf*e,af.z+=yf.z*mf*e,sf.angularVelocity&&(Nf.vmult(cf,B),B.mult(e,B),B.vadd(rf,rf)),wf.x+=af.x*e,wf.y+=af.y*e,wf.z+=af.z*e,sf.angularVelocity&&(nf.set(rf.x,rf.y,rf.z,0),nf.mult(bf,of),bf.x+=pf*of.x,bf.y+=pf*of.y,bf.z+=pf*of.z,bf.w+=pf*of.w,lf&&(uf?bf.normalizeFast():bf.normalize())),sf.aabb&&(sf.aabbNeedsUpdate=!0),sf.updateInertiaWorld&&sf.updateInertiaWorld()}}for(this.clearForces(),this.broadphase.dirty=!0,s&&(y.integrate=performance.now()-f),this.time+=e,this.stepnumber+=1,this.dispatchEvent(j),g=0;g!==t;g++){var x=l[g],gf=x.postStep;gf&&gf.call(x)}if(this.allowSleep)for(g=0;g!==t;g++)l[g].sleepTick(this.time)},n.prototype.clearForces=function(){for(var e=this.bodies,f=e.length,n=0;n!==f;n++){{var o=e[n];o.force,o.torque}o.force.set(0,0,0),o.torque.set(0,0,0)}}},{"../collision/AABB":3,"../collision/ArrayCollisionMatrix":4,"../collision/NaiveBroadphase":7,"../collision/Ray":9,"../collision/RaycastResult":10,"../equations/ContactEquation":19,"../equations/FrictionEquation":21,"../material/ContactMaterial":24,"../material/Material":25,"../math/Quaternion":28,"../math/Vec3":30,"../objects/Body":31,"../shapes/Shape":43,"../solver/GSSolver":46,"../utils/EventTarget":49,"../utils/TupleDictionary":52,"../utils/Vec3Pool":54,"./Narrowphase":55}]},{},[2])(2)}); \ No newline at end of file diff --git a/demos/bodyTypes.html b/demos/bodyTypes.html index 8df6ff57c..c30f6c26b 100644 --- a/demos/bodyTypes.html +++ b/demos/bodyTypes.html @@ -1,93 +1,100 @@ - - cannon.js - body types demo - - - - - - - - - - - - - - - - + + + cannon.js - body types demo + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/demos/bounce.html b/demos/bounce.html index b4fa1be7e..b19b4da7f 100644 --- a/demos/bounce.html +++ b/demos/bounce.html @@ -1,90 +1,95 @@ - - cannon.js - bounce demo - - - - - - - - - - - - - - + + + + + + + + + - - + world.addContactMaterial(mat1_ground); + world.addContactMaterial(mat2_ground); + world.addContactMaterial(mat3_ground); + }); + + demo.start(); + + + + + \ No newline at end of file diff --git a/demos/bunny.html b/demos/bunny.html index 28768c7ba..428663aef 100644 --- a/demos/bunny.html +++ b/demos/bunny.html @@ -1,30 +1,34 @@ - - cannon.js - bunny demo - - - - - - - - - - - - - - - + + + + + + + + + + - - + + + + \ No newline at end of file diff --git a/demos/callbacks.html b/demos/callbacks.html index b0f0731b0..3e711846b 100644 --- a/demos/callbacks.html +++ b/demos/callbacks.html @@ -1,75 +1,81 @@ - - cannon.js - callbacks demo - - - - - - - - - - - - - - - - + + + cannon.js - callbacks demo + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/demos/collisionFilter.html b/demos/collisionFilter.html index 39cd89f27..0640c7270 100644 --- a/demos/collisionFilter.html +++ b/demos/collisionFilter.html @@ -1,13 +1,16 @@ - + + cannon.js - collisionfilter demo - + - - - + + + + + @@ -35,9 +38,10 @@ var GROUP2 = 2; var GROUP3 = 4; - demo.addScene("filter",function(){ + demo.addScene("filter", function () + { var world = demo.getWorld(); - world.gravity.set(0,0,0); // no gravity + world.gravity.set(0, 0, 0); // no gravity world.broadphase = new CANNON.NaiveBroadphase(); world.solver.iterations = 5; @@ -45,8 +49,8 @@ var sphereShape = new CANNON.Sphere(size); var sphereBody = new CANNON.Body({ mass: mass, - position: new CANNON.Vec3(5,0,0), - velocity: new CANNON.Vec3(-5,0,0), + position: new CANNON.Vector3(5, 0, 0), + velocity: new CANNON.Vector3(-5, 0, 0), collisionFilterGroup: GROUP1, // Put the sphere in group 1 collisionFilterMask: GROUP2 | GROUP3, // It can only collide with group 2 and 3 shape: sphereShape @@ -55,19 +59,19 @@ // Box var boxBody = new CANNON.Body({ mass: mass, - shape: new CANNON.Box(new CANNON.Vec3(size,size,size)), + shape: new CANNON.Box(new CANNON.Vector3(size, size, size)), collisionFilterGroup: GROUP2, // Put the box in group 2 - collisionFilterMask: GROUP1 // It can only collide with group 1 (the sphere) + collisionFilterMask: GROUP1 // It can only collide with group 1 (the sphere) }); // Cylinder - var cylinderShape = new CANNON.Cylinder(size,size,size*2.2,10); + var cylinderShape = new CANNON.Cylinder(size, size, size * 2.2, 10); var cylinderBody = new CANNON.Body({ mass: mass, shape: cylinderShape, - position: new CANNON.Vec3(-5,0,0), + position: new CANNON.Vector3(-5, 0, 0), collisionFilterGroup: GROUP3, // Put the cylinder in group 3 - collisionFilterMask: GROUP1 // It can only collide with group 1 (the sphere) + collisionFilterMask: GROUP1 // It can only collide with group 1 (the sphere) }); // Add everything to the world and demo @@ -82,5 +86,6 @@ demo.start(); - - + + + \ No newline at end of file diff --git a/demos/collisions.html b/demos/collisions.html index 8e5255e85..050d30a92 100644 --- a/demos/collisions.html +++ b/demos/collisions.html @@ -1,148 +1,157 @@ - - cannon.js - collisions demo - - - - - - - - - - - - - - - - + + + cannon.js - collisions demo + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/demos/compound.html b/demos/compound.html index 5acaf8d54..35446285c 100644 --- a/demos/compound.html +++ b/demos/compound.html @@ -1,21 +1,24 @@ - - cannon.js - compound demo - - - - - - - - - - - - - - + + + + + + + + + - - + var demo = new CANNON.Demo(); + + // A compound shape consisting of a number of boxes. + demo.addScene("Boxes", function () + { + var world = setupWorld(demo); + + // Create the compound shape + //var compoundShape = new CANNON.Compound(); + var s = 1.5; + + // Now create a Body for our Compound + var mass = 10; + var body = new CANNON.Body({ mass: mass }); + body.position.set(0, 0, 6); + body.quaternion.fromAxisAngle(new CANNON.Vector3(0, 1, 0), Math.PI / 32); + + // Use a box shape as child shape + var shape = new CANNON.Box(new CANNON.Vector3(0.5 * s, 0.5 * s, 0.5 * s)); + + // We can add the same shape several times to position child shapes within the Compound. + body.addShape(shape, new CANNON.Vector3(s, 0, -s)); + body.addShape(shape, new CANNON.Vector3(s, 0, s)); + body.addShape(shape, new CANNON.Vector3(-s, 0, -s)); + body.addShape(shape, new CANNON.Vector3(-s, 0, s)); + // Note: we only use translational offsets. The third argument could be a CANNON.Quaternion to rotate the child shape. + body.addShape(shape, new CANNON.Vector3(-s, 0, 0)); + body.addShape(shape, new CANNON.Vector3(0, 0, -s)); + body.addShape(shape, new CANNON.Vector3(0, 0, s)); + + world.addBody(body); + demo.addVisual(body); + + }); + + // Here we create a compound made out of spheres + demo.addScene("Spheres", function () + { + var world = setupWorld(demo); + + var mass = 10; + var body = new CANNON.Body({ mass: mass }); + + // Compound shape + var sphereShape = new CANNON.Sphere(1); + body.addShape(sphereShape, new CANNON.Vector3(1, 0, -1)); + body.addShape(sphereShape, new CANNON.Vector3(1, 0, 1)); + body.addShape(sphereShape, new CANNON.Vector3(-1, 0, -1)); + body.addShape(sphereShape, new CANNON.Vector3(-1, 0, 1)); + + body.position.set(0, 0, 6); + body.quaternion.set(0, 1, 0, 0.1); + world.addBody(body); + demo.addVisual(body); + + }); + + function setupWorld(demo) + { + var world = demo.getWorld(); + world.gravity.set(0, 0, -30); + world.broadphase = new CANNON.NaiveBroadphase(); + world.solver.iterations = 10; + + // ground plane + var groundShape = new CANNON.Plane(); + var groundBody = new CANNON.Body({ mass: 0 }); + groundBody.addShape(groundShape); + world.addBody(groundBody); + demo.addVisual(groundBody); + + return world; + }; + + demo.start(); + + + + + \ No newline at end of file diff --git a/demos/constraints.html b/demos/constraints.html index 739730d98..7edcaf680 100644 --- a/demos/constraints.html +++ b/demos/constraints.html @@ -1,335 +1,375 @@ - - cannon.js - constraints demo - - - - - - - - - - - - - - + + + + + + + + + + - - - + \ No newline at end of file diff --git a/demos/container.html b/demos/container.html index a5e3e3ca4..9a310dcb8 100644 --- a/demos/container.html +++ b/demos/container.html @@ -1,131 +1,139 @@ - - cannon.js - container demo - - - - - - - - - - - - - - + + + + + + + + + + - - - + \ No newline at end of file diff --git a/demos/convex.html b/demos/convex.html index e0ac0a96e..56754abe9 100644 --- a/demos/convex.html +++ b/demos/convex.html @@ -1,155 +1,168 @@ - - cannon.js - convex demo - - - - - - - - - - - - - - + + + + + + + + + + - // ConvexPolyhedron tetra shape - var tetraShape = createTetra(); - var tetraBody = new CANNON.Body({ mass: mass }); - tetraBody.addShape(tetraShape); - tetraBody.position.set(5,-3,size+1); - world.addBody(tetraBody); - demo.addVisual(tetraBody); - - // ConvexPolyhedron cylinder shape - var num = 20; - var verts = []; - var normals = []; - var faces = []; - var bottomface = []; - var topface = []; - var L = 2, R = 0.5; - var cylinderShape = new CANNON.Cylinder(R,R,L,num); - var cylinderBody = new CANNON.Body({ mass: mass }); - cylinderBody.addShape(cylinderShape); - cylinderBody.position.set(0,0,size*4+1); - cylinderBody.quaternion.setFromAxisAngle(new CANNON.Vec3(0,1,0),Math.PI/3); - world.addBody(cylinderBody); - demo.addVisual(cylinderBody); - - }); - - - // Box on box tilting over - demo.addScene("convex on convex",function(){ - var world = setupWorld(demo); - // ConvexPolyhedron box shape - var size = 2; - var convexShape = createBoxPolyhedron(size); - var mass = 10; - var boxbody1 = new CANNON.Body({ mass: mass }); - boxbody1.addShape(convexShape); - var boxbody2 = new CANNON.Body({ mass: mass }); - boxbody2.addShape(convexShape); - boxbody1.position.set(0,0,size+1); - boxbody2.position.set(1.5,0,4*size+1); - world.addBody(boxbody1); - world.addBody(boxbody2); - demo.addVisual(boxbody1); - demo.addVisual(boxbody2); - }); - - // Pile of boxes - demo.addScene("convex wall",function(){ - var world = setupWorld(demo); - // ConvexPolyhedron box shape - var size = 1; - var convexShape = createBoxPolyhedron(size); - var mass = 10; - for(var i=0; i<3; i++){ - for(var j=0; j<3; j++){ - var boxbody = new CANNON.Body({ mass: mass }); - boxbody.addShape(convexShape); - boxbody.position.set(2*size*i+0.01,0,2*size*j + size*1.2); - world.addBody(boxbody); - demo.addVisual(boxbody); - } - } - }); - - function setupWorld(demo){ - var world = demo.getWorld(); - world.gravity.set(0,0,-30); - world.broadphase = new CANNON.NaiveBroadphase(); - world.solver.iterations = 10; - - world.defaultContactMaterial.contactEquationStiffness = 5e6; - world.defaultContactMaterial.contactEquationRelaxation = 3; - - // ground plane - var n = new CANNON.Vec3(0,0,1); - n.normalize(); - var groundShape = new CANNON.Plane(n); - var groundBody = new CANNON.Body({ mass: 0 }); - groundBody.addShape(groundShape); - groundBody.position.set(-10,0,0); - world.addBody(groundBody); - demo.addVisual(groundBody); - - return world; - }; - - demo.start(); - - - - + \ No newline at end of file diff --git a/demos/events.html b/demos/events.html index 40b8c96f0..366cc4073 100644 --- a/demos/events.html +++ b/demos/events.html @@ -1,66 +1,72 @@ - - cannon.js - events demo - - - - - - - - - - - - - - + + + + + + + + + + - - - + \ No newline at end of file diff --git a/demos/fixedRotation.html b/demos/fixedRotation.html index b8833b352..4c8b995ba 100644 --- a/demos/fixedRotation.html +++ b/demos/fixedRotation.html @@ -1,64 +1,69 @@ - - cannon.js - fixedRotation demo - - - - - - - - - - - - - - + + + + + + + + + - - + // Change gravity so the boxes will slide along x axis + world.gravity.set(0, 0, -10); + }); + + demo.start(); + + + + + \ No newline at end of file diff --git a/demos/friction.html b/demos/friction.html index aaf3666c0..7955bd885 100644 --- a/demos/friction.html +++ b/demos/friction.html @@ -1,94 +1,99 @@ - - cannon.js - friction demo - - - - - - - - - - - - - - + + + + + + + + + + - - - + \ No newline at end of file diff --git a/demos/heightfield.html b/demos/heightfield.html index 4348fd301..4c5f7bcf9 100644 --- a/demos/heightfield.html +++ b/demos/heightfield.html @@ -1,13 +1,16 @@ - + + cannon.js - Heightfield demo - + - - - + + + + + @@ -19,7 +22,8 @@ var demo = new CANNON.Demo(); - demo.addScene("Heightfield", function () { + demo.addScene("Heightfield", function () + { // Init world var world = demo.getWorld(); @@ -30,11 +34,13 @@ var matrix = []; var sizeX = 15, sizeY = 15; - for (var i = 0; i < sizeX; i++) { + for (var i = 0; i < sizeX; i++) + { matrix.push([]); - for (var j = 0; j < sizeY; j++) { - var height = Math.cos(i/sizeX * Math.PI * 2)*Math.cos(j/sizeY * Math.PI * 2) + 2; - if(i===0 || i === sizeX-1 || j===0 || j === sizeY-1) + for (var j = 0; j < sizeY; j++) + { + var height = Math.cos(i / sizeX * Math.PI * 2) * Math.cos(j / sizeY * Math.PI * 2) + 2; + if (i === 0 || i === sizeX - 1 || j === 0 || j === sizeY - 1) height = 3; matrix[i].push(height); } @@ -52,9 +58,11 @@ // Add spheres var mass = 1; - for(var i=0; i= sizeX-2 || j===0 || j >= sizeY-2) + for (var i = 0; i < sizeX - 1; i++) + { + for (var j = 0; j < sizeY - 1; j++) + { + if (i === 0 || i >= sizeX - 2 || j === 0 || j >= sizeY - 2) continue; var sphereShape = new CANNON.Sphere(0.1); var sphereBody = new CANNON.Body({ mass: mass }); @@ -67,8 +75,9 @@ } }); - demo.start(); + demo.start(); - - + + + \ No newline at end of file diff --git a/demos/hinge.html b/demos/hinge.html index b74ccbfa3..f24b44d05 100644 --- a/demos/hinge.html +++ b/demos/hinge.html @@ -1,13 +1,16 @@ - + + cannon.js - hinge demo - + - - - + + + + + @@ -25,61 +28,64 @@ var demo = new CANNON.Demo(); var mass = 1; - demo.addScene("car",function(){ + demo.addScene("car", function () + { var world = demo.getWorld(); - world.gravity.set(0,0,-20); + world.gravity.set(0, 0, -20); var groundMaterial = new CANNON.Material("groundMaterial"); var wheelMaterial = new CANNON.Material("wheelMaterial"); - var wheelGroundContactMaterial = new CANNON.ContactMaterial(groundMaterial,wheelMaterial, { friction: 0.5, restitution: 0.3 }); + var wheelGroundContactMaterial = new CANNON.ContactMaterial(groundMaterial, wheelMaterial, { friction: 0.5, restitution: 0.3 }); world.addContactMaterial(wheelGroundContactMaterial); - var wheelShape = new CANNON.Sphere(1.2); - var leftFrontWheel = new CANNON.Body({ mass: mass, material: wheelMaterial }); + var wheelShape = new CANNON.Sphere(1.2); + var leftFrontWheel = new CANNON.Body({ mass: mass, material: wheelMaterial }); leftFrontWheel.addShape(wheelShape); var rightFrontWheel = new CANNON.Body({ mass: mass, material: wheelMaterial }); rightFrontWheel.addShape(wheelShape); - var leftRearWheel = new CANNON.Body({ mass: mass, material: wheelMaterial }); + var leftRearWheel = new CANNON.Body({ mass: mass, material: wheelMaterial }); leftRearWheel.addShape(wheelShape); - var rightRearWheel = new CANNON.Body({ mass: mass, material: wheelMaterial }); + var rightRearWheel = new CANNON.Body({ mass: mass, material: wheelMaterial }); rightRearWheel.addShape(wheelShape); - var chassisShape = new CANNON.Box(new CANNON.Vec3(5,2,0.5)); + var chassisShape = new CANNON.Box(new CANNON.Vector3(5, 2, 0.5)); var chassis = new CANNON.Body({ mass: mass }); chassis.addShape(chassisShape); // Position constrain wheels - var zero = new CANNON.Vec3(); - leftFrontWheel .position.set( 5, 5, 0); - rightFrontWheel.position.set( 5, -5, 0); - leftRearWheel .position.set( -5, 5, 0); - rightRearWheel .position.set( -5, -5, 0); + var zero = new CANNON.Vector3(); + leftFrontWheel.position.set(5, 5, 0); + rightFrontWheel.position.set(5, -5, 0); + leftRearWheel.position.set(-5, 5, 0); + rightRearWheel.position.set(-5, -5, 0); // Constrain wheels var constraints = []; // Hinge the wheels - var leftAxis = new CANNON.Vec3(0,1,0); - var rightAxis = new CANNON.Vec3(0,-1,0); - var leftFrontAxis = new CANNON.Vec3(0,1,0); - var rightFrontAxis = new CANNON.Vec3(0,-1,0); - if(true){ - leftFrontAxis = new CANNON.Vec3(0.3,0.7,0); - rightFrontAxis = new CANNON.Vec3(-0.3,-0.7,0); + var leftAxis = new CANNON.Vector3(0, 1, 0); + var rightAxis = new CANNON.Vector3(0, -1, 0); + var leftFrontAxis = new CANNON.Vector3(0, 1, 0); + var rightFrontAxis = new CANNON.Vector3(0, -1, 0); + if (true) + { + leftFrontAxis = new CANNON.Vector3(0.3, 0.7, 0); + rightFrontAxis = new CANNON.Vector3(-0.3, -0.7, 0); leftFrontAxis.normalize(); rightFrontAxis.normalize(); } - constraints.push(new CANNON.HingeConstraint(chassis, leftFrontWheel, { pivotA: new CANNON.Vec3( 5, 5, 0), axisA: leftFrontAxis, pivotB: zero, axisB: leftAxis })); - constraints.push(new CANNON.HingeConstraint(chassis, rightFrontWheel, { pivotA: new CANNON.Vec3( 5,-5, 0), axisA: rightFrontAxis, pivotB: zero, axisB: rightAxis })); - constraints.push(new CANNON.HingeConstraint(chassis, leftRearWheel, { pivotA: new CANNON.Vec3(-5, 5, 0), axisA: leftAxis, pivotB: zero, axisB: leftAxis })); - constraints.push(new CANNON.HingeConstraint(chassis, rightRearWheel, { pivotA: new CANNON.Vec3(-5,-5, 0), axisA: rightAxis, pivotB: zero, axisB: rightAxis })); + constraints.push(new CANNON.HingeConstraint(chassis, leftFrontWheel, { pivotA: new CANNON.Vector3(5, 5, 0), axisA: leftFrontAxis, pivotB: zero, axisB: leftAxis })); + constraints.push(new CANNON.HingeConstraint(chassis, rightFrontWheel, { pivotA: new CANNON.Vector3(5, -5, 0), axisA: rightFrontAxis, pivotB: zero, axisB: rightAxis })); + constraints.push(new CANNON.HingeConstraint(chassis, leftRearWheel, { pivotA: new CANNON.Vector3(-5, 5, 0), axisA: leftAxis, pivotB: zero, axisB: leftAxis })); + constraints.push(new CANNON.HingeConstraint(chassis, rightRearWheel, { pivotA: new CANNON.Vector3(-5, -5, 0), axisA: rightAxis, pivotB: zero, axisB: rightAxis })); - for(var i=0; i - - + + + \ No newline at end of file diff --git a/demos/impulses.html b/demos/impulses.html index 5571a35e7..8d46a9501 100644 --- a/demos/impulses.html +++ b/demos/impulses.html @@ -1,13 +1,16 @@ - + + cannon.js - impulse demo - + - - - + + + + + @@ -17,24 +20,25 @@ - - + + + \ No newline at end of file diff --git a/demos/jenga.html b/demos/jenga.html index 5da2f2dc1..9193ae2cf 100644 --- a/demos/jenga.html +++ b/demos/jenga.html @@ -1,76 +1,84 @@ - - cannon.js - jenga demo - - - - - - - - - - - - - - + + + + + + + + + + - - - + \ No newline at end of file diff --git a/demos/pile.html b/demos/pile.html index bebccfcb9..42f10fed3 100644 --- a/demos/pile.html +++ b/demos/pile.html @@ -1,13 +1,16 @@ + cannon.js - pile demo - + + - + + @@ -17,96 +20,100 @@ - + + \ No newline at end of file diff --git a/demos/ragdoll.html b/demos/ragdoll.html index be8fefba7..78db1d828 100644 --- a/demos/ragdoll.html +++ b/demos/ragdoll.html @@ -1,13 +1,16 @@ - + + cannon.js - ragdoll demo - + - - - + + + + + @@ -19,36 +22,39 @@ var demo = new CANNON.Demo(); - demo.addScene("NoCone",function(){ + demo.addScene("NoCone", function () + { var world = demo.getWorld(); - world.gravity.set(0,0,-5); + world.gravity.set(0, 0, -5); var scale = 3; - var position = new CANNON.Vec3(0,0,10); + var position = new CANNON.Vector3(0, 0, 10); createRagdoll(scale, position, world, Math.PI, Math.PI, Math.PI); createGround(world); createStaticSphere(world); }); - demo.addScene("NormalCone",function(){ + demo.addScene("NormalCone", function () + { var world = demo.getWorld(); - world.gravity.set(0,0,-5); + world.gravity.set(0, 0, -5); var scale = 3; - var position = new CANNON.Vec3(0,0,10); + var position = new CANNON.Vector3(0, 0, 10); createRagdoll(scale, position, world, Math.PI / 4, Math.PI / 3, Math.PI / 8); createGround(world); createStaticSphere(world); }); - demo.addScene("ThinCone",function(){ + demo.addScene("ThinCone", function () + { var world = demo.getWorld(); - world.gravity.set(0,0,-5); + world.gravity.set(0, 0, -5); var scale = 3; - var position = new CANNON.Vec3(0,0,10); + var position = new CANNON.Vector3(0, 0, 10); createRagdoll(scale, position, world, 0, 0, 0); createGround(world); @@ -57,7 +63,8 @@ demo.start(); - function createRagdoll(scale, position, world, angleA, angleB, twistAngle){ + function createRagdoll(scale, position, world, angleA, angleB, twistAngle) + { var numBodiesAtStart = world.bodies.length; var shouldersDistance = 0.5 * scale, @@ -74,22 +81,22 @@ lowerLegSize = 0.2 * scale, lowerLegLength = 0.5 * scale; - var headShape = new CANNON.Sphere(headRadius), - upperArmShape = new CANNON.Box(new CANNON.Vec3(upperArmLength * 0.5, upperArmSize * 0.5, upperArmSize * 0.5)), - lowerArmShape = new CANNON.Box(new CANNON.Vec3(lowerArmLength * 0.5, lowerArmSize * 0.5, lowerArmSize * 0.5)), - upperBodyShape = new CANNON.Box(new CANNON.Vec3(shouldersDistance * 0.5, upperBodyLength * 0.5, lowerArmSize * 0.5)), - pelvisShape = new CANNON.Box(new CANNON.Vec3(shouldersDistance * 0.5, pelvisLength * 0.5, lowerArmSize * 0.5)), - upperLegShape = new CANNON.Box(new CANNON.Vec3(upperLegSize * 0.5, upperLegLength * 0.5, lowerArmSize * 0.5)), - lowerLegShape = new CANNON.Box(new CANNON.Vec3(lowerLegSize * 0.5, lowerLegLength * 0.5, lowerArmSize * 0.5)); + var headShape = new CANNON.Sphere(headRadius), + upperArmShape = new CANNON.Box(new CANNON.Vector3(upperArmLength * 0.5, upperArmSize * 0.5, upperArmSize * 0.5)), + lowerArmShape = new CANNON.Box(new CANNON.Vector3(lowerArmLength * 0.5, lowerArmSize * 0.5, lowerArmSize * 0.5)), + upperBodyShape = new CANNON.Box(new CANNON.Vector3(shouldersDistance * 0.5, upperBodyLength * 0.5, lowerArmSize * 0.5)), + pelvisShape = new CANNON.Box(new CANNON.Vector3(shouldersDistance * 0.5, pelvisLength * 0.5, lowerArmSize * 0.5)), + upperLegShape = new CANNON.Box(new CANNON.Vector3(upperLegSize * 0.5, upperLegLength * 0.5, lowerArmSize * 0.5)), + lowerLegShape = new CANNON.Box(new CANNON.Vector3(lowerLegSize * 0.5, lowerLegLength * 0.5, lowerArmSize * 0.5)); // Lower legs var lowerLeftLeg = new CANNON.Body({ mass: 1, - position: new CANNON.Vec3(-shouldersDistance/2,lowerLegLength / 2, 0) + position: new CANNON.Vector3(-shouldersDistance / 2, lowerLegLength / 2, 0) }); var lowerRightLeg = new CANNON.Body({ mass: 1, - position: new CANNON.Vec3(shouldersDistance/2,lowerLegLength / 2, 0) + position: new CANNON.Vector3(shouldersDistance / 2, lowerLegLength / 2, 0) }); lowerLeftLeg.addShape(lowerLegShape); lowerRightLeg.addShape(lowerLegShape); @@ -101,11 +108,11 @@ // Upper legs var upperLeftLeg = new CANNON.Body({ mass: 1, - position: new CANNON.Vec3(-shouldersDistance/2,lowerLeftLeg.position.y+lowerLegLength/2+upperLegLength / 2, 0), + position: new CANNON.Vector3(-shouldersDistance / 2, lowerLeftLeg.position.y + lowerLegLength / 2 + upperLegLength / 2, 0), }); var upperRightLeg = new CANNON.Body({ mass: 1, - position: new CANNON.Vec3(shouldersDistance/2,lowerRightLeg.position.y+lowerLegLength/2+upperLegLength / 2, 0), + position: new CANNON.Vector3(shouldersDistance / 2, lowerRightLeg.position.y + lowerLegLength / 2 + upperLegLength / 2, 0), }); upperLeftLeg.addShape(upperLegShape); upperRightLeg.addShape(upperLegShape); @@ -117,7 +124,7 @@ // Pelvis var pelvis = new CANNON.Body({ mass: 1, - position: new CANNON.Vec3(0, upperLeftLeg.position.y+upperLegLength/2+pelvisLength/2, 0), + position: new CANNON.Vector3(0, upperLeftLeg.position.y + upperLegLength / 2 + pelvisLength / 2, 0), }); pelvis.addShape(pelvisShape); world.addBody(pelvis); @@ -126,7 +133,7 @@ // Upper body var upperBody = new CANNON.Body({ mass: 1, - position: new CANNON.Vec3(0,pelvis.position.y+pelvisLength/2+upperBodyLength/2, 0), + position: new CANNON.Vector3(0, pelvis.position.y + pelvisLength / 2 + upperBodyLength / 2, 0), }); upperBody.addShape(upperBodyShape); world.addBody(upperBody); @@ -135,7 +142,7 @@ // Head var head = new CANNON.Body({ mass: 1, - position: new CANNON.Vec3(0,upperBody.position.y+upperBodyLength/2+headRadius+neckLength, 0), + position: new CANNON.Vector3(0, upperBody.position.y + upperBodyLength / 2 + headRadius + neckLength, 0), }); head.addShape(headShape); world.addBody(head); @@ -144,11 +151,11 @@ // Upper arms var upperLeftArm = new CANNON.Body({ mass: 1, - position: new CANNON.Vec3(-shouldersDistance/2-upperArmLength/2, upperBody.position.y+upperBodyLength/2, 0), + position: new CANNON.Vector3(-shouldersDistance / 2 - upperArmLength / 2, upperBody.position.y + upperBodyLength / 2, 0), }); var upperRightArm = new CANNON.Body({ mass: 1, - position: new CANNON.Vec3(shouldersDistance/2+upperArmLength/2, upperBody.position.y+upperBodyLength/2, 0), + position: new CANNON.Vector3(shouldersDistance / 2 + upperArmLength / 2, upperBody.position.y + upperBodyLength / 2, 0), }); upperLeftArm.addShape(upperArmShape); upperRightArm.addShape(upperArmShape); @@ -160,11 +167,11 @@ // lower arms var lowerLeftArm = new CANNON.Body({ mass: 1, - position: new CANNON.Vec3( upperLeftArm.position.x - lowerArmLength/2 - upperArmLength/2, upperLeftArm.position.y, 0) + position: new CANNON.Vector3(upperLeftArm.position.x - lowerArmLength / 2 - upperArmLength / 2, upperLeftArm.position.y, 0) }); var lowerRightArm = new CANNON.Body({ mass: 1, - position: new CANNON.Vec3( upperRightArm.position.x + lowerArmLength/2 + upperArmLength/2, upperRightArm.position.y, 0) + position: new CANNON.Vector3(upperRightArm.position.x + lowerArmLength / 2 + upperArmLength / 2, upperRightArm.position.y, 0) }); lowerLeftArm.addShape(lowerArmShape); lowerRightArm.addShape(lowerArmShape); @@ -176,8 +183,8 @@ // Neck joint var neckJoint = new CANNON.ConeTwistConstraint(head, upperBody, { - pivotA: new CANNON.Vec3(0,-headRadius-neckLength/2,0), - pivotB: new CANNON.Vec3(0,upperBodyLength/2,0), + pivotA: new CANNON.Vector3(0, -headRadius - neckLength / 2, 0), + pivotB: new CANNON.Vector3(0, upperBodyLength / 2, 0), axisA: CANNON.Vec3.UNIT_Y, axisB: CANNON.Vec3.UNIT_Y, angle: angleA, @@ -187,16 +194,16 @@ // Knee joints var leftKneeJoint = new CANNON.ConeTwistConstraint(lowerLeftLeg, upperLeftLeg, { - pivotA: new CANNON.Vec3(0, lowerLegLength/2,0), - pivotB: new CANNON.Vec3(0,-upperLegLength/2,0), + pivotA: new CANNON.Vector3(0, lowerLegLength / 2, 0), + pivotB: new CANNON.Vector3(0, -upperLegLength / 2, 0), axisA: CANNON.Vec3.UNIT_Y, axisB: CANNON.Vec3.UNIT_Y, angle: angleA, twistAngle: twistAngle }); - var rightKneeJoint= new CANNON.ConeTwistConstraint(lowerRightLeg, upperRightLeg, { - pivotA: new CANNON.Vec3(0, lowerLegLength/2,0), - pivotB: new CANNON.Vec3(0,-upperLegLength/2,0), + var rightKneeJoint = new CANNON.ConeTwistConstraint(lowerRightLeg, upperRightLeg, { + pivotA: new CANNON.Vector3(0, lowerLegLength / 2, 0), + pivotB: new CANNON.Vector3(0, -upperLegLength / 2, 0), axisA: CANNON.Vec3.UNIT_Y, axisB: CANNON.Vec3.UNIT_Y, angle: angleA, @@ -207,16 +214,16 @@ // Hip joints var leftHipJoint = new CANNON.ConeTwistConstraint(upperLeftLeg, pelvis, { - pivotA: new CANNON.Vec3(0, upperLegLength/2,0), - pivotB: new CANNON.Vec3(-shouldersDistance/2,-pelvisLength/2,0), + pivotA: new CANNON.Vector3(0, upperLegLength / 2, 0), + pivotB: new CANNON.Vector3(-shouldersDistance / 2, -pelvisLength / 2, 0), axisA: CANNON.Vec3.UNIT_Y, axisB: CANNON.Vec3.UNIT_Y, angle: angleA, twistAngle: twistAngle }); var rightHipJoint = new CANNON.ConeTwistConstraint(upperRightLeg, pelvis, { - pivotA: new CANNON.Vec3(0, upperLegLength/2,0), - pivotB: new CANNON.Vec3(shouldersDistance/2,-pelvisLength/2,0), + pivotA: new CANNON.Vector3(0, upperLegLength / 2, 0), + pivotB: new CANNON.Vector3(shouldersDistance / 2, -pelvisLength / 2, 0), axisA: CANNON.Vec3.UNIT_Y, axisB: CANNON.Vec3.UNIT_Y, angle: angleA, @@ -227,8 +234,8 @@ // Spine var spineJoint = new CANNON.ConeTwistConstraint(pelvis, upperBody, { - pivotA: new CANNON.Vec3(0,pelvisLength/2,0), - pivotB: new CANNON.Vec3(0,-upperBodyLength/2,0), + pivotA: new CANNON.Vector3(0, pelvisLength / 2, 0), + pivotB: new CANNON.Vector3(0, -upperBodyLength / 2, 0), axisA: CANNON.Vec3.UNIT_Y, axisB: CANNON.Vec3.UNIT_Y, angle: angleA, @@ -238,15 +245,15 @@ // Shoulders var leftShoulder = new CANNON.ConeTwistConstraint(upperBody, upperLeftArm, { - pivotA: new CANNON.Vec3(-shouldersDistance/2, upperBodyLength/2,0), - pivotB: new CANNON.Vec3(upperArmLength/2,0,0), + pivotA: new CANNON.Vector3(-shouldersDistance / 2, upperBodyLength / 2, 0), + pivotB: new CANNON.Vector3(upperArmLength / 2, 0, 0), axisA: CANNON.Vec3.UNIT_X, axisB: CANNON.Vec3.UNIT_X, angle: angleB }); - var rightShoulder= new CANNON.ConeTwistConstraint(upperBody, upperRightArm, { - pivotA: new CANNON.Vec3(shouldersDistance/2, upperBodyLength/2,0), - pivotB: new CANNON.Vec3(-upperArmLength/2,0,0), + var rightShoulder = new CANNON.ConeTwistConstraint(upperBody, upperRightArm, { + pivotA: new CANNON.Vector3(shouldersDistance / 2, upperBodyLength / 2, 0), + pivotB: new CANNON.Vector3(-upperArmLength / 2, 0, 0), axisA: CANNON.Vec3.UNIT_X, axisB: CANNON.Vec3.UNIT_X, angle: angleB, @@ -257,16 +264,16 @@ // Elbow joint var leftElbowJoint = new CANNON.ConeTwistConstraint(lowerLeftArm, upperLeftArm, { - pivotA: new CANNON.Vec3(lowerArmLength/2, 0,0), - pivotB: new CANNON.Vec3(-upperArmLength/2,0,0), + pivotA: new CANNON.Vector3(lowerArmLength / 2, 0, 0), + pivotB: new CANNON.Vector3(-upperArmLength / 2, 0, 0), axisA: CANNON.Vec3.UNIT_X, axisB: CANNON.Vec3.UNIT_X, angle: angleA, twistAngle: twistAngle }); - var rightElbowJoint= new CANNON.ConeTwistConstraint(lowerRightArm, upperRightArm, { - pivotA: new CANNON.Vec3(-lowerArmLength/2,0,0), - pivotB: new CANNON.Vec3(upperArmLength/2,0,0), + var rightElbowJoint = new CANNON.ConeTwistConstraint(lowerRightArm, upperRightArm, { + pivotA: new CANNON.Vector3(-lowerArmLength / 2, 0, 0), + pivotB: new CANNON.Vector3(upperArmLength / 2, 0, 0), axisA: CANNON.Vec3.UNIT_X, axisB: CANNON.Vec3.UNIT_X, angle: angleA, @@ -276,32 +283,36 @@ world.addConstraint(rightElbowJoint); // Move all body parts - for (var i = numBodiesAtStart; i < world.bodies.length; i++) { + for (var i = numBodiesAtStart; i < world.bodies.length; i++) + { var body = world.bodies[i]; body.position.vadd(position, body.position); } } - function createGround(world){ + function createGround(world) + { // add ground plane var groundShape = new CANNON.Plane(); var groundBody = new CANNON.Body({ mass: 0 }); groundBody.addShape(groundShape); - groundBody.position.set(0,0,-1); + groundBody.position.set(0, 0, -1); world.addBody(groundBody); demo.addVisual(groundBody); } - function createStaticSphere(world){ + function createStaticSphere(world) + { // Add a sphere to land on var sphereShape = new CANNON.Sphere(4); var sphereBody = new CANNON.Body({ mass: 0 }); sphereBody.addShape(sphereShape); - sphereBody.position.set(0,0,-1); + sphereBody.position.set(0, 0, -1); world.addBody(sphereBody); demo.addVisual(sphereBody); } - - + + + \ No newline at end of file diff --git a/demos/raycastVehicle.html b/demos/raycastVehicle.html index 0354ec22f..dc7c8bc15 100644 --- a/demos/raycastVehicle.html +++ b/demos/raycastVehicle.html @@ -1,13 +1,16 @@ - + + cannon.js - RaycastVehicle - + - - - + + + + + @@ -21,7 +24,8 @@ var mass = 150; var vehicle; - demo.addScene("car",function(){ + demo.addScene("car", function () + { var world = demo.getWorld(); world.broadphase = new CANNON.SAPBroadphase(world); world.gravity.set(0, 0, -10); @@ -39,7 +43,7 @@ world.addContactMaterial(wheelGroundContactMaterial); var chassisShape; - chassisShape = new CANNON.Box(new CANNON.Vec3(2, 1,0.5)); + chassisShape = new CANNON.Box(new CANNON.Vector3(2, 1, 0.5)); var chassisBody = new CANNON.Body({ mass: mass }); chassisBody.addShape(chassisShape); chassisBody.position.set(0, 0, 4); @@ -48,16 +52,16 @@ var options = { radius: 0.5, - directionLocal: new CANNON.Vec3(0, 0, -1), + directionLocal: new CANNON.Vector3(0, 0, -1), suspensionStiffness: 30, suspensionRestLength: 0.3, frictionSlip: 5, dampingRelaxation: 2.3, dampingCompression: 4.4, maxSuspensionForce: 100000, - rollInfluence: 0.01, - axleLocal: new CANNON.Vec3(0, 1, 0), - chassisConnectionPointLocal: new CANNON.Vec3(1, 1, 0), + rollInfluence: 0.01, + axleLocal: new CANNON.Vector3(0, 1, 0), + chassisConnectionPointLocal: new CANNON.Vector3(1, 1, 0), maxSuspensionTravel: 0.3, customSlidingRotationalSpeed: -30, useCustomSlidingRotationalSpeed: true @@ -83,7 +87,8 @@ vehicle.addToWorld(world); var wheelBodies = []; - for(var i=0; i - - + + + \ No newline at end of file diff --git a/demos/rigidVehicle.html b/demos/rigidVehicle.html index c77dd7269..5358e65af 100644 --- a/demos/rigidVehicle.html +++ b/demos/rigidVehicle.html @@ -1,13 +1,16 @@ - + + cannon.js - RigidVehicle - + - - - + + + + + @@ -21,7 +24,8 @@ var mass = 1; var vehicle; - demo.addScene("car",function(){ + demo.addScene("car", function () + { var world = demo.getWorld(); world.gravity.set(0, 0, -30); world.broadphase = new CANNON.SAPBroadphase(world); @@ -39,8 +43,8 @@ world.addContactMaterial(wheelGroundContactMaterial); var chassisShape; - var centerOfMassAdjust = new CANNON.Vec3(0, 0, -1); - chassisShape = new CANNON.Box(new CANNON.Vec3(5, 2, 0.5)); + var centerOfMassAdjust = new CANNON.Vector3(0, 0, -1); + chassisShape = new CANNON.Box(new CANNON.Vector3(5, 2, 0.5)); var chassisBody = new CANNON.Body({ mass: 1 }); chassisBody.addShape(chassisShape, centerOfMassAdjust); chassisBody.position.set(0, 0, 0); @@ -52,14 +56,14 @@ var axisWidth = 7; var wheelShape = new CANNON.Sphere(1.5); - var down = new CANNON.Vec3(0, 0, -1); + var down = new CANNON.Vector3(0, 0, -1); var wheelBody = new CANNON.Body({ mass: mass, material: wheelMaterial }); wheelBody.addShape(wheelShape); vehicle.addWheel({ body: wheelBody, - position: new CANNON.Vec3(5, axisWidth/2, 0).vadd(centerOfMassAdjust), - axis: new CANNON.Vec3(0, 1, 0), + position: new CANNON.Vector3(5, axisWidth / 2, 0).vadd(centerOfMassAdjust), + axis: new CANNON.Vector3(0, 1, 0), direction: down }); @@ -67,8 +71,8 @@ wheelBody.addShape(wheelShape); vehicle.addWheel({ body: wheelBody, - position: new CANNON.Vec3(5, -axisWidth/2, 0).vadd(centerOfMassAdjust), - axis: new CANNON.Vec3(0, -1, 0), + position: new CANNON.Vector3(5, -axisWidth / 2, 0).vadd(centerOfMassAdjust), + axis: new CANNON.Vector3(0, -1, 0), direction: down }); @@ -76,8 +80,8 @@ wheelBody.addShape(wheelShape); vehicle.addWheel({ body: wheelBody, - position: new CANNON.Vec3(-5, axisWidth/2, 0).vadd(centerOfMassAdjust), - axis: new CANNON.Vec3(0, 1, 0), + position: new CANNON.Vector3(-5, axisWidth / 2, 0).vadd(centerOfMassAdjust), + axis: new CANNON.Vector3(0, 1, 0), direction: down }); @@ -85,13 +89,14 @@ wheelBody.addShape(wheelShape); vehicle.addWheel({ body: wheelBody, - position: new CANNON.Vec3(-5, -axisWidth/2, 0).vadd(centerOfMassAdjust), - axis: new CANNON.Vec3(0, -1, 0), + position: new CANNON.Vector3(-5, -axisWidth / 2, 0).vadd(centerOfMassAdjust), + axis: new CANNON.Vector3(0, -1, 0), direction: down }); // Some damping to not spin wheels too fast - for(var i=0; i - - + + + \ No newline at end of file diff --git a/demos/shapes.html b/demos/shapes.html index 5448d58fa..3e9ee8885 100644 --- a/demos/shapes.html +++ b/demos/shapes.html @@ -1,154 +1,163 @@ - - cannon.js - shapes demo - - - - - - - - - - - - - - - - + + + cannon.js - shapes demo + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/demos/simpleFriction.html b/demos/simpleFriction.html index 87b3af1fa..98bae58d6 100644 --- a/demos/simpleFriction.html +++ b/demos/simpleFriction.html @@ -1,21 +1,24 @@ - - cannon.js - friction demo - - - - - - - - - - - - - - + + + + + + + + + - - + + + + \ No newline at end of file diff --git a/demos/singleBodyOnPlane.html b/demos/singleBodyOnPlane.html index 07147ec2d..7f4bb87d6 100644 --- a/demos/singleBodyOnPlane.html +++ b/demos/singleBodyOnPlane.html @@ -1,66 +1,73 @@ - - cannon.js - single body on plane demo - - - - - - - - - - - - - - + + + + + + + + + - - + // ground plane + var groundShape = new CANNON.Plane(); + var groundBody = new CANNON.Body({ mass: 0 }); + groundBody.addShape(groundShape); + world.addBody(groundBody); + demo.addVisual(groundBody); + + // Shape on plane + var shapeBody = new CANNON.Body({ mass: 30 }); + shapeBody.addShape(shape); + var pos = new CANNON.Vector3(0, 0, size); + shapeBody.position.set(0, size, size * 2); + shapeBody.velocity.set(0, 0, 0); + shapeBody.angularVelocity.set(0, 0, 0); + world.addBody(shapeBody); + demo.addVisual(shapeBody); + } + + + + + \ No newline at end of file diff --git a/demos/sleep.html b/demos/sleep.html index 05e9d8c8e..833d13c54 100644 --- a/demos/sleep.html +++ b/demos/sleep.html @@ -1,134 +1,143 @@ - - cannon.js - sleep demo - - - - - - - - - - - - - - + + + + + + + + + + - - - + \ No newline at end of file diff --git a/demos/sph.html b/demos/sph.html index 38af9d3aa..367ff5147 100644 --- a/demos/sph.html +++ b/demos/sph.html @@ -1,126 +1,135 @@ - - cannon.js - sph demo - - - - - - - - - - - - - - + + + + + + + + + + - - - + \ No newline at end of file diff --git a/demos/splitSolver.html b/demos/splitSolver.html index e9b68d4cd..88dc2e293 100644 --- a/demos/splitSolver.html +++ b/demos/splitSolver.html @@ -1,21 +1,24 @@ - - cannon.js - splitsolver demo - - - - - - - - - - - - - - + + + + + + + + + + - - - + \ No newline at end of file diff --git a/demos/spring.html b/demos/spring.html index 2949bae93..dbd8fbc7e 100644 --- a/demos/spring.html +++ b/demos/spring.html @@ -1,13 +1,16 @@ - + + cannon.js - spring demo - + - - - + + + + + @@ -17,48 +20,51 @@ - - + + + \ No newline at end of file diff --git a/demos/stacks.html b/demos/stacks.html index ee35750fc..d454ffc97 100644 --- a/demos/stacks.html +++ b/demos/stacks.html @@ -1,399 +1,427 @@ - - cannon.js - stacks demo - - - - - - - - - - - - - - + + + + + + + + + - - + return new CANNON.ConvexPolyhedron(verts, + [ + [0, 3, 2], // -x + [0, 1, 3], // -y + [0, 2, 1], // -z + [1, 2, 3], // +xyz + ]); + } + function createBoxPolyhedron(size) + { + size = size || 1; + var box = new CANNON.Box(new CANNON.Vector3(size, size, size)); + return box.convexPolyhedronRepresentation; + } + + function createCompound(mass) + { + var compoundBody = new CANNON.Body({ mass: mass }); + var s = size; + var shape = new CANNON.Box(new CANNON.Vector3(0.5 * s, 0.5 * s, 0.5 * s)); + compoundBody.addShape(shape, new CANNON.Vector3(0, 0, s)); + compoundBody.addShape(shape, new CANNON.Vector3(0, 0, 0)); + compoundBody.addShape(shape, new CANNON.Vector3(0, 0, -s)); + compoundBody.addShape(shape, new CANNON.Vector3(-s, 0, -s)); + return compoundBody; + } + + // Spheres + demo.addScene("sphere/sphere", function () + { + var world = setupWorld(demo); + + // Sphere 1 + var sphereShape = new CANNON.Sphere(size); + var b1 = new CANNON.Body({ mass: 5 }); + b1.addShape(sphereShape); + b1.position.set(0, 0, 3 * size); + world.addBody(b1); + demo.addVisual(b1); + + // Sphere 2 + var b2 = new CANNON.Body({ mass: 5 }); + b2.addShape(sphereShape); + b2.position.set(0, 0, 1 * size); + world.addBody(b2); + demo.addVisual(b2); + }); + + // Sphere/plane + demo.addScene("sphere/plane", function () + { + var world = setupWorld(demo); + + // Sphere 1 + var sphereShape = new CANNON.Sphere(size); + var b1 = new CANNON.Body({ mass: 5 }); + b1.addShape(sphereShape); + b1.position.set(0, 0, 3 * size); + world.addBody(b1); + demo.addVisual(b1); + }); + + // Sphere / box side + demo.addScene("sphere/box", function () + { + var world = setupWorld(demo); + + var boxShape = new CANNON.Box(new CANNON.Vector3(size, size, size)); + var sphereShape = new CANNON.Sphere(size); + + // Box + var b1 = new CANNON.Body({ mass: 5 }); + b1.addShape(boxShape); + b1.position.set(0, 0, 1 * size); + world.addBody(b1); + demo.addVisual(b1); + + // Sphere + var b2 = new CANNON.Body({ mass: 5 }); + b2.addShape(sphereShape); + b2.position.set(0, 0, 3 * size); + world.addBody(b2); + demo.addVisual(b2); + }); + + demo.addScene("sphere/compound", function () + { + var world = setupWorld(demo); + + // Sphere + var sphereShape = new CANNON.Sphere(size * 0.5); + var b = new CANNON.Body({ mass: 5 }); + b.addShape(sphereShape); + b.position.set(-size, 0, 6 * size); + world.addBody(b); + demo.addVisual(b); + + var compoundBody = createCompound(mass); + compoundBody.position.set(0, 0, size * 3); + world.addBody(compoundBody); + demo.addVisual(compoundBody); + }); + + demo.addScene("sphere/convex", function () + { + var world = setupWorld(demo); + + // Sphere + var sphereShape = new CANNON.Sphere(size * 0.5); + var b = new CANNON.Body({ mass: 5 }); + b.addShape(sphereShape); + b.position.set(0, 0, 6 * size); + world.addBody(b); + demo.addVisual(b); + + var shape = createTetra();//createBoxPolyhedron(size); + var b1 = new CANNON.Body({ mass: 5 }); + b1.addShape(shape); + b1.position.set(0, 0, 1 * size); + world.addBody(b1); + demo.addVisual(b1); + }); + + demo.addScene("sphere/particle", function () + { + var world = setupWorld(demo); + + // Sphere + var sphereShape = new CANNON.Sphere(size * 0.5); + var b = new CANNON.Body({ mass: 5 }); + b.addShape(sphereShape); + b.position.set(0, 0, size); + world.addBody(b); + demo.addVisual(b); + + // Particle + var p = new CANNON.Body({ mass: 1 }); + p.addShape(new CANNON.Particle()); + p.position.set(0.02, 0, 3 * size); + + world.addBody(p); + demo.addVisual(p); + }); + + demo.addScene("plane/box", function () + { + var world = setupWorld(demo); + var boxShape = new CANNON.Box(new CANNON.Vector3(size, size, size)); + var b1 = new CANNON.Body({ mass: 5 }); + b1.addShape(boxShape); + b1.position.set(0, 0, 1 * size); + world.addBody(b1); + demo.addVisual(b1); + }); + + demo.addScene("plane/compound", function () + { + var world = setupWorld(demo); + var b1 = createCompound(5); + b1.position.set(0, 0, 4 * size); + world.addBody(b1); + demo.addVisual(b1); + }); + + demo.addScene("plane/convex", function () + { + var world = setupWorld(demo); + var shape = createTetra(); + var b1 = new CANNON.Body({ mass: 5 }); + b1.addShape(shape); + b1.position.set(0, 0, 1 * size); + world.addBody(b1); + demo.addVisual(b1); + }); + + demo.addScene("plane/particle", function () + { + var world = setupWorld(demo); + // Particle + var p = new CANNON.Body({ mass: 1 }); + p.addShape(new CANNON.Particle()); + p.position.set(0.02, 0, 3 * size); + world.addBody(p); + demo.addVisual(p); + }); + + // Boxes + demo.addScene("box/box", function () + { + var world = setupWorld(demo); + + // Box 1 + var boxShape = new CANNON.Box(new CANNON.Vector3(size * 0.5, size * 0.5, size * 0.5)); + var b1 = new CANNON.Body({ mass: 5 }); + b1.addShape(boxShape); + b1.position.set(0, 0, 1 * size); + world.addBody(b1); + demo.addVisual(b1); + + // Box 2 + var b2 = new CANNON.Body({ mass: 5 }); + b2.addShape(boxShape); + b2.position.set(size * 0.5, 0, 3 * size); + world.addBody(b2); + demo.addVisual(b2); + }); + + demo.addScene("box/compound", function () + { + var world = setupWorld(demo); + + var b2 = createCompound(5); + b2.position.set(size * 0.5, 0, 2 * size); + world.addBody(b2); + demo.addVisual(b2); + + var boxShape = new CANNON.Box(new CANNON.Vector3(size * 0.5, size * 0.5, size * 0.5)); + var b1 = new CANNON.Body({ mass: 5 }); + b1.addShape(boxShape); + b1.position.set(0, 0, 7 * size); + world.addBody(b1); + demo.addVisual(b1); + }); + + demo.addScene("box/convex", function () + { + var world = setupWorld(demo); + + var shape = createTetra(size); + var b2 = new CANNON.Body({ mass: 5 }); + b2.addShape(shape); + b2.position.set(0, 0, 5 * size); + world.addBody(b2); + demo.addVisual(b2); + + var boxShape = new CANNON.Box(new CANNON.Vector3(size * 0.5, size * 0.5, size * 0.5)); + var b1 = new CANNON.Body({ mass: 5 }); + b1.addShape(boxShape); + b1.position.set(0, 0, 2 * size); + world.addBody(b1); + demo.addVisual(b1); + }); + + demo.addScene("box/particle", function () + { + var world = setupWorld(demo); + + var boxShape = new CANNON.Box(new CANNON.Vector3(size * 0.5, size * 0.5, size * 0.5)); + var b1 = new CANNON.Body({ mass: 5 }); + b1.addShape(boxShape); + b1.position.set(0, 0, 1 * size); + world.addBody(b1); + demo.addVisual(b1) + + // Particle + var p = new CANNON.Body({ mass: 1 }); + p.addShape(new CANNON.Particle()); + p.position.set(0, 0, 3 * size); + world.addBody(p); + demo.addVisual(p); + }); + + demo.addScene("compound/compound", function () + { + var world = setupWorld(demo); + + var b2 = createCompound(5); + b2.position.set(size * 0.5, 0, 6 * size); + world.addBody(b2); + demo.addVisual(b2); + + var b2 = createCompound(5); + b2.position.set(size * 0.5, 0, 2 * size); + world.addBody(b2); + demo.addVisual(b2); + }); + + demo.addScene("compound/convex", function () + { + var world = setupWorld(demo); + + var tetraShape = createTetra(); + + var b1 = new CANNON.Body({ mass: 5 }); + b1.addShape(tetraShape); + b1.position.set(0, 0, 3 * size); + world.addBody(b1); + demo.addVisual(b1); + + // Box 2 + var boxShape = new CANNON.Box(new CANNON.Vector3(size * 0.5, size * 0.5, size * 0.5)); + var b2 = new CANNON.Body({ mass: 5 }); + b2.addShape(boxShape); + b2.position.set(0, 0, 1 * size); + world.addBody(b2); + demo.addVisual(b2); + }); + + demo.addScene("compound/particle", function () + { + var world = setupWorld(demo); + + var t = createCompound(5); + t.position.set(0, 0, 4 * size); + + // Particle + var p = new CANNON.Body({ mass: 1 }); + p.addShape(new CANNON.Particle()); + p.position.set(0, 0, 7 * size); + world.addBody(t); + demo.addVisual(t); + world.addBody(p); + demo.addVisual(p); + }); + + // ConvexPolyhedron and box + demo.addScene("convex/convex", function () + { + var world = setupWorld(demo); + + var tetraShape = createTetra(); + var b1 = new CANNON.Body({ mass: 5 }); + b1.addShape(tetraShape); + b1.position.set(0.1, 0.1, 5 * size); + world.addBody(b1); + demo.addVisual(b1); + + var tetraShape = createTetra(); + var b1 = new CANNON.Body({ mass: 5 }); + b1.addShape(tetraShape); + b1.position.set(0, 0, 3 * size); + world.addBody(b1); + demo.addVisual(b1); + + }); + + // ConvexPolyhedron and particle + demo.addScene("convex/particle", function () + { + var world = setupWorld(demo); + + var tetraShape = createBoxPolyhedron(size); + var t = new CANNON.Body({ mass: 5 }); + t.addShape(tetraShape); + t.position.set(0, 0, 1 * size); + t.quaternion.fromAxisAngle(new CANNON.Vector3(1, 0, 0), Math.PI / 2); + + // Particle + var p = new CANNON.Body({ mass: 1 }); + p.addShape(new CANNON.Particle()); + p.position.set(0, 0, 3 * size); + + world.addBody(t); + demo.addVisual(t); + world.addBody(p); + demo.addVisual(p); + }); + + demo.start(); + + function setupWorld(demo) + { + var world = demo.getWorld(); + world.gravity.set(0, 0, -10); + world.broadphase = new CANNON.NaiveBroadphase(); + world.solver.iterations = 20; + + world.defaultContactMaterial.contactEquationStiffness = 1e7; + world.defaultContactMaterial.contactEquationRelaxation = 5; + + // ground plane + var groundShape = new CANNON.Plane(); + var groundBody = new CANNON.Body({ mass: 0 }); + groundBody.addShape(groundShape); + groundBody.position.set(0, 0, -1); + //groundBody.quaternion.fromAxisAngle(new CANNON.Vector3(0,1,0),0.2); + world.addBody(groundBody); + demo.addVisual(groundBody); + return world; + } + + + + + \ No newline at end of file diff --git a/demos/tear.html b/demos/tear.html index 1c32d1f79..c0da98ff9 100644 --- a/demos/tear.html +++ b/demos/tear.html @@ -1,96 +1,107 @@ - - cannon.js - constraints demo - - - - - - - - - - - - - - + + + + + + + + + + - - - + \ No newline at end of file diff --git a/demos/trimesh.html b/demos/trimesh.html index 4ca970427..78e537053 100644 --- a/demos/trimesh.html +++ b/demos/trimesh.html @@ -1,129 +1,140 @@ - - cannon.js - trimesh demo - - - - - - - - - - - - - - + + + + + + + + + - - + } + }; + world.addEventListener('postStep', listener); + var destroyer = function () + { + world.removeEventListener('postStep', listener); + demo.removeEventListener('destroy', destroyer); + }; + demo.addEventListener('destroy', destroyer); + }); + + demo.start(); + + + + + \ No newline at end of file diff --git a/demos/tween.html b/demos/tween.html index a5ea84156..7f575da57 100644 --- a/demos/tween.html +++ b/demos/tween.html @@ -1,82 +1,91 @@ - - cannon.js - tween demo - - - - - - - - - - - - - - + + + + + + + + + + - - - + \ No newline at end of file diff --git a/docs/api.js b/docs/api.js deleted file mode 100644 index af08abf34..000000000 --- a/docs/api.js +++ /dev/null @@ -1,63 +0,0 @@ -YUI.add("yuidoc-meta", function(Y) { - Y.YUIDoc = { meta: { - "classes": [ - "AABB", - "ArrayCollisionMatrix", - "Body", - "Box", - "Broadphase", - "ConeEquation", - "ConeTwistConstraint", - "Constraint", - "ContactEquation", - "ContactMaterial", - "ConvexPolyhedron", - "Cylinder", - "Demo", - "DistanceConstraint", - "Equation", - "EventTarget", - "FrictionEquation", - "GSSolver", - "GridBroadphase", - "Heightfield", - "HingeConstraint", - "JacobianElement", - "LockConstraint", - "Mat3", - "Material", - "NaiveBroadphase", - "Narrowphase", - "ObjectCollisionMatrix", - "Octree", - "OctreeNode", - "Particle", - "Plane", - "PointToPointConstraint", - "Pool", - "Quaternion", - "Ray", - "RaycastResult", - "RaycastVehicle", - "RigidVehicle", - "RotationalEquation", - "RotationalMotorEquation", - "SAPBroadphase", - "SPHSystem", - "Shape", - "Solver", - "Sphere", - "SplitSolver", - "Spring", - "Transform", - "Trimesh", - "TupleDictionary", - "Vec3", - "Vec3Pool", - "WheelInfo", - "World" - ], - "modules": [], - "allModules": [] -} }; -}); \ No newline at end of file diff --git a/docs/assets/css/external-small.png b/docs/assets/css/external-small.png deleted file mode 100644 index 759a1cdcb..000000000 Binary files a/docs/assets/css/external-small.png and /dev/null differ diff --git a/docs/assets/css/logo.png b/docs/assets/css/logo.png deleted file mode 100644 index c82444af9..000000000 Binary files a/docs/assets/css/logo.png and /dev/null differ diff --git a/docs/assets/css/main.css b/docs/assets/css/main.css deleted file mode 100644 index cdfe209e8..000000000 --- a/docs/assets/css/main.css +++ /dev/null @@ -1,783 +0,0 @@ -/* -Font sizes for all selectors other than the body are given in percentages, -with 100% equal to 13px. To calculate a font size percentage, multiply the -desired size in pixels by 7.6923076923. - -Here's a quick lookup table: - -10px - 76.923% -11px - 84.615% -12px - 92.308% -13px - 100% -14px - 107.692% -15px - 115.385% -16px - 123.077% -17px - 130.769% -18px - 138.462% -19px - 146.154% -20px - 153.846% -*/ - -html { - background: #fff; - color: #333; - overflow-y: scroll; -} - -body { - /*font: 13px/1.4 'Lucida Grande', 'Lucida Sans Unicode', 'DejaVu Sans', 'Bitstream Vera Sans', 'Helvetica', 'Arial', sans-serif;*/ - font: 13px/1.4 'Helvetica', 'Arial', sans-serif; - margin: 0; - padding: 0; -} - -/* -- Links ----------------------------------------------------------------- */ -a { - color: #356de4; - text-decoration: none; -} - -.hidden { - display: none; -} - -a:hover { text-decoration: underline; } - -/* "Jump to Table of Contents" link is shown to assistive tools, but hidden from - sight until it's focused. */ -.jump { - position: absolute; - padding: 3px 6px; - left: -99999px; - top: 0; -} - -.jump:focus { left: 40%; } - -/* -- Paragraphs ------------------------------------------------------------ */ -p { margin: 1.3em 0; } -dd p, td p { margin-bottom: 0; } -dd p:first-child, td p:first-child { margin-top: 0; } - -/* -- Headings -------------------------------------------------------------- */ -h1, h2, h3, h4, h5, h6 { - color: #D98527;/*was #f80*/ - font-family: 'Trebuchet MS', sans-serif; - font-weight: bold; - line-height: 1.1; - margin: 1.1em 0 0.5em; -} - -h1 { - font-size: 184.6%; - color: #30418C; - margin: 0.75em 0 0.5em; -} - -h2 { - font-size: 153.846%; - color: #E48A2B; -} - -h3 { font-size: 138.462%; } - -h4 { - border-bottom: 1px solid #DBDFEA; - color: #E48A2B; - font-size: 115.385%; - font-weight: normal; - padding-bottom: 2px; -} - -h5, h6 { font-size: 107.692%; } - -/* -- Code and examples ----------------------------------------------------- */ -code, kbd, pre, samp { - font-family: Menlo, Monaco, 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace; - font-size: 92.308%; - line-height: 1.35; -} - -p code, p kbd, p samp, li code { - background: #FCFBFA; - border: 1px solid #EFEEED; - padding: 0 3px; -} - -a code, a kbd, a samp, -pre code, pre kbd, pre samp, -table code, table kbd, table samp, -.intro code, .intro kbd, .intro samp, -.toc code, .toc kbd, .toc samp { - background: none; - border: none; - padding: 0; -} - -pre.code, pre.terminal, pre.cmd { - overflow-x: auto; - *overflow-x: scroll; - padding: 0.3em 0.6em; -} - -pre.code { - background: #FCFBFA; - border: 1px solid #EFEEED; - border-left-width: 5px; -} - -pre.terminal, pre.cmd { - background: #F0EFFC; - border: 1px solid #D0CBFB; - border-left: 5px solid #D0CBFB; -} - -/* Don't reduce the font size of // elements inside
-   blocks. */
-pre code, pre kbd, pre samp { font-size: 100%; }
-
-/* Used to denote text that shouldn't be selectable, such as line numbers or
-   shell prompts. Guess which browser this doesn't work in. */
-.noselect {
-    -moz-user-select: -moz-none;
-    -khtml-user-select: none;
-    -webkit-user-select: none;
-    -o-user-select: none;
-    user-select: none;
-}
-
-/* -- Lists ----------------------------------------------------------------- */
-dd { margin: 0.2em 0 0.7em 1em; }
-dl { margin: 1em 0; }
-dt { font-weight: bold; }
-
-/* -- Tables ---------------------------------------------------------------- */
-caption, th { text-align: left; }
-
-table {
-    border-collapse: collapse;
-    width: 100%;
-}
-
-td, th {
-    border: 1px solid #fff;
-    padding: 5px 12px;
-    vertical-align: top;
-}
-
-td { background: #E6E9F5; }
-td dl { margin: 0; }
-td dl dl { margin: 1em 0; }
-td pre:first-child { margin-top: 0; }
-
-th {
-    background: #D2D7E6;/*#97A0BF*/
-    border-bottom: none;
-    border-top: none;
-    color: #000;/*#FFF1D5*/
-    font-family: 'Trebuchet MS', sans-serif;
-    font-weight: bold;
-    line-height: 1.3;
-    white-space: nowrap;
-}
-
-
-/* -- Layout and Content ---------------------------------------------------- */
-#doc {
-    margin: auto;
-    min-width: 1024px;
-}
-
-.content { padding: 0 20px 0 25px; }
-
-.sidebar {
-    padding: 0 15px 0 10px;
-}
-#bd {
-    padding: 7px 0 130px;
-    position: relative;
-    width: 99%;
-}
-
-/* -- Table of Contents ----------------------------------------------------- */
-
-/* The #toc id refers to the single global table of contents, while the .toc
-   class refers to generic TOC lists that could be used throughout the page. */
-
-.toc code, .toc kbd, .toc samp { font-size: 100%; }
-.toc li { font-weight: bold; }
-.toc li li { font-weight: normal; }
-
-/* -- Intro and Example Boxes ----------------------------------------------- */
-/*
-.intro, .example { margin-bottom: 2em; }
-.example {
-    -moz-border-radius: 4px;
-    -webkit-border-radius: 4px;
-    border-radius: 4px;
-    -moz-box-shadow: 0 0 5px #bfbfbf;
-    -webkit-box-shadow: 0 0 5px #bfbfbf;
-    box-shadow: 0 0 5px #bfbfbf;
-    padding: 1em;
-}
-.intro {
-    background: none repeat scroll 0 0 #F0F1F8; border: 1px solid #D4D8EB; padding: 0 1em;
-}
-*/
-
-/* -- Other Styles ---------------------------------------------------------- */
-
-/* These are probably YUI-specific, and should be moved out of Selleck's default
-   theme. */
-
-.button {
-    border: 1px solid #dadada;
-    -moz-border-radius: 3px;
-    -webkit-border-radius: 3px;
-    border-radius: 3px;
-    color: #444;
-    display: inline-block;
-    font-family: Helvetica, Arial, sans-serif;
-    font-size: 92.308%;
-    font-weight: bold;
-    padding: 4px 13px 3px;
-    -moz-text-shadow: 1px 1px 0 #fff;
-    -webkit-text-shadow: 1px 1px 0 #fff;
-    text-shadow: 1px 1px 0 #fff;
-    white-space: nowrap;
-
-    background: #EFEFEF; /* old browsers */
-    background: -moz-linear-gradient(top, #f5f5f5 0%, #efefef 50%, #e5e5e5 51%, #dfdfdf 100%); /* firefox */
-    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f5f5f5), color-stop(50%,#efefef), color-stop(51%,#e5e5e5), color-stop(100%,#dfdfdf)); /* webkit */
-    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f5f5f5', endColorstr='#dfdfdf',GradientType=0 ); /* ie */
-}
-
-.button:hover {
-    border-color: #466899;
-    color: #fff;
-    text-decoration: none;
-    -moz-text-shadow: 1px 1px 0 #222;
-    -webkit-text-shadow: 1px 1px 0 #222;
-    text-shadow: 1px 1px 0 #222;
-
-    background: #6396D8; /* old browsers */
-    background: -moz-linear-gradient(top, #6396D8 0%, #5A83BC 50%, #547AB7 51%, #466899 100%); /* firefox */
-    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#6396D8), color-stop(50%,#5A83BC), color-stop(51%,#547AB7), color-stop(100%,#466899)); /* webkit */
-    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#6396D8', endColorstr='#466899',GradientType=0 ); /* ie */
-}
-
-.newwindow { text-align: center; }
-
-.header .version em {
-    display: block;
-    text-align: right;
-}
-
-
-#classdocs .item {
-    border-bottom: 1px solid #466899;
-    margin: 1em 0;
-    padding: 1.5em;
-}
-
-#classdocs .item .params p,
-    #classdocs .item .returns p,{
-    display: inline;
-}
-
-#classdocs .item em code, #classdocs .item em.comment {
-    color: green;
-}
-
-#classdocs .item em.comment a {
-    color: green;
-    text-decoration: underline;
-}
-
-#classdocs .foundat {
-    font-size: 11px;
-    font-style: normal;
-}
-
-.attrs .emits {
-    margin-left: 2em;
-    padding: .5em;
-    border-left: 1px dashed #ccc;
-}
-
-abbr {
-    border-bottom: 1px dashed #ccc;
-    font-size: 80%;
-    cursor: help;
-}
-
-.prettyprint li.L0, 
-.prettyprint li.L1, 
-.prettyprint li.L2, 
-.prettyprint li.L3, 
-.prettyprint li.L5, 
-.prettyprint li.L6, 
-.prettyprint li.L7, 
-.prettyprint li.L8 {
-    list-style: decimal;
-}
-
-ul li p {
-    margin-top: 0;
-}
-
-.method .name {
-    font-size: 110%;
-}
-
-.apidocs .methods .extends .method,
-.apidocs .properties .extends .property,
-.apidocs .attrs .extends .attr,
-.apidocs .events .extends .event {
-    font-weight: bold;
-}
-
-.apidocs .methods .extends .inherited,
-.apidocs .properties .extends .inherited,
-.apidocs .attrs .extends .inherited,
-.apidocs .events .extends .inherited {
-    font-weight: normal;
-}
-
-#hd {
-    background: whiteSmoke;
-    background: -moz-linear-gradient(top,#DCDBD9 0,#F6F5F3 100%);
-    background: -webkit-gradient(linear,left top,left bottom,color-stop(0%,#DCDBD9),color-stop(100%,#F6F5F3));
-    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#dcdbd9',endColorstr='#F6F5F3',GradientType=0);
-    border-bottom: 1px solid #DFDFDF;
-    padding: 0 15px 1px 20px;
-    margin-bottom: 15px;
-}
-
-#hd img {
-    margin-right: 10px;
-    vertical-align: middle;
-}
-
-
-/* -- API Docs CSS ---------------------------------------------------------- */
-
-/*
-This file is organized so that more generic styles are nearer the top, and more
-specific styles are nearer the bottom of the file. This allows us to take full
-advantage of the cascade to avoid redundant style rules. Please respect this
-convention when making changes.
-*/
-
-/* -- Generic TabView styles ------------------------------------------------ */
-
-/*
-These styles apply to all API doc tabviews. To change styles only for a
-specific tabview, see the other sections below.
-*/
-
-.yui3-js-enabled .apidocs .tabview {
-    visibility: hidden; /* Hide until the TabView finishes rendering. */
-    _visibility: visible;
-}
-
-.apidocs .tabview.yui3-tabview-content { visibility: visible; }
-.apidocs .tabview .yui3-tabview-panel { background: #fff; }
-
-/* -- Generic Content Styles ------------------------------------------------ */
-
-/* Headings */
-h2, h3, h4, h5, h6 {
-    border: none;
-    color: #30418C;
-    font-weight: bold;
-    text-decoration: none;
-}
-
-.link-docs {
-    float: right;
-    font-size: 15px;
-    margin: 4px 4px 6px;
-    padding: 6px 30px 5px;
-}
-
-.apidocs { zoom: 1; }
-
-/* Generic box styles. */
-.apidocs .box {
-    border: 1px solid;
-    border-radius: 3px;
-    margin: 1em 0;
-    padding: 0 1em;
-}
-
-/* A flag is a compact, capsule-like indicator of some kind. It's used to
-   indicate private and protected items, item return types, etc. in an
-   attractive and unobtrusive way. */
-.apidocs .flag {
-    background: #bababa;
-    border-radius: 3px;
-    color: #fff;
-    font-size: 11px;
-    margin: 0 0.5em;
-    padding: 2px 4px 1px;
-}
-
-/* Class/module metadata such as "Uses", "Extends", "Defined in", etc. */
-.apidocs .meta {
-    background: #f9f9f9;
-    border-color: #efefef;
-    color: #555;
-    font-size: 11px;
-    padding: 3px 6px;
-}
-
-.apidocs .meta p { margin: 0; }
-
-/* Deprecation warning. */
-.apidocs .box.deprecated,
-.apidocs .flag.deprecated {
-    background: #fdac9f;
-    border: 1px solid #fd7775;
-}
-
-.apidocs .box.deprecated p { margin: 0.5em 0; }
-.apidocs .flag.deprecated { color: #333; }
-
-/* Module/Class intro description. */
-.apidocs .intro {
-    background: #f0f1f8;
-    border-color: #d4d8eb;
-}
-
-/* Loading spinners. */
-#bd.loading .apidocs,
-#api-list.loading .yui3-tabview-panel {
-    background: #fff url(../img/spinner.gif) no-repeat center 70px;
-    min-height: 150px;
-}
-
-#bd.loading .apidocs .content,
-#api-list.loading .yui3-tabview-panel .apis {
-    display: none;
-}
-
-.apidocs .no-visible-items { color: #666; }
-
-/* Generic inline list. */
-.apidocs ul.inline {
-    display: inline;
-    list-style: none;
-    margin: 0;
-    padding: 0;
-}
-
-.apidocs ul.inline li { display: inline; }
-
-/* Comma-separated list. */
-.apidocs ul.commas li:after { content: ','; }
-.apidocs ul.commas li:last-child:after { content: ''; }
-
-/* Keyboard shortcuts. */
-kbd .cmd { font-family: Monaco, Helvetica; }
-
-/* -- Generic Access Level styles ------------------------------------------- */
-.apidocs .item.protected,
-.apidocs .item.private,
-.apidocs .index-item.protected,
-.apidocs .index-item.deprecated,
-.apidocs .index-item.private {
-    display: none;
-}
-
-.show-deprecated .item.deprecated,
-.show-deprecated .index-item.deprecated,
-.show-protected .item.protected,
-.show-protected .index-item.protected,
-.show-private .item.private,
-.show-private .index-item.private {
-    display: block;
-}
-
-.hide-inherited .item.inherited,
-.hide-inherited .index-item.inherited {
-    display: none;
-}
-
-/* -- Generic Item Index styles --------------------------------------------- */
-.apidocs .index { margin: 1.5em 0 3em; }
-
-.apidocs .index h3 {
-    border-bottom: 1px solid #efefef;
-    color: #333;
-    font-size: 13px;
-    margin: 2em 0 0.6em;
-    padding-bottom: 2px;
-}
-
-.apidocs .index .no-visible-items { margin-top: 2em; }
-
-.apidocs .index-list {
-    border-color: #efefef;
-    font-size: 12px;
-    list-style: none;
-    margin: 0;
-    padding: 0;
-    -moz-column-count: 4;
-    -moz-column-gap: 10px;
-    -moz-column-width: 170px;
-    -ms-column-count: 4;
-    -ms-column-gap: 10px;
-    -ms-column-width: 170px;
-    -o-column-count: 4;
-    -o-column-gap: 10px;
-    -o-column-width: 170px;
-    -webkit-column-count: 4;
-    -webkit-column-gap: 10px;
-    -webkit-column-width: 170px;
-    column-count: 4;
-    column-gap: 10px;
-    column-width: 170px;
-}
-
-.apidocs .no-columns .index-list {
-    -moz-column-count: 1;
-    -ms-column-count: 1;
-    -o-column-count: 1;
-    -webkit-column-count: 1;
-    column-count: 1;
-}
-
-.apidocs .index-item { white-space: nowrap; }
-
-.apidocs .index-item .flag {
-    background: none;
-    border: none;
-    color: #afafaf;
-    display: inline;
-    margin: 0 0 0 0.2em;
-    padding: 0;
-}
-
-/* -- Generic API item styles ----------------------------------------------- */
-.apidocs .args {
-    display: inline;
-    margin: 0 0.5em;
-}
-
-.apidocs .flag.chainable { background: #46ca3b; }
-.apidocs .flag.protected { background: #9b86fc; }
-.apidocs .flag.private { background: #fd6b1b; }
-.apidocs .flag.async { background: #356de4; }
-.apidocs .flag.required { background: #e60923; }
-
-.apidocs .item {
-    border-bottom: 1px solid #efefef;
-    margin: 1.5em 0 2em;
-    padding-bottom: 2em;
-}
-
-.apidocs .item h4,
-.apidocs .item h5,
-.apidocs .item h6 {
-    color: #333;
-    font-family: inherit;
-    font-size: 100%;
-}
-
-.apidocs .item .description p,
-.apidocs .item pre.code {
-    margin: 1em 0 0;
-}
-
-.apidocs .item .meta {
-    background: none;
-    border: none;
-    padding: 0;
-}
-
-.apidocs .item .name {
-    display: inline;
-    font-size: 14px;
-}
-
-.apidocs .item .type,
-.apidocs .item .type a,
-.apidocs .returns-inline {
-    color: #555;
-}
-
-.apidocs .item .type,
-.apidocs .returns-inline {
-    font-size: 11px;
-    margin: 0 0 0 0;
-}
-
-.apidocs .item .type a { border-bottom: 1px dotted #afafaf; }
-.apidocs .item .type a:hover { border: none; }
-
-/* -- Item Parameter List --------------------------------------------------- */
-.apidocs .params-list {
-    list-style: square;
-    margin: 1em 0 0 2em;
-    padding: 0;
-}
-
-.apidocs .param { margin-bottom: 1em; }
-
-.apidocs .param .type,
-.apidocs .param .type a {
-    color: #666;
-}
-
-.apidocs .param .type {
-    margin: 0 0 0 0.5em;
-    *margin-left: 0.5em;
-}
-
-.apidocs .param-name { font-weight: bold; }
-
-/* -- Item "Emits" block ---------------------------------------------------- */
-.apidocs .item .emits {
-    background: #f9f9f9;
-    border-color: #eaeaea;
-}
-
-/* -- Item "Returns" block -------------------------------------------------- */
-.apidocs .item .returns .type,
-.apidocs .item .returns .type a {
-    font-size: 100%;
-    margin: 0;
-}
-
-/* -- Class Constructor block ----------------------------------------------- */
-.apidocs .constructor .item {
-    border: none;
-    padding-bottom: 0;
-}
-
-/* -- File Source View ------------------------------------------------------ */
-.apidocs .file pre.code,
-#doc .apidocs .file pre.prettyprint {
-    background: inherit;
-    border: none;
-    overflow: visible;
-    padding: 0;
-}
-
-.apidocs .L0,
-.apidocs .L1,
-.apidocs .L2,
-.apidocs .L3,
-.apidocs .L4,
-.apidocs .L5,
-.apidocs .L6,
-.apidocs .L7,
-.apidocs .L8,
-.apidocs .L9 {
-    background: inherit;
-}
-
-/* -- Submodule List -------------------------------------------------------- */
-.apidocs .module-submodule-description {
-    font-size: 12px;
-    margin: 0.3em 0 1em;
-}
-
-.apidocs .module-submodule-description p:first-child { margin-top: 0; }
-
-/* -- Sidebar TabView ------------------------------------------------------- */
-#api-tabview { margin-top: 0.6em; }
-
-#api-tabview-filter,
-#api-tabview-panel {
-    border: 1px solid #dfdfdf;
-}
-
-#api-tabview-filter {
-    border-bottom: none;
-    border-top: none;
-    padding: 0.6em 10px 0 10px;
-}
-
-#api-tabview-panel { border-top: none; }
-#api-filter { width: 97%; }
-
-/* -- Content TabView ------------------------------------------------------- */
-#classdocs .yui3-tabview-panel { border: none; }
-
-/* -- Source File Contents -------------------------------------------------- */
-.prettyprint li.L0,
-.prettyprint li.L1,
-.prettyprint li.L2,
-.prettyprint li.L3,
-.prettyprint li.L5,
-.prettyprint li.L6,
-.prettyprint li.L7,
-.prettyprint li.L8 {
-    list-style: decimal;
-}
-
-/* -- API options ----------------------------------------------------------- */
-#api-options {
-    font-size: 11px;
-    margin-top: 2.2em;
-    position: absolute;
-    right: 1.5em;
-}
-
-/*#api-options label { margin-right: 0.6em; }*/
-
-/* -- API list -------------------------------------------------------------- */
-#api-list {
-    margin-top: 1.5em;
-    *zoom: 1;
-}
-
-.apis {
-    font-size: 12px;
-    line-height: 1.4;
-    list-style: none;
-    margin: 0;
-    padding: 0.5em 0 0.5em 0.4em;
-}
-
-.apis a {
-    border: 1px solid transparent;
-    display: block;
-    margin: 0 0 0 -4px;
-    padding: 1px 4px 0;
-    text-decoration: none;
-    _border: none;
-    _display: inline;
-}
-
-.apis a:hover,
-.apis a:focus {
-    background: #E8EDFC;
-    background: -moz-linear-gradient(top, #e8edfc 0%, #becef7 100%);
-    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#E8EDFC), color-stop(100%,#BECEF7));
-    border-color: #AAC0FA;
-    border-radius: 3px;
-    color: #333;
-    outline: none;
-}
-
-.api-list-item a:hover,
-.api-list-item a:focus {
-    font-weight: bold;
-    text-shadow: 1px 1px 1px #fff;
-}
-
-.apis .message { color: #888; }
-.apis .result a { padding: 3px 5px 2px; }
-
-.apis .result .type {
-    right: 4px;
-    top: 7px;
-}
-
-.api-list-item .yui3-highlight {
-    font-weight: bold;
-}
-
diff --git a/docs/assets/favicon.ico b/docs/assets/favicon.ico
deleted file mode 100644
index 414ac4fb9..000000000
Binary files a/docs/assets/favicon.ico and /dev/null differ
diff --git a/docs/assets/img/spinner.gif b/docs/assets/img/spinner.gif
deleted file mode 100644
index 44f96ba68..000000000
Binary files a/docs/assets/img/spinner.gif and /dev/null differ
diff --git a/docs/assets/index.html b/docs/assets/index.html
deleted file mode 100644
index 487fe15b2..000000000
--- a/docs/assets/index.html
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-    
-        Redirector
-        
-    
-    
-        Click here to redirect
-    
-
diff --git a/docs/assets/js/api-filter.js b/docs/assets/js/api-filter.js
deleted file mode 100644
index 37aefbab9..000000000
--- a/docs/assets/js/api-filter.js
+++ /dev/null
@@ -1,52 +0,0 @@
-YUI.add('api-filter', function (Y) {
-
-Y.APIFilter = Y.Base.create('apiFilter', Y.Base, [Y.AutoCompleteBase], {
-    // -- Initializer ----------------------------------------------------------
-    initializer: function () {
-        this._bindUIACBase();
-        this._syncUIACBase();
-    },
-    getDisplayName: function(name) {
-
-        Y.each(Y.YUIDoc.meta.allModules, function(i) {
-            if (i.name === name && i.displayName) {
-                name = i.displayName;
-            }
-        });
-
-        return name;
-    }
-
-}, {
-    // -- Attributes -----------------------------------------------------------
-    ATTRS: {
-        resultHighlighter: {
-            value: 'phraseMatch'
-        },
-
-        // May be set to "classes" or "modules".
-        queryType: {
-            value: 'classes'
-        },
-
-        source: {
-            valueFn: function() {
-                var self = this;
-                return function(q) {
-                    var data = Y.YUIDoc.meta[self.get('queryType')],
-                        out = [];
-                    Y.each(data, function(v) {
-                        if (v.toLowerCase().indexOf(q.toLowerCase()) > -1) {
-                            out.push(v);
-                        }
-                    });
-                    return out;
-                };
-            }
-        }
-    }
-});
-
-}, '3.4.0', {requires: [
-    'autocomplete-base', 'autocomplete-highlighters', 'autocomplete-sources'
-]});
diff --git a/docs/assets/js/api-list.js b/docs/assets/js/api-list.js
deleted file mode 100644
index 88905b52e..000000000
--- a/docs/assets/js/api-list.js
+++ /dev/null
@@ -1,251 +0,0 @@
-YUI.add('api-list', function (Y) {
-
-var Lang   = Y.Lang,
-    YArray = Y.Array,
-
-    APIList = Y.namespace('APIList'),
-
-    classesNode    = Y.one('#api-classes'),
-    inputNode      = Y.one('#api-filter'),
-    modulesNode    = Y.one('#api-modules'),
-    tabviewNode    = Y.one('#api-tabview'),
-
-    tabs = APIList.tabs = {},
-
-    filter = APIList.filter = new Y.APIFilter({
-        inputNode : inputNode,
-        maxResults: 1000,
-
-        on: {
-            results: onFilterResults
-        }
-    }),
-
-    search = APIList.search = new Y.APISearch({
-        inputNode : inputNode,
-        maxResults: 100,
-
-        on: {
-            clear  : onSearchClear,
-            results: onSearchResults
-        }
-    }),
-
-    tabview = APIList.tabview = new Y.TabView({
-        srcNode  : tabviewNode,
-        panelNode: '#api-tabview-panel',
-        render   : true,
-
-        on: {
-            selectionChange: onTabSelectionChange
-        }
-    }),
-
-    focusManager = APIList.focusManager = tabviewNode.plug(Y.Plugin.NodeFocusManager, {
-        circular   : true,
-        descendants: '#api-filter, .yui3-tab-panel-selected .api-list-item a, .yui3-tab-panel-selected .result a',
-        keys       : {next: 'down:40', previous: 'down:38'}
-    }).focusManager,
-
-    LIST_ITEM_TEMPLATE =
-        '
  • ' + - '{displayName}' + - '
  • '; - -// -- Init --------------------------------------------------------------------- - -// Duckpunch FocusManager's key event handling to prevent it from handling key -// events when a modifier is pressed. -Y.before(function (e, activeDescendant) { - if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { - return new Y.Do.Prevent(); - } -}, focusManager, '_focusPrevious', focusManager); - -Y.before(function (e, activeDescendant) { - if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { - return new Y.Do.Prevent(); - } -}, focusManager, '_focusNext', focusManager); - -// Create a mapping of tabs in the tabview so we can refer to them easily later. -tabview.each(function (tab, index) { - var name = tab.get('label').toLowerCase(); - - tabs[name] = { - index: index, - name : name, - tab : tab - }; -}); - -// Switch tabs on Ctrl/Cmd-Left/Right arrows. -tabviewNode.on('key', onTabSwitchKey, 'down:37,39'); - -// Focus the filter input when the `/` key is pressed. -Y.one(Y.config.doc).on('key', onSearchKey, 'down:83'); - -// Keep the Focus Manager up to date. -inputNode.on('focus', function () { - focusManager.set('activeDescendant', inputNode); -}); - -// Update all tabview links to resolved URLs. -tabview.get('panelNode').all('a').each(function (link) { - link.setAttribute('href', link.get('href')); -}); - -// -- Private Functions -------------------------------------------------------- -function getFilterResultNode() { - return filter.get('queryType') === 'classes' ? classesNode : modulesNode; -} - -// -- Event Handlers ----------------------------------------------------------- -function onFilterResults(e) { - var frag = Y.one(Y.config.doc.createDocumentFragment()), - resultNode = getFilterResultNode(), - typePlural = filter.get('queryType'), - typeSingular = typePlural === 'classes' ? 'class' : 'module'; - - if (e.results.length) { - YArray.each(e.results, function (result) { - frag.append(Lang.sub(LIST_ITEM_TEMPLATE, { - rootPath : APIList.rootPath, - displayName : filter.getDisplayName(result.highlighted), - name : result.text, - typePlural : typePlural, - typeSingular: typeSingular - })); - }); - } else { - frag.append( - '
  • ' + - 'No ' + typePlural + ' found.' + - '
  • ' - ); - } - - resultNode.empty(true); - resultNode.append(frag); - - focusManager.refresh(); -} - -function onSearchClear(e) { - - focusManager.refresh(); -} - -function onSearchKey(e) { - var target = e.target; - - if (target.test('input,select,textarea') - || target.get('isContentEditable')) { - return; - } - - e.preventDefault(); - - inputNode.focus(); - focusManager.refresh(); -} - -function onSearchResults(e) { - var frag = Y.one(Y.config.doc.createDocumentFragment()); - - if (e.results.length) { - YArray.each(e.results, function (result) { - frag.append(result.display); - }); - } else { - frag.append( - '
  • ' + - 'No results found. Maybe you\'ll have better luck with a ' + - 'different query?' + - '
  • ' - ); - } - - - focusManager.refresh(); -} - -function onTabSelectionChange(e) { - var tab = e.newVal, - name = tab.get('label').toLowerCase(); - - tabs.selected = { - index: tab.get('index'), - name : name, - tab : tab - }; - - switch (name) { - case 'classes': // fallthru - case 'modules': - filter.setAttrs({ - minQueryLength: 0, - queryType : name - }); - - search.set('minQueryLength', -1); - - // Only send a request if this isn't the initially-selected tab. - if (e.prevVal) { - filter.sendRequest(filter.get('value')); - } - break; - - case 'everything': - filter.set('minQueryLength', -1); - search.set('minQueryLength', 1); - - if (search.get('value')) { - search.sendRequest(search.get('value')); - } else { - inputNode.focus(); - } - break; - - default: - // WTF? We shouldn't be here! - filter.set('minQueryLength', -1); - search.set('minQueryLength', -1); - } - - if (focusManager) { - setTimeout(function () { - focusManager.refresh(); - }, 1); - } -} - -function onTabSwitchKey(e) { - var currentTabIndex = tabs.selected.index; - - if (!(e.ctrlKey || e.metaKey)) { - return; - } - - e.preventDefault(); - - switch (e.keyCode) { - case 37: // left arrow - if (currentTabIndex > 0) { - tabview.selectChild(currentTabIndex - 1); - inputNode.focus(); - } - break; - - case 39: // right arrow - if (currentTabIndex < (Y.Object.size(tabs) - 2)) { - tabview.selectChild(currentTabIndex + 1); - inputNode.focus(); - } - break; - } -} - -}, '3.4.0', {requires: [ - 'api-filter', 'api-search', 'event-key', 'node-focusmanager', 'tabview' -]}); diff --git a/docs/assets/js/api-search.js b/docs/assets/js/api-search.js deleted file mode 100644 index 175f6a617..000000000 --- a/docs/assets/js/api-search.js +++ /dev/null @@ -1,98 +0,0 @@ -YUI.add('api-search', function (Y) { - -var Lang = Y.Lang, - Node = Y.Node, - YArray = Y.Array; - -Y.APISearch = Y.Base.create('apiSearch', Y.Base, [Y.AutoCompleteBase], { - // -- Public Properties ---------------------------------------------------- - RESULT_TEMPLATE: - '
  • ' + - '' + - '

    {name}

    ' + - '{resultType}' + - '
    {description}
    ' + - '{class}' + - '
    ' + - '
  • ', - - // -- Initializer ---------------------------------------------------------- - initializer: function () { - this._bindUIACBase(); - this._syncUIACBase(); - }, - - // -- Protected Methods ---------------------------------------------------- - _apiResultFilter: function (query, results) { - // Filter components out of the results. - return YArray.filter(results, function (result) { - return result.raw.resultType === 'component' ? false : result; - }); - }, - - _apiResultFormatter: function (query, results) { - return YArray.map(results, function (result) { - var raw = Y.merge(result.raw), // create a copy - desc = raw.description || ''; - - // Convert description to text and truncate it if necessary. - desc = Node.create('
    ' + desc + '
    ').get('text'); - - if (desc.length > 65) { - desc = Y.Escape.html(desc.substr(0, 65)) + ' …'; - } else { - desc = Y.Escape.html(desc); - } - - raw['class'] || (raw['class'] = ''); - raw.description = desc; - - // Use the highlighted result name. - raw.name = result.highlighted; - - return Lang.sub(this.RESULT_TEMPLATE, raw); - }, this); - }, - - _apiTextLocator: function (result) { - return result.displayName || result.name; - } -}, { - // -- Attributes ----------------------------------------------------------- - ATTRS: { - resultFormatter: { - valueFn: function () { - return this._apiResultFormatter; - } - }, - - resultFilters: { - valueFn: function () { - return this._apiResultFilter; - } - }, - - resultHighlighter: { - value: 'phraseMatch' - }, - - resultListLocator: { - value: 'data.results' - }, - - resultTextLocator: { - valueFn: function () { - return this._apiTextLocator; - } - }, - - source: { - value: '/api/v1/search?q={query}&count={maxResults}' - } - } -}); - -}, '3.4.0', {requires: [ - 'autocomplete-base', 'autocomplete-highlighters', 'autocomplete-sources', - 'escape' -]}); diff --git a/docs/assets/js/apidocs.js b/docs/assets/js/apidocs.js deleted file mode 100644 index e00591c1e..000000000 --- a/docs/assets/js/apidocs.js +++ /dev/null @@ -1,370 +0,0 @@ -YUI().use( - 'yuidoc-meta', - 'api-list', 'history-hash', 'node-screen', 'node-style', 'pjax', -function (Y) { - -var win = Y.config.win, - localStorage = win.localStorage, - - bdNode = Y.one('#bd'), - - pjax, - defaultRoute, - - classTabView, - selectedTab; - -// Kill pjax functionality unless serving over HTTP. -if (!Y.getLocation().protocol.match(/^https?\:/)) { - Y.Router.html5 = false; -} - -// Create the default route with middleware which enables syntax highlighting -// on the loaded content. -defaultRoute = Y.Pjax.defaultRoute.concat(function (req, res, next) { - prettyPrint(); - bdNode.removeClass('loading'); - - next(); -}); - -pjax = new Y.Pjax({ - container : '#docs-main', - contentSelector: '#docs-main > .content', - linkSelector : '#bd a', - titleSelector : '#xhr-title', - - navigateOnHash: true, - root : '/', - routes : [ - // -- / ---------------------------------------------------------------- - { - path : '/(index.html)?', - callbacks: defaultRoute - }, - - // -- /classes/* ------------------------------------------------------- - { - path : '/classes/:class.html*', - callbacks: [defaultRoute, 'handleClasses'] - }, - - // -- /files/* --------------------------------------------------------- - { - path : '/files/*file', - callbacks: [defaultRoute, 'handleFiles'] - }, - - // -- /modules/* ------------------------------------------------------- - { - path : '/modules/:module.html*', - callbacks: defaultRoute - } - ] -}); - -// -- Utility Functions -------------------------------------------------------- - -pjax.checkVisibility = function (tab) { - tab || (tab = selectedTab); - - if (!tab) { return; } - - var panelNode = tab.get('panelNode'), - visibleItems; - - // If no items are visible in the tab panel due to the current visibility - // settings, display a message to that effect. - visibleItems = panelNode.all('.item,.index-item').some(function (itemNode) { - if (itemNode.getComputedStyle('display') !== 'none') { - return true; - } - }); - - panelNode.all('.no-visible-items').remove(); - - if (!visibleItems) { - if (Y.one('#index .index-item')) { - panelNode.append( - '
    ' + - '

    ' + - 'Some items are not shown due to the current visibility ' + - 'settings. Use the checkboxes at the upper right of this ' + - 'page to change the visibility settings.' + - '

    ' + - '
    ' - ); - } else { - panelNode.append( - '
    ' + - '

    ' + - 'This class doesn\'t provide any methods, properties, ' + - 'attributes, or events.' + - '

    ' + - '
    ' - ); - } - } - - // Hide index sections without any visible items. - Y.all('.index-section').each(function (section) { - var items = 0, - visibleItems = 0; - - section.all('.index-item').each(function (itemNode) { - items += 1; - - if (itemNode.getComputedStyle('display') !== 'none') { - visibleItems += 1; - } - }); - - section.toggleClass('hidden', !visibleItems); - section.toggleClass('no-columns', visibleItems < 4); - }); -}; - -pjax.initClassTabView = function () { - if (!Y.all('#classdocs .api-class-tab').size()) { - return; - } - - if (classTabView) { - classTabView.destroy(); - selectedTab = null; - } - - classTabView = new Y.TabView({ - srcNode: '#classdocs', - - on: { - selectionChange: pjax.onTabSelectionChange - } - }); - - pjax.updateTabState(); - classTabView.render(); -}; - -pjax.initLineNumbers = function () { - var hash = win.location.hash.substring(1), - container = pjax.get('container'), - hasLines, node; - - // Add ids for each line number in the file source view. - container.all('.linenums>li').each(function (lineNode, index) { - lineNode.set('id', 'l' + (index + 1)); - lineNode.addClass('file-line'); - hasLines = true; - }); - - // Scroll to the desired line. - if (hasLines && /^l\d+$/.test(hash)) { - if ((node = container.getById(hash))) { - win.scroll(0, node.getY()); - } - } -}; - -pjax.initRoot = function () { - var terminators = /^(?:classes|files|modules)$/, - parts = pjax._getPathRoot().split('/'), - root = [], - i, len, part; - - for (i = 0, len = parts.length; i < len; i += 1) { - part = parts[i]; - - if (part.match(terminators)) { - // Makes sure the path will end with a "/". - root.push(''); - break; - } - - root.push(part); - } - - pjax.set('root', root.join('/')); -}; - -pjax.updateTabState = function (src) { - var hash = win.location.hash.substring(1), - defaultTab, node, tab, tabPanel; - - function scrollToNode() { - if (node.hasClass('protected')) { - Y.one('#api-show-protected').set('checked', true); - pjax.updateVisibility(); - } - - if (node.hasClass('private')) { - Y.one('#api-show-private').set('checked', true); - pjax.updateVisibility(); - } - - setTimeout(function () { - // For some reason, unless we re-get the node instance here, - // getY() always returns 0. - var node = Y.one('#classdocs').getById(hash); - win.scrollTo(0, node.getY() - 70); - }, 1); - } - - if (!classTabView) { - return; - } - - if (src === 'hashchange' && !hash) { - defaultTab = 'index'; - } else { - if (localStorage) { - defaultTab = localStorage.getItem('tab_' + pjax.getPath()) || - 'index'; - } else { - defaultTab = 'index'; - } - } - - if (hash && (node = Y.one('#classdocs').getById(hash))) { - if ((tabPanel = node.ancestor('.api-class-tabpanel', true))) { - if ((tab = Y.one('#classdocs .api-class-tab.' + tabPanel.get('id')))) { - if (classTabView.get('rendered')) { - Y.Widget.getByNode(tab).set('selected', 1); - } else { - tab.addClass('yui3-tab-selected'); - } - } - } - - // Scroll to the desired element if this is a hash URL. - if (node) { - if (classTabView.get('rendered')) { - scrollToNode(); - } else { - classTabView.once('renderedChange', scrollToNode); - } - } - } else { - tab = Y.one('#classdocs .api-class-tab.' + defaultTab); - - // When the `defaultTab` node isn't found, `localStorage` is stale. - if (!tab && defaultTab !== 'index') { - tab = Y.one('#classdocs .api-class-tab.index'); - } - - if (classTabView.get('rendered')) { - Y.Widget.getByNode(tab).set('selected', 1); - } else { - tab.addClass('yui3-tab-selected'); - } - } -}; - -pjax.updateVisibility = function () { - var container = pjax.get('container'); - - container.toggleClass('hide-inherited', - !Y.one('#api-show-inherited').get('checked')); - - container.toggleClass('show-deprecated', - Y.one('#api-show-deprecated').get('checked')); - - container.toggleClass('show-protected', - Y.one('#api-show-protected').get('checked')); - - container.toggleClass('show-private', - Y.one('#api-show-private').get('checked')); - - pjax.checkVisibility(); -}; - -// -- Route Handlers ----------------------------------------------------------- - -pjax.handleClasses = function (req, res, next) { - var status = res.ioResponse.status; - - // Handles success and local filesystem XHRs. - if (res.ioResponse.readyState === 4 && (!status || (status >= 200 && status < 300))) { - pjax.initClassTabView(); - } - - next(); -}; - -pjax.handleFiles = function (req, res, next) { - var status = res.ioResponse.status; - - // Handles success and local filesystem XHRs. - if (res.ioResponse.readyState === 4 && (!status || (status >= 200 && status < 300))) { - pjax.initLineNumbers(); - } - - next(); -}; - -// -- Event Handlers ----------------------------------------------------------- - -pjax.onNavigate = function (e) { - var hash = e.hash, - originTarget = e.originEvent && e.originEvent.target, - tab; - - if (hash) { - tab = originTarget && originTarget.ancestor('.yui3-tab', true); - - if (hash === win.location.hash) { - pjax.updateTabState('hashchange'); - } else if (!tab) { - win.location.hash = hash; - } - - e.preventDefault(); - return; - } - - // Only scroll to the top of the page when the URL doesn't have a hash. - this.set('scrollToTop', !e.url.match(/#.+$/)); - - bdNode.addClass('loading'); -}; - -pjax.onOptionClick = function (e) { - pjax.updateVisibility(); -}; - -pjax.onTabSelectionChange = function (e) { - var tab = e.newVal, - tabId = tab.get('contentBox').getAttribute('href').substring(1); - - selectedTab = tab; - - // If switching from a previous tab (i.e., this is not the default tab), - // replace the history entry with a hash URL that will cause this tab to - // be selected if the user navigates away and then returns using the back - // or forward buttons. - if (e.prevVal && localStorage) { - localStorage.setItem('tab_' + pjax.getPath(), tabId); - } - - pjax.checkVisibility(tab); -}; - -// -- Init --------------------------------------------------------------------- - -pjax.on('navigate', pjax.onNavigate); - -pjax.initRoot(); -pjax.upgrade(); -pjax.initClassTabView(); -pjax.initLineNumbers(); -pjax.updateVisibility(); - -Y.APIList.rootPath = pjax.get('root'); - -Y.one('#api-options').delegate('click', pjax.onOptionClick, 'input'); - -Y.on('hashchange', function (e) { - pjax.updateTabState('hashchange'); -}, win); - -}); diff --git a/docs/assets/js/yui-prettify.js b/docs/assets/js/yui-prettify.js deleted file mode 100644 index 18de86495..000000000 --- a/docs/assets/js/yui-prettify.js +++ /dev/null @@ -1,17 +0,0 @@ -YUI().use('node', function(Y) { - var code = Y.all('.prettyprint.linenums'); - if (code.size()) { - code.each(function(c) { - var lis = c.all('ol li'), - l = 1; - lis.each(function(n) { - n.prepend(''); - l++; - }); - }); - var h = location.hash; - location.hash = ''; - h = h.replace('LINE_', 'LINENUM_'); - location.hash = h; - } -}); diff --git a/docs/assets/vendor/prettify/CHANGES.html b/docs/assets/vendor/prettify/CHANGES.html deleted file mode 100644 index b50b84149..000000000 --- a/docs/assets/vendor/prettify/CHANGES.html +++ /dev/null @@ -1,130 +0,0 @@ - - - - Change Log - - - README - -

    Known Issues

    -
      -
    • Perl formatting is really crappy. Partly because the author is lazy and - partly because Perl is - hard to parse. -
    • On some browsers, <code> elements with newlines in the text - which use CSS to specify white-space:pre will have the newlines - improperly stripped if the element is not attached to the document at the time - the stripping is done. Also, on IE 6, all newlines will be stripped from - <code> elements because of the way IE6 produces - innerHTML. Workaround: use <pre> for code with - newlines. -
    - -

    Change Log

    -

    29 March 2007

    -
      -
    • Added tests for PHP support - to address - issue 3. -
    • Fixed - bug: prettyPrintOne was not halting. This was not - reachable through the normal entry point. -
    • Fixed - bug: recursing into a script block or PHP tag that was not properly - closed would not silently drop the content. - (test) -
    • Fixed - bug: was eating tabs - (test) -
    • Fixed entity handling so that the caveat -
      -

      Caveats: please properly escape less-thans. x&lt;y - instead of x<y, and use " instead of - &quot; for string delimiters.

      -
      - is no longer applicable. -
    • Added noisefree's C# - patch -
    • Added a distribution that has comments and - whitespace removed to reduce download size from 45.5kB to 12.8kB. -
    -

    4 Jul 2008

    -
      -
    • Added language specific formatters that are triggered by the presence - of a lang-<language-file-extension>
    • -
    • Fixed bug: python handling of '''string''' -
    • Fixed bug: / in regex [charsets] should not end regex -
    -

    5 Jul 2008

    -
      -
    • Defined language extensions for Lisp and Lua -
    -

    14 Jul 2008

    -
      -
    • Language handlers for F#, OCAML, SQL -
    • Support for nocode spans to allow embedding of line - numbers and code annotations which should not be styled or otherwise - affect the tokenization of prettified code. - See the issue 22 - testcase. -
    -

    6 Jan 2009

    -
      -
    • Language handlers for Visual Basic, Haskell, CSS, and WikiText
    • -
    • Added .mxml extension to the markup style handler for - Flex MXML files. See - issue 37. -
    • Added .m extension to the C style handler so that Objective - C source files properly highlight. See - issue 58. -
    • Changed HTML lexer to use the same embedded source mechanism as the - wiki language handler, and changed to use the registered - CSS handler for STYLE element content. -
    -

    21 May 2009

    -
      -
    • Rewrote to improve performance on large files. - See benchmarks.
    • -
    • Fixed bugs with highlighting of Haskell line comments, Lisp - number literals, Lua strings, C preprocessor directives, - newlines in Wiki code on Windows, and newlines in IE6.
    • -
    -

    14 August 2009

    -
      -
    • Fixed prettifying of <code> blocks with embedded newlines. -
    -

    3 October 2009

    -
      -
    • Fixed prettifying of XML/HTML tags that contain uppercase letters. -
    -

    19 July 2010

    -
      -
    • Added support for line numbers. Bug - 22
    • -
    • Added YAML support. Bug - 123
    • -
    • Added VHDL support courtesy Le Poussin.
    • -
    • IE performance improvements. Bug - 102 courtesy jacobly.
    • -
    • A variety of markup formatting fixes courtesy smain and thezbyg.
    • -
    • Fixed copy and paste in IE[678]. -
    • Changed output to use &#160; instead of - &nbsp; so that the output works when embedded in XML. - Bug - 108.
    • -
    - - diff --git a/docs/assets/vendor/prettify/COPYING b/docs/assets/vendor/prettify/COPYING deleted file mode 100644 index d64569567..000000000 --- a/docs/assets/vendor/prettify/COPYING +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/docs/assets/vendor/prettify/README.html b/docs/assets/vendor/prettify/README.html deleted file mode 100644 index c6fe1a32c..000000000 --- a/docs/assets/vendor/prettify/README.html +++ /dev/null @@ -1,203 +0,0 @@ - - - - - Javascript code prettifier - - - - - - - - - - Languages : CH -

    Javascript code prettifier

    - -

    Setup

    -
      -
    1. Download a distribution -
    2. Include the script and stylesheets in your document - (you will need to make sure the css and js file are on your server, and - adjust the paths in the script and link tag) -
      -<link href="prettify.css" type="text/css" rel="stylesheet" />
      -<script type="text/javascript" src="prettify.js"></script>
      -
    3. Add onload="prettyPrint()" to your - document's body tag. -
    4. Modify the stylesheet to get the coloring you prefer
    5. -
    - -

    Usage

    -

    Put code snippets in - <pre class="prettyprint">...</pre> - or <code class="prettyprint">...</code> - and it will automatically be pretty printed. - - - - -
    The original - Prettier -
    class Voila {
    -public:
    -  // Voila
    -  static const string VOILA = "Voila";
    -
    -  // will not interfere with embedded tags.
    -}
    - -
    class Voila {
    -public:
    -  // Voila
    -  static const string VOILA = "Voila";
    -
    -  // will not interfere with embedded tags.
    -}
    -
    - -

    FAQ

    -

    Which languages does it work for?

    -

    The comments in prettify.js are authoritative but the lexer - should work on a number of languages including C and friends, - Java, Python, Bash, SQL, HTML, XML, CSS, Javascript, and Makefiles. - It works passably on Ruby, PHP, VB, and Awk and a decent subset of Perl - and Ruby, but, because of commenting conventions, doesn't work on - Smalltalk, or CAML-like languages.

    - -

    LISPy languages are supported via an extension: - lang-lisp.js.

    -

    And similarly for - CSS, - Haskell, - Lua, - OCAML, SML, F#, - Visual Basic, - SQL, - Protocol Buffers, and - WikiText.. - -

    If you'd like to add an extension for your favorite language, please - look at src/lang-lisp.js and file an - issue including your language extension, and a testcase.

    - -

    How do I specify which language my code is in?

    -

    You don't need to specify the language since prettyprint() - will guess. You can specify a language by specifying the language extension - along with the prettyprint class like so:

    -
    <pre class="prettyprint lang-html">
    -  The lang-* class specifies the language file extensions.
    -  File extensions supported by default include
    -    "bsh", "c", "cc", "cpp", "cs", "csh", "cyc", "cv", "htm", "html",
    -    "java", "js", "m", "mxml", "perl", "pl", "pm", "py", "rb", "sh",
    -    "xhtml", "xml", "xsl".
    -</pre>
    - -

    It doesn't work on <obfuscated code sample>?

    -

    Yes. Prettifying obfuscated code is like putting lipstick on a pig - — i.e. outside the scope of this tool.

    - -

    Which browsers does it work with?

    -

    It's been tested with IE 6, Firefox 1.5 & 2, and Safari 2.0.4. - Look at the test page to see if it - works in your browser.

    - -

    What's changed?

    -

    See the change log

    - -

    Why doesn't Prettyprinting of strings work on WordPress?

    -

    Apparently wordpress does "smart quoting" which changes close quotes. - This causes end quotes to not match up with open quotes. -

    This breaks prettifying as well as copying and pasting of code samples. - See - WordPress's help center for info on how to stop smart quoting of code - snippets.

    - -

    How do I put line numbers in my code?

    -

    You can use the linenums class to turn on line - numbering. If your code doesn't start at line number 1, you can - add a colon and a line number to the end of that class as in - linenums:52. - -

    For example -

    <pre class="prettyprint linenums:4"
    ->// This is line 4.
    -foo();
    -bar();
    -baz();
    -boo();
    -far();
    -faz();
    -<pre>
    - produces -
    // This is line 4.
    -foo();
    -bar();
    -baz();
    -boo();
    -far();
    -faz();
    -
    - -

    How do I prevent a portion of markup from being marked as code?

    -

    You can use the nocode class to identify a span of markup - that is not code. -

    <pre class=prettyprint>
    -int x = foo();  /* This is a comment  <span class="nocode">This is not code</span>
    -  Continuation of comment */
    -int y = bar();
    -</pre>
    -produces -
    -int x = foo();  /* This is a comment  This is not code
    -  Continuation of comment */
    -int y = bar();
    -
    - -

    For a more complete example see the issue22 - testcase.

    - -

    I get an error message "a is not a function" or "opt_whenDone is not a function"

    -

    If you are calling prettyPrint via an event handler, wrap it in a function. - Instead of doing -

    - addEventListener('load', prettyPrint, false); -
    - wrap it in a closure like -
    - addEventListener('load', function (event) { prettyPrint() }, false); -
    - so that the browser does not pass an event object to prettyPrint which - will confuse it. - -


    - - - - diff --git a/docs/assets/vendor/prettify/prettify-min.css b/docs/assets/vendor/prettify/prettify-min.css deleted file mode 100644 index d44b3a228..000000000 --- a/docs/assets/vendor/prettify/prettify-min.css +++ /dev/null @@ -1 +0,0 @@ -.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} \ No newline at end of file diff --git a/docs/assets/vendor/prettify/prettify-min.js b/docs/assets/vendor/prettify/prettify-min.js deleted file mode 100644 index 4845d05d4..000000000 --- a/docs/assets/vendor/prettify/prettify-min.js +++ /dev/null @@ -1 +0,0 @@ -window.PR_SHOULD_USE_CONTINUATION=true;var prettyPrintOne;var prettyPrint;(function(){var O=window;var j=["break,continue,do,else,for,if,return,while"];var v=[j,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var q=[v,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var m=[q,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var y=[q,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var T=[y,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,let,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var,virtual,where"];var s="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes";var x=[q,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var t="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var J=[j,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var g=[j,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var I=[j,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var B=[m,T,x,t+J,g,I];var f=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/;var D="str";var A="kwd";var k="com";var Q="typ";var H="lit";var M="pun";var G="pln";var n="tag";var F="dec";var K="src";var R="atn";var o="atv";var P="nocode";var N="(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function l(ab){var af=0;var U=false;var ae=false;for(var X=0,W=ab.length;X122)){if(!(am<65||ai>90)){ah.push([Math.max(65,ai)|32,Math.min(am,90)|32])}if(!(am<97||ai>122)){ah.push([Math.max(97,ai)&~32,Math.min(am,122)&~32])}}}}ah.sort(function(aw,av){return(aw[0]-av[0])||(av[1]-aw[1])});var ak=[];var aq=[];for(var at=0;atau[0]){if(au[1]+1>au[0]){ao.push("-")}ao.push(V(au[1]))}}ao.push("]");return ao.join("")}function Y(an){var al=an.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var aj=al.length;var ap=[];for(var am=0,ao=0;am=2&&ak==="["){al[am]=Z(ai)}else{if(ak!=="\\"){al[am]=ai.replace(/[a-zA-Z]/g,function(aq){var ar=aq.charCodeAt(0);return"["+String.fromCharCode(ar&~32,ar|32)+"]"})}}}}return al.join("")}var ac=[];for(var X=0,W=ab.length;X=0;){U[ae.charAt(ag)]=aa}}var ah=aa[1];var ac=""+ah;if(!ai.hasOwnProperty(ac)){aj.push(ah);ai[ac]=null}}aj.push(/[\0-\uffff]/);X=l(aj)})();var Z=V.length;var Y=function(aj){var ab=aj.sourceCode,aa=aj.basePos;var af=[aa,G];var ah=0;var ap=ab.match(X)||[];var al={};for(var ag=0,at=ap.length;ag=5&&"lang-"===ar.substring(0,5);if(ao&&!(ak&&typeof ak[1]==="string")){ao=false;ar=K}if(!ao){al[ai]=ar}}var ad=ah;ah+=ai.length;if(!ao){af.push(aa+ad,ar)}else{var an=ak[1];var am=ai.indexOf(an);var ae=am+an.length;if(ak[2]){ae=ai.length-ak[2].length;am=ae-an.length}var au=ar.substring(5);C(aa+ad,ai.substring(0,am),Y,af);C(aa+ad+am,an,r(au,an),af);C(aa+ad+ae,ai.substring(ae),Y,af)}}aj.decorations=af};return Y}function i(V){var Y=[],U=[];if(V.tripleQuotedStrings){Y.push([D,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(V.multiLineStrings){Y.push([D,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{Y.push([D,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(V.verbatimStrings){U.push([D,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var ab=V.hashComments;if(ab){if(V.cStyleComments){if(ab>1){Y.push([k,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{Y.push([k,/^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}U.push([D,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,null])}else{Y.push([k,/^#[^\r\n]*/,null,"#"])}}if(V.cStyleComments){U.push([k,/^\/\/[^\r\n]*/,null]);U.push([k,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(V.regexLiterals){var aa=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");U.push(["lang-regex",new RegExp("^"+N+"("+aa+")")])}var X=V.types;if(X){U.push([Q,X])}var W=(""+V.keywords).replace(/^ | $/g,"");if(W.length){U.push([A,new RegExp("^(?:"+W.replace(/[\s,]+/g,"|")+")\\b"),null])}Y.push([G,/^\s+/,null," \r\n\t\xA0"]);var Z=/^.[^\s\w\.$@\'\"\`\/\\]*/;U.push([H,/^@[a-z_$][a-z_$@0-9]*/i,null],[Q,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[G,/^[a-z_$][a-z_$@0-9]*/i,null],[H,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[G,/^\\[\s\S]?/,null],[M,Z,null]);return h(Y,U)}var L=i({keywords:B,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function S(W,ah,aa){var V=/(?:^|\s)nocode(?:\s|$)/;var ac=/\r\n?|\n/;var ad=W.ownerDocument;var ag=ad.createElement("li");while(W.firstChild){ag.appendChild(W.firstChild)}var X=[ag];function af(am){switch(am.nodeType){case 1:if(V.test(am.className)){break}if("br"===am.nodeName){ae(am);if(am.parentNode){am.parentNode.removeChild(am)}}else{for(var ao=am.firstChild;ao;ao=ao.nextSibling){af(ao)}}break;case 3:case 4:if(aa){var an=am.nodeValue;var ak=an.match(ac);if(ak){var aj=an.substring(0,ak.index);am.nodeValue=aj;var ai=an.substring(ak.index+ak[0].length);if(ai){var al=am.parentNode;al.insertBefore(ad.createTextNode(ai),am.nextSibling)}ae(am);if(!aj){am.parentNode.removeChild(am)}}}break}}function ae(al){while(!al.nextSibling){al=al.parentNode;if(!al){return}}function aj(am,at){var ar=at?am.cloneNode(false):am;var ap=am.parentNode;if(ap){var aq=aj(ap,1);var ao=am.nextSibling;aq.appendChild(ar);for(var an=ao;an;an=ao){ao=an.nextSibling;aq.appendChild(an)}}return ar}var ai=aj(al.nextSibling,0);for(var ak;(ak=ai.parentNode)&&ak.nodeType===1;){ai=ak}X.push(ai)}for(var Z=0;Z=U){aj+=2}if(Y>=ar){ac+=2}}}finally{if(au){au.style.display=ak}}}var u={};function d(W,X){for(var U=X.length;--U>=0;){var V=X[U];if(!u.hasOwnProperty(V)){u[V]=W}else{if(O.console){console.warn("cannot override language handler %s",V)}}}}function r(V,U){if(!(V&&u.hasOwnProperty(V))){V=/^\s*]*(?:>|$)/],[k,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[M,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);d(h([[G,/^[\s]+/,null," \t\r\n"],[o,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[n,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[R,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[M,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);d(h([],[[o,/^[\s\S]+/]]),["uq.val"]);d(i({keywords:m,hashComments:true,cStyleComments:true,types:f}),["c","cc","cpp","cxx","cyc","m"]);d(i({keywords:"null,true,false"}),["json"]);d(i({keywords:T,hashComments:true,cStyleComments:true,verbatimStrings:true,types:f}),["cs"]);d(i({keywords:y,cStyleComments:true}),["java"]);d(i({keywords:I,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);d(i({keywords:J,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);d(i({keywords:t,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);d(i({keywords:g,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);d(i({keywords:x,cStyleComments:true,regexLiterals:true}),["js"]);d(i({keywords:s,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);d(h([],[[D,/^[\s\S]+/]]),["regex"]);function e(X){var W=X.langExtension;try{var U=b(X.sourceNode,X.pre);var V=U.sourceCode;X.sourceCode=V;X.spans=U.spans;X.basePos=0;r(W,V)(X);E(X)}catch(Y){if(O.console){console.log(Y&&Y.stack?Y.stack:Y)}}}function z(Y,X,W){var U=document.createElement("pre");U.innerHTML=Y;if(W){S(U,W,true)}var V={langExtension:X,numberLines:W,sourceNode:U,pre:1};e(V);return U.innerHTML}function c(aj){function ab(al){return document.getElementsByTagName(al)}var ah=[ab("pre"),ab("code"),ab("xmp")];var V=[];for(var ae=0;ae]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); \ No newline at end of file diff --git a/docs/classes/AABB.html b/docs/classes/AABB.html deleted file mode 100644 index 874802676..000000000 --- a/docs/classes/AABB.html +++ /dev/null @@ -1,1059 +0,0 @@ - - - - - AABB - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    AABB Class

    -
    - - -
    - Defined in: src/collision/AABB.js:6 -
    - - -
    - - -
    -

    Axis aligned bounding box class.

    - -
    - -
    -

    Constructor

    -
    -

    AABB

    - -
    - (
      -
    • - [options] -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/collision/AABB.js:6 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - [options] - Object - optional - - -
      - -
      - -
        -
      • - [upperBound] - Vec3 - optional - -
        - -
        - -
      • -
      • - [lowerBound] - Vec3 - optional - -
        - -
        - -
      • -
      -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    clone

    - - () - - - - - - - - -
    -

    - Defined in - src/collision/AABB.js:107 -

    - - - -
    - -
    -

    Clone an AABB

    - -
    - - - - -
    -
    -

    contains

    - -
    - (
      -
    • - aabb -
    • -
    ) -
    - - - Boolean - - - - - - - - -
    -

    - Defined in - src/collision/AABB.js:180 -

    - - - -
    - -
    -

    Returns true if the given AABB is fully contained in this AABB.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - aabb - AABB - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Boolean: -
    -
    - - -
    -
    -

    copy

    - -
    - (
      -
    • - aabb -
    • -
    ) -
    - - - AABB - - - - - - - - -
    -

    - Defined in - src/collision/AABB.js:95 -

    - - - -
    - -
    -

    Copy bounds from an AABB to this AABB

    - -
    - -
    -

    Parameters:

    - -
      -
    • - aabb - AABB - - -
      -

      Source to copy from

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - AABB: -

    The this object, for chainability

    - -
    -
    - - -
    -
    -

    extend

    - -
    - (
      -
    • - aabb -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/collision/AABB.js:115 -

    - - - -
    - -
    -

    Extend this AABB so that it covers the given AABB too.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - aabb - AABB - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    getCorners

    - -
    - (
      -
    • - a -
    • -
    • - b -
    • -
    • - c -
    • -
    • - d -
    • -
    • - e -
    • -
    • - f -
    • -
    • - g -
    • -
    • - h -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/collision/AABB.js:204 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - a - Vec3 - - -
      - -
      - -
    • -
    • - b - Vec3 - - -
      - -
      - -
    • -
    • - c - Vec3 - - -
      - -
      - -
    • -
    • - d - Vec3 - - -
      - -
      - -
    • -
    • - e - Vec3 - - -
      - -
      - -
    • -
    • - f - Vec3 - - -
      - -
      - -
    • -
    • - g - Vec3 - - -
      - -
      - -
    • -
    • - h - Vec3 - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    overlaps

    - -
    - (
      -
    • - aabb -
    • -
    ) -
    - - - Boolean - - - - - - - - -
    -

    - Defined in - src/collision/AABB.js:158 -

    - - - -
    - -
    -

    Returns true if the given AABB overlaps this AABB.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - aabb - AABB - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Boolean: -
    -
    - - -
    -
    -

    setFromPoints

    - -
    - (
      -
    • - points -
    • -
    • - position -
    • -
    • - quaternion -
    • -
    • - skinSize -
    • -
    ) -
    - - - AABB - - - - - - - - -
    -

    - Defined in - src/collision/AABB.js:40 -

    - - - -
    - -
    -

    Set the AABB bounds from a set of points.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - points - Array - - -
      -

      An array of Vec3's.

      - -
      - -
    • -
    • - position - Vec3 - - -
      - -
      - -
    • -
    • - quaternion - Quaternion - - -
      - -
      - -
    • -
    • - skinSize - Number - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - AABB: -

    The self object

    - -
    -
    - - -
    -
    -

    toLocalFrame

    - -
    - (
      -
    • - frame -
    • -
    • - target -
    • -
    ) -
    - - - AABB - - - - - - - - -
    -

    - Defined in - src/collision/AABB.js:240 -

    - - - -
    - -
    -

    Get the representation of an AABB in another frame.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - frame - Transform - - -
      - -
      - -
    • -
    • - target - AABB - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - AABB: -

    The "target" AABB object.

    - -
    -
    - - -
    -
    -

    toWorldFrame

    - -
    - (
      -
    • - frame -
    • -
    • - target -
    • -
    ) -
    - - - AABB - - - - - - - - -
    -

    - Defined in - src/collision/AABB.js:271 -

    - - - -
    - -
    -

    Get the representation of an AABB in the global frame.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - frame - Transform - - -
      - -
      - -
    • -
    • - target - AABB - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - AABB: -

    The "target" AABB object.

    - -
    -
    - - -
    -
    - -
    -

    Properties

    - -
    -

    lowerBound

    - Vec3 - - - - - -
    -

    - Defined in - src/collision/AABB.js:17 -

    - - -
    - -
    -

    The lower bound of the bounding box.

    - -
    - - - -
    -
    -

    upperBound

    - Vec3 - - - - - -
    -

    - Defined in - src/collision/AABB.js:27 -

    - - -
    - -
    -

    The upper bound of the bounding box.

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/ArrayCollisionMatrix.html b/docs/classes/ArrayCollisionMatrix.html deleted file mode 100644 index 0c8715e16..000000000 --- a/docs/classes/ArrayCollisionMatrix.html +++ /dev/null @@ -1,524 +0,0 @@ - - - - - ArrayCollisionMatrix - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    ArrayCollisionMatrix Class

    -
    - - - - - -
    - - -
    -

    Collision "matrix". It's actually a triangular-shaped array of whether two bodies are touching this step, for reference next step

    - -
    - -
    -

    Constructor

    -
    -

    ArrayCollisionMatrix

    - - () - - - - - - - - -
    -

    - Defined in - src/collision/ArrayCollisionMatrix.js:3 -

    - - - -
    - -
    - -
    - - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    get

    - -
    - (
      -
    • - i -
    • -
    • - j -
    • -
    ) -
    - - - Number - - - - - - - - -
    -

    - Defined in - src/collision/ArrayCollisionMatrix.js:18 -

    - - - -
    - -
    -

    Get an element

    - -
    - -
    -

    Parameters:

    - -
      -
    • - i - Number - - -
      - -
      - -
    • -
    • - j - Number - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    reset

    - - () - - - - - - - - -
    -

    - Defined in - src/collision/ArrayCollisionMatrix.js:54 -

    - - - -
    - -
    -

    Sets all elements to zero

    - -
    - - - - -
    -
    -

    set

    - -
    - (
      -
    • - i -
    • -
    • - j -
    • -
    • - value -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/collision/ArrayCollisionMatrix.js:36 -

    - - - -
    - -
    -

    Set an element

    - -
    - -
    -

    Parameters:

    - -
      -
    • - i - Number - - -
      - -
      - -
    • -
    • - j - Number - - -
      - -
      - -
    • -
    • - value - Number - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    setNumObjects

    - -
    - (
      -
    • - n -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/collision/ArrayCollisionMatrix.js:64 -

    - - - -
    - -
    -

    Sets the max number of objects

    - -
    - -
    -

    Parameters:

    - -
      -
    • - n - Number - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    -

    Properties

    - -
    -

    matrix

    - Array - - - - - -
    -

    - Defined in - src/collision/ArrayCollisionMatrix.js:10 -

    - - -
    - -
    -

    The matrix storage

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/Body.html b/docs/classes/Body.html deleted file mode 100644 index 5c3633fba..000000000 --- a/docs/classes/Body.html +++ /dev/null @@ -1,3126 +0,0 @@ - - - - - Body - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    Body Class

    -
    - -
    - Extends EventTarget -
    - -
    - Defined in: src/objects/Body.js:12 -
    - - -
    - - -
    -

    Base class for all body types.

    - -
    - -
    -

    Constructor

    -
    -

    Body

    - -
    - (
      -
    • - [options] -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/Body.js:12 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - [options] - Object - optional - - -
      - -
      - -
        -
      • - [position] - Vec3 - optional - -
        - -
        - -
      • -
      • - [velocity] - Vec3 - optional - -
        - -
        - -
      • -
      • - [angularVelocity] - Vec3 - optional - -
        - -
        - -
      • -
      • - [quaternion] - Quaternion - optional - -
        - -
        - -
      • -
      • - [mass] - Number - optional - -
        - -
        - -
      • -
      • - [material] - Material - optional - -
        - -
        - -
      • -
      • - [type] - Number - optional - -
        - -
        - -
      • -
      • - [linearDamping=0.01] - Number - optional - -
        - -
        - -
      • -
      • - [angularDamping=0.01] - Number - optional - -
        - -
        - -
      • -
      • - [allowSleep=true] - Boolean - optional - -
        - -
        - -
      • -
      • - [sleepSpeedLimit=0.1] - Number - optional - -
        - -
        - -
      • -
      • - [sleepTimeLimit=1] - Number - optional - -
        - -
        - -
      • -
      • - [collisionFilterGroup=1] - Number - optional - -
        - -
        - -
      • -
      • - [collisionFilterMask=1] - Number - optional - -
        - -
        - -
      • -
      • - [fixedRotation=false] - Boolean - optional - -
        - -
        - -
      • -
      • - [shape] - Body - optional - -
        - -
        - -
      • -
      -
    • -
    -
    - - - -
    -

    Example:

    - -
    -
    var body = new Body({
    -            mass: 1
    -        });
    -        var shape = new Sphere(1);
    -        body.addShape(shape);
    -        world.add(body);
    -        
    -
    -
    -
    -
    - -
    - - -
    -
    -

    Item Index

    - - - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    addEventListener

    - -
    - (
      -
    • - type -
    • -
    • - listener -
    • -
    ) -
    - - - EventTarget - - - - - - - - -
    -

    Inherited from - EventTarget: - src/utils/EventTarget.js:15 -

    - - - -
    - -
    -

    Add an event listener

    - -
    - -
    -

    Parameters:

    - -
      -
    • - type - String - - -
      - -
      - -
    • -
    • - listener - Function - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - EventTarget: -

    The self object, for chainability.

    - -
    -
    - - -
    -
    -

    addShape

    - -
    - (
      -
    • - shape -
    • -
    • - offset -
    • -
    • - quaternion -
    • -
    ) -
    - - - Body - - - - - - - - -
    -

    - Defined in - src/objects/Body.js:507 -

    - - - -
    - -
    -

    Add a shape to the body with a local offset and orientation.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - shape - Shape - - -
      - -
      - -
    • -
    • - offset - Vec3 - - -
      - -
      - -
    • -
    • - quaternion - Quaternion - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Body: -

    The body object, for chainability.

    - -
    -
    - - -
    -
    -

    applyForce

    - -
    - (
      -
    • - force -
    • -
    • - worldPoint -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/Body.js:637 -

    - - - -
    - -
    -

    Apply force to a world point. This could for example be a point on the Body surface. Applying force this way will add to Body.force and Body.torque.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - force - Vec3 - - -
      -

      The amount of force to add.

      - -
      - -
    • -
    • - worldPoint - Vec3 - - -
      -

      A world point to apply the force on.

      - -
      - -
    • -
    -
    - - - -
    -
    -

    applyImpulse

    - -
    - (
      -
    • - impulse -
    • -
    • - worldPoint -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/Body.js:688 -

    - - - -
    - -
    -

    Apply impulse to a world point. This could for example be a point on the Body surface. An impulse is a force added to a body during a short period of time (impulse = force * time). Impulses will be added to Body.velocity and Body.angularVelocity.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - impulse - Vec3 - - -
      -

      The amount of impulse to add.

      - -
      - -
    • -
    • - worldPoint - Vec3 - - -
      -

      A world point to apply the force on.

      - -
      - -
    • -
    -
    - - - -
    -
    -

    applyLocalForce

    - -
    - (
      -
    • - force -
    • -
    • - localPoint -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/Body.js:665 -

    - - - -
    - -
    -

    Apply force to a local point in the body.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - force - Vec3 - - -
      -

      The force vector to apply, defined locally in the body frame.

      - -
      - -
    • -
    • - localPoint - Vec3 - - -
      -

      A local point in the body to apply the force on.

      - -
      - -
    • -
    -
    - - - -
    -
    -

    applyLocalImpulse

    - -
    - (
      -
    • - force -
    • -
    • - localPoint -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/Body.js:729 -

    - - - -
    - -
    -

    Apply locally-defined impulse to a local point in the body.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - force - Vec3 - - -
      -

      The force vector to apply, defined locally in the body frame.

      - -
      - -
    • -
    • - localPoint - Vec3 - - -
      -

      A local point in the body to apply the force on.

      - -
      - -
    • -
    -
    - - - -
    -
    -

    computeAABB

    - - () - - - - - - - - -
    -

    - Defined in - src/objects/Body.js:562 -

    - - - -
    - -
    -

    Updates the .aabb

    - -
    - - - - -
    -
    -

    dispatchEvent

    - -
    - (
      -
    • - event -
    • -
    ) -
    - - - EventTarget - - - - - - - - -
    -

    Inherited from - EventTarget: - src/utils/EventTarget.js:68 -

    - - - -
    - -
    -

    Emit an event.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - event - Object - - -
      - -
      - -
        -
      • - type - String - -
        - -
        - -
      • -
      -
    • -
    -
    - -
    -

    Returns:

    - -
    - EventTarget: -

    The self object, for chainability.

    - -
    -
    - - -
    -
    -

    getVelocityAtWorldPoint

    - -
    - (
      -
    • - worldPoint -
    • -
    • - result -
    • -
    ) -
    - - - Vec3 - - - - - - - - -
    -

    - Defined in - src/objects/Body.js:782 -

    - - - -
    - -
    -

    Get world velocity of a point in the body.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - worldPoint - Vec3 - - -
      - -
      - -
    • -
    • - result - Vec3 - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Vec3: -

    The result vector.

    - -
    -
    - - -
    -
    -

    hasEventListener

    - -
    - (
      -
    • - type -
    • -
    • - listener -
    • -
    ) -
    - - - Boolean - - - - - - - - -
    -

    Inherited from - EventTarget: - src/utils/EventTarget.js:34 -

    - - - -
    - -
    -

    Check if an event listener is added

    - -
    - -
    -

    Parameters:

    - -
      -
    • - type - String - - -
      - -
      - -
    • -
    • - listener - Function - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Boolean: -
    -
    - - -
    -
    -

    pointToLocalFrame

    - -
    - (
      -
    • - worldPoint -
    • -
    • - result -
    • -
    ) -
    - - - Vec3 - - - - - - - - -
    -

    - Defined in - src/objects/Body.js:450 -

    - - - -
    - -
    -

    Convert a world point to local body frame.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - worldPoint - Vec3 - - -
      - -
      - -
    • -
    • - result - Vec3 - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Vec3: -
    -
    - - -
    -
    -

    pointToWorldFrame

    - -
    - (
      -
    • - localPoint -
    • -
    • - result -
    • -
    ) -
    - - - Vec3 - - - - - - - - -
    -

    - Defined in - src/objects/Body.js:477 -

    - - - -
    - -
    -

    Convert a local body point to world frame.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - localPoint - Vec3 - - -
      - -
      - -
    • -
    • - result - Vec3 - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Vec3: -
    -
    - - -
    -
    -

    removeEventListener

    - -
    - (
      -
    • - type -
    • -
    • - listener -
    • -
    ) -
    - - - EventTarget - - - - - - - - -
    -

    Inherited from - EventTarget: - src/utils/EventTarget.js:50 -

    - - - -
    - -
    -

    Remove an event listener

    - -
    - -
    -

    Parameters:

    - -
      -
    • - type - String - - -
      - -
      - -
    • -
    • - listener - Function - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - EventTarget: -

    The self object, for chainability.

    - -
    -
    - - -
    -
    -

    sleep

    - - () - - - - - - - - -
    -

    - Defined in - src/objects/Body.js:393 -

    - - - -
    - -
    -

    Force body sleep

    - -
    - - - - -
    -
    -

    sleepTick

    - -
    - (
      -
    • - time -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/Body.js:411 -

    - - - -
    - -
    -

    Called every timestep to update internal sleep timer and change sleep state if needed.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - time - Number - - -
      -

      The world time in seconds

      - -
      - -
    • -
    -
    - - - -
    -
    -

    updateBoundingRadius

    - - () - - - - - - - - -
    -

    - Defined in - src/objects/Body.js:537 -

    - - - -
    - -
    -

    Update the bounding radius of the body. Should be done if any of the shapes are changed.

    - -
    - - - - -
    -
    -

    updateInertiaWorld

    - - () - - - - - - - - -
    -

    - Defined in - src/objects/Body.js:608 -

    - - - -
    - -
    -

    Update .inertiaWorld and .invInertiaWorld

    - -
    - - - - -
    -
    -

    updateMassProperties

    - - () - - - - - - - - -
    -

    - Defined in - src/objects/Body.js:754 -

    - - - -
    - -
    -

    Should be called whenever you change the body shape or mass.

    - -
    - - - - -
    -
    -

    updateSolveMassProperties

    - - () - - - - - - - - -
    -

    - Defined in - src/objects/Body.js:434 -

    - - - -
    - -
    -

    If the body is sleeping, it should be immovable / have infinite mass during solve. We solve it by having a separate "solve mass".

    - -
    - - - - -
    -
    -

    vectorToLocalFrame

    - -
    - (
      -
    • - worldPoint -
    • -
    • - result -
    • -
    ) -
    - - - Vec3 - - - - - - - - -
    -

    - Defined in - src/objects/Body.js:464 -

    - - - -
    - -
    -

    Convert a world vector to local body frame.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - worldPoint - Vec3 - - -
      - -
      - -
    • -
    • - result - Vec3 - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Vec3: -
    -
    - - -
    -
    -

    vectorToWorldFrame

    - -
    - (
      -
    • - localVector -
    • -
    • - result -
    • -
    ) -
    - - - Vec3 - - - - - - - - -
    -

    - Defined in - src/objects/Body.js:491 -

    - - - -
    - -
    -

    Convert a local body point to world frame.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - localVector - Vec3 - - -
      - -
      - -
    • -
    • - result - Vec3 - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Vec3: -
    -
    - - -
    -
    -

    wakeUp

    - - () - - - - - - - - -
    -

    - Defined in - src/objects/Body.js:381 -

    - - - -
    - -
    -

    Wake the body up.

    - -
    - - - - -
    -
    - -
    -

    Properties

    - -
    -

    aabb

    - AABB - - - - - -
    -

    - Defined in - src/objects/Body.js:308 -

    - - -
    - -
    - -
    - - - -
    -
    -

    aabbNeedsUpdate

    - Boolean - - - - - -
    -

    - Defined in - src/objects/Body.js:314 -

    - - -
    - -
    -

    Indicates if the AABB needs to be updated before use.

    - -
    - - - -
    -
    -

    allowSleep

    - Boolean - - - - - -
    -

    - Defined in - src/objects/Body.js:172 -

    - - -
    - -
    -

    If true, the body will automatically fall to sleep.

    - -
    - -

    Default: true

    - - -
    -
    -

    angularDamping

    - Number - - - - - -
    -

    - Defined in - src/objects/Body.js:303 -

    - - -
    - -
    - -
    - - - -
    -
    -

    angularVelocity

    - Vec3 - - - - - -
    -

    - Defined in - src/objects/Body.js:231 -

    - - -
    - -
    - -
    - - - -
    -
    -

    AWAKE

    - Number - - - - - static - -
    -

    - Defined in - src/objects/Body.js:358 -

    - - -
    - -
    - -
    - - - -
    -
    -

    collisionFilterGroup

    - Number - - - - - -
    -

    - Defined in - src/objects/Body.js:74 -

    - - -
    - -
    - -
    - - - -
    -
    -

    collisionFilterMask

    - Number - - - - - -
    -

    - Defined in - src/objects/Body.js:79 -

    - - -
    - -
    - -
    - - - -
    -
    -

    collisionResponse

    - Number - - - - - -
    -

    - Defined in - src/objects/Body.js:84 -

    - - -
    - -
    -

    Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled.

    - -
    - - - -
    -
    -

    DYNAMIC

    - Number - - - - - static - -
    -

    - Defined in - src/objects/Body.js:332 -

    - - -
    - -
    -

    A dynamic body is fully simulated. Can be moved manually by the user, but normally they move according to forces. A dynamic body can collide with all body types. A dynamic body always has finite, non-zero mass.

    - -
    - - - -
    -
    -

    fixedRotation

    - Boolean - - - - - -
    -

    - Defined in - src/objects/Body.js:296 -

    - - -
    - -
    -

    Set to true if you don't want the body to rotate. Make sure to run .updateMassProperties() after changing this.

    - -
    - -

    Default: false

    - - -
    -
    -

    force

    - Vec3 - - - - - -
    -

    - Defined in - src/objects/Body.js:128 -

    - - -
    - -
    -

    Linear force on the body

    - -
    - - - -
    -
    -

    inertia

    - Vec3 - - - - - -
    -

    - Defined in - src/objects/Body.js:268 -

    - - -
    - -
    - -
    - - - -
    -
    -

    initAngularVelocity

    - Vec3 - - - - - -
    -

    - Defined in - src/objects/Body.js:241 -

    - - -
    - -
    - -
    - - - -
    -
    -

    initPosition

    - Vec3 - - - - - -
    -

    - Defined in - src/objects/Body.js:105 -

    - - -
    - -
    -

    Initial position of the body

    - -
    - - - -
    -
    -

    initQuaternion

    - Quaternion - - - - - -
    -

    - Defined in - src/objects/Body.js:225 -

    - - -
    - -
    - -
    - - - -
    -
    -

    initVelocity

    - Vec3 - - - - - -
    -

    - Defined in - src/objects/Body.js:122 -

    - - -
    - -
    - -
    - - - -
    -
    -

    invInertia

    - Vec3 - - - - - -
    -

    - Defined in - src/objects/Body.js:274 -

    - - -
    - -
    - -
    - - - -
    -
    -

    invInertiaSolve

    - Vec3 - - - - - -
    -

    - Defined in - src/objects/Body.js:286 -

    - - -
    - -
    - -
    - - - -
    -
    -

    invInertiaWorld

    - Mat3 - - - - - -
    -

    - Defined in - src/objects/Body.js:279 -

    - - -
    - -
    - -
    - - - -
    -
    -

    invInertiaWorldSolve

    - Mat3 - - - - - -
    -

    - Defined in - src/objects/Body.js:291 -

    - - -
    - -
    - -
    - - - -
    -
    -

    invMass

    - Number - - - - - -
    -

    - Defined in - src/objects/Body.js:144 -

    - - -
    - -
    - -
    - - - -
    -
    -

    KINEMATIC

    - Number - - - - - static - -
    -

    - Defined in - src/objects/Body.js:348 -

    - - -
    - -
    -

    A kinematic body moves under simulation according to its velocity. They do not respond to forces. They can be moved manually, but normally a kinematic body is moved by setting its velocity. A kinematic body behaves as if it has infinite mass. Kinematic bodies do not collide with other static or kinematic bodies.

    - -
    - - - -
    -
    -

    linearDamping

    - Number - - - - - -
    -

    - Defined in - src/objects/Body.js:156 -

    - - -
    - -
    - -
    - - - -
    -
    -

    mass

    - Number - - - - - -
    -

    - Defined in - src/objects/Body.js:137 -

    - - -
    - -
    - -
    - -

    Default: 0

    - - -
    -
    -

    material

    - Material - - - - - -
    -

    - Defined in - src/objects/Body.js:150 -

    - - -
    - -
    - -
    - - - -
    -
    -

    position

    - Vec3 - - - - - -
    -

    - Defined in - src/objects/Body.js:90 -

    - - -
    - -
    - -
    - - - -
    -
    -

    postStep

    - Function - - deprecated - - - - -
    -

    - Defined in - src/objects/Body.js:64 -

    - -

    Deprecated: Use World events instead

    - -
    - -
    -

    Callback function that is used AFTER stepping the system. Inside the function, "this" will refer to this Body object.

    - -
    - - - -
    -
    -

    preStep

    - Function - - deprecated - - - - -
    -

    - Defined in - src/objects/Body.js:56 -

    - -

    Deprecated: Use World events instead

    - -
    - -
    -

    Callback function that is used BEFORE stepping the system. Use it to apply forces, for example. Inside the function, "this" will refer to this Body object.

    - -
    - - - -
    -
    -

    previousPosition

    - Vec3 - - - - - -
    -

    - Defined in - src/objects/Body.js:100 -

    - - -
    - -
    - -
    - - - -
    -
    -

    quaternion

    - Quaternion - - - - - -
    -

    - Defined in - src/objects/Body.js:214 -

    - - -
    - -
    -

    Orientation of the body

    - -
    - - - -
    -
    -

    shapeOffsets

    - Array - - - - - -
    -

    - Defined in - src/objects/Body.js:256 -

    - - -
    - -
    - -
    - - - -
    -
    -

    shapeOrientations

    - Array - - - - - -
    -

    - Defined in - src/objects/Body.js:262 -

    - - -
    - -
    - -
    - - - -
    -
    -

    shapes

    - Array - - - - - -
    -

    - Defined in - src/objects/Body.js:250 -

    - - -
    - -
    - -
    - - - -
    -
    -

    SLEEPING

    - Number - - - - - static - -
    -

    - Defined in - src/objects/Body.js:372 -

    - - -
    - -
    - -
    - - - -
    -
    -

    sleepSpeedLimit

    - Number - - - - - -
    -

    - Defined in - src/objects/Body.js:187 -

    - - -
    - -
    -

    If the speed (the norm of the velocity) is smaller than this value, the body is considered sleepy.

    - -
    - -

    Default: 0.1

    - - -
    -
    -

    sleepState

    - Number - - - - - -
    -

    - Defined in - src/objects/Body.js:180 -

    - - -
    - -
    -

    Current sleep state.

    - -
    - - - -
    -
    -

    sleepTimeLimit

    - Number - - - - - -
    -

    - Defined in - src/objects/Body.js:195 -

    - - -
    - -
    -

    If the body has been sleepy for this sleepTimeLimit seconds, it is considered sleeping.

    - -
    - -

    Default: 1

    - - -
    -
    -

    SLEEPY

    - Number - - - - - static - -
    -

    - Defined in - src/objects/Body.js:365 -

    - - -
    - -
    - -
    - - - -
    -
    -

    STATIC

    - Number - - - - - static - -
    -

    - Defined in - src/objects/Body.js:340 -

    - - -
    - -
    -

    A static body does not move during simulation and behaves as if it has infinite mass. Static bodies can be moved manually by setting the position of the body. The velocity of a static body is always zero. Static bodies do not collide with other static or kinematic bodies.

    - -
    - - - -
    -
    -

    torque

    - Vec3 - - - - - -
    -

    - Defined in - src/objects/Body.js:208 -

    - - -
    - -
    -

    Rotational force on the body, around center of mass

    - -
    - - - -
    -
    -

    type

    - Number - - - - - -
    -

    - Defined in - src/objects/Body.js:162 -

    - - -
    - -
    -

    One of: Body.DYNAMIC, Body.STATIC and Body.KINEMATIC.

    - -
    - - - -
    -
    -

    velocity

    - Vec3 - - - - - -
    -

    - Defined in - src/objects/Body.js:112 -

    - - -
    - -
    - -
    - - - -
    -
    -

    world

    - World - - - - - -
    -

    - Defined in - src/objects/Body.js:49 -

    - - -
    - -
    -

    Reference to the world the body is living in

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/Box.html b/docs/classes/Box.html deleted file mode 100644 index 2452db7a1..000000000 --- a/docs/classes/Box.html +++ /dev/null @@ -1,755 +0,0 @@ - - - - - Box - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    Box Class

    -
    - -
    - Extends Shape -
    - -
    - Defined in: src/shapes/Box.js:7 -
    - - -
    - - -
    -

    A 3d box shape.

    - -
    - -
    -

    Constructor

    -
    -

    Box

    - -
    - (
      -
    • - halfExtents -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/shapes/Box.js:7 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - halfExtents - Vec3 - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - - - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    calculateLocalInertia

    - -
    - (
      -
    • - mass -
    • -
    • - target -
    • -
    ) -
    - - - Vec3 - - - - - - - - -
    -

    Inherited from - - Shape - - but overwritten in - src/shapes/Box.js:80 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - mass - Number - - -
      - -
      - -
    • -
    • - target - Vec3 - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Vec3: -
    -
    - - -
    -
    -

    getSideNormals

    - -
    - (
      -
    • - sixTargetVectors -
    • -
    • - quat -
    • -
    ) -
    - - - Array - - - - - - - - -
    -

    - Defined in - src/shapes/Box.js:99 -

    - - - -
    - -
    -

    Get the box 6 side normals

    - -
    - -
    -

    Parameters:

    - -
      -
    • - sixTargetVectors - Array - - -
      -

      An array of 6 vectors, to store the resulting side normals in.

      - -
      - -
    • -
    • - quat - Quaternion - - -
      -

      Orientation to apply to the normal vectors. If not provided, the vectors will be in respect to the local frame.

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Array: -
    -
    - - -
    -
    -

    updateBoundingSphereRadius

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:50 -

    - - - -
    - -
    -

    Computes the bounding sphere radius. The result is stored in the property .boundingSphereRadius

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    updateConvexPolyhedronRepresentation

    - - () - - - - - - - - -
    -

    - Defined in - src/shapes/Box.js:39 -

    - - - -
    - -
    -

    Updates the local convex polyhedron representation used for some collisions.

    - -
    - - - - -
    -
    -

    volume

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:59 -

    - - - -
    - -
    -

    Get the volume of this shape

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    - -
    -

    Properties

    - -
    -

    boundingSphereRadius

    - Number - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:31 -

    - - -
    - -
    -

    The local bounding sphere radius of this shape.

    - -
    - - - -
    -
    -

    collisionResponse

    - Boolean - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:37 -

    - - -
    - -
    -

    Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled.

    - -
    - - - -
    -
    -

    convexPolyhedronRepresentation

    - ConvexPolyhedron - - - - - -
    -

    - Defined in - src/shapes/Box.js:26 -

    - - -
    - -
    -

    Used by the contact generator to make contacts with other convex polyhedra for example

    - -
    - - - -
    -
    -

    halfExtents

    - Vec3 - - - - - -
    -

    - Defined in - src/shapes/Box.js:20 -

    - - -
    - -
    - -
    - - - -
    -
    -

    id

    - Number - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:17 -

    - - -
    - -
    -

    Identifyer of the Shape.

    - -
    - - - -
    -
    -

    material

    - Material - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:43 -

    - - -
    - -
    - -
    - - - -
    -
    -

    type

    - Number - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:23 -

    - - -
    - -
    -

    The type of this shape. Must be set to an int > 0 by subclasses.

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/Broadphase.html b/docs/classes/Broadphase.html deleted file mode 100644 index 0df398b70..000000000 --- a/docs/classes/Broadphase.html +++ /dev/null @@ -1,1084 +0,0 @@ - - - - - Broadphase - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    Broadphase Class

    -
    - - - - - -
    - - -
    -

    Base class for broadphase implementations

    - -
    - -
    -

    Constructor

    -
    -

    Broadphase

    - - () - - - - - - - - -
    -

    - Defined in - src/collision/Broadphase.js:9 -

    - - - -
    - -
    - -
    - - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - - - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    aabbQuery

    - -
    - (
      -
    • - world -
    • -
    • - aabb -
    • -
    • - result -
    • -
    ) -
    - - - Array - - - - - - - - -
    -

    - Defined in - src/collision/Broadphase.js:197 -

    - - - -
    - -
    -

    Returns all the bodies within the AABB.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - world - World - - -
      - -
      - -
    • -
    • - aabb - AABB - - -
      - -
      - -
    • -
    • - result - Array - - -
      -

      An array to store resulting bodies in.

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Array: -
    -
    - - -
    -
    -

    boundingSphereCheck

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    ) -
    - - - Boolean - - - - - - - - -
    -

    - Defined in - src/collision/Broadphase.js:183 -

    - - - -
    - -
    -

    Check if the bounding spheres of two bodies overlap.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Boolean: -
    -
    - - -
    -
    -

    collisionPairs

    - -
    - (
      -
    • - world -
    • -
    • - p1 -
    • -
    • - p2 -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/collision/Broadphase.js:37 -

    - - - -
    - -
    -

    Get the collision pairs from the world

    - -
    - -
    -

    Parameters:

    - -
      -
    • - world - World - - -
      -

      The world to search in

      - -
      - -
    • -
    • - p1 - Array - - -
      -

      Empty array to be filled with body objects

      - -
      - -
    • -
    • - p2 - Array - - -
      -

      Empty array to be filled with body objects

      - -
      - -
    • -
    -
    - - - -
    -
    -

    doBoundingBoxBroadphase

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    • - pairs1 -
    • -
    • - pairs2 -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/collision/Broadphase.js:112 -

    - - - -
    - -
    -

    Check if the bounding boxes of two bodies are intersecting.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    • - pairs1 - Array - - -
      - -
      - -
    • -
    • - pairs2 - Array - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    doBoundingSphereBroadphase

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    • - pairs1 -
    • -
    • - pairs2 -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/collision/Broadphase.js:89 -

    - - - -
    - -
    -

    Check if the bounding spheres of two bodies are intersecting.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    • - pairs1 - Array - - -
      -

      bodyA is appended to this array if intersection

      - -
      - -
    • -
    • - pairs2 - Array - - -
      -

      bodyB is appended to this array if intersection

      - -
      - -
    • -
    -
    - - - -
    -
    -

    intersectionTest

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    • - pairs1 -
    • -
    • - pairs2 -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/collision/Broadphase.js:73 -

    - - - -
    - -
    -

    Check if the bounding volumes of two bodies intersect.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    • - pairs1 - Array - - -
      - -
      - -
    • -
    • - pairs2 - Array - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    makePairsUnique

    - -
    - (
      -
    • - pairs1 -
    • -
    • - pairs2 -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/collision/Broadphase.js:135 -

    - - - -
    - -
    -

    Removes duplicate pairs from the pair arrays.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - pairs1 - Array - - -
      - -
      - -
    • -
    • - pairs2 - Array - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    needBroadphaseCollision

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    ) -
    - - - Bool - - - - - - - - -
    -

    - Defined in - src/collision/Broadphase.js:48 -

    - - - -
    - -
    -

    Check if a body pair needs to be intersection tested at all.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Bool: -
    -
    - - -
    -
    -

    setWorld

    - -
    - (
      -
    • - world -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/collision/Broadphase.js:175 -

    - - - -
    - -
    -

    To be implemented by subcasses

    - -
    - -
    -

    Parameters:

    - -
      -
    • - world - World - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    -

    Properties

    - -
    -

    dirty

    - Boolean - - - - - -
    -

    - Defined in - src/collision/Broadphase.js:30 -

    - - -
    - -
    -

    Set to true if the objects in the world moved.

    - -
    - - - -
    -
    -

    useBoundingBoxes

    - Boolean - - - - - -
    -

    - Defined in - src/collision/Broadphase.js:23 -

    - - -
    - -
    -

    If set to true, the broadphase uses bounding boxes for intersection test, else it uses bounding spheres.

    - -
    - - - -
    -
    -

    world

    - World - - - - - -
    -

    - Defined in - src/collision/Broadphase.js:16 -

    - - -
    - -
    -

    The world to search for collisions in.

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/ConeEquation.html b/docs/classes/ConeEquation.html deleted file mode 100644 index d2f07a3c8..000000000 --- a/docs/classes/ConeEquation.html +++ /dev/null @@ -1,1079 +0,0 @@ - - - - - ConeEquation - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    ConeEquation Class

    -
    - -
    - Extends Equation -
    - - - - -
    - - -
    -

    Cone equation. Works to keep the given body world vectors aligned, or tilted within a given angle from each other.

    - -
    - -
    -

    Constructor

    -
    -

    ConeEquation

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    • - [options.axisA] -
    • -
    • - [options.axisB] -
    • -
    • - [options.angle] -
    • -
    • - [options.maxForce=1e6] -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/equations/ConeEquation.js:7 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    • - [options.axisA] - Vec3 - optional - - -
      -

      Local axis in A

      - -
      - -
    • -
    • - [options.axisB] - Vec3 - optional - - -
      -

      Local axis in B

      - -
      - -
    • -
    • - [options.angle] - Vec3 - optional - - -
      -

      The "cone angle" to keep

      - -
      - -
    • -
    • - [options.maxForce=1e6] - Number - optional - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    addToWlambda

    - -
    - (
      -
    • - deltalambda -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:228 -

    - - - -
    - -
    -

    Add constraint velocity to the bodies.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - deltalambda - Number - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    computeB

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:95 -

    - - - -
    - -
    -

    Computes the RHS of the SPOOK equation

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGiMf

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:159 -

    - - - -
    - -
    -

    Computes Ginv(M)f, where M is the mass matrix with diagonal blocks for each body, and f are the forces on the bodies.

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGiMGt

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:191 -

    - - - -
    - -
    -

    Computes Ginv(M)G'

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGq

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:107 -

    - - - -
    - -
    -

    Computes G*q, where q are the generalized body coordinates

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGW

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:124 -

    - - - -
    - -
    -

    Computes G*W, where W are the body velocities

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGWlambda

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:142 -

    - - - -
    - -
    -

    Computes G*Wlambda, where W are the body velocities

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeInvC

    - -
    - (
      -
    • - eps -
    • -
    ) -
    - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:262 -

    - - - -
    - -
    -

    Compute the denominator part of the SPOOK equation: C = Ginv(M)G' + eps

    - -
    - -
    -

    Parameters:

    - -
      -
    • - eps - Number - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    setSpookParams

    - - () - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:82 -

    - - - -
    - -
    -

    Recalculates a,b,eps.

    - -
    - - - - -
    -
    - -
    -

    Properties

    - -
    -

    a

    - Number - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:41 -

    - - -
    - -
    -

    SPOOK parameter

    - -
    - - - -
    -
    -

    angle

    - Number - - - - - -
    -

    - Defined in - src/equations/ConeEquation.js:29 -

    - - -
    - -
    -

    The cone angle to keep

    - -
    - - - -
    -
    -

    b

    - Number - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:47 -

    - - -
    - -
    -

    SPOOK parameter

    - -
    - - - -
    -
    -

    bi

    - Body - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:29 -

    - - -
    - -
    - -
    - - - -
    -
    -

    bj

    - Body - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:35 -

    - - -
    - -
    - -
    - - - -
    -
    -

    enabled

    - Boolean - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:69 -

    - - -
    - -
    - -
    - -

    Default: true

    - - -
    -
    -

    eps

    - Number - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:53 -

    - - -
    - -
    -

    SPOOK parameter

    - -
    - - - -
    -
    -

    jacobianElementA

    - JacobianElement - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:59 -

    - - -
    - -
    - -
    - - - -
    -
    -

    jacobianElementB

    - JacobianElement - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:64 -

    - - -
    - -
    - -
    - - - -
    -
    -

    maxForce

    - Number - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:24 -

    - - -
    - -
    - -
    - - - -
    -
    -

    minForce

    - Number - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:19 -

    - - -
    - -
    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/ConeTwistConstraint.html b/docs/classes/ConeTwistConstraint.html deleted file mode 100644 index b4bfe32bf..000000000 --- a/docs/classes/ConeTwistConstraint.html +++ /dev/null @@ -1,788 +0,0 @@ - - - - - ConeTwistConstraint - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    ConeTwistConstraint Class

    -
    - - - - - - -
    - - -
    - -
    - -
    -

    Constructor

    -
    -

    ConeTwistConstraint

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    • - [options] -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/constraints/ConeTwistConstraint.js:10 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    • - [options] - Object - optional - - -
      - -
      - -
        -
      • - [pivotA] - Vec3 - optional - -
        - -
        - -
      • -
      • - [pivotB] - Vec3 - optional - -
        - -
        - -
      • -
      • - [axisA] - Vec3 - optional - -
        - -
        - -
      • -
      • - [axisB] - Vec3 - optional - -
        - -
        - -
      • -
      • - [maxForce=1e6] - Number - optional - -
        - -
        - -
      • -
      -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    disable

    - - () - - - - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:80 -

    - - - -
    - -
    -

    Disables all equations in the constraint.

    - -
    - - - - -
    -
    -

    enable

    - - () - - - - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:69 -

    - - - -
    - -
    -

    Enables all equations in the constraint.

    - -
    - - - - -
    -
    -

    update

    - - () - - - - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:61 -

    - - - -
    - -
    -

    Update all the equations with data.

    - -
    - - - - -
    -
    - -
    -

    Properties

    - -
    -

    bodyA

    - Body - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:29 -

    - - -
    - -
    - -
    - - - -
    -
    -

    bodyB

    - Body - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:34 -

    - - -
    - -
    - -
    - - - -
    -
    -

    collideConnected

    - Boolean - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:44 -

    - - -
    - -
    -

    Set to true if you want the bodies to collide when they are connected.

    - -
    - - - -
    -
    -

    coneEquation

    - ConeEquation - - - - - - - -
    - -
    - - - -
    -
    -

    equations

    - Array - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:22 -

    - - -
    - -
    -

    Equations to be solved in this constraint

    - -
    - - - -
    -
    -

    equationX

    - ContactEquation - - - - - - - -
    - -
    - - - -
    -
    -

    equationY

    - ContactEquation - - - - - - - -
    - -
    - - - -
    -
    -

    equationZ

    - ContactEquation - - - - - - - -
    - -
    - - - -
    -
    -

    id

    - Number - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:39 -

    - - -
    - -
    - -
    - - - -
    -
    -

    pivotA

    - Vec3 - - - - - - - -
    -

    Pivot, defined locally in bodyA.

    - -
    - - - -
    -
    -

    pivotB

    - Vec3 - - - - - - - -
    -

    Pivot, defined locally in bodyB.

    - -
    - - - -
    -
    -

    twistEquation

    - RotationalEquation - - - - - - - -
    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/Constraint.html b/docs/classes/Constraint.html deleted file mode 100644 index f835f2c1f..000000000 --- a/docs/classes/Constraint.html +++ /dev/null @@ -1,558 +0,0 @@ - - - - - Constraint - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    Constraint Class

    -
    - - - - - -
    - - -
    -

    Constraint base class

    - -
    - -
    -

    Constructor

    -
    -

    Constraint

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    • - [options] -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/constraints/Constraint.js:5 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    • - [options] - Object - optional - - -
      - -
      - -
        -
      • - [collideConnected=true] - Boolean - optional - -
        - -
        - -
      • -
      • - [wakeUpBodies=true] - Boolean - optional - -
        - -
        - -
      • -
      -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    disable

    - - () - - - - - - - - -
    -

    - Defined in - src/constraints/Constraint.js:80 -

    - - - -
    - -
    -

    Disables all equations in the constraint.

    - -
    - - - - -
    -
    -

    enable

    - - () - - - - - - - - -
    -

    - Defined in - src/constraints/Constraint.js:69 -

    - - - -
    - -
    -

    Enables all equations in the constraint.

    - -
    - - - - -
    -
    -

    update

    - - () - - - - - - - - -
    -

    - Defined in - src/constraints/Constraint.js:61 -

    - - - -
    - -
    -

    Update all the equations with data.

    - -
    - - - - -
    -
    - -
    -

    Properties

    - -
    -

    bodyA

    - Body - - - - - -
    -

    - Defined in - src/constraints/Constraint.js:29 -

    - - -
    - -
    - -
    - - - -
    -
    -

    bodyB

    - Body - - - - - -
    -

    - Defined in - src/constraints/Constraint.js:34 -

    - - -
    - -
    - -
    - - - -
    -
    -

    collideConnected

    - Boolean - - - - - -
    -

    - Defined in - src/constraints/Constraint.js:44 -

    - - -
    - -
    -

    Set to true if you want the bodies to collide when they are connected.

    - -
    - - - -
    -
    -

    equations

    - Array - - - - - -
    -

    - Defined in - src/constraints/Constraint.js:22 -

    - - -
    - -
    -

    Equations to be solved in this constraint

    - -
    - - - -
    -
    -

    id

    - Number - - - - - -
    -

    - Defined in - src/constraints/Constraint.js:39 -

    - - -
    - -
    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/ContactEquation.html b/docs/classes/ContactEquation.html deleted file mode 100644 index f2abf9b9d..000000000 --- a/docs/classes/ContactEquation.html +++ /dev/null @@ -1,1151 +0,0 @@ - - - - - ContactEquation - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    ContactEquation Class

    -
    - -
    - Extends Equation -
    - - - - -
    - - -
    -

    Contact/non-penetration constraint equation

    - -
    - -
    -

    Constructor

    -
    -

    ContactEquation

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/equations/ContactEquation.js:7 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    addToWlambda

    - -
    - (
      -
    • - deltalambda -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:228 -

    - - - -
    - -
    -

    Add constraint velocity to the bodies.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - deltalambda - Number - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    computeB

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:95 -

    - - - -
    - -
    -

    Computes the RHS of the SPOOK equation

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGiMf

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:159 -

    - - - -
    - -
    -

    Computes Ginv(M)f, where M is the mass matrix with diagonal blocks for each body, and f are the forces on the bodies.

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGiMGt

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:191 -

    - - - -
    - -
    -

    Computes Ginv(M)G'

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGq

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:107 -

    - - - -
    - -
    -

    Computes G*q, where q are the generalized body coordinates

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGW

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:124 -

    - - - -
    - -
    -

    Computes G*W, where W are the body velocities

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGWlambda

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:142 -

    - - - -
    - -
    -

    Computes G*Wlambda, where W are the body velocities

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeInvC

    - -
    - (
      -
    • - eps -
    • -
    ) -
    - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:262 -

    - - - -
    - -
    -

    Compute the denominator part of the SPOOK equation: C = Ginv(M)G' + eps

    - -
    - -
    -

    Parameters:

    - -
      -
    • - eps - Number - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    getImpactVelocityAlongNormal

    - - () - - - Number - - - - - - - - -
    -

    - Defined in - src/equations/ContactEquation.js:113 -

    - - - -
    - -
    -

    Get the current relative velocity in the contact point.

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    setSpookParams

    - - () - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:82 -

    - - - -
    - -
    -

    Recalculates a,b,eps.

    - -
    - - - - -
    -
    - -
    -

    Properties

    - -
    -

    a

    - Number - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:41 -

    - - -
    - -
    -

    SPOOK parameter

    - -
    - - - -
    -
    -

    b

    - Number - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:47 -

    - - -
    - -
    -

    SPOOK parameter

    - -
    - - - -
    -
    -

    bi

    - Body - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:29 -

    - - -
    - -
    - -
    - - - -
    -
    -

    bj

    - Body - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:35 -

    - - -
    - -
    - -
    - - - -
    -
    -

    enabled

    - Boolean - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:69 -

    - - -
    - -
    - -
    - -

    Default: true

    - - -
    -
    -

    eps

    - Number - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:53 -

    - - -
    - -
    -

    SPOOK parameter

    - -
    - - - -
    -
    -

    jacobianElementA

    - JacobianElement - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:59 -

    - - -
    - -
    - -
    - - - -
    -
    -

    jacobianElementB

    - JacobianElement - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:64 -

    - - -
    - -
    - -
    - - - -
    -
    -

    maxForce

    - Number - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:24 -

    - - -
    - -
    - -
    - - - -
    -
    -

    minForce

    - Number - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:19 -

    - - -
    - -
    - -
    - - - -
    -
    -

    ni

    - Vec3 - - - - - -
    -

    - Defined in - src/equations/ContactEquation.js:38 -

    - - -
    - -
    -

    Contact normal, pointing out of body i.

    - -
    - - - -
    -
    -

    restitution

    - Number - - - - - -
    -

    - Defined in - src/equations/ContactEquation.js:20 -

    - - -
    - -
    - -
    - - - -
    -
    -

    ri

    - Vec3 - - - - - -
    -

    - Defined in - src/equations/ContactEquation.js:26 -

    - - -
    - -
    -

    World-oriented vector that goes from the center of bi to the contact point.

    - -
    - - - -
    -
    -

    rj

    - Vec3 - - - - - -
    -

    - Defined in - src/equations/ContactEquation.js:32 -

    - - -
    - -
    -

    World-oriented vector that starts in body j position and goes to the contact point.

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/ContactMaterial.html b/docs/classes/ContactMaterial.html deleted file mode 100644 index a01ab3430..000000000 --- a/docs/classes/ContactMaterial.html +++ /dev/null @@ -1,572 +0,0 @@ - - - - - ContactMaterial - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    ContactMaterial Class

    -
    - - - - - -
    - - -
    -

    Defines what happens when two materials meet.

    - -
    - -
    -

    Constructor

    -
    -

    ContactMaterial

    - -
    - (
      -
    • - m1 -
    • -
    • - m2 -
    • -
    • - [options] -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/material/ContactMaterial.js:5 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - m1 - Material - - -
      - -
      - -
    • -
    • - m2 - Material - - -
      - -
      - -
    • -
    • - [options] - Object - optional - - -
      - -
      - -
        -
      • - [friction=0.3] - Number - optional - -
        - -
        - -
      • -
      • - [restitution=0.3] - Number - optional - -
        - -
        - -
      • -
      • - [contactEquationStiffness=1e7] - Number - optional - -
        - -
        - -
      • -
      • - [contactEquationRelaxation=3] - Number - optional - -
        - -
        - -
      • -
      • - [frictionEquationStiffness=1e7] - Number - optional - -
        - -
        - -
      • -
      • - [frictionEquationRelaxation=3] - Number - optional - -
        - -
        - -
      • -
      -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - - -
    -

    Properties

    - - -
    - - -
    - - -
    -

    Properties

    - -
    -

    contactEquationRelaxation

    - Number - - - - - -
    -

    - Defined in - src/material/ContactMaterial.js:60 -

    - - -
    - -
    -

    Relaxation time of the produced contact equations

    - -
    - - - -
    -
    -

    contactEquationStiffness

    - Number - - - - - -
    -

    - Defined in - src/material/ContactMaterial.js:54 -

    - - -
    - -
    -

    Stiffness of the produced contact equations

    - -
    - - - -
    -
    -

    friction

    - Number - - - - - -
    -

    - Defined in - src/material/ContactMaterial.js:42 -

    - - -
    - -
    -

    Friction coefficient

    - -
    - - - -
    -
    -

    frictionEquationRelaxation

    - Number - - - - - -
    -

    - Defined in - src/material/ContactMaterial.js:72 -

    - - -
    - -
    -

    Relaxation time of the produced friction equations

    - -
    - - - -
    -
    -

    frictionEquationStiffness

    - Number - - - - - -
    -

    - Defined in - src/material/ContactMaterial.js:66 -

    - - -
    - -
    -

    Stiffness of the produced friction equations

    - -
    - - - -
    -
    -

    id

    - Number - - - - - -
    -

    - Defined in - src/material/ContactMaterial.js:29 -

    - - -
    - -
    -

    Identifier of this material

    - -
    - - - -
    -
    -

    materials

    - Array - - - - - -
    -

    - Defined in - src/material/ContactMaterial.js:35 -

    - - -
    - -
    -

    Participating materials

    - -
    - - - -
    -
    -

    restitution

    - Number - - - - - -
    -

    - Defined in - src/material/ContactMaterial.js:48 -

    - - -
    - -
    -

    Restitution coefficient

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/ConvexPolyhedron.html b/docs/classes/ConvexPolyhedron.html deleted file mode 100644 index 63d853bb4..000000000 --- a/docs/classes/ConvexPolyhedron.html +++ /dev/null @@ -1,2166 +0,0 @@ - - - - - ConvexPolyhedron - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    ConvexPolyhedron Class

    -
    - -
    - Extends Shape -
    - - - - -
    - - -
    -

    A set of polygons describing a convex shape.

    - -
    - -
    -

    Constructor

    -
    -

    ConvexPolyhedron

    - -
    - (
      -
    • - points -
    • -
    • - faces -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/shapes/ConvexPolyhedron.js:8 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - points - Array - - -
      -

      An array of Vec3's

      - -
      - -
    • -
    • - faces - Array - - -
      -

      Array of integer arrays, describing which vertices that is included in each face.

      - -
      - -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    calculateLocalInertia

    - -
    - (
      -
    • - mass -
    • -
    • - target -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - - Shape - - but overwritten in - src/shapes/ConvexPolyhedron.js:413 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - mass - Number - - -
      - -
      - -
    • -
    • - target - Vec3 - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    calculateWorldAABB

    - -
    - (
      -
    • - pos -
    • -
    • - quat -
    • -
    • - min -
    • -
    • - max -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/shapes/ConvexPolyhedron.js:727 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - pos - Vec3 - - -
      - -
      - -
    • -
    • - quat - Quaternion - - -
      - -
      - -
    • -
    • - min - Vec3 - - -
      - -
      - -
    • -
    • - max - Vec3 - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    clipAgainstHull

    - -
    - (
      -
    • - posA -
    • -
    • - quatA -
    • -
    • - hullB -
    • -
    • - posB -
    • -
    • - quatB -
    • -
    • - separatingNormal -
    • -
    • - minDist -
    • -
    • - maxDist -
    • -
    • - result -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/shapes/ConvexPolyhedron.js:181 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - posA - Vec3 - - -
      - -
      - -
    • -
    • - quatA - Quaternion - - -
      - -
      - -
    • -
    • - hullB - ConvexPolyhedron - - -
      - -
      - -
    • -
    • - posB - Vec3 - - -
      - -
      - -
    • -
    • - quatB - Quaternion - - -
      - -
      - -
    • -
    • - separatingNormal - Vec3 - - -
      - -
      - -
    • -
    • - minDist - Number - - -
      -

      Clamp distance

      - -
      - -
    • -
    • - maxDist - Number - - -
      - -
      - -
    • -
    • - result - Array - - -
      -

      The an array of contact point objects, see clipFaceAgainstHull

      - -
      - -
    • -
    -
    - - - -
    -
    -

    clipFaceAgainstHull

    - -
    - (
      -
    • - separatingNormal -
    • -
    • - posA -
    • -
    • - quatA -
    • -
    • - worldVertsB1 -
    • -
    • - minDist -
    • -
    • - maxDist -
    • -
    • - Array -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/shapes/ConvexPolyhedron.js:443 -

    - - - -
    - -
    -

    Clip a face against a hull.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - separatingNormal - Vec3 - - -
      - -
      - -
    • -
    • - posA - Vec3 - - -
      - -
      - -
    • -
    • - quatA - Quaternion - - -
      - -
      - -
    • -
    • - worldVertsB1 - Array - - -
      -

      An array of Vec3 with vertices in the world frame.

      - -
      - -
    • -
    • - minDist - Number - - -
      -

      Distance clamping

      - -
      - -
    • -
    • - maxDist - Number - - -
      - -
      - -
    • -
    • - Array - Object - - -
      -

      result Array to store resulting contact points in. Will be objects with properties: point, depth, normal. These are represented in world coordinates.

      - -
      - -
    • -
    -
    - - - -
    -
    -

    clipFaceAgainstPlane

    - -
    - (
      -
    • - inVertices -
    • -
    • - outVertices -
    • -
    • - planeNormal -
    • -
    • - planeConstant -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/shapes/ConvexPolyhedron.js:588 -

    - - - -
    - -
    -

    Clip a face in a hull against the back of a plane.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - inVertices - Array - - -
      - -
      - -
    • -
    • - outVertices - Array - - -
      - -
      - -
    • -
    • - planeNormal - Vec3 - - -
      - -
      - -
    • -
    • - planeConstant - Number - - -
      -

      The constant in the mathematical plane equation

      - -
      - -
    • -
    -
    - - - -
    -
    -

    computeEdges

    - - () - - - - - - - - -
    -

    - Defined in - src/shapes/ConvexPolyhedron.js:80 -

    - - - -
    - -
    -

    Computes uniqueEdges

    - -
    - - - - -
    -
    -

    computeNormals

    - - () - - - - - - - - -
    -

    - Defined in - src/shapes/ConvexPolyhedron.js:116 -

    - - - -
    - -
    -

    Compute the normals of the faces. Will reuse existing Vec3 objects in the .faceNormals array if they exist.

    - -
    - - - - -
    -
    -

    computeWorldFaceNormals

    - -
    - (
      -
    • - quat -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/shapes/ConvexPolyhedron.js:689 -

    - - - -
    - -
    -

    Updates .worldVertices and sets .worldVerticesNeedsUpdate to false.

    - -
    - -
    -

    Parameters:

    - - -
    - - - -
    -
    -

    findSeparatingAxis

    - -
    - (
      -
    • - hullB -
    • -
    • - posA -
    • -
    • - quatA -
    • -
    • - posB -
    • -
    • - quatB -
    • -
    • - target -
    • -
    ) -
    - - - Bool - - - - - - - - -
    -

    - Defined in - src/shapes/ConvexPolyhedron.js:234 -

    - - - -
    - -
    -

    Find the separating axis between this hull and another

    - -
    - -
    -

    Parameters:

    - -
      -
    • - hullB - ConvexPolyhedron - - -
      - -
      - -
    • -
    • - posA - Vec3 - - -
      - -
      - -
    • -
    • - quatA - Quaternion - - -
      - -
      - -
    • -
    • - posB - Vec3 - - -
      - -
      - -
    • -
    • - quatB - Quaternion - - -
      - -
      - -
    • -
    • - target - Vec3 - - -
      -

      The target vector to save the axis in

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Bool: -

    Returns false if a separation is found, else true

    - -
    -
    - - -
    -
    -

    getAveragePointLocal

    - -
    - (
      -
    • - target -
    • -
    ) -
    - - - Vec3 - - - - - - - - -
    -

    - Defined in - src/shapes/ConvexPolyhedron.js:773 -

    - - - -
    - -
    -

    Get an average of all the vertices positions

    - -
    - -
    -

    Parameters:

    - -
      -
    • - target - Vec3 - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Vec3: -
    -
    - - -
    -
    -

    getFaceNormal

    - -
    - (
      -
    • - i -
    • -
    • - target -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/shapes/ConvexPolyhedron.js:167 -

    - - - -
    - -
    -

    Compute the normal of a face from its vertices

    - -
    - -
    -

    Parameters:

    - -
      -
    • - i - Number - - -
      - -
      - -
    • -
    • - target - Vec3 - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    getFaceNormal

    - -
    - (
      -
    • - va -
    • -
    • - vb -
    • -
    • - vc -
    • -
    • - target -
    • -
    ) -
    - - - - - - static - - - -
    -

    - Defined in - src/shapes/ConvexPolyhedron.js:147 -

    - - - -
    - -
    -

    Get face normal given 3 vertices

    - -
    - -
    -

    Parameters:

    - -
      -
    • - va - Vec3 - - -
      - -
      - -
    • -
    • - vb - Vec3 - - -
      - -
      - -
    • -
    • - vc - Vec3 - - -
      - -
      - -
    • -
    • - target - Vec3 - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    getPlaneConstantOfFace

    - -
    - (
      -
    • - face_i -
    • -
    ) -
    - - - Number - - - - - - - - -
    -

    - Defined in - src/shapes/ConvexPolyhedron.js:430 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - face_i - Number - - -
      -

      Index of the face

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    pointIsInside

    - -
    - (
      -
    • - p -
    • -
    ) -
    - - - Boolean - - - - - - - - -
    -

    - Defined in - src/shapes/ConvexPolyhedron.js:829 -

    - - - -
    - -
    -

    Checks whether p is inside the polyhedra. Must be in local coords. The point lies outside of the convex hull of the other points if and only if the direction of all the vectors from it to those other points are on less than one half of a sphere around it.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - p - Vec3 - - -
      -

      A point given in local coordinates

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Boolean: -
    -
    - - -
    -
    -

    project

    - -
    - (
      -
    • - hull -
    • -
    • - axis -
    • -
    • - pos -
    • -
    • - quat -
    • -
    • - result -
    • -
    ) -
    - - - - - - static - - - -
    -

    - Defined in - src/shapes/ConvexPolyhedron.js:871 -

    - - - -
    - -
    -

    Get max and min dot product of a convex hull at position (pos,quat) projected onto an axis. Results are saved in the array maxmin.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - hull - ConvexPolyhedron - - -
      - -
      - -
    • -
    • - axis - Vec3 - - -
      - -
      - -
    • -
    • - pos - Vec3 - - -
      - -
      - -
    • -
    • - quat - Quaternion - - -
      - -
      - -
    • -
    • - result - Array - - -
      -

      result[0] and result[1] will be set to maximum and minimum, respectively.

      - -
      - -
    • -
    -
    - - - -
    -
    -

    testSepAxis

    - -
    - (
      -
    • - axis -
    • -
    • - hullB -
    • -
    • - posA -
    • -
    • - quatA -
    • -
    • - posB -
    • -
    • - quatB -
    • -
    ) -
    - - - Number - - - - - - - - -
    -

    - Defined in - src/shapes/ConvexPolyhedron.js:382 -

    - - - -
    - -
    -

    Test separating axis against two hulls. Both hulls are projected onto the axis and the overlap size is returned if there is one.

    - -
    - -
    -

    Parameters:

    - - -
    - -
    -

    Returns:

    - -
    - Number: -

    The overlap depth, or FALSE if no penetration.

    - -
    -
    - - -
    -
    -

    transformAllPoints

    - -
    - (
      -
    • - offset -
    • -
    • - quat -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/shapes/ConvexPolyhedron.js:790 -

    - - - -
    - -
    -

    Transform all local points. Will change the .vertices

    - -
    - -
    -

    Parameters:

    - -
      -
    • - offset - Vec3 - - -
      - -
      - -
    • -
    • - quat - Quaternion - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    updateBoundingSphereRadius

    - - () - - - - - - - - -
    -

    Inherited from - - Shape - - but overwritten in - src/shapes/ConvexPolyhedron.js:709 -

    - - - -
    - -
    - -
    - - - - -
    -
    -

    volume

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - - Shape - - but overwritten in - src/shapes/ConvexPolyhedron.js:764 -

    - - - -
    - -
    -

    Get approximate convex volume

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    - -
    -

    Properties

    - -
    -

    boundingSphereRadius

    - Number - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:31 -

    - - -
    - -
    -

    The local bounding sphere radius of this shape.

    - -
    - - - -
    -
    -

    collisionResponse

    - Boolean - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:37 -

    - - -
    - -
    -

    Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled.

    - -
    - - - -
    -
    -

    faceNormals

    - Array - - - - - -
    -

    - Defined in - src/shapes/ConvexPolyhedron.js:49 -

    - - -
    - -
    -

    Array of Vec3

    - -
    - - - -
    -
    -

    faces

    - Array - - - - - -
    -

    - Defined in - src/shapes/ConvexPolyhedron.js:42 -

    - - -
    - -
    -

    Array of integer arrays, indicating which vertices each face consists of

    - -
    - - - -
    -
    -

    id

    - Number - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:17 -

    - - -
    - -
    -

    Identifyer of the Shape.

    - -
    - - - -
    -
    -

    material

    - Material - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:43 -

    - - -
    - -
    - -
    - - - -
    -
    -

    type

    - Number - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:23 -

    - - -
    - -
    -

    The type of this shape. Must be set to an int > 0 by subclasses.

    - -
    - - - -
    -
    -

    uniqueAxes

    - Array - - - - - -
    -

    - Defined in - src/shapes/ConvexPolyhedron.js:67 -

    - - -
    - -
    -

    If given, these locally defined, normalized axes are the only ones being checked when doing separating axis check.

    - -
    - - - -
    -
    -

    uniqueEdges

    - Array - - - - - -
    -

    - Defined in - src/shapes/ConvexPolyhedron.js:60 -

    - - -
    - -
    -

    Array of Vec3

    - -
    - - - -
    -
    -

    vertices

    - Array - - - - - -
    -

    - Defined in - src/shapes/ConvexPolyhedron.js:32 -

    - - -
    - -
    -

    Array of Vec3

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/Cylinder.html b/docs/classes/Cylinder.html deleted file mode 100644 index 070c35267..000000000 --- a/docs/classes/Cylinder.html +++ /dev/null @@ -1,1980 +0,0 @@ - - - - - Cylinder - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    Cylinder Class

    -
    - -
    - Extends ConvexPolyhedron -
    - -
    - Defined in: src/shapes/Cylinder.js:8 -
    - - -
    - - -
    - -
    - -
    -

    Constructor

    -
    -

    Cylinder

    - -
    - (
      -
    • - radiusTop -
    • -
    • - radiusBottom -
    • -
    • - height -
    • -
    • - numSegments -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/shapes/Cylinder.js:8 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - radiusTop - Number - - -
      - -
      - -
    • -
    • - radiusBottom - Number - - -
      - -
      - -
    • -
    • - height - Number - - -
      - -
      - -
    • -
    • - numSegments - Number - - -
      -

      The number of segments to build the cylinder out of

      - -
      - -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - - - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    calculateLocalInertia

    - -
    - (
      -
    • - mass -
    • -
    • - target -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - - Shape - - but overwritten in - src/shapes/ConvexPolyhedron.js:413 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - mass - Number - - -
      - -
      - -
    • -
    • - target - Vec3 - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    calculateWorldAABB

    - -
    - (
      -
    • - pos -
    • -
    • - quat -
    • -
    • - min -
    • -
    • - max -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - ConvexPolyhedron: - src/shapes/ConvexPolyhedron.js:727 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - pos - Vec3 - - -
      - -
      - -
    • -
    • - quat - Quaternion - - -
      - -
      - -
    • -
    • - min - Vec3 - - -
      - -
      - -
    • -
    • - max - Vec3 - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    clipAgainstHull

    - -
    - (
      -
    • - posA -
    • -
    • - quatA -
    • -
    • - hullB -
    • -
    • - posB -
    • -
    • - quatB -
    • -
    • - separatingNormal -
    • -
    • - minDist -
    • -
    • - maxDist -
    • -
    • - result -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - ConvexPolyhedron: - src/shapes/ConvexPolyhedron.js:181 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - posA - Vec3 - - -
      - -
      - -
    • -
    • - quatA - Quaternion - - -
      - -
      - -
    • -
    • - hullB - ConvexPolyhedron - - -
      - -
      - -
    • -
    • - posB - Vec3 - - -
      - -
      - -
    • -
    • - quatB - Quaternion - - -
      - -
      - -
    • -
    • - separatingNormal - Vec3 - - -
      - -
      - -
    • -
    • - minDist - Number - - -
      -

      Clamp distance

      - -
      - -
    • -
    • - maxDist - Number - - -
      - -
      - -
    • -
    • - result - Array - - -
      -

      The an array of contact point objects, see clipFaceAgainstHull

      - -
      - -
    • -
    -
    - - - -
    -
    -

    clipFaceAgainstHull

    - -
    - (
      -
    • - separatingNormal -
    • -
    • - posA -
    • -
    • - quatA -
    • -
    • - worldVertsB1 -
    • -
    • - minDist -
    • -
    • - maxDist -
    • -
    • - Array -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - ConvexPolyhedron: - src/shapes/ConvexPolyhedron.js:443 -

    - - - -
    - -
    -

    Clip a face against a hull.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - separatingNormal - Vec3 - - -
      - -
      - -
    • -
    • - posA - Vec3 - - -
      - -
      - -
    • -
    • - quatA - Quaternion - - -
      - -
      - -
    • -
    • - worldVertsB1 - Array - - -
      -

      An array of Vec3 with vertices in the world frame.

      - -
      - -
    • -
    • - minDist - Number - - -
      -

      Distance clamping

      - -
      - -
    • -
    • - maxDist - Number - - -
      - -
      - -
    • -
    • - Array - Object - - -
      -

      result Array to store resulting contact points in. Will be objects with properties: point, depth, normal. These are represented in world coordinates.

      - -
      - -
    • -
    -
    - - - -
    -
    -

    clipFaceAgainstPlane

    - -
    - (
      -
    • - inVertices -
    • -
    • - outVertices -
    • -
    • - planeNormal -
    • -
    • - planeConstant -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - ConvexPolyhedron: - src/shapes/ConvexPolyhedron.js:588 -

    - - - -
    - -
    -

    Clip a face in a hull against the back of a plane.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - inVertices - Array - - -
      - -
      - -
    • -
    • - outVertices - Array - - -
      - -
      - -
    • -
    • - planeNormal - Vec3 - - -
      - -
      - -
    • -
    • - planeConstant - Number - - -
      -

      The constant in the mathematical plane equation

      - -
      - -
    • -
    -
    - - - -
    -
    -

    computeEdges

    - - () - - - - - - - - -
    -

    Inherited from - ConvexPolyhedron: - src/shapes/ConvexPolyhedron.js:80 -

    - - - -
    - -
    -

    Computes uniqueEdges

    - -
    - - - - -
    -
    -

    computeNormals

    - - () - - - - - - - - -
    -

    Inherited from - ConvexPolyhedron: - src/shapes/ConvexPolyhedron.js:116 -

    - - - -
    - -
    -

    Compute the normals of the faces. Will reuse existing Vec3 objects in the .faceNormals array if they exist.

    - -
    - - - - -
    -
    -

    computeWorldFaceNormals

    - -
    - (
      -
    • - quat -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - ConvexPolyhedron: - src/shapes/ConvexPolyhedron.js:689 -

    - - - -
    - -
    -

    Updates .worldVertices and sets .worldVerticesNeedsUpdate to false.

    - -
    - -
    -

    Parameters:

    - - -
    - - - -
    -
    -

    findSeparatingAxis

    - -
    - (
      -
    • - hullB -
    • -
    • - posA -
    • -
    • - quatA -
    • -
    • - posB -
    • -
    • - quatB -
    • -
    • - target -
    • -
    ) -
    - - - Bool - - - - - - - - -
    -

    Inherited from - ConvexPolyhedron: - src/shapes/ConvexPolyhedron.js:234 -

    - - - -
    - -
    -

    Find the separating axis between this hull and another

    - -
    - -
    -

    Parameters:

    - -
      -
    • - hullB - ConvexPolyhedron - - -
      - -
      - -
    • -
    • - posA - Vec3 - - -
      - -
      - -
    • -
    • - quatA - Quaternion - - -
      - -
      - -
    • -
    • - posB - Vec3 - - -
      - -
      - -
    • -
    • - quatB - Quaternion - - -
      - -
      - -
    • -
    • - target - Vec3 - - -
      -

      The target vector to save the axis in

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Bool: -

    Returns false if a separation is found, else true

    - -
    -
    - - -
    -
    -

    getAveragePointLocal

    - -
    - (
      -
    • - target -
    • -
    ) -
    - - - Vec3 - - - - - - - - -
    -

    Inherited from - ConvexPolyhedron: - src/shapes/ConvexPolyhedron.js:773 -

    - - - -
    - -
    -

    Get an average of all the vertices positions

    - -
    - -
    -

    Parameters:

    - -
      -
    • - target - Vec3 - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Vec3: -
    -
    - - -
    -
    -

    getFaceNormal

    - -
    - (
      -
    • - i -
    • -
    • - target -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - ConvexPolyhedron: - src/shapes/ConvexPolyhedron.js:167 -

    - - - -
    - -
    -

    Compute the normal of a face from its vertices

    - -
    - -
    -

    Parameters:

    - -
      -
    • - i - Number - - -
      - -
      - -
    • -
    • - target - Vec3 - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    getPlaneConstantOfFace

    - -
    - (
      -
    • - face_i -
    • -
    ) -
    - - - Number - - - - - - - - -
    -

    Inherited from - ConvexPolyhedron: - src/shapes/ConvexPolyhedron.js:430 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - face_i - Number - - -
      -

      Index of the face

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    pointIsInside

    - -
    - (
      -
    • - p -
    • -
    ) -
    - - - Boolean - - - - - - - - -
    -

    Inherited from - ConvexPolyhedron: - src/shapes/ConvexPolyhedron.js:829 -

    - - - -
    - -
    -

    Checks whether p is inside the polyhedra. Must be in local coords. The point lies outside of the convex hull of the other points if and only if the direction of all the vectors from it to those other points are on less than one half of a sphere around it.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - p - Vec3 - - -
      -

      A point given in local coordinates

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Boolean: -
    -
    - - -
    -
    -

    testSepAxis

    - -
    - (
      -
    • - axis -
    • -
    • - hullB -
    • -
    • - posA -
    • -
    • - quatA -
    • -
    • - posB -
    • -
    • - quatB -
    • -
    ) -
    - - - Number - - - - - - - - -
    -

    Inherited from - ConvexPolyhedron: - src/shapes/ConvexPolyhedron.js:382 -

    - - - -
    - -
    -

    Test separating axis against two hulls. Both hulls are projected onto the axis and the overlap size is returned if there is one.

    - -
    - -
    -

    Parameters:

    - - -
    - -
    -

    Returns:

    - -
    - Number: -

    The overlap depth, or FALSE if no penetration.

    - -
    -
    - - -
    -
    -

    transformAllPoints

    - -
    - (
      -
    • - offset -
    • -
    • - quat -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - ConvexPolyhedron: - src/shapes/ConvexPolyhedron.js:790 -

    - - - -
    - -
    -

    Transform all local points. Will change the .vertices

    - -
    - -
    -

    Parameters:

    - -
      -
    • - offset - Vec3 - - -
      - -
      - -
    • -
    • - quat - Quaternion - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    updateBoundingSphereRadius

    - - () - - - - - - - - -
    -

    Inherited from - - Shape - - but overwritten in - src/shapes/ConvexPolyhedron.js:709 -

    - - - -
    - -
    - -
    - - - - -
    -
    -

    volume

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - - Shape - - but overwritten in - src/shapes/ConvexPolyhedron.js:764 -

    - - - -
    - -
    -

    Get approximate convex volume

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    - -
    -

    Properties

    - -
    -

    boundingSphereRadius

    - Number - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:31 -

    - - -
    - -
    -

    The local bounding sphere radius of this shape.

    - -
    - - - -
    -
    -

    collisionResponse

    - Boolean - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:37 -

    - - -
    - -
    -

    Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled.

    - -
    - - - -
    -
    -

    faceNormals

    - Array - - - - - -
    -

    Inherited from - ConvexPolyhedron: - src/shapes/ConvexPolyhedron.js:49 -

    - - -
    - -
    -

    Array of Vec3

    - -
    - - - -
    -
    -

    faces

    - Array - - - - - -
    -

    Inherited from - ConvexPolyhedron: - src/shapes/ConvexPolyhedron.js:42 -

    - - -
    - -
    -

    Array of integer arrays, indicating which vertices each face consists of

    - -
    - - - -
    -
    -

    id

    - Number - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:17 -

    - - -
    - -
    -

    Identifyer of the Shape.

    - -
    - - - -
    -
    -

    material

    - Material - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:43 -

    - - -
    - -
    - -
    - - - -
    -
    -

    type

    - Number - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:23 -

    - - -
    - -
    -

    The type of this shape. Must be set to an int > 0 by subclasses.

    - -
    - - - -
    -
    -

    uniqueAxes

    - Array - - - - - -
    -

    Inherited from - ConvexPolyhedron: - src/shapes/ConvexPolyhedron.js:67 -

    - - -
    - -
    -

    If given, these locally defined, normalized axes are the only ones being checked when doing separating axis check.

    - -
    - - - -
    -
    -

    uniqueEdges

    - Array - - - - - -
    -

    Inherited from - ConvexPolyhedron: - src/shapes/ConvexPolyhedron.js:60 -

    - - -
    - -
    -

    Array of Vec3

    - -
    - - - -
    -
    -

    vertices

    - Array - - - - - -
    -

    Inherited from - ConvexPolyhedron: - src/shapes/ConvexPolyhedron.js:32 -

    - - -
    - -
    -

    Array of Vec3

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/Demo.html b/docs/classes/Demo.html deleted file mode 100644 index ad97e9bfe..000000000 --- a/docs/classes/Demo.html +++ /dev/null @@ -1,358 +0,0 @@ - - - - - Demo - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    Demo Class

    -
    - - -
    - Defined in: src/demo/Demo.js:5 -
    - - -
    - - -
    -

    Demo framework class. If you want to learn how to connect Cannon.js with Three.js, please look at the examples/ instead.

    - -
    - -
    -

    Constructor

    -
    -

    Demo

    - -
    - (
      -
    • - options -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/demo/Demo.js:5 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - options - Object - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - - - -
    - -
    -

    Methods

    - -
    -

    addScene

    - -
    - (
      -
    • - title -
    • -
    • - initfunc -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/demo/Demo.js:209 -

    - - - -
    - -
    -

    Add a scene to the demo app

    - -
    - -
    -

    Parameters:

    - -
      -
    • - title - String - - -
      -

      Title of the scene

      - -
      - -
    • -
    • - initfunc - Function - - -
      -

      A function that takes one argument, app, and initializes a physics scene. The function runs app.setWorld(body), app.addVisual(body), app.removeVisual(body) etc.

      - -
      - -
    • -
    -
    - - - -
    -
    -

    restartCurrentScene

    - - () - - - - - - - - -
    -

    - Defined in - src/demo/Demo.js:230 -

    - - - -
    - -
    -

    Restarts the current scene

    - -
    - - - - -
    -
    - - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/DistanceConstraint.html b/docs/classes/DistanceConstraint.html deleted file mode 100644 index d91a94742..000000000 --- a/docs/classes/DistanceConstraint.html +++ /dev/null @@ -1,610 +0,0 @@ - - - - - DistanceConstraint - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    DistanceConstraint Class

    -
    - -
    - Extends Constraint -
    - - - - -
    - - -
    -

    Constrains two bodies to be at a constant distance from each others center of mass.

    - -
    - -
    -

    Constructor

    -
    -

    DistanceConstraint

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    • - [distance] -
    • -
    • - [maxForce=1e6] -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/constraints/DistanceConstraint.js:6 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    • - [distance] - Number - optional - - -
      -

      The distance to keep. If undefined, it will be set to the current distance between bodyA and bodyB

      - -
      - -
    • -
    • - [maxForce=1e6] - Number - optional - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    disable

    - - () - - - - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:80 -

    - - - -
    - -
    -

    Disables all equations in the constraint.

    - -
    - - - - -
    -
    -

    enable

    - - () - - - - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:69 -

    - - - -
    - -
    -

    Enables all equations in the constraint.

    - -
    - - - - -
    -
    -

    update

    - - () - - - - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:61 -

    - - - -
    - -
    -

    Update all the equations with data.

    - -
    - - - - -
    -
    - -
    -

    Properties

    - -
    -

    bodyA

    - Body - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:29 -

    - - -
    - -
    - -
    - - - -
    -
    -

    bodyB

    - Body - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:34 -

    - - -
    - -
    - -
    - - - -
    -
    -

    collideConnected

    - Boolean - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:44 -

    - - -
    - -
    -

    Set to true if you want the bodies to collide when they are connected.

    - -
    - - - -
    -
    -

    distance

    - Number - - - - - -
    -

    - Defined in - src/constraints/DistanceConstraint.js:28 -

    - - -
    - -
    - -
    - - - -
    -
    -

    distanceEquation

    - ContactEquation - - - - - -
    -

    - Defined in - src/constraints/DistanceConstraint.js:33 -

    - - -
    - -
    - -
    - - - -
    -
    -

    equations

    - Array - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:22 -

    - - -
    - -
    -

    Equations to be solved in this constraint

    - -
    - - - -
    -
    -

    id

    - Number - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:39 -

    - - -
    - -
    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/Equation.html b/docs/classes/Equation.html deleted file mode 100644 index c3829c22c..000000000 --- a/docs/classes/Equation.html +++ /dev/null @@ -1,1016 +0,0 @@ - - - - - Equation - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    Equation Class

    -
    - - -
    - Defined in: src/equations/Equation.js:6 -
    - - -
    - - -
    -

    Equation base class

    - -
    - -
    -

    Constructor

    -
    -

    Equation

    - -
    - (
      -
    • - bi -
    • -
    • - bj -
    • -
    • - minForce -
    • -
    • - maxForce -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/equations/Equation.js:6 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - bi - Body - - -
      - -
      - -
    • -
    • - bj - Body - - -
      - -
      - -
    • -
    • - minForce - Number - - -
      -

      Minimum (read: negative max) force to be applied by the constraint.

      - -
      - -
    • -
    • - maxForce - Number - - -
      -

      Maximum (read: positive max) force to be applied by the constraint.

      - -
      - -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    addToWlambda

    - -
    - (
      -
    • - deltalambda -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/equations/Equation.js:228 -

    - - - -
    - -
    -

    Add constraint velocity to the bodies.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - deltalambda - Number - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    computeB

    - - () - - - Number - - - - - - - - -
    -

    - Defined in - src/equations/Equation.js:95 -

    - - - -
    - -
    -

    Computes the RHS of the SPOOK equation

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGiMf

    - - () - - - Number - - - - - - - - -
    -

    - Defined in - src/equations/Equation.js:159 -

    - - - -
    - -
    -

    Computes Ginv(M)f, where M is the mass matrix with diagonal blocks for each body, and f are the forces on the bodies.

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGiMGt

    - - () - - - Number - - - - - - - - -
    -

    - Defined in - src/equations/Equation.js:191 -

    - - - -
    - -
    -

    Computes Ginv(M)G'

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGq

    - - () - - - Number - - - - - - - - -
    -

    - Defined in - src/equations/Equation.js:107 -

    - - - -
    - -
    -

    Computes G*q, where q are the generalized body coordinates

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGW

    - - () - - - Number - - - - - - - - -
    -

    - Defined in - src/equations/Equation.js:124 -

    - - - -
    - -
    -

    Computes G*W, where W are the body velocities

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGWlambda

    - - () - - - Number - - - - - - - - -
    -

    - Defined in - src/equations/Equation.js:142 -

    - - - -
    - -
    -

    Computes G*Wlambda, where W are the body velocities

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeInvC

    - -
    - (
      -
    • - eps -
    • -
    ) -
    - - - Number - - - - - - - - -
    -

    - Defined in - src/equations/Equation.js:262 -

    - - - -
    - -
    -

    Compute the denominator part of the SPOOK equation: C = Ginv(M)G' + eps

    - -
    - -
    -

    Parameters:

    - -
      -
    • - eps - Number - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    setSpookParams

    - - () - - - - - - - - -
    -

    - Defined in - src/equations/Equation.js:82 -

    - - - -
    - -
    -

    Recalculates a,b,eps.

    - -
    - - - - -
    -
    - -
    -

    Properties

    - -
    -

    a

    - Number - - - - - -
    -

    - Defined in - src/equations/Equation.js:41 -

    - - -
    - -
    -

    SPOOK parameter

    - -
    - - - -
    -
    -

    b

    - Number - - - - - -
    -

    - Defined in - src/equations/Equation.js:47 -

    - - -
    - -
    -

    SPOOK parameter

    - -
    - - - -
    -
    -

    bi

    - Body - - - - - -
    -

    - Defined in - src/equations/Equation.js:29 -

    - - -
    - -
    - -
    - - - -
    -
    -

    bj

    - Body - - - - - -
    -

    - Defined in - src/equations/Equation.js:35 -

    - - -
    - -
    - -
    - - - -
    -
    -

    enabled

    - Boolean - - - - - -
    -

    - Defined in - src/equations/Equation.js:69 -

    - - -
    - -
    - -
    - -

    Default: true

    - - -
    -
    -

    eps

    - Number - - - - - -
    -

    - Defined in - src/equations/Equation.js:53 -

    - - -
    - -
    -

    SPOOK parameter

    - -
    - - - -
    -
    -

    jacobianElementA

    - JacobianElement - - - - - -
    -

    - Defined in - src/equations/Equation.js:59 -

    - - -
    - -
    - -
    - - - -
    -
    -

    jacobianElementB

    - JacobianElement - - - - - -
    -

    - Defined in - src/equations/Equation.js:64 -

    - - -
    - -
    - -
    - - - -
    -
    -

    maxForce

    - Number - - - - - -
    -

    - Defined in - src/equations/Equation.js:24 -

    - - -
    - -
    - -
    - - - -
    -
    -

    minForce

    - Number - - - - - -
    -

    - Defined in - src/equations/Equation.js:19 -

    - - -
    - -
    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/EventTarget.html b/docs/classes/EventTarget.html deleted file mode 100644 index 2a4c186fd..000000000 --- a/docs/classes/EventTarget.html +++ /dev/null @@ -1,553 +0,0 @@ - - - - - EventTarget - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    EventTarget Class

    -
    - - -
    - Defined in: src/utils/EventTarget.js:1 -
    - - -
    - - -
    -

    Base class for objects that dispatches events.

    - -
    - -
    -

    Constructor

    -
    -

    EventTarget

    - - () - - - - - - - - -
    -

    - Defined in - src/utils/EventTarget.js:1 -

    - - - -
    - -
    - -
    - - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - - - -
    - -
    -

    Methods

    - -
    -

    addEventListener

    - -
    - (
      -
    • - type -
    • -
    • - listener -
    • -
    ) -
    - - - EventTarget - - - - - - - - -
    -

    - Defined in - src/utils/EventTarget.js:15 -

    - - - -
    - -
    -

    Add an event listener

    - -
    - -
    -

    Parameters:

    - -
      -
    • - type - String - - -
      - -
      - -
    • -
    • - listener - Function - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - EventTarget: -

    The self object, for chainability.

    - -
    -
    - - -
    -
    -

    dispatchEvent

    - -
    - (
      -
    • - event -
    • -
    ) -
    - - - EventTarget - - - - - - - - -
    -

    - Defined in - src/utils/EventTarget.js:68 -

    - - - -
    - -
    -

    Emit an event.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - event - Object - - -
      - -
      - -
        -
      • - type - String - -
        - -
        - -
      • -
      -
    • -
    -
    - -
    -

    Returns:

    - -
    - EventTarget: -

    The self object, for chainability.

    - -
    -
    - - -
    -
    -

    hasEventListener

    - -
    - (
      -
    • - type -
    • -
    • - listener -
    • -
    ) -
    - - - Boolean - - - - - - - - -
    -

    - Defined in - src/utils/EventTarget.js:34 -

    - - - -
    - -
    -

    Check if an event listener is added

    - -
    - -
    -

    Parameters:

    - -
      -
    • - type - String - - -
      - -
      - -
    • -
    • - listener - Function - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Boolean: -
    -
    - - -
    -
    -

    removeEventListener

    - -
    - (
      -
    • - type -
    • -
    • - listener -
    • -
    ) -
    - - - EventTarget - - - - - - - - -
    -

    - Defined in - src/utils/EventTarget.js:50 -

    - - - -
    - -
    -

    Remove an event listener

    - -
    - -
    -

    Parameters:

    - -
      -
    • - type - String - - -
      - -
      - -
    • -
    • - listener - Function - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - EventTarget: -

    The self object, for chainability.

    - -
    -
    - - -
    -
    - - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/FrictionEquation.html b/docs/classes/FrictionEquation.html deleted file mode 100644 index 930f4a21c..000000000 --- a/docs/classes/FrictionEquation.html +++ /dev/null @@ -1,1005 +0,0 @@ - - - - - FrictionEquation - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    FrictionEquation Class

    -
    - -
    - Extends Equation -
    - - - - -
    - - -
    -

    Constrains the slipping in a contact along a tangent

    - -
    - -
    -

    Constructor

    -
    -

    FrictionEquation

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    • - slipForce -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/equations/FrictionEquation.js:7 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    • - slipForce - Number - - -
      -

      should be +-F_friction = +-mu F_normal = +-mu m * g

      - -
      - -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    addToWlambda

    - -
    - (
      -
    • - deltalambda -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:228 -

    - - - -
    - -
    -

    Add constraint velocity to the bodies.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - deltalambda - Number - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    computeB

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:95 -

    - - - -
    - -
    -

    Computes the RHS of the SPOOK equation

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGiMf

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:159 -

    - - - -
    - -
    -

    Computes Ginv(M)f, where M is the mass matrix with diagonal blocks for each body, and f are the forces on the bodies.

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGiMGt

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:191 -

    - - - -
    - -
    -

    Computes Ginv(M)G'

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGq

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:107 -

    - - - -
    - -
    -

    Computes G*q, where q are the generalized body coordinates

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGW

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:124 -

    - - - -
    - -
    -

    Computes G*W, where W are the body velocities

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGWlambda

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:142 -

    - - - -
    - -
    -

    Computes G*Wlambda, where W are the body velocities

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeInvC

    - -
    - (
      -
    • - eps -
    • -
    ) -
    - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:262 -

    - - - -
    - -
    -

    Compute the denominator part of the SPOOK equation: C = Ginv(M)G' + eps

    - -
    - -
    -

    Parameters:

    - -
      -
    • - eps - Number - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    setSpookParams

    - - () - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:82 -

    - - - -
    - -
    -

    Recalculates a,b,eps.

    - -
    - - - - -
    -
    - -
    -

    Properties

    - -
    -

    a

    - Number - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:41 -

    - - -
    - -
    -

    SPOOK parameter

    - -
    - - - -
    -
    -

    b

    - Number - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:47 -

    - - -
    - -
    -

    SPOOK parameter

    - -
    - - - -
    -
    -

    bi

    - Body - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:29 -

    - - -
    - -
    - -
    - - - -
    -
    -

    bj

    - Body - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:35 -

    - - -
    - -
    - -
    - - - -
    -
    -

    enabled

    - Boolean - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:69 -

    - - -
    - -
    - -
    - -

    Default: true

    - - -
    -
    -

    eps

    - Number - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:53 -

    - - -
    - -
    -

    SPOOK parameter

    - -
    - - - -
    -
    -

    jacobianElementA

    - JacobianElement - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:59 -

    - - -
    - -
    - -
    - - - -
    -
    -

    jacobianElementB

    - JacobianElement - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:64 -

    - - -
    - -
    - -
    - - - -
    -
    -

    maxForce

    - Number - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:24 -

    - - -
    - -
    - -
    - - - -
    -
    -

    minForce

    - Number - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:19 -

    - - -
    - -
    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/GSSolver.html b/docs/classes/GSSolver.html deleted file mode 100644 index 0016556da..000000000 --- a/docs/classes/GSSolver.html +++ /dev/null @@ -1,549 +0,0 @@ - - - - - GSSolver - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    GSSolver Class

    -
    - -
    - Extends Solver -
    - -
    - Defined in: src/solver/GSSolver.js:7 -
    - - -
    - - -
    -

    Constraint equation Gauss-Seidel solver.

    - -
    - -
    -

    Constructor

    -
    -

    GSSolver

    - - () - - - - - - - - -
    -

    - Defined in - src/solver/GSSolver.js:7 -

    - - - -
    - -
    - -
    - - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    addEquation

    - -
    - (
      -
    • - eq -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - Solver: - src/solver/Solver.js:28 -

    - - - -
    - -
    -

    Add an equation

    - -
    - -
    -

    Parameters:

    - - -
    - - - -
    -
    -

    removeAllEquations

    - - () - - - - - - - - -
    -

    Inherited from - Solver: - src/solver/Solver.js:52 -

    - - - -
    - -
    -

    Add all equations

    - -
    - - - - -
    -
    -

    removeEquation

    - -
    - (
      -
    • - eq -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - Solver: - src/solver/Solver.js:39 -

    - - - -
    - -
    -

    Remove an equation

    - -
    - -
    -

    Parameters:

    - - -
    - - - -
    -
    -

    solve

    - -
    - (
      -
    • - dt -
    • -
    • - world -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - Solver: - src/solver/Solver.js:17 -

    - - - -
    - -
    -

    Should be implemented in subclasses!

    - -
    - -
    -

    Parameters:

    - -
      -
    • - dt - Number - - -
      - -
      - -
    • -
    • - world - World - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    -

    Properties

    - -
    -

    equations

    - Array - - - - - -
    -

    Inherited from - Solver: - src/solver/Solver.js:10 -

    - - -
    - -
    -

    All equations to be solved

    - -
    - - - -
    -
    -

    iterations

    - Number - - - - - -
    -

    - Defined in - src/solver/GSSolver.js:19 -

    - - -
    - -
    -

    The number of solver iterations determines quality of the constraints in the world. The more iterations, the more correct simulation. More iterations need more computations though. If you have a large gravity force in your world, you will need more iterations.

    - -
    - - - -
    -
    -

    tolerance

    - Number - - - - - -
    -

    - Defined in - src/solver/GSSolver.js:27 -

    - - -
    - -
    -

    When tolerance is reached, the system is assumed to be converged.

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/GridBroadphase.html b/docs/classes/GridBroadphase.html deleted file mode 100644 index b45aa8c40..000000000 --- a/docs/classes/GridBroadphase.html +++ /dev/null @@ -1,1164 +0,0 @@ - - - - - GridBroadphase - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    GridBroadphase Class

    -
    - -
    - Extends Broadphase -
    - - - - -
    - - -
    -

    Axis aligned uniform grid broadphase.

    - -
    - -
    -

    Constructor

    -
    -

    GridBroadphase

    - -
    - (
      -
    • - aabbMin -
    • -
    • - aabbMax -
    • -
    • - nx -
    • -
    • - ny -
    • -
    • - nz -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/collision/GridBroadphase.js:7 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - aabbMin - Vec3 - - -
      - -
      - -
    • -
    • - aabbMax - Vec3 - - -
      - -
      - -
    • -
    • - nx - Number - - -
      -

      Number of boxes along x

      - -
      - -
    • -
    • - ny - Number - - -
      -

      Number of boxes along y

      - -
      - -
    • -
    • - nz - Number - - -
      -

      Number of boxes along z

      - -
      - -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - - - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    aabbQuery

    - -
    - (
      -
    • - world -
    • -
    • - aabb -
    • -
    • - result -
    • -
    ) -
    - - - Array - - - - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:197 -

    - - - -
    - -
    -

    Returns all the bodies within the AABB.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - world - World - - -
      - -
      - -
    • -
    • - aabb - AABB - - -
      - -
      - -
    • -
    • - result - Array - - -
      -

      An array to store resulting bodies in.

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Array: -
    -
    - - -
    -
    -

    boundingSphereCheck

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    ) -
    - - - Boolean - - - - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:183 -

    - - - -
    - -
    -

    Check if the bounding spheres of two bodies overlap.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Boolean: -
    -
    - - -
    -
    -

    collisionPairs

    - -
    - (
      -
    • - world -
    • -
    • - pairs1 -
    • -
    • - pairs2 -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - - Broadphase - - but overwritten in - src/collision/GridBroadphase.js:42 -

    - - - -
    - -
    -

    Get all the collision pairs in the physics world

    - -
    - -
    -

    Parameters:

    - -
      -
    • - world - World - - -
      - -
      - -
    • -
    • - pairs1 - Array - - -
      - -
      - -
    • -
    • - pairs2 - Array - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    doBoundingBoxBroadphase

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    • - pairs1 -
    • -
    • - pairs2 -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:112 -

    - - - -
    - -
    -

    Check if the bounding boxes of two bodies are intersecting.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    • - pairs1 - Array - - -
      - -
      - -
    • -
    • - pairs2 - Array - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    doBoundingSphereBroadphase

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    • - pairs1 -
    • -
    • - pairs2 -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:89 -

    - - - -
    - -
    -

    Check if the bounding spheres of two bodies are intersecting.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    • - pairs1 - Array - - -
      -

      bodyA is appended to this array if intersection

      - -
      - -
    • -
    • - pairs2 - Array - - -
      -

      bodyB is appended to this array if intersection

      - -
      - -
    • -
    -
    - - - -
    -
    -

    intersectionTest

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    • - pairs1 -
    • -
    • - pairs2 -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:73 -

    - - - -
    - -
    -

    Check if the bounding volumes of two bodies intersect.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    • - pairs1 - Array - - -
      - -
      - -
    • -
    • - pairs2 - Array - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    makePairsUnique

    - -
    - (
      -
    • - pairs1 -
    • -
    • - pairs2 -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:135 -

    - - - -
    - -
    -

    Removes duplicate pairs from the pair arrays.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - pairs1 - Array - - -
      - -
      - -
    • -
    • - pairs2 - Array - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    needBroadphaseCollision

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    ) -
    - - - Bool - - - - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:48 -

    - - - -
    - -
    -

    Check if a body pair needs to be intersection tested at all.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Bool: -
    -
    - - -
    -
    -

    setWorld

    - -
    - (
      -
    • - world -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:175 -

    - - - -
    - -
    -

    To be implemented by subcasses

    - -
    - -
    -

    Parameters:

    - -
      -
    • - world - World - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    -

    Properties

    - -
    -

    dirty

    - Boolean - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:30 -

    - - -
    - -
    -

    Set to true if the objects in the world moved.

    - -
    - - - -
    -
    -

    useBoundingBoxes

    - Boolean - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:23 -

    - - -
    - -
    -

    If set to true, the broadphase uses bounding boxes for intersection test, else it uses bounding spheres.

    - -
    - - - -
    -
    -

    world

    - World - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:16 -

    - - -
    - -
    -

    The world to search for collisions in.

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/Heightfield.html b/docs/classes/Heightfield.html deleted file mode 100644 index c3cd786f8..000000000 --- a/docs/classes/Heightfield.html +++ /dev/null @@ -1,1232 +0,0 @@ - - - - - Heightfield - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    Heightfield Class

    -
    - -
    - Extends Shape -
    - -
    - Defined in: src/shapes/Heightfield.js:8 -
    - - -
    - - -
    -

    Heightfield shape class. Height data is given as an array. These data points are spread out evenly with a given distance.

    - -
    - -
    -

    Constructor

    -
    -

    Heightfield

    - -
    - (
      -
    • - data -
    • -
    • - options -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/shapes/Heightfield.js:8 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - data - Array - - -
      -

      An array of Y values that will be used to construct the terrain.

      - -
      - -
    • -
    • - options - Object - - -
      - -
      - -
        -
      • - [minValue] - Number - optional - -
        -

        Minimum value of the data points in the data array. Will be computed automatically if not given.

        - -
        - -
      • -
      • - [maxValue] - Number - optional - -
        -

        Maximum value.

        - -
        - -
      • -
      • - [elementSize=0.1] - Number - optional - -
        -

        World spacing between the data points in X direction.

        - -
        - -
      • -
      -
    • -
    -
    - - - -
    -

    Example:

    - -
    -
    // Generate some height data (y-values).
    -        var data = [];
    -        for(var i = 0; i < 1000; i++){
    -            var y = 0.5 * Math.cos(0.2 * i);
    -            data.push(y);
    -        }
    -        
    -        // Create the heightfield shape
    -        var heightfieldShape = new Heightfield(data, {
    -            elementSize: 1 // Distance between the data points in X and Y directions
    -        });
    -        var heightfieldBody = new Body();
    -        heightfieldBody.addShape(heightfieldShape);
    -        world.addBody(heightfieldBody);
    -        
    -
    -
    -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    calculateLocalInertia

    - - () - - - Vec3 - - - - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:68 -

    - - - -
    - -
    -

    Calculates the inertia in the local frame for this shape.

    - -
    - - -
    -

    Returns:

    - -
    - Vec3: -
    -
    - - -
    -
    -

    getConvexTrianglePillar

    - -
    - (
      -
    • - i -
    • -
    • - j -
    • -
    • - getUpperTriangle -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/shapes/Heightfield.js:257 -

    - - - -
    - -
    -

    Get a triangle in the terrain in the form of a triangular convex shape.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - i - Integer - - -
      - -
      - -
    • -
    • - j - Integer - - -
      - -
      - -
    • -
    • - getUpperTriangle - Boolean - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    getIndexOfPosition

    - -
    - (
      -
    • - x -
    • -
    • - y -
    • -
    • - result -
    • -
    • - clamp -
    • -
    ) -
    - - - Boolean - - - - - - - - -
    -

    - Defined in - src/shapes/Heightfield.js:191 -

    - - - -
    - -
    -

    Get the index of a local position on the heightfield. The indexes indicate the rectangles, so if your terrain is made of N x N height data points, you will have rectangle indexes ranging from 0 to N-1.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - x - Number - - -
      - -
      - -
    • -
    • - y - Number - - -
      - -
      - -
    • -
    • - result - Array - - -
      -

      Two-element array

      - -
      - -
    • -
    • - clamp - Boolean - - -
      -

      If the position should be clamped to the heightfield edge.

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Boolean: -
    -
    - - -
    -
    -

    getRectMinMax

    - -
    - (
      -
    • - iMinX -
    • -
    • - iMinY -
    • -
    • - iMaxX -
    • -
    • - iMaxY -
    • -
    • - [result] -
    • -
    ) -
    - - - Array - - - - - - - - -
    -

    - Defined in - src/shapes/Heightfield.js:162 -

    - - - -
    - -
    -

    Get max/min in a rectangle in the matrix data

    - -
    - -
    -

    Parameters:

    - -
      -
    • - iMinX - Integer - - -
      - -
      - -
    • -
    • - iMinY - Integer - - -
      - -
      - -
    • -
    • - iMaxX - Integer - - -
      - -
      - -
    • -
    • - iMaxY - Integer - - -
      - -
      - -
    • -
    • - [result] - Array - optional - - -
      -

      An array to store the results in.

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Array: -

    The result array, if it was passed in. Minimum will be at position 0 and max at 1.

    - -
    -
    - - -
    -
    -

    setHeightValueAtIndex

    - -
    - (
      -
    • - xi -
    • -
    • - yi -
    • -
    • - value -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/shapes/Heightfield.js:136 -

    - - - -
    - -
    -

    Set the height value at an index. Don't forget to update maxValue and minValue after you're done.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - xi - Integer - - -
      - -
      - -
    • -
    • - yi - Integer - - -
      - -
      - -
    • -
    • - value - Number - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    update

    - - () - - - - - - - - -
    -

    - Defined in - src/shapes/Heightfield.js:92 -

    - - - -
    - -
    -

    Call whenever you change the data array.

    - -
    - - - - -
    -
    -

    updateBoundingSphereRadius

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:50 -

    - - - -
    - -
    -

    Computes the bounding sphere radius. The result is stored in the property .boundingSphereRadius

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    updateMaxValue

    - - () - - - - - - - - -
    -

    - Defined in - src/shapes/Heightfield.js:118 -

    - - - -
    - -
    -

    Update the .maxValue property

    - -
    - - - - -
    -
    -

    updateMinValue

    - - () - - - - - - - - -
    -

    - Defined in - src/shapes/Heightfield.js:100 -

    - - - -
    - -
    -

    Update the .minValue property

    - -
    - - - - -
    -
    -

    volume

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:59 -

    - - - -
    - -
    -

    Get the volume of this shape

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    - -
    -

    Properties

    - -
    -

    boundingSphereRadius

    - Number - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:31 -

    - - -
    - -
    -

    The local bounding sphere radius of this shape.

    - -
    - - - -
    -
    -

    collisionResponse

    - Boolean - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:37 -

    - - -
    - -
    -

    Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled.

    - -
    - - - -
    -
    -

    data

    - Array - - - - - -
    -

    - Defined in - src/shapes/Heightfield.js:43 -

    - - -
    - -
    -

    An array of numbers, or height values, that are spread out along the x axis.

    - -
    - - - -
    -
    -

    elementSize

    - Number - - - - - -
    -

    - Defined in - src/shapes/Heightfield.js:61 -

    - - -
    - -
    -

    The width of each element

    - -
    - - - -
    -
    -

    id

    - Number - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:17 -

    - - -
    - -
    -

    Identifyer of the Shape.

    - -
    - - - -
    -
    -

    material

    - Material - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:43 -

    - - -
    - -
    - -
    - - - -
    -
    -

    maxValue

    - Number - - - - - -
    -

    - Defined in - src/shapes/Heightfield.js:49 -

    - - -
    - -
    -

    Max value of the data

    - -
    - - - -
    -
    -

    minValue

    - Number - - - - - -
    -

    - Defined in - src/shapes/Heightfield.js:55 -

    - - -
    - -
    -

    Max value of the data

    - -
    - - - -
    -
    -

    type

    - Number - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:23 -

    - - -
    - -
    -

    The type of this shape. Must be set to an int > 0 by subclasses.

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/HingeConstraint.html b/docs/classes/HingeConstraint.html deleted file mode 100644 index 7121e6787..000000000 --- a/docs/classes/HingeConstraint.html +++ /dev/null @@ -1,1057 +0,0 @@ - - - - - HingeConstraint - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    HingeConstraint Class

    -
    - - - - - - -
    - - -
    -

    Hinge constraint. Think of it as a door hinge. It tries to keep the door in the correct place and with the correct orientation.

    - -
    - -
    -

    Constructor

    -
    -

    HingeConstraint

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    • - [options] -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/constraints/HingeConstraint.js:10 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    • - [options] - Object - optional - - -
      - -
      - -
        -
      • - [pivotA] - Vec3 - optional - -
        -

        A point defined locally in bodyA. This defines the offset of axisA.

        - -
        - -
      • -
      • - [axisA] - Vec3 - optional - -
        -

        An axis that bodyA can rotate around, defined locally in bodyA.

        - -
        - -
      • -
      • - [pivotB] - Vec3 - optional - -
        - -
        - -
      • -
      • - [axisB] - Vec3 - optional - -
        - -
        - -
      • -
      • - [maxForce=1e6] - Number - optional - -
        - -
        - -
      • -
      -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    disable

    - - () - - - - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:80 -

    - - - -
    - -
    -

    Disables all equations in the constraint.

    - -
    - - - - -
    -
    -

    disableMotor

    - - () - - - - - - - - -
    -

    - Defined in - src/constraints/HingeConstraint.js:80 -

    - - - -
    - -
    - -
    - - - - -
    -
    -

    enable

    - - () - - - - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:69 -

    - - - -
    - -
    -

    Enables all equations in the constraint.

    - -
    - - - - -
    -
    -

    enableMotor

    - - () - - - - - - - - -
    -

    - Defined in - src/constraints/HingeConstraint.js:73 -

    - - - -
    - -
    - -
    - - - - -
    -
    -

    setMotorMaxForce

    - -
    - (
      -
    • - maxForce -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/constraints/HingeConstraint.js:95 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - maxForce - Number - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    setMotorSpeed

    - -
    - (
      -
    • - speed -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/constraints/HingeConstraint.js:87 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - speed - Number - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    update

    - - () - - - - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:61 -

    - - - -
    - -
    -

    Update all the equations with data.

    - -
    - - - - -
    -
    - -
    -

    Properties

    - -
    -

    axisA

    - Vec3 - - - - - -
    -

    - Defined in - src/constraints/HingeConstraint.js:33 -

    - - -
    - -
    -

    Rotation axis, defined locally in bodyA.

    - -
    - - - -
    -
    -

    axisB

    - Vec3 - - - - - -
    -

    - Defined in - src/constraints/HingeConstraint.js:40 -

    - - -
    - -
    -

    Rotation axis, defined locally in bodyB.

    - -
    - - - -
    -
    -

    bodyA

    - Body - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:29 -

    - - -
    - -
    - -
    - - - -
    -
    -

    bodyB

    - Body - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:34 -

    - - -
    - -
    - -
    - - - -
    -
    -

    collideConnected

    - Boolean - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:44 -

    - - -
    - -
    -

    Set to true if you want the bodies to collide when they are connected.

    - -
    - - - -
    -
    -

    equations

    - Array - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:22 -

    - - -
    - -
    -

    Equations to be solved in this constraint

    - -
    - - - -
    -
    -

    equationX

    - ContactEquation - - - - - - - -
    - -
    - - - -
    -
    -

    equationY

    - ContactEquation - - - - - - - -
    - -
    - - - -
    -
    -

    equationZ

    - ContactEquation - - - - - - - -
    - -
    - - - -
    -
    -

    id

    - Number - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:39 -

    - - -
    - -
    - -
    - - - -
    -
    -

    motorEquation

    - RotationalMotorEquation - - - - - -
    -

    - Defined in - src/constraints/HingeConstraint.js:57 -

    - - -
    - -
    - -
    - - - -
    -
    -

    pivotA

    - Vec3 - - - - - - - -
    -

    Pivot, defined locally in bodyA.

    - -
    - - - -
    -
    -

    pivotB

    - Vec3 - - - - - - - -
    -

    Pivot, defined locally in bodyB.

    - -
    - - - -
    -
    -

    rotationalEquation1

    - RotationalEquation - - - - - -
    -

    - Defined in - src/constraints/HingeConstraint.js:47 -

    - - -
    - -
    - -
    - - - -
    -
    -

    rotationalEquation2

    - RotationalEquation - - - - - -
    -

    - Defined in - src/constraints/HingeConstraint.js:52 -

    - - -
    - -
    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/JacobianElement.html b/docs/classes/JacobianElement.html deleted file mode 100644 index bcafaf930..000000000 --- a/docs/classes/JacobianElement.html +++ /dev/null @@ -1,443 +0,0 @@ - - - - - JacobianElement - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    JacobianElement Class

    -
    - - - - - -
    - - -
    -

    An element containing 6 entries, 3 spatial and 3 rotational degrees of freedom.

    - -
    - -
    -

    Constructor

    -
    -

    JacobianElement

    - - () - - - - - - - - -
    -

    - Defined in - src/math/JacobianElement.js:5 -

    - - - -
    - -
    - -
    - - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    multiplyElement

    - -
    - (
      -
    • - element -
    • -
    ) -
    - - - Number - - - - - - - - -
    -

    - Defined in - src/math/JacobianElement.js:23 -

    - - - -
    - -
    -

    Multiply with other JacobianElement

    - -
    - -
    -

    Parameters:

    - - -
    - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    multiplyVectors

    - -
    - (
      -
    • - spatial -
    • -
    • - rotational -
    • -
    ) -
    - - - Number - - - - - - - - -
    -

    - Defined in - src/math/JacobianElement.js:33 -

    - - - -
    - -
    -

    Multiply with two vectors

    - -
    - -
    -

    Parameters:

    - -
      -
    • - spatial - Vec3 - - -
      - -
      - -
    • -
    • - rotational - Vec3 - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    - -
    -

    Properties

    - -
    -

    rotational

    - Vec3 - - - - - -
    -

    - Defined in - src/math/JacobianElement.js:17 -

    - - -
    - -
    - -
    - - - -
    -
    -

    spatial

    - Vec3 - - - - - -
    -

    - Defined in - src/math/JacobianElement.js:12 -

    - - -
    - -
    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/LockConstraint.html b/docs/classes/LockConstraint.html deleted file mode 100644 index 1400d9a68..000000000 --- a/docs/classes/LockConstraint.html +++ /dev/null @@ -1,777 +0,0 @@ - - - - - LockConstraint - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    LockConstraint Class

    -
    - - - - - - -
    - - -
    -

    Lock constraint. Will remove all degrees of freedom between the bodies.

    - -
    - -
    -

    Constructor

    -
    -

    LockConstraint

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    • - [options] -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/constraints/LockConstraint.js:10 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    • - [options] - Object - optional - - -
      - -
      - -
        -
      • - [maxForce=1e6] - Number - optional - -
        - -
        - -
      • -
      -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    disable

    - - () - - - - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:80 -

    - - - -
    - -
    -

    Disables all equations in the constraint.

    - -
    - - - - -
    -
    -

    enable

    - - () - - - - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:69 -

    - - - -
    - -
    -

    Enables all equations in the constraint.

    - -
    - - - - -
    -
    -

    update

    - - () - - - - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:61 -

    - - - -
    - -
    -

    Update all the equations with data.

    - -
    - - - - -
    -
    - -
    -

    Properties

    - -
    -

    bodyA

    - Body - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:29 -

    - - -
    - -
    - -
    - - - -
    -
    -

    bodyB

    - Body - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:34 -

    - - -
    - -
    - -
    - - - -
    -
    -

    collideConnected

    - Boolean - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:44 -

    - - -
    - -
    -

    Set to true if you want the bodies to collide when they are connected.

    - -
    - - - -
    -
    -

    equations

    - Array - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:22 -

    - - -
    - -
    -

    Equations to be solved in this constraint

    - -
    - - - -
    -
    -

    equationX

    - ContactEquation - - - - - - - -
    - -
    - - - -
    -
    -

    equationY

    - ContactEquation - - - - - - - -
    - -
    - - - -
    -
    -

    equationZ

    - ContactEquation - - - - - - - -
    - -
    - - - -
    -
    -

    id

    - Number - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:39 -

    - - -
    - -
    - -
    - - - -
    -
    -

    pivotA

    - Vec3 - - - - - - - -
    -

    Pivot, defined locally in bodyA.

    - -
    - - - -
    -
    -

    pivotB

    - Vec3 - - - - - - - -
    -

    Pivot, defined locally in bodyB.

    - -
    - - - -
    -
    -

    rotationalEquation1

    - RotationalEquation - - - - - -
    -

    - Defined in - src/constraints/LockConstraint.js:35 -

    - - -
    - -
    - -
    - - - -
    -
    -

    rotationalEquation2

    - RotationalEquation - - - - - -
    -

    - Defined in - src/constraints/LockConstraint.js:40 -

    - - -
    - -
    - -
    - - - -
    -
    -

    rotationalEquation3

    - RotationalEquation - - - - - -
    -

    - Defined in - src/constraints/LockConstraint.js:45 -

    - - -
    - -
    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/Mat3.html b/docs/classes/Mat3.html deleted file mode 100644 index 36d82d63a..000000000 --- a/docs/classes/Mat3.html +++ /dev/null @@ -1,1222 +0,0 @@ - - - - - Mat3 - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    Mat3 Class

    -
    - - -
    - Defined in: src/math/Mat3.js:5 -
    - - -
    - - -
    -

    A 3x3 matrix.

    - -
    - -
    -

    Constructor

    -
    -

    Mat3

    - -
    - (
      -
    • - array -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/math/Mat3.js:5 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - array - Object - - -
      -

      elements Array of nine elements. Optional.

      - -
      - -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    copy

    - -
    - (
      -
    • - source -
    • -
    ) -
    - - - Mat3 - - - - - - - - -
    -

    - Defined in - src/math/Mat3.js:244 -

    - - - -
    - -
    -

    Copy another matrix into this matrix object.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - source - Mat3 - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Mat3: -

    this

    - -
    -
    - - -
    -
    -

    e

    - -
    - (
      -
    • - row -
    • -
    • - column -
    • -
    • - value -
    • -
    ) -
    - - - Number - - - - - - - - -
    -

    - Defined in - src/math/Mat3.js:227 -

    - - - -
    - -
    -

    Get an element in the matrix by index. Index starts at 0, not 1!!!

    - -
    - -
    -

    Parameters:

    - -
      -
    • - row - Number - - -
      - -
      - -
    • -
    • - column - Number - - -
      - -
      - -
    • -
    • - value - Number - - -
      -

      Optional. If provided, the matrix element will be set to this value.

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    getTrace

    - - () - - - Vec3 - - - - - - - - -
    -

    - Defined in - src/math/Mat3.js:74 -

    - - - -
    - -
    -

    Gets the matrix diagonal elements

    - -
    - - -
    -

    Returns:

    - -
    - Vec3: -
    -
    - - -
    -
    -

    identity

    - - () - - - - - - - - -
    -

    - Defined in - src/math/Mat3.js:24 -

    - - - -
    - -
    -

    Sets the matrix to identity

    - -
    - - - - -
    -
    -

    mmult

    - -
    - (
      -
    • - m -
    • -
    ) -
    - - - Mat3 - - - - - - - - -
    -

    - Defined in - src/math/Mat3.js:118 -

    - - - -
    - -
    -

    Matrix multiplication

    - -
    - -
    -

    Parameters:

    - -
      -
    • - m - Mat3 - - -
      -

      Matrix to multiply with from left side.

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Mat3: -

    The result.

    - -
    -
    - - -
    -
    -

    reverse

    - -
    - (
      -
    • - target -
    • -
    ) -
    - - - Mat3 - - - - - - - - -
    -

    - Defined in - src/math/Mat3.js:271 -

    - - - -
    - -
    -

    reverse the matrix

    - -
    - -
    -

    Parameters:

    - -
      -
    • - target - Mat3 - - -
      -

      Optional. Target matrix to save in.

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Mat3: -

    The solution x

    - -
    -
    - - -
    -
    -

    scale

    - -
    - (
      -
    • - v -
    • -
    ) -
    - - - Mat3 - - - - - - - - -
    -

    - Defined in - src/math/Mat3.js:138 -

    - - - -
    - -
    -

    Scale each column of the matrix

    - -
    - -
    -

    Parameters:

    - -
      -
    • - v - Vec3 - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Mat3: -

    The result.

    - -
    -
    - - -
    -
    -

    setRotationFromQuaternion

    - -
    - (
      -
    • - q -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/math/Mat3.js:375 -

    - - - -
    - -
    -

    Set the matrix from a quaterion

    - -
    - -
    -

    Parameters:

    - - -
    - - - -
    -
    -

    setTrace

    - -
    - (
      -
    • - vec3 -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/math/Mat3.js:62 -

    - - - -
    - -
    -

    Sets the matrix diagonal elements from a Vec3

    - -
    - -
    -

    Parameters:

    - -
      -
    • - vec3 - Vec3 - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    setZero

    - - () - - - - - - - - -
    -

    - Defined in - src/math/Mat3.js:45 -

    - - - -
    - -
    -

    Set all elements to zero

    - -
    - - - - -
    -
    -

    smult

    - -
    - (
      -
    • - s -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/math/Mat3.js:107 -

    - - - -
    - -
    -

    Matrix-scalar multiplication

    - -
    - -
    -

    Parameters:

    - -
      -
    • - s - Number - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    solve

    - -
    - (
      -
    • - b -
    • -
    • - target -
    • -
    ) -
    - - - Vec3 - - - - - - - - -
    -

    - Defined in - src/math/Mat3.js:156 -

    - - - -
    - -
    -

    Solve Ax=b

    - -
    - -
    -

    Parameters:

    - -
      -
    • - b - Vec3 - - -
      -

      The right hand side

      - -
      - -
    • -
    • - target - Vec3 - - -
      -

      Optional. Target vector to save in.

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Vec3: -

    The solution x

    - -
    -
    - - -
    -
    -

    toString

    - - () - - - - - - - - - - - -
    -

    - Defined in - src/math/Mat3.js:257 -

    - - - -
    - -
    -

    Returns a string representation of the matrix.

    - -
    - - -
    -

    Returns:

    - -
    -

    string

    - -
    -
    - - -
    -
    -

    transpose

    - -
    - (
      -
    • - target -
    • -
    ) -
    - - - Mat3 - - - - - - - - -
    -

    - Defined in - src/math/Mat3.js:403 -

    - - - -
    - -
    -

    Transpose the matrix

    - -
    - -
    -

    Parameters:

    - -
      -
    • - target - Mat3 - - -
      -

      Where to store the result.

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Mat3: -

    The target Mat3, or a new Mat3 if target was omitted.

    - -
    -
    - - -
    -
    -

    vmult

    - -
    - (
      -
    • - v -
    • -
    • - target -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/math/Mat3.js:87 -

    - - - -
    - -
    -

    Matrix-Vector multiplication

    - -
    - -
    -

    Parameters:

    - -
      -
    • - v - Vec3 - - -
      -

      The vector to multiply with

      - -
      - -
    • -
    • - target - Vec3 - - -
      -

      Optional, target to save the result in.

      - -
      - -
    • -
    -
    - - - -
    -
    - -
    -

    Properties

    - -
    -

    elements

    - Array - - - - - -
    -

    - Defined in - src/math/Mat3.js:13 -

    - - -
    - -
    -

    A vector of length 9, containing all matrix elements

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/Material.html b/docs/classes/Material.html deleted file mode 100644 index e8915d876..000000000 --- a/docs/classes/Material.html +++ /dev/null @@ -1,367 +0,0 @@ - - - - - Material - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    Material Class

    -
    - - -
    - Defined in: src/material/Material.js:3 -
    - - -
    - - -
    -

    Defines a physics material.

    - -
    - -
    -

    Constructor

    -
    -

    Material

    - -
    - (
      -
    • - [options] -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/material/Material.js:3 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - [options] - Object - optional - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - - -
    -

    Properties

    - - -
    - - -
    - - -
    -

    Properties

    - -
    -

    friction

    - Number - - - - - -
    -

    - Defined in - src/material/Material.js:35 -

    - - -
    - -
    -

    Friction for this material. If non-negative, it will be used instead of the friction given by ContactMaterials. If there's no matching ContactMaterial, the value from .defaultContactMaterial in the World will be used.

    - -
    - - - -
    -
    -

    id

    - Number - - - - - -
    -

    - Defined in - src/material/Material.js:28 -

    - - -
    - -
    -

    material id.

    - -
    - - - -
    -
    -

    name

    - String - - - - - -
    -

    - Defined in - src/material/Material.js:22 -

    - - -
    - -
    - -
    - - - -
    -
    -

    restitution

    - Number - - - - - -
    -

    - Defined in - src/material/Material.js:41 -

    - - -
    - -
    -

    Restitution for this material. If non-negative, it will be used instead of the restitution given by ContactMaterials. If there's no matching ContactMaterial, the value from .defaultContactMaterial in the World will be used.

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/NaiveBroadphase.html b/docs/classes/NaiveBroadphase.html deleted file mode 100644 index 248cf81fd..000000000 --- a/docs/classes/NaiveBroadphase.html +++ /dev/null @@ -1,1090 +0,0 @@ - - - - - NaiveBroadphase - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    NaiveBroadphase Class

    -
    - -
    - Extends Broadphase -
    - - - - -
    - - -
    -

    Naive broadphase implementation, used in lack of better ones.

    - -
    - -
    -

    Constructor

    -
    -

    NaiveBroadphase

    - - () - - - - - - - - -
    -

    - Defined in - src/collision/NaiveBroadphase.js:6 -

    - - - -
    - -
    - -
    - - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - - - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    aabbQuery

    - -
    - (
      -
    • - world -
    • -
    • - aabb -
    • -
    • - result -
    • -
    ) -
    - - - Array - - - - - - - - -
    -

    Inherited from - - Broadphase - - but overwritten in - src/collision/NaiveBroadphase.js:49 -

    - - - -
    - -
    -

    Returns all the bodies within an AABB.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - world - World - - -
      - -
      - -
    • -
    • - aabb - AABB - - -
      - -
      - -
    • -
    • - result - Array - - -
      -

      An array to store resulting bodies in.

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Array: -
    -
    - - -
    -
    -

    boundingSphereCheck

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    ) -
    - - - Boolean - - - - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:183 -

    - - - -
    - -
    -

    Check if the bounding spheres of two bodies overlap.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Boolean: -
    -
    - - -
    -
    -

    collisionPairs

    - -
    - (
      -
    • - world -
    • -
    • - pairs1 -
    • -
    • - pairs2 -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - - Broadphase - - but overwritten in - src/collision/NaiveBroadphase.js:19 -

    - - - -
    - -
    -

    Get all the collision pairs in the physics world

    - -
    - -
    -

    Parameters:

    - -
      -
    • - world - World - - -
      - -
      - -
    • -
    • - pairs1 - Array - - -
      - -
      - -
    • -
    • - pairs2 - Array - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    doBoundingBoxBroadphase

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    • - pairs1 -
    • -
    • - pairs2 -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:112 -

    - - - -
    - -
    -

    Check if the bounding boxes of two bodies are intersecting.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    • - pairs1 - Array - - -
      - -
      - -
    • -
    • - pairs2 - Array - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    doBoundingSphereBroadphase

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    • - pairs1 -
    • -
    • - pairs2 -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:89 -

    - - - -
    - -
    -

    Check if the bounding spheres of two bodies are intersecting.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    • - pairs1 - Array - - -
      -

      bodyA is appended to this array if intersection

      - -
      - -
    • -
    • - pairs2 - Array - - -
      -

      bodyB is appended to this array if intersection

      - -
      - -
    • -
    -
    - - - -
    -
    -

    intersectionTest

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    • - pairs1 -
    • -
    • - pairs2 -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:73 -

    - - - -
    - -
    -

    Check if the bounding volumes of two bodies intersect.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    • - pairs1 - Array - - -
      - -
      - -
    • -
    • - pairs2 - Array - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    makePairsUnique

    - -
    - (
      -
    • - pairs1 -
    • -
    • - pairs2 -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:135 -

    - - - -
    - -
    -

    Removes duplicate pairs from the pair arrays.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - pairs1 - Array - - -
      - -
      - -
    • -
    • - pairs2 - Array - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    needBroadphaseCollision

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    ) -
    - - - Bool - - - - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:48 -

    - - - -
    - -
    -

    Check if a body pair needs to be intersection tested at all.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Bool: -
    -
    - - -
    -
    -

    setWorld

    - -
    - (
      -
    • - world -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:175 -

    - - - -
    - -
    -

    To be implemented by subcasses

    - -
    - -
    -

    Parameters:

    - -
      -
    • - world - World - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    -

    Properties

    - -
    -

    dirty

    - Boolean - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:30 -

    - - -
    - -
    -

    Set to true if the objects in the world moved.

    - -
    - - - -
    -
    -

    useBoundingBoxes

    - Boolean - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:23 -

    - - -
    - -
    -

    If set to true, the broadphase uses bounding boxes for intersection test, else it uses bounding spheres.

    - -
    - - - -
    -
    -

    world

    - World - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:16 -

    - - -
    - -
    -

    The world to search for collisions in.

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/Narrowphase.html b/docs/classes/Narrowphase.html deleted file mode 100644 index 98bc47c08..000000000 --- a/docs/classes/Narrowphase.html +++ /dev/null @@ -1,2528 +0,0 @@ - - - - - Narrowphase - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    Narrowphase Class

    -
    - - -
    - Defined in: src/world/Narrowphase.js:15 -
    - - -
    - - -
    -

    Helper class for the World. Generates ContactEquations.

    - -
    - -
    -

    Constructor

    -
    -

    Narrowphase

    - - () - - - - - - - - -
    -

    - Defined in - src/world/Narrowphase.js:15 -

    - - - -
    - -
    - -
    - - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    convexConvex

    - -
    - (
      -
    • - si -
    • -
    • - sj -
    • -
    • - xi -
    • -
    • - xj -
    • -
    • - qi -
    • -
    • - qj -
    • -
    • - bi -
    • -
    • - bj -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/world/Narrowphase.js:1252 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - si - Shape - - -
      - -
      - -
    • -
    • - sj - Shape - - -
      - -
      - -
    • -
    • - xi - Vec3 - - -
      - -
      - -
    • -
    • - xj - Vec3 - - -
      - -
      - -
    • -
    • - qi - Quaternion - - -
      - -
      - -
    • -
    • - qj - Quaternion - - -
      - -
      - -
    • -
    • - bi - Body - - -
      - -
      - -
    • -
    • - bj - Body - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    convexHeightfield

    - - () - - - - - - - - -
    -

    - Defined in - src/world/Narrowphase.js:1573 -

    - - - -
    - -
    - -
    - - - - -
    -
    -

    convexParticle

    - -
    - (
      -
    • - result -
    • -
    • - si -
    • -
    • - sj -
    • -
    • - xi -
    • -
    • - xj -
    • -
    • - qi -
    • -
    • - qj -
    • -
    • - bi -
    • -
    • - bj -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/world/Narrowphase.js:1475 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - result - Array - - -
      - -
      - -
    • -
    • - si - Shape - - -
      - -
      - -
    • -
    • - sj - Shape - - -
      - -
      - -
    • -
    • - xi - Vec3 - - -
      - -
      - -
    • -
    • - xj - Vec3 - - -
      - -
      - -
    • -
    • - qi - Quaternion - - -
      - -
      - -
    • -
    • - qj - Quaternion - - -
      - -
      - -
    • -
    • - bi - Body - - -
      - -
      - -
    • -
    • - bj - Body - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    convexTrimesh

    - -
    - (
      -
    • - result -
    • -
    • - si -
    • -
    • - sj -
    • -
    • - xi -
    • -
    • - xj -
    • -
    • - qi -
    • -
    • - qj -
    • -
    • - bi -
    • -
    • - bj -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/world/Narrowphase.js:1309 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - result - Array - - -
      - -
      - -
    • -
    • - si - Shape - - -
      - -
      - -
    • -
    • - sj - Shape - - -
      - -
      - -
    • -
    • - xi - Vec3 - - -
      - -
      - -
    • -
    • - xj - Vec3 - - -
      - -
      - -
    • -
    • - qi - Quaternion - - -
      - -
      - -
    • -
    • - qj - Quaternion - - -
      - -
      - -
    • -
    • - bi - Body - - -
      - -
      - -
    • -
    • - bj - Body - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    createContactEquation

    - - () - - - ContactEquation - - - - - - - - -
    -

    - Defined in - src/world/Narrowphase.js:51 -

    - - - -
    - -
    -

    Make a contact object, by using the internal pool or creating a new one.

    - -
    - - -
    -

    Returns:

    - - -
    - - -
    -
    -

    getContacts

    - -
    - (
      -
    • - p1 -
    • -
    • - p2 -
    • -
    • - world -
    • -
    • - result -
    • -
    • - oldcontacts -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/world/Narrowphase.js:199 -

    - - - -
    - -
    -

    Generate all contacts between a list of body pairs

    - -
    - -
    -

    Parameters:

    - -
      -
    • - p1 - Array - - -
      -

      Array of body indices

      - -
      - -
    • -
    • - p2 - Array - - -
      -

      Array of body indices

      - -
      - -
    • -
    • - world - World - - -
      - -
      - -
    • -
    • - result - Array - - -
      -

      Array to store generated contacts

      - -
      - -
    • -
    • - oldcontacts - Array - - -
      -

      Optional. Array of reusable contact objects

      - -
      - -
    • -
    -
    - - - -
    -
    -

    particlePlane

    - -
    - (
      -
    • - result -
    • -
    • - si -
    • -
    • - sj -
    • -
    • - xi -
    • -
    • - xj -
    • -
    • - qi -
    • -
    • - qj -
    • -
    • - bi -
    • -
    • - bj -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/world/Narrowphase.js:1393 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - result - Array - - -
      - -
      - -
    • -
    • - si - Shape - - -
      - -
      - -
    • -
    • - sj - Shape - - -
      - -
      - -
    • -
    • - xi - Vec3 - - -
      - -
      - -
    • -
    • - xj - Vec3 - - -
      - -
      - -
    • -
    • - qi - Quaternion - - -
      - -
      - -
    • -
    • - qj - Quaternion - - -
      - -
      - -
    • -
    • - bi - Body - - -
      - -
      - -
    • -
    • - bj - Body - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    particleSphere

    - -
    - (
      -
    • - result -
    • -
    • - si -
    • -
    • - sj -
    • -
    • - xi -
    • -
    • - xj -
    • -
    • - qi -
    • -
    • - qj -
    • -
    • - bi -
    • -
    • - bj -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/world/Narrowphase.js:1434 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - result - Array - - -
      - -
      - -
    • -
    • - si - Shape - - -
      - -
      - -
    • -
    • - sj - Shape - - -
      - -
      - -
    • -
    • - xi - Vec3 - - -
      - -
      - -
    • -
    • - xj - Vec3 - - -
      - -
      - -
    • -
    • - qi - Quaternion - - -
      - -
      - -
    • -
    • - qj - Quaternion - - -
      - -
      - -
    • -
    • - bi - Body - - -
      - -
      - -
    • -
    • - bj - Body - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    planeBox

    - -
    - (
      -
    • - result -
    • -
    • - si -
    • -
    • - sj -
    • -
    • - xi -
    • -
    • - xj -
    • -
    • - qi -
    • -
    • - qj -
    • -
    • - bi -
    • -
    • - bj -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/world/Narrowphase.js:1152 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - result - Array - - -
      - -
      - -
    • -
    • - si - Shape - - -
      - -
      - -
    • -
    • - sj - Shape - - -
      - -
      - -
    • -
    • - xi - Vec3 - - -
      - -
      - -
    • -
    • - xj - Vec3 - - -
      - -
      - -
    • -
    • - qi - Quaternion - - -
      - -
      - -
    • -
    • - qj - Quaternion - - -
      - -
      - -
    • -
    • - bi - Body - - -
      - -
      - -
    • -
    • - bj - Body - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    planeConvex

    - -
    - (
      -
    • - si -
    • -
    • - sj -
    • -
    • - xi -
    • -
    • - xj -
    • -
    • - qi -
    • -
    • - qj -
    • -
    • - bi -
    • -
    • - bj -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/world/Narrowphase.js:1176 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - si - Shape - - -
      - -
      - -
    • -
    • - sj - Shape - - -
      - -
      - -
    • -
    • - xi - Vec3 - - -
      - -
      - -
    • -
    • - xj - Vec3 - - -
      - -
      - -
    • -
    • - qi - Quaternion - - -
      - -
      - -
    • -
    • - qj - Quaternion - - -
      - -
      - -
    • -
    • - bi - Body - - -
      - -
      - -
    • -
    • - bj - Body - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    planeTrimesh

    - -
    - (
      -
    • - si -
    • -
    • - sj -
    • -
    • - xi -
    • -
    • - xj -
    • -
    • - qi -
    • -
    • - qj -
    • -
    • - bi -
    • -
    • - bj -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/world/Narrowphase.js:345 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - si - Shape - - -
      - -
      - -
    • -
    • - sj - Shape - - -
      - -
      - -
    • -
    • - xi - Vec3 - - -
      - -
      - -
    • -
    • - xj - Vec3 - - -
      - -
      - -
    • -
    • - qi - Quaternion - - -
      - -
      - -
    • -
    • - qj - Quaternion - - -
      - -
      - -
    • -
    • - bi - Body - - -
      - -
      - -
    • -
    • - bj - Body - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    sphereBox

    - -
    - (
      -
    • - si -
    • -
    • - sj -
    • -
    • - xi -
    • -
    • - xj -
    • -
    • - qi -
    • -
    • - qj -
    • -
    • - bi -
    • -
    • - bj -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/world/Narrowphase.js:708 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - si - Shape - - -
      - -
      - -
    • -
    • - sj - Shape - - -
      - -
      - -
    • -
    • - xi - Vec3 - - -
      - -
      - -
    • -
    • - xj - Vec3 - - -
      - -
      - -
    • -
    • - qi - Quaternion - - -
      - -
      - -
    • -
    • - qj - Quaternion - - -
      - -
      - -
    • -
    • - bi - Body - - -
      - -
      - -
    • -
    • - bj - Body - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    sphereConvex

    - -
    - (
      -
    • - si -
    • -
    • - sj -
    • -
    • - xi -
    • -
    • - xj -
    • -
    • - qi -
    • -
    • - qj -
    • -
    • - bi -
    • -
    • - bj -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/world/Narrowphase.js:929 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - si - Shape - - -
      - -
      - -
    • -
    • - sj - Shape - - -
      - -
      - -
    • -
    • - xi - Vec3 - - -
      - -
      - -
    • -
    • - xj - Vec3 - - -
      - -
      - -
    • -
    • - qi - Quaternion - - -
      - -
      - -
    • -
    • - qj - Quaternion - - -
      - -
      - -
    • -
    • - bi - Body - - -
      - -
      - -
    • -
    • - bj - Body - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    sphereHeightfield

    - - () - - - - - - - - -
    -

    - Defined in - src/world/Narrowphase.js:1651 -

    - - - -
    - -
    - -
    - - - - -
    -
    -

    spherePlane

    - -
    - (
      -
    • - si -
    • -
    • - sj -
    • -
    • - xi -
    • -
    • - xj -
    • -
    • - qi -
    • -
    • - qj -
    • -
    • - bi -
    • -
    • - bj -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/world/Narrowphase.js:612 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - si - Shape - - -
      - -
      - -
    • -
    • - sj - Shape - - -
      - -
      - -
    • -
    • - xi - Vec3 - - -
      - -
      - -
    • -
    • - xj - Vec3 - - -
      - -
      - -
    • -
    • - qi - Quaternion - - -
      - -
      - -
    • -
    • - qj - Quaternion - - -
      - -
      - -
    • -
    • - bi - Body - - -
      - -
      - -
    • -
    • - bj - Body - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    sphereSphere

    - -
    - (
      -
    • - si -
    • -
    • - sj -
    • -
    • - xi -
    • -
    • - xj -
    • -
    • - qi -
    • -
    • - qj -
    • -
    • - bi -
    • -
    • - bj -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/world/Narrowphase.js:308 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - si - Shape - - -
      - -
      - -
    • -
    • - sj - Shape - - -
      - -
      - -
    • -
    • - xi - Vec3 - - -
      - -
      - -
    • -
    • - xj - Vec3 - - -
      - -
      - -
    • -
    • - qi - Quaternion - - -
      - -
      - -
    • -
    • - qj - Quaternion - - -
      - -
      - -
    • -
    • - bi - Body - - -
      - -
      - -
    • -
    • - bj - Body - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    sphereTrimesh

    - -
    - (
      -
    • - sphereShape -
    • -
    • - trimeshShape -
    • -
    • - spherePos -
    • -
    • - trimeshPos -
    • -
    • - sphereQuat -
    • -
    • - trimeshQuat -
    • -
    • - sphereBody -
    • -
    • - trimeshBody -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/world/Narrowphase.js:416 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - sphereShape - Shape - - -
      - -
      - -
    • -
    • - trimeshShape - Shape - - -
      - -
      - -
    • -
    • - spherePos - Vec3 - - -
      - -
      - -
    • -
    • - trimeshPos - Vec3 - - -
      - -
      - -
    • -
    • - sphereQuat - Quaternion - - -
      - -
      - -
    • -
    • - trimeshQuat - Quaternion - - -
      - -
      - -
    • -
    • - sphereBody - Body - - -
      - -
      - -
    • -
    • - trimeshBody - Body - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    -

    Properties

    - -
    -

    contactPointPool

    - Array - - - - - -
    -

    - Defined in - src/world/Narrowphase.js:25 -

    - - -
    - -
    -

    Internal storage of pooled contact points.

    - -
    - - - -
    -
    -

    enableFrictionReduction

    - Boolean - - - - - -
    -

    - Defined in - src/world/Narrowphase.js:45 -

    - - -
    - -
    - -
    - - - -
    -
    -

    v3pool

    - Vec3Pool - - - - - -
    -

    - Defined in - src/world/Narrowphase.js:36 -

    - - -
    - -
    -

    Pooled vectors.

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/ObjectCollisionMatrix.html b/docs/classes/ObjectCollisionMatrix.html deleted file mode 100644 index 61b7c2f18..000000000 --- a/docs/classes/ObjectCollisionMatrix.html +++ /dev/null @@ -1,522 +0,0 @@ - - - - - ObjectCollisionMatrix - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    ObjectCollisionMatrix Class

    -
    - - - - - -
    - - -
    -

    Records what objects are colliding with each other

    - -
    - -
    -

    Constructor

    -
    -

    ObjectCollisionMatrix

    - - () - - - - - - - - -
    -

    - Defined in - src/collision/ObjectCollisionMatrix.js:3 -

    - - - -
    - -
    - -
    - - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    get

    - -
    - (
      -
    • - i -
    • -
    • - j -
    • -
    ) -
    - - - Number - - - - - - - - -
    -

    - Defined in - src/collision/ObjectCollisionMatrix.js:18 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - i - Number - - -
      - -
      - -
    • -
    • - j - Number - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    reset

    - - () - - - - - - - - -
    -

    - Defined in - src/collision/ObjectCollisionMatrix.js:57 -

    - - - -
    - -
    -

    Empty the matrix

    - -
    - - - - -
    -
    -

    set

    - -
    - (
      -
    • - i -
    • -
    • - j -
    • -
    • - value -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/collision/ObjectCollisionMatrix.js:35 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - i - Number - - -
      - -
      - -
    • -
    • - j - Number - - -
      - -
      - -
    • -
    • - value - Number - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    setNumObjects

    - -
    - (
      -
    • - n -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/collision/ObjectCollisionMatrix.js:65 -

    - - - -
    - -
    -

    Set max number of objects

    - -
    - -
    -

    Parameters:

    - -
      -
    • - n - Number - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    -

    Properties

    - -
    -

    matrix

    - Object - - - - - - - -
    -

    The matrix storage

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/Octree.html b/docs/classes/Octree.html deleted file mode 100644 index e05d3acc6..000000000 --- a/docs/classes/Octree.html +++ /dev/null @@ -1,682 +0,0 @@ - - - - - Octree - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    Octree Class

    -
    - -
    - Extends OctreeNode -
    - -
    - Defined in: src/utils/Octree.js:40 -
    - - -
    - - -
    - -
    - - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    aabbQuery

    - -
    - (
      -
    • - aabb -
    • -
    • - result -
    • -
    ) -
    - - - Array - - - - - - - - -
    -

    - Defined in - src/utils/Octree.js:158 -

    - - - -
    - -
    -

    Get all data, potentially within an AABB

    - -
    - -
    -

    Parameters:

    - -
      -
    • - aabb - AABB - - -
      - -
      - -
    • -
    • - result - Array - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Array: -

    The "result" object

    - -
    -
    - - -
    -
    -

    insert

    - -
    - (
      -
    • - aabb -
    • -
    • - elementData -
    • -
    ) -
    - - - Boolean - - - - - - - - -
    -

    - Defined in - src/utils/Octree.js:65 -

    - - - -
    - -
    -

    Insert data into this node

    - -
    - -
    -

    Parameters:

    - -
      -
    • - aabb - AABB - - -
      - -
      - -
    • -
    • - elementData - Object - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Boolean: -

    True if successful, otherwise false

    - -
    -
    - - -
    -
    -

    rayQuery

    - -
    - (
      -
    • - ray -
    • -
    • - treeTransform -
    • -
    • - result -
    • -
    ) -
    - - - Array - - - - - - - - -
    -

    - Defined in - src/utils/Octree.js:200 -

    - - - -
    - -
    -

    Get all data, potentially intersected by a ray.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - ray - Ray - - -
      - -
      - -
    • -
    • - treeTransform - Transform - - -
      - -
      - -
    • -
    • - result - Array - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Array: -

    The "result" object

    - -
    -
    - - -
    -
    -

    removeEmptyNodes

    - - () - - - - - - - - -
    -

    - Defined in - src/utils/Octree.js:219 -

    - - - -
    - -
    - -
    - - - - -
    -
    -

    subdivide

    - - () - - - - - - - - -
    -

    - Defined in - src/utils/Octree.js:112 -

    - - - -
    - -
    -

    Create 8 equally sized children nodes and put them in the .children array.

    - -
    - - - - -
    -
    - -
    -

    Properties

    - -
    -

    aabb

    - AABB - - - - - -
    -

    Inherited from - OctreeNode: - src/utils/Octree.js:21 -

    - - -
    - -
    -

    Boundary of this node

    - -
    - - - -
    -
    -

    children

    - Array - - - - - -
    -

    Inherited from - OctreeNode: - src/utils/Octree.js:33 -

    - - -
    - -
    -

    Children to this node

    - -
    - - - -
    -
    -

    data

    - Array - - - - - -
    -

    Inherited from - OctreeNode: - src/utils/Octree.js:27 -

    - - -
    - -
    -

    Contained data at the current node level.

    - -
    - - - -
    -
    -

    maxDepth

    - Number - - - - - -
    -

    - Defined in - src/utils/Octree.js:53 -

    - - -
    - -
    -

    Maximum subdivision depth

    - -
    - - - -
    -
    -

    root

    - OctreeNode - - - - - -
    -

    Inherited from - OctreeNode: - src/utils/Octree.js:15 -

    - - -
    - -
    -

    The root node

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/OctreeNode.html b/docs/classes/OctreeNode.html deleted file mode 100644 index 5f3b2da4c..000000000 --- a/docs/classes/OctreeNode.html +++ /dev/null @@ -1,311 +0,0 @@ - - - - - OctreeNode - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    OctreeNode Class

    -
    - - -
    - Defined in: src/utils/Octree.js:6 -
    - - -
    - - -
    - -
    - - -
    - - -
    -
    -

    Item Index

    - - -
    -

    Properties

    - - -
    - - -
    - - -
    -

    Properties

    - -
    -

    aabb

    - AABB - - - - - -
    -

    - Defined in - src/utils/Octree.js:21 -

    - - -
    - -
    -

    Boundary of this node

    - -
    - - - -
    -
    -

    children

    - Array - - - - - -
    -

    - Defined in - src/utils/Octree.js:33 -

    - - -
    - -
    -

    Children to this node

    - -
    - - - -
    -
    -

    data

    - Array - - - - - -
    -

    - Defined in - src/utils/Octree.js:27 -

    - - -
    - -
    -

    Contained data at the current node level.

    - -
    - - - -
    -
    -

    root

    - OctreeNode - - - - - -
    -

    - Defined in - src/utils/Octree.js:15 -

    - - -
    - -
    -

    The root node

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/Particle.html b/docs/classes/Particle.html deleted file mode 100644 index eeda31a61..000000000 --- a/docs/classes/Particle.html +++ /dev/null @@ -1,559 +0,0 @@ - - - - - Particle - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    Particle Class

    -
    - -
    - Extends Shape -
    - -
    - Defined in: src/shapes/Particle.js:6 -
    - - -
    - - -
    -

    Particle shape.

    - -
    - -
    -

    Constructor

    -
    -

    Particle

    - - () - - - - - - - - -
    -

    - Defined in - src/shapes/Particle.js:6 -

    - - - -
    - -
    - -
    - - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    calculateLocalInertia

    - -
    - (
      -
    • - mass -
    • -
    • - target -
    • -
    ) -
    - - - Vec3 - - - - - - - - -
    -

    Inherited from - - Shape - - but overwritten in - src/shapes/Particle.js:21 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - mass - Number - - -
      - -
      - -
    • -
    • - target - Vec3 - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Vec3: -
    -
    - - -
    -
    -

    updateBoundingSphereRadius

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:50 -

    - - - -
    - -
    -

    Computes the bounding sphere radius. The result is stored in the property .boundingSphereRadius

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    volume

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:59 -

    - - - -
    - -
    -

    Get the volume of this shape

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    - -
    -

    Properties

    - -
    -

    boundingSphereRadius

    - Number - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:31 -

    - - -
    - -
    -

    The local bounding sphere radius of this shape.

    - -
    - - - -
    -
    -

    collisionResponse

    - Boolean - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:37 -

    - - -
    - -
    -

    Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled.

    - -
    - - - -
    -
    -

    id

    - Number - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:17 -

    - - -
    - -
    -

    Identifyer of the Shape.

    - -
    - - - -
    -
    -

    material

    - Material - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:43 -

    - - -
    - -
    - -
    - - - -
    -
    -

    type

    - Number - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:23 -

    - - -
    - -
    -

    The type of this shape. Must be set to an int > 0 by subclasses.

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/Plane.html b/docs/classes/Plane.html deleted file mode 100644 index 05e62b01e..000000000 --- a/docs/classes/Plane.html +++ /dev/null @@ -1,522 +0,0 @@ - - - - - Plane - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    Plane Class

    -
    - -
    - Extends Shape -
    - -
    - Defined in: src/shapes/Plane.js:6 -
    - - -
    - - -
    -

    A plane, facing in the Z direction. The plane has its surface at z=0 and everything below z=0 is assumed to be solid plane. To make the plane face in some other direction than z, you must put it inside a RigidBody and rotate that body. See the demos.

    - -
    - -
    -

    Constructor

    -
    -

    Plane

    - - () - - - - - - - - -
    -

    - Defined in - src/shapes/Plane.js:6 -

    - - - -
    - -
    - -
    - - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    calculateLocalInertia

    - - () - - - Vec3 - - - - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:68 -

    - - - -
    - -
    -

    Calculates the inertia in the local frame for this shape.

    - -
    - - -
    -

    Returns:

    - -
    - Vec3: -
    -
    - - -
    -
    -

    updateBoundingSphereRadius

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:50 -

    - - - -
    - -
    -

    Computes the bounding sphere radius. The result is stored in the property .boundingSphereRadius

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    volume

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:59 -

    - - - -
    - -
    -

    Get the volume of this shape

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    - -
    -

    Properties

    - -
    -

    boundingSphereRadius

    - Number - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:31 -

    - - -
    - -
    -

    The local bounding sphere radius of this shape.

    - -
    - - - -
    -
    -

    collisionResponse

    - Boolean - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:37 -

    - - -
    - -
    -

    Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled.

    - -
    - - - -
    -
    -

    id

    - Number - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:17 -

    - - -
    - -
    -

    Identifyer of the Shape.

    - -
    - - - -
    -
    -

    material

    - Material - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:43 -

    - - -
    - -
    - -
    - - - -
    -
    -

    type

    - Number - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:23 -

    - - -
    - -
    -

    The type of this shape. Must be set to an int > 0 by subclasses.

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/PointToPointConstraint.html b/docs/classes/PointToPointConstraint.html deleted file mode 100644 index dcb57bb7b..000000000 --- a/docs/classes/PointToPointConstraint.html +++ /dev/null @@ -1,729 +0,0 @@ - - - - - PointToPointConstraint - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    PointToPointConstraint Class

    -
    - -
    - Extends Constraint -
    - - - - -
    - - -
    -

    Connects two bodies at given offset points.

    - -
    - -
    -

    Constructor

    -
    -

    PointToPointConstraint

    - -
    - (
      -
    • - bodyA -
    • -
    • - pivotA -
    • -
    • - bodyB -
    • -
    • - pivotB -
    • -
    • - maxForce -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/constraints/PointToPointConstraint.js:7 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - pivotA - Vec3 - - -
      -

      The point relative to the center of mass of bodyA which bodyA is constrained to.

      - -
      - -
    • -
    • - bodyB - Body - - -
      -

      Body that will be constrained in a similar way to the same point as bodyA. We will therefore get a link between bodyA and bodyB. If not specified, bodyA will be constrained to a static point.

      - -
      - -
    • -
    • - pivotB - Vec3 - - -
      -

      See pivotA.

      - -
      - -
    • -
    • - maxForce - Number - - -
      -

      The maximum force that should be applied to constrain the bodies.

      - -
      - -
    • -
    -
    - - - -
    -

    Example:

    - -
    -
    var bodyA = new Body({ mass: 1 });
    -        var bodyB = new Body({ mass: 1 });
    -        bodyA.position.set(-1, 0, 0);
    -        bodyB.position.set(1, 0, 0);
    -        bodyA.addShape(shapeA);
    -        bodyB.addShape(shapeB);
    -        world.addBody(bodyA);
    -        world.addBody(bodyB);
    -        var localPivotA = new Vec3(1, 0, 0);
    -        var localPivotB = new Vec3(-1, 0, 0);
    -        var constraint = new PointToPointConstraint(bodyA, localPivotA, bodyB, localPivotB);
    -        world.addConstraint(constraint);
    -        
    -
    -
    -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    disable

    - - () - - - - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:80 -

    - - - -
    - -
    -

    Disables all equations in the constraint.

    - -
    - - - - -
    -
    -

    enable

    - - () - - - - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:69 -

    - - - -
    - -
    -

    Enables all equations in the constraint.

    - -
    - - - - -
    -
    -

    update

    - - () - - - - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:61 -

    - - - -
    - -
    -

    Update all the equations with data.

    - -
    - - - - -
    -
    - -
    -

    Properties

    - -
    -

    bodyA

    - Body - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:29 -

    - - -
    - -
    - -
    - - - -
    -
    -

    bodyB

    - Body - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:34 -

    - - -
    - -
    - -
    - - - -
    -
    -

    collideConnected

    - Boolean - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:44 -

    - - -
    - -
    -

    Set to true if you want the bodies to collide when they are connected.

    - -
    - - - -
    -
    -

    equations

    - Array - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:22 -

    - - -
    - -
    -

    Equations to be solved in this constraint

    - -
    - - - -
    -
    -

    equationX

    - ContactEquation - - - - - - - -
    - -
    - - - -
    -
    -

    equationY

    - ContactEquation - - - - - - - -
    - -
    - - - -
    -
    -

    equationZ

    - ContactEquation - - - - - - - -
    - -
    - - - -
    -
    -

    id

    - Number - - - - - -
    -

    Inherited from - Constraint: - src/constraints/Constraint.js:39 -

    - - -
    - -
    - -
    - - - -
    -
    -

    pivotA

    - Vec3 - - - - - - - -
    -

    Pivot, defined locally in bodyA.

    - -
    - - - -
    -
    -

    pivotB

    - Vec3 - - - - - - - -
    -

    Pivot, defined locally in bodyB.

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/Pool.html b/docs/classes/Pool.html deleted file mode 100644 index 581d99a6f..000000000 --- a/docs/classes/Pool.html +++ /dev/null @@ -1,445 +0,0 @@ - - - - - Pool - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    Pool Class

    -
    - - -
    - Defined in: src/utils/Pool.js:3 -
    - - -
    - - -
    -

    For pooling objects that can be reused.

    - -
    - -
    -

    Constructor

    -
    -

    Pool

    - - () - - - - - - - - -
    -

    - Defined in - src/utils/Pool.js:3 -

    - - - -
    - -
    - -
    - - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    constructObject

    - - () - - - Mixed - - - - - - - - -
    -

    - Defined in - src/utils/Pool.js:47 -

    - - - -
    - -
    -

    Construct an object. Should be implmented in each subclass.

    - -
    - - -
    -

    Returns:

    - -
    - Mixed: -
    -
    - - -
    -
    -

    get

    - - () - - - Mixed - - - - - - - - -
    -

    - Defined in - src/utils/Pool.js:34 -

    - - - -
    - -
    -

    Get an object

    - -
    - - -
    -

    Returns:

    - -
    - Mixed: -
    -
    - - -
    -
    -

    release

    - -
    - (
      -
    • - obj -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/utils/Pool.js:22 -

    - - - -
    - -
    -

    Release an object after use

    - -
    - -
    -

    Parameters:

    - -
      -
    • - obj - Object - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    -

    Properties

    - -
    -

    objects

    - Array - - - - - -
    -

    - Defined in - src/utils/Pool.js:9 -

    - - -
    - -
    -

    The pooled objects

    - -
    - - - -
    -
    -

    type

    - Mixed - - - - - -
    -

    - Defined in - src/utils/Pool.js:15 -

    - - -
    - -
    -

    Constructor of the objects

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/Quaternion.html b/docs/classes/Quaternion.html deleted file mode 100644 index e903f2ba3..000000000 --- a/docs/classes/Quaternion.html +++ /dev/null @@ -1,1419 +0,0 @@ - - - - - Quaternion - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    Quaternion Class

    -
    - - -
    - Defined in: src/math/Quaternion.js:5 -
    - - -
    - - -
    -

    A Quaternion describes a rotation in 3D space. The Quaternion is mathematically defined as Q = xi + yj + z*k + w, where (i,j,k) are imaginary basis vectors. (x,y,z) can be seen as a vector related to the axis of rotation, while the real multiplier, w, is related to the amount of rotation.

    - -
    - -
    -

    Constructor

    -
    -

    Quaternion

    - -
    - (
      -
    • - x -
    • -
    • - y -
    • -
    • - z -
    • -
    • - w -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/math/Quaternion.js:5 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - x - Number - - -
      -

      Multiplier of the imaginary basis vector i.

      - -
      - -
    • -
    • - y - Number - - -
      -

      Multiplier of the imaginary basis vector j.

      - -
      - -
    • -
    • - z - Number - - -
      -

      Multiplier of the imaginary basis vector k.

      - -
      - -
    • -
    • - w - Number - - -
      -

      Multiplier of the real part.

      - -
      - -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - -
      -
    • - w - -
    • -
    • - x - -
    • -
    • - y - -
    • -
    • - z - -
    • -
    -
    - - -
    - -
    -

    Methods

    - -
    -

    conjugate

    - -
    - (
      -
    • - target -
    • -
    ) -
    - - - Quaternion - - - - - - - - -
    -

    - Defined in - src/math/Quaternion.js:184 -

    - - - -
    - -
    -

    Get the quaternion conjugate

    - -
    - -
    -

    Parameters:

    - - -
    - -
    -

    Returns:

    - -
    - Quaternion: -
    -
    - - -
    -
    -

    copy

    - -
    - (
      -
    • - source -
    • -
    ) -
    - - - Quaternion - - - - - - - - -
    -

    - Defined in - src/math/Quaternion.js:274 -

    - - - -
    - -
    -

    Copies value of source to this quaternion.

    - -
    - -
    -

    Parameters:

    - - -
    - -
    -

    Returns:

    - -
    - Quaternion: -

    this

    - -
    -
    - - -
    -
    -

    inverse

    - -
    - (
      -
    • - target -
    • -
    ) -
    - - - Quaternion - - - - - - - - -
    -

    - Defined in - src/math/Quaternion.js:164 -

    - - - -
    - -
    -

    Get the inverse quaternion rotation.

    - -
    - -
    -

    Parameters:

    - - -
    - -
    -

    Returns:

    - -
    - Quaternion: -
    -
    - - -
    -
    -

    mult

    - -
    - (
      -
    • - q -
    • -
    • - target -
    • -
    ) -
    - - - Quaternion - - - - - - - - -
    -

    - Defined in - src/math/Quaternion.js:135 -

    - - - -
    - -
    -

    Quaternion multiplication

    - -
    - -
    -

    Parameters:

    - - -
    - -
    -

    Returns:

    - -
    - Quaternion: -
    -
    - - -
    -
    -

    normalize

    - - () - - - - - - - - -
    -

    - Defined in - src/math/Quaternion.js:201 -

    - - - -
    - -
    -

    Normalize the quaternion. Note that this changes the values of the quaternion.

    - -
    - - - - -
    -
    -

    normalizeFast

    - - () - - - - - - - - -
    -

    - Defined in - src/math/Quaternion.js:221 -

    - - - -
    - -
    -

    Approximation of quaternion normalization. Works best when quat is already almost-normalized.

    - -
    - - - - -
    -
    -

    set

    - -
    - (
      -
    • - x -
    • -
    • - y -
    • -
    • - z -
    • -
    • - w -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/math/Quaternion.js:38 -

    - - - -
    - -
    -

    Set the value of the quaternion.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - x - Number - - -
      - -
      - -
    • -
    • - y - Number - - -
      - -
      - -
    • -
    • - z - Number - - -
      - -
      - -
    • -
    • - w - Number - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    setFromAxisAngle

    - -
    - (
      -
    • - axis -
    • -
    • - angle -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/math/Quaternion.js:71 -

    - - - -
    - -
    -

    Set the quaternion components given an axis and an angle.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - axis - Vec3 - - -
      - -
      - -
    • -
    • - angle - Number - - -
      -

      in radians

      - -
      - -
    • -
    -
    - - - -
    -
    -

    setFromEuler

    - -
    - (
      -
    • - x -
    • -
    • - y -
    • -
    • - z -
    • -
    • - order -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/math/Quaternion.js:331 -

    - - - -
    - - - -
    -

    Parameters:

    - -
      -
    • - x - Number - - -
      - -
      - -
    • -
    • - y - Number - - -
      - -
      - -
    • -
    • - z - Number - - -
      - -
      - -
    • -
    • - order - String - - -
      -

      The order to apply angles: 'XYZ' or 'YXZ' or any other combination

      - -
      - -
    • -
    -
    - - - -
    -
    -

    setFromVectors

    - -
    - (
      -
    • - u -
    • -
    • - v -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/math/Quaternion.js:112 -

    - - - -
    - -
    -

    Set the quaternion value given two vectors. The resulting rotation will be the needed rotation to rotate u to v.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - u - Vec3 - - -
      - -
      - -
    • -
    • - v - Vec3 - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    toArray

    - - () - - - - - - - - - - - -
    -

    - Defined in - src/math/Quaternion.js:62 -

    - - - -
    - -
    -

    Convert to an Array

    - -
    - - -
    -

    Returns:

    - -
    -

    Array

    - -
    -
    - - -
    -
    -

    toAxisAngle

    - -
    - (
      -
    • - targetAxis -
    • -
    ) -
    - - - - - - - - - - - -
    -

    - Defined in - src/math/Quaternion.js:85 -

    - - - -
    - -
    -

    Converts the quaternion to axis/angle representation.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - targetAxis - Vec3 - - -
      -

      Optional. A vector object to reuse for storing the axis.

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    -

    Array An array, first elemnt is the axis and the second is the angle in radians.

    - -
    -
    - - -
    -
    -

    toEuler

    - -
    - (
      -
    • - target -
    • -
    • - string -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/math/Quaternion.js:288 -

    - - - -
    - -
    -

    Convert the quaternion to euler angle representation. Order: YZX, as this page describes: http://www.euclideanspace.com/maths/standards/index.htm

    - -
    - -
    -

    Parameters:

    - -
      -
    • - target - Vec3 - - -
      - -
      - -
    • -
    • - string - Object - - -
      -

      order Three-character string e.g. "YZX", which also is default.

      - -
      - -
    • -
    -
    - - - -
    -
    -

    toString

    - - () - - - - - - - - - - - -
    -

    - Defined in - src/math/Quaternion.js:53 -

    - - - -
    - -
    -

    Convert to a readable format

    - -
    - - -
    -

    Returns:

    - -
    -

    string

    - -
    -
    - - -
    -
    -

    vmult

    - -
    - (
      -
    • - v -
    • -
    • - target -
    • -
    ) -
    - - - Vec3 - - - - - - - - -
    -

    - Defined in - src/math/Quaternion.js:242 -

    - - - -
    - -
    -

    Multiply the quaternion by a vector

    - -
    - -
    -

    Parameters:

    - -
      -
    • - v - Vec3 - - -
      - -
      - -
    • -
    • - target - Vec3 - - -
      -

      Optional

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Vec3: -
    -
    - - -
    -
    - -
    -

    Properties

    - -
    -

    w

    - Number - - - - - -
    -

    - Defined in - src/math/Quaternion.js:31 -

    - - -
    - -
    -

    The multiplier of the real quaternion basis vector.

    - -
    - - - -
    -
    -

    x

    - Number - - - - - -
    -

    - Defined in - src/math/Quaternion.js:16 -

    - - -
    - -
    - -
    - - - -
    -
    -

    y

    - Number - - - - - -
    -

    - Defined in - src/math/Quaternion.js:21 -

    - - -
    - -
    - -
    - - - -
    -
    -

    z

    - Number - - - - - -
    -

    - Defined in - src/math/Quaternion.js:26 -

    - - -
    - -
    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/Ray.html b/docs/classes/Ray.html deleted file mode 100644 index 070aaadf1..000000000 --- a/docs/classes/Ray.html +++ /dev/null @@ -1,1761 +0,0 @@ - - - - - Ray - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    Ray Class

    -
    - - -
    - Defined in: src/collision/Ray.js:12 -
    - - -
    - - -
    -

    A line in 3D space that intersects bodies and return points.

    - -
    - -
    -

    Constructor

    -
    -

    Ray

    - -
    - (
      -
    • - from -
    • -
    • - to -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/collision/Ray.js:12 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - from - Vec3 - - -
      - -
      - -
    • -
    • - to - Vec3 - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    _updateDirection

    - - () - - - - private - - - - - -
    -

    - Defined in - src/collision/Ray.js:224 -

    - - - -
    - -
    -

    Updates the _direction vector.

    - -
    - - - - -
    -
    -

    getAABB

    - -
    - (
      -
    • - aabb -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/collision/Ray.js:334 -

    - - - -
    - -
    -

    Get the world AABB of the ray.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - aabb - AABB - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    intersectBodies

    - -
    - (
      -
    • - bodies -
    • -
    • - [result] -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/collision/Ray.js:208 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodies - Array - - -
      -

      An array of Body objects.

      - -
      - -
    • -
    • - [result] - RaycastResult - optional - - -
      -

      Deprecated

      - -
      - -
    • -
    -
    - - - -
    -
    -

    intersectBody

    - -
    - (
      -
    • - body -
    • -
    • - [result] -
    • -
    ) -
    - - - - private - - - - - -
    -

    - Defined in - src/collision/Ray.js:157 -

    - - - -
    - -
    -

    Shoot a ray at a body, get back information about the hit.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - body - Body - - -
      - -
      - -
    • -
    • - [result] - RaycastResult - optional - - -
      -

      Deprecated - set the result property of the Ray instead.

      - -
      - -
    • -
    -
    - - - -
    -
    -

    intersectBox

    - -
    - (
      -
    • - shape -
    • -
    • - quat -
    • -
    • - position -
    • -
    • - body -
    • -
    ) -
    - - - - private - - - - - -
    -

    - Defined in - src/collision/Ray.js:269 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - shape - Shape - - -
      - -
      - -
    • -
    • - quat - Quaternion - - -
      - -
      - -
    • -
    • - position - Vec3 - - -
      - -
      - -
    • -
    • - body - Body - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    intersectConvex

    - -
    - (
      -
    • - shape -
    • -
    • - quat -
    • -
    • - position -
    • -
    • - body -
    • -
    • - [options] -
    • -
    ) -
    - - - - private - - - - - -
    -

    - Defined in - src/collision/Ray.js:505 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - shape - Shape - - -
      - -
      - -
    • -
    • - quat - Quaternion - - -
      - -
      - -
    • -
    • - position - Vec3 - - -
      - -
      - -
    • -
    • - body - Body - - -
      - -
      - -
    • -
    • - [options] - Object - optional - - -
      - -
      - -
        -
      • - [faceList] - Array - optional - -
        - -
        - -
      • -
      -
    • -
    -
    - - - -
    -
    -

    intersectHeightfield

    - -
    - (
      -
    • - shape -
    • -
    • - quat -
    • -
    • - position -
    • -
    • - body -
    • -
    ) -
    - - - - private - - - - - -
    -

    - Defined in - src/collision/Ray.js:354 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - shape - Shape - - -
      - -
      - -
    • -
    • - quat - Quaternion - - -
      - -
      - -
    • -
    • - position - Vec3 - - -
      - -
      - -
    • -
    • - body - Body - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    intersectPlane

    - -
    - (
      -
    • - shape -
    • -
    • - quat -
    • -
    • - position -
    • -
    • - body -
    • -
    ) -
    - - - - private - - - - - -
    -

    - Defined in - src/collision/Ray.js:282 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - shape - Shape - - -
      - -
      - -
    • -
    • - quat - Quaternion - - -
      - -
      - -
    • -
    • - position - Vec3 - - -
      - -
      - -
    • -
    • - body - Body - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    intersectShape

    - -
    - (
      -
    • - shape -
    • -
    • - quat -
    • -
    • - position -
    • -
    • - body -
    • -
    ) -
    - - - - private - - - - - -
    -

    - Defined in - src/collision/Ray.js:234 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - shape - Shape - - -
      - -
      - -
    • -
    • - quat - Quaternion - - -
      - -
      - -
    • -
    • - position - Vec3 - - -
      - -
      - -
    • -
    • - body - Body - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    intersectSphere

    - -
    - (
      -
    • - shape -
    • -
    • - quat -
    • -
    • - position -
    • -
    • - body -
    • -
    ) -
    - - - - private - - - - - -
    -

    - Defined in - src/collision/Ray.js:439 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - shape - Shape - - -
      - -
      - -
    • -
    • - quat - Quaternion - - -
      - -
      - -
    • -
    • - position - Vec3 - - -
      - -
      - -
    • -
    • - body - Body - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    intersectTrimesh

    - -
    - (
      -
    • - shape -
    • -
    • - quat -
    • -
    • - position -
    • -
    • - body -
    • -
    • - [options] -
    • -
    ) -
    - - - - private - - - - - -
    -

    - Defined in - src/collision/Ray.js:623 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - shape - Shape - - -
      - -
      - -
    • -
    • - quat - Quaternion - - -
      - -
      - -
    • -
    • - position - Vec3 - - -
      - -
      - -
    • -
    • - body - Body - - -
      - -
      - -
    • -
    • - [options] - Object - optional - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    intersectWorld

    - -
    - (
      -
    • - world -
    • -
    • - options -
    • -
    ) -
    - - - Boolean - - - - - - - - -
    -

    - Defined in - src/collision/Ray.js:99 -

    - - - -
    - -
    -

    Do itersection against all bodies in the given World.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - world - World - - -
      - -
      - -
    • -
    • - options - Object - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Boolean: -

    True if the ray hit anything, otherwise false.

    - -
    -
    - - -
    -
    -

    reportIntersection

    - -
    - (
      -
    • - normal -
    • -
    • - hitPointWorld -
    • -
    • - shape -
    • -
    • - body -
    • -
    ) -
    - - - Boolean - - - - private - - - - - -
    -

    - Defined in - src/collision/Ray.js:738 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - normal - Vec3 - - -
      - -
      - -
    • -
    • - hitPointWorld - Vec3 - - -
      - -
      - -
    • -
    • - shape - Shape - - -
      - -
      - -
    • -
    • - body - Body - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Boolean: -

    True if the intersections should continue

    - -
    -
    - - -
    -
    - -
    -

    Properties

    - -
    -

    _direction

    - Vec3 - - - private - - - -
    -

    - Defined in - src/collision/Ray.js:30 -

    - - -
    - -
    - -
    - - - -
    -
    -

    callback

    - Function - - - - - -
    -

    - Defined in - src/collision/Ray.js:84 -

    - - -
    - -
    -

    Current, user-provided result callback. Will be used if mode is Ray.ALL.

    - -
    - - - -
    -
    -

    checkCollisionResponse

    - Boolean - - - - - -
    -

    - Defined in - src/collision/Ray.js:42 -

    - - -
    - -
    -

    Set to true if you want the Ray to take .collisionResponse flags into account on bodies and shapes.

    - -
    - - - -
    -
    -

    collisionFilterGroup

    - Number - - - - - -
    -

    - Defined in - src/collision/Ray.js:60 -

    - - -
    - -
    - -
    - -

    Default: -1

    - - -
    -
    -

    collisionFilterMask

    - Number - - - - - -
    -

    - Defined in - src/collision/Ray.js:54 -

    - - -
    - -
    - -
    - -

    Default: -1

    - - -
    -
    -

    from

    - Vec3 - - - - - -
    -

    - Defined in - src/collision/Ray.js:20 -

    - - -
    - -
    - -
    - - - -
    -
    -

    hasHit

    - Boolean - - - - - -
    -

    - Defined in - src/collision/Ray.js:78 -

    - - -
    - -
    -

    Will be set to true during intersectWorld() if the ray hit anything.

    - -
    - - - -
    -
    -

    mode

    - Number - - - - - -
    -

    - Defined in - src/collision/Ray.js:66 -

    - - -
    - -
    -

    The intersection mode. Should be Ray.ANY, Ray.ALL or Ray.CLOSEST.

    - -
    - - - -
    -
    -

    precision

    - Number - - - - - -
    -

    - Defined in - src/collision/Ray.js:36 -

    - - -
    - -
    -

    The precision of the ray. Used when checking parallelity etc.

    - -
    - - - -
    -
    -

    result

    - RaycastResult - - - - - -
    -

    - Defined in - src/collision/Ray.js:72 -

    - - -
    - -
    -

    Current result object.

    - -
    - - - -
    -
    -

    skipBackfaces

    - Boolean - - - - - -
    -

    - Defined in - src/collision/Ray.js:48 -

    - - -
    - -
    -

    If set to true, the ray skips any hits with normal.dot(rayDirection) < 0.

    - -
    - - - -
    -
    -

    to

    - Vec3 - - - - - -
    -

    - Defined in - src/collision/Ray.js:25 -

    - - -
    - -
    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/RaycastResult.html b/docs/classes/RaycastResult.html deleted file mode 100644 index 2737a83e1..000000000 --- a/docs/classes/RaycastResult.html +++ /dev/null @@ -1,732 +0,0 @@ - - - - - RaycastResult - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    RaycastResult Class

    -
    - - - - - -
    - - -
    -

    Storage for Ray casting data.

    - -
    - -
    -

    Constructor

    -
    -

    RaycastResult

    - - () - - - - - - - - -
    -

    - Defined in - src/collision/RaycastResult.js:5 -

    - - - -
    - -
    - -
    - - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    abort

    - - () - - - - - - - - -
    -

    - Defined in - src/collision/RaycastResult.js:89 -

    - - - -
    - -
    - -
    - - - - -
    -
    -

    reset

    - - () - - - - - - - - -
    -

    - Defined in - src/collision/RaycastResult.js:72 -

    - - - -
    - -
    -

    Reset all result data.

    - -
    - - - - -
    -
    -

    set

    - -
    - (
      -
    • - rayFromWorld -
    • -
    • - rayToWorld -
    • -
    • - hitNormalWorld -
    • -
    • - hitPointWorld -
    • -
    • - shape -
    • -
    • - body -
    • -
    • - distance -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/collision/RaycastResult.js:96 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - rayFromWorld - Vec3 - - -
      - -
      - -
    • -
    • - rayToWorld - Vec3 - - -
      - -
      - -
    • -
    • - hitNormalWorld - Vec3 - - -
      - -
      - -
    • -
    • - hitPointWorld - Vec3 - - -
      - -
      - -
    • -
    • - shape - Shape - - -
      - -
      - -
    • -
    • - body - Body - - -
      - -
      - -
    • -
    • - distance - Number - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    -

    Properties

    - -
    -

    _shouldStop

    - Boolean - - - private - - - -
    -

    - Defined in - src/collision/RaycastResult.js:63 -

    - - -
    - -
    -

    If the ray should stop traversing the bodies.

    - -
    - -

    Default: false

    - - -
    -
    -

    body

    - Body - - - - - -
    -

    - Defined in - src/collision/RaycastResult.js:43 -

    - - -
    - -
    -

    The hit body, or null.

    - -
    - - - -
    -
    -

    distance

    - Number - - - - - -
    -

    - Defined in - src/collision/RaycastResult.js:56 -

    - - -
    - -
    -

    Distance to the hit. Will be set to -1 if there was no hit.

    - -
    - -

    Default: -1

    - - -
    -
    -

    hasHit

    - Boolean - - - - - -
    -

    - Defined in - src/collision/RaycastResult.js:32 -

    - - -
    - -
    - -
    - - - -
    -
    -

    hitFaceIndex

    - Number - - - - - -
    -

    - Defined in - src/collision/RaycastResult.js:49 -

    - - -
    - -
    -

    The index of the hit triangle, if the hit shape was a trimesh.

    - -
    - -

    Default: -1

    - - -
    -
    -

    hitNormalWorld

    - Vec3 - - - - - -
    -

    - Defined in - src/collision/RaycastResult.js:22 -

    - - -
    - -
    - -
    - - - -
    -
    -

    hitPointWorld

    - Vec3 - - - - - -
    -

    - Defined in - src/collision/RaycastResult.js:27 -

    - - -
    - -
    - -
    - - - -
    -
    -

    rayFromWorld

    - Vec3 - - - - - -
    -

    - Defined in - src/collision/RaycastResult.js:12 -

    - - -
    - -
    - -
    - - - -
    -
    -

    rayToWorld

    - Vec3 - - - - - -
    -

    - Defined in - src/collision/RaycastResult.js:17 -

    - - -
    - -
    - -
    - - - -
    -
    -

    shape

    - Shape - - - - - -
    -

    - Defined in - src/collision/RaycastResult.js:37 -

    - - -
    - -
    -

    The hit shape, or null.

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/RaycastVehicle.html b/docs/classes/RaycastVehicle.html deleted file mode 100644 index cd524af44..000000000 --- a/docs/classes/RaycastVehicle.html +++ /dev/null @@ -1,1090 +0,0 @@ - - - - - RaycastVehicle - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    RaycastVehicle Class

    -
    - - - - - -
    - - -
    -

    Vehicle helper class that casts rays from the wheel positions towards the ground and applies forces.

    - -
    - -
    -

    Constructor

    -
    -

    RaycastVehicle

    - -
    - (
      -
    • - [options] -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/RaycastVehicle.js:10 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - [options] - Object - optional - - -
      - -
      - -
        -
      • - [chassisBody] - Body - optional - -
        -

        The car chassis body.

        - -
        - -
      • -
      • - [indexRightAxis] - Integer - optional - -
        -

        Axis to use for right. x=0, y=1, z=2

        - -
        - -
      • -
      • - [indexLeftAxis] - Integer - optional - -
        - -
        - -
      • -
      • - [indexUpAxis] - Integer - optional - -
        - -
        - -
      • -
      -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    addToWorld

    - -
    - (
      -
    • - world -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/RaycastVehicle.js:122 -

    - - - -
    - -
    -

    Add the vehicle including its constraints to the world.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - world - World - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    addWheel

    - -
    - (
      -
    • - [options] -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/RaycastVehicle.js:74 -

    - - - -
    - -
    -

    Add a wheel. For information about the options, see WheelInfo.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - [options] - Object - optional - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    applyEngineForce

    - -
    - (
      -
    • - value -
    • -
    • - wheelIndex -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/RaycastVehicle.js:102 -

    - - - -
    - -
    -

    Set the wheel force to apply on one of the wheels each time step

    - -
    - -
    -

    Parameters:

    - -
      -
    • - value - Number - - -
      - -
      - -
    • -
    • - wheelIndex - Integer - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    getVehicleAxisWorld

    - -
    - (
      -
    • - axisIndex -
    • -
    • - result -
    • -
    ) -
    - - - - private - - - - - -
    -

    - Defined in - src/objects/RaycastVehicle.js:138 -

    - - - -
    - -
    -

    Get one of the wheel axles, world-oriented.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - axisIndex - Integer - - -
      - -
      - -
    • -
    • - result - Vec3 - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    getWheelTransformWorld

    - -
    - (
      -
    • - wheelIndex -
    • -
    ) -
    - - - Transform - - - - - - - - -
    -

    - Defined in - src/objects/RaycastVehicle.js:428 -

    - - - -
    - -
    -

    Get the world transform of one of the wheels

    - -
    - -
    -

    Parameters:

    - -
      -
    • - wheelIndex - Integer - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Transform: -
    -
    - - -
    -
    -

    removeFromWorld

    - -
    - (
      -
    • - world -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/RaycastVehicle.js:279 -

    - - - -
    - -
    -

    Remove the vehicle including its constraints from the world.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - world - World - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    setBrake

    - -
    - (
      -
    • - brake -
    • -
    • - wheelIndex -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/RaycastVehicle.js:112 -

    - - - -
    - -
    -

    Set the braking force of a wheel

    - -
    - -
    -

    Parameters:

    - -
      -
    • - brake - Number - - -
      - -
      - -
    • -
    • - wheelIndex - Integer - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    setSteeringValue

    - -
    - (
      -
    • - value -
    • -
    • - wheelIndex -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/RaycastVehicle.js:89 -

    - - - -
    - -
    -

    Set the steering value of a wheel.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - value - Number - - -
      - -
      - -
    • -
    • - wheelIndex - Integer - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    updateWheelTransform

    - -
    - (
      -
    • - wheelIndex -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/RaycastVehicle.js:380 -

    - - - -
    - -
    -

    Update one of the wheel transform. -Note when rendering wheels: during each step, wheel transforms are updated BEFORE the chassis; ie. their position becomes invalid after the step. Thus when you render wheels, you must update wheel transforms before rendering them. See raycastVehicle demo for an example.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - wheelIndex - Integer - - -
      -

      The wheel index to update.

      - -
      - -
    • -
    -
    - - - -
    -
    - -
    -

    Properties

    - -
    -

    chassisBody

    - Body - - - - - -
    -

    - Defined in - src/objects/RaycastVehicle.js:22 -

    - - -
    - -
    - -
    - - - -
    -
    -

    indexForwardAxis

    - Integer - - - - - -
    -

    - Defined in - src/objects/RaycastVehicle.js:51 -

    - - -
    - -
    -

    Index of the forward axis, 0=x, 1=y, 2=z

    - -
    - -

    Default: 0

    - - -
    -
    -

    indexRightAxis

    - Integer - - - - - -
    -

    - Defined in - src/objects/RaycastVehicle.js:44 -

    - - -
    - -
    -

    Index of the right axis, 0=x, 1=y, 2=z

    - -
    - -

    Default: 1

    - - -
    -
    -

    indexUpAxis

    - Integer - - - - - -
    -

    - Defined in - src/objects/RaycastVehicle.js:58 -

    - - -
    - -
    -

    Index of the up axis, 0=x, 1=y, 2=z

    - -
    - -

    Default: 2

    - - -
    -
    -

    sliding

    - Boolean - - - - - -
    -

    - Defined in - src/objects/RaycastVehicle.js:33 -

    - - -
    - -
    -

    Will be set to true if the car is sliding.

    - -
    - - - -
    -
    -

    wheelInfos

    - Array - - - - - -
    -

    - Defined in - src/objects/RaycastVehicle.js:27 -

    - - -
    - -
    -

    An array of WheelInfo objects.

    - -
    - - - -
    -
    -

    world

    - World - - - - - -
    -

    - Defined in - src/objects/RaycastVehicle.js:39 -

    - - -
    - -
    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/RigidVehicle.html b/docs/classes/RigidVehicle.html deleted file mode 100644 index 16772e259..000000000 --- a/docs/classes/RigidVehicle.html +++ /dev/null @@ -1,981 +0,0 @@ - - - - - RigidVehicle - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    RigidVehicle Class

    -
    - - - - - -
    - - -
    -

    Simple vehicle helper class with spherical rigid body wheels.

    - -
    - -
    -

    Constructor

    -
    -

    RigidVehicle

    - -
    - (
      -
    • - [options.chassisBody] -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/RigidVehicle.js:9 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - [options.chassisBody] - Body - optional - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    addToWorld

    - -
    - (
      -
    • - world -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/RigidVehicle.js:162 -

    - - - -
    - -
    -

    Add the vehicle including its constraints to the world.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - world - World - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    addWheel

    - -
    - (
      -
    • - options -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/RigidVehicle.js:45 -

    - - - -
    - -
    -

    Add a wheel

    - -
    - -
    -

    Parameters:

    - -
      -
    • - options - Object - - -
      - -
      - -
        -
      • - [isFrontWheel] - Boolean - optional - -
        - -
        - -
      • -
      • - [position] - Vec3 - optional - -
        -

        Position of the wheel, locally in the chassis body.

        - -
        - -
      • -
      • - [direction] - Vec3 - optional - -
        -

        Slide direction of the wheel along the suspension.

        - -
        - -
      • -
      • - [axis] - Vec3 - optional - -
        -

        Axis of rotation of the wheel, locally defined in the chassis.

        - -
        - -
      • -
      • - [body] - Body - optional - -
        -

        The wheel body.

        - -
        - -
      • -
      -
    • -
    -
    - - - -
    -
    -

    applyWheelForce

    - -
    - (
      -
    • - value -
    • -
    • - wheelIndex -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/RigidVehicle.js:146 -

    - - - -
    - -
    -

    Apply a torque on one of the wheels.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - value - Number - - -
      - -
      - -
    • -
    • - wheelIndex - Integer - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    disableMotor

    - -
    - (
      -
    • - value -
    • -
    • - wheelIndex -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/RigidVehicle.js:123 -

    - - - -
    - -
    -

    Set the target rotational speed of the hinge constraint.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - value - Number - - -
      - -
      - -
    • -
    • - wheelIndex - Integer - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    getWheelSpeed

    - -
    - (
      -
    • - wheelIndex -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/RigidVehicle.js:209 -

    - - - -
    - -
    -

    Get current rotational velocity of a wheel

    - -
    - -
    -

    Parameters:

    - -
      -
    • - wheelIndex - Integer - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    removeFromWorld

    - -
    - (
      -
    • - world -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/RigidVehicle.js:189 -

    - - - -
    - -
    -

    Remove the vehicle including its constraints from the world.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - world - World - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    setMotorSpeed

    - -
    - (
      -
    • - value -
    • -
    • - wheelIndex -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/RigidVehicle.js:111 -

    - - - -
    - -
    -

    Set the target rotational speed of the hinge constraint.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - value - Number - - -
      - -
      - -
    • -
    • - wheelIndex - Integer - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    setSteeringValue

    - -
    - (
      -
    • - value -
    • -
    • - wheelIndex -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/RigidVehicle.js:89 -

    - - - -
    - -
    -

    Set the steering value of a wheel.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - value - Number - - -
      - -
      - -
    • -
    • - wheelIndex - Integer - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    setWheelForce

    - -
    - (
      -
    • - value -
    • -
    • - wheelIndex -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/RigidVehicle.js:136 -

    - - - -
    - -
    -

    Set the wheel force to apply on one of the wheels each time step

    - -
    - -
    -

    Parameters:

    - -
      -
    • - value - Number - - -
      - -
      - -
    • -
    • - wheelIndex - Integer - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    -

    Properties

    - -
    -

    chassisBody

    - Body - - - - - -
    -

    - Defined in - src/objects/RigidVehicle.js:24 -

    - - -
    - -
    - -
    - - - -
    -
    -

    constraints

    - Array - - - - - -
    -

    - Defined in - src/objects/RigidVehicle.js:35 -

    - - -
    - -
    - -
    - - - -
    -
    -

    coordinateSystem

    - Vec3 - - - - - -
    -

    - Defined in - src/objects/RigidVehicle.js:18 -

    - - -
    - -
    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/RotationalEquation.html b/docs/classes/RotationalEquation.html deleted file mode 100644 index b4e0f66f0..000000000 --- a/docs/classes/RotationalEquation.html +++ /dev/null @@ -1,1033 +0,0 @@ - - - - - RotationalEquation - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    RotationalEquation Class

    -
    - -
    - Extends Equation -
    - - - - -
    - - -
    -

    Rotational constraint. Works to keep the local vectors orthogonal to each other in world space.

    - -
    - -
    -

    Constructor

    -
    -

    RotationalEquation

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    • - [options.axisA] -
    • -
    • - [options.axisB] -
    • -
    • - [options.maxForce] -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/equations/RotationalEquation.js:7 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    • - [options.axisA] - Vec3 - optional - - -
      - -
      - -
    • -
    • - [options.axisB] - Vec3 - optional - - -
      - -
      - -
    • -
    • - [options.maxForce] - Number - optional - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    addToWlambda

    - -
    - (
      -
    • - deltalambda -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:228 -

    - - - -
    - -
    -

    Add constraint velocity to the bodies.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - deltalambda - Number - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    computeB

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:95 -

    - - - -
    - -
    -

    Computes the RHS of the SPOOK equation

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGiMf

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:159 -

    - - - -
    - -
    -

    Computes Ginv(M)f, where M is the mass matrix with diagonal blocks for each body, and f are the forces on the bodies.

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGiMGt

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:191 -

    - - - -
    - -
    -

    Computes Ginv(M)G'

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGq

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:107 -

    - - - -
    - -
    -

    Computes G*q, where q are the generalized body coordinates

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGW

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:124 -

    - - - -
    - -
    -

    Computes G*W, where W are the body velocities

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGWlambda

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:142 -

    - - - -
    - -
    -

    Computes G*Wlambda, where W are the body velocities

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeInvC

    - -
    - (
      -
    • - eps -
    • -
    ) -
    - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:262 -

    - - - -
    - -
    -

    Compute the denominator part of the SPOOK equation: C = Ginv(M)G' + eps

    - -
    - -
    -

    Parameters:

    - -
      -
    • - eps - Number - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    setSpookParams

    - - () - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:82 -

    - - - -
    - -
    -

    Recalculates a,b,eps.

    - -
    - - - - -
    -
    - -
    -

    Properties

    - -
    -

    a

    - Number - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:41 -

    - - -
    - -
    -

    SPOOK parameter

    - -
    - - - -
    -
    -

    b

    - Number - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:47 -

    - - -
    - -
    -

    SPOOK parameter

    - -
    - - - -
    -
    -

    bi

    - Body - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:29 -

    - - -
    - -
    - -
    - - - -
    -
    -

    bj

    - Body - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:35 -

    - - -
    - -
    - -
    - - - -
    -
    -

    enabled

    - Boolean - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:69 -

    - - -
    - -
    - -
    - -

    Default: true

    - - -
    -
    -

    eps

    - Number - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:53 -

    - - -
    - -
    -

    SPOOK parameter

    - -
    - - - -
    -
    -

    jacobianElementA

    - JacobianElement - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:59 -

    - - -
    - -
    - -
    - - - -
    -
    -

    jacobianElementB

    - JacobianElement - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:64 -

    - - -
    - -
    - -
    - - - -
    -
    -

    maxForce

    - Number - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:24 -

    - - -
    - -
    - -
    - - - -
    -
    -

    minForce

    - Number - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:19 -

    - - -
    - -
    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/RotationalMotorEquation.html b/docs/classes/RotationalMotorEquation.html deleted file mode 100644 index 47e8ff48d..000000000 --- a/docs/classes/RotationalMotorEquation.html +++ /dev/null @@ -1,1091 +0,0 @@ - - - - - RotationalMotorEquation - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    RotationalMotorEquation Class

    -
    - -
    - Extends Equation -
    - - - - -
    - - -
    -

    Rotational motor constraint. Tries to keep the relative angular velocity of the bodies to a given value.

    - -
    - -
    -

    Constructor

    -
    -

    RotationalMotorEquation

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    • - maxForce -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/equations/RotationalMotorEquation.js:7 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    • - maxForce - Number - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    addToWlambda

    - -
    - (
      -
    • - deltalambda -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:228 -

    - - - -
    - -
    -

    Add constraint velocity to the bodies.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - deltalambda - Number - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    computeB

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:95 -

    - - - -
    - -
    -

    Computes the RHS of the SPOOK equation

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGiMf

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:159 -

    - - - -
    - -
    -

    Computes Ginv(M)f, where M is the mass matrix with diagonal blocks for each body, and f are the forces on the bodies.

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGiMGt

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:191 -

    - - - -
    - -
    -

    Computes Ginv(M)G'

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGq

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:107 -

    - - - -
    - -
    -

    Computes G*q, where q are the generalized body coordinates

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGW

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:124 -

    - - - -
    - -
    -

    Computes G*W, where W are the body velocities

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeGWlambda

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:142 -

    - - - -
    - -
    -

    Computes G*Wlambda, where W are the body velocities

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    computeInvC

    - -
    - (
      -
    • - eps -
    • -
    ) -
    - - - Number - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:262 -

    - - - -
    - -
    -

    Compute the denominator part of the SPOOK equation: C = Ginv(M)G' + eps

    - -
    - -
    -

    Parameters:

    - -
      -
    • - eps - Number - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    setSpookParams

    - - () - - - - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:82 -

    - - - -
    - -
    -

    Recalculates a,b,eps.

    - -
    - - - - -
    -
    - -
    -

    Properties

    - -
    -

    a

    - Number - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:41 -

    - - -
    - -
    -

    SPOOK parameter

    - -
    - - - -
    -
    -

    axisA

    - Vec3 - - - - - - - -
    -

    World oriented rotational axis

    - -
    - - - -
    -
    -

    axisB

    - Vec3 - - - - - - - -
    -

    World oriented rotational axis

    - -
    - - - -
    -
    -

    b

    - Number - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:47 -

    - - -
    - -
    -

    SPOOK parameter

    - -
    - - - -
    -
    -

    bi

    - Body - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:29 -

    - - -
    - -
    - -
    - - - -
    -
    -

    bj

    - Body - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:35 -

    - - -
    - -
    - -
    - - - -
    -
    -

    enabled

    - Boolean - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:69 -

    - - -
    - -
    - -
    - -

    Default: true

    - - -
    -
    -

    eps

    - Number - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:53 -

    - - -
    - -
    -

    SPOOK parameter

    - -
    - - - -
    -
    -

    jacobianElementA

    - JacobianElement - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:59 -

    - - -
    - -
    - -
    - - - -
    -
    -

    jacobianElementB

    - JacobianElement - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:64 -

    - - -
    - -
    - -
    - - - -
    -
    -

    maxForce

    - Number - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:24 -

    - - -
    - -
    - -
    - - - -
    -
    -

    minForce

    - Number - - - - - -
    -

    Inherited from - Equation: - src/equations/Equation.js:19 -

    - - -
    - -
    - -
    - - - -
    -
    -

    targetVelocity

    - Number - - - - - - - -
    -

    Motor velocity

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/SAPBroadphase.html b/docs/classes/SAPBroadphase.html deleted file mode 100644 index 89ad06e58..000000000 --- a/docs/classes/SAPBroadphase.html +++ /dev/null @@ -1,1512 +0,0 @@ - - - - - SAPBroadphase - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    SAPBroadphase Class

    -
    - -
    - Extends Broadphase -
    - - - - -
    - - -
    -

    Sweep and prune broadphase along one axis.

    - -
    - -
    -

    Constructor

    -
    -

    SAPBroadphase

    - -
    - (
      -
    • - [world] -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/collision/SAPBroadphase.js:6 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - [world] - World - optional - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    aabbQuery

    - -
    - (
      -
    • - world -
    • -
    • - aabb -
    • -
    • - result -
    • -
    ) -
    - - - Array - - - - - - - - -
    -

    Inherited from - - Broadphase - - but overwritten in - src/collision/SAPBroadphase.js:287 -

    - - - -
    - -
    -

    Returns all the bodies within an AABB.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - world - World - - -
      - -
      - -
    • -
    • - aabb - AABB - - -
      - -
      - -
    • -
    • - result - Array - - -
      -

      An array to store resulting bodies in.

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Array: -
    -
    - - -
    -
    -

    autoDetectAxis

    - - () - - - - - - - - -
    -

    - Defined in - src/collision/SAPBroadphase.js:238 -

    - - - -
    - -
    -

    Computes the variance of the body positions and estimates the best -axis to use. Will automatically set property .axisIndex.

    - -
    - - - - -
    -
    -

    boundingSphereCheck

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    ) -
    - - - Boolean - - - - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:183 -

    - - - -
    - -
    -

    Check if the bounding spheres of two bodies overlap.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Boolean: -
    -
    - - -
    -
    -

    checkBounds

    - -
    - (
      -
    • - bi -
    • -
    • - bj -
    • -
    • - axisIndex -
    • -
    ) -
    - - - Boolean - - - - - - static - - - -
    -

    - Defined in - src/collision/SAPBroadphase.js:204 -

    - - - -
    - -
    -

    Check if the bounds of two bodies overlap, along the given SAP axis.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - bi - Body - - -
      - -
      - -
    • -
    • - bj - Body - - -
      - -
      - -
    • -
    • - axisIndex - Number - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Boolean: -
    -
    - - -
    -
    -

    collisionPairs

    - -
    - (
      -
    • - world -
    • -
    • - p1 -
    • -
    • - p2 -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - - Broadphase - - but overwritten in - src/collision/SAPBroadphase.js:143 -

    - - - -
    - -
    -

    Collect all collision pairs

    - -
    - -
    -

    Parameters:

    - -
      -
    • - world - World - - -
      - -
      - -
    • -
    • - p1 - Array - - -
      - -
      - -
    • -
    • - p2 - Array - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    doBoundingBoxBroadphase

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    • - pairs1 -
    • -
    • - pairs2 -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:112 -

    - - - -
    - -
    -

    Check if the bounding boxes of two bodies are intersecting.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    • - pairs1 - Array - - -
      - -
      - -
    • -
    • - pairs2 - Array - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    doBoundingSphereBroadphase

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    • - pairs1 -
    • -
    • - pairs2 -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:89 -

    - - - -
    - -
    -

    Check if the bounding spheres of two bodies are intersecting.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    • - pairs1 - Array - - -
      -

      bodyA is appended to this array if intersection

      - -
      - -
    • -
    • - pairs2 - Array - - -
      -

      bodyB is appended to this array if intersection

      - -
      - -
    • -
    -
    - - - -
    -
    -

    insertionSortX

    - -
    - (
      -
    • - a -
    • -
    ) -
    - - - Array - - - - - - static - - - -
    -

    - Defined in - src/collision/SAPBroadphase.js:83 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - a - Array - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Array: -
    -
    - - -
    -
    -

    insertionSortY

    - -
    - (
      -
    • - a -
    • -
    ) -
    - - - Array - - - - - - static - - - -
    -

    - Defined in - src/collision/SAPBroadphase.js:103 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - a - Array - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Array: -
    -
    - - -
    -
    -

    insertionSortZ

    - -
    - (
      -
    • - a -
    • -
    ) -
    - - - Array - - - - - - static - - - -
    -

    - Defined in - src/collision/SAPBroadphase.js:123 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - a - Array - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Array: -
    -
    - - -
    -
    -

    intersectionTest

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    • - pairs1 -
    • -
    • - pairs2 -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:73 -

    - - - -
    - -
    -

    Check if the bounding volumes of two bodies intersect.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    • - pairs1 - Array - - -
      - -
      - -
    • -
    • - pairs2 - Array - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    makePairsUnique

    - -
    - (
      -
    • - pairs1 -
    • -
    • - pairs2 -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:135 -

    - - - -
    - -
    -

    Removes duplicate pairs from the pair arrays.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - pairs1 - Array - - -
      - -
      - -
    • -
    • - pairs2 - Array - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    needBroadphaseCollision

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    ) -
    - - - Bool - - - - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:48 -

    - - - -
    - -
    -

    Check if a body pair needs to be intersection tested at all.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Bool: -
    -
    - - -
    -
    -

    setWorld

    - -
    - (
      -
    • - world -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - - Broadphase - - but overwritten in - src/collision/SAPBroadphase.js:57 -

    - - - -
    - -
    -

    Change the world

    - -
    - -
    -

    Parameters:

    - -
      -
    • - world - World - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    -

    Properties

    - -
    -

    axisIndex

    - Number - - - - - -
    -

    - Defined in - src/collision/SAPBroadphase.js:31 -

    - - -
    - -
    -

    Axis to sort the bodies along. Set to 0 for x axis, and 1 for y axis. For best performance, choose an axis that the bodies are spread out more on.

    - -
    - - - -
    -
    -

    axisList

    - Array - - - - - -
    -

    - Defined in - src/collision/SAPBroadphase.js:17 -

    - - -
    - -
    -

    List of bodies currently in the broadphase.

    - -
    - - - -
    -
    -

    dirty

    - Boolean - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:30 -

    - - -
    - -
    -

    Set to true if the objects in the world moved.

    - -
    - - - -
    -
    -

    useBoundingBoxes

    - Boolean - - - - - -
    -

    Inherited from - Broadphase: - src/collision/Broadphase.js:23 -

    - - -
    - -
    -

    If set to true, the broadphase uses bounding boxes for intersection test, else it uses bounding spheres.

    - -
    - - - -
    -
    -

    world

    - World - - - - - -
    -

    Inherited from - - Broadphase - - but overwritten in - src/collision/SAPBroadphase.js:24 -

    - - -
    - -
    -

    The world to search in.

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/SPHSystem.html b/docs/classes/SPHSystem.html deleted file mode 100644 index bce84c032..000000000 --- a/docs/classes/SPHSystem.html +++ /dev/null @@ -1,512 +0,0 @@ - - - - - SPHSystem - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    SPHSystem Class

    -
    - - -
    - Defined in: src/objects/SPHSystem.js:10 -
    - - -
    - - -
    -

    Smoothed-particle hydrodynamics system

    - -
    - -
    -

    Constructor

    -
    -

    SPHSystem

    - - () - - - - - - - - -
    -

    - Defined in - src/objects/SPHSystem.js:10 -

    - - - -
    - -
    - -
    - - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    add

    - -
    - (
      -
    • - particle -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/SPHSystem.js:45 -

    - - - -
    - -
    -

    Add a particle to the system.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - particle - Body - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    getNeighbors

    - -
    - (
      -
    • - particle -
    • -
    • - neighbors -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/SPHSystem.js:72 -

    - - - -
    - -
    -

    Get neighbors within smoothing volume, save in the array neighbors

    - -
    - -
    -

    Parameters:

    - -
      -
    • - particle - Body - - -
      - -
      - -
    • -
    • - neighbors - Array - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    remove

    - -
    - (
      -
    • - particle -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/SPHSystem.js:57 -

    - - - -
    - -
    -

    Remove a particle from the system.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - particle - Body - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    -

    Properties

    - -
    -

    density

    - Number - - - - - -
    -

    - Defined in - src/objects/SPHSystem.js:18 -

    - - -
    - -
    -

    Density of the system (kg/m3).

    - -
    - - - -
    -
    -

    smoothingRadius

    - Number - - - - - -
    -

    - Defined in - src/objects/SPHSystem.js:24 -

    - - -
    - -
    -

    Distance below which two particles are considered to be neighbors. - It should be adjusted so there are about 15-20 neighbor particles within this radius.

    - -
    - - - -
    -
    -

    viscosity

    - Number - - - - - -
    -

    - Defined in - src/objects/SPHSystem.js:32 -

    - - -
    - -
    -

    Viscosity of the system.

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/Shape.html b/docs/classes/Shape.html deleted file mode 100644 index b44ad9fa8..000000000 --- a/docs/classes/Shape.html +++ /dev/null @@ -1,550 +0,0 @@ - - - - - Shape - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    Shape Class

    -
    - - -
    - Defined in: src/shapes/Shape.js:8 -
    - - -
    - - -
    -

    Base class for shapes

    - -
    - -
    -

    Constructor

    -
    -

    Shape

    - - () - - - - - - - - -
    -

    - Defined in - src/shapes/Shape.js:8 -

    - - - -
    - -
    - -
    - - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    calculateLocalInertia

    - - () - - - Vec3 - - - - - - - - -
    -

    - Defined in - src/shapes/Shape.js:68 -

    - - - -
    - -
    -

    Calculates the inertia in the local frame for this shape.

    - -
    - - -
    -

    Returns:

    - -
    - Vec3: -
    -
    - - -
    -
    -

    updateBoundingSphereRadius

    - - () - - - Number - - - - - - - - -
    -

    - Defined in - src/shapes/Shape.js:50 -

    - - - -
    - -
    -

    Computes the bounding sphere radius. The result is stored in the property .boundingSphereRadius

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    volume

    - - () - - - Number - - - - - - - - -
    -

    - Defined in - src/shapes/Shape.js:59 -

    - - - -
    - -
    -

    Get the volume of this shape

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    - -
    -

    Properties

    - -
    -

    boundingSphereRadius

    - Number - - - - - -
    -

    - Defined in - src/shapes/Shape.js:31 -

    - - -
    - -
    -

    The local bounding sphere radius of this shape.

    - -
    - - - -
    -
    -

    collisionResponse

    - Boolean - - - - - -
    -

    - Defined in - src/shapes/Shape.js:37 -

    - - -
    - -
    -

    Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled.

    - -
    - - - -
    -
    -

    id

    - Number - - - - - -
    -

    - Defined in - src/shapes/Shape.js:17 -

    - - -
    - -
    -

    Identifyer of the Shape.

    - -
    - - - -
    -
    -

    material

    - Material - - - - - -
    -

    - Defined in - src/shapes/Shape.js:43 -

    - - -
    - -
    - -
    - - - -
    -
    -

    type

    - Number - - - - - -
    -

    - Defined in - src/shapes/Shape.js:23 -

    - - -
    - -
    -

    The type of this shape. Must be set to an int > 0 by subclasses.

    - -
    - - - -
    -
    -

    types

    - Object - - - - - static - -
    -

    - Defined in - src/shapes/Shape.js:80 -

    - - -
    - -
    -

    The available shape types.

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/Solver.html b/docs/classes/Solver.html deleted file mode 100644 index 1ba180750..000000000 --- a/docs/classes/Solver.html +++ /dev/null @@ -1,488 +0,0 @@ - - - - - Solver - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    Solver Class

    -
    - - -
    - Defined in: src/solver/Solver.js:3 -
    - - -
    - - -
    -

    Constraint equation solver base class.

    - -
    - -
    -

    Constructor

    -
    -

    Solver

    - - () - - - - - - - - -
    -

    - Defined in - src/solver/Solver.js:3 -

    - - - -
    - -
    - -
    - - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    addEquation

    - -
    - (
      -
    • - eq -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/solver/Solver.js:28 -

    - - - -
    - -
    -

    Add an equation

    - -
    - -
    -

    Parameters:

    - - -
    - - - -
    -
    -

    removeAllEquations

    - - () - - - - - - - - -
    -

    - Defined in - src/solver/Solver.js:52 -

    - - - -
    - -
    -

    Add all equations

    - -
    - - - - -
    -
    -

    removeEquation

    - -
    - (
      -
    • - eq -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/solver/Solver.js:39 -

    - - - -
    - -
    -

    Remove an equation

    - -
    - -
    -

    Parameters:

    - - -
    - - - -
    -
    -

    solve

    - -
    - (
      -
    • - dt -
    • -
    • - world -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/solver/Solver.js:17 -

    - - - -
    - -
    -

    Should be implemented in subclasses!

    - -
    - -
    -

    Parameters:

    - -
      -
    • - dt - Number - - -
      - -
      - -
    • -
    • - world - World - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    -

    Properties

    - -
    -

    equations

    - Array - - - - - -
    -

    - Defined in - src/solver/Solver.js:10 -

    - - -
    - -
    -

    All equations to be solved

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/Sphere.html b/docs/classes/Sphere.html deleted file mode 100644 index 376b5688e..000000000 --- a/docs/classes/Sphere.html +++ /dev/null @@ -1,573 +0,0 @@ - - - - - Sphere - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    Sphere Class

    -
    - -
    - Extends Shape -
    - -
    - Defined in: src/shapes/Sphere.js:6 -
    - - -
    - - -
    -

    Spherical shape

    - -
    - -
    -

    Constructor

    -
    -

    Sphere

    - -
    - (
      -
    • - radius -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/shapes/Sphere.js:6 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - radius - Number - - -
      -

      The radius of the sphere, a non-negative number.

      - -
      - -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    calculateLocalInertia

    - - () - - - Vec3 - - - - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:68 -

    - - - -
    - -
    -

    Calculates the inertia in the local frame for this shape.

    - -
    - - -
    -

    Returns:

    - -
    - Vec3: -
    -
    - - -
    -
    -

    updateBoundingSphereRadius

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:50 -

    - - - -
    - -
    -

    Computes the bounding sphere radius. The result is stored in the property .boundingSphereRadius

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    volume

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:59 -

    - - - -
    - -
    -

    Get the volume of this shape

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    - -
    -

    Properties

    - -
    -

    boundingSphereRadius

    - Number - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:31 -

    - - -
    - -
    -

    The local bounding sphere radius of this shape.

    - -
    - - - -
    -
    -

    collisionResponse

    - Boolean - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:37 -

    - - -
    - -
    -

    Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled.

    - -
    - - - -
    -
    -

    id

    - Number - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:17 -

    - - -
    - -
    -

    Identifyer of the Shape.

    - -
    - - - -
    -
    -

    material

    - Material - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:43 -

    - - -
    - -
    - -
    - - - -
    -
    -

    radius

    - Number - - - - - -
    -

    - Defined in - src/shapes/Sphere.js:17 -

    - - -
    - -
    - -
    - - - -
    -
    -

    type

    - Number - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:23 -

    - - -
    - -
    -

    The type of this shape. Must be set to an int > 0 by subclasses.

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/SplitSolver.html b/docs/classes/SplitSolver.html deleted file mode 100644 index 10da0eb08..000000000 --- a/docs/classes/SplitSolver.html +++ /dev/null @@ -1,516 +0,0 @@ - - - - - SplitSolver - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    SplitSolver Class

    -
    - -
    - Extends Solver -
    - -
    - Defined in: src/solver/SplitSolver.js:8 -
    - - -
    - - -
    -

    Splits the equations into islands and solves them independently. Can improve performance.

    - -
    - -
    -

    Constructor

    -
    -

    SplitSolver

    - -
    - (
      -
    • - subsolver -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/solver/SplitSolver.js:8 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - subsolver - Solver - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    addEquation

    - -
    - (
      -
    • - eq -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - Solver: - src/solver/Solver.js:28 -

    - - - -
    - -
    -

    Add an equation

    - -
    - -
    -

    Parameters:

    - - -
    - - - -
    -
    -

    removeAllEquations

    - - () - - - - - - - - -
    -

    Inherited from - Solver: - src/solver/Solver.js:52 -

    - - - -
    - -
    -

    Add all equations

    - -
    - - - - -
    -
    -

    removeEquation

    - -
    - (
      -
    • - eq -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - Solver: - src/solver/Solver.js:39 -

    - - - -
    - -
    -

    Remove an equation

    - -
    - -
    -

    Parameters:

    - - -
    - - - -
    -
    -

    solve

    - -
    - (
      -
    • - dt -
    • -
    • - world -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - - Solver - - but overwritten in - src/solver/SplitSolver.js:81 -

    - - - -
    - -
    -

    Solve the subsystems

    - -
    - -
    -

    Parameters:

    - -
      -
    • - dt - Number - - -
      - -
      - -
    • -
    • - world - World - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    -

    Properties

    - -
    -

    equations

    - Array - - - - - -
    -

    Inherited from - Solver: - src/solver/Solver.js:10 -

    - - -
    - -
    -

    All equations to be solved

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/Spring.html b/docs/classes/Spring.html deleted file mode 100644 index 95e01529b..000000000 --- a/docs/classes/Spring.html +++ /dev/null @@ -1,834 +0,0 @@ - - - - - Spring - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    Spring Class

    -
    - - -
    - Defined in: src/objects/Spring.js:5 -
    - - -
    - - -
    -

    A spring, connecting two bodies.

    - -
    - -
    -

    Constructor

    -
    -

    Spring

    - -
    - (
      -
    • - bodyA -
    • -
    • - bodyB -
    • -
    • - [options] -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/Spring.js:5 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - bodyA - Body - - -
      - -
      - -
    • -
    • - bodyB - Body - - -
      - -
      - -
    • -
    • - [options] - Object - optional - - -
      - -
      - -
        -
      • - [restLength] - Number - optional - -
        -

        A number > 0. Default: 1

        - -
        - -
      • -
      • - [stiffness] - Number - optional - -
        -

        A number >= 0. Default: 100

        - -
        - -
      • -
      • - [damping] - Number - optional - -
        -

        A number >= 0. Default: 1

        - -
        - -
      • -
      • - [worldAnchorA] - Vec3 - optional - -
        -

        Where to hook the spring to body A, in world coordinates.

        - -
        - -
      • -
      • - [worldAnchorB] - Vec3 - optional - -
        - -
        - -
      • -
      • - [localAnchorA] - Vec3 - optional - -
        -

        Where to hook the spring to body A, in local body coordinates.

        - -
        - -
      • -
      • - [localAnchorB] - Vec3 - optional - -
        - -
        - -
      • -
      -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    applyForce

    - - () - - - - - - - - -
    -

    - Defined in - src/objects/Spring.js:135 -

    - - - -
    - -
    -

    Apply the spring force to the connected bodies.

    - -
    - - - - -
    -
    -

    getWorldAnchorA

    - -
    - (
      -
    • - result -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/Spring.js:105 -

    - - - -
    - -
    -

    Get the anchor point on body A, in world coordinates.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - result - Vec3 - - -
      -

      The vector to store the result in.

      - -
      - -
    • -
    -
    - - - -
    -
    -

    getWorldAnchorB

    - -
    - (
      -
    • - result -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/Spring.js:114 -

    - - - -
    - -
    -

    Get the anchor point on body B, in world coordinates.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - result - Vec3 - - -
      -

      The vector to store the result in.

      - -
      - -
    • -
    -
    - - - -
    -
    -

    setWorldAnchorA

    - -
    - (
      -
    • - worldAnchorA -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/Spring.js:87 -

    - - - -
    - -
    -

    Set the anchor point on body A, using world coordinates.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - worldAnchorA - Vec3 - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    setWorldAnchorB

    - -
    - (
      -
    • - worldAnchorB -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/Spring.js:96 -

    - - - -
    - -
    -

    Set the anchor point on body B, using world coordinates.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - worldAnchorB - Vec3 - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    -

    Properties

    - -
    -

    bodyA

    - Body - - - - - -
    -

    - Defined in - src/objects/Spring.js:45 -

    - - -
    - -
    -

    First connected body.

    - -
    - - - -
    -
    -

    bodyB

    - Body - - - - - -
    -

    - Defined in - src/objects/Spring.js:52 -

    - - -
    - -
    -

    Second connected body.

    - -
    - - - -
    -
    -

    damping

    - Number - - - - - -
    -

    - Defined in - src/objects/Spring.js:38 -

    - - -
    - -
    -

    Damping of the spring.

    - -
    - - - -
    -
    -

    localAnchorA

    - Vec3 - - - - - -
    -

    - Defined in - src/objects/Spring.js:59 -

    - - -
    - -
    -

    Anchor for bodyA in local bodyA coordinates.

    - -
    - - - -
    -
    -

    localAnchorB

    - Vec3 - - - - - -
    -

    - Defined in - src/objects/Spring.js:66 -

    - - -
    - -
    -

    Anchor for bodyB in local bodyB coordinates.

    - -
    - - - -
    -
    -

    restLength

    - Number - - - - - -
    -

    - Defined in - src/objects/Spring.js:24 -

    - - -
    - -
    -

    Rest length of the spring.

    - -
    - - - -
    -
    -

    stiffness

    - Number - - - - - -
    -

    - Defined in - src/objects/Spring.js:31 -

    - - -
    - -
    -

    Stiffness of the spring.

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/Transform.html b/docs/classes/Transform.html deleted file mode 100644 index 5dce5078e..000000000 --- a/docs/classes/Transform.html +++ /dev/null @@ -1,653 +0,0 @@ - - - - - Transform - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    Transform Class

    -
    - - -
    - Defined in: src/math/Transform.js:6 -
    - - -
    - - -
    - -
    - -
    -

    Constructor

    -
    -

    Transform

    - - () - - - - - - - - -
    -

    - Defined in - src/math/Transform.js:6 -

    - - - -
    - -
    - -
    - - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    pointToLocaFrame

    - -
    - (
      -
    • - position -
    • -
    • - quaternion -
    • -
    • - worldPoint -
    • -
    • - result -
    • -
    ) -
    - - - - - - static - - - -
    -

    - Defined in - src/math/Transform.js:32 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - position - Vec3 - - -
      - -
      - -
    • -
    • - quaternion - Quaternion - - -
      - -
      - -
    • -
    • - worldPoint - Vec3 - - -
      - -
      - -
    • -
    • - result - Vec3 - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    pointToLocal

    - -
    - (
      -
    • - point -
    • -
    • - result -
    • -
    ) -
    - - - Vec3 - - - - - - - - -
    -

    - Defined in - src/math/Transform.js:48 -

    - - - -
    - -
    -

    Get a global point in local transform coordinates.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - point - Vec3 - - -
      - -
      - -
    • -
    • - result - Vec3 - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Vec3: -

    The "result" vector object

    - -
    -
    - - -
    -
    -

    pointToWorld

    - -
    - (
      -
    • - point -
    • -
    • - result -
    • -
    ) -
    - - - Vec3 - - - - - - - - -
    -

    - Defined in - src/math/Transform.js:74 -

    - - - -
    - -
    -

    Get a local point in global transform coordinates.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - point - Vec3 - - -
      - -
      - -
    • -
    • - result - Vec3 - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Vec3: -

    The "result" vector object

    - -
    -
    - - -
    -
    -

    pointToWorldFrame

    - -
    - (
      -
    • - position -
    • -
    • - quaternion -
    • -
    • - localPoint -
    • -
    • - result -
    • -
    ) -
    - - - - - - static - - - -
    -

    - Defined in - src/math/Transform.js:59 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - position - Vec3 - - -
      - -
      - -
    • -
    • - quaternion - Vec3 - - -
      - -
      - -
    • -
    • - localPoint - Vec3 - - -
      - -
      - -
    • -
    • - result - Vec3 - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    -

    Properties

    - -
    -

    position

    - Vec3 - - - - - -
    -

    - Defined in - src/math/Transform.js:13 -

    - - -
    - -
    - -
    - - - -
    -
    -

    quaternion

    - Quaternion - - - - - -
    -

    - Defined in - src/math/Transform.js:21 -

    - - -
    - -
    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/Trimesh.html b/docs/classes/Trimesh.html deleted file mode 100644 index e5cc00bcc..000000000 --- a/docs/classes/Trimesh.html +++ /dev/null @@ -1,2060 +0,0 @@ - - - - - Trimesh - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    Trimesh Class

    -
    - -
    - Extends Shape -
    - -
    - Defined in: src/shapes/Trimesh.js:10 -
    - - -
    - - -
    - -
    - -
    -

    Constructor

    -
    -

    Trimesh

    - -
    - (
      -
    • - vertices -
    • -
    • - indices -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/shapes/Trimesh.js:10 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - vertices - Array - - -
      - -
      - -
    • -
    • - indices - Array - - -
      - -
      - -
    • -
    -
    - - - -
    -

    Example:

    - -
    -
    // How to make a mesh with a single triangle
    -        var vertices = [
    -            0, 0, 0, // vertex 0
    -            1, 0, 0, // vertex 1
    -            0, 1, 0  // vertex 2
    -        ];
    -        var indices = [
    -            0, 1, 2  // triangle 0
    -        ];
    -        var trimeshShape = new Trimesh(vertices, indices);
    -        
    -
    -
    -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    _getUnscaledVertex

    - -
    - (
      -
    • - i -
    • -
    • - out -
    • -
    ) -
    - - - Vec3 - - - - private - - - - - -
    -

    - Defined in - src/shapes/Trimesh.js:295 -

    - - - -
    - -
    -

    Get raw vertex i

    - -
    - -
    -

    Parameters:

    - -
      -
    • - i - Number - - -
      - -
      - -
    • -
    • - out - Vec3 - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Vec3: -

    The "out" vector object

    - -
    -
    - - -
    -
    -

    calculateLocalInertia

    - -
    - (
      -
    • - mass -
    • -
    • - target -
    • -
    ) -
    - - - Vec3 - - - - - - - - -
    -

    Inherited from - - Shape - - but overwritten in - src/shapes/Trimesh.js:361 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - mass - Number - - -
      - -
      - -
    • -
    • - target - Vec3 - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Vec3: -

    The "target" vector object

    - -
    -
    - - -
    -
    -

    calculateWorldAABB

    - -
    - (
      -
    • - pos -
    • -
    • - quat -
    • -
    • - min -
    • -
    • - max -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/shapes/Trimesh.js:454 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - pos - Vec3 - - -
      - -
      - -
    • -
    • - quat - Quaternion - - -
      - -
      - -
    • -
    • - min - Vec3 - - -
      - -
      - -
    • -
    • - max - Vec3 - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    computeLocalAABB

    - -
    - (
      -
    • - aabb -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/shapes/Trimesh.js:383 -

    - - - -
    - -
    -

    Compute the local AABB for the trimesh

    - -
    - -
    -

    Parameters:

    - -
      -
    • - aabb - AABB - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    computeNormal

    - -
    - (
      -
    • - va -
    • -
    • - vb -
    • -
    • - vc -
    • -
    • - target -
    • -
    ) -
    - - - - - - static - - - -
    -

    - Defined in - src/shapes/Trimesh.js:255 -

    - - - -
    - -
    -

    Get face normal given 3 vertices

    - -
    - -
    -

    Parameters:

    - -
      -
    • - va - Vec3 - - -
      - -
      - -
    • -
    • - vb - Vec3 - - -
      - -
      - -
    • -
    • - vc - Vec3 - - -
      - -
      - -
    • -
    • - target - Vec3 - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    createTorus

    - -
    - (
      -
    • - [radius=1] -
    • -
    • - [tube=0.5] -
    • -
    • - [radialSegments=8] -
    • -
    • - [tubularSegments=6] -
    • -
    • - [arc=6.283185307179586] -
    • -
    ) -
    - - - Trimesh - - - - - - static - - - -
    -

    - Defined in - src/shapes/Trimesh.js:513 -

    - - - -
    - -
    -

    Create a Trimesh instance, shaped as a torus.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - [radius=1] - Number - optional - - -
      - -
      - -
    • -
    • - [tube=0.5] - Number - optional - - -
      - -
      - -
    • -
    • - [radialSegments=8] - Number - optional - - -
      - -
      - -
    • -
    • - [tubularSegments=6] - Number - optional - - -
      - -
      - -
    • -
    • - [arc=6.283185307179586] - Number - optional - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Trimesh: -

    A torus

    - -
    -
    - - -
    -
    -

    getEdgeVector

    - -
    - (
      -
    • - edgeIndex -
    • -
    • - vectorStore -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/shapes/Trimesh.js:241 -

    - - - -
    - -
    -

    Get a vector along an edge.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - edgeIndex - Number - - -
      - -
      - -
    • -
    • - vectorStore - Vec3 - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    getEdgeVertex

    - -
    - (
      -
    • - edgeIndex -
    • -
    • - firstOrSecond -
    • -
    • - vertexStore -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/shapes/Trimesh.js:226 -

    - - - -
    - -
    -

    Get an edge vertex

    - -
    - -
    -

    Parameters:

    - -
      -
    • - edgeIndex - Number - - -
      - -
      - -
    • -
    • - firstOrSecond - Number - - -
      -

      0 or 1, depending on which one of the vertices you need.

      - -
      - -
    • -
    • - vertexStore - Vec3 - - -
      -

      Where to store the result

      - -
      - -
    • -
    -
    - - - -
    -
    -

    getNormal

    - -
    - (
      -
    • - i -
    • -
    • - target -
    • -
    ) -
    - - - Vec3 - - - - - - - - -
    -

    - Defined in - src/shapes/Trimesh.js:343 -

    - - - -
    - -
    -

    Compute the normal of triangle i.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - i - Number - - -
      - -
      - -
    • -
    • - target - Vec3 - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Vec3: -

    The "target" vector object

    - -
    -
    - - -
    -
    -

    getTrianglesInAABB

    - -
    - (
      -
    • - aabb -
    • -
    • - result -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/shapes/Trimesh.js:127 -

    - - - -
    - -
    -

    Get triangles in a local AABB from the trimesh.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - aabb - AABB - - -
      - -
      - -
    • -
    • - result - Array - - -
      -

      An array of integers, referencing the queried triangles.

      - -
      - -
    • -
    -
    - - - -
    -
    -

    getTriangleVertices

    - -
    - (
      -
    • - i -
    • -
    • - a -
    • -
    • - b -
    • -
    • - c -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/shapes/Trimesh.js:328 -

    - - - -
    - -
    -

    Get the three vertices for triangle i.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - i - Number - - -
      - -
      - -
    • -
    • - a - Vec3 - - -
      - -
      - -
    • -
    • - b - Vec3 - - -
      - -
      - -
    • -
    • - c - Vec3 - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    getVertex

    - -
    - (
      -
    • - i -
    • -
    • - out -
    • -
    ) -
    - - - Vec3 - - - - - - - - -
    -

    - Defined in - src/shapes/Trimesh.js:279 -

    - - - -
    - -
    -

    Get vertex i.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - i - Number - - -
      - -
      - -
    • -
    • - out - Vec3 - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Vec3: -

    The "out" vector object

    - -
    -
    - - -
    -
    -

    getWorldVertex

    - -
    - (
      -
    • - i -
    • -
    • - pos -
    • -
    • - quat -
    • -
    • - out -
    • -
    ) -
    - - - Vec3 - - - - - - - - -
    -

    - Defined in - src/shapes/Trimesh.js:313 -

    - - - -
    - -
    -

    Get a vertex from the trimesh,transformed by the given position and quaternion.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - i - Number - - -
      - -
      - -
    • -
    • - pos - Vec3 - - -
      - -
      - -
    • -
    • - quat - Quaternion - - -
      - -
      - -
    • -
    • - out - Vec3 - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Vec3: -

    The "out" vector object

    - -
    -
    - - -
    -
    -

    setScale

    - -
    - (
      -
    • - scale -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/shapes/Trimesh.js:153 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - scale - Vec3 - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    updateAABB

    - - () - - - - - - - - -
    -

    - Defined in - src/shapes/Trimesh.js:423 -

    - - - -
    - -
    -

    Update the .aabb property

    - -
    - - - - -
    -
    -

    updateBoundingSphereRadius

    - - () - - - - - - - - -
    -

    Inherited from - - Shape - - but overwritten in - src/shapes/Trimesh.js:431 -

    - - - -
    - -
    -

    Will update the .boundingSphereRadius property

    - -
    - - - - -
    -
    -

    updateEdges

    - - () - - - - - - - - -
    -

    - Defined in - src/shapes/Trimesh.js:198 -

    - - - -
    - -
    -

    Update the .edges property

    - -
    - - - - -
    -
    -

    updateNormals

    - - () - - - - - - - - -
    -

    - Defined in - src/shapes/Trimesh.js:170 -

    - - - -
    - -
    -

    Compute the normals of the faces. Will save in the .normals array.

    - -
    - - - - -
    -
    -

    updateTree

    - - () - - - - - - - - -
    -

    - Defined in - src/shapes/Trimesh.js:88 -

    - - - -
    - -
    - -
    - - - - -
    -
    -

    volume

    - - () - - - Number - - - - - - - - -
    -

    Inherited from - - Shape - - but overwritten in - src/shapes/Trimesh.js:504 -

    - - - -
    - -
    -

    Get approximate volume

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    - -
    -

    Properties

    - -
    -

    aabb

    - Array - - - - - -
    -

    - Defined in - src/shapes/Trimesh.js:52 -

    - - -
    - -
    -

    The local AABB of the mesh.

    - -
    - - - -
    -
    -

    boundingSphereRadius

    - Number - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:31 -

    - - -
    - -
    -

    The local bounding sphere radius of this shape.

    - -
    - - - -
    -
    -

    collisionResponse

    - Boolean - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:37 -

    - - -
    - -
    -

    Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled.

    - -
    - - - -
    -
    -

    edges

    - Array - - - - - -
    -

    - Defined in - src/shapes/Trimesh.js:59 -

    - - -
    - -
    -

    References to vertex pairs, making up all unique edges in the trimesh.

    - -
    - - - -
    -
    -

    id

    - Number - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:17 -

    - - -
    - -
    -

    Identifyer of the Shape.

    - -
    - - - -
    -
    -

    indices

    - Array - - - - - -
    -

    - Defined in - src/shapes/Trimesh.js:38 -

    - - -
    - -
    -

    Array of integers, indicating which vertices each triangle consists of. The length of this array is thus 3 times the number of triangles.

    - -
    - - - -
    -
    -

    material

    - Material - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:43 -

    - - -
    - -
    - -
    - - - -
    -
    -

    normals

    - Array - - - - - -
    -

    - Defined in - src/shapes/Trimesh.js:45 -

    - - -
    - -
    -

    The normals data.

    - -
    - - - -
    -
    -

    scale

    - Vec3 - - - - - -
    -

    - Defined in - src/shapes/Trimesh.js:65 -

    - - -
    - -
    -

    Local scaling of the mesh. Use .setScale() to set it.

    - -
    - - - -
    -
    -

    tree

    - Octree - - - - - -
    -

    - Defined in - src/shapes/Trimesh.js:71 -

    - - -
    - -
    -

    The indexed triangles. Use .updateTree() to update it.

    - -
    - - - -
    -
    -

    type

    - Number - - - - - -
    -

    Inherited from - Shape: - src/shapes/Shape.js:23 -

    - - -
    - -
    -

    The type of this shape. Must be set to an int > 0 by subclasses.

    - -
    - - - -
    -
    -

    vertices

    - Array - - - - - -
    -

    - Defined in - src/shapes/Trimesh.js:32 -

    - - -
    - -
    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/TupleDictionary.html b/docs/classes/TupleDictionary.html deleted file mode 100644 index a42115f92..000000000 --- a/docs/classes/TupleDictionary.html +++ /dev/null @@ -1,463 +0,0 @@ - - - - - TupleDictionary - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    TupleDictionary Class

    -
    - - - - - -
    - - -
    - -
    - -
    -

    Constructor

    -
    -

    TupleDictionary

    - - () - - - - - - - - -
    -

    - Defined in - src/utils/TupleDictionary.js:3 -

    - - - -
    - -
    - -
    - - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    get

    - -
    - (
      -
    • - i -
    • -
    • - j -
    • -
    ) -
    - - - Number - - - - - - - - -
    -

    - Defined in - src/utils/TupleDictionary.js:17 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - i - Number - - -
      - -
      - -
    • -
    • - j - Number - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    reset

    - - () - - - - - - - - -
    -

    - Defined in - src/utils/TupleDictionary.js:55 -

    - - - -
    - -
    - -
    - - - - -
    -
    -

    set

    - -
    - (
      -
    • - i -
    • -
    • - j -
    • -
    • - value -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/utils/TupleDictionary.js:33 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - i - Number - - -
      - -
      - -
    • -
    • - j - Number - - -
      - -
      - -
    • -
    • - value - Number - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    -

    Properties

    - -
    -

    data

    - Object - - - - - -
    -

    - Defined in - src/utils/TupleDictionary.js:9 -

    - - -
    - -
    -

    The data storage

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/Vec3.html b/docs/classes/Vec3.html deleted file mode 100644 index f2cb4578d..000000000 --- a/docs/classes/Vec3.html +++ /dev/null @@ -1,2280 +0,0 @@ - - - - - Vec3 - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    Vec3 Class

    -
    - - -
    - Defined in: src/math/Vec3.js:5 -
    - - -
    - - -
    -

    3-dimensional vector

    - -
    - -
    -

    Constructor

    -
    -

    Vec3

    - -
    - (
      -
    • - x -
    • -
    • - y -
    • -
    • - z -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/math/Vec3.js:5 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - x - Number - - -
      - -
      - -
    • -
    • - y - Number - - -
      - -
      - -
    • -
    • - z - Number - - -
      - -
      - -
    • -
    -
    - - - -
    -

    Example:

    - -
    -
    var v = new Vec3(1, 2, 3);
    -        console.log('x=' + v.x); // x=1
    -        
    -
    -
    -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - -
      -
    • - UNIT_X - - static -
    • -
    • - UNIT_Y - - static -
    • -
    • - UNIT_Z - - static -
    • -
    • - x - -
    • -
    • - y - -
    • -
    • - z - -
    • -
    • - ZERO - - static -
    • -
    -
    - - -
    - -
    -

    Methods

    - -
    -

    almostEquals

    - -
    - (
      -
    • - v -
    • -
    • - precision -
    • -
    ) -
    - - - - - - - - - - - -
    -

    - Defined in - src/math/Vec3.js:395 -

    - - - -
    - -
    -

    Check if a vector equals is almost equal to another one.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - v - Vec3 - - -
      - -
      - -
    • -
    • - precision - Number - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    -

    bool

    - -
    -
    - - -
    -
    -

    almostZero

    - -
    - (
      -
    • - precision -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/math/Vec3.js:414 -

    - - - -
    - -
    -

    Check if a vector is almost zero

    - -
    - -
    -

    Parameters:

    - -
      -
    • - precision - Number - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    clone

    - - () - - - Vec3 - - - - - - - - -
    -

    - Defined in - src/math/Vec3.js:445 -

    - - - -
    - -
    -

    Clone the vector

    - -
    - - -
    -

    Returns:

    - -
    - Vec3: -
    -
    - - -
    -
    -

    copy

    - -
    - (
      -
    • - source -
    • -
    ) -
    - - - Vec3 - - - - - - - - -
    -

    - Defined in - src/math/Vec3.js:367 -

    - - - -
    - -
    -

    Copies value of source to this vector.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - source - Vec3 - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Vec3: -

    this

    - -
    -
    - - -
    -
    -

    cross

    - -
    - (
      -
    • - v -
    • -
    • - target -
    • -
    ) -
    - - - Vec3 - - - - - - - - -
    -

    - Defined in - src/math/Vec3.js:61 -

    - - - -
    - -
    -

    Vector cross product

    - -
    - -
    -

    Parameters:

    - -
      -
    • - v - Vec3 - - -
      - -
      - -
    • -
    • - target - Vec3 - - -
      -

      Optional. Target to save in.

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Vec3: -
    -
    - - -
    -
    -

    crossmat

    - - () - - - Mat3 - - - - - - - - -
    -

    - Defined in - src/math/Vec3.js:140 -

    - - - -
    - -
    -

    Get the cross product matrix a_cross from a vector, such that a x b = a_cross * b = c

    - -
    - - -
    -

    Returns:

    - -
    - Mat3: -
    -
    - - -
    -
    -

    distanceSquared

    - -
    - (
      -
    • - p -
    • -
    ) -
    - - - Number - - - - - - - - -
    -

    - Defined in - src/math/Vec3.js:246 -

    - - - -
    - -
    -

    Get squared distance from this point to another point

    - -
    - -
    -

    Parameters:

    - -
      -
    • - p - Vec3 - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    distanceTo

    - -
    - (
      -
    • - p -
    • -
    ) -
    - - - Number - - - - - - - - -
    -

    - Defined in - src/math/Vec3.js:232 -

    - - - -
    - -
    -

    Get distance from this point to another point

    - -
    - -
    -

    Parameters:

    - -
      -
    • - p - Vec3 - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    dot

    - -
    - (
      -
    • - v -
    • -
    ) -
    - - - Number - - - - - - - - -
    -

    - Defined in - src/math/Vec3.js:287 -

    - - - -
    - -
    -

    Calculate dot product

    - -
    - -
    -

    Parameters:

    - -
      -
    • - v - Vec3 - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    isAntiparallelTo

    - -
    - (
      -
    • - v -
    • -
    • - precision -
    • -
    ) -
    - - - Boolean - - - - - - - - -
    -

    - Defined in - src/math/Vec3.js:433 -

    - - - -
    - -
    -

    Check if the vector is anti-parallel to another vector.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - v - Vec3 - - -
      - -
      - -
    • -
    • - precision - Number - - -
      -

      Set to zero for exact comparisons

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Boolean: -
    -
    - - -
    -
    -

    isZero

    - - () - - - - - - - - - - - -
    -

    - Defined in - src/math/Vec3.js:297 -

    - - - -
    - -
    - -
    - - -
    -

    Returns:

    - -
    -

    bool

    - -
    -
    - - -
    -
    -

    length

    - - () - - - Number - - - - - - - - -
    -

    - Defined in - src/math/Vec3.js:208 -

    - - - -
    - -
    -

    Get the length of the vector

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    lengthSquared

    - - () - - - Number - - - - - - - - -
    -

    - Defined in - src/math/Vec3.js:225 -

    - - - -
    - -
    -

    Get the squared length of the vector.

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    lerp

    - -
    - (
      -
    • - v -
    • -
    • - t -
    • -
    • - target -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/math/Vec3.js:381 -

    - - - -
    - -
    -

    Do a linear interpolation between two vectors

    - -
    - -
    -

    Parameters:

    - -
      -
    • - v - Vec3 - - -
      - -
      - -
    • -
    • - t - Number - - -
      -

      A number between 0 and 1. 0 will make this function return u, and 1 will make it return v. Numbers in between will generate a vector in between them.

      - -
      - -
    • -
    • - target - Vec3 - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    mult

    - -
    - (
      -
    • - scalar -
    • -
    • - target -
    • -
    ) -
    - - - Vec3 - - - deprecated - - - - - - -
    -

    - Defined in - src/math/Vec3.js:258 -

    - - -

    Deprecated: Use .scale() instead

    - -
    - -
    -

    Multiply all the components of the vector with a scalar.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - scalar - Number - - -
      - -
      - -
    • -
    • - target - Vec3 - - -
      -

      The vector to save the result in.

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Vec3: -
    -
    - - -
    -
    -

    negate

    - -
    - (
      -
    • - target -
    • -
    ) -
    - - - Vec3 - - - - - - - - -
    -

    - Defined in - src/math/Vec3.js:305 -

    - - - -
    - -
    -

    Make the vector point in the opposite direction.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - target - Vec3 - - -
      -

      Optional target to save in

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Vec3: -
    -
    - - -
    -
    -

    norm

    - - () - - - Number - - - deprecated - - - - - - -
    -

    - Defined in - src/math/Vec3.js:197 -

    - - -

    Deprecated: Use .length() instead

    - -
    - -
    -

    Get the length of the vector

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    norm2

    - - () - - - Number - - - deprecated - - - - - - -
    -

    - Defined in - src/math/Vec3.js:215 -

    - - -

    Deprecated: Use .lengthSquared() instead.

    - -
    - -
    -

    Get the squared length of the vector

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    normalize

    - - () - - - Number - - - - - - - - -
    -

    - Defined in - src/math/Vec3.js:152 -

    - - - -
    - -
    -

    Normalize the vector. Note that this changes the values in the vector.

    - -
    - - -
    -

    Returns:

    - -
    - Number: -

    Returns the norm of the vector

    - -
    -
    - - -
    -
    -

    scale

    - -
    - (
      -
    • - scalar -
    • -
    • - target -
    • -
    ) -
    - - - Vec3 - - - - - - - - -
    -

    - Defined in - src/math/Vec3.js:278 -

    - - - -
    - -
    -

    Multiply the vector with a scalar.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - scalar - Number - - -
      - -
      - -
    • -
    • - target - Vec3 - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Vec3: -
    -
    - - -
    -
    -

    set

    - -
    - (
      -
    • - x -
    • -
    • - y -
    • -
    • - z -
    • -
    ) -
    - - - - - - - - - - - -
    -

    - Defined in - src/math/Vec3.js:79 -

    - - - -
    - -
    -

    Set the vectors' 3 elements

    - -
    - -
    -

    Parameters:

    - -
      -
    • - x - Number - - -
      - -
      - -
    • -
    • - y - Number - - -
      - -
      - -
    • -
    • - z - Number - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    -

    Vec3

    - -
    -
    - - -
    -
    -

    setZero

    - - () - - - - - - - - -
    -

    - Defined in - src/math/Vec3.js:94 -

    - - - -
    - -
    -

    Set all components of the vector to zero.

    - -
    - - - - -
    -
    -

    tangents

    - -
    - (
      -
    • - t1 -
    • -
    • - t2 -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/math/Vec3.js:319 -

    - - - -
    - -
    -

    Compute two artificial tangents to the vector

    - -
    - -
    -

    Parameters:

    - -
      -
    • - t1 - Vec3 - - -
      -

      Vector object to save the first tangent in

      - -
      - -
    • -
    • - t2 - Vec3 - - -
      -

      Vector object to save the second tangent in

      - -
      - -
    • -
    -
    - - - -
    -
    -

    toArray

    - - () - - - - - - - - - - - -
    -

    - Defined in - src/math/Vec3.js:358 -

    - - - -
    - -
    -

    Converts to an array

    - -
    - - -
    -

    Returns:

    - -
    -

    Array

    - -
    -
    - - -
    -
    -

    toString

    - - () - - - - - - - - - - - -
    -

    - Defined in - src/math/Vec3.js:349 -

    - - - -
    - -
    -

    Converts to a more readable format

    - -
    - - -
    -

    Returns:

    - -
    -

    string

    - -
    -
    - - -
    -
    -

    unit

    - -
    - (
      -
    • - target -
    • -
    ) -
    - - - Vec3 - - - - - - - - -
    -

    - Defined in - src/math/Vec3.js:174 -

    - - - -
    - -
    -

    Get the version of this vector that is of length 1.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - target - Vec3 - - -
      -

      Optional target to save in

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Vec3: -

    Returns the unit vector

    - -
    -
    - - -
    -
    -

    vadd

    - -
    - (
      -
    • - v -
    • -
    • - target -
    • -
    ) -
    - - - Vec3 - - - - - - - - -
    -

    - Defined in - src/math/Vec3.js:102 -

    - - - -
    - -
    -

    Vector addition

    - -
    - -
    -

    Parameters:

    - -
      -
    • - v - Vec3 - - -
      - -
      - -
    • -
    • - target - Vec3 - - -
      -

      Optional.

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Vec3: -
    -
    - - -
    -
    -

    vsub

    - -
    - (
      -
    • - v -
    • -
    • - target -
    • -
    ) -
    - - - Vec3 - - - - - - - - -
    -

    - Defined in - src/math/Vec3.js:121 -

    - - - -
    - -
    -

    Vector subtraction

    - -
    - -
    -

    Parameters:

    - -
      -
    • - v - Vec3 - - -
      - -
      - -
    • -
    • - target - Vec3 - - -
      -

      Optional. Target to save in.

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Vec3: -
    -
    - - -
    -
    - -
    -

    Properties

    - -
    -

    UNIT_X

    - Vec3 - - - - - static - -
    -

    - Defined in - src/math/Vec3.js:43 -

    - - -
    - -
    - -
    - - - -
    -
    -

    UNIT_Y

    - Vec3 - - - - - static - -
    -

    - Defined in - src/math/Vec3.js:49 -

    - - -
    - -
    - -
    - - - -
    -
    -

    UNIT_Z

    - Vec3 - - - - - static - -
    -

    - Defined in - src/math/Vec3.js:55 -

    - - -
    - -
    - -
    - - - -
    -
    -

    x

    - Number - - - - - -
    -

    - Defined in - src/math/Vec3.js:18 -

    - - -
    - -
    - -
    - - - -
    -
    -

    y

    - Number - - - - - -
    -

    - Defined in - src/math/Vec3.js:24 -

    - - -
    - -
    - -
    - - - -
    -
    -

    z

    - Number - - - - - -
    -

    - Defined in - src/math/Vec3.js:30 -

    - - -
    - -
    - -
    - - - -
    -
    -

    ZERO

    - Vec3 - - - - - static - -
    -

    - Defined in - src/math/Vec3.js:37 -

    - - -
    - -
    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/Vec3Pool.html b/docs/classes/Vec3Pool.html deleted file mode 100644 index f17c517c7..000000000 --- a/docs/classes/Vec3Pool.html +++ /dev/null @@ -1,536 +0,0 @@ - - - - - Vec3Pool - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    Vec3Pool Class

    -
    - -
    - Extends Pool -
    - -
    - Defined in: src/utils/Vec3Pool.js:6 -
    - - -
    - - -
    - -
    - -
    -

    Constructor

    -
    -

    Vec3Pool

    - - () - - - - - - - - -
    -

    - Defined in - src/utils/Vec3Pool.js:6 -

    - - - -
    - -
    - -
    - - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    constructObject

    - - () - - - Vec3 - - - - - - - - -
    -

    Inherited from - - Pool - - but overwritten in - src/utils/Vec3Pool.js:17 -

    - - - -
    - -
    -

    Construct a vector

    - -
    - - -
    -

    Returns:

    - -
    - Vec3: -
    -
    - - -
    -
    -

    defaults

    - -
    - (
      -
    • - options -
    • -
    • - defaults -
    • -
    ) -
    - - - Object - - - - - - static - - - -
    -

    - Defined in - src/utils/Utils.js:5 -

    - - - -
    - -
    -

    Extend an options object with default values.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - options - Object - - -
      -

      The options object. May be falsy: in this case, a new object is created and returned.

      - -
      - -
    • -
    • - defaults - Object - - -
      -

      An object containing default values.

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Object: -

    The modified options object.

    - -
    -
    - - -
    -
    -

    get

    - - () - - - Mixed - - - - - - - - -
    -

    Inherited from - Pool: - src/utils/Pool.js:34 -

    - - - -
    - -
    -

    Get an object

    - -
    - - -
    -

    Returns:

    - -
    - Mixed: -
    -
    - - -
    -
    -

    release

    - -
    - (
      -
    • - obj -
    • -
    ) -
    - - - - - - - - -
    -

    Inherited from - Pool: - src/utils/Pool.js:22 -

    - - - -
    - -
    -

    Release an object after use

    - -
    - -
    -

    Parameters:

    - -
      -
    • - obj - Object - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    -

    Properties

    - -
    -

    objects

    - Array - - - - - -
    -

    Inherited from - Pool: - src/utils/Pool.js:9 -

    - - -
    - -
    -

    The pooled objects

    - -
    - - - -
    -
    -

    type

    - Mixed - - - - - -
    -

    Inherited from - Pool: - src/utils/Pool.js:15 -

    - - -
    - -
    -

    Constructor of the objects

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/WheelInfo.html b/docs/classes/WheelInfo.html deleted file mode 100644 index 7341123c6..000000000 --- a/docs/classes/WheelInfo.html +++ /dev/null @@ -1,1510 +0,0 @@ - - - - - WheelInfo - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    WheelInfo Class

    -
    - - -
    - Defined in: src/objects/WheelInfo.js:8 -
    - - -
    - - -
    - -
    - -
    -

    Constructor

    -
    -

    WheelInfo

    - -
    - (
      -
    • - [options] -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:8 -

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - [options] - Object - optional - - -
      - -
      - -
        -
      • - [chassisConnectionPointLocal] - Vec3 - optional - -
        - -
        - -
      • -
      • - [chassisConnectionPointWorld] - Vec3 - optional - -
        - -
        - -
      • -
      • - [directionLocal] - Vec3 - optional - -
        - -
        - -
      • -
      • - [directionWorld] - Vec3 - optional - -
        - -
        - -
      • -
      • - [axleLocal] - Vec3 - optional - -
        - -
        - -
      • -
      • - [axleWorld] - Vec3 - optional - -
        - -
        - -
      • -
      • - [suspensionRestLength=1] - Number - optional - -
        - -
        - -
      • -
      • - [suspensionMaxLength=2] - Number - optional - -
        - -
        - -
      • -
      • - [radius=1] - Number - optional - -
        - -
        - -
      • -
      • - [suspensionStiffness=100] - Number - optional - -
        - -
        - -
      • -
      • - [dampingCompression=10] - Number - optional - -
        - -
        - -
      • -
      • - [dampingRelaxation=10] - Number - optional - -
        - -
        - -
      • -
      • - [frictionSlip=10000] - Number - optional - -
        - -
        - -
      • -
      • - [steering=0] - Number - optional - -
        - -
        - -
      • -
      • - [rotation=0] - Number - optional - -
        - -
        - -
      • -
      • - [deltaRotation=0] - Number - optional - -
        - -
        - -
      • -
      • - [rollInfluence=0.01] - Number - optional - -
        - -
        - -
      • -
      • - [maxSuspensionForce] - Number - optional - -
        - -
        - -
      • -
      • - [isFrontWheel=true] - Boolean - optional - -
        - -
        - -
      • -
      • - [clippedInvContactDotSuspension=1] - Number - optional - -
        - -
        - -
      • -
      • - [suspensionRelativeVelocity=0] - Number - optional - -
        - -
        - -
      • -
      • - [suspensionForce=0] - Number - optional - -
        - -
        - -
      • -
      • - [skidInfo=0] - Number - optional - -
        - -
        - -
      • -
      • - [suspensionLength=0] - Number - optional - -
        - -
        - -
      • -
      • - [maxSuspensionTravel=1] - Number - optional - -
        - -
        - -
      • -
      • - [useCustomSlidingRotationalSpeed=false] - Boolean - optional - -
        - -
        - -
      • -
      • - [customSlidingRotationalSpeed=-0.1] - Number - optional - -
        - -
        - -
      • -
      -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - - - - - -
    - - -
    -

    Properties

    - -
    -

    axleLocal

    - Vec3 - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:116 -

    - - -
    - -
    - -
    - - - -
    -
    -

    axleWorld

    - Vec3 - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:121 -

    - - -
    - -
    - -
    - - - -
    -
    -

    brake

    - Number - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:192 -

    - - -
    - -
    - -
    - - - -
    -
    -

    chassisConnectionPointLocal

    - Vec3 - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:95 -

    - - -
    - -
    -

    Connection point, defined locally in the chassis body frame.

    - -
    - - - -
    -
    -

    chassisConnectionPointWorld

    - Vec3 - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:101 -

    - - -
    - -
    - -
    - - - -
    -
    -

    clippedInvContactDotSuspension

    - Number - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:202 -

    - - -
    - -
    - -
    - - - -
    -
    -

    customSlidingRotationalSpeed

    - Number - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:78 -

    - - -
    - -
    -

    Speed to apply to the wheel rotation when the wheel is sliding.

    - -
    - - - -
    -
    -

    dampingCompression

    - Number - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:146 -

    - - -
    - -
    - -
    - - - -
    -
    -

    dampingRelaxation

    - Number - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:151 -

    - - -
    - -
    - -
    - - - -
    -
    -

    deltaRotation

    - Number - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:172 -

    - - -
    - -
    - -
    - - - -
    -
    -

    directionLocal

    - Vec3 - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:106 -

    - - -
    - -
    - -
    - - - -
    -
    -

    directionWorld

    - Vec3 - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:111 -

    - - -
    - -
    - -
    - - - -
    -
    -

    engineForce

    - Number - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:187 -

    - - -
    - -
    - -
    - - - -
    -
    -

    forwardImpulse

    - Number - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:232 -

    - - -
    - -
    - -
    - - - -
    -
    -

    frictionSlip

    - Number - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:156 -

    - - -
    - -
    - -
    - - - -
    -
    -

    isFrontWheel

    - Number - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:197 -

    - - -
    - -
    - -
    - - - -
    -
    -

    isInContact

    - Boolean - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:249 -

    - - -
    - -
    - -
    - - - -
    -
    -

    maxSuspensionForce

    - Number - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:182 -

    - - -
    - -
    - -
    - - - -
    -
    -

    maxSuspensionTravel

    - Number - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:72 -

    - - -
    - -
    -

    Max travel distance of the suspension, in meters.

    - -
    - - - -
    -
    -

    radius

    - Number - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:136 -

    - - -
    - -
    - -
    - - - -
    -
    -

    raycastResult

    - RaycastResult - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:237 -

    - - -
    - -
    -

    The result from raycasting

    - -
    - - - -
    -
    -

    rollInfluence

    - Number - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:177 -

    - - -
    - -
    - -
    - - - -
    -
    -

    rotation

    - Number - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:166 -

    - - -
    - -
    -

    Rotation value, in radians.

    - -
    - - - -
    -
    -

    sideImpulse

    - Number - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:227 -

    - - -
    - -
    - -
    - - - -
    -
    -

    skidInfo

    - Number - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:217 -

    - - -
    - -
    - -
    - - - -
    -
    -

    sliding

    - Boolean - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:90 -

    - - -
    - -
    - -
    - - - -
    -
    -

    steering

    - Number - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:161 -

    - - -
    - -
    - -
    - - - -
    -
    -

    suspensionForce

    - Number - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:212 -

    - - -
    - -
    - -
    - - - -
    -
    -

    suspensionLength

    - Number - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:222 -

    - - -
    - -
    - -
    - - - -
    -
    -

    suspensionMaxLength

    - Number - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:131 -

    - - -
    - -
    - -
    - - - -
    -
    -

    suspensionRelativeVelocity

    - Number - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:207 -

    - - -
    - -
    - -
    - - - -
    -
    -

    suspensionRestLength

    - Number - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:126 -

    - - -
    - -
    - -
    - - - -
    -
    -

    suspensionStiffness

    - Number - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:141 -

    - - -
    - -
    - -
    - - - -
    -
    -

    useCustomSlidingRotationalSpeed

    - Boolean - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:84 -

    - - -
    - -
    -

    If the customSlidingRotationalSpeed should be used.

    - -
    - - - -
    -
    -

    worldTransform

    - Transform - - - - - -
    -

    - Defined in - src/objects/WheelInfo.js:243 -

    - - -
    - -
    -

    Wheel world transform

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/World.html b/docs/classes/World.html deleted file mode 100644 index d8e544f21..000000000 --- a/docs/classes/World.html +++ /dev/null @@ -1,2489 +0,0 @@ - - - - - World - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    World Class

    -
    - -
    - Extends EventTarget -
    - -
    - Defined in: src/world/World.js:24 -
    - - -
    - - -
    -

    The physics world

    - -
    - -
    -

    Constructor

    -
    -

    World

    - - () - - - - - - - - -
    -

    - Defined in - src/world/World.js:24 -

    - - - -
    - -
    - -
    - - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - -
    -

    Properties

    - - -
    - - -
    - -
    -

    Methods

    - -
    -

    add

    - -
    - (
      -
    • - body -
    • -
    ) -
    - - - deprecated - - - - - - -
    -

    - Defined in - src/world/World.js:238 -

    - - -

    Deprecated: Use .addBody instead

    - -
    - -
    -

    Add a rigid body to the simulation.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - body - Body - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    addConstraint

    - -
    - (
      -
    • - c -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/world/World.js:265 -

    - - - -
    - -
    -

    Add a constraint to the simulation.

    - -
    - -
    -

    Parameters:

    - - -
    - - - -
    -
    -

    addContactMaterial

    - -
    - (
      -
    • - cmat -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/world/World.js:413 -

    - - - -
    - -
    -

    Adds a contact material to the World

    - -
    - -
    -

    Parameters:

    - - -
    - - - -
    -
    -

    addEventListener

    - -
    - (
      -
    • - type -
    • -
    • - listener -
    • -
    ) -
    - - - EventTarget - - - - - - - - -
    -

    Inherited from - EventTarget: - src/utils/EventTarget.js:15 -

    - - - -
    - -
    -

    Add an event listener

    - -
    - -
    -

    Parameters:

    - -
      -
    • - type - String - - -
      - -
      - -
    • -
    • - listener - Function - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - EventTarget: -

    The self object, for chainability.

    - -
    -
    - - -
    -
    -

    addMaterial

    - -
    - (
      -
    • - m -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/world/World.js:403 -

    - - - -
    - -
    -

    Adds a material to the World.

    - -
    - -
    -

    Parameters:

    - - -
    - - - -
    -
    -

    clearForces

    - - () - - - - - - - - -
    -

    - Defined in - src/world/World.js:936 -

    - - - -
    - -
    -

    Sets all body forces in the world to zero.

    - -
    - - - - -
    -
    -

    collisionMatrixTick

    - - () - - - - - - - - -
    -

    - Defined in - src/world/World.js:227 -

    - - - -
    - -
    -

    Store old collision state info

    - -
    - - - - -
    -
    -

    dispatchEvent

    - -
    - (
      -
    • - event -
    • -
    ) -
    - - - EventTarget - - - - - - - - -
    -

    Inherited from - EventTarget: - src/utils/EventTarget.js:68 -

    - - - -
    - -
    -

    Emit an event.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - event - Object - - -
      - -
      - -
        -
      • - type - String - -
        - -
        - -
      • -
      -
    • -
    -
    - -
    -

    Returns:

    - -
    - EventTarget: -

    The self object, for chainability.

    - -
    -
    - - -
    -
    -

    getContactMaterial

    - -
    - (
      -
    • - m1 -
    • -
    • - m2 -
    • -
    ) -
    - - - ContactMaterial - - - - - - - - -
    -

    - Defined in - src/world/World.js:206 -

    - - - -
    - -
    -

    Get the contact material between materials m1 and m2

    - -
    - -
    -

    Parameters:

    - - -
    - -
    -

    Returns:

    - -
    - ContactMaterial: -

    The contact material if it was found.

    - -
    -
    - - -
    -
    -

    hasEventListener

    - -
    - (
      -
    • - type -
    • -
    • - listener -
    • -
    ) -
    - - - Boolean - - - - - - - - -
    -

    Inherited from - EventTarget: - src/utils/EventTarget.js:34 -

    - - - -
    - -
    -

    Check if an event listener is added

    - -
    - -
    -

    Parameters:

    - -
      -
    • - type - String - - -
      - -
      - -
    • -
    • - listener - Function - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Boolean: -
    -
    - - -
    -
    -

    numObjects

    - - () - - - Number - - - deprecated - - - - - - -
    -

    - Defined in - src/world/World.js:217 -

    - - - -
    - -
    -

    Get number of objects in the world.

    - -
    - - -
    -

    Returns:

    - -
    - Number: -
    -
    - - -
    -
    -

    raycastAll

    - -
    - (
      -
    • - from -
    • -
    • - to -
    • -
    • - options -
    • -
    • - callback -
    • -
    ) -
    - - - Boolean - - - - - - - - -
    -

    - Defined in - src/world/World.js:308 -

    - - - -
    - -
    -

    Ray cast against all bodies. The provided callback will be executed for each hit with a RaycastResult as single argument.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - from - Vec3 - - -
      - -
      - -
    • -
    • - to - Vec3 - - -
      - -
      - -
    • -
    • - options - Object - - -
      - -
      - -
        -
      • - [collisionFilterMask=-1] - Number - optional - -
        - -
        - -
      • -
      • - [collisionFilterGroup=-1] - Number - optional - -
        - -
        - -
      • -
      • - [skipBackfaces=false] - Boolean - optional - -
        - -
        - -
      • -
      • - [checkCollisionResponse=true] - Boolean - optional - -
        - -
        - -
      • -
      -
    • -
    • - callback - Function - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Boolean: -

    True if any body was hit.

    - -
    -
    - - -
    -
    -

    raycastAny

    - -
    - (
      -
    • - from -
    • -
    • - to -
    • -
    • - options -
    • -
    • - result -
    • -
    ) -
    - - - Boolean - - - - - - - - -
    -

    - Defined in - src/world/World.js:329 -

    - - - -
    - -
    -

    Ray cast, and stop at the first result. Note that the order is random - but the method is fast.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - from - Vec3 - - -
      - -
      - -
    • -
    • - to - Vec3 - - -
      - -
      - -
    • -
    • - options - Object - - -
      - -
      - -
        -
      • - [collisionFilterMask=-1] - Number - optional - -
        - -
        - -
      • -
      • - [collisionFilterGroup=-1] - Number - optional - -
        - -
        - -
      • -
      • - [skipBackfaces=false] - Boolean - optional - -
        - -
        - -
      • -
      • - [checkCollisionResponse=true] - Boolean - optional - -
        - -
        - -
      • -
      -
    • -
    • - result - RaycastResult - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Boolean: -

    True if any body was hit.

    - -
    -
    - - -
    -
    -

    raycastClosest

    - -
    - (
      -
    • - from -
    • -
    • - to -
    • -
    • - options -
    • -
    • - result -
    • -
    ) -
    - - - Boolean - - - - - - - - -
    -

    - Defined in - src/world/World.js:350 -

    - - - -
    - -
    -

    Ray cast, and return information of the closest hit.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - from - Vec3 - - -
      - -
      - -
    • -
    • - to - Vec3 - - -
      - -
      - -
    • -
    • - options - Object - - -
      - -
      - -
        -
      • - [collisionFilterMask=-1] - Number - optional - -
        - -
        - -
      • -
      • - [collisionFilterGroup=-1] - Number - optional - -
        - -
        - -
      • -
      • - [skipBackfaces=false] - Boolean - optional - -
        - -
        - -
      • -
      • - [checkCollisionResponse=true] - Boolean - optional - -
        - -
        - -
      • -
      -
    • -
    • - result - RaycastResult - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Boolean: -

    True if any body was hit.

    - -
    -
    - - -
    -
    -

    rayTest

    - -
    - (
      -
    • - from -
    • -
    • - to -
    • -
    • - result -
    • -
    ) -
    - - - deprecated - - - - - - -
    -

    - Defined in - src/world/World.js:286 -

    - - -

    Deprecated: Use .raycastAll, .raycastClosest or .raycastAny instead.

    - -
    - -
    -

    Raycast test

    - -
    - -
    -

    Parameters:

    - -
      -
    • - from - Vec3 - - -
      - -
      - -
    • -
    • - to - Vec3 - - -
      - -
      - -
    • -
    • - result - Function | RaycastResult - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    remove

    - -
    - (
      -
    • - body -
    • -
    ) -
    - - - deprecated - - - - - - -
    -

    - Defined in - src/world/World.js:371 -

    - - -

    Deprecated: Use .removeBody instead

    - -
    - -
    -

    Remove a rigid body from the simulation.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - body - Body - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    removeBody

    - -
    - (
      -
    • - body -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/world/World.js:396 -

    - - - -
    - -
    -

    Remove a rigid body from the simulation.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - body - Body - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    removeConstraint

    - -
    - (
      -
    • - c -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/world/World.js:274 -

    - - - -
    - -
    -

    Removes a constraint

    - -
    - -
    -

    Parameters:

    - - -
    - - - -
    -
    -

    removeEventListener

    - -
    - (
      -
    • - type -
    • -
    • - listener -
    • -
    ) -
    - - - EventTarget - - - - - - - - -
    -

    Inherited from - EventTarget: - src/utils/EventTarget.js:50 -

    - - - -
    - -
    -

    Remove an event listener

    - -
    - -
    -

    Parameters:

    - -
      -
    • - type - String - - -
      - -
      - -
    • -
    • - listener - Function - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - EventTarget: -

    The self object, for chainability.

    - -
    -
    - - -
    -
    -

    step

    - -
    - (
      -
    • - dt -
    • -
    • - [timeSinceLastCalled] -
    • -
    • - [maxSubSteps=10] -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/world/World.js:443 -

    - - - -
    - -
    -

    Step the physics world forward in time.

    -

    There are two modes. The simple mode is fixed timestepping without interpolation. In this case you only use the first argument. The second case uses interpolation. In that you also provide the time since the function was last used, as well as the maximum fixed timesteps to take.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - dt - Number - - -
      -

      The fixed time step size to use.

      - -
      - -
    • -
    • - [timeSinceLastCalled] - Number - optional - - -
      -

      The time elapsed since the function was last called.

      - -
      - -
    • -
    • - [maxSubSteps=10] - Number - optional - - -
      -

      Maximum number of fixed steps to take per function call.

      - -
      - -
    • -
    -
    - - - -
    -

    Example:

    - -
    -
    // fixed timestepping without interpolation
    -world.step(1/60);
    -
    -
    -
    -
    -
    -

    step

    - -
    - (
      -
    • - dt -
    • -
    ) -
    - - - - - - - - -
    -

    - Defined in - src/world/World.js:517 -

    - - - -
    - -
    -

    Step the simulation

    - -
    - -
    -

    Parameters:

    - -
      -
    • - dt - Number - - -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    -

    Properties

    - -
    -

    allowSleep

    - Boolean - - - - - -
    -

    - Defined in - src/world/World.js:39 -

    - - -
    - -
    -

    Makes bodies go to sleep when they've been inactive

    - -
    - - - -
    -
    -

    bodies

    - Array - - - - - -
    -

    - Defined in - src/world/World.js:100 -

    - - -
    - -
    - -
    - - - -
    -
    -

    broadphase

    - Broadphase - - - - - -
    -

    - Defined in - src/world/World.js:94 -

    - - -
    - -
    - -
    - - - -
    -
    -

    collisionMatrix

    - ArrayCollisionMatrix - - - - - -
    -

    - Defined in - src/world/World.js:124 -

    - - -
    - -
    - -
    - - - -
    -
    -

    collisionMatrixPrevious

    - ArrayCollisionMatrix - - - - - -
    -

    - Defined in - src/world/World.js:130 -

    - - -
    - -
    -

    CollisionMatrix from the previous step.

    - -
    - - - -
    -
    -

    constraints

    - Array - - - - - -
    -

    - Defined in - src/world/World.js:112 -

    - - -
    - -
    - -
    - - - -
    -
    -

    contactmaterials

    - Array - - - - - -
    -

    - Defined in - src/world/World.js:144 -

    - - -
    - -
    - -
    - - - -
    -
    -

    contactMaterialTable

    - TupleDictionary - - - - - -
    -

    - Defined in - src/world/World.js:150 -

    - - -
    - -
    -

    Used to look up a ContactMaterial given two instances of Material.

    - -
    - - - -
    -
    -

    contacts

    - Array - - - - - -
    -

    - Defined in - src/world/World.js:46 -

    - - -
    - -
    -

    All the current contacts (instances of ContactEquation) in the world.

    - -
    - - - -
    -
    -

    defaultContactMaterial

    - ContactMaterial - - - - - -
    -

    - Defined in - src/world/World.js:158 -

    - - -
    - -
    -

    This contact material is used if no suitable contactmaterial is found for a contact.

    - -
    - - - -
    -
    -

    doProfiling

    - Boolean - - - - - -
    -

    - Defined in - src/world/World.js:165 -

    - - -
    - -
    - -
    - - - -
    -
    -

    dt

    - Number - - - - - -
    -

    - Defined in - src/world/World.js:33 -

    - - -
    - -
    -

    Currently / last used timestep. Is set to -1 if not available. This value is updated before each internal step, which means that it is "fresh" inside event callbacks.

    - -
    - - - -
    -
    -

    gravity

    - Vec3 - - - - - -
    -

    - Defined in - src/world/World.js:88 -

    - - -
    - -
    - -
    - - - -
    -
    -

    materials

    - Array - - - - - -
    -

    - Defined in - src/world/World.js:137 -

    - - -
    - -
    -

    All added materials

    - -
    - - - -
    -
    -

    narrowphase

    - Narrowphase - - - - - -
    -

    - Defined in - src/world/World.js:118 -

    - - -
    - -
    - -
    - - - -
    -
    -

    profile

    - Object - - - - - -
    -

    - Defined in - src/world/World.js:171 -

    - - -
    - -
    - -
    - - - -
    -
    -

    quatNormalizeFast

    - Boolean - - - - - -
    -

    - Defined in - src/world/World.js:61 -

    - - -
    - -
    -

    Set to true to use fast quaternion normalization. It is often enough accurate to use. If bodies tend to explode, set to false.

    - -
    - - - -
    -
    -

    quatNormalizeSkip

    - Number - - - - - -
    -

    - Defined in - src/world/World.js:54 -

    - - -
    - -
    -

    How often to normalize quaternions. Set to 0 for every step, 1 for every second etc.. A larger value increases performance. If bodies tend to explode, set to a smaller value (zero to be sure nothing can go wrong).

    - -
    - - - -
    -
    -

    solver

    - Solver - - - - - -
    -

    - Defined in - src/world/World.js:106 -

    - - -
    - -
    - -
    - - - -
    -
    -

    stepnumber

    - Number - - - - - -
    -

    - Defined in - src/world/World.js:77 -

    - - -
    - -
    -

    Number of timesteps taken since start

    - -
    - - - -
    -
    -

    subsystems

    - Array - - - - - -
    -

    - Defined in - src/world/World.js:183 -

    - - -
    - -
    - -
    - - - -
    -
    -

    time

    - Number - - - - - -
    -

    - Defined in - src/world/World.js:70 -

    - - -
    - -
    -

    The wall-clock time since simulation start

    - -
    - - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/classes/index.html b/docs/classes/index.html deleted file mode 100644 index 487fe15b2..000000000 --- a/docs/classes/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - Redirector - - - - Click here to redirect - - diff --git a/docs/data.json b/docs/data.json deleted file mode 100644 index 28e3c2c9e..000000000 --- a/docs/data.json +++ /dev/null @@ -1,10836 +0,0 @@ -{ - "project": { - "name": "cannon", - "description": "A lightweight 3D physics engine written in JavaScript.", - "version": "0.6.1" - }, - "files": { - "src/collision/AABB.js": { - "name": "src/collision/AABB.js", - "modules": {}, - "classes": { - "AABB": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/collision/ArrayCollisionMatrix.js": { - "name": "src/collision/ArrayCollisionMatrix.js", - "modules": {}, - "classes": { - "ArrayCollisionMatrix": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/collision/Broadphase.js": { - "name": "src/collision/Broadphase.js", - "modules": {}, - "classes": { - "Broadphase": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/collision/GridBroadphase.js": { - "name": "src/collision/GridBroadphase.js", - "modules": {}, - "classes": { - "GridBroadphase": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/collision/NaiveBroadphase.js": { - "name": "src/collision/NaiveBroadphase.js", - "modules": {}, - "classes": { - "NaiveBroadphase": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/collision/ObjectCollisionMatrix.js": { - "name": "src/collision/ObjectCollisionMatrix.js", - "modules": {}, - "classes": { - "ObjectCollisionMatrix": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/collision/Ray.js": { - "name": "src/collision/Ray.js", - "modules": {}, - "classes": { - "Ray": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/collision/RaycastResult.js": { - "name": "src/collision/RaycastResult.js", - "modules": {}, - "classes": { - "RaycastResult": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/collision/SAPBroadphase.js": { - "name": "src/collision/SAPBroadphase.js", - "modules": {}, - "classes": { - "SAPBroadphase": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/constraints/ConeTwistConstraint.js": { - "name": "src/constraints/ConeTwistConstraint.js", - "modules": {}, - "classes": { - "ConeTwistConstraint": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/constraints/Constraint.js": { - "name": "src/constraints/Constraint.js", - "modules": {}, - "classes": { - "Constraint": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/constraints/DistanceConstraint.js": { - "name": "src/constraints/DistanceConstraint.js", - "modules": {}, - "classes": { - "DistanceConstraint": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/constraints/HingeConstraint.js": { - "name": "src/constraints/HingeConstraint.js", - "modules": {}, - "classes": { - "HingeConstraint": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/constraints/LockConstraint.js": { - "name": "src/constraints/LockConstraint.js", - "modules": {}, - "classes": { - "LockConstraint": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/constraints/PointToPointConstraint.js": { - "name": "src/constraints/PointToPointConstraint.js", - "modules": {}, - "classes": { - "PointToPointConstraint": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/demo/Demo.js": { - "name": "src/demo/Demo.js", - "modules": {}, - "classes": { - "Demo": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/equations/ConeEquation.js": { - "name": "src/equations/ConeEquation.js", - "modules": {}, - "classes": { - "ConeEquation": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/equations/ContactEquation.js": { - "name": "src/equations/ContactEquation.js", - "modules": {}, - "classes": { - "ContactEquation": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/equations/Equation.js": { - "name": "src/equations/Equation.js", - "modules": {}, - "classes": { - "Equation": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/equations/FrictionEquation.js": { - "name": "src/equations/FrictionEquation.js", - "modules": {}, - "classes": { - "FrictionEquation": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/equations/RotationalEquation.js": { - "name": "src/equations/RotationalEquation.js", - "modules": {}, - "classes": { - "RotationalEquation": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/equations/RotationalMotorEquation.js": { - "name": "src/equations/RotationalMotorEquation.js", - "modules": {}, - "classes": { - "RotationalMotorEquation": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/material/ContactMaterial.js": { - "name": "src/material/ContactMaterial.js", - "modules": {}, - "classes": { - "ContactMaterial": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/material/Material.js": { - "name": "src/material/Material.js", - "modules": {}, - "classes": { - "Material": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/math/JacobianElement.js": { - "name": "src/math/JacobianElement.js", - "modules": {}, - "classes": { - "JacobianElement": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/math/Mat3.js": { - "name": "src/math/Mat3.js", - "modules": {}, - "classes": { - "Mat3": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/math/Quaternion.js": { - "name": "src/math/Quaternion.js", - "modules": {}, - "classes": { - "Quaternion": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/math/Transform.js": { - "name": "src/math/Transform.js", - "modules": {}, - "classes": { - "Transform": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/math/Vec3.js": { - "name": "src/math/Vec3.js", - "modules": {}, - "classes": { - "Vec3": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/objects/Body.js": { - "name": "src/objects/Body.js", - "modules": {}, - "classes": { - "Body": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/objects/RaycastVehicle.js": { - "name": "src/objects/RaycastVehicle.js", - "modules": {}, - "classes": { - "RaycastVehicle": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/objects/RigidVehicle.js": { - "name": "src/objects/RigidVehicle.js", - "modules": {}, - "classes": { - "RigidVehicle": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/objects/SPHSystem.js": { - "name": "src/objects/SPHSystem.js", - "modules": {}, - "classes": { - "SPHSystem": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/objects/Spring.js": { - "name": "src/objects/Spring.js", - "modules": {}, - "classes": { - "Spring": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/objects/WheelInfo.js": { - "name": "src/objects/WheelInfo.js", - "modules": {}, - "classes": { - "WheelInfo": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/shapes/Box.js": { - "name": "src/shapes/Box.js", - "modules": {}, - "classes": { - "Box": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/shapes/ConvexPolyhedron.js": { - "name": "src/shapes/ConvexPolyhedron.js", - "modules": {}, - "classes": { - "ConvexPolyhedron": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/shapes/Cylinder.js": { - "name": "src/shapes/Cylinder.js", - "modules": {}, - "classes": { - "Cylinder": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/shapes/Heightfield.js": { - "name": "src/shapes/Heightfield.js", - "modules": {}, - "classes": { - "Heightfield": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/shapes/Particle.js": { - "name": "src/shapes/Particle.js", - "modules": {}, - "classes": { - "Particle": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/shapes/Plane.js": { - "name": "src/shapes/Plane.js", - "modules": {}, - "classes": { - "Plane": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/shapes/Shape.js": { - "name": "src/shapes/Shape.js", - "modules": {}, - "classes": { - "Shape": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/shapes/Sphere.js": { - "name": "src/shapes/Sphere.js", - "modules": {}, - "classes": { - "Sphere": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/shapes/Trimesh.js": { - "name": "src/shapes/Trimesh.js", - "modules": {}, - "classes": { - "Trimesh": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/solver/GSSolver.js": { - "name": "src/solver/GSSolver.js", - "modules": {}, - "classes": { - "GSSolver": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/solver/Solver.js": { - "name": "src/solver/Solver.js", - "modules": {}, - "classes": { - "Solver": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/solver/SplitSolver.js": { - "name": "src/solver/SplitSolver.js", - "modules": {}, - "classes": { - "SplitSolver": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/utils/EventTarget.js": { - "name": "src/utils/EventTarget.js", - "modules": {}, - "classes": { - "EventTarget": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/utils/Octree.js": { - "name": "src/utils/Octree.js", - "modules": {}, - "classes": { - "OctreeNode": 1, - "Octree": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/utils/Pool.js": { - "name": "src/utils/Pool.js", - "modules": {}, - "classes": { - "Pool": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/utils/TupleDictionary.js": { - "name": "src/utils/TupleDictionary.js", - "modules": {}, - "classes": { - "TupleDictionary": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/utils/Utils.js": { - "name": "src/utils/Utils.js", - "modules": {}, - "classes": {}, - "fors": {}, - "namespaces": {} - }, - "src/utils/Vec3Pool.js": { - "name": "src/utils/Vec3Pool.js", - "modules": {}, - "classes": { - "Vec3Pool": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/world/Narrowphase.js": { - "name": "src/world/Narrowphase.js", - "modules": {}, - "classes": { - "Narrowphase": 1 - }, - "fors": {}, - "namespaces": {} - }, - "src/world/World.js": { - "name": "src/world/World.js", - "modules": {}, - "classes": { - "World": 1 - }, - "fors": {}, - "namespaces": {} - } - }, - "modules": {}, - "classes": { - "AABB": { - "name": "AABB", - "shortname": "AABB", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/collision/AABB.js", - "line": 6, - "description": "Axis aligned bounding box class.", - "is_constructor": 1, - "params": [ - { - "name": "options", - "description": "", - "type": "Object", - "optional": true, - "props": [ - { - "name": "upperBound", - "description": "", - "type": "Vec3", - "optional": true - }, - { - "name": "lowerBound", - "description": "", - "type": "Vec3", - "optional": true - } - ] - } - ] - }, - "ArrayCollisionMatrix": { - "name": "ArrayCollisionMatrix", - "shortname": "ArrayCollisionMatrix", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/collision/ArrayCollisionMatrix.js", - "line": 3, - "description": "Collision \"matrix\". It's actually a triangular-shaped array of whether two bodies are touching this step, for reference next step", - "is_constructor": 1 - }, - "Broadphase": { - "name": "Broadphase", - "shortname": "Broadphase", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/collision/Broadphase.js", - "line": 9, - "description": "Base class for broadphase implementations", - "is_constructor": 1, - "author": "schteppe" - }, - "GridBroadphase": { - "name": "GridBroadphase", - "shortname": "GridBroadphase", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/collision/GridBroadphase.js", - "line": 7, - "description": "Axis aligned uniform grid broadphase.", - "is_constructor": 1, - "extends": "Broadphase", - "todo": [ - "Needs support for more than just planes and spheres." - ], - "params": [ - { - "name": "aabbMin", - "description": "", - "type": "Vec3" - }, - { - "name": "aabbMax", - "description": "", - "type": "Vec3" - }, - { - "name": "nx", - "description": "Number of boxes along x", - "type": "Number" - }, - { - "name": "ny", - "description": "Number of boxes along y", - "type": "Number" - }, - { - "name": "nz", - "description": "Number of boxes along z", - "type": "Number" - } - ] - }, - "NaiveBroadphase": { - "name": "NaiveBroadphase", - "shortname": "NaiveBroadphase", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/collision/NaiveBroadphase.js", - "line": 6, - "description": "Naive broadphase implementation, used in lack of better ones.", - "is_constructor": 1, - "extends": "Broadphase" - }, - "ObjectCollisionMatrix": { - "name": "ObjectCollisionMatrix", - "shortname": "ObjectCollisionMatrix", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/collision/ObjectCollisionMatrix.js", - "line": 3, - "description": "Records what objects are colliding with each other", - "is_constructor": 1 - }, - "Ray": { - "name": "Ray", - "shortname": "Ray", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/collision/Ray.js", - "line": 12, - "description": "A line in 3D space that intersects bodies and return points.", - "is_constructor": 1, - "params": [ - { - "name": "from", - "description": "", - "type": "Vec3" - }, - { - "name": "to", - "description": "", - "type": "Vec3" - } - ] - }, - "RaycastResult": { - "name": "RaycastResult", - "shortname": "RaycastResult", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/collision/RaycastResult.js", - "line": 5, - "description": "Storage for Ray casting data.", - "is_constructor": 1 - }, - "SAPBroadphase": { - "name": "SAPBroadphase", - "shortname": "SAPBroadphase", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/collision/SAPBroadphase.js", - "line": 6, - "description": "Sweep and prune broadphase along one axis.", - "is_constructor": 1, - "params": [ - { - "name": "world", - "description": "", - "type": "World", - "optional": true - } - ], - "extends": "Broadphase" - }, - "ConeTwistConstraint": { - "name": "ConeTwistConstraint", - "shortname": "ConeTwistConstraint", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/constraints/ConeTwistConstraint.js", - "line": 10, - "is_constructor": 1, - "author": "schteppe", - "params": [ - { - "name": "bodyA", - "description": "", - "type": "Body" - }, - { - "name": "bodyB", - "description": "", - "type": "Body" - }, - { - "name": "options", - "description": "", - "type": "Object", - "optional": true, - "props": [ - { - "name": "pivotA", - "description": "", - "type": "Vec3", - "optional": true - }, - { - "name": "pivotB", - "description": "", - "type": "Vec3", - "optional": true - }, - { - "name": "axisA", - "description": "", - "type": "Vec3", - "optional": true - }, - { - "name": "axisB", - "description": "", - "type": "Vec3", - "optional": true - }, - { - "name": "maxForce", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "1e6" - } - ] - } - ], - "extends": "PointToPointConstraint" - }, - "Constraint": { - "name": "Constraint", - "shortname": "Constraint", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/constraints/Constraint.js", - "line": 5, - "description": "Constraint base class", - "author": "schteppe", - "is_constructor": 1, - "params": [ - { - "name": "bodyA", - "description": "", - "type": "Body" - }, - { - "name": "bodyB", - "description": "", - "type": "Body" - }, - { - "name": "options", - "description": "", - "type": "Object", - "optional": true, - "props": [ - { - "name": "collideConnected", - "description": "", - "type": "Boolean", - "optional": true, - "optdefault": "true" - }, - { - "name": "wakeUpBodies", - "description": "", - "type": "Boolean", - "optional": true, - "optdefault": "true" - } - ] - } - ] - }, - "DistanceConstraint": { - "name": "DistanceConstraint", - "shortname": "DistanceConstraint", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/constraints/DistanceConstraint.js", - "line": 6, - "description": "Constrains two bodies to be at a constant distance from each others center of mass.", - "is_constructor": 1, - "author": "schteppe", - "params": [ - { - "name": "bodyA", - "description": "", - "type": "Body" - }, - { - "name": "bodyB", - "description": "", - "type": "Body" - }, - { - "name": "distance", - "description": "The distance to keep. If undefined, it will be set to the current distance between bodyA and bodyB", - "type": "Number", - "optional": true - }, - { - "name": "maxForce", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "1e6" - } - ], - "extends": "Constraint" - }, - "HingeConstraint": { - "name": "HingeConstraint", - "shortname": "HingeConstraint", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/constraints/HingeConstraint.js", - "line": 10, - "description": "Hinge constraint. Think of it as a door hinge. It tries to keep the door in the correct place and with the correct orientation.", - "is_constructor": 1, - "author": "schteppe", - "params": [ - { - "name": "bodyA", - "description": "", - "type": "Body" - }, - { - "name": "bodyB", - "description": "", - "type": "Body" - }, - { - "name": "options", - "description": "", - "type": "Object", - "optional": true, - "props": [ - { - "name": "pivotA", - "description": "A point defined locally in bodyA. This defines the offset of axisA.", - "type": "Vec3", - "optional": true - }, - { - "name": "axisA", - "description": "An axis that bodyA can rotate around, defined locally in bodyA.", - "type": "Vec3", - "optional": true - }, - { - "name": "pivotB", - "description": "", - "type": "Vec3", - "optional": true - }, - { - "name": "axisB", - "description": "", - "type": "Vec3", - "optional": true - }, - { - "name": "maxForce", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "1e6" - } - ] - } - ], - "extends": "PointToPointConstraint" - }, - "LockConstraint": { - "name": "LockConstraint", - "shortname": "LockConstraint", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/constraints/LockConstraint.js", - "line": 10, - "description": "Lock constraint. Will remove all degrees of freedom between the bodies.", - "is_constructor": 1, - "author": "schteppe", - "params": [ - { - "name": "bodyA", - "description": "", - "type": "Body" - }, - { - "name": "bodyB", - "description": "", - "type": "Body" - }, - { - "name": "options", - "description": "", - "type": "Object", - "optional": true, - "props": [ - { - "name": "maxForce", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "1e6" - } - ] - } - ], - "extends": "PointToPointConstraint" - }, - "PointToPointConstraint": { - "name": "PointToPointConstraint", - "shortname": "PointToPointConstraint", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/constraints/PointToPointConstraint.js", - "line": 7, - "description": "Connects two bodies at given offset points.", - "extends": "Constraint", - "is_constructor": 1, - "params": [ - { - "name": "bodyA", - "description": "", - "type": "Body" - }, - { - "name": "pivotA", - "description": "The point relative to the center of mass of bodyA which bodyA is constrained to.", - "type": "Vec3" - }, - { - "name": "bodyB", - "description": "Body that will be constrained in a similar way to the same point as bodyA. We will therefore get a link between bodyA and bodyB. If not specified, bodyA will be constrained to a static point.", - "type": "Body" - }, - { - "name": "pivotB", - "description": "See pivotA.", - "type": "Vec3" - }, - { - "name": "maxForce", - "description": "The maximum force that should be applied to constrain the bodies.", - "type": "Number" - } - ], - "example": [ - "\n var bodyA = new Body({ mass: 1 });\n var bodyB = new Body({ mass: 1 });\n bodyA.position.set(-1, 0, 0);\n bodyB.position.set(1, 0, 0);\n bodyA.addShape(shapeA);\n bodyB.addShape(shapeB);\n world.addBody(bodyA);\n world.addBody(bodyB);\n var localPivotA = new Vec3(1, 0, 0);\n var localPivotB = new Vec3(-1, 0, 0);\n var constraint = new PointToPointConstraint(bodyA, localPivotA, bodyB, localPivotB);\n world.addConstraint(constraint);" - ] - }, - "Demo": { - "name": "Demo", - "shortname": "Demo", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/demo/Demo.js", - "line": 5, - "description": "Demo framework class. If you want to learn how to connect Cannon.js with Three.js, please look at the examples/ instead.", - "is_constructor": 1, - "params": [ - { - "name": "options", - "description": "", - "type": "Object" - } - ] - }, - "ConeEquation": { - "name": "ConeEquation", - "shortname": "ConeEquation", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/equations/ConeEquation.js", - "line": 7, - "description": "Cone equation. Works to keep the given body world vectors aligned, or tilted within a given angle from each other.", - "is_constructor": 1, - "author": "schteppe", - "params": [ - { - "name": "bodyA", - "description": "", - "type": "Body" - }, - { - "name": "bodyB", - "description": "", - "type": "Body" - }, - { - "name": "options.axisA", - "description": "Local axis in A", - "type": "Vec3", - "optional": true - }, - { - "name": "options.axisB", - "description": "Local axis in B", - "type": "Vec3", - "optional": true - }, - { - "name": "options.angle", - "description": "The \"cone angle\" to keep", - "type": "Vec3", - "optional": true - }, - { - "name": "options.maxForce", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "1e6" - } - ], - "extends": "Equation" - }, - "ContactEquation": { - "name": "ContactEquation", - "shortname": "ContactEquation", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/equations/ContactEquation.js", - "line": 7, - "description": "Contact/non-penetration constraint equation", - "is_constructor": 1, - "author": "schteppe", - "params": [ - { - "name": "bodyA", - "description": "", - "type": "Body" - }, - { - "name": "bodyB", - "description": "", - "type": "Body" - } - ], - "extends": "Equation" - }, - "Equation": { - "name": "Equation", - "shortname": "Equation", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/equations/Equation.js", - "line": 6, - "description": "Equation base class", - "is_constructor": 1, - "author": "schteppe", - "params": [ - { - "name": "bi", - "description": "", - "type": "Body" - }, - { - "name": "bj", - "description": "", - "type": "Body" - }, - { - "name": "minForce", - "description": "Minimum (read: negative max) force to be applied by the constraint.", - "type": "Number" - }, - { - "name": "maxForce", - "description": "Maximum (read: positive max) force to be applied by the constraint.", - "type": "Number" - } - ] - }, - "FrictionEquation": { - "name": "FrictionEquation", - "shortname": "FrictionEquation", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/equations/FrictionEquation.js", - "line": 7, - "description": "Constrains the slipping in a contact along a tangent", - "is_constructor": 1, - "author": "schteppe", - "params": [ - { - "name": "bodyA", - "description": "", - "type": "Body" - }, - { - "name": "bodyB", - "description": "", - "type": "Body" - }, - { - "name": "slipForce", - "description": "should be +-F_friction = +-mu * F_normal = +-mu * m * g", - "type": "Number" - } - ], - "extends": "Equation" - }, - "RotationalEquation": { - "name": "RotationalEquation", - "shortname": "RotationalEquation", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/equations/RotationalEquation.js", - "line": 7, - "description": "Rotational constraint. Works to keep the local vectors orthogonal to each other in world space.", - "is_constructor": 1, - "author": "schteppe", - "params": [ - { - "name": "bodyA", - "description": "", - "type": "Body" - }, - { - "name": "bodyB", - "description": "", - "type": "Body" - }, - { - "name": "options.axisA", - "description": "", - "type": "Vec3", - "optional": true - }, - { - "name": "options.axisB", - "description": "", - "type": "Vec3", - "optional": true - }, - { - "name": "options.maxForce", - "description": "", - "type": "Number", - "optional": true - } - ], - "extends": "Equation" - }, - "RotationalMotorEquation": { - "name": "RotationalMotorEquation", - "shortname": "RotationalMotorEquation", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/equations/RotationalMotorEquation.js", - "line": 7, - "description": "Rotational motor constraint. Tries to keep the relative angular velocity of the bodies to a given value.", - "is_constructor": 1, - "author": "schteppe", - "params": [ - { - "name": "bodyA", - "description": "", - "type": "Body" - }, - { - "name": "bodyB", - "description": "", - "type": "Body" - }, - { - "name": "maxForce", - "description": "", - "type": "Number" - } - ], - "extends": "Equation" - }, - "ContactMaterial": { - "name": "ContactMaterial", - "shortname": "ContactMaterial", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/material/ContactMaterial.js", - "line": 5, - "description": "Defines what happens when two materials meet.", - "is_constructor": 1, - "params": [ - { - "name": "m1", - "description": "", - "type": "Material" - }, - { - "name": "m2", - "description": "", - "type": "Material" - }, - { - "name": "options", - "description": "", - "type": "Object", - "optional": true, - "props": [ - { - "name": "friction", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "0.3" - }, - { - "name": "restitution", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "0.3" - }, - { - "name": "contactEquationStiffness", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "1e7" - }, - { - "name": "contactEquationRelaxation", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "3" - }, - { - "name": "frictionEquationStiffness", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "1e7" - }, - { - "name": "frictionEquationRelaxation", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "3" - } - ] - } - ] - }, - "Material": { - "name": "Material", - "shortname": "Material", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/material/Material.js", - "line": 3, - "description": "Defines a physics material.", - "is_constructor": 1, - "params": [ - { - "name": "options", - "description": "", - "type": "Object", - "optional": true - } - ], - "author": "schteppe" - }, - "JacobianElement": { - "name": "JacobianElement", - "shortname": "JacobianElement", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/math/JacobianElement.js", - "line": 5, - "description": "An element containing 6 entries, 3 spatial and 3 rotational degrees of freedom.", - "is_constructor": 1 - }, - "Mat3": { - "name": "Mat3", - "shortname": "Mat3", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/math/Mat3.js", - "line": 5, - "description": "A 3x3 matrix.", - "is_constructor": 1, - "params": [ - { - "name": "array", - "description": "elements Array of nine elements. Optional." - } - ], - "author": "schteppe / http://github.com/schteppe" - }, - "Quaternion": { - "name": "Quaternion", - "shortname": "Quaternion", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/math/Quaternion.js", - "line": 5, - "description": "A Quaternion describes a rotation in 3D space. The Quaternion is mathematically defined as Q = x*i + y*j + z*k + w, where (i,j,k) are imaginary basis vectors. (x,y,z) can be seen as a vector related to the axis of rotation, while the real multiplier, w, is related to the amount of rotation.", - "is_constructor": 1, - "params": [ - { - "name": "x", - "description": "Multiplier of the imaginary basis vector i.", - "type": "Number" - }, - { - "name": "y", - "description": "Multiplier of the imaginary basis vector j.", - "type": "Number" - }, - { - "name": "z", - "description": "Multiplier of the imaginary basis vector k.", - "type": "Number" - }, - { - "name": "w", - "description": "Multiplier of the real part.", - "type": "Number" - } - ], - "see": [ - "http://en.wikipedia.org/wiki/Quaternion" - ] - }, - "Transform": { - "name": "Transform", - "shortname": "Transform", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/math/Transform.js", - "line": 6, - "is_constructor": 1 - }, - "Vec3": { - "name": "Vec3", - "shortname": "Vec3", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/math/Vec3.js", - "line": 5, - "description": "3-dimensional vector", - "is_constructor": 1, - "params": [ - { - "name": "x", - "description": "", - "type": "Number" - }, - { - "name": "y", - "description": "", - "type": "Number" - }, - { - "name": "z", - "description": "", - "type": "Number" - } - ], - "author": "schteppe", - "example": [ - "\n var v = new Vec3(1, 2, 3);\n console.log('x=' + v.x); // x=1" - ] - }, - "Body": { - "name": "Body", - "shortname": "Body", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/objects/Body.js", - "line": 12, - "description": "Base class for all body types.", - "is_constructor": 1, - "extends": "EventTarget", - "params": [ - { - "name": "options", - "description": "", - "type": "Object", - "optional": true, - "props": [ - { - "name": "position", - "description": "", - "type": "Vec3", - "optional": true - }, - { - "name": "velocity", - "description": "", - "type": "Vec3", - "optional": true - }, - { - "name": "angularVelocity", - "description": "", - "type": "Vec3", - "optional": true - }, - { - "name": "quaternion", - "description": "", - "type": "Quaternion", - "optional": true - }, - { - "name": "mass", - "description": "", - "type": "Number", - "optional": true - }, - { - "name": "material", - "description": "", - "type": "Material", - "optional": true - }, - { - "name": "type", - "description": "", - "type": "Number", - "optional": true - }, - { - "name": "linearDamping", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "0.01" - }, - { - "name": "angularDamping", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "0.01" - }, - { - "name": "allowSleep", - "description": "", - "type": "Boolean", - "optional": true, - "optdefault": "true" - }, - { - "name": "sleepSpeedLimit", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "0.1" - }, - { - "name": "sleepTimeLimit", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "1" - }, - { - "name": "collisionFilterGroup", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "1" - }, - { - "name": "collisionFilterMask", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "1" - }, - { - "name": "fixedRotation", - "description": "", - "type": "Boolean", - "optional": true, - "optdefault": "false" - }, - { - "name": "shape", - "description": "", - "type": "Body", - "optional": true - } - ] - } - ], - "example": [ - "\n var body = new Body({\n mass: 1\n });\n var shape = new Sphere(1);\n body.addShape(shape);\n world.add(body);" - ] - }, - "RaycastVehicle": { - "name": "RaycastVehicle", - "shortname": "RaycastVehicle", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/objects/RaycastVehicle.js", - "line": 10, - "description": "Vehicle helper class that casts rays from the wheel positions towards the ground and applies forces.", - "is_constructor": 1, - "params": [ - { - "name": "options", - "description": "", - "type": "Object", - "optional": true, - "props": [ - { - "name": "chassisBody", - "description": "The car chassis body.", - "type": "Body", - "optional": true - }, - { - "name": "indexRightAxis", - "description": "Axis to use for right. x=0, y=1, z=2", - "type": "Integer", - "optional": true - }, - { - "name": "indexLeftAxis", - "description": "", - "type": "Integer", - "optional": true - }, - { - "name": "indexUpAxis", - "description": "", - "type": "Integer", - "optional": true - } - ] - } - ] - }, - "RigidVehicle": { - "name": "RigidVehicle", - "shortname": "RigidVehicle", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/objects/RigidVehicle.js", - "line": 9, - "description": "Simple vehicle helper class with spherical rigid body wheels.", - "is_constructor": 1, - "params": [ - { - "name": "options.chassisBody", - "description": "", - "type": "Body", - "optional": true - } - ] - }, - "SPHSystem": { - "name": "SPHSystem", - "shortname": "SPHSystem", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/objects/SPHSystem.js", - "line": 10, - "description": "Smoothed-particle hydrodynamics system", - "is_constructor": 1 - }, - "Spring": { - "name": "Spring", - "shortname": "Spring", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/objects/Spring.js", - "line": 5, - "description": "A spring, connecting two bodies.", - "is_constructor": 1, - "params": [ - { - "name": "bodyA", - "description": "", - "type": "Body" - }, - { - "name": "bodyB", - "description": "", - "type": "Body" - }, - { - "name": "options", - "description": "", - "type": "Object", - "optional": true, - "props": [ - { - "name": "restLength", - "description": "A number > 0. Default: 1", - "type": "Number", - "optional": true - }, - { - "name": "stiffness", - "description": "A number >= 0. Default: 100", - "type": "Number", - "optional": true - }, - { - "name": "damping", - "description": "A number >= 0. Default: 1", - "type": "Number", - "optional": true - }, - { - "name": "worldAnchorA", - "description": "Where to hook the spring to body A, in world coordinates.", - "type": "Vec3", - "optional": true - }, - { - "name": "worldAnchorB", - "description": "", - "type": "Vec3", - "optional": true - }, - { - "name": "localAnchorA", - "description": "Where to hook the spring to body A, in local body coordinates.", - "type": "Vec3", - "optional": true - }, - { - "name": "localAnchorB", - "description": "", - "type": "Vec3", - "optional": true - } - ] - } - ] - }, - "WheelInfo": { - "name": "WheelInfo", - "shortname": "WheelInfo", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/objects/WheelInfo.js", - "line": 8, - "is_constructor": 1, - "params": [ - { - "name": "options", - "description": "", - "type": "Object", - "optional": true, - "props": [ - { - "name": "chassisConnectionPointLocal", - "description": "", - "type": "Vec3", - "optional": true - }, - { - "name": "chassisConnectionPointWorld", - "description": "", - "type": "Vec3", - "optional": true - }, - { - "name": "directionLocal", - "description": "", - "type": "Vec3", - "optional": true - }, - { - "name": "directionWorld", - "description": "", - "type": "Vec3", - "optional": true - }, - { - "name": "axleLocal", - "description": "", - "type": "Vec3", - "optional": true - }, - { - "name": "axleWorld", - "description": "", - "type": "Vec3", - "optional": true - }, - { - "name": "suspensionRestLength", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "1" - }, - { - "name": "suspensionMaxLength", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "2" - }, - { - "name": "radius", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "1" - }, - { - "name": "suspensionStiffness", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "100" - }, - { - "name": "dampingCompression", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "10" - }, - { - "name": "dampingRelaxation", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "10" - }, - { - "name": "frictionSlip", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "10000" - }, - { - "name": "steering", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "0" - }, - { - "name": "rotation", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "0" - }, - { - "name": "deltaRotation", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "0" - }, - { - "name": "rollInfluence", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "0.01" - }, - { - "name": "maxSuspensionForce", - "description": "", - "type": "Number", - "optional": true - }, - { - "name": "isFrontWheel", - "description": "", - "type": "Boolean", - "optional": true, - "optdefault": "true" - }, - { - "name": "clippedInvContactDotSuspension", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "1" - }, - { - "name": "suspensionRelativeVelocity", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "0" - }, - { - "name": "suspensionForce", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "0" - }, - { - "name": "skidInfo", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "0" - }, - { - "name": "suspensionLength", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "0" - }, - { - "name": "maxSuspensionTravel", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "1" - }, - { - "name": "useCustomSlidingRotationalSpeed", - "description": "", - "type": "Boolean", - "optional": true, - "optdefault": "false" - }, - { - "name": "customSlidingRotationalSpeed", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "-0.1" - } - ] - } - ] - }, - "Box": { - "name": "Box", - "shortname": "Box", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/shapes/Box.js", - "line": 7, - "description": "A 3d box shape.", - "is_constructor": 1, - "params": [ - { - "name": "halfExtents", - "description": "", - "type": "Vec3" - } - ], - "author": "schteppe", - "extends": "Shape" - }, - "ConvexPolyhedron": { - "name": "ConvexPolyhedron", - "shortname": "ConvexPolyhedron", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/shapes/ConvexPolyhedron.js", - "line": 8, - "description": "A set of polygons describing a convex shape.", - "is_constructor": 1, - "extends": "Shape", - "params": [ - { - "name": "points", - "description": "An array of Vec3's", - "type": "Array" - }, - { - "name": "faces", - "description": "Array of integer arrays, describing which vertices that is included in each face.", - "type": "Array" - } - ], - "author": "schteppe / https://github.com/schteppe", - "see": [ - "http://www.altdevblogaday.com/2011/05/13/contact-generation-between-3d-convex-meshes/", - "http://bullet.googlecode.com/svn/trunk/src/BulletCollision/NarrowPhaseCollision/btPolyhedralContactClipping.cpp" - ], - "todo": [ - "Move the clipping functions to ContactGenerator?", - "Automatically merge coplanar polygons in constructor." - ] - }, - "Cylinder": { - "name": "Cylinder", - "shortname": "Cylinder", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/shapes/Cylinder.js", - "line": 8, - "is_constructor": 1, - "extends": "ConvexPolyhedron", - "author": "schteppe / https://github.com/schteppe", - "params": [ - { - "name": "radiusTop", - "description": "", - "type": "Number" - }, - { - "name": "radiusBottom", - "description": "", - "type": "Number" - }, - { - "name": "height", - "description": "", - "type": "Number" - }, - { - "name": "numSegments", - "description": "The number of segments to build the cylinder out of", - "type": "Number" - } - ] - }, - "Heightfield": { - "name": "Heightfield", - "shortname": "Heightfield", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/shapes/Heightfield.js", - "line": 8, - "description": "Heightfield shape class. Height data is given as an array. These data points are spread out evenly with a given distance.", - "extends": "Shape", - "is_constructor": 1, - "params": [ - { - "name": "data", - "description": "An array of Y values that will be used to construct the terrain.", - "type": "Array" - }, - { - "name": "options", - "description": "", - "type": "Object", - "props": [ - { - "name": "minValue", - "description": "Minimum value of the data points in the data array. Will be computed automatically if not given.", - "type": "Number", - "optional": true - }, - { - "name": "maxValue", - "description": "Maximum value.", - "type": "Number", - "optional": true - }, - { - "name": "elementSize", - "description": "World spacing between the data points in X direction.", - "type": "Number", - "optional": true, - "optdefault": "0.1" - } - ] - } - ], - "todo": [ - "Should be possible to use along all axes", - "not just y" - ], - "example": [ - "\n // Generate some height data (y-values).\n var data = [];\n for(var i = 0; i < 1000; i++){\n var y = 0.5 * Math.cos(0.2 * i);\n data.push(y);\n }\n\n // Create the heightfield shape\n var heightfieldShape = new Heightfield(data, {\n elementSize: 1 // Distance between the data points in X and Y directions\n });\n var heightfieldBody = new Body();\n heightfieldBody.addShape(heightfieldShape);\n world.addBody(heightfieldBody);" - ] - }, - "Particle": { - "name": "Particle", - "shortname": "Particle", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/shapes/Particle.js", - "line": 6, - "description": "Particle shape.", - "is_constructor": 1, - "author": "schteppe", - "extends": "Shape" - }, - "Plane": { - "name": "Plane", - "shortname": "Plane", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/shapes/Plane.js", - "line": 6, - "description": "A plane, facing in the Z direction. The plane has its surface at z=0 and everything below z=0 is assumed to be solid plane. To make the plane face in some other direction than z, you must put it inside a RigidBody and rotate that body. See the demos.", - "is_constructor": 1, - "extends": "Shape", - "author": "schteppe" - }, - "Shape": { - "name": "Shape", - "shortname": "Shape", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/shapes/Shape.js", - "line": 8, - "description": "Base class for shapes", - "is_constructor": 1, - "author": "schteppe", - "todo": [ - "Should have a mechanism for caching bounding sphere radius instead of calculating it each time" - ] - }, - "Sphere": { - "name": "Sphere", - "shortname": "Sphere", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/shapes/Sphere.js", - "line": 6, - "description": "Spherical shape", - "is_constructor": 1, - "extends": "Shape", - "params": [ - { - "name": "radius", - "description": "The radius of the sphere, a non-negative number.", - "type": "Number" - } - ], - "author": "schteppe / http://github.com/schteppe" - }, - "Trimesh": { - "name": "Trimesh", - "shortname": "Trimesh", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/shapes/Trimesh.js", - "line": 10, - "is_constructor": 1, - "params": [ - { - "name": "vertices", - "description": "", - "type": "Array" - }, - { - "name": "indices", - "description": "", - "type": "Array" - } - ], - "extends": "Shape", - "example": [ - "\n // How to make a mesh with a single triangle\n var vertices = [\n 0, 0, 0, // vertex 0\n 1, 0, 0, // vertex 1\n 0, 1, 0 // vertex 2\n ];\n var indices = [\n 0, 1, 2 // triangle 0\n ];\n var trimeshShape = new Trimesh(vertices, indices);" - ] - }, - "GSSolver": { - "name": "GSSolver", - "shortname": "GSSolver", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/solver/GSSolver.js", - "line": 7, - "description": "Constraint equation Gauss-Seidel solver.", - "is_constructor": 1, - "todo": [ - "The spook parameters should be specified for each constraint", - "not globally." - ], - "author": "schteppe / https://github.com/schteppe", - "see": [ - "https://www8.cs.umu.se/kurser/5DV058/VT09/lectures/spooknotes.pdf" - ], - "extends": "Solver" - }, - "Solver": { - "name": "Solver", - "shortname": "Solver", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/solver/Solver.js", - "line": 3, - "description": "Constraint equation solver base class.", - "is_constructor": 1, - "author": "schteppe / https://github.com/schteppe" - }, - "SplitSolver": { - "name": "SplitSolver", - "shortname": "SplitSolver", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/solver/SplitSolver.js", - "line": 8, - "description": "Splits the equations into islands and solves them independently. Can improve performance.", - "is_constructor": 1, - "extends": "Solver", - "params": [ - { - "name": "subsolver", - "description": "", - "type": "Solver" - } - ] - }, - "EventTarget": { - "name": "EventTarget", - "shortname": "EventTarget", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/utils/EventTarget.js", - "line": 1, - "description": "Base class for objects that dispatches events.", - "is_constructor": 1 - }, - "OctreeNode": { - "name": "OctreeNode", - "shortname": "OctreeNode", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/utils/Octree.js", - "line": 6, - "params": [ - { - "name": "options", - "description": "", - "type": "Object", - "optional": true, - "props": [ - { - "name": "root", - "description": "", - "type": "Octree", - "optional": true - }, - { - "name": "aabb", - "description": "", - "type": "AABB", - "optional": true - } - ] - } - ] - }, - "Octree": { - "name": "Octree", - "shortname": "Octree", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/utils/Octree.js", - "line": 40, - "params": [ - { - "name": "aabb", - "description": "The total AABB of the tree", - "type": "AABB" - }, - { - "name": "options", - "description": "", - "type": "Object", - "optional": true, - "props": [ - { - "name": "maxDepth", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "8" - } - ] - } - ], - "extends": "OctreeNode" - }, - "Pool": { - "name": "Pool", - "shortname": "Pool", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/utils/Pool.js", - "line": 3, - "description": "For pooling objects that can be reused.", - "is_constructor": 1 - }, - "TupleDictionary": { - "name": "TupleDictionary", - "shortname": "TupleDictionary", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/utils/TupleDictionary.js", - "line": 3, - "is_constructor": 1 - }, - "Vec3Pool": { - "name": "Vec3Pool", - "shortname": "Vec3Pool", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/utils/Vec3Pool.js", - "line": 6, - "is_constructor": 1, - "extends": "Pool" - }, - "Narrowphase": { - "name": "Narrowphase", - "shortname": "Narrowphase", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/world/Narrowphase.js", - "line": 15, - "description": "Helper class for the World. Generates ContactEquations.", - "is_constructor": 1, - "todo": [ - "Sphere-ConvexPolyhedron contacts", - "Contact reduction", - "should move methods to prototype" - ] - }, - "World": { - "name": "World", - "shortname": "World", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "src/world/World.js", - "line": 24, - "description": "The physics world", - "is_constructor": 1, - "extends": "EventTarget" - } - }, - "classitems": [ - { - "file": "src/collision/AABB.js", - "line": 17, - "description": "The lower bound of the bounding box.", - "itemtype": "property", - "name": "lowerBound", - "type": "{Vec3}", - "class": "AABB" - }, - { - "file": "src/collision/AABB.js", - "line": 27, - "description": "The upper bound of the bounding box.", - "itemtype": "property", - "name": "upperBound", - "type": "{Vec3}", - "class": "AABB" - }, - { - "file": "src/collision/AABB.js", - "line": 40, - "description": "Set the AABB bounds from a set of points.", - "itemtype": "method", - "name": "setFromPoints", - "params": [ - { - "name": "points", - "description": "An array of Vec3's.", - "type": "Array" - }, - { - "name": "position", - "description": "", - "type": "Vec3" - }, - { - "name": "quaternion", - "description": "", - "type": "Quaternion" - }, - { - "name": "skinSize", - "description": "", - "type": "Number" - } - ], - "return": { - "description": "The self object", - "type": "AABB" - }, - "class": "AABB" - }, - { - "file": "src/collision/AABB.js", - "line": 95, - "description": "Copy bounds from an AABB to this AABB", - "itemtype": "method", - "name": "copy", - "params": [ - { - "name": "aabb", - "description": "Source to copy from", - "type": "AABB" - } - ], - "return": { - "description": "The this object, for chainability", - "type": "AABB" - }, - "class": "AABB" - }, - { - "file": "src/collision/AABB.js", - "line": 107, - "description": "Clone an AABB", - "itemtype": "method", - "name": "clone", - "class": "AABB" - }, - { - "file": "src/collision/AABB.js", - "line": 115, - "description": "Extend this AABB so that it covers the given AABB too.", - "itemtype": "method", - "name": "extend", - "params": [ - { - "name": "aabb", - "description": "", - "type": "AABB" - } - ], - "class": "AABB" - }, - { - "file": "src/collision/AABB.js", - "line": 158, - "description": "Returns true if the given AABB overlaps this AABB.", - "itemtype": "method", - "name": "overlaps", - "params": [ - { - "name": "aabb", - "description": "", - "type": "AABB" - } - ], - "return": { - "description": "", - "type": "Boolean" - }, - "class": "AABB" - }, - { - "file": "src/collision/AABB.js", - "line": 180, - "description": "Returns true if the given AABB is fully contained in this AABB.", - "itemtype": "method", - "name": "contains", - "params": [ - { - "name": "aabb", - "description": "", - "type": "AABB" - } - ], - "return": { - "description": "", - "type": "Boolean" - }, - "class": "AABB" - }, - { - "file": "src/collision/AABB.js", - "line": 204, - "itemtype": "method", - "name": "getCorners", - "params": [ - { - "name": "a", - "description": "", - "type": "Vec3" - }, - { - "name": "b", - "description": "", - "type": "Vec3" - }, - { - "name": "c", - "description": "", - "type": "Vec3" - }, - { - "name": "d", - "description": "", - "type": "Vec3" - }, - { - "name": "e", - "description": "", - "type": "Vec3" - }, - { - "name": "f", - "description": "", - "type": "Vec3" - }, - { - "name": "g", - "description": "", - "type": "Vec3" - }, - { - "name": "h", - "description": "", - "type": "Vec3" - } - ], - "class": "AABB" - }, - { - "file": "src/collision/AABB.js", - "line": 240, - "description": "Get the representation of an AABB in another frame.", - "itemtype": "method", - "name": "toLocalFrame", - "params": [ - { - "name": "frame", - "description": "", - "type": "Transform" - }, - { - "name": "target", - "description": "", - "type": "AABB" - } - ], - "return": { - "description": "The \"target\" AABB object.", - "type": "AABB" - }, - "class": "AABB" - }, - { - "file": "src/collision/AABB.js", - "line": 271, - "description": "Get the representation of an AABB in the global frame.", - "itemtype": "method", - "name": "toWorldFrame", - "params": [ - { - "name": "frame", - "description": "", - "type": "Transform" - }, - { - "name": "target", - "description": "", - "type": "AABB" - } - ], - "return": { - "description": "The \"target\" AABB object.", - "type": "AABB" - }, - "class": "AABB" - }, - { - "file": "src/collision/ArrayCollisionMatrix.js", - "line": 10, - "description": "The matrix storage", - "itemtype": "property", - "name": "matrix", - "type": "{Array}", - "class": "ArrayCollisionMatrix" - }, - { - "file": "src/collision/ArrayCollisionMatrix.js", - "line": 18, - "description": "Get an element", - "itemtype": "method", - "name": "get", - "params": [ - { - "name": "i", - "description": "", - "type": "Number" - }, - { - "name": "j", - "description": "", - "type": "Number" - } - ], - "return": { - "description": "", - "type": "Number" - }, - "class": "ArrayCollisionMatrix" - }, - { - "file": "src/collision/ArrayCollisionMatrix.js", - "line": 36, - "description": "Set an element", - "itemtype": "method", - "name": "set", - "params": [ - { - "name": "i", - "description": "", - "type": "Number" - }, - { - "name": "j", - "description": "", - "type": "Number" - }, - { - "name": "value", - "description": "", - "type": "Number" - } - ], - "class": "ArrayCollisionMatrix" - }, - { - "file": "src/collision/ArrayCollisionMatrix.js", - "line": 54, - "description": "Sets all elements to zero", - "itemtype": "method", - "name": "reset", - "class": "ArrayCollisionMatrix" - }, - { - "file": "src/collision/ArrayCollisionMatrix.js", - "line": 64, - "description": "Sets the max number of objects", - "itemtype": "method", - "name": "setNumObjects", - "params": [ - { - "name": "n", - "description": "", - "type": "Number" - } - ], - "class": "ArrayCollisionMatrix" - }, - { - "file": "src/collision/Broadphase.js", - "line": 16, - "description": "The world to search for collisions in.", - "itemtype": "property", - "name": "world", - "type": "{World}", - "class": "Broadphase" - }, - { - "file": "src/collision/Broadphase.js", - "line": 23, - "description": "If set to true, the broadphase uses bounding boxes for intersection test, else it uses bounding spheres.", - "itemtype": "property", - "name": "useBoundingBoxes", - "type": "{Boolean}", - "class": "Broadphase" - }, - { - "file": "src/collision/Broadphase.js", - "line": 30, - "description": "Set to true if the objects in the world moved.", - "itemtype": "property", - "name": "dirty", - "type": "Boolean", - "class": "Broadphase" - }, - { - "file": "src/collision/Broadphase.js", - "line": 37, - "description": "Get the collision pairs from the world", - "itemtype": "method", - "name": "collisionPairs", - "params": [ - { - "name": "world", - "description": "The world to search in", - "type": "World" - }, - { - "name": "p1", - "description": "Empty array to be filled with body objects", - "type": "Array" - }, - { - "name": "p2", - "description": "Empty array to be filled with body objects", - "type": "Array" - } - ], - "class": "Broadphase" - }, - { - "file": "src/collision/Broadphase.js", - "line": 48, - "description": "Check if a body pair needs to be intersection tested at all.", - "itemtype": "method", - "name": "needBroadphaseCollision", - "params": [ - { - "name": "bodyA", - "description": "", - "type": "Body" - }, - { - "name": "bodyB", - "description": "", - "type": "Body" - } - ], - "return": { - "description": "", - "type": "Bool" - }, - "class": "Broadphase" - }, - { - "file": "src/collision/Broadphase.js", - "line": 73, - "description": "Check if the bounding volumes of two bodies intersect.", - "itemtype": "method", - "name": "intersectionTest", - "params": [ - { - "name": "bodyA", - "description": "", - "type": "Body" - }, - { - "name": "bodyB", - "description": "", - "type": "Body" - }, - { - "name": "pairs1", - "description": "", - "type": "Array" - }, - { - "name": "pairs2", - "description": "", - "type": "Array" - } - ], - "class": "Broadphase" - }, - { - "file": "src/collision/Broadphase.js", - "line": 89, - "description": "Check if the bounding spheres of two bodies are intersecting.", - "itemtype": "method", - "name": "doBoundingSphereBroadphase", - "params": [ - { - "name": "bodyA", - "description": "", - "type": "Body" - }, - { - "name": "bodyB", - "description": "", - "type": "Body" - }, - { - "name": "pairs1", - "description": "bodyA is appended to this array if intersection", - "type": "Array" - }, - { - "name": "pairs2", - "description": "bodyB is appended to this array if intersection", - "type": "Array" - } - ], - "class": "Broadphase" - }, - { - "file": "src/collision/Broadphase.js", - "line": 112, - "description": "Check if the bounding boxes of two bodies are intersecting.", - "itemtype": "method", - "name": "doBoundingBoxBroadphase", - "params": [ - { - "name": "bodyA", - "description": "", - "type": "Body" - }, - { - "name": "bodyB", - "description": "", - "type": "Body" - }, - { - "name": "pairs1", - "description": "", - "type": "Array" - }, - { - "name": "pairs2", - "description": "", - "type": "Array" - } - ], - "class": "Broadphase" - }, - { - "file": "src/collision/Broadphase.js", - "line": 135, - "description": "Removes duplicate pairs from the pair arrays.", - "itemtype": "method", - "name": "makePairsUnique", - "params": [ - { - "name": "pairs1", - "description": "", - "type": "Array" - }, - { - "name": "pairs2", - "description": "", - "type": "Array" - } - ], - "class": "Broadphase" - }, - { - "file": "src/collision/Broadphase.js", - "line": 175, - "description": "To be implemented by subcasses", - "itemtype": "method", - "name": "setWorld", - "params": [ - { - "name": "world", - "description": "", - "type": "World" - } - ], - "class": "Broadphase" - }, - { - "file": "src/collision/Broadphase.js", - "line": 183, - "description": "Check if the bounding spheres of two bodies overlap.", - "itemtype": "method", - "name": "boundingSphereCheck", - "params": [ - { - "name": "bodyA", - "description": "", - "type": "Body" - }, - { - "name": "bodyB", - "description": "", - "type": "Body" - } - ], - "return": { - "description": "", - "type": "Boolean" - }, - "class": "Broadphase" - }, - { - "file": "src/collision/Broadphase.js", - "line": 197, - "description": "Returns all the bodies within the AABB.", - "itemtype": "method", - "name": "aabbQuery", - "params": [ - { - "name": "world", - "description": "", - "type": "World" - }, - { - "name": "aabb", - "description": "", - "type": "AABB" - }, - { - "name": "result", - "description": "An array to store resulting bodies in.", - "type": "Array" - } - ], - "return": { - "description": "", - "type": "Array" - }, - "class": "Broadphase" - }, - { - "file": "src/collision/GridBroadphase.js", - "line": 42, - "description": "Get all the collision pairs in the physics world", - "itemtype": "method", - "name": "collisionPairs", - "params": [ - { - "name": "world", - "description": "", - "type": "World" - }, - { - "name": "pairs1", - "description": "", - "type": "Array" - }, - { - "name": "pairs2", - "description": "", - "type": "Array" - } - ], - "class": "GridBroadphase" - }, - { - "file": "src/collision/NaiveBroadphase.js", - "line": 19, - "description": "Get all the collision pairs in the physics world", - "itemtype": "method", - "name": "collisionPairs", - "params": [ - { - "name": "world", - "description": "", - "type": "World" - }, - { - "name": "pairs1", - "description": "", - "type": "Array" - }, - { - "name": "pairs2", - "description": "", - "type": "Array" - } - ], - "class": "NaiveBroadphase" - }, - { - "file": "src/collision/NaiveBroadphase.js", - "line": 49, - "description": "Returns all the bodies within an AABB.", - "itemtype": "method", - "name": "aabbQuery", - "params": [ - { - "name": "world", - "description": "", - "type": "World" - }, - { - "name": "aabb", - "description": "", - "type": "AABB" - }, - { - "name": "result", - "description": "An array to store resulting bodies in.", - "type": "Array" - } - ], - "return": { - "description": "", - "type": "Array" - }, - "class": "NaiveBroadphase" - }, - { - "file": "src/collision/ObjectCollisionMatrix.js", - "line": 10, - "description": "The matrix storage", - "itemtype": "property", - "name": "matrix", - "type": "{Object}", - "class": "ObjectCollisionMatrix" - }, - { - "file": "src/collision/ObjectCollisionMatrix.js", - "line": 18, - "itemtype": "method", - "name": "get", - "params": [ - { - "name": "i", - "description": "", - "type": "Number" - }, - { - "name": "j", - "description": "", - "type": "Number" - } - ], - "return": { - "description": "", - "type": "Number" - }, - "class": "ObjectCollisionMatrix" - }, - { - "file": "src/collision/ObjectCollisionMatrix.js", - "line": 35, - "itemtype": "method", - "name": "set", - "params": [ - { - "name": "i", - "description": "", - "type": "Number" - }, - { - "name": "j", - "description": "", - "type": "Number" - }, - { - "name": "value", - "description": "", - "type": "Number" - } - ], - "class": "ObjectCollisionMatrix" - }, - { - "file": "src/collision/ObjectCollisionMatrix.js", - "line": 57, - "description": "Empty the matrix", - "itemtype": "method", - "name": "reset", - "class": "ObjectCollisionMatrix" - }, - { - "file": "src/collision/ObjectCollisionMatrix.js", - "line": 65, - "description": "Set max number of objects", - "itemtype": "method", - "name": "setNumObjects", - "params": [ - { - "name": "n", - "description": "", - "type": "Number" - } - ], - "class": "ObjectCollisionMatrix" - }, - { - "file": "src/collision/Ray.js", - "line": 20, - "itemtype": "property", - "name": "from", - "type": "Vec3", - "class": "Ray" - }, - { - "file": "src/collision/Ray.js", - "line": 25, - "itemtype": "property", - "name": "to", - "type": "Vec3", - "class": "Ray" - }, - { - "file": "src/collision/Ray.js", - "line": 30, - "access": "private", - "tagname": "", - "itemtype": "property", - "name": "_direction", - "type": "Vec3", - "class": "Ray" - }, - { - "file": "src/collision/Ray.js", - "line": 36, - "description": "The precision of the ray. Used when checking parallelity etc.", - "itemtype": "property", - "name": "precision", - "type": "Number", - "class": "Ray" - }, - { - "file": "src/collision/Ray.js", - "line": 42, - "description": "Set to true if you want the Ray to take .collisionResponse flags into account on bodies and shapes.", - "itemtype": "property", - "name": "checkCollisionResponse", - "type": "Boolean", - "class": "Ray" - }, - { - "file": "src/collision/Ray.js", - "line": 48, - "description": "If set to true, the ray skips any hits with normal.dot(rayDirection) < 0.", - "itemtype": "property", - "name": "skipBackfaces", - "type": "Boolean", - "class": "Ray" - }, - { - "file": "src/collision/Ray.js", - "line": 54, - "itemtype": "property", - "name": "collisionFilterMask", - "type": "Number", - "default": "-1", - "class": "Ray" - }, - { - "file": "src/collision/Ray.js", - "line": 60, - "itemtype": "property", - "name": "collisionFilterGroup", - "type": "Number", - "default": "-1", - "class": "Ray" - }, - { - "file": "src/collision/Ray.js", - "line": 66, - "description": "The intersection mode. Should be Ray.ANY, Ray.ALL or Ray.CLOSEST.", - "itemtype": "property", - "name": "mode", - "type": "Number", - "class": "Ray" - }, - { - "file": "src/collision/Ray.js", - "line": 72, - "description": "Current result object.", - "itemtype": "property", - "name": "result", - "type": "RaycastResult", - "class": "Ray" - }, - { - "file": "src/collision/Ray.js", - "line": 78, - "description": "Will be set to true during intersectWorld() if the ray hit anything.", - "itemtype": "property", - "name": "hasHit", - "type": "Boolean", - "class": "Ray" - }, - { - "file": "src/collision/Ray.js", - "line": 84, - "description": "Current, user-provided result callback. Will be used if mode is Ray.ALL.", - "itemtype": "property", - "name": "callback", - "type": "Function", - "class": "Ray" - }, - { - "file": "src/collision/Ray.js", - "line": 99, - "description": "Do itersection against all bodies in the given World.", - "itemtype": "method", - "name": "intersectWorld", - "params": [ - { - "name": "world", - "description": "", - "type": "World" - }, - { - "name": "options", - "description": "", - "type": "Object" - } - ], - "return": { - "description": "True if the ray hit anything, otherwise false.", - "type": "Boolean" - }, - "class": "Ray" - }, - { - "file": "src/collision/Ray.js", - "line": 157, - "description": "Shoot a ray at a body, get back information about the hit.", - "itemtype": "method", - "name": "intersectBody", - "access": "private", - "tagname": "", - "params": [ - { - "name": "body", - "description": "", - "type": "Body" - }, - { - "name": "result", - "description": "Deprecated - set the result property of the Ray instead.", - "type": "RaycastResult", - "optional": true - } - ], - "class": "Ray" - }, - { - "file": "src/collision/Ray.js", - "line": 208, - "itemtype": "method", - "name": "intersectBodies", - "params": [ - { - "name": "bodies", - "description": "An array of Body objects.", - "type": "Array" - }, - { - "name": "result", - "description": "Deprecated", - "type": "RaycastResult", - "optional": true - } - ], - "class": "Ray" - }, - { - "file": "src/collision/Ray.js", - "line": 224, - "description": "Updates the _direction vector.", - "access": "private", - "tagname": "", - "itemtype": "method", - "name": "_updateDirection", - "class": "Ray" - }, - { - "file": "src/collision/Ray.js", - "line": 234, - "itemtype": "method", - "name": "intersectShape", - "access": "private", - "tagname": "", - "params": [ - { - "name": "shape", - "description": "", - "type": "Shape" - }, - { - "name": "quat", - "description": "", - "type": "Quaternion" - }, - { - "name": "position", - "description": "", - "type": "Vec3" - }, - { - "name": "body", - "description": "", - "type": "Body" - } - ], - "class": "Ray" - }, - { - "file": "src/collision/Ray.js", - "line": 269, - "itemtype": "method", - "name": "intersectBox", - "access": "private", - "tagname": "", - "params": [ - { - "name": "shape", - "description": "", - "type": "Shape" - }, - { - "name": "quat", - "description": "", - "type": "Quaternion" - }, - { - "name": "position", - "description": "", - "type": "Vec3" - }, - { - "name": "body", - "description": "", - "type": "Body" - } - ], - "class": "Ray" - }, - { - "file": "src/collision/Ray.js", - "line": 282, - "itemtype": "method", - "name": "intersectPlane", - "access": "private", - "tagname": "", - "params": [ - { - "name": "shape", - "description": "", - "type": "Shape" - }, - { - "name": "quat", - "description": "", - "type": "Quaternion" - }, - { - "name": "position", - "description": "", - "type": "Vec3" - }, - { - "name": "body", - "description": "", - "type": "Body" - } - ], - "class": "Ray" - }, - { - "file": "src/collision/Ray.js", - "line": 334, - "description": "Get the world AABB of the ray.", - "itemtype": "method", - "name": "getAABB", - "params": [ - { - "name": "aabb", - "description": "", - "type": "AABB" - } - ], - "class": "Ray" - }, - { - "file": "src/collision/Ray.js", - "line": 354, - "itemtype": "method", - "name": "intersectHeightfield", - "access": "private", - "tagname": "", - "params": [ - { - "name": "shape", - "description": "", - "type": "Shape" - }, - { - "name": "quat", - "description": "", - "type": "Quaternion" - }, - { - "name": "position", - "description": "", - "type": "Vec3" - }, - { - "name": "body", - "description": "", - "type": "Body" - } - ], - "class": "Ray" - }, - { - "file": "src/collision/Ray.js", - "line": 439, - "itemtype": "method", - "name": "intersectSphere", - "access": "private", - "tagname": "", - "params": [ - { - "name": "shape", - "description": "", - "type": "Shape" - }, - { - "name": "quat", - "description": "", - "type": "Quaternion" - }, - { - "name": "position", - "description": "", - "type": "Vec3" - }, - { - "name": "body", - "description": "", - "type": "Body" - } - ], - "class": "Ray" - }, - { - "file": "src/collision/Ray.js", - "line": 505, - "itemtype": "method", - "name": "intersectConvex", - "access": "private", - "tagname": "", - "params": [ - { - "name": "shape", - "description": "", - "type": "Shape" - }, - { - "name": "quat", - "description": "", - "type": "Quaternion" - }, - { - "name": "position", - "description": "", - "type": "Vec3" - }, - { - "name": "body", - "description": "", - "type": "Body" - }, - { - "name": "options", - "description": "", - "type": "Object", - "optional": true, - "props": [ - { - "name": "faceList", - "description": "", - "type": "Array", - "optional": true - } - ] - } - ], - "class": "Ray" - }, - { - "file": "src/collision/Ray.js", - "line": 623, - "itemtype": "method", - "name": "intersectTrimesh", - "access": "private", - "tagname": "", - "params": [ - { - "name": "shape", - "description": "", - "type": "Shape" - }, - { - "name": "quat", - "description": "", - "type": "Quaternion" - }, - { - "name": "position", - "description": "", - "type": "Vec3" - }, - { - "name": "body", - "description": "", - "type": "Body" - }, - { - "name": "options", - "description": "", - "type": "Object", - "optional": true - } - ], - "todo": [ - "Optimize by transforming the world to local space first.", - "Use Octree lookup" - ], - "class": "Ray" - }, - { - "file": "src/collision/Ray.js", - "line": 738, - "itemtype": "method", - "name": "reportIntersection", - "access": "private", - "tagname": "", - "params": [ - { - "name": "normal", - "description": "", - "type": "Vec3" - }, - { - "name": "hitPointWorld", - "description": "", - "type": "Vec3" - }, - { - "name": "shape", - "description": "", - "type": "Shape" - }, - { - "name": "body", - "description": "", - "type": "Body" - } - ], - "return": { - "description": "True if the intersections should continue", - "type": "Boolean" - }, - "class": "Ray" - }, - { - "file": "src/collision/RaycastResult.js", - "line": 12, - "itemtype": "property", - "name": "rayFromWorld", - "type": "Vec3", - "class": "RaycastResult" - }, - { - "file": "src/collision/RaycastResult.js", - "line": 17, - "itemtype": "property", - "name": "rayToWorld", - "type": "Vec3", - "class": "RaycastResult" - }, - { - "file": "src/collision/RaycastResult.js", - "line": 22, - "itemtype": "property", - "name": "hitNormalWorld", - "type": "Vec3", - "class": "RaycastResult" - }, - { - "file": "src/collision/RaycastResult.js", - "line": 27, - "itemtype": "property", - "name": "hitPointWorld", - "type": "Vec3", - "class": "RaycastResult" - }, - { - "file": "src/collision/RaycastResult.js", - "line": 32, - "itemtype": "property", - "name": "hasHit", - "type": "Boolean", - "class": "RaycastResult" - }, - { - "file": "src/collision/RaycastResult.js", - "line": 37, - "description": "The hit shape, or null.", - "itemtype": "property", - "name": "shape", - "type": "Shape", - "class": "RaycastResult" - }, - { - "file": "src/collision/RaycastResult.js", - "line": 43, - "description": "The hit body, or null.", - "itemtype": "property", - "name": "body", - "type": "Body", - "class": "RaycastResult" - }, - { - "file": "src/collision/RaycastResult.js", - "line": 49, - "description": "The index of the hit triangle, if the hit shape was a trimesh.", - "itemtype": "property", - "name": "hitFaceIndex", - "type": "Number", - "default": "-1", - "class": "RaycastResult" - }, - { - "file": "src/collision/RaycastResult.js", - "line": 56, - "description": "Distance to the hit. Will be set to -1 if there was no hit.", - "itemtype": "property", - "name": "distance", - "type": "Number", - "default": "-1", - "class": "RaycastResult" - }, - { - "file": "src/collision/RaycastResult.js", - "line": 63, - "description": "If the ray should stop traversing the bodies.", - "access": "private", - "tagname": "", - "itemtype": "property", - "name": "_shouldStop", - "type": "Boolean", - "default": "false", - "class": "RaycastResult" - }, - { - "file": "src/collision/RaycastResult.js", - "line": 72, - "description": "Reset all result data.", - "itemtype": "method", - "name": "reset", - "class": "RaycastResult" - }, - { - "file": "src/collision/RaycastResult.js", - "line": 89, - "itemtype": "method", - "name": "abort", - "class": "RaycastResult" - }, - { - "file": "src/collision/RaycastResult.js", - "line": 96, - "itemtype": "method", - "name": "set", - "params": [ - { - "name": "rayFromWorld", - "description": "", - "type": "Vec3" - }, - { - "name": "rayToWorld", - "description": "", - "type": "Vec3" - }, - { - "name": "hitNormalWorld", - "description": "", - "type": "Vec3" - }, - { - "name": "hitPointWorld", - "description": "", - "type": "Vec3" - }, - { - "name": "shape", - "description": "", - "type": "Shape" - }, - { - "name": "body", - "description": "", - "type": "Body" - }, - { - "name": "distance", - "description": "", - "type": "Number" - } - ], - "class": "RaycastResult" - }, - { - "file": "src/collision/SAPBroadphase.js", - "line": 17, - "description": "List of bodies currently in the broadphase.", - "itemtype": "property", - "name": "axisList", - "type": "{Array}", - "class": "SAPBroadphase" - }, - { - "file": "src/collision/SAPBroadphase.js", - "line": 24, - "description": "The world to search in.", - "itemtype": "property", - "name": "world", - "type": "{World}", - "class": "SAPBroadphase" - }, - { - "file": "src/collision/SAPBroadphase.js", - "line": 31, - "description": "Axis to sort the bodies along. Set to 0 for x axis, and 1 for y axis. For best performance, choose an axis that the bodies are spread out more on.", - "itemtype": "property", - "name": "axisIndex", - "type": "{Number}", - "class": "SAPBroadphase" - }, - { - "file": "src/collision/SAPBroadphase.js", - "line": 57, - "description": "Change the world", - "itemtype": "method", - "name": "setWorld", - "params": [ - { - "name": "world", - "description": "", - "type": "World" - } - ], - "class": "SAPBroadphase" - }, - { - "file": "src/collision/SAPBroadphase.js", - "line": 83, - "static": 1, - "itemtype": "method", - "name": "insertionSortX", - "params": [ - { - "name": "a", - "description": "", - "type": "Array" - } - ], - "return": { - "description": "", - "type": "Array" - }, - "class": "SAPBroadphase" - }, - { - "file": "src/collision/SAPBroadphase.js", - "line": 103, - "static": 1, - "itemtype": "method", - "name": "insertionSortY", - "params": [ - { - "name": "a", - "description": "", - "type": "Array" - } - ], - "return": { - "description": "", - "type": "Array" - }, - "class": "SAPBroadphase" - }, - { - "file": "src/collision/SAPBroadphase.js", - "line": 123, - "static": 1, - "itemtype": "method", - "name": "insertionSortZ", - "params": [ - { - "name": "a", - "description": "", - "type": "Array" - } - ], - "return": { - "description": "", - "type": "Array" - }, - "class": "SAPBroadphase" - }, - { - "file": "src/collision/SAPBroadphase.js", - "line": 143, - "description": "Collect all collision pairs", - "itemtype": "method", - "name": "collisionPairs", - "params": [ - { - "name": "world", - "description": "", - "type": "World" - }, - { - "name": "p1", - "description": "", - "type": "Array" - }, - { - "name": "p2", - "description": "", - "type": "Array" - } - ], - "class": "SAPBroadphase" - }, - { - "file": "src/collision/SAPBroadphase.js", - "line": 204, - "description": "Check if the bounds of two bodies overlap, along the given SAP axis.", - "static": 1, - "itemtype": "method", - "name": "checkBounds", - "params": [ - { - "name": "bi", - "description": "", - "type": "Body" - }, - { - "name": "bj", - "description": "", - "type": "Body" - }, - { - "name": "axisIndex", - "description": "", - "type": "Number" - } - ], - "return": { - "description": "", - "type": "Boolean" - }, - "class": "SAPBroadphase" - }, - { - "file": "src/collision/SAPBroadphase.js", - "line": 238, - "description": "Computes the variance of the body positions and estimates the best\naxis to use. Will automatically set property .axisIndex.", - "itemtype": "method", - "name": "autoDetectAxis", - "class": "SAPBroadphase" - }, - { - "file": "src/collision/SAPBroadphase.js", - "line": 287, - "description": "Returns all the bodies within an AABB.", - "itemtype": "method", - "name": "aabbQuery", - "params": [ - { - "name": "world", - "description": "", - "type": "World" - }, - { - "name": "aabb", - "description": "", - "type": "AABB" - }, - { - "name": "result", - "description": "An array to store resulting bodies in.", - "type": "Array" - } - ], - "return": { - "description": "", - "type": "Array" - }, - "class": "SAPBroadphase" - }, - { - "file": "src/constraints/ConeTwistConstraint.js", - "line": 40, - "itemtype": "property", - "name": "coneEquation", - "type": "ConeEquation", - "class": "ConeTwistConstraint" - }, - { - "file": "src/constraints/ConeTwistConstraint.js", - "line": 45, - "itemtype": "property", - "name": "twistEquation", - "type": "RotationalEquation", - "class": "ConeTwistConstraint" - }, - { - "file": "src/constraints/Constraint.js", - "line": 22, - "description": "Equations to be solved in this constraint", - "itemtype": "property", - "name": "equations", - "type": "{Array}", - "class": "Constraint" - }, - { - "file": "src/constraints/Constraint.js", - "line": 29, - "itemtype": "property", - "name": "bodyA", - "type": "Body", - "class": "Constraint" - }, - { - "file": "src/constraints/Constraint.js", - "line": 34, - "itemtype": "property", - "name": "bodyB", - "type": "Body", - "class": "Constraint" - }, - { - "file": "src/constraints/Constraint.js", - "line": 39, - "itemtype": "property", - "name": "id", - "type": "Number", - "class": "Constraint" - }, - { - "file": "src/constraints/Constraint.js", - "line": 44, - "description": "Set to true if you want the bodies to collide when they are connected.", - "itemtype": "property", - "name": "collideConnected", - "type": "{boolean}", - "class": "Constraint" - }, - { - "file": "src/constraints/Constraint.js", - "line": 61, - "description": "Update all the equations with data.", - "itemtype": "method", - "name": "update", - "class": "Constraint" - }, - { - "file": "src/constraints/Constraint.js", - "line": 69, - "description": "Enables all equations in the constraint.", - "itemtype": "method", - "name": "enable", - "class": "Constraint" - }, - { - "file": "src/constraints/Constraint.js", - "line": 80, - "description": "Disables all equations in the constraint.", - "itemtype": "method", - "name": "disable", - "class": "Constraint" - }, - { - "file": "src/constraints/DistanceConstraint.js", - "line": 28, - "itemtype": "property", - "name": "distance", - "type": "Number", - "class": "DistanceConstraint" - }, - { - "file": "src/constraints/DistanceConstraint.js", - "line": 33, - "itemtype": "property", - "name": "distanceEquation", - "type": "ContactEquation", - "class": "DistanceConstraint" - }, - { - "file": "src/constraints/HingeConstraint.js", - "line": 33, - "description": "Rotation axis, defined locally in bodyA.", - "itemtype": "property", - "name": "axisA", - "type": "Vec3", - "class": "HingeConstraint" - }, - { - "file": "src/constraints/HingeConstraint.js", - "line": 40, - "description": "Rotation axis, defined locally in bodyB.", - "itemtype": "property", - "name": "axisB", - "type": "Vec3", - "class": "HingeConstraint" - }, - { - "file": "src/constraints/HingeConstraint.js", - "line": 47, - "itemtype": "property", - "name": "rotationalEquation1", - "type": "RotationalEquation", - "class": "HingeConstraint" - }, - { - "file": "src/constraints/HingeConstraint.js", - "line": 52, - "itemtype": "property", - "name": "rotationalEquation2", - "type": "RotationalEquation", - "class": "HingeConstraint" - }, - { - "file": "src/constraints/HingeConstraint.js", - "line": 57, - "itemtype": "property", - "name": "motorEquation", - "type": "RotationalMotorEquation", - "class": "HingeConstraint" - }, - { - "file": "src/constraints/HingeConstraint.js", - "line": 73, - "itemtype": "method", - "name": "enableMotor", - "class": "HingeConstraint" - }, - { - "file": "src/constraints/HingeConstraint.js", - "line": 80, - "itemtype": "method", - "name": "disableMotor", - "class": "HingeConstraint" - }, - { - "file": "src/constraints/HingeConstraint.js", - "line": 87, - "itemtype": "method", - "name": "setMotorSpeed", - "params": [ - { - "name": "speed", - "description": "", - "type": "Number" - } - ], - "class": "HingeConstraint" - }, - { - "file": "src/constraints/HingeConstraint.js", - "line": 95, - "itemtype": "method", - "name": "setMotorMaxForce", - "params": [ - { - "name": "maxForce", - "description": "", - "type": "Number" - } - ], - "class": "HingeConstraint" - }, - { - "file": "src/constraints/LockConstraint.js", - "line": 35, - "itemtype": "property", - "name": "rotationalEquation1", - "type": "RotationalEquation", - "class": "LockConstraint" - }, - { - "file": "src/constraints/LockConstraint.js", - "line": 40, - "itemtype": "property", - "name": "rotationalEquation2", - "type": "RotationalEquation", - "class": "LockConstraint" - }, - { - "file": "src/constraints/LockConstraint.js", - "line": 45, - "itemtype": "property", - "name": "rotationalEquation3", - "type": "RotationalEquation", - "class": "LockConstraint" - }, - { - "file": "src/constraints/PointToPointConstraint.js", - "line": 37, - "description": "Pivot, defined locally in bodyA.", - "itemtype": "property", - "name": "pivotA", - "type": "Vec3", - "class": "PointToPointConstraint" - }, - { - "file": "src/constraints/PointToPointConstraint.js", - "line": 43, - "description": "Pivot, defined locally in bodyB.", - "itemtype": "property", - "name": "pivotB", - "type": "Vec3", - "class": "PointToPointConstraint" - }, - { - "file": "src/constraints/PointToPointConstraint.js", - "line": 49, - "itemtype": "property", - "name": "equationX", - "type": "ContactEquation", - "class": "PointToPointConstraint" - }, - { - "file": "src/constraints/PointToPointConstraint.js", - "line": 54, - "itemtype": "property", - "name": "equationY", - "type": "ContactEquation", - "class": "PointToPointConstraint" - }, - { - "file": "src/constraints/PointToPointConstraint.js", - "line": 59, - "itemtype": "property", - "name": "equationZ", - "type": "ContactEquation", - "class": "PointToPointConstraint" - }, - { - "file": "src/demo/Demo.js", - "line": 209, - "description": "Add a scene to the demo app", - "itemtype": "method", - "name": "addScene", - "params": [ - { - "name": "title", - "description": "Title of the scene", - "type": "String" - }, - { - "name": "initfunc", - "description": "A function that takes one argument, app, and initializes a physics scene. The function runs app.setWorld(body), app.addVisual(body), app.removeVisual(body) etc.", - "type": "Function" - } - ], - "class": "Demo" - }, - { - "file": "src/demo/Demo.js", - "line": 230, - "description": "Restarts the current scene", - "itemtype": "method", - "name": "restartCurrentScene", - "class": "Demo" - }, - { - "file": "src/equations/ConeEquation.js", - "line": 29, - "description": "The cone angle to keep", - "itemtype": "property", - "name": "angle", - "type": "Number", - "class": "ConeEquation" - }, - { - "file": "src/equations/ContactEquation.js", - "line": 20, - "itemtype": "property", - "name": "restitution", - "type": "{Number}", - "class": "ContactEquation" - }, - { - "file": "src/equations/ContactEquation.js", - "line": 26, - "description": "World-oriented vector that goes from the center of bi to the contact point.", - "itemtype": "property", - "name": "ri", - "type": "Vec3", - "class": "ContactEquation" - }, - { - "file": "src/equations/ContactEquation.js", - "line": 32, - "description": "World-oriented vector that starts in body j position and goes to the contact point.", - "itemtype": "property", - "name": "rj", - "type": "Vec3", - "class": "ContactEquation" - }, - { - "file": "src/equations/ContactEquation.js", - "line": 38, - "description": "Contact normal, pointing out of body i.", - "itemtype": "property", - "name": "ni", - "type": "Vec3", - "class": "ContactEquation" - }, - { - "file": "src/equations/ContactEquation.js", - "line": 113, - "description": "Get the current relative velocity in the contact point.", - "itemtype": "method", - "name": "getImpactVelocityAlongNormal", - "return": { - "description": "", - "type": "Number" - }, - "class": "ContactEquation" - }, - { - "file": "src/equations/Equation.js", - "line": 19, - "itemtype": "property", - "name": "minForce", - "type": "Number", - "class": "Equation" - }, - { - "file": "src/equations/Equation.js", - "line": 24, - "itemtype": "property", - "name": "maxForce", - "type": "Number", - "class": "Equation" - }, - { - "file": "src/equations/Equation.js", - "line": 29, - "itemtype": "property", - "name": "bi", - "type": "{Body}", - "class": "Equation" - }, - { - "file": "src/equations/Equation.js", - "line": 35, - "itemtype": "property", - "name": "bj", - "type": "{Body}", - "class": "Equation" - }, - { - "file": "src/equations/Equation.js", - "line": 41, - "description": "SPOOK parameter", - "itemtype": "property", - "name": "a", - "type": "Number", - "class": "Equation" - }, - { - "file": "src/equations/Equation.js", - "line": 47, - "description": "SPOOK parameter", - "itemtype": "property", - "name": "b", - "type": "Number", - "class": "Equation" - }, - { - "file": "src/equations/Equation.js", - "line": 53, - "description": "SPOOK parameter", - "itemtype": "property", - "name": "eps", - "type": "Number", - "class": "Equation" - }, - { - "file": "src/equations/Equation.js", - "line": 59, - "itemtype": "property", - "name": "jacobianElementA", - "type": "JacobianElement", - "class": "Equation" - }, - { - "file": "src/equations/Equation.js", - "line": 64, - "itemtype": "property", - "name": "jacobianElementB", - "type": "JacobianElement", - "class": "Equation" - }, - { - "file": "src/equations/Equation.js", - "line": 69, - "itemtype": "property", - "name": "enabled", - "type": "Boolean", - "default": "true", - "class": "Equation" - }, - { - "file": "src/equations/Equation.js", - "line": 82, - "description": "Recalculates a,b,eps.", - "itemtype": "method", - "name": "setSpookParams", - "class": "Equation" - }, - { - "file": "src/equations/Equation.js", - "line": 95, - "description": "Computes the RHS of the SPOOK equation", - "itemtype": "method", - "name": "computeB", - "return": { - "description": "", - "type": "Number" - }, - "class": "Equation" - }, - { - "file": "src/equations/Equation.js", - "line": 107, - "description": "Computes G*q, where q are the generalized body coordinates", - "itemtype": "method", - "name": "computeGq", - "return": { - "description": "", - "type": "Number" - }, - "class": "Equation" - }, - { - "file": "src/equations/Equation.js", - "line": 124, - "description": "Computes G*W, where W are the body velocities", - "itemtype": "method", - "name": "computeGW", - "return": { - "description": "", - "type": "Number" - }, - "class": "Equation" - }, - { - "file": "src/equations/Equation.js", - "line": 142, - "description": "Computes G*Wlambda, where W are the body velocities", - "itemtype": "method", - "name": "computeGWlambda", - "return": { - "description": "", - "type": "Number" - }, - "class": "Equation" - }, - { - "file": "src/equations/Equation.js", - "line": 159, - "description": "Computes G*inv(M)*f, where M is the mass matrix with diagonal blocks for each body, and f are the forces on the bodies.", - "itemtype": "method", - "name": "computeGiMf", - "return": { - "description": "", - "type": "Number" - }, - "class": "Equation" - }, - { - "file": "src/equations/Equation.js", - "line": 191, - "description": "Computes G*inv(M)*G'", - "itemtype": "method", - "name": "computeGiMGt", - "return": { - "description": "", - "type": "Number" - }, - "class": "Equation" - }, - { - "file": "src/equations/Equation.js", - "line": 228, - "description": "Add constraint velocity to the bodies.", - "itemtype": "method", - "name": "addToWlambda", - "params": [ - { - "name": "deltalambda", - "description": "", - "type": "Number" - } - ], - "class": "Equation" - }, - { - "file": "src/equations/Equation.js", - "line": 262, - "description": "Compute the denominator part of the SPOOK equation: C = G*inv(M)*G' + eps", - "itemtype": "method", - "name": "computeInvC", - "params": [ - { - "name": "eps", - "description": "", - "type": "Number" - } - ], - "return": { - "description": "", - "type": "Number" - }, - "class": "Equation" - }, - { - "file": "src/equations/RotationalMotorEquation.js", - "line": 21, - "description": "World oriented rotational axis", - "itemtype": "property", - "name": "axisA", - "type": "Vec3", - "class": "RotationalMotorEquation" - }, - { - "file": "src/equations/RotationalMotorEquation.js", - "line": 27, - "description": "World oriented rotational axis", - "itemtype": "property", - "name": "axisB", - "type": "Vec3", - "class": "RotationalMotorEquation" - }, - { - "file": "src/equations/RotationalMotorEquation.js", - "line": 33, - "description": "Motor velocity", - "itemtype": "property", - "name": "targetVelocity", - "type": "Number", - "class": "RotationalMotorEquation" - }, - { - "file": "src/material/ContactMaterial.js", - "line": 29, - "description": "Identifier of this material", - "itemtype": "property", - "name": "id", - "type": "Number", - "class": "ContactMaterial" - }, - { - "file": "src/material/ContactMaterial.js", - "line": 35, - "description": "Participating materials", - "itemtype": "property", - "name": "materials", - "type": "Array", - "todo": [ - "Should be .materialA and .materialB instead" - ], - "class": "ContactMaterial" - }, - { - "file": "src/material/ContactMaterial.js", - "line": 42, - "description": "Friction coefficient", - "itemtype": "property", - "name": "friction", - "type": "Number", - "class": "ContactMaterial" - }, - { - "file": "src/material/ContactMaterial.js", - "line": 48, - "description": "Restitution coefficient", - "itemtype": "property", - "name": "restitution", - "type": "Number", - "class": "ContactMaterial" - }, - { - "file": "src/material/ContactMaterial.js", - "line": 54, - "description": "Stiffness of the produced contact equations", - "itemtype": "property", - "name": "contactEquationStiffness", - "type": "Number", - "class": "ContactMaterial" - }, - { - "file": "src/material/ContactMaterial.js", - "line": 60, - "description": "Relaxation time of the produced contact equations", - "itemtype": "property", - "name": "contactEquationRelaxation", - "type": "Number", - "class": "ContactMaterial" - }, - { - "file": "src/material/ContactMaterial.js", - "line": 66, - "description": "Stiffness of the produced friction equations", - "itemtype": "property", - "name": "frictionEquationStiffness", - "type": "Number", - "class": "ContactMaterial" - }, - { - "file": "src/material/ContactMaterial.js", - "line": 72, - "description": "Relaxation time of the produced friction equations", - "itemtype": "property", - "name": "frictionEquationRelaxation", - "type": "Number", - "class": "ContactMaterial" - }, - { - "file": "src/material/Material.js", - "line": 22, - "itemtype": "property", - "name": "name", - "type": "{String}", - "class": "Material" - }, - { - "file": "src/material/Material.js", - "line": 28, - "description": "material id.", - "itemtype": "property", - "name": "id", - "type": "{number}", - "class": "Material" - }, - { - "file": "src/material/Material.js", - "line": 35, - "description": "Friction for this material. If non-negative, it will be used instead of the friction given by ContactMaterials. If there's no matching ContactMaterial, the value from .defaultContactMaterial in the World will be used.", - "itemtype": "property", - "name": "friction", - "type": "Number", - "class": "Material" - }, - { - "file": "src/material/Material.js", - "line": 41, - "description": "Restitution for this material. If non-negative, it will be used instead of the restitution given by ContactMaterials. If there's no matching ContactMaterial, the value from .defaultContactMaterial in the World will be used.", - "itemtype": "property", - "name": "restitution", - "type": "Number", - "class": "Material" - }, - { - "file": "src/math/JacobianElement.js", - "line": 12, - "itemtype": "property", - "name": "spatial", - "type": "Vec3", - "class": "JacobianElement" - }, - { - "file": "src/math/JacobianElement.js", - "line": 17, - "itemtype": "property", - "name": "rotational", - "type": "Vec3", - "class": "JacobianElement" - }, - { - "file": "src/math/JacobianElement.js", - "line": 23, - "description": "Multiply with other JacobianElement", - "itemtype": "method", - "name": "multiplyElement", - "params": [ - { - "name": "element", - "description": "", - "type": "JacobianElement" - } - ], - "return": { - "description": "", - "type": "Number" - }, - "class": "JacobianElement" - }, - { - "file": "src/math/JacobianElement.js", - "line": 33, - "description": "Multiply with two vectors", - "itemtype": "method", - "name": "multiplyVectors", - "params": [ - { - "name": "spatial", - "description": "", - "type": "Vec3" - }, - { - "name": "rotational", - "description": "", - "type": "Vec3" - } - ], - "return": { - "description": "", - "type": "Number" - }, - "class": "JacobianElement" - }, - { - "file": "src/math/Mat3.js", - "line": 13, - "description": "A vector of length 9, containing all matrix elements", - "itemtype": "property", - "name": "elements", - "type": "Array", - "class": "Mat3" - }, - { - "file": "src/math/Mat3.js", - "line": 24, - "description": "Sets the matrix to identity", - "itemtype": "method", - "name": "identity", - "todo": [ - "Should perhaps be renamed to setIdentity() to be more clear.", - "Create another function that immediately creates an identity matrix eg. eye()" - ], - "class": "Mat3" - }, - { - "file": "src/math/Mat3.js", - "line": 45, - "description": "Set all elements to zero", - "itemtype": "method", - "name": "setZero", - "class": "Mat3" - }, - { - "file": "src/math/Mat3.js", - "line": 62, - "description": "Sets the matrix diagonal elements from a Vec3", - "itemtype": "method", - "name": "setTrace", - "params": [ - { - "name": "vec3", - "description": "", - "type": "Vec3" - } - ], - "class": "Mat3" - }, - { - "file": "src/math/Mat3.js", - "line": 74, - "description": "Gets the matrix diagonal elements", - "itemtype": "method", - "name": "getTrace", - "return": { - "description": "", - "type": "Vec3" - }, - "class": "Mat3" - }, - { - "file": "src/math/Mat3.js", - "line": 87, - "description": "Matrix-Vector multiplication", - "itemtype": "method", - "name": "vmult", - "params": [ - { - "name": "v", - "description": "The vector to multiply with", - "type": "Vec3" - }, - { - "name": "target", - "description": "Optional, target to save the result in.", - "type": "Vec3" - } - ], - "class": "Mat3" - }, - { - "file": "src/math/Mat3.js", - "line": 107, - "description": "Matrix-scalar multiplication", - "itemtype": "method", - "name": "smult", - "params": [ - { - "name": "s", - "description": "", - "type": "Number" - } - ], - "class": "Mat3" - }, - { - "file": "src/math/Mat3.js", - "line": 118, - "description": "Matrix multiplication", - "itemtype": "method", - "name": "mmult", - "params": [ - { - "name": "m", - "description": "Matrix to multiply with from left side.", - "type": "Mat3" - } - ], - "return": { - "description": "The result.", - "type": "Mat3" - }, - "class": "Mat3" - }, - { - "file": "src/math/Mat3.js", - "line": 138, - "description": "Scale each column of the matrix", - "itemtype": "method", - "name": "scale", - "params": [ - { - "name": "v", - "description": "", - "type": "Vec3" - } - ], - "return": { - "description": "The result.", - "type": "Mat3" - }, - "class": "Mat3" - }, - { - "file": "src/math/Mat3.js", - "line": 156, - "description": "Solve Ax=b", - "itemtype": "method", - "name": "solve", - "params": [ - { - "name": "b", - "description": "The right hand side", - "type": "Vec3" - }, - { - "name": "target", - "description": "Optional. Target vector to save in.", - "type": "Vec3" - } - ], - "return": { - "description": "The solution x", - "type": "Vec3" - }, - "todo": [ - "should reuse arrays" - ], - "class": "Mat3" - }, - { - "file": "src/math/Mat3.js", - "line": 227, - "description": "Get an element in the matrix by index. Index starts at 0, not 1!!!", - "itemtype": "method", - "name": "e", - "params": [ - { - "name": "row", - "description": "", - "type": "Number" - }, - { - "name": "column", - "description": "", - "type": "Number" - }, - { - "name": "value", - "description": "Optional. If provided, the matrix element will be set to this value.", - "type": "Number" - } - ], - "return": { - "description": "", - "type": "Number" - }, - "class": "Mat3" - }, - { - "file": "src/math/Mat3.js", - "line": 244, - "description": "Copy another matrix into this matrix object.", - "itemtype": "method", - "name": "copy", - "params": [ - { - "name": "source", - "description": "", - "type": "Mat3" - } - ], - "return": { - "description": "this", - "type": "Mat3" - }, - "class": "Mat3" - }, - { - "file": "src/math/Mat3.js", - "line": 257, - "description": "Returns a string representation of the matrix.", - "itemtype": "method", - "name": "toString", - "return": { - "description": "string" - }, - "class": "Mat3" - }, - { - "file": "src/math/Mat3.js", - "line": 271, - "description": "reverse the matrix", - "itemtype": "method", - "name": "reverse", - "params": [ - { - "name": "target", - "description": "Optional. Target matrix to save in.", - "type": "Mat3" - } - ], - "return": { - "description": "The solution x", - "type": "Mat3" - }, - "class": "Mat3" - }, - { - "file": "src/math/Mat3.js", - "line": 375, - "description": "Set the matrix from a quaterion", - "itemtype": "method", - "name": "setRotationFromQuaternion", - "params": [ - { - "name": "q", - "description": "", - "type": "Quaternion" - } - ], - "class": "Mat3" - }, - { - "file": "src/math/Mat3.js", - "line": 403, - "description": "Transpose the matrix", - "itemtype": "method", - "name": "transpose", - "params": [ - { - "name": "target", - "description": "Where to store the result.", - "type": "Mat3" - } - ], - "return": { - "description": "The target Mat3, or a new Mat3 if target was omitted.", - "type": "Mat3" - }, - "class": "Mat3" - }, - { - "file": "src/math/Quaternion.js", - "line": 16, - "itemtype": "property", - "name": "x", - "type": "Number", - "class": "Quaternion" - }, - { - "file": "src/math/Quaternion.js", - "line": 21, - "itemtype": "property", - "name": "y", - "type": "Number", - "class": "Quaternion" - }, - { - "file": "src/math/Quaternion.js", - "line": 26, - "itemtype": "property", - "name": "z", - "type": "Number", - "class": "Quaternion" - }, - { - "file": "src/math/Quaternion.js", - "line": 31, - "description": "The multiplier of the real quaternion basis vector.", - "itemtype": "property", - "name": "w", - "type": "Number", - "class": "Quaternion" - }, - { - "file": "src/math/Quaternion.js", - "line": 38, - "description": "Set the value of the quaternion.", - "itemtype": "method", - "name": "set", - "params": [ - { - "name": "x", - "description": "", - "type": "Number" - }, - { - "name": "y", - "description": "", - "type": "Number" - }, - { - "name": "z", - "description": "", - "type": "Number" - }, - { - "name": "w", - "description": "", - "type": "Number" - } - ], - "class": "Quaternion" - }, - { - "file": "src/math/Quaternion.js", - "line": 53, - "description": "Convert to a readable format", - "itemtype": "method", - "name": "toString", - "return": { - "description": "string" - }, - "class": "Quaternion" - }, - { - "file": "src/math/Quaternion.js", - "line": 62, - "description": "Convert to an Array", - "itemtype": "method", - "name": "toArray", - "return": { - "description": "Array" - }, - "class": "Quaternion" - }, - { - "file": "src/math/Quaternion.js", - "line": 71, - "description": "Set the quaternion components given an axis and an angle.", - "itemtype": "method", - "name": "setFromAxisAngle", - "params": [ - { - "name": "axis", - "description": "", - "type": "Vec3" - }, - { - "name": "angle", - "description": "in radians", - "type": "Number" - } - ], - "class": "Quaternion" - }, - { - "file": "src/math/Quaternion.js", - "line": 85, - "description": "Converts the quaternion to axis/angle representation.", - "itemtype": "method", - "name": "toAxisAngle", - "params": [ - { - "name": "targetAxis", - "description": "Optional. A vector object to reuse for storing the axis.", - "type": "Vec3" - } - ], - "return": { - "description": "Array An array, first elemnt is the axis and the second is the angle in radians." - }, - "class": "Quaternion" - }, - { - "file": "src/math/Quaternion.js", - "line": 112, - "description": "Set the quaternion value given two vectors. The resulting rotation will be the needed rotation to rotate u to v.", - "itemtype": "method", - "name": "setFromVectors", - "params": [ - { - "name": "u", - "description": "", - "type": "Vec3" - }, - { - "name": "v", - "description": "", - "type": "Vec3" - } - ], - "class": "Quaternion" - }, - { - "file": "src/math/Quaternion.js", - "line": 135, - "description": "Quaternion multiplication", - "itemtype": "method", - "name": "mult", - "params": [ - { - "name": "q", - "description": "", - "type": "Quaternion" - }, - { - "name": "target", - "description": "Optional.", - "type": "Quaternion" - } - ], - "return": { - "description": "", - "type": "Quaternion" - }, - "class": "Quaternion" - }, - { - "file": "src/math/Quaternion.js", - "line": 164, - "description": "Get the inverse quaternion rotation.", - "itemtype": "method", - "name": "inverse", - "params": [ - { - "name": "target", - "description": "", - "type": "Quaternion" - } - ], - "return": { - "description": "", - "type": "Quaternion" - }, - "class": "Quaternion" - }, - { - "file": "src/math/Quaternion.js", - "line": 184, - "description": "Get the quaternion conjugate", - "itemtype": "method", - "name": "conjugate", - "params": [ - { - "name": "target", - "description": "", - "type": "Quaternion" - } - ], - "return": { - "description": "", - "type": "Quaternion" - }, - "class": "Quaternion" - }, - { - "file": "src/math/Quaternion.js", - "line": 201, - "description": "Normalize the quaternion. Note that this changes the values of the quaternion.", - "itemtype": "method", - "name": "normalize", - "class": "Quaternion" - }, - { - "file": "src/math/Quaternion.js", - "line": 221, - "description": "Approximation of quaternion normalization. Works best when quat is already almost-normalized.", - "itemtype": "method", - "name": "normalizeFast", - "see": [ - "http://jsperf.com/fast-quaternion-normalization" - ], - "author": "unphased, https://github.com/unphased", - "class": "Quaternion" - }, - { - "file": "src/math/Quaternion.js", - "line": 242, - "description": "Multiply the quaternion by a vector", - "itemtype": "method", - "name": "vmult", - "params": [ - { - "name": "v", - "description": "", - "type": "Vec3" - }, - { - "name": "target", - "description": "Optional", - "type": "Vec3" - } - ], - "return": { - "description": "", - "type": "Vec3" - }, - "class": "Quaternion" - }, - { - "file": "src/math/Quaternion.js", - "line": 274, - "description": "Copies value of source to this quaternion.", - "itemtype": "method", - "name": "copy", - "params": [ - { - "name": "source", - "description": "", - "type": "Quaternion" - } - ], - "return": { - "description": "this", - "type": "Quaternion" - }, - "class": "Quaternion" - }, - { - "file": "src/math/Quaternion.js", - "line": 288, - "description": "Convert the quaternion to euler angle representation. Order: YZX, as this page describes: http://www.euclideanspace.com/maths/standards/index.htm", - "itemtype": "method", - "name": "toEuler", - "params": [ - { - "name": "target", - "description": "", - "type": "Vec3" - }, - { - "name": "string", - "description": "order Three-character string e.g. \"YZX\", which also is default." - } - ], - "class": "Quaternion" - }, - { - "file": "src/math/Quaternion.js", - "line": 331, - "description": "See http://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/content/SpinCalc.m", - "itemtype": "method", - "name": "setFromEuler", - "params": [ - { - "name": "x", - "description": "", - "type": "Number" - }, - { - "name": "y", - "description": "", - "type": "Number" - }, - { - "name": "z", - "description": "", - "type": "Number" - }, - { - "name": "order", - "description": "The order to apply angles: 'XYZ' or 'YXZ' or any other combination", - "type": "String" - } - ], - "class": "Quaternion" - }, - { - "file": "src/math/Transform.js", - "line": 13, - "itemtype": "property", - "name": "position", - "type": "Vec3", - "class": "Transform" - }, - { - "file": "src/math/Transform.js", - "line": 21, - "itemtype": "property", - "name": "quaternion", - "type": "Quaternion", - "class": "Transform" - }, - { - "file": "src/math/Transform.js", - "line": 32, - "static": 1, - "itemtype": "method", - "name": "pointToLocaFrame", - "params": [ - { - "name": "position", - "description": "", - "type": "Vec3" - }, - { - "name": "quaternion", - "description": "", - "type": "Quaternion" - }, - { - "name": "worldPoint", - "description": "", - "type": "Vec3" - }, - { - "name": "result", - "description": "", - "type": "Vec3" - } - ], - "class": "Transform" - }, - { - "file": "src/math/Transform.js", - "line": 48, - "description": "Get a global point in local transform coordinates.", - "itemtype": "method", - "name": "pointToLocal", - "params": [ - { - "name": "point", - "description": "", - "type": "Vec3" - }, - { - "name": "result", - "description": "", - "type": "Vec3" - } - ], - "return": { - "description": "The \"result\" vector object", - "type": "Vec3" - }, - "class": "Transform" - }, - { - "file": "src/math/Transform.js", - "line": 59, - "static": 1, - "itemtype": "method", - "name": "pointToWorldFrame", - "params": [ - { - "name": "position", - "description": "", - "type": "Vec3" - }, - { - "name": "quaternion", - "description": "", - "type": "Vec3" - }, - { - "name": "localPoint", - "description": "", - "type": "Vec3" - }, - { - "name": "result", - "description": "", - "type": "Vec3" - } - ], - "class": "Transform" - }, - { - "file": "src/math/Transform.js", - "line": 74, - "description": "Get a local point in global transform coordinates.", - "itemtype": "method", - "name": "pointToWorld", - "params": [ - { - "name": "point", - "description": "", - "type": "Vec3" - }, - { - "name": "result", - "description": "", - "type": "Vec3" - } - ], - "return": { - "description": "The \"result\" vector object", - "type": "Vec3" - }, - "class": "Transform" - }, - { - "file": "src/math/Vec3.js", - "line": 18, - "itemtype": "property", - "name": "x", - "type": "{Number}", - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 24, - "itemtype": "property", - "name": "y", - "type": "{Number}", - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 30, - "itemtype": "property", - "name": "z", - "type": "{Number}", - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 37, - "static": 1, - "itemtype": "property", - "name": "ZERO", - "type": "Vec3", - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 43, - "static": 1, - "itemtype": "property", - "name": "UNIT_X", - "type": "Vec3", - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 49, - "static": 1, - "itemtype": "property", - "name": "UNIT_Y", - "type": "Vec3", - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 55, - "static": 1, - "itemtype": "property", - "name": "UNIT_Z", - "type": "Vec3", - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 61, - "description": "Vector cross product", - "itemtype": "method", - "name": "cross", - "params": [ - { - "name": "v", - "description": "", - "type": "Vec3" - }, - { - "name": "target", - "description": "Optional. Target to save in.", - "type": "Vec3" - } - ], - "return": { - "description": "", - "type": "Vec3" - }, - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 79, - "description": "Set the vectors' 3 elements", - "itemtype": "method", - "name": "set", - "params": [ - { - "name": "x", - "description": "", - "type": "Number" - }, - { - "name": "y", - "description": "", - "type": "Number" - }, - { - "name": "z", - "description": "", - "type": "Number" - } - ], - "return": { - "description": "Vec3" - }, - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 94, - "description": "Set all components of the vector to zero.", - "itemtype": "method", - "name": "setZero", - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 102, - "description": "Vector addition", - "itemtype": "method", - "name": "vadd", - "params": [ - { - "name": "v", - "description": "", - "type": "Vec3" - }, - { - "name": "target", - "description": "Optional.", - "type": "Vec3" - } - ], - "return": { - "description": "", - "type": "Vec3" - }, - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 121, - "description": "Vector subtraction", - "itemtype": "method", - "name": "vsub", - "params": [ - { - "name": "v", - "description": "", - "type": "Vec3" - }, - { - "name": "target", - "description": "Optional. Target to save in.", - "type": "Vec3" - } - ], - "return": { - "description": "", - "type": "Vec3" - }, - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 140, - "description": "Get the cross product matrix a_cross from a vector, such that a x b = a_cross * b = c", - "itemtype": "method", - "name": "crossmat", - "see": [ - "http://www8.cs.umu.se/kurser/TDBD24/VT06/lectures/Lecture6.pdf" - ], - "return": { - "description": "", - "type": "Mat3" - }, - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 152, - "description": "Normalize the vector. Note that this changes the values in the vector.", - "itemtype": "method", - "name": "normalize", - "return": { - "description": "Returns the norm of the vector", - "type": "Number" - }, - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 174, - "description": "Get the version of this vector that is of length 1.", - "itemtype": "method", - "name": "unit", - "params": [ - { - "name": "target", - "description": "Optional target to save in", - "type": "Vec3" - } - ], - "return": { - "description": "Returns the unit vector", - "type": "Vec3" - }, - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 197, - "description": "Get the length of the vector", - "itemtype": "method", - "name": "norm", - "return": { - "description": "", - "type": "Number" - }, - "deprecated": true, - "deprecationMessage": "Use .length() instead", - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 208, - "description": "Get the length of the vector", - "itemtype": "method", - "name": "length", - "return": { - "description": "", - "type": "Number" - }, - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 215, - "description": "Get the squared length of the vector", - "itemtype": "method", - "name": "norm2", - "return": { - "description": "", - "type": "Number" - }, - "deprecated": true, - "deprecationMessage": "Use .lengthSquared() instead.", - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 225, - "description": "Get the squared length of the vector.", - "itemtype": "method", - "name": "lengthSquared", - "return": { - "description": "", - "type": "Number" - }, - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 232, - "description": "Get distance from this point to another point", - "itemtype": "method", - "name": "distanceTo", - "params": [ - { - "name": "p", - "description": "", - "type": "Vec3" - } - ], - "return": { - "description": "", - "type": "Number" - }, - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 246, - "description": "Get squared distance from this point to another point", - "itemtype": "method", - "name": "distanceSquared", - "params": [ - { - "name": "p", - "description": "", - "type": "Vec3" - } - ], - "return": { - "description": "", - "type": "Number" - }, - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 258, - "description": "Multiply all the components of the vector with a scalar.", - "deprecated": true, - "deprecationMessage": "Use .scale() instead", - "itemtype": "method", - "name": "mult", - "params": [ - { - "name": "scalar", - "description": "", - "type": "Number" - }, - { - "name": "target", - "description": "The vector to save the result in.", - "type": "Vec3" - } - ], - "return": { - "description": "", - "type": "Vec3" - }, - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 278, - "description": "Multiply the vector with a scalar.", - "itemtype": "method", - "name": "scale", - "params": [ - { - "name": "scalar", - "description": "", - "type": "Number" - }, - { - "name": "target", - "description": "", - "type": "Vec3" - } - ], - "return": { - "description": "", - "type": "Vec3" - }, - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 287, - "description": "Calculate dot product", - "itemtype": "method", - "name": "dot", - "params": [ - { - "name": "v", - "description": "", - "type": "Vec3" - } - ], - "return": { - "description": "", - "type": "Number" - }, - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 297, - "itemtype": "method", - "name": "isZero", - "return": { - "description": "bool" - }, - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 305, - "description": "Make the vector point in the opposite direction.", - "itemtype": "method", - "name": "negate", - "params": [ - { - "name": "target", - "description": "Optional target to save in", - "type": "Vec3" - } - ], - "return": { - "description": "", - "type": "Vec3" - }, - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 319, - "description": "Compute two artificial tangents to the vector", - "itemtype": "method", - "name": "tangents", - "params": [ - { - "name": "t1", - "description": "Vector object to save the first tangent in", - "type": "Vec3" - }, - { - "name": "t2", - "description": "Vector object to save the second tangent in", - "type": "Vec3" - } - ], - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 349, - "description": "Converts to a more readable format", - "itemtype": "method", - "name": "toString", - "return": { - "description": "string" - }, - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 358, - "description": "Converts to an array", - "itemtype": "method", - "name": "toArray", - "return": { - "description": "Array" - }, - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 367, - "description": "Copies value of source to this vector.", - "itemtype": "method", - "name": "copy", - "params": [ - { - "name": "source", - "description": "", - "type": "Vec3" - } - ], - "return": { - "description": "this", - "type": "Vec3" - }, - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 381, - "description": "Do a linear interpolation between two vectors", - "itemtype": "method", - "name": "lerp", - "params": [ - { - "name": "v", - "description": "", - "type": "Vec3" - }, - { - "name": "t", - "description": "A number between 0 and 1. 0 will make this function return u, and 1 will make it return v. Numbers in between will generate a vector in between them.", - "type": "Number" - }, - { - "name": "target", - "description": "", - "type": "Vec3" - } - ], - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 395, - "description": "Check if a vector equals is almost equal to another one.", - "itemtype": "method", - "name": "almostEquals", - "params": [ - { - "name": "v", - "description": "", - "type": "Vec3" - }, - { - "name": "precision", - "description": "", - "type": "Number" - } - ], - "return": { - "description": "bool" - }, - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 414, - "description": "Check if a vector is almost zero", - "itemtype": "method", - "name": "almostZero", - "params": [ - { - "name": "precision", - "description": "", - "type": "Number" - } - ], - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 433, - "description": "Check if the vector is anti-parallel to another vector.", - "itemtype": "method", - "name": "isAntiparallelTo", - "params": [ - { - "name": "v", - "description": "", - "type": "Vec3" - }, - { - "name": "precision", - "description": "Set to zero for exact comparisons", - "type": "Number" - } - ], - "return": { - "description": "", - "type": "Boolean" - }, - "class": "Vec3" - }, - { - "file": "src/math/Vec3.js", - "line": 445, - "description": "Clone the vector", - "itemtype": "method", - "name": "clone", - "return": { - "description": "", - "type": "Vec3" - }, - "class": "Vec3" - }, - { - "file": "src/objects/Body.js", - "line": 49, - "description": "Reference to the world the body is living in", - "itemtype": "property", - "name": "world", - "type": "{World}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 56, - "description": "Callback function that is used BEFORE stepping the system. Use it to apply forces, for example. Inside the function, \"this\" will refer to this Body object.", - "itemtype": "property", - "name": "preStep", - "type": "{Function}", - "deprecated": true, - "deprecationMessage": "Use World events instead", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 64, - "description": "Callback function that is used AFTER stepping the system. Inside the function, \"this\" will refer to this Body object.", - "itemtype": "property", - "name": "postStep", - "type": "{Function}", - "deprecated": true, - "deprecationMessage": "Use World events instead", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 74, - "itemtype": "property", - "name": "collisionFilterGroup", - "type": "Number", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 79, - "itemtype": "property", - "name": "collisionFilterMask", - "type": "Number", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 84, - "description": "Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled.", - "itemtype": "property", - "name": "collisionResponse", - "type": "Number", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 90, - "itemtype": "property", - "name": "position", - "type": "{Vec3}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 100, - "itemtype": "property", - "name": "previousPosition", - "type": "Vec3", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 105, - "description": "Initial position of the body", - "itemtype": "property", - "name": "initPosition", - "type": "{Vec3}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 112, - "itemtype": "property", - "name": "velocity", - "type": "{Vec3}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 122, - "itemtype": "property", - "name": "initVelocity", - "type": "{Vec3}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 128, - "description": "Linear force on the body", - "itemtype": "property", - "name": "force", - "type": "{Vec3}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 137, - "itemtype": "property", - "name": "mass", - "type": "{Number}", - "default": "0", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 144, - "itemtype": "property", - "name": "invMass", - "type": "{Number}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 150, - "itemtype": "property", - "name": "material", - "type": "{Material}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 156, - "itemtype": "property", - "name": "linearDamping", - "type": "{Number}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 162, - "description": "One of: Body.DYNAMIC, Body.STATIC and Body.KINEMATIC.", - "itemtype": "property", - "name": "type", - "type": "{Number}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 172, - "description": "If true, the body will automatically fall to sleep.", - "itemtype": "property", - "name": "allowSleep", - "type": "{Boolean}", - "default": "true", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 180, - "description": "Current sleep state.", - "itemtype": "property", - "name": "sleepState", - "type": "{Number}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 187, - "description": "If the speed (the norm of the velocity) is smaller than this value, the body is considered sleepy.", - "itemtype": "property", - "name": "sleepSpeedLimit", - "type": "{Number}", - "default": "0.1", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 195, - "description": "If the body has been sleepy for this sleepTimeLimit seconds, it is considered sleeping.", - "itemtype": "property", - "name": "sleepTimeLimit", - "type": "{Number}", - "default": "1", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 208, - "description": "Rotational force on the body, around center of mass", - "itemtype": "property", - "name": "torque", - "type": "Vec3", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 214, - "description": "Orientation of the body", - "itemtype": "property", - "name": "quaternion", - "type": "{Quaternion}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 225, - "itemtype": "property", - "name": "initQuaternion", - "type": "{Quaternion}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 231, - "itemtype": "property", - "name": "angularVelocity", - "type": "{Vec3}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 241, - "itemtype": "property", - "name": "initAngularVelocity", - "type": "{Vec3}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 250, - "itemtype": "property", - "name": "shapes", - "type": "{array}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 256, - "itemtype": "property", - "name": "shapeOffsets", - "type": "{array}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 262, - "itemtype": "property", - "name": "shapeOrientations", - "type": "{array}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 268, - "itemtype": "property", - "name": "inertia", - "type": "{Vec3}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 274, - "itemtype": "property", - "name": "invInertia", - "type": "Vec3", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 279, - "itemtype": "property", - "name": "invInertiaWorld", - "type": "Mat3", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 286, - "itemtype": "property", - "name": "invInertiaSolve", - "type": "Vec3", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 291, - "itemtype": "property", - "name": "invInertiaWorldSolve", - "type": "Mat3", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 296, - "description": "Set to true if you don't want the body to rotate. Make sure to run .updateMassProperties() after changing this.", - "itemtype": "property", - "name": "fixedRotation", - "type": "Boolean", - "default": "false", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 303, - "itemtype": "property", - "name": "angularDamping", - "type": "Number", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 308, - "itemtype": "property", - "name": "aabb", - "type": "{AABB}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 314, - "description": "Indicates if the AABB needs to be updated before use.", - "itemtype": "property", - "name": "aabbNeedsUpdate", - "type": "{Boolean}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 332, - "description": "A dynamic body is fully simulated. Can be moved manually by the user, but normally they move according to forces. A dynamic body can collide with all body types. A dynamic body always has finite, non-zero mass.", - "static": 1, - "itemtype": "property", - "name": "DYNAMIC", - "type": "{Number}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 340, - "description": "A static body does not move during simulation and behaves as if it has infinite mass. Static bodies can be moved manually by setting the position of the body. The velocity of a static body is always zero. Static bodies do not collide with other static or kinematic bodies.", - "static": 1, - "itemtype": "property", - "name": "STATIC", - "type": "{Number}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 348, - "description": "A kinematic body moves under simulation according to its velocity. They do not respond to forces. They can be moved manually, but normally a kinematic body is moved by setting its velocity. A kinematic body behaves as if it has infinite mass. Kinematic bodies do not collide with other static or kinematic bodies.", - "static": 1, - "itemtype": "property", - "name": "KINEMATIC", - "type": "{Number}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 358, - "static": 1, - "itemtype": "property", - "name": "AWAKE", - "type": "{number}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 365, - "static": 1, - "itemtype": "property", - "name": "SLEEPY", - "type": "{number}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 372, - "static": 1, - "itemtype": "property", - "name": "SLEEPING", - "type": "{number}", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 381, - "description": "Wake the body up.", - "itemtype": "method", - "name": "wakeUp", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 393, - "description": "Force body sleep", - "itemtype": "method", - "name": "sleep", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 411, - "description": "Called every timestep to update internal sleep timer and change sleep state if needed.", - "itemtype": "method", - "name": "sleepTick", - "params": [ - { - "name": "time", - "description": "The world time in seconds", - "type": "Number" - } - ], - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 434, - "description": "If the body is sleeping, it should be immovable / have infinite mass during solve. We solve it by having a separate \"solve mass\".", - "itemtype": "method", - "name": "updateSolveMassProperties", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 450, - "description": "Convert a world point to local body frame.", - "itemtype": "method", - "name": "pointToLocalFrame", - "params": [ - { - "name": "worldPoint", - "description": "", - "type": "Vec3" - }, - { - "name": "result", - "description": "", - "type": "Vec3" - } - ], - "return": { - "description": "", - "type": "Vec3" - }, - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 464, - "description": "Convert a world vector to local body frame.", - "itemtype": "method", - "name": "vectorToLocalFrame", - "params": [ - { - "name": "worldPoint", - "description": "", - "type": "Vec3" - }, - { - "name": "result", - "description": "", - "type": "Vec3" - } - ], - "return": { - "description": "", - "type": "Vec3" - }, - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 477, - "description": "Convert a local body point to world frame.", - "itemtype": "method", - "name": "pointToWorldFrame", - "params": [ - { - "name": "localPoint", - "description": "", - "type": "Vec3" - }, - { - "name": "result", - "description": "", - "type": "Vec3" - } - ], - "return": { - "description": "", - "type": "Vec3" - }, - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 491, - "description": "Convert a local body point to world frame.", - "itemtype": "method", - "name": "vectorToWorldFrame", - "params": [ - { - "name": "localVector", - "description": "", - "type": "Vec3" - }, - { - "name": "result", - "description": "", - "type": "Vec3" - } - ], - "return": { - "description": "", - "type": "Vec3" - }, - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 507, - "description": "Add a shape to the body with a local offset and orientation.", - "itemtype": "method", - "name": "addShape", - "params": [ - { - "name": "shape", - "description": "", - "type": "Shape" - }, - { - "name": "offset", - "description": "", - "type": "Vec3" - }, - { - "name": "quaternion", - "description": "", - "type": "Quaternion" - } - ], - "return": { - "description": "The body object, for chainability.", - "type": "Body" - }, - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 537, - "description": "Update the bounding radius of the body. Should be done if any of the shapes are changed.", - "itemtype": "method", - "name": "updateBoundingRadius", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 562, - "description": "Updates the .aabb", - "itemtype": "method", - "name": "computeAABB", - "todo": [ - "rename to updateAABB()" - ], - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 608, - "description": "Update .inertiaWorld and .invInertiaWorld", - "itemtype": "method", - "name": "updateInertiaWorld", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 637, - "description": "Apply force to a world point. This could for example be a point on the Body surface. Applying force this way will add to Body.force and Body.torque.", - "itemtype": "method", - "name": "applyForce", - "params": [ - { - "name": "force", - "description": "The amount of force to add.", - "type": "Vec3" - }, - { - "name": "worldPoint", - "description": "A world point to apply the force on.", - "type": "Vec3" - } - ], - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 665, - "description": "Apply force to a local point in the body.", - "itemtype": "method", - "name": "applyLocalForce", - "params": [ - { - "name": "force", - "description": "The force vector to apply, defined locally in the body frame.", - "type": "Vec3" - }, - { - "name": "localPoint", - "description": "A local point in the body to apply the force on.", - "type": "Vec3" - } - ], - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 688, - "description": "Apply impulse to a world point. This could for example be a point on the Body surface. An impulse is a force added to a body during a short period of time (impulse = force * time). Impulses will be added to Body.velocity and Body.angularVelocity.", - "itemtype": "method", - "name": "applyImpulse", - "params": [ - { - "name": "impulse", - "description": "The amount of impulse to add.", - "type": "Vec3" - }, - { - "name": "worldPoint", - "description": "A world point to apply the force on.", - "type": "Vec3" - } - ], - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 729, - "description": "Apply locally-defined impulse to a local point in the body.", - "itemtype": "method", - "name": "applyLocalImpulse", - "params": [ - { - "name": "force", - "description": "The force vector to apply, defined locally in the body frame.", - "type": "Vec3" - }, - { - "name": "localPoint", - "description": "A local point in the body to apply the force on.", - "type": "Vec3" - } - ], - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 754, - "description": "Should be called whenever you change the body shape or mass.", - "itemtype": "method", - "name": "updateMassProperties", - "class": "Body" - }, - { - "file": "src/objects/Body.js", - "line": 782, - "description": "Get world velocity of a point in the body.", - "itemtype": "method", - "name": "getVelocityAtWorldPoint", - "params": [ - { - "name": "worldPoint", - "description": "", - "type": "Vec3" - }, - { - "name": "result", - "description": "", - "type": "Vec3" - } - ], - "return": { - "description": "The result vector.", - "type": "Vec3" - }, - "class": "Body" - }, - { - "file": "src/objects/RaycastVehicle.js", - "line": 22, - "itemtype": "property", - "name": "chassisBody", - "type": "Body", - "class": "RaycastVehicle" - }, - { - "file": "src/objects/RaycastVehicle.js", - "line": 27, - "description": "An array of WheelInfo objects.", - "itemtype": "property", - "name": "wheelInfos", - "type": "Array", - "class": "RaycastVehicle" - }, - { - "file": "src/objects/RaycastVehicle.js", - "line": 33, - "description": "Will be set to true if the car is sliding.", - "itemtype": "property", - "name": "sliding", - "type": "Boolean", - "class": "RaycastVehicle" - }, - { - "file": "src/objects/RaycastVehicle.js", - "line": 39, - "itemtype": "property", - "name": "world", - "type": "World", - "class": "RaycastVehicle" - }, - { - "file": "src/objects/RaycastVehicle.js", - "line": 44, - "description": "Index of the right axis, 0=x, 1=y, 2=z", - "itemtype": "property", - "name": "indexRightAxis", - "type": "Integer", - "default": "1", - "class": "RaycastVehicle" - }, - { - "file": "src/objects/RaycastVehicle.js", - "line": 51, - "description": "Index of the forward axis, 0=x, 1=y, 2=z", - "itemtype": "property", - "name": "indexForwardAxis", - "type": "Integer", - "default": "0", - "class": "RaycastVehicle" - }, - { - "file": "src/objects/RaycastVehicle.js", - "line": 58, - "description": "Index of the up axis, 0=x, 1=y, 2=z", - "itemtype": "property", - "name": "indexUpAxis", - "type": "Integer", - "default": "2", - "class": "RaycastVehicle" - }, - { - "file": "src/objects/RaycastVehicle.js", - "line": 74, - "description": "Add a wheel. For information about the options, see WheelInfo.", - "itemtype": "method", - "name": "addWheel", - "params": [ - { - "name": "options", - "description": "", - "type": "Object", - "optional": true - } - ], - "class": "RaycastVehicle" - }, - { - "file": "src/objects/RaycastVehicle.js", - "line": 89, - "description": "Set the steering value of a wheel.", - "itemtype": "method", - "name": "setSteeringValue", - "params": [ - { - "name": "value", - "description": "", - "type": "Number" - }, - { - "name": "wheelIndex", - "description": "", - "type": "Integer" - } - ], - "class": "RaycastVehicle" - }, - { - "file": "src/objects/RaycastVehicle.js", - "line": 102, - "description": "Set the wheel force to apply on one of the wheels each time step", - "itemtype": "method", - "name": "applyEngineForce", - "params": [ - { - "name": "value", - "description": "", - "type": "Number" - }, - { - "name": "wheelIndex", - "description": "", - "type": "Integer" - } - ], - "class": "RaycastVehicle" - }, - { - "file": "src/objects/RaycastVehicle.js", - "line": 112, - "description": "Set the braking force of a wheel", - "itemtype": "method", - "name": "setBrake", - "params": [ - { - "name": "brake", - "description": "", - "type": "Number" - }, - { - "name": "wheelIndex", - "description": "", - "type": "Integer" - } - ], - "class": "RaycastVehicle" - }, - { - "file": "src/objects/RaycastVehicle.js", - "line": 122, - "description": "Add the vehicle including its constraints to the world.", - "itemtype": "method", - "name": "addToWorld", - "params": [ - { - "name": "world", - "description": "", - "type": "World" - } - ], - "class": "RaycastVehicle" - }, - { - "file": "src/objects/RaycastVehicle.js", - "line": 138, - "description": "Get one of the wheel axles, world-oriented.", - "access": "private", - "tagname": "", - "itemtype": "method", - "name": "getVehicleAxisWorld", - "params": [ - { - "name": "axisIndex", - "description": "", - "type": "Integer" - }, - { - "name": "result", - "description": "", - "type": "Vec3" - } - ], - "class": "RaycastVehicle" - }, - { - "file": "src/objects/RaycastVehicle.js", - "line": 279, - "description": "Remove the vehicle including its constraints from the world.", - "itemtype": "method", - "name": "removeFromWorld", - "params": [ - { - "name": "world", - "description": "", - "type": "World" - } - ], - "class": "RaycastVehicle" - }, - { - "file": "src/objects/RaycastVehicle.js", - "line": 380, - "description": "Update one of the wheel transform.\nNote when rendering wheels: during each step, wheel transforms are updated BEFORE the chassis; ie. their position becomes invalid after the step. Thus when you render wheels, you must update wheel transforms before rendering them. See raycastVehicle demo for an example.", - "itemtype": "method", - "name": "updateWheelTransform", - "params": [ - { - "name": "wheelIndex", - "description": "The wheel index to update.", - "type": "Integer" - } - ], - "class": "RaycastVehicle" - }, - { - "file": "src/objects/RaycastVehicle.js", - "line": 428, - "description": "Get the world transform of one of the wheels", - "itemtype": "method", - "name": "getWheelTransformWorld", - "params": [ - { - "name": "wheelIndex", - "description": "", - "type": "Integer" - } - ], - "return": { - "description": "", - "type": "Transform" - }, - "class": "RaycastVehicle" - }, - { - "file": "src/objects/RigidVehicle.js", - "line": 18, - "itemtype": "property", - "name": "coordinateSystem", - "type": "{Vec3}", - "class": "RigidVehicle" - }, - { - "file": "src/objects/RigidVehicle.js", - "line": 24, - "itemtype": "property", - "name": "chassisBody", - "type": "Body", - "class": "RigidVehicle" - }, - { - "file": "src/objects/RigidVehicle.js", - "line": 35, - "itemtype": "property", - "name": "constraints", - "type": "{Array}", - "class": "RigidVehicle" - }, - { - "file": "src/objects/RigidVehicle.js", - "line": 45, - "description": "Add a wheel", - "itemtype": "method", - "name": "addWheel", - "params": [ - { - "name": "options", - "description": "", - "type": "Object", - "props": [ - { - "name": "isFrontWheel", - "description": "", - "type": "Boolean", - "optional": true - }, - { - "name": "position", - "description": "Position of the wheel, locally in the chassis body.", - "type": "Vec3", - "optional": true - }, - { - "name": "direction", - "description": "Slide direction of the wheel along the suspension.", - "type": "Vec3", - "optional": true - }, - { - "name": "axis", - "description": "Axis of rotation of the wheel, locally defined in the chassis.", - "type": "Vec3", - "optional": true - }, - { - "name": "body", - "description": "The wheel body.", - "type": "Body", - "optional": true - } - ] - } - ], - "class": "RigidVehicle" - }, - { - "file": "src/objects/RigidVehicle.js", - "line": 89, - "description": "Set the steering value of a wheel.", - "itemtype": "method", - "name": "setSteeringValue", - "params": [ - { - "name": "value", - "description": "", - "type": "Number" - }, - { - "name": "wheelIndex", - "description": "", - "type": "Integer" - } - ], - "todo": [ - "check coordinateSystem" - ], - "class": "RigidVehicle" - }, - { - "file": "src/objects/RigidVehicle.js", - "line": 111, - "description": "Set the target rotational speed of the hinge constraint.", - "itemtype": "method", - "name": "setMotorSpeed", - "params": [ - { - "name": "value", - "description": "", - "type": "Number" - }, - { - "name": "wheelIndex", - "description": "", - "type": "Integer" - } - ], - "class": "RigidVehicle" - }, - { - "file": "src/objects/RigidVehicle.js", - "line": 123, - "description": "Set the target rotational speed of the hinge constraint.", - "itemtype": "method", - "name": "disableMotor", - "params": [ - { - "name": "value", - "description": "", - "type": "Number" - }, - { - "name": "wheelIndex", - "description": "", - "type": "Integer" - } - ], - "class": "RigidVehicle" - }, - { - "file": "src/objects/RigidVehicle.js", - "line": 136, - "description": "Set the wheel force to apply on one of the wheels each time step", - "itemtype": "method", - "name": "setWheelForce", - "params": [ - { - "name": "value", - "description": "", - "type": "Number" - }, - { - "name": "wheelIndex", - "description": "", - "type": "Integer" - } - ], - "class": "RigidVehicle" - }, - { - "file": "src/objects/RigidVehicle.js", - "line": 146, - "description": "Apply a torque on one of the wheels.", - "itemtype": "method", - "name": "applyWheelForce", - "params": [ - { - "name": "value", - "description": "", - "type": "Number" - }, - { - "name": "wheelIndex", - "description": "", - "type": "Integer" - } - ], - "class": "RigidVehicle" - }, - { - "file": "src/objects/RigidVehicle.js", - "line": 162, - "description": "Add the vehicle including its constraints to the world.", - "itemtype": "method", - "name": "addToWorld", - "params": [ - { - "name": "world", - "description": "", - "type": "World" - } - ], - "class": "RigidVehicle" - }, - { - "file": "src/objects/RigidVehicle.js", - "line": 189, - "description": "Remove the vehicle including its constraints from the world.", - "itemtype": "method", - "name": "removeFromWorld", - "params": [ - { - "name": "world", - "description": "", - "type": "World" - } - ], - "class": "RigidVehicle" - }, - { - "file": "src/objects/RigidVehicle.js", - "line": 209, - "description": "Get current rotational velocity of a wheel", - "itemtype": "method", - "name": "getWheelSpeed", - "params": [ - { - "name": "wheelIndex", - "description": "", - "type": "Integer" - } - ], - "class": "RigidVehicle" - }, - { - "file": "src/objects/SPHSystem.js", - "line": 18, - "description": "Density of the system (kg/m3).", - "itemtype": "property", - "name": "density", - "type": "Number", - "class": "SPHSystem" - }, - { - "file": "src/objects/SPHSystem.js", - "line": 24, - "description": "Distance below which two particles are considered to be neighbors.\nIt should be adjusted so there are about 15-20 neighbor particles within this radius.", - "itemtype": "property", - "name": "smoothingRadius", - "type": "Number", - "class": "SPHSystem" - }, - { - "file": "src/objects/SPHSystem.js", - "line": 32, - "description": "Viscosity of the system.", - "itemtype": "property", - "name": "viscosity", - "type": "Number", - "class": "SPHSystem" - }, - { - "file": "src/objects/SPHSystem.js", - "line": 45, - "description": "Add a particle to the system.", - "itemtype": "method", - "name": "add", - "params": [ - { - "name": "particle", - "description": "", - "type": "Body" - } - ], - "class": "SPHSystem" - }, - { - "file": "src/objects/SPHSystem.js", - "line": 57, - "description": "Remove a particle from the system.", - "itemtype": "method", - "name": "remove", - "params": [ - { - "name": "particle", - "description": "", - "type": "Body" - } - ], - "class": "SPHSystem" - }, - { - "file": "src/objects/SPHSystem.js", - "line": 72, - "description": "Get neighbors within smoothing volume, save in the array neighbors", - "itemtype": "method", - "name": "getNeighbors", - "params": [ - { - "name": "particle", - "description": "", - "type": "Body" - }, - { - "name": "neighbors", - "description": "", - "type": "Array" - } - ], - "class": "SPHSystem" - }, - { - "file": "src/objects/Spring.js", - "line": 24, - "description": "Rest length of the spring.", - "itemtype": "property", - "name": "restLength", - "type": "{number}", - "class": "Spring" - }, - { - "file": "src/objects/Spring.js", - "line": 31, - "description": "Stiffness of the spring.", - "itemtype": "property", - "name": "stiffness", - "type": "{number}", - "class": "Spring" - }, - { - "file": "src/objects/Spring.js", - "line": 38, - "description": "Damping of the spring.", - "itemtype": "property", - "name": "damping", - "type": "{number}", - "class": "Spring" - }, - { - "file": "src/objects/Spring.js", - "line": 45, - "description": "First connected body.", - "itemtype": "property", - "name": "bodyA", - "type": "{Body}", - "class": "Spring" - }, - { - "file": "src/objects/Spring.js", - "line": 52, - "description": "Second connected body.", - "itemtype": "property", - "name": "bodyB", - "type": "{Body}", - "class": "Spring" - }, - { - "file": "src/objects/Spring.js", - "line": 59, - "description": "Anchor for bodyA in local bodyA coordinates.", - "itemtype": "property", - "name": "localAnchorA", - "type": "{Vec3}", - "class": "Spring" - }, - { - "file": "src/objects/Spring.js", - "line": 66, - "description": "Anchor for bodyB in local bodyB coordinates.", - "itemtype": "property", - "name": "localAnchorB", - "type": "{Vec3}", - "class": "Spring" - }, - { - "file": "src/objects/Spring.js", - "line": 87, - "description": "Set the anchor point on body A, using world coordinates.", - "itemtype": "method", - "name": "setWorldAnchorA", - "params": [ - { - "name": "worldAnchorA", - "description": "", - "type": "Vec3" - } - ], - "class": "Spring" - }, - { - "file": "src/objects/Spring.js", - "line": 96, - "description": "Set the anchor point on body B, using world coordinates.", - "itemtype": "method", - "name": "setWorldAnchorB", - "params": [ - { - "name": "worldAnchorB", - "description": "", - "type": "Vec3" - } - ], - "class": "Spring" - }, - { - "file": "src/objects/Spring.js", - "line": 105, - "description": "Get the anchor point on body A, in world coordinates.", - "itemtype": "method", - "name": "getWorldAnchorA", - "params": [ - { - "name": "result", - "description": "The vector to store the result in.", - "type": "Vec3" - } - ], - "class": "Spring" - }, - { - "file": "src/objects/Spring.js", - "line": 114, - "description": "Get the anchor point on body B, in world coordinates.", - "itemtype": "method", - "name": "getWorldAnchorB", - "params": [ - { - "name": "result", - "description": "The vector to store the result in.", - "type": "Vec3" - } - ], - "class": "Spring" - }, - { - "file": "src/objects/Spring.js", - "line": 135, - "description": "Apply the spring force to the connected bodies.", - "itemtype": "method", - "name": "applyForce", - "class": "Spring" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 72, - "description": "Max travel distance of the suspension, in meters.", - "itemtype": "property", - "name": "maxSuspensionTravel", - "type": "Number", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 78, - "description": "Speed to apply to the wheel rotation when the wheel is sliding.", - "itemtype": "property", - "name": "customSlidingRotationalSpeed", - "type": "Number", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 84, - "description": "If the customSlidingRotationalSpeed should be used.", - "itemtype": "property", - "name": "useCustomSlidingRotationalSpeed", - "type": "Boolean", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 90, - "itemtype": "property", - "name": "sliding", - "type": "Boolean", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 95, - "description": "Connection point, defined locally in the chassis body frame.", - "itemtype": "property", - "name": "chassisConnectionPointLocal", - "type": "Vec3", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 101, - "itemtype": "property", - "name": "chassisConnectionPointWorld", - "type": "Vec3", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 106, - "itemtype": "property", - "name": "directionLocal", - "type": "Vec3", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 111, - "itemtype": "property", - "name": "directionWorld", - "type": "Vec3", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 116, - "itemtype": "property", - "name": "axleLocal", - "type": "Vec3", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 121, - "itemtype": "property", - "name": "axleWorld", - "type": "Vec3", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 126, - "itemtype": "property", - "name": "suspensionRestLength", - "type": "Number", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 131, - "itemtype": "property", - "name": "suspensionMaxLength", - "type": "Number", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 136, - "itemtype": "property", - "name": "radius", - "type": "Number", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 141, - "itemtype": "property", - "name": "suspensionStiffness", - "type": "Number", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 146, - "itemtype": "property", - "name": "dampingCompression", - "type": "Number", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 151, - "itemtype": "property", - "name": "dampingRelaxation", - "type": "Number", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 156, - "itemtype": "property", - "name": "frictionSlip", - "type": "Number", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 161, - "itemtype": "property", - "name": "steering", - "type": "Number", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 166, - "description": "Rotation value, in radians.", - "itemtype": "property", - "name": "rotation", - "type": "Number", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 172, - "itemtype": "property", - "name": "deltaRotation", - "type": "Number", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 177, - "itemtype": "property", - "name": "rollInfluence", - "type": "Number", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 182, - "itemtype": "property", - "name": "maxSuspensionForce", - "type": "Number", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 187, - "itemtype": "property", - "name": "engineForce", - "type": "Number", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 192, - "itemtype": "property", - "name": "brake", - "type": "Number", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 197, - "itemtype": "property", - "name": "isFrontWheel", - "type": "Number", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 202, - "itemtype": "property", - "name": "clippedInvContactDotSuspension", - "type": "Number", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 207, - "itemtype": "property", - "name": "suspensionRelativeVelocity", - "type": "Number", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 212, - "itemtype": "property", - "name": "suspensionForce", - "type": "Number", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 217, - "itemtype": "property", - "name": "skidInfo", - "type": "Number", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 222, - "itemtype": "property", - "name": "suspensionLength", - "type": "Number", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 227, - "itemtype": "property", - "name": "sideImpulse", - "type": "Number", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 232, - "itemtype": "property", - "name": "forwardImpulse", - "type": "Number", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 237, - "description": "The result from raycasting", - "itemtype": "property", - "name": "raycastResult", - "type": "RaycastResult", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 243, - "description": "Wheel world transform", - "itemtype": "property", - "name": "worldTransform", - "type": "Transform", - "class": "WheelInfo" - }, - { - "file": "src/objects/WheelInfo.js", - "line": 249, - "itemtype": "property", - "name": "isInContact", - "type": "Boolean", - "class": "WheelInfo" - }, - { - "file": "src/shapes/Box.js", - "line": 20, - "itemtype": "property", - "name": "halfExtents", - "type": "{Vec3}", - "class": "Box" - }, - { - "file": "src/shapes/Box.js", - "line": 26, - "description": "Used by the contact generator to make contacts with other convex polyhedra for example", - "itemtype": "property", - "name": "convexPolyhedronRepresentation", - "type": "{ConvexPolyhedron}", - "class": "Box" - }, - { - "file": "src/shapes/Box.js", - "line": 39, - "description": "Updates the local convex polyhedron representation used for some collisions.", - "itemtype": "method", - "name": "updateConvexPolyhedronRepresentation", - "class": "Box" - }, - { - "file": "src/shapes/Box.js", - "line": 80, - "itemtype": "method", - "name": "calculateLocalInertia", - "params": [ - { - "name": "mass", - "description": "", - "type": "Number" - }, - { - "name": "target", - "description": "", - "type": "Vec3" - } - ], - "return": { - "description": "", - "type": "Vec3" - }, - "class": "Box" - }, - { - "file": "src/shapes/Box.js", - "line": 99, - "description": "Get the box 6 side normals", - "itemtype": "method", - "name": "getSideNormals", - "params": [ - { - "name": "sixTargetVectors", - "description": "An array of 6 vectors, to store the resulting side normals in.", - "type": "Array" - }, - { - "name": "quat", - "description": "Orientation to apply to the normal vectors. If not provided, the vectors will be in respect to the local frame.", - "type": "Quaternion" - } - ], - "return": { - "description": "", - "type": "Array" - }, - "class": "Box" - }, - { - "file": "src/shapes/ConvexPolyhedron.js", - "line": 32, - "description": "Array of Vec3", - "itemtype": "property", - "name": "vertices", - "type": "{Array}", - "class": "ConvexPolyhedron" - }, - { - "file": "src/shapes/ConvexPolyhedron.js", - "line": 42, - "description": "Array of integer arrays, indicating which vertices each face consists of", - "itemtype": "property", - "name": "faces", - "type": "{Array}", - "class": "ConvexPolyhedron" - }, - { - "file": "src/shapes/ConvexPolyhedron.js", - "line": 49, - "description": "Array of Vec3", - "itemtype": "property", - "name": "faceNormals", - "type": "{Array}", - "class": "ConvexPolyhedron" - }, - { - "file": "src/shapes/ConvexPolyhedron.js", - "line": 60, - "description": "Array of Vec3", - "itemtype": "property", - "name": "uniqueEdges", - "type": "{Array}", - "class": "ConvexPolyhedron" - }, - { - "file": "src/shapes/ConvexPolyhedron.js", - "line": 67, - "description": "If given, these locally defined, normalized axes are the only ones being checked when doing separating axis check.", - "itemtype": "property", - "name": "uniqueAxes", - "type": "Array", - "class": "ConvexPolyhedron" - }, - { - "file": "src/shapes/ConvexPolyhedron.js", - "line": 80, - "description": "Computes uniqueEdges", - "itemtype": "method", - "name": "computeEdges", - "class": "ConvexPolyhedron" - }, - { - "file": "src/shapes/ConvexPolyhedron.js", - "line": 116, - "description": "Compute the normals of the faces. Will reuse existing Vec3 objects in the .faceNormals array if they exist.", - "itemtype": "method", - "name": "computeNormals", - "class": "ConvexPolyhedron" - }, - { - "file": "src/shapes/ConvexPolyhedron.js", - "line": 147, - "description": "Get face normal given 3 vertices", - "static": 1, - "itemtype": "method", - "name": "getFaceNormal", - "params": [ - { - "name": "va", - "description": "", - "type": "Vec3" - }, - { - "name": "vb", - "description": "", - "type": "Vec3" - }, - { - "name": "vc", - "description": "", - "type": "Vec3" - }, - { - "name": "target", - "description": "", - "type": "Vec3" - } - ], - "class": "ConvexPolyhedron" - }, - { - "file": "src/shapes/ConvexPolyhedron.js", - "line": 167, - "description": "Compute the normal of a face from its vertices", - "itemtype": "method", - "name": "getFaceNormal", - "params": [ - { - "name": "i", - "description": "", - "type": "Number" - }, - { - "name": "target", - "description": "", - "type": "Vec3" - } - ], - "class": "ConvexPolyhedron" - }, - { - "file": "src/shapes/ConvexPolyhedron.js", - "line": 181, - "itemtype": "method", - "name": "clipAgainstHull", - "params": [ - { - "name": "posA", - "description": "", - "type": "Vec3" - }, - { - "name": "quatA", - "description": "", - "type": "Quaternion" - }, - { - "name": "hullB", - "description": "", - "type": "ConvexPolyhedron" - }, - { - "name": "posB", - "description": "", - "type": "Vec3" - }, - { - "name": "quatB", - "description": "", - "type": "Quaternion" - }, - { - "name": "separatingNormal", - "description": "", - "type": "Vec3" - }, - { - "name": "minDist", - "description": "Clamp distance", - "type": "Number" - }, - { - "name": "maxDist", - "description": "", - "type": "Number" - }, - { - "name": "result", - "description": "The an array of contact point objects, see clipFaceAgainstHull", - "type": "Array" - } - ], - "see": [ - "http://bullet.googlecode.com/svn/trunk/src/BulletCollision/NarrowPhaseCollision/btPolyhedralContactClipping.cpp" - ], - "class": "ConvexPolyhedron" - }, - { - "file": "src/shapes/ConvexPolyhedron.js", - "line": 234, - "description": "Find the separating axis between this hull and another", - "itemtype": "method", - "name": "findSeparatingAxis", - "params": [ - { - "name": "hullB", - "description": "", - "type": "ConvexPolyhedron" - }, - { - "name": "posA", - "description": "", - "type": "Vec3" - }, - { - "name": "quatA", - "description": "", - "type": "Quaternion" - }, - { - "name": "posB", - "description": "", - "type": "Vec3" - }, - { - "name": "quatB", - "description": "", - "type": "Quaternion" - }, - { - "name": "target", - "description": "The target vector to save the axis in", - "type": "Vec3" - } - ], - "return": { - "description": "Returns false if a separation is found, else true", - "type": "Bool" - }, - "class": "ConvexPolyhedron" - }, - { - "file": "src/shapes/ConvexPolyhedron.js", - "line": 382, - "description": "Test separating axis against two hulls. Both hulls are projected onto the axis and the overlap size is returned if there is one.", - "itemtype": "method", - "name": "testSepAxis", - "params": [ - { - "name": "axis", - "description": "", - "type": "Vec3" - }, - { - "name": "hullB", - "description": "", - "type": "ConvexPolyhedron" - }, - { - "name": "posA", - "description": "", - "type": "Vec3" - }, - { - "name": "quatA", - "description": "", - "type": "Quaternion" - }, - { - "name": "posB", - "description": "", - "type": "Vec3" - }, - { - "name": "quatB", - "description": "", - "type": "Quaternion" - } - ], - "return": { - "description": "The overlap depth, or FALSE if no penetration.", - "type": "Number" - }, - "class": "ConvexPolyhedron" - }, - { - "file": "src/shapes/ConvexPolyhedron.js", - "line": 413, - "itemtype": "method", - "name": "calculateLocalInertia", - "params": [ - { - "name": "mass", - "description": "", - "type": "Number" - }, - { - "name": "target", - "description": "", - "type": "Vec3" - } - ], - "class": "ConvexPolyhedron" - }, - { - "file": "src/shapes/ConvexPolyhedron.js", - "line": 430, - "itemtype": "method", - "name": "getPlaneConstantOfFace", - "params": [ - { - "name": "face_i", - "description": "Index of the face", - "type": "Number" - } - ], - "return": { - "description": "", - "type": "Number" - }, - "class": "ConvexPolyhedron" - }, - { - "file": "src/shapes/ConvexPolyhedron.js", - "line": 443, - "description": "Clip a face against a hull.", - "itemtype": "method", - "name": "clipFaceAgainstHull", - "params": [ - { - "name": "separatingNormal", - "description": "", - "type": "Vec3" - }, - { - "name": "posA", - "description": "", - "type": "Vec3" - }, - { - "name": "quatA", - "description": "", - "type": "Quaternion" - }, - { - "name": "worldVertsB1", - "description": "An array of Vec3 with vertices in the world frame.", - "type": "Array" - }, - { - "name": "minDist", - "description": "Distance clamping", - "type": "Number" - }, - { - "name": "maxDist", - "description": "", - "type": "Number" - }, - { - "name": "Array", - "description": "result Array to store resulting contact points in. Will be objects with properties: point, depth, normal. These are represented in world coordinates." - } - ], - "class": "ConvexPolyhedron" - }, - { - "file": "src/shapes/ConvexPolyhedron.js", - "line": 588, - "description": "Clip a face in a hull against the back of a plane.", - "itemtype": "method", - "name": "clipFaceAgainstPlane", - "params": [ - { - "name": "inVertices", - "description": "", - "type": "Array" - }, - { - "name": "outVertices", - "description": "", - "type": "Array" - }, - { - "name": "planeNormal", - "description": "", - "type": "Vec3" - }, - { - "name": "planeConstant", - "description": "The constant in the mathematical plane equation", - "type": "Number" - } - ], - "class": "ConvexPolyhedron" - }, - { - "file": "src/shapes/ConvexPolyhedron.js", - "line": 689, - "description": "Updates .worldVertices and sets .worldVerticesNeedsUpdate to false.", - "itemtype": "method", - "name": "computeWorldFaceNormals", - "params": [ - { - "name": "quat", - "description": "", - "type": "Quaternion" - } - ], - "class": "ConvexPolyhedron" - }, - { - "file": "src/shapes/ConvexPolyhedron.js", - "line": 709, - "itemtype": "method", - "name": "updateBoundingSphereRadius", - "class": "ConvexPolyhedron" - }, - { - "file": "src/shapes/ConvexPolyhedron.js", - "line": 727, - "itemtype": "method", - "name": "calculateWorldAABB", - "params": [ - { - "name": "pos", - "description": "", - "type": "Vec3" - }, - { - "name": "quat", - "description": "", - "type": "Quaternion" - }, - { - "name": "min", - "description": "", - "type": "Vec3" - }, - { - "name": "max", - "description": "", - "type": "Vec3" - } - ], - "class": "ConvexPolyhedron" - }, - { - "file": "src/shapes/ConvexPolyhedron.js", - "line": 764, - "description": "Get approximate convex volume", - "itemtype": "method", - "name": "volume", - "return": { - "description": "", - "type": "Number" - }, - "class": "ConvexPolyhedron" - }, - { - "file": "src/shapes/ConvexPolyhedron.js", - "line": 773, - "description": "Get an average of all the vertices positions", - "itemtype": "method", - "name": "getAveragePointLocal", - "params": [ - { - "name": "target", - "description": "", - "type": "Vec3" - } - ], - "return": { - "description": "", - "type": "Vec3" - }, - "class": "ConvexPolyhedron" - }, - { - "file": "src/shapes/ConvexPolyhedron.js", - "line": 790, - "description": "Transform all local points. Will change the .vertices", - "itemtype": "method", - "name": "transformAllPoints", - "params": [ - { - "name": "offset", - "description": "", - "type": "Vec3" - }, - { - "name": "quat", - "description": "", - "type": "Quaternion" - } - ], - "class": "ConvexPolyhedron" - }, - { - "file": "src/shapes/ConvexPolyhedron.js", - "line": 829, - "description": "Checks whether p is inside the polyhedra. Must be in local coords. The point lies outside of the convex hull of the other points if and only if the direction of all the vectors from it to those other points are on less than one half of a sphere around it.", - "itemtype": "method", - "name": "pointIsInside", - "params": [ - { - "name": "p", - "description": "A point given in local coordinates", - "type": "Vec3" - } - ], - "return": { - "description": "", - "type": "Boolean" - }, - "class": "ConvexPolyhedron" - }, - { - "file": "src/shapes/ConvexPolyhedron.js", - "line": 871, - "description": "Get max and min dot product of a convex hull at position (pos,quat) projected onto an axis. Results are saved in the array maxmin.", - "static": 1, - "itemtype": "method", - "name": "project", - "params": [ - { - "name": "hull", - "description": "", - "type": "ConvexPolyhedron" - }, - { - "name": "axis", - "description": "", - "type": "Vec3" - }, - { - "name": "pos", - "description": "", - "type": "Vec3" - }, - { - "name": "quat", - "description": "", - "type": "Quaternion" - }, - { - "name": "result", - "description": "result[0] and result[1] will be set to maximum and minimum, respectively.", - "type": "Array" - } - ], - "class": "ConvexPolyhedron" - }, - { - "file": "src/shapes/Heightfield.js", - "line": 43, - "description": "An array of numbers, or height values, that are spread out along the x axis.", - "itemtype": "property", - "name": "data", - "type": "Array", - "class": "Heightfield" - }, - { - "file": "src/shapes/Heightfield.js", - "line": 49, - "description": "Max value of the data", - "itemtype": "property", - "name": "maxValue", - "type": "Number", - "class": "Heightfield" - }, - { - "file": "src/shapes/Heightfield.js", - "line": 55, - "description": "Max value of the data", - "itemtype": "property", - "name": "minValue", - "type": "Number", - "class": "Heightfield" - }, - { - "file": "src/shapes/Heightfield.js", - "line": 61, - "description": "The width of each element", - "itemtype": "property", - "name": "elementSize", - "type": "Number", - "todo": [ - "elementSizeX and Y" - ], - "class": "Heightfield" - }, - { - "file": "src/shapes/Heightfield.js", - "line": 92, - "description": "Call whenever you change the data array.", - "itemtype": "method", - "name": "update", - "class": "Heightfield" - }, - { - "file": "src/shapes/Heightfield.js", - "line": 100, - "description": "Update the .minValue property", - "itemtype": "method", - "name": "updateMinValue", - "class": "Heightfield" - }, - { - "file": "src/shapes/Heightfield.js", - "line": 118, - "description": "Update the .maxValue property", - "itemtype": "method", - "name": "updateMaxValue", - "class": "Heightfield" - }, - { - "file": "src/shapes/Heightfield.js", - "line": 136, - "description": "Set the height value at an index. Don't forget to update maxValue and minValue after you're done.", - "itemtype": "method", - "name": "setHeightValueAtIndex", - "params": [ - { - "name": "xi", - "description": "", - "type": "Integer" - }, - { - "name": "yi", - "description": "", - "type": "Integer" - }, - { - "name": "value", - "description": "", - "type": "Number" - } - ], - "class": "Heightfield" - }, - { - "file": "src/shapes/Heightfield.js", - "line": 162, - "description": "Get max/min in a rectangle in the matrix data", - "itemtype": "method", - "name": "getRectMinMax", - "params": [ - { - "name": "iMinX", - "description": "", - "type": "Integer" - }, - { - "name": "iMinY", - "description": "", - "type": "Integer" - }, - { - "name": "iMaxX", - "description": "", - "type": "Integer" - }, - { - "name": "iMaxY", - "description": "", - "type": "Integer" - }, - { - "name": "result", - "description": "An array to store the results in.", - "type": "Array", - "optional": true - } - ], - "return": { - "description": "The result array, if it was passed in. Minimum will be at position 0 and max at 1.", - "type": "Array" - }, - "class": "Heightfield" - }, - { - "file": "src/shapes/Heightfield.js", - "line": 191, - "description": "Get the index of a local position on the heightfield. The indexes indicate the rectangles, so if your terrain is made of N x N height data points, you will have rectangle indexes ranging from 0 to N-1.", - "itemtype": "method", - "name": "getIndexOfPosition", - "params": [ - { - "name": "x", - "description": "", - "type": "Number" - }, - { - "name": "y", - "description": "", - "type": "Number" - }, - { - "name": "result", - "description": "Two-element array", - "type": "Array" - }, - { - "name": "clamp", - "description": "If the position should be clamped to the heightfield edge.", - "type": "Boolean" - } - ], - "return": { - "description": "", - "type": "Boolean" - }, - "class": "Heightfield" - }, - { - "file": "src/shapes/Heightfield.js", - "line": 257, - "description": "Get a triangle in the terrain in the form of a triangular convex shape.", - "itemtype": "method", - "name": "getConvexTrianglePillar", - "params": [ - { - "name": "i", - "description": "", - "type": "Integer" - }, - { - "name": "j", - "description": "", - "type": "Integer" - }, - { - "name": "getUpperTriangle", - "description": "", - "type": "Boolean" - } - ], - "class": "Heightfield" - }, - { - "file": "src/shapes/Particle.js", - "line": 21, - "itemtype": "method", - "name": "calculateLocalInertia", - "params": [ - { - "name": "mass", - "description": "", - "type": "Number" - }, - { - "name": "target", - "description": "", - "type": "Vec3" - } - ], - "return": { - "description": "", - "type": "Vec3" - }, - "class": "Particle" - }, - { - "file": "src/shapes/Shape.js", - "line": 17, - "description": "Identifyer of the Shape.", - "itemtype": "property", - "name": "id", - "type": "Number", - "class": "Shape" - }, - { - "file": "src/shapes/Shape.js", - "line": 23, - "description": "The type of this shape. Must be set to an int > 0 by subclasses.", - "itemtype": "property", - "name": "type", - "type": "{Number}", - "see": [ - "Shape.types" - ], - "class": "Shape" - }, - { - "file": "src/shapes/Shape.js", - "line": 31, - "description": "The local bounding sphere radius of this shape.", - "itemtype": "property", - "name": "boundingSphereRadius", - "type": "Number", - "class": "Shape" - }, - { - "file": "src/shapes/Shape.js", - "line": 37, - "description": "Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled.", - "itemtype": "property", - "name": "collisionResponse", - "type": "Boolean", - "class": "Shape" - }, - { - "file": "src/shapes/Shape.js", - "line": 43, - "itemtype": "property", - "name": "material", - "type": "Material", - "class": "Shape" - }, - { - "file": "src/shapes/Shape.js", - "line": 50, - "description": "Computes the bounding sphere radius. The result is stored in the property .boundingSphereRadius", - "itemtype": "method", - "name": "updateBoundingSphereRadius", - "return": { - "description": "", - "type": "Number" - }, - "class": "Shape" - }, - { - "file": "src/shapes/Shape.js", - "line": 59, - "description": "Get the volume of this shape", - "itemtype": "method", - "name": "volume", - "return": { - "description": "", - "type": "Number" - }, - "class": "Shape" - }, - { - "file": "src/shapes/Shape.js", - "line": 68, - "description": "Calculates the inertia in the local frame for this shape.", - "itemtype": "method", - "name": "calculateLocalInertia", - "return": { - "description": "", - "type": "Vec3" - }, - "see": [ - "http://en.wikipedia.org/wiki/List_of_moments_of_inertia" - ], - "class": "Shape" - }, - { - "file": "src/shapes/Shape.js", - "line": 80, - "description": "The available shape types.", - "static": 1, - "itemtype": "property", - "name": "types", - "type": "{Object}", - "class": "Shape" - }, - { - "file": "src/shapes/Sphere.js", - "line": 17, - "itemtype": "property", - "name": "radius", - "type": "Number", - "class": "Sphere" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 32, - "itemtype": "property", - "name": "vertices", - "type": "{Array}", - "class": "Trimesh" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 38, - "description": "Array of integers, indicating which vertices each triangle consists of. The length of this array is thus 3 times the number of triangles.", - "itemtype": "property", - "name": "indices", - "type": "{Array}", - "class": "Trimesh" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 45, - "description": "The normals data.", - "itemtype": "property", - "name": "normals", - "type": "{Array}", - "class": "Trimesh" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 52, - "description": "The local AABB of the mesh.", - "itemtype": "property", - "name": "aabb", - "type": "{Array}", - "class": "Trimesh" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 59, - "description": "References to vertex pairs, making up all unique edges in the trimesh.", - "itemtype": "property", - "name": "edges", - "type": "Array", - "class": "Trimesh" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 65, - "description": "Local scaling of the mesh. Use .setScale() to set it.", - "itemtype": "property", - "name": "scale", - "type": "Vec3", - "class": "Trimesh" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 71, - "description": "The indexed triangles. Use .updateTree() to update it.", - "itemtype": "property", - "name": "tree", - "type": "Octree", - "class": "Trimesh" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 88, - "itemtype": "method", - "name": "updateTree", - "class": "Trimesh" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 127, - "description": "Get triangles in a local AABB from the trimesh.", - "itemtype": "method", - "name": "getTrianglesInAABB", - "params": [ - { - "name": "aabb", - "description": "", - "type": "AABB" - }, - { - "name": "result", - "description": "An array of integers, referencing the queried triangles.", - "type": "Array" - } - ], - "class": "Trimesh" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 153, - "itemtype": "method", - "name": "setScale", - "params": [ - { - "name": "scale", - "description": "", - "type": "Vec3" - } - ], - "class": "Trimesh" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 170, - "description": "Compute the normals of the faces. Will save in the .normals array.", - "itemtype": "method", - "name": "updateNormals", - "class": "Trimesh" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 198, - "description": "Update the .edges property", - "itemtype": "method", - "name": "updateEdges", - "class": "Trimesh" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 226, - "description": "Get an edge vertex", - "itemtype": "method", - "name": "getEdgeVertex", - "params": [ - { - "name": "edgeIndex", - "description": "", - "type": "Number" - }, - { - "name": "firstOrSecond", - "description": "0 or 1, depending on which one of the vertices you need.", - "type": "Number" - }, - { - "name": "vertexStore", - "description": "Where to store the result", - "type": "Vec3" - } - ], - "class": "Trimesh" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 241, - "description": "Get a vector along an edge.", - "itemtype": "method", - "name": "getEdgeVector", - "params": [ - { - "name": "edgeIndex", - "description": "", - "type": "Number" - }, - { - "name": "vectorStore", - "description": "", - "type": "Vec3" - } - ], - "class": "Trimesh" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 255, - "description": "Get face normal given 3 vertices", - "static": 1, - "itemtype": "method", - "name": "computeNormal", - "params": [ - { - "name": "va", - "description": "", - "type": "Vec3" - }, - { - "name": "vb", - "description": "", - "type": "Vec3" - }, - { - "name": "vc", - "description": "", - "type": "Vec3" - }, - { - "name": "target", - "description": "", - "type": "Vec3" - } - ], - "class": "Trimesh" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 279, - "description": "Get vertex i.", - "itemtype": "method", - "name": "getVertex", - "params": [ - { - "name": "i", - "description": "", - "type": "Number" - }, - { - "name": "out", - "description": "", - "type": "Vec3" - } - ], - "return": { - "description": "The \"out\" vector object", - "type": "Vec3" - }, - "class": "Trimesh" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 295, - "description": "Get raw vertex i", - "access": "private", - "tagname": "", - "itemtype": "method", - "name": "_getUnscaledVertex", - "params": [ - { - "name": "i", - "description": "", - "type": "Number" - }, - { - "name": "out", - "description": "", - "type": "Vec3" - } - ], - "return": { - "description": "The \"out\" vector object", - "type": "Vec3" - }, - "class": "Trimesh" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 313, - "description": "Get a vertex from the trimesh,transformed by the given position and quaternion.", - "itemtype": "method", - "name": "getWorldVertex", - "params": [ - { - "name": "i", - "description": "", - "type": "Number" - }, - { - "name": "pos", - "description": "", - "type": "Vec3" - }, - { - "name": "quat", - "description": "", - "type": "Quaternion" - }, - { - "name": "out", - "description": "", - "type": "Vec3" - } - ], - "return": { - "description": "The \"out\" vector object", - "type": "Vec3" - }, - "class": "Trimesh" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 328, - "description": "Get the three vertices for triangle i.", - "itemtype": "method", - "name": "getTriangleVertices", - "params": [ - { - "name": "i", - "description": "", - "type": "Number" - }, - { - "name": "a", - "description": "", - "type": "Vec3" - }, - { - "name": "b", - "description": "", - "type": "Vec3" - }, - { - "name": "c", - "description": "", - "type": "Vec3" - } - ], - "class": "Trimesh" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 343, - "description": "Compute the normal of triangle i.", - "itemtype": "method", - "name": "getNormal", - "params": [ - { - "name": "i", - "description": "", - "type": "Number" - }, - { - "name": "target", - "description": "", - "type": "Vec3" - } - ], - "return": { - "description": "The \"target\" vector object", - "type": "Vec3" - }, - "class": "Trimesh" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 361, - "itemtype": "method", - "name": "calculateLocalInertia", - "params": [ - { - "name": "mass", - "description": "", - "type": "Number" - }, - { - "name": "target", - "description": "", - "type": "Vec3" - } - ], - "return": { - "description": "The \"target\" vector object", - "type": "Vec3" - }, - "class": "Trimesh" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 383, - "description": "Compute the local AABB for the trimesh", - "itemtype": "method", - "name": "computeLocalAABB", - "params": [ - { - "name": "aabb", - "description": "", - "type": "AABB" - } - ], - "class": "Trimesh" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 423, - "description": "Update the .aabb property", - "itemtype": "method", - "name": "updateAABB", - "class": "Trimesh" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 431, - "description": "Will update the .boundingSphereRadius property", - "itemtype": "method", - "name": "updateBoundingSphereRadius", - "class": "Trimesh" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 454, - "itemtype": "method", - "name": "calculateWorldAABB", - "params": [ - { - "name": "pos", - "description": "", - "type": "Vec3" - }, - { - "name": "quat", - "description": "", - "type": "Quaternion" - }, - { - "name": "min", - "description": "", - "type": "Vec3" - }, - { - "name": "max", - "description": "", - "type": "Vec3" - } - ], - "class": "Trimesh" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 504, - "description": "Get approximate volume", - "itemtype": "method", - "name": "volume", - "return": { - "description": "", - "type": "Number" - }, - "class": "Trimesh" - }, - { - "file": "src/shapes/Trimesh.js", - "line": 513, - "description": "Create a Trimesh instance, shaped as a torus.", - "static": 1, - "itemtype": "method", - "name": "createTorus", - "params": [ - { - "name": "radius", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "1" - }, - { - "name": "tube", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "0.5" - }, - { - "name": "radialSegments", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "8" - }, - { - "name": "tubularSegments", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "6" - }, - { - "name": "arc", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "6.283185307179586" - } - ], - "return": { - "description": "A torus", - "type": "Trimesh" - }, - "class": "Trimesh" - }, - { - "file": "src/solver/GSSolver.js", - "line": 19, - "description": "The number of solver iterations determines quality of the constraints in the world. The more iterations, the more correct simulation. More iterations need more computations though. If you have a large gravity force in your world, you will need more iterations.", - "itemtype": "property", - "name": "iterations", - "type": "{Number}", - "todo": [ - "write more about solver and iterations in the wiki" - ], - "class": "GSSolver" - }, - { - "file": "src/solver/GSSolver.js", - "line": 27, - "description": "When tolerance is reached, the system is assumed to be converged.", - "itemtype": "property", - "name": "tolerance", - "type": "{Number}", - "class": "GSSolver" - }, - { - "file": "src/solver/Solver.js", - "line": 10, - "description": "All equations to be solved", - "itemtype": "property", - "name": "equations", - "type": "Array", - "class": "Solver" - }, - { - "file": "src/solver/Solver.js", - "line": 17, - "description": "Should be implemented in subclasses!", - "itemtype": "method", - "name": "solve", - "params": [ - { - "name": "dt", - "description": "", - "type": "Number" - }, - { - "name": "world", - "description": "", - "type": "World" - } - ], - "class": "Solver" - }, - { - "file": "src/solver/Solver.js", - "line": 28, - "description": "Add an equation", - "itemtype": "method", - "name": "addEquation", - "params": [ - { - "name": "eq", - "description": "", - "type": "Equation" - } - ], - "class": "Solver" - }, - { - "file": "src/solver/Solver.js", - "line": 39, - "description": "Remove an equation", - "itemtype": "method", - "name": "removeEquation", - "params": [ - { - "name": "eq", - "description": "", - "type": "Equation" - } - ], - "class": "Solver" - }, - { - "file": "src/solver/Solver.js", - "line": 52, - "description": "Add all equations", - "itemtype": "method", - "name": "removeAllEquations", - "class": "Solver" - }, - { - "file": "src/solver/SplitSolver.js", - "line": 81, - "description": "Solve the subsystems", - "itemtype": "method", - "name": "solve", - "params": [ - { - "name": "dt", - "description": "", - "type": "Number" - }, - { - "name": "world", - "description": "", - "type": "World" - } - ], - "class": "SplitSolver" - }, - { - "file": "src/utils/EventTarget.js", - "line": 15, - "description": "Add an event listener", - "itemtype": "method", - "name": "addEventListener", - "params": [ - { - "name": "type", - "description": "", - "type": "String" - }, - { - "name": "listener", - "description": "", - "type": "Function" - } - ], - "return": { - "description": "The self object, for chainability.", - "type": "EventTarget" - }, - "class": "EventTarget" - }, - { - "file": "src/utils/EventTarget.js", - "line": 34, - "description": "Check if an event listener is added", - "itemtype": "method", - "name": "hasEventListener", - "params": [ - { - "name": "type", - "description": "", - "type": "String" - }, - { - "name": "listener", - "description": "", - "type": "Function" - } - ], - "return": { - "description": "", - "type": "Boolean" - }, - "class": "EventTarget" - }, - { - "file": "src/utils/EventTarget.js", - "line": 50, - "description": "Remove an event listener", - "itemtype": "method", - "name": "removeEventListener", - "params": [ - { - "name": "type", - "description": "", - "type": "String" - }, - { - "name": "listener", - "description": "", - "type": "Function" - } - ], - "return": { - "description": "The self object, for chainability.", - "type": "EventTarget" - }, - "class": "EventTarget" - }, - { - "file": "src/utils/EventTarget.js", - "line": 68, - "description": "Emit an event.", - "itemtype": "method", - "name": "dispatchEvent", - "params": [ - { - "name": "event", - "description": "", - "type": "Object", - "props": [ - { - "name": "type", - "description": "", - "type": "String" - } - ] - } - ], - "return": { - "description": "The self object, for chainability.", - "type": "EventTarget" - }, - "class": "EventTarget" - }, - { - "file": "src/utils/Octree.js", - "line": 15, - "description": "The root node", - "itemtype": "property", - "name": "root", - "type": "OctreeNode", - "class": "OctreeNode" - }, - { - "file": "src/utils/Octree.js", - "line": 21, - "description": "Boundary of this node", - "itemtype": "property", - "name": "aabb", - "type": "AABB", - "class": "OctreeNode" - }, - { - "file": "src/utils/Octree.js", - "line": 27, - "description": "Contained data at the current node level.", - "itemtype": "property", - "name": "data", - "type": "Array", - "class": "OctreeNode" - }, - { - "file": "src/utils/Octree.js", - "line": 33, - "description": "Children to this node", - "itemtype": "property", - "name": "children", - "type": "Array", - "class": "OctreeNode" - }, - { - "file": "src/utils/Octree.js", - "line": 53, - "description": "Maximum subdivision depth", - "itemtype": "property", - "name": "maxDepth", - "type": "Number", - "class": "Octree" - }, - { - "file": "src/utils/Octree.js", - "line": 65, - "description": "Insert data into this node", - "itemtype": "method", - "name": "insert", - "params": [ - { - "name": "aabb", - "description": "", - "type": "AABB" - }, - { - "name": "elementData", - "description": "", - "type": "Object" - } - ], - "return": { - "description": "True if successful, otherwise false", - "type": "Boolean" - }, - "class": "Octree" - }, - { - "file": "src/utils/Octree.js", - "line": 112, - "description": "Create 8 equally sized children nodes and put them in the .children array.", - "itemtype": "method", - "name": "subdivide", - "class": "Octree" - }, - { - "file": "src/utils/Octree.js", - "line": 158, - "description": "Get all data, potentially within an AABB", - "itemtype": "method", - "name": "aabbQuery", - "params": [ - { - "name": "aabb", - "description": "", - "type": "AABB" - }, - { - "name": "result", - "description": "", - "type": "Array" - } - ], - "return": { - "description": "The \"result\" object", - "type": "Array" - }, - "class": "Octree" - }, - { - "file": "src/utils/Octree.js", - "line": 200, - "description": "Get all data, potentially intersected by a ray.", - "itemtype": "method", - "name": "rayQuery", - "params": [ - { - "name": "ray", - "description": "", - "type": "Ray" - }, - { - "name": "treeTransform", - "description": "", - "type": "Transform" - }, - { - "name": "result", - "description": "", - "type": "Array" - } - ], - "return": { - "description": "The \"result\" object", - "type": "Array" - }, - "class": "Octree" - }, - { - "file": "src/utils/Octree.js", - "line": 219, - "itemtype": "method", - "name": "removeEmptyNodes", - "class": "Octree" - }, - { - "file": "src/utils/Pool.js", - "line": 9, - "description": "The pooled objects", - "itemtype": "property", - "name": "objects", - "type": "Array", - "class": "Pool" - }, - { - "file": "src/utils/Pool.js", - "line": 15, - "description": "Constructor of the objects", - "itemtype": "property", - "name": "type", - "type": "Mixed", - "class": "Pool" - }, - { - "file": "src/utils/Pool.js", - "line": 22, - "description": "Release an object after use", - "itemtype": "method", - "name": "release", - "params": [ - { - "name": "obj", - "description": "", - "type": "Object" - } - ], - "class": "Pool" - }, - { - "file": "src/utils/Pool.js", - "line": 34, - "description": "Get an object", - "itemtype": "method", - "name": "get", - "return": { - "description": "", - "type": "Mixed" - }, - "class": "Pool" - }, - { - "file": "src/utils/Pool.js", - "line": 47, - "description": "Construct an object. Should be implmented in each subclass.", - "itemtype": "method", - "name": "constructObject", - "return": { - "description": "", - "type": "Mixed" - }, - "class": "Pool" - }, - { - "file": "src/utils/TupleDictionary.js", - "line": 9, - "description": "The data storage", - "itemtype": "property", - "name": "data", - "type": "{Object}", - "class": "TupleDictionary" - }, - { - "file": "src/utils/TupleDictionary.js", - "line": 17, - "itemtype": "method", - "name": "get", - "params": [ - { - "name": "i", - "description": "", - "type": "Number" - }, - { - "name": "j", - "description": "", - "type": "Number" - } - ], - "return": { - "description": "", - "type": "Number" - }, - "class": "TupleDictionary" - }, - { - "file": "src/utils/TupleDictionary.js", - "line": 33, - "itemtype": "method", - "name": "set", - "params": [ - { - "name": "i", - "description": "", - "type": "Number" - }, - { - "name": "j", - "description": "", - "type": "Number" - }, - { - "name": "value", - "description": "", - "type": "Number" - } - ], - "class": "TupleDictionary" - }, - { - "file": "src/utils/TupleDictionary.js", - "line": 55, - "itemtype": "method", - "name": "reset", - "class": "TupleDictionary" - }, - { - "file": "src/utils/Utils.js", - "line": 5, - "description": "Extend an options object with default values.", - "static": 1, - "itemtype": "method", - "name": "defaults", - "params": [ - { - "name": "options", - "description": "The options object. May be falsy: in this case, a new object is created and returned.", - "type": "Object" - }, - { - "name": "defaults", - "description": "An object containing default values.", - "type": "Object" - } - ], - "return": { - "description": "The modified options object.", - "type": "Object" - }, - "class": "Vec3Pool" - }, - { - "file": "src/utils/Vec3Pool.js", - "line": 17, - "description": "Construct a vector", - "itemtype": "method", - "name": "constructObject", - "return": { - "description": "", - "type": "Vec3" - }, - "class": "Vec3Pool" - }, - { - "file": "src/world/Narrowphase.js", - "line": 25, - "description": "Internal storage of pooled contact points.", - "itemtype": "property", - "name": "contactPointPool", - "type": "Array", - "class": "Narrowphase" - }, - { - "file": "src/world/Narrowphase.js", - "line": 36, - "description": "Pooled vectors.", - "itemtype": "property", - "name": "v3pool", - "type": "Vec3Pool", - "class": "Narrowphase" - }, - { - "file": "src/world/Narrowphase.js", - "line": 45, - "itemtype": "property", - "name": "enableFrictionReduction", - "type": "Boolean", - "class": "Narrowphase" - }, - { - "file": "src/world/Narrowphase.js", - "line": 51, - "description": "Make a contact object, by using the internal pool or creating a new one.", - "itemtype": "method", - "name": "createContactEquation", - "return": { - "description": "", - "type": "ContactEquation" - }, - "class": "Narrowphase" - }, - { - "file": "src/world/Narrowphase.js", - "line": 199, - "description": "Generate all contacts between a list of body pairs", - "itemtype": "method", - "name": "getContacts", - "params": [ - { - "name": "p1", - "description": "Array of body indices", - "type": "Array" - }, - { - "name": "p2", - "description": "Array of body indices", - "type": "Array" - }, - { - "name": "world", - "description": "", - "type": "World" - }, - { - "name": "result", - "description": "Array to store generated contacts", - "type": "Array" - }, - { - "name": "oldcontacts", - "description": "Optional. Array of reusable contact objects", - "type": "Array" - } - ], - "class": "Narrowphase" - }, - { - "file": "src/world/Narrowphase.js", - "line": 308, - "itemtype": "method", - "name": "sphereSphere", - "params": [ - { - "name": "si", - "description": "", - "type": "Shape" - }, - { - "name": "sj", - "description": "", - "type": "Shape" - }, - { - "name": "xi", - "description": "", - "type": "Vec3" - }, - { - "name": "xj", - "description": "", - "type": "Vec3" - }, - { - "name": "qi", - "description": "", - "type": "Quaternion" - }, - { - "name": "qj", - "description": "", - "type": "Quaternion" - }, - { - "name": "bi", - "description": "", - "type": "Body" - }, - { - "name": "bj", - "description": "", - "type": "Body" - } - ], - "class": "Narrowphase" - }, - { - "file": "src/world/Narrowphase.js", - "line": 345, - "itemtype": "method", - "name": "planeTrimesh", - "params": [ - { - "name": "si", - "description": "", - "type": "Shape" - }, - { - "name": "sj", - "description": "", - "type": "Shape" - }, - { - "name": "xi", - "description": "", - "type": "Vec3" - }, - { - "name": "xj", - "description": "", - "type": "Vec3" - }, - { - "name": "qi", - "description": "", - "type": "Quaternion" - }, - { - "name": "qj", - "description": "", - "type": "Quaternion" - }, - { - "name": "bi", - "description": "", - "type": "Body" - }, - { - "name": "bj", - "description": "", - "type": "Body" - } - ], - "class": "Narrowphase" - }, - { - "file": "src/world/Narrowphase.js", - "line": 416, - "itemtype": "method", - "name": "sphereTrimesh", - "params": [ - { - "name": "sphereShape", - "description": "", - "type": "Shape" - }, - { - "name": "trimeshShape", - "description": "", - "type": "Shape" - }, - { - "name": "spherePos", - "description": "", - "type": "Vec3" - }, - { - "name": "trimeshPos", - "description": "", - "type": "Vec3" - }, - { - "name": "sphereQuat", - "description": "", - "type": "Quaternion" - }, - { - "name": "trimeshQuat", - "description": "", - "type": "Quaternion" - }, - { - "name": "sphereBody", - "description": "", - "type": "Body" - }, - { - "name": "trimeshBody", - "description": "", - "type": "Body" - } - ], - "class": "Narrowphase" - }, - { - "file": "src/world/Narrowphase.js", - "line": 612, - "itemtype": "method", - "name": "spherePlane", - "params": [ - { - "name": "si", - "description": "", - "type": "Shape" - }, - { - "name": "sj", - "description": "", - "type": "Shape" - }, - { - "name": "xi", - "description": "", - "type": "Vec3" - }, - { - "name": "xj", - "description": "", - "type": "Vec3" - }, - { - "name": "qi", - "description": "", - "type": "Quaternion" - }, - { - "name": "qj", - "description": "", - "type": "Quaternion" - }, - { - "name": "bi", - "description": "", - "type": "Body" - }, - { - "name": "bj", - "description": "", - "type": "Body" - } - ], - "class": "Narrowphase" - }, - { - "file": "src/world/Narrowphase.js", - "line": 708, - "itemtype": "method", - "name": "sphereBox", - "params": [ - { - "name": "si", - "description": "", - "type": "Shape" - }, - { - "name": "sj", - "description": "", - "type": "Shape" - }, - { - "name": "xi", - "description": "", - "type": "Vec3" - }, - { - "name": "xj", - "description": "", - "type": "Vec3" - }, - { - "name": "qi", - "description": "", - "type": "Quaternion" - }, - { - "name": "qj", - "description": "", - "type": "Quaternion" - }, - { - "name": "bi", - "description": "", - "type": "Body" - }, - { - "name": "bj", - "description": "", - "type": "Body" - } - ], - "class": "Narrowphase" - }, - { - "file": "src/world/Narrowphase.js", - "line": 929, - "itemtype": "method", - "name": "sphereConvex", - "params": [ - { - "name": "si", - "description": "", - "type": "Shape" - }, - { - "name": "sj", - "description": "", - "type": "Shape" - }, - { - "name": "xi", - "description": "", - "type": "Vec3" - }, - { - "name": "xj", - "description": "", - "type": "Vec3" - }, - { - "name": "qi", - "description": "", - "type": "Quaternion" - }, - { - "name": "qj", - "description": "", - "type": "Quaternion" - }, - { - "name": "bi", - "description": "", - "type": "Body" - }, - { - "name": "bj", - "description": "", - "type": "Body" - } - ], - "class": "Narrowphase" - }, - { - "file": "src/world/Narrowphase.js", - "line": 1152, - "itemtype": "method", - "name": "planeBox", - "params": [ - { - "name": "result", - "description": "", - "type": "Array" - }, - { - "name": "si", - "description": "", - "type": "Shape" - }, - { - "name": "sj", - "description": "", - "type": "Shape" - }, - { - "name": "xi", - "description": "", - "type": "Vec3" - }, - { - "name": "xj", - "description": "", - "type": "Vec3" - }, - { - "name": "qi", - "description": "", - "type": "Quaternion" - }, - { - "name": "qj", - "description": "", - "type": "Quaternion" - }, - { - "name": "bi", - "description": "", - "type": "Body" - }, - { - "name": "bj", - "description": "", - "type": "Body" - } - ], - "class": "Narrowphase" - }, - { - "file": "src/world/Narrowphase.js", - "line": 1176, - "itemtype": "method", - "name": "planeConvex", - "params": [ - { - "name": "si", - "description": "", - "type": "Shape" - }, - { - "name": "sj", - "description": "", - "type": "Shape" - }, - { - "name": "xi", - "description": "", - "type": "Vec3" - }, - { - "name": "xj", - "description": "", - "type": "Vec3" - }, - { - "name": "qi", - "description": "", - "type": "Quaternion" - }, - { - "name": "qj", - "description": "", - "type": "Quaternion" - }, - { - "name": "bi", - "description": "", - "type": "Body" - }, - { - "name": "bj", - "description": "", - "type": "Body" - } - ], - "class": "Narrowphase" - }, - { - "file": "src/world/Narrowphase.js", - "line": 1252, - "itemtype": "method", - "name": "convexConvex", - "params": [ - { - "name": "si", - "description": "", - "type": "Shape" - }, - { - "name": "sj", - "description": "", - "type": "Shape" - }, - { - "name": "xi", - "description": "", - "type": "Vec3" - }, - { - "name": "xj", - "description": "", - "type": "Vec3" - }, - { - "name": "qi", - "description": "", - "type": "Quaternion" - }, - { - "name": "qj", - "description": "", - "type": "Quaternion" - }, - { - "name": "bi", - "description": "", - "type": "Body" - }, - { - "name": "bj", - "description": "", - "type": "Body" - } - ], - "class": "Narrowphase" - }, - { - "file": "src/world/Narrowphase.js", - "line": 1309, - "itemtype": "method", - "name": "convexTrimesh", - "params": [ - { - "name": "result", - "description": "", - "type": "Array" - }, - { - "name": "si", - "description": "", - "type": "Shape" - }, - { - "name": "sj", - "description": "", - "type": "Shape" - }, - { - "name": "xi", - "description": "", - "type": "Vec3" - }, - { - "name": "xj", - "description": "", - "type": "Vec3" - }, - { - "name": "qi", - "description": "", - "type": "Quaternion" - }, - { - "name": "qj", - "description": "", - "type": "Quaternion" - }, - { - "name": "bi", - "description": "", - "type": "Body" - }, - { - "name": "bj", - "description": "", - "type": "Body" - } - ], - "class": "Narrowphase" - }, - { - "file": "src/world/Narrowphase.js", - "line": 1393, - "itemtype": "method", - "name": "particlePlane", - "params": [ - { - "name": "result", - "description": "", - "type": "Array" - }, - { - "name": "si", - "description": "", - "type": "Shape" - }, - { - "name": "sj", - "description": "", - "type": "Shape" - }, - { - "name": "xi", - "description": "", - "type": "Vec3" - }, - { - "name": "xj", - "description": "", - "type": "Vec3" - }, - { - "name": "qi", - "description": "", - "type": "Quaternion" - }, - { - "name": "qj", - "description": "", - "type": "Quaternion" - }, - { - "name": "bi", - "description": "", - "type": "Body" - }, - { - "name": "bj", - "description": "", - "type": "Body" - } - ], - "class": "Narrowphase" - }, - { - "file": "src/world/Narrowphase.js", - "line": 1434, - "itemtype": "method", - "name": "particleSphere", - "params": [ - { - "name": "result", - "description": "", - "type": "Array" - }, - { - "name": "si", - "description": "", - "type": "Shape" - }, - { - "name": "sj", - "description": "", - "type": "Shape" - }, - { - "name": "xi", - "description": "", - "type": "Vec3" - }, - { - "name": "xj", - "description": "", - "type": "Vec3" - }, - { - "name": "qi", - "description": "", - "type": "Quaternion" - }, - { - "name": "qj", - "description": "", - "type": "Quaternion" - }, - { - "name": "bi", - "description": "", - "type": "Body" - }, - { - "name": "bj", - "description": "", - "type": "Body" - } - ], - "class": "Narrowphase" - }, - { - "file": "src/world/Narrowphase.js", - "line": 1475, - "itemtype": "method", - "name": "convexParticle", - "params": [ - { - "name": "result", - "description": "", - "type": "Array" - }, - { - "name": "si", - "description": "", - "type": "Shape" - }, - { - "name": "sj", - "description": "", - "type": "Shape" - }, - { - "name": "xi", - "description": "", - "type": "Vec3" - }, - { - "name": "xj", - "description": "", - "type": "Vec3" - }, - { - "name": "qi", - "description": "", - "type": "Quaternion" - }, - { - "name": "qj", - "description": "", - "type": "Quaternion" - }, - { - "name": "bi", - "description": "", - "type": "Body" - }, - { - "name": "bj", - "description": "", - "type": "Body" - } - ], - "class": "Narrowphase" - }, - { - "file": "src/world/Narrowphase.js", - "line": 1573, - "itemtype": "method", - "name": "convexHeightfield", - "class": "Narrowphase" - }, - { - "file": "src/world/Narrowphase.js", - "line": 1651, - "itemtype": "method", - "name": "sphereHeightfield", - "class": "Narrowphase" - }, - { - "file": "src/world/World.js", - "line": 33, - "description": "Currently / last used timestep. Is set to -1 if not available. This value is updated before each internal step, which means that it is \"fresh\" inside event callbacks.", - "itemtype": "property", - "name": "dt", - "type": "Number", - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 39, - "description": "Makes bodies go to sleep when they've been inactive", - "itemtype": "property", - "name": "allowSleep", - "type": "{Boolean}", - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 46, - "description": "All the current contacts (instances of ContactEquation) in the world.", - "itemtype": "property", - "name": "contacts", - "type": "{Array}", - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 54, - "description": "How often to normalize quaternions. Set to 0 for every step, 1 for every second etc.. A larger value increases performance. If bodies tend to explode, set to a smaller value (zero to be sure nothing can go wrong).", - "itemtype": "property", - "name": "quatNormalizeSkip", - "type": "{Number}", - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 61, - "description": "Set to true to use fast quaternion normalization. It is often enough accurate to use. If bodies tend to explode, set to false.", - "itemtype": "property", - "name": "quatNormalizeFast", - "type": "{Boolean}", - "see": [ - "Quaternion.normalizeFast", - "Quaternion.normalize" - ], - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 70, - "description": "The wall-clock time since simulation start", - "itemtype": "property", - "name": "time", - "type": "{Number}", - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 77, - "description": "Number of timesteps taken since start", - "itemtype": "property", - "name": "stepnumber", - "type": "{Number}", - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 88, - "itemtype": "property", - "name": "gravity", - "type": "{Vec3}", - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 94, - "itemtype": "property", - "name": "broadphase", - "type": "{Broadphase}", - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 100, - "itemtype": "property", - "name": "bodies", - "type": "{Array}", - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 106, - "itemtype": "property", - "name": "solver", - "type": "{Solver}", - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 112, - "itemtype": "property", - "name": "constraints", - "type": "{Array}", - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 118, - "itemtype": "property", - "name": "narrowphase", - "type": "{Narrowphase}", - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 124, - "itemtype": "property", - "name": "collisionMatrix", - "type": "{ArrayCollisionMatrix}", - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 130, - "description": "CollisionMatrix from the previous step.", - "itemtype": "property", - "name": "collisionMatrixPrevious", - "type": "{ArrayCollisionMatrix}", - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 137, - "description": "All added materials", - "itemtype": "property", - "name": "materials", - "type": "{Array}", - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 144, - "itemtype": "property", - "name": "contactmaterials", - "type": "{Array}", - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 150, - "description": "Used to look up a ContactMaterial given two instances of Material.", - "itemtype": "property", - "name": "contactMaterialTable", - "type": "TupleDictionary", - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 158, - "description": "This contact material is used if no suitable contactmaterial is found for a contact.", - "itemtype": "property", - "name": "defaultContactMaterial", - "type": "{ContactMaterial}", - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 165, - "itemtype": "property", - "name": "doProfiling", - "type": "{Boolean}", - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 171, - "itemtype": "property", - "name": "profile", - "type": "{Object}", - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 183, - "itemtype": "property", - "name": "subsystems", - "type": "{Array}", - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 206, - "description": "Get the contact material between materials m1 and m2", - "itemtype": "method", - "name": "getContactMaterial", - "params": [ - { - "name": "m1", - "description": "", - "type": "Material" - }, - { - "name": "m2", - "description": "", - "type": "Material" - } - ], - "return": { - "description": "The contact material if it was found.", - "type": "ContactMaterial" - }, - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 217, - "description": "Get number of objects in the world.", - "itemtype": "method", - "name": "numObjects", - "return": { - "description": "", - "type": "Number" - }, - "deprecated": true, - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 227, - "description": "Store old collision state info", - "itemtype": "method", - "name": "collisionMatrixTick", - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 238, - "description": "Add a rigid body to the simulation.", - "itemtype": "method", - "name": "add", - "params": [ - { - "name": "body", - "description": "", - "type": "Body" - } - ], - "todo": [ - "If the simulation has not yet started", - "why recrete and copy arrays for each body? Accumulate in dynamic arrays in this case.", - "Adding an array of bodies should be possible. This would save some loops too" - ], - "deprecated": true, - "deprecationMessage": "Use .addBody instead", - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 265, - "description": "Add a constraint to the simulation.", - "itemtype": "method", - "name": "addConstraint", - "params": [ - { - "name": "c", - "description": "", - "type": "Constraint" - } - ], - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 274, - "description": "Removes a constraint", - "itemtype": "method", - "name": "removeConstraint", - "params": [ - { - "name": "c", - "description": "", - "type": "Constraint" - } - ], - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 286, - "description": "Raycast test", - "itemtype": "method", - "name": "rayTest", - "params": [ - { - "name": "from", - "description": "", - "type": "Vec3" - }, - { - "name": "to", - "description": "", - "type": "Vec3" - }, - { - "name": "result", - "description": "", - "type": "Function|RaycastResult" - } - ], - "deprecated": true, - "deprecationMessage": "Use .raycastAll, .raycastClosest or .raycastAny instead.", - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 308, - "description": "Ray cast against all bodies. The provided callback will be executed for each hit with a RaycastResult as single argument.", - "itemtype": "method", - "name": "raycastAll", - "params": [ - { - "name": "from", - "description": "", - "type": "Vec3" - }, - { - "name": "to", - "description": "", - "type": "Vec3" - }, - { - "name": "options", - "description": "", - "type": "Object", - "props": [ - { - "name": "collisionFilterMask", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "-1" - }, - { - "name": "collisionFilterGroup", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "-1" - }, - { - "name": "skipBackfaces", - "description": "", - "type": "Boolean", - "optional": true, - "optdefault": "false" - }, - { - "name": "checkCollisionResponse", - "description": "", - "type": "Boolean", - "optional": true, - "optdefault": "true" - } - ] - }, - { - "name": "callback", - "description": "", - "type": "Function" - } - ], - "return": { - "description": "True if any body was hit.", - "type": "Boolean" - }, - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 329, - "description": "Ray cast, and stop at the first result. Note that the order is random - but the method is fast.", - "itemtype": "method", - "name": "raycastAny", - "params": [ - { - "name": "from", - "description": "", - "type": "Vec3" - }, - { - "name": "to", - "description": "", - "type": "Vec3" - }, - { - "name": "options", - "description": "", - "type": "Object", - "props": [ - { - "name": "collisionFilterMask", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "-1" - }, - { - "name": "collisionFilterGroup", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "-1" - }, - { - "name": "skipBackfaces", - "description": "", - "type": "Boolean", - "optional": true, - "optdefault": "false" - }, - { - "name": "checkCollisionResponse", - "description": "", - "type": "Boolean", - "optional": true, - "optdefault": "true" - } - ] - }, - { - "name": "result", - "description": "", - "type": "RaycastResult" - } - ], - "return": { - "description": "True if any body was hit.", - "type": "Boolean" - }, - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 350, - "description": "Ray cast, and return information of the closest hit.", - "itemtype": "method", - "name": "raycastClosest", - "params": [ - { - "name": "from", - "description": "", - "type": "Vec3" - }, - { - "name": "to", - "description": "", - "type": "Vec3" - }, - { - "name": "options", - "description": "", - "type": "Object", - "props": [ - { - "name": "collisionFilterMask", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "-1" - }, - { - "name": "collisionFilterGroup", - "description": "", - "type": "Number", - "optional": true, - "optdefault": "-1" - }, - { - "name": "skipBackfaces", - "description": "", - "type": "Boolean", - "optional": true, - "optdefault": "false" - }, - { - "name": "checkCollisionResponse", - "description": "", - "type": "Boolean", - "optional": true, - "optdefault": "true" - } - ] - }, - { - "name": "result", - "description": "", - "type": "RaycastResult" - } - ], - "return": { - "description": "True if any body was hit.", - "type": "Boolean" - }, - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 371, - "description": "Remove a rigid body from the simulation.", - "itemtype": "method", - "name": "remove", - "params": [ - { - "name": "body", - "description": "", - "type": "Body" - } - ], - "deprecated": true, - "deprecationMessage": "Use .removeBody instead", - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 396, - "description": "Remove a rigid body from the simulation.", - "itemtype": "method", - "name": "removeBody", - "params": [ - { - "name": "body", - "description": "", - "type": "Body" - } - ], - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 403, - "description": "Adds a material to the World.", - "itemtype": "method", - "name": "addMaterial", - "params": [ - { - "name": "m", - "description": "", - "type": "Material" - } - ], - "todo": [ - "Necessary?" - ], - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 413, - "description": "Adds a contact material to the World", - "itemtype": "method", - "name": "addContactMaterial", - "params": [ - { - "name": "cmat", - "description": "", - "type": "ContactMaterial" - } - ], - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 443, - "description": "Step the physics world forward in time.\n\nThere are two modes. The simple mode is fixed timestepping without interpolation. In this case you only use the first argument. The second case uses interpolation. In that you also provide the time since the function was last used, as well as the maximum fixed timesteps to take.", - "itemtype": "method", - "name": "step", - "params": [ - { - "name": "dt", - "description": "The fixed time step size to use.", - "type": "Number" - }, - { - "name": "timeSinceLastCalled", - "description": "The time elapsed since the function was last called.", - "type": "Number", - "optional": true - }, - { - "name": "maxSubSteps", - "description": "Maximum number of fixed steps to take per function call.", - "type": "Number", - "optional": true, - "optdefault": "10" - } - ], - "example": [ - "\n // fixed timestepping without interpolation\n world.step(1/60);" - ], - "see": [ - "http://bulletphysics.org/mediawiki-1.5.8/index.php/Stepping_The_World" - ], - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 517, - "description": "Step the simulation", - "itemtype": "method", - "name": "step", - "params": [ - { - "name": "dt", - "description": "", - "type": "Number" - } - ], - "class": "World" - }, - { - "file": "src/world/World.js", - "line": 936, - "description": "Sets all body forces in the world to zero.", - "itemtype": "method", - "name": "clearForces", - "class": "World" - } - ], - "warnings": [] -} \ No newline at end of file diff --git a/docs/files/index.html b/docs/files/index.html deleted file mode 100644 index 487fe15b2..000000000 --- a/docs/files/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - Redirector - - - - Click here to redirect - - diff --git a/docs/files/src_collision_AABB.js.html b/docs/files/src_collision_AABB.js.html deleted file mode 100644 index 95c548f3e..000000000 --- a/docs/files/src_collision_AABB.js.html +++ /dev/null @@ -1,454 +0,0 @@ - - - - - src/collision/AABB.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/collision/AABB.js

    - -
    -
    -var Vec3 = require('../math/Vec3');
    -var Utils = require('../utils/Utils');
    -
    -module.exports = AABB;
    -
    -/**
    - * Axis aligned bounding box class.
    - * @class AABB
    - * @constructor
    - * @param {Object} [options]
    - * @param {Vec3}   [options.upperBound]
    - * @param {Vec3}   [options.lowerBound]
    - */
    -function AABB(options){
    -    options = options || {};
    -
    -    /**
    -     * The lower bound of the bounding box.
    -     * @property lowerBound
    -     * @type {Vec3}
    -     */
    -    this.lowerBound = new Vec3();
    -    if(options.lowerBound){
    -        this.lowerBound.copy(options.lowerBound);
    -    }
    -
    -    /**
    -     * The upper bound of the bounding box.
    -     * @property upperBound
    -     * @type {Vec3}
    -     */
    -    this.upperBound = new Vec3();
    -    if(options.upperBound){
    -        this.upperBound.copy(options.upperBound);
    -    }
    -}
    -
    -var tmp = new Vec3();
    -
    -/**
    - * Set the AABB bounds from a set of points.
    - * @method setFromPoints
    - * @param {Array} points An array of Vec3's.
    - * @param {Vec3} position
    - * @param {Quaternion} quaternion
    - * @param {number} skinSize
    - * @return {AABB} The self object
    - */
    -AABB.prototype.setFromPoints = function(points, position, quaternion, skinSize){
    -    var l = this.lowerBound,
    -        u = this.upperBound,
    -        q = quaternion;
    -
    -    // Set to the first point
    -    l.copy(points[0]);
    -    if(q){
    -        q.vmult(l, l);
    -    }
    -    u.copy(l);
    -
    -    for(var i = 1; i<points.length; i++){
    -        var p = points[i];
    -
    -        if(q){
    -            q.vmult(p, tmp);
    -            p = tmp;
    -        }
    -
    -        if(p.x > u.x){ u.x = p.x; }
    -        if(p.x < l.x){ l.x = p.x; }
    -        if(p.y > u.y){ u.y = p.y; }
    -        if(p.y < l.y){ l.y = p.y; }
    -        if(p.z > u.z){ u.z = p.z; }
    -        if(p.z < l.z){ l.z = p.z; }
    -    }
    -
    -    // Add offset
    -    if (position) {
    -        position.vadd(l, l);
    -        position.vadd(u, u);
    -    }
    -
    -    if(skinSize){
    -        l.x -= skinSize;
    -        l.y -= skinSize;
    -        l.z -= skinSize;
    -        u.x += skinSize;
    -        u.y += skinSize;
    -        u.z += skinSize;
    -    }
    -
    -    return this;
    -};
    -
    -/**
    - * Copy bounds from an AABB to this AABB
    - * @method copy
    - * @param  {AABB} aabb Source to copy from
    - * @return {AABB} The this object, for chainability
    - */
    -AABB.prototype.copy = function(aabb){
    -    this.lowerBound.copy(aabb.lowerBound);
    -    this.upperBound.copy(aabb.upperBound);
    -    return this;
    -};
    -
    -/**
    - * Clone an AABB
    - * @method clone
    - */
    -AABB.prototype.clone = function(){
    -    return new AABB().copy(this);
    -};
    -
    -/**
    - * Extend this AABB so that it covers the given AABB too.
    - * @method extend
    - * @param  {AABB} aabb
    - */
    -AABB.prototype.extend = function(aabb){
    -    // Extend lower bound
    -    var l = aabb.lowerBound.x;
    -    if(this.lowerBound.x > l){
    -        this.lowerBound.x = l;
    -    }
    -
    -    // Upper
    -    var u = aabb.upperBound.x;
    -    if(this.upperBound.x < u){
    -        this.upperBound.x = u;
    -    }
    -
    -    // Extend lower bound
    -    var l = aabb.lowerBound.y;
    -    if(this.lowerBound.y > l){
    -        this.lowerBound.y = l;
    -    }
    -
    -    // Upper
    -    var u = aabb.upperBound.y;
    -    if(this.upperBound.y < u){
    -        this.upperBound.y = u;
    -    }
    -
    -    // Extend lower bound
    -    var l = aabb.lowerBound.z;
    -    if(this.lowerBound.z > l){
    -        this.lowerBound.z = l;
    -    }
    -
    -    // Upper
    -    var u = aabb.upperBound.z;
    -    if(this.upperBound.z < u){
    -        this.upperBound.z = u;
    -    }
    -};
    -
    -/**
    - * Returns true if the given AABB overlaps this AABB.
    - * @method overlaps
    - * @param  {AABB} aabb
    - * @return {Boolean}
    - */
    -AABB.prototype.overlaps = function(aabb){
    -    var l1 = this.lowerBound,
    -        u1 = this.upperBound,
    -        l2 = aabb.lowerBound,
    -        u2 = aabb.upperBound;
    -
    -    //      l2        u2
    -    //      |---------|
    -    // |--------|
    -    // l1       u1
    -
    -    return ((l2.x <= u1.x && u1.x <= u2.x) || (l1.x <= u2.x && u2.x <= u1.x)) &&
    -           ((l2.y <= u1.y && u1.y <= u2.y) || (l1.y <= u2.y && u2.y <= u1.y)) &&
    -           ((l2.z <= u1.z && u1.z <= u2.z) || (l1.z <= u2.z && u2.z <= u1.z));
    -};
    -
    -/**
    - * Returns true if the given AABB is fully contained in this AABB.
    - * @method contains
    - * @param {AABB} aabb
    - * @return {Boolean}
    - */
    -AABB.prototype.contains = function(aabb){
    -    var l1 = this.lowerBound,
    -        u1 = this.upperBound,
    -        l2 = aabb.lowerBound,
    -        u2 = aabb.upperBound;
    -
    -    //      l2        u2
    -    //      |---------|
    -    // |---------------|
    -    // l1              u1
    -
    -    return (
    -        (l1.x <= l2.x && u1.x >= u2.x) &&
    -        (l1.y <= l2.y && u1.y >= u2.y) &&
    -        (l1.z <= l2.z && u1.z >= u2.z)
    -    );
    -};
    -
    -/**
    - * @method getCorners
    - * @param {Vec3} a
    - * @param {Vec3} b
    - * @param {Vec3} c
    - * @param {Vec3} d
    - * @param {Vec3} e
    - * @param {Vec3} f
    - * @param {Vec3} g
    - * @param {Vec3} h
    - */
    -AABB.prototype.getCorners = function(a, b, c, d, e, f, g, h){
    -    var l = this.lowerBound,
    -        u = this.upperBound;
    -
    -    a.copy(l);
    -    b.set( u.x, l.y, l.z );
    -    c.set( u.x, u.y, l.z );
    -    d.set( l.x, u.y, u.z );
    -    e.set( u.x, l.y, l.z );
    -    f.set( l.x, u.y, l.z );
    -    g.set( l.x, l.y, u.z );
    -    h.copy(u);
    -};
    -
    -var transformIntoFrame_corners = [
    -    new Vec3(),
    -    new Vec3(),
    -    new Vec3(),
    -    new Vec3(),
    -    new Vec3(),
    -    new Vec3(),
    -    new Vec3(),
    -    new Vec3()
    -];
    -
    -/**
    - * Get the representation of an AABB in another frame.
    - * @method toLocalFrame
    - * @param  {Transform} frame
    - * @param  {AABB} target
    - * @return {AABB} The "target" AABB object.
    - */
    -AABB.prototype.toLocalFrame = function(frame, target){
    -
    -    var corners = transformIntoFrame_corners;
    -    var a = corners[0];
    -    var b = corners[1];
    -    var c = corners[2];
    -    var d = corners[3];
    -    var e = corners[4];
    -    var f = corners[5];
    -    var g = corners[6];
    -    var h = corners[7];
    -
    -    // Get corners in current frame
    -    this.getCorners(a, b, c, d, e, f, g, h);
    -
    -    // Transform them to new local frame
    -    for(var i=0; i !== 8; i++){
    -        var corner = corners[i];
    -        frame.pointToLocal(corner, corner);
    -    }
    -
    -    return target.setFromPoints(corners);
    -};
    -
    -/**
    - * Get the representation of an AABB in the global frame.
    - * @method toWorldFrame
    - * @param  {Transform} frame
    - * @param  {AABB} target
    - * @return {AABB} The "target" AABB object.
    - */
    -AABB.prototype.toWorldFrame = function(frame, target){
    -
    -    var corners = transformIntoFrame_corners;
    -    var a = corners[0];
    -    var b = corners[1];
    -    var c = corners[2];
    -    var d = corners[3];
    -    var e = corners[4];
    -    var f = corners[5];
    -    var g = corners[6];
    -    var h = corners[7];
    -
    -    // Get corners in current frame
    -    this.getCorners(a, b, c, d, e, f, g, h);
    -
    -    // Transform them to new local frame
    -    for(var i=0; i !== 8; i++){
    -        var corner = corners[i];
    -        frame.pointToWorld(corner, corner);
    -    }
    -
    -    return target.setFromPoints(corners);
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_collision_ArrayCollisionMatrix.js.html b/docs/files/src_collision_ArrayCollisionMatrix.js.html deleted file mode 100644 index bccca991c..000000000 --- a/docs/files/src_collision_ArrayCollisionMatrix.js.html +++ /dev/null @@ -1,225 +0,0 @@ - - - - - src/collision/ArrayCollisionMatrix.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/collision/ArrayCollisionMatrix.js

    - -
    -
    -module.exports = ArrayCollisionMatrix;
    -
    -/**
    - * Collision "matrix". It's actually a triangular-shaped array of whether two bodies are touching this step, for reference next step
    - * @class ArrayCollisionMatrix
    - * @constructor
    - */
    -function ArrayCollisionMatrix() {
    -
    -    /**
    -     * The matrix storage
    -     * @property matrix
    -     * @type {Array}
    -     */
    -	this.matrix = [];
    -}
    -
    -/**
    - * Get an element
    - * @method get
    - * @param  {Number} i
    - * @param  {Number} j
    - * @return {Number}
    - */
    -ArrayCollisionMatrix.prototype.get = function(i, j) {
    -	i = i.index;
    -	j = j.index;
    -    if (j > i) {
    -        var temp = j;
    -        j = i;
    -        i = temp;
    -    }
    -	return this.matrix[(i*(i + 1)>>1) + j-1];
    -};
    -
    -/**
    - * Set an element
    - * @method set
    - * @param {Number} i
    - * @param {Number} j
    - * @param {Number} value
    - */
    -ArrayCollisionMatrix.prototype.set = function(i, j, value) {
    -	i = i.index;
    -	j = j.index;
    -    if (j > i) {
    -        var temp = j;
    -        j = i;
    -        i = temp;
    -    }
    -	this.matrix[(i*(i + 1)>>1) + j-1] = value ? 1 : 0;
    -};
    -
    -/**
    - * Sets all elements to zero
    - * @method reset
    - */
    -ArrayCollisionMatrix.prototype.reset = function() {
    -	for (var i=0, l=this.matrix.length; i!==l; i++) {
    -		this.matrix[i]=0;
    -	}
    -};
    -
    -/**
    - * Sets the max number of objects
    - * @method setNumObjects
    - * @param {Number} n
    - */
    -ArrayCollisionMatrix.prototype.setNumObjects = function(n) {
    -	this.matrix.length = n*(n-1)>>1;
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_collision_Broadphase.js.html b/docs/files/src_collision_Broadphase.js.html deleted file mode 100644 index cee40e858..000000000 --- a/docs/files/src_collision_Broadphase.js.html +++ /dev/null @@ -1,361 +0,0 @@ - - - - - src/collision/Broadphase.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/collision/Broadphase.js

    - -
    -
    -var Body = require('../objects/Body');
    -var Vec3 = require('../math/Vec3');
    -var Quaternion = require('../math/Quaternion');
    -var Shape = require('../shapes/Shape');
    -var Plane = require('../shapes/Plane');
    -
    -module.exports = Broadphase;
    -
    -/**
    - * Base class for broadphase implementations
    - * @class Broadphase
    - * @constructor
    - * @author schteppe
    - */
    -function Broadphase(){
    -    /**
    -    * The world to search for collisions in.
    -    * @property world
    -    * @type {World}
    -    */
    -    this.world = null;
    -
    -    /**
    -     * If set to true, the broadphase uses bounding boxes for intersection test, else it uses bounding spheres.
    -     * @property useBoundingBoxes
    -     * @type {Boolean}
    -     */
    -    this.useBoundingBoxes = false;
    -
    -    /**
    -     * Set to true if the objects in the world moved.
    -     * @property {Boolean} dirty
    -     */
    -    this.dirty = true;
    -}
    -
    -/**
    - * Get the collision pairs from the world
    - * @method collisionPairs
    - * @param {World} world The world to search in
    - * @param {Array} p1 Empty array to be filled with body objects
    - * @param {Array} p2 Empty array to be filled with body objects
    - */
    -Broadphase.prototype.collisionPairs = function(world,p1,p2){
    -    throw new Error("collisionPairs not implemented for this BroadPhase class!");
    -};
    -
    -/**
    - * Check if a body pair needs to be intersection tested at all.
    - * @method needBroadphaseCollision
    - * @param {Body} bodyA
    - * @param {Body} bodyB
    - * @return {bool}
    - */
    -var Broadphase_needBroadphaseCollision_STATIC_OR_KINEMATIC = Body.STATIC | Body.KINEMATIC;
    -Broadphase.prototype.needBroadphaseCollision = function(bodyA,bodyB){
    -
    -    // Check collision filter masks
    -    if( (bodyA.collisionFilterGroup & bodyB.collisionFilterMask)===0 || (bodyB.collisionFilterGroup & bodyA.collisionFilterMask)===0){
    -        return false;
    -    }
    -
    -    // Check types
    -    if(((bodyA.type & Broadphase_needBroadphaseCollision_STATIC_OR_KINEMATIC)!==0 || bodyA.sleepState === Body.SLEEPING) &&
    -       ((bodyB.type & Broadphase_needBroadphaseCollision_STATIC_OR_KINEMATIC)!==0 || bodyB.sleepState === Body.SLEEPING)) {
    -        // Both bodies are static, kinematic or sleeping. Skip.
    -        return false;
    -    }
    -
    -    return true;
    -};
    -
    -/**
    - * Check if the bounding volumes of two bodies intersect.
    - * @method intersectionTest
    - * @param {Body} bodyA
    - * @param {Body} bodyB
    - * @param {array} pairs1
    - * @param {array} pairs2
    -  */
    -Broadphase.prototype.intersectionTest = function(bodyA, bodyB, pairs1, pairs2){
    -    if(this.useBoundingBoxes){
    -        this.doBoundingBoxBroadphase(bodyA,bodyB,pairs1,pairs2);
    -    } else {
    -        this.doBoundingSphereBroadphase(bodyA,bodyB,pairs1,pairs2);
    -    }
    -};
    -
    -/**
    - * Check if the bounding spheres of two bodies are intersecting.
    - * @method doBoundingSphereBroadphase
    - * @param {Body} bodyA
    - * @param {Body} bodyB
    - * @param {Array} pairs1 bodyA is appended to this array if intersection
    - * @param {Array} pairs2 bodyB is appended to this array if intersection
    - */
    -var Broadphase_collisionPairs_r = new Vec3(), // Temp objects
    -    Broadphase_collisionPairs_normal =  new Vec3(),
    -    Broadphase_collisionPairs_quat =  new Quaternion(),
    -    Broadphase_collisionPairs_relpos  =  new Vec3();
    -Broadphase.prototype.doBoundingSphereBroadphase = function(bodyA,bodyB,pairs1,pairs2){
    -    var r = Broadphase_collisionPairs_r;
    -    bodyB.position.vsub(bodyA.position,r);
    -    var boundingRadiusSum2 = Math.pow(bodyA.boundingRadius + bodyB.boundingRadius, 2);
    -    var norm2 = r.norm2();
    -    if(norm2 < boundingRadiusSum2){
    -        pairs1.push(bodyA);
    -        pairs2.push(bodyB);
    -    }
    -};
    -
    -/**
    - * Check if the bounding boxes of two bodies are intersecting.
    - * @method doBoundingBoxBroadphase
    - * @param {Body} bodyA
    - * @param {Body} bodyB
    - * @param {Array} pairs1
    - * @param {Array} pairs2
    - */
    -Broadphase.prototype.doBoundingBoxBroadphase = function(bodyA,bodyB,pairs1,pairs2){
    -    if(bodyA.aabbNeedsUpdate){
    -        bodyA.computeAABB();
    -    }
    -    if(bodyB.aabbNeedsUpdate){
    -        bodyB.computeAABB();
    -    }
    -
    -    // Check AABB / AABB
    -    if(bodyA.aabb.overlaps(bodyB.aabb)){
    -        pairs1.push(bodyA);
    -        pairs2.push(bodyB);
    -    }
    -};
    -
    -/**
    - * Removes duplicate pairs from the pair arrays.
    - * @method makePairsUnique
    - * @param {Array} pairs1
    - * @param {Array} pairs2
    - */
    -var Broadphase_makePairsUnique_temp = { keys:[] },
    -    Broadphase_makePairsUnique_p1 = [],
    -    Broadphase_makePairsUnique_p2 = [];
    -Broadphase.prototype.makePairsUnique = function(pairs1,pairs2){
    -    var t = Broadphase_makePairsUnique_temp,
    -        p1 = Broadphase_makePairsUnique_p1,
    -        p2 = Broadphase_makePairsUnique_p2,
    -        N = pairs1.length;
    -
    -    for(var i=0; i!==N; i++){
    -        p1[i] = pairs1[i];
    -        p2[i] = pairs2[i];
    -    }
    -
    -    pairs1.length = 0;
    -    pairs2.length = 0;
    -
    -    for(var i=0; i!==N; i++){
    -        var id1 = p1[i].id,
    -            id2 = p2[i].id;
    -        var key = id1 < id2 ? id1+","+id2 :  id2+","+id1;
    -        t[key] = i;
    -        t.keys.push(key);
    -    }
    -
    -    for(var i=0; i!==t.keys.length; i++){
    -        var key = t.keys.pop(),
    -            pairIndex = t[key];
    -        pairs1.push(p1[pairIndex]);
    -        pairs2.push(p2[pairIndex]);
    -        delete t[key];
    -    }
    -};
    -
    -/**
    - * To be implemented by subcasses
    - * @method setWorld
    - * @param {World} world
    - */
    -Broadphase.prototype.setWorld = function(world){
    -};
    -
    -/**
    - * Check if the bounding spheres of two bodies overlap.
    - * @method boundingSphereCheck
    - * @param {Body} bodyA
    - * @param {Body} bodyB
    - * @return {boolean}
    - */
    -var bsc_dist = new Vec3();
    -Broadphase.boundingSphereCheck = function(bodyA,bodyB){
    -    var dist = bsc_dist;
    -    bodyA.position.vsub(bodyB.position,dist);
    -    return Math.pow(bodyA.shape.boundingSphereRadius + bodyB.shape.boundingSphereRadius,2) > dist.norm2();
    -};
    -
    -/**
    - * Returns all the bodies within the AABB.
    - * @method aabbQuery
    - * @param  {World} world
    - * @param  {AABB} aabb
    - * @param  {array} result An array to store resulting bodies in.
    - * @return {array}
    - */
    -Broadphase.prototype.aabbQuery = function(world, aabb, result){
    -    console.warn('.aabbQuery is not implemented in this Broadphase subclass.');
    -    return [];
    -};
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_collision_GridBroadphase.js.html b/docs/files/src_collision_GridBroadphase.js.html deleted file mode 100644 index 4aa58345f..000000000 --- a/docs/files/src_collision_GridBroadphase.js.html +++ /dev/null @@ -1,382 +0,0 @@ - - - - - src/collision/GridBroadphase.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/collision/GridBroadphase.js

    - -
    -
    -module.exports = GridBroadphase;
    -
    -var Broadphase = require('./Broadphase');
    -var Vec3 = require('../math/Vec3');
    -var Shape = require('../shapes/Shape');
    -
    -/**
    - * Axis aligned uniform grid broadphase.
    - * @class GridBroadphase
    - * @constructor
    - * @extends Broadphase
    - * @todo Needs support for more than just planes and spheres.
    - * @param {Vec3} aabbMin
    - * @param {Vec3} aabbMax
    - * @param {Number} nx Number of boxes along x
    - * @param {Number} ny Number of boxes along y
    - * @param {Number} nz Number of boxes along z
    - */
    -function GridBroadphase(aabbMin,aabbMax,nx,ny,nz){
    -    Broadphase.apply(this);
    -    this.nx = nx || 10;
    -    this.ny = ny || 10;
    -    this.nz = nz || 10;
    -    this.aabbMin = aabbMin || new Vec3(100,100,100);
    -    this.aabbMax = aabbMax || new Vec3(-100,-100,-100);
    -	var nbins = this.nx * this.ny * this.nz;
    -	if (nbins <= 0) {
    -		throw "GridBroadphase: Each dimension's n must be >0";
    -	}
    -    this.bins = [];
    -	this.binLengths = []; //Rather than continually resizing arrays (thrashing the memory), just record length and allow them to grow
    -	this.bins.length = nbins;
    -	this.binLengths.length = nbins;
    -	for (var i=0;i<nbins;i++) {
    -		this.bins[i]=[];
    -		this.binLengths[i]=0;
    -	}
    -}
    -GridBroadphase.prototype = new Broadphase();
    -GridBroadphase.prototype.constructor = GridBroadphase;
    -
    -/**
    - * Get all the collision pairs in the physics world
    - * @method collisionPairs
    - * @param {World} world
    - * @param {Array} pairs1
    - * @param {Array} pairs2
    - */
    -var GridBroadphase_collisionPairs_d = new Vec3();
    -var GridBroadphase_collisionPairs_binPos = new Vec3();
    -GridBroadphase.prototype.collisionPairs = function(world,pairs1,pairs2){
    -    var N = world.numObjects(),
    -        bodies = world.bodies;
    -
    -    var max = this.aabbMax,
    -        min = this.aabbMin,
    -        nx = this.nx,
    -        ny = this.ny,
    -        nz = this.nz;
    -
    -	var xstep = ny*nz;
    -	var ystep = nz;
    -	var zstep = 1;
    -
    -    var xmax = max.x,
    -        ymax = max.y,
    -        zmax = max.z,
    -        xmin = min.x,
    -        ymin = min.y,
    -        zmin = min.z;
    -
    -    var xmult = nx / (xmax-xmin),
    -        ymult = ny / (ymax-ymin),
    -        zmult = nz / (zmax-zmin);
    -
    -    var binsizeX = (xmax - xmin) / nx,
    -        binsizeY = (ymax - ymin) / ny,
    -        binsizeZ = (zmax - zmin) / nz;
    -
    -	var binRadius = Math.sqrt(binsizeX*binsizeX + binsizeY*binsizeY + binsizeZ*binsizeZ) * 0.5;
    -
    -    var types = Shape.types;
    -    var SPHERE =            types.SPHERE,
    -        PLANE =             types.PLANE,
    -        BOX =               types.BOX,
    -        COMPOUND =          types.COMPOUND,
    -        CONVEXPOLYHEDRON =  types.CONVEXPOLYHEDRON;
    -
    -    var bins=this.bins,
    -		binLengths=this.binLengths,
    -        Nbins=this.bins.length;
    -
    -    // Reset bins
    -    for(var i=0; i!==Nbins; i++){
    -        binLengths[i] = 0;
    -    }
    -
    -    var ceil = Math.ceil;
    -	var min = Math.min;
    -	var max = Math.max;
    -
    -	function addBoxToBins(x0,y0,z0,x1,y1,z1,bi) {
    -		var xoff0 = ((x0 - xmin) * xmult)|0,
    -			yoff0 = ((y0 - ymin) * ymult)|0,
    -			zoff0 = ((z0 - zmin) * zmult)|0,
    -			xoff1 = ceil((x1 - xmin) * xmult),
    -			yoff1 = ceil((y1 - ymin) * ymult),
    -			zoff1 = ceil((z1 - zmin) * zmult);
    -
    -		if (xoff0 < 0) { xoff0 = 0; } else if (xoff0 >= nx) { xoff0 = nx - 1; }
    -		if (yoff0 < 0) { yoff0 = 0; } else if (yoff0 >= ny) { yoff0 = ny - 1; }
    -		if (zoff0 < 0) { zoff0 = 0; } else if (zoff0 >= nz) { zoff0 = nz - 1; }
    -		if (xoff1 < 0) { xoff1 = 0; } else if (xoff1 >= nx) { xoff1 = nx - 1; }
    -		if (yoff1 < 0) { yoff1 = 0; } else if (yoff1 >= ny) { yoff1 = ny - 1; }
    -		if (zoff1 < 0) { zoff1 = 0; } else if (zoff1 >= nz) { zoff1 = nz - 1; }
    -
    -		xoff0 *= xstep;
    -		yoff0 *= ystep;
    -		zoff0 *= zstep;
    -		xoff1 *= xstep;
    -		yoff1 *= ystep;
    -		zoff1 *= zstep;
    -
    -		for (var xoff = xoff0; xoff <= xoff1; xoff += xstep) {
    -			for (var yoff = yoff0; yoff <= yoff1; yoff += ystep) {
    -				for (var zoff = zoff0; zoff <= zoff1; zoff += zstep) {
    -					var idx = xoff+yoff+zoff;
    -					bins[idx][binLengths[idx]++] = bi;
    -				}
    -			}
    -		}
    -	}
    -
    -    // Put all bodies into the bins
    -    for(var i=0; i!==N; i++){
    -        var bi = bodies[i];
    -        var si = bi.shape;
    -
    -        switch(si.type){
    -        case SPHERE:
    -            // Put in bin
    -            // check if overlap with other bins
    -            var x = bi.position.x,
    -                y = bi.position.y,
    -                z = bi.position.z;
    -            var r = si.radius;
    -
    -			addBoxToBins(x-r, y-r, z-r, x+r, y+r, z+r, bi);
    -            break;
    -
    -        case PLANE:
    -            if(si.worldNormalNeedsUpdate){
    -                si.computeWorldNormal(bi.quaternion);
    -            }
    -            var planeNormal = si.worldNormal;
    -
    -			//Relative position from origin of plane object to the first bin
    -			//Incremented as we iterate through the bins
    -			var xreset = xmin + binsizeX*0.5 - bi.position.x,
    -				yreset = ymin + binsizeY*0.5 - bi.position.y,
    -				zreset = zmin + binsizeZ*0.5 - bi.position.z;
    -
    -            var d = GridBroadphase_collisionPairs_d;
    -			d.set(xreset, yreset, zreset);
    -
    -			for (var xi = 0, xoff = 0; xi !== nx; xi++, xoff += xstep, d.y = yreset, d.x += binsizeX) {
    -				for (var yi = 0, yoff = 0; yi !== ny; yi++, yoff += ystep, d.z = zreset, d.y += binsizeY) {
    -					for (var zi = 0, zoff = 0; zi !== nz; zi++, zoff += zstep, d.z += binsizeZ) {
    -						if (d.dot(planeNormal) < binRadius) {
    -							var idx = xoff + yoff + zoff;
    -							bins[idx][binLengths[idx]++] = bi;
    -						}
    -					}
    -				}
    -			}
    -            break;
    -
    -        default:
    -			if (bi.aabbNeedsUpdate) {
    -				bi.computeAABB();
    -			}
    -
    -			addBoxToBins(
    -				bi.aabb.lowerBound.x,
    -				bi.aabb.lowerBound.y,
    -				bi.aabb.lowerBound.z,
    -				bi.aabb.upperBound.x,
    -				bi.aabb.upperBound.y,
    -				bi.aabb.upperBound.z,
    -				bi);
    -            break;
    -        }
    -    }
    -
    -    // Check each bin
    -    for(var i=0; i!==Nbins; i++){
    -		var binLength = binLengths[i];
    -		//Skip bins with no potential collisions
    -		if (binLength > 1) {
    -			var bin = bins[i];
    -
    -			// Do N^2 broadphase inside
    -			for(var xi=0; xi!==binLength; xi++){
    -				var bi = bin[xi];
    -				for(var yi=0; yi!==xi; yi++){
    -					var bj = bin[yi];
    -					if(this.needBroadphaseCollision(bi,bj)){
    -						this.intersectionTest(bi,bj,pairs1,pairs2);
    -					}
    -				}
    -			}
    -		}
    -    }
    -
    -//	for (var zi = 0, zoff=0; zi < nz; zi++, zoff+= zstep) {
    -//		console.log("layer "+zi);
    -//		for (var yi = 0, yoff=0; yi < ny; yi++, yoff += ystep) {
    -//			var row = '';
    -//			for (var xi = 0, xoff=0; xi < nx; xi++, xoff += xstep) {
    -//				var idx = xoff + yoff + zoff;
    -//				row += ' ' + binLengths[idx];
    -//			}
    -//			console.log(row);
    -//		}
    -//	}
    -
    -    this.makePairsUnique(pairs1,pairs2);
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_collision_NaiveBroadphase.js.html b/docs/files/src_collision_NaiveBroadphase.js.html deleted file mode 100644 index 2ca1859dd..000000000 --- a/docs/files/src_collision_NaiveBroadphase.js.html +++ /dev/null @@ -1,227 +0,0 @@ - - - - - src/collision/NaiveBroadphase.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/collision/NaiveBroadphase.js

    - -
    -
    -module.exports = NaiveBroadphase;
    -
    -var Broadphase = require('./Broadphase');
    -var AABB = require('./AABB');
    -
    -/**
    - * Naive broadphase implementation, used in lack of better ones.
    - * @class NaiveBroadphase
    - * @constructor
    - * @description The naive broadphase looks at all possible pairs without restriction, therefore it has complexity N^2 (which is bad)
    - * @extends Broadphase
    - */
    -function NaiveBroadphase(){
    -    Broadphase.apply(this);
    -}
    -NaiveBroadphase.prototype = new Broadphase();
    -NaiveBroadphase.prototype.constructor = NaiveBroadphase;
    -
    -/**
    - * Get all the collision pairs in the physics world
    - * @method collisionPairs
    - * @param {World} world
    - * @param {Array} pairs1
    - * @param {Array} pairs2
    - */
    -NaiveBroadphase.prototype.collisionPairs = function(world,pairs1,pairs2){
    -    var bodies = world.bodies,
    -        n = bodies.length,
    -        i,j,bi,bj;
    -
    -    // Naive N^2 ftw!
    -    for(i=0; i!==n; i++){
    -        for(j=0; j!==i; j++){
    -
    -            bi = bodies[i];
    -            bj = bodies[j];
    -
    -            if(!this.needBroadphaseCollision(bi,bj)){
    -                continue;
    -            }
    -
    -            this.intersectionTest(bi,bj,pairs1,pairs2);
    -        }
    -    }
    -};
    -
    -var tmpAABB = new AABB();
    -
    -/**
    - * Returns all the bodies within an AABB.
    - * @method aabbQuery
    - * @param  {World} world
    - * @param  {AABB} aabb
    - * @param {array} result An array to store resulting bodies in.
    - * @return {array}
    - */
    -NaiveBroadphase.prototype.aabbQuery = function(world, aabb, result){
    -    result = result || [];
    -
    -    for(var i = 0; i < world.bodies.length; i++){
    -        var b = world.bodies[i];
    -
    -        if(b.aabbNeedsUpdate){
    -            b.computeAABB();
    -        }
    -
    -        // Ugly hack until Body gets aabb
    -        if(b.aabb.overlaps(aabb)){
    -            result.push(b);
    -        }
    -    }
    -
    -    return result;
    -};
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_collision_ObjectCollisionMatrix.js.html b/docs/files/src_collision_ObjectCollisionMatrix.js.html deleted file mode 100644 index 09a78e686..000000000 --- a/docs/files/src_collision_ObjectCollisionMatrix.js.html +++ /dev/null @@ -1,225 +0,0 @@ - - - - - src/collision/ObjectCollisionMatrix.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/collision/ObjectCollisionMatrix.js

    - -
    -
    -module.exports = ObjectCollisionMatrix;
    -
    -/**
    - * Records what objects are colliding with each other
    - * @class ObjectCollisionMatrix
    - * @constructor
    - */
    -function ObjectCollisionMatrix() {
    -
    -    /**
    -     * The matrix storage
    -     * @property matrix
    -     * @type {Object}
    -     */
    -	this.matrix = {};
    -}
    -
    -/**
    - * @method get
    - * @param  {Number} i
    - * @param  {Number} j
    - * @return {Number}
    - */
    -ObjectCollisionMatrix.prototype.get = function(i, j) {
    -	i = i.id;
    -	j = j.id;
    -    if (j > i) {
    -        var temp = j;
    -        j = i;
    -        i = temp;
    -    }
    -	return i+'-'+j in this.matrix;
    -};
    -
    -/**
    - * @method set
    - * @param  {Number} i
    - * @param  {Number} j
    - * @param {Number} value
    - */
    -ObjectCollisionMatrix.prototype.set = function(i, j, value) {
    -	i = i.id;
    -	j = j.id;
    -    if (j > i) {
    -        var temp = j;
    -        j = i;
    -        i = temp;
    -	}
    -	if (value) {
    -		this.matrix[i+'-'+j] = true;
    -	}
    -	else {
    -		delete this.matrix[i+'-'+j];
    -	}
    -};
    -
    -/**
    - * Empty the matrix
    - * @method reset
    - */
    -ObjectCollisionMatrix.prototype.reset = function() {
    -	this.matrix = {};
    -};
    -
    -/**
    - * Set max number of objects
    - * @method setNumObjects
    - * @param {Number} n
    - */
    -ObjectCollisionMatrix.prototype.setNumObjects = function(n) {
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_collision_Ray.js.html b/docs/files/src_collision_Ray.js.html deleted file mode 100644 index f594dbb4a..000000000 --- a/docs/files/src_collision_Ray.js.html +++ /dev/null @@ -1,983 +0,0 @@ - - - - - src/collision/Ray.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/collision/Ray.js

    - -
    -
    -module.exports = Ray;
    -
    -var Vec3 = require('../math/Vec3');
    -var Quaternion = require('../math/Quaternion');
    -var Transform = require('../math/Transform');
    -var ConvexPolyhedron = require('../shapes/ConvexPolyhedron');
    -var Box = require('../shapes/Box');
    -var RaycastResult = require('../collision/RaycastResult');
    -var Shape = require('../shapes/Shape');
    -var AABB = require('../collision/AABB');
    -
    -/**
    - * A line in 3D space that intersects bodies and return points.
    - * @class Ray
    - * @constructor
    - * @param {Vec3} from
    - * @param {Vec3} to
    - */
    -function Ray(from, to){
    -    /**
    -     * @property {Vec3} from
    -     */
    -    this.from = from ? from.clone() : new Vec3();
    -
    -    /**
    -     * @property {Vec3} to
    -     */
    -    this.to = to ? to.clone() : new Vec3();
    -
    -    /**
    -     * @private
    -     * @property {Vec3} _direction
    -     */
    -    this._direction = new Vec3();
    -
    -    /**
    -     * The precision of the ray. Used when checking parallelity etc.
    -     * @property {Number} precision
    -     */
    -    this.precision = 0.0001;
    -
    -    /**
    -     * Set to true if you want the Ray to take .collisionResponse flags into account on bodies and shapes.
    -     * @property {Boolean} checkCollisionResponse
    -     */
    -    this.checkCollisionResponse = true;
    -
    -    /**
    -     * If set to true, the ray skips any hits with normal.dot(rayDirection) < 0.
    -     * @property {Boolean} skipBackfaces
    -     */
    -    this.skipBackfaces = false;
    -
    -    /**
    -     * @property {number} collisionFilterMask
    -     * @default -1
    -     */
    -    this.collisionFilterMask = -1;
    -
    -    /**
    -     * @property {number} collisionFilterGroup
    -     * @default -1
    -     */
    -    this.collisionFilterGroup = -1;
    -
    -    /**
    -     * The intersection mode. Should be Ray.ANY, Ray.ALL or Ray.CLOSEST.
    -     * @property {number} mode
    -     */
    -    this.mode = Ray.ANY;
    -
    -    /**
    -     * Current result object.
    -     * @property {RaycastResult} result
    -     */
    -    this.result = new RaycastResult();
    -
    -    /**
    -     * Will be set to true during intersectWorld() if the ray hit anything.
    -     * @property {Boolean} hasHit
    -     */
    -    this.hasHit = false;
    -
    -    /**
    -     * Current, user-provided result callback. Will be used if mode is Ray.ALL.
    -     * @property {Function} callback
    -     */
    -    this.callback = function(result){};
    -}
    -Ray.prototype.constructor = Ray;
    -
    -Ray.CLOSEST = 1;
    -Ray.ANY = 2;
    -Ray.ALL = 4;
    -
    -var tmpAABB = new AABB();
    -var tmpArray = [];
    -
    -/**
    - * Do itersection against all bodies in the given World.
    - * @method intersectWorld
    - * @param  {World} world
    - * @param  {object} options
    - * @return {Boolean} True if the ray hit anything, otherwise false.
    - */
    -Ray.prototype.intersectWorld = function (world, options) {
    -    this.mode = options.mode || Ray.ANY;
    -    this.result = options.result || new RaycastResult();
    -    this.skipBackfaces = !!options.skipBackfaces;
    -    this.collisionFilterMask = typeof(options.collisionFilterMask) !== 'undefined' ? options.collisionFilterMask : -1;
    -    this.collisionFilterGroup = typeof(options.collisionFilterGroup) !== 'undefined' ? options.collisionFilterGroup : -1;
    -    if(options.from){
    -        this.from.copy(options.from);
    -    }
    -    if(options.to){
    -        this.to.copy(options.to);
    -    }
    -    this.callback = options.callback || function(){};
    -    this.hasHit = false;
    -
    -    this.result.reset();
    -    this._updateDirection();
    -
    -    this.getAABB(tmpAABB);
    -    tmpArray.length = 0;
    -    world.broadphase.aabbQuery(world, tmpAABB, tmpArray);
    -    this.intersectBodies(tmpArray);
    -
    -    return this.hasHit;
    -};
    -
    -var v1 = new Vec3(),
    -    v2 = new Vec3();
    -
    -/*
    - * As per "Barycentric Technique" as named here http://www.blackpawn.com/texts/pointinpoly/default.html But without the division
    - */
    -Ray.pointInTriangle = pointInTriangle;
    -function pointInTriangle(p, a, b, c) {
    -    c.vsub(a,v0);
    -    b.vsub(a,v1);
    -    p.vsub(a,v2);
    -
    -    var dot00 = v0.dot( v0 );
    -    var dot01 = v0.dot( v1 );
    -    var dot02 = v0.dot( v2 );
    -    var dot11 = v1.dot( v1 );
    -    var dot12 = v1.dot( v2 );
    -
    -    var u,v;
    -
    -    return  ( (u = dot11 * dot02 - dot01 * dot12) >= 0 ) &&
    -            ( (v = dot00 * dot12 - dot01 * dot02) >= 0 ) &&
    -            ( u + v < ( dot00 * dot11 - dot01 * dot01 ) );
    -}
    -
    -/**
    - * Shoot a ray at a body, get back information about the hit.
    - * @method intersectBody
    - * @private
    - * @param {Body} body
    - * @param {RaycastResult} [result] Deprecated - set the result property of the Ray instead.
    - */
    -var intersectBody_xi = new Vec3();
    -var intersectBody_qi = new Quaternion();
    -Ray.prototype.intersectBody = function (body, result) {
    -    if(result){
    -        this.result = result;
    -        this._updateDirection();
    -    }
    -    var checkCollisionResponse = this.checkCollisionResponse;
    -
    -    if(checkCollisionResponse && !body.collisionResponse){
    -        return;
    -    }
    -
    -    if((this.collisionFilterGroup & body.collisionFilterMask)===0 || (body.collisionFilterGroup & this.collisionFilterMask)===0){
    -        return;
    -    }
    -
    -    var xi = intersectBody_xi;
    -    var qi = intersectBody_qi;
    -
    -    for (var i = 0, N = body.shapes.length; i < N; i++) {
    -        var shape = body.shapes[i];
    -
    -        if(checkCollisionResponse && !shape.collisionResponse){
    -            continue; // Skip
    -        }
    -
    -        body.quaternion.mult(body.shapeOrientations[i], qi);
    -        body.quaternion.vmult(body.shapeOffsets[i], xi);
    -        xi.vadd(body.position, xi);
    -
    -        this.intersectShape(
    -            shape,
    -            qi,
    -            xi,
    -            body
    -        );
    -
    -        if(this.result._shouldStop){
    -            break;
    -        }
    -    }
    -};
    -
    -/**
    - * @method intersectBodies
    - * @param {Array} bodies An array of Body objects.
    - * @param {RaycastResult} [result] Deprecated
    - */
    -Ray.prototype.intersectBodies = function (bodies, result) {
    -    if(result){
    -        this.result = result;
    -        this._updateDirection();
    -    }
    -
    -    for ( var i = 0, l = bodies.length; !this.result._shouldStop && i < l; i ++ ) {
    -        this.intersectBody(bodies[i]);
    -    }
    -};
    -
    -/**
    - * Updates the _direction vector.
    - * @private
    - * @method _updateDirection
    - */
    -Ray.prototype._updateDirection = function(){
    -    this.to.vsub(this.from, this._direction);
    -    this._direction.normalize();
    -};
    -
    -/**
    - * @method intersectShape
    - * @private
    - * @param {Shape} shape
    - * @param {Quaternion} quat
    - * @param {Vec3} position
    - * @param {Body} body
    - */
    -Ray.prototype.intersectShape = function(shape, quat, position, body){
    -    var from = this.from;
    -
    -
    -    // Checking boundingSphere
    -    var distance = distanceFromIntersection(from, this._direction, position);
    -    if ( distance > shape.boundingSphereRadius ) {
    -        return;
    -    }
    -
    -    var intersectMethod = this[shape.type];
    -    if(intersectMethod){
    -        intersectMethod.call(this, shape, quat, position, body);
    -    }
    -};
    -
    -var vector = new Vec3();
    -var normal = new Vec3();
    -var intersectPoint = new Vec3();
    -
    -var a = new Vec3();
    -var b = new Vec3();
    -var c = new Vec3();
    -var d = new Vec3();
    -
    -var tmpRaycastResult = new RaycastResult();
    -
    -/**
    - * @method intersectBox
    - * @private
    - * @param  {Shape} shape
    - * @param  {Quaternion} quat
    - * @param  {Vec3} position
    - * @param  {Body} body
    - */
    -Ray.prototype.intersectBox = function(shape, quat, position, body){
    -    return this.intersectConvex(shape.convexPolyhedronRepresentation, quat, position, body);
    -};
    -Ray.prototype[Shape.types.BOX] = Ray.prototype.intersectBox;
    -
    -/**
    - * @method intersectPlane
    - * @private
    - * @param  {Shape} shape
    - * @param  {Quaternion} quat
    - * @param  {Vec3} position
    - * @param  {Body} body
    - */
    -Ray.prototype.intersectPlane = function(shape, quat, position, body){
    -    var from = this.from;
    -    var to = this.to;
    -    var direction = this._direction;
    -
    -    // Get plane normal
    -    var worldNormal = new Vec3(0, 0, 1);
    -    quat.vmult(worldNormal, worldNormal);
    -
    -    var len = new Vec3();
    -    from.vsub(position, len);
    -    var planeToFrom = len.dot(worldNormal);
    -    to.vsub(position, len);
    -    var planeToTo = len.dot(worldNormal);
    -
    -    if(planeToFrom * planeToTo > 0){
    -        // "from" and "to" are on the same side of the plane... bail out
    -        return;
    -    }
    -
    -    if(from.distanceTo(to) < planeToFrom){
    -        return;
    -    }
    -
    -    var n_dot_dir = worldNormal.dot(direction);
    -
    -    if (Math.abs(n_dot_dir) < this.precision) {
    -        // No intersection
    -        return;
    -    }
    -
    -    var planePointToFrom = new Vec3();
    -    var dir_scaled_with_t = new Vec3();
    -    var hitPointWorld = new Vec3();
    -
    -    from.vsub(position, planePointToFrom);
    -    var t = -worldNormal.dot(planePointToFrom) / n_dot_dir;
    -    direction.scale(t, dir_scaled_with_t);
    -    from.vadd(dir_scaled_with_t, hitPointWorld);
    -
    -    this.reportIntersection(worldNormal, hitPointWorld, shape, body, -1);
    -};
    -Ray.prototype[Shape.types.PLANE] = Ray.prototype.intersectPlane;
    -
    -/**
    - * Get the world AABB of the ray.
    - * @method getAABB
    - * @param  {AABB} aabb
    - */
    -Ray.prototype.getAABB = function(result){
    -    var to = this.to;
    -    var from = this.from;
    -    result.lowerBound.x = Math.min(to.x, from.x);
    -    result.lowerBound.y = Math.min(to.y, from.y);
    -    result.lowerBound.z = Math.min(to.z, from.z);
    -    result.upperBound.x = Math.max(to.x, from.x);
    -    result.upperBound.y = Math.max(to.y, from.y);
    -    result.upperBound.z = Math.max(to.z, from.z);
    -};
    -
    -var intersectConvexOptions = {
    -    faceList: [0]
    -};
    -
    -/**
    - * @method intersectHeightfield
    - * @private
    - * @param  {Shape} shape
    - * @param  {Quaternion} quat
    - * @param  {Vec3} position
    - * @param  {Body} body
    - */
    -Ray.prototype.intersectHeightfield = function(shape, quat, position, body){
    -    var data = shape.data,
    -        w = shape.elementSize,
    -        worldPillarOffset = new Vec3();
    -
    -    // Convert the ray to local heightfield coordinates
    -    var localRay = new Ray(this.from, this.to);
    -    Transform.pointToLocalFrame(position, quat, localRay.from, localRay.from);
    -    Transform.pointToLocalFrame(position, quat, localRay.to, localRay.to);
    -
    -    // Get the index of the data points to test against
    -    var index = [];
    -    var iMinX = null;
    -    var iMinY = null;
    -    var iMaxX = null;
    -    var iMaxY = null;
    -
    -    var inside = shape.getIndexOfPosition(localRay.from.x, localRay.from.y, index, false);
    -    if(inside){
    -        iMinX = index[0];
    -        iMinY = index[1];
    -        iMaxX = index[0];
    -        iMaxY = index[1];
    -    }
    -    inside = shape.getIndexOfPosition(localRay.to.x, localRay.to.y, index, false);
    -    if(inside){
    -        if (iMinX === null || index[0] < iMinX) { iMinX = index[0]; }
    -        if (iMaxX === null || index[0] > iMaxX) { iMaxX = index[0]; }
    -        if (iMinY === null || index[1] < iMinY) { iMinY = index[1]; }
    -        if (iMaxY === null || index[1] > iMaxY) { iMaxY = index[1]; }
    -    }
    -
    -    if(iMinX === null){
    -        return;
    -    }
    -
    -    var minMax = [];
    -    shape.getRectMinMax(iMinX, iMinY, iMaxX, iMaxY, minMax);
    -    var min = minMax[0];
    -    var max = minMax[1];
    -
    -    // // Bail out if the ray can't touch the bounding box
    -    // // TODO
    -    // var aabb = new AABB();
    -    // this.getAABB(aabb);
    -    // if(aabb.intersects()){
    -    //     return;
    -    // }
    -
    -    for(var i = iMinX; i <= iMaxX; i++){
    -        for(var j = iMinY; j <= iMaxY; j++){
    -
    -            if(this.result._shouldStop){
    -                return;
    -            }
    -
    -            // Lower triangle
    -            shape.getConvexTrianglePillar(i, j, false);
    -            Transform.pointToWorldFrame(position, quat, shape.pillarOffset, worldPillarOffset);
    -            this.intersectConvex(shape.pillarConvex, quat, worldPillarOffset, body, intersectConvexOptions);
    -
    -            if(this.result._shouldStop){
    -                return;
    -            }
    -
    -            // Upper triangle
    -            shape.getConvexTrianglePillar(i, j, true);
    -            Transform.pointToWorldFrame(position, quat, shape.pillarOffset, worldPillarOffset);
    -            this.intersectConvex(shape.pillarConvex, quat, worldPillarOffset, body, intersectConvexOptions);
    -        }
    -    }
    -};
    -Ray.prototype[Shape.types.HEIGHTFIELD] = Ray.prototype.intersectHeightfield;
    -
    -var Ray_intersectSphere_intersectionPoint = new Vec3();
    -var Ray_intersectSphere_normal = new Vec3();
    -
    -/**
    - * @method intersectSphere
    - * @private
    - * @param  {Shape} shape
    - * @param  {Quaternion} quat
    - * @param  {Vec3} position
    - * @param  {Body} body
    - */
    -Ray.prototype.intersectSphere = function(shape, quat, position, body){
    -    var from = this.from,
    -        to = this.to,
    -        r = shape.radius;
    -
    -    var a = Math.pow(to.x - from.x, 2) + Math.pow(to.y - from.y, 2) + Math.pow(to.z - from.z, 2);
    -    var b = 2 * ((to.x - from.x) * (from.x - position.x) + (to.y - from.y) * (from.y - position.y) + (to.z - from.z) * (from.z - position.z));
    -    var c = Math.pow(from.x - position.x, 2) + Math.pow(from.y - position.y, 2) + Math.pow(from.z - position.z, 2) - Math.pow(r, 2);
    -
    -    var delta = Math.pow(b, 2) - 4 * a * c;
    -
    -    var intersectionPoint = Ray_intersectSphere_intersectionPoint;
    -    var normal = Ray_intersectSphere_normal;
    -
    -    if(delta < 0){
    -        // No intersection
    -        return;
    -
    -    } else if(delta === 0){
    -        // single intersection point
    -        from.lerp(to, delta, intersectionPoint);
    -
    -        intersectionPoint.vsub(position, normal);
    -        normal.normalize();
    -
    -        this.reportIntersection(normal, intersectionPoint, shape, body, -1);
    -
    -    } else {
    -        var d1 = (- b - Math.sqrt(delta)) / (2 * a);
    -        var d2 = (- b + Math.sqrt(delta)) / (2 * a);
    -
    -        if(d1 >= 0 && d1 <= 1){
    -            from.lerp(to, d1, intersectionPoint);
    -            intersectionPoint.vsub(position, normal);
    -            normal.normalize();
    -            this.reportIntersection(normal, intersectionPoint, shape, body, -1);
    -        }
    -
    -        if(this.result._shouldStop){
    -            return;
    -        }
    -
    -        if(d2 >= 0 && d2 <= 1){
    -            from.lerp(to, d2, intersectionPoint);
    -            intersectionPoint.vsub(position, normal);
    -            normal.normalize();
    -            this.reportIntersection(normal, intersectionPoint, shape, body, -1);
    -        }
    -    }
    -};
    -Ray.prototype[Shape.types.SPHERE] = Ray.prototype.intersectSphere;
    -
    -
    -var intersectConvex_normal = new Vec3();
    -var intersectConvex_minDistNormal = new Vec3();
    -var intersectConvex_minDistIntersect = new Vec3();
    -var intersectConvex_vector = new Vec3();
    -
    -/**
    - * @method intersectConvex
    - * @private
    - * @param  {Shape} shape
    - * @param  {Quaternion} quat
    - * @param  {Vec3} position
    - * @param  {Body} body
    - * @param {object} [options]
    - * @param {array} [options.faceList]
    - */
    -Ray.prototype.intersectConvex = function intersectConvex(
    -    shape,
    -    quat,
    -    position,
    -    body,
    -    options
    -){
    -    var minDistNormal = intersectConvex_minDistNormal;
    -    var normal = intersectConvex_normal;
    -    var vector = intersectConvex_vector;
    -    var minDistIntersect = intersectConvex_minDistIntersect;
    -    var faceList = (options && options.faceList) || null;
    -
    -    // Checking faces
    -    var faces = shape.faces,
    -        vertices = shape.vertices,
    -        normals = shape.faceNormals;
    -    var direction = this._direction;
    -
    -    var from = this.from;
    -    var to = this.to;
    -    var fromToDistance = from.distanceTo(to);
    -
    -    var minDist = -1;
    -    var Nfaces = faceList ? faceList.length : faces.length;
    -    var result = this.result;
    -
    -    for (var j = 0; !result._shouldStop && j < Nfaces; j++) {
    -        var fi = faceList ? faceList[j] : j;
    -
    -        var face = faces[fi];
    -        var faceNormal = normals[fi];
    -        var q = quat;
    -        var x = position;
    -
    -        // determine if ray intersects the plane of the face
    -        // note: this works regardless of the direction of the face normal
    -
    -        // Get plane point in world coordinates...
    -        vector.copy(vertices[face[0]]);
    -        q.vmult(vector,vector);
    -        vector.vadd(x,vector);
    -
    -        // ...but make it relative to the ray from. We'll fix this later.
    -        vector.vsub(from,vector);
    -
    -        // Get plane normal
    -        q.vmult(faceNormal,normal);
    -
    -        // If this dot product is negative, we have something interesting
    -        var dot = direction.dot(normal);
    -
    -        // Bail out if ray and plane are parallel
    -        if ( Math.abs( dot ) < this.precision ){
    -            continue;
    -        }
    -
    -        // calc distance to plane
    -        var scalar = normal.dot(vector) / dot;
    -
    -        // if negative distance, then plane is behind ray
    -        if (scalar < 0){
    -            continue;
    -        }
    -
    -        // if (dot < 0) {
    -
    -        // Intersection point is from + direction * scalar
    -        direction.mult(scalar,intersectPoint);
    -        intersectPoint.vadd(from,intersectPoint);
    -
    -        // a is the point we compare points b and c with.
    -        a.copy(vertices[face[0]]);
    -        q.vmult(a,a);
    -        x.vadd(a,a);
    -
    -        for(var i = 1; !result._shouldStop && i < face.length - 1; i++){
    -            // Transform 3 vertices to world coords
    -            b.copy(vertices[face[i]]);
    -            c.copy(vertices[face[i+1]]);
    -            q.vmult(b,b);
    -            q.vmult(c,c);
    -            x.vadd(b,b);
    -            x.vadd(c,c);
    -
    -            var distance = intersectPoint.distanceTo(from);
    -
    -            if(!(pointInTriangle(intersectPoint, a, b, c) || pointInTriangle(intersectPoint, b, a, c)) || distance > fromToDistance){
    -                continue;
    -            }
    -
    -            this.reportIntersection(normal, intersectPoint, shape, body, fi);
    -        }
    -        // }
    -    }
    -};
    -Ray.prototype[Shape.types.CONVEXPOLYHEDRON] = Ray.prototype.intersectConvex;
    -
    -var intersectTrimesh_normal = new Vec3();
    -var intersectTrimesh_localDirection = new Vec3();
    -var intersectTrimesh_localFrom = new Vec3();
    -var intersectTrimesh_localTo = new Vec3();
    -var intersectTrimesh_worldNormal = new Vec3();
    -var intersectTrimesh_worldIntersectPoint = new Vec3();
    -var intersectTrimesh_localAABB = new AABB();
    -var intersectTrimesh_triangles = [];
    -var intersectTrimesh_treeTransform = new Transform();
    -
    -/**
    - * @method intersectTrimesh
    - * @private
    - * @param  {Shape} shape
    - * @param  {Quaternion} quat
    - * @param  {Vec3} position
    - * @param  {Body} body
    - * @param {object} [options]
    - * @todo Optimize by transforming the world to local space first.
    - * @todo Use Octree lookup
    - */
    -Ray.prototype.intersectTrimesh = function intersectTrimesh(
    -    mesh,
    -    quat,
    -    position,
    -    body,
    -    options
    -){
    -    var normal = intersectTrimesh_normal;
    -    var triangles = intersectTrimesh_triangles;
    -    var treeTransform = intersectTrimesh_treeTransform;
    -    var minDistNormal = intersectConvex_minDistNormal;
    -    var vector = intersectConvex_vector;
    -    var minDistIntersect = intersectConvex_minDistIntersect;
    -    var localAABB = intersectTrimesh_localAABB;
    -    var localDirection = intersectTrimesh_localDirection;
    -    var localFrom = intersectTrimesh_localFrom;
    -    var localTo = intersectTrimesh_localTo;
    -    var worldIntersectPoint = intersectTrimesh_worldIntersectPoint;
    -    var worldNormal = intersectTrimesh_worldNormal;
    -    var faceList = (options && options.faceList) || null;
    -
    -    // Checking faces
    -    var indices = mesh.indices,
    -        vertices = mesh.vertices,
    -        normals = mesh.faceNormals;
    -
    -    var from = this.from;
    -    var to = this.to;
    -    var direction = this._direction;
    -
    -    var minDist = -1;
    -    treeTransform.position.copy(position);
    -    treeTransform.quaternion.copy(quat);
    -
    -    // Transform ray to local space!
    -    Transform.vectorToLocalFrame(position, quat, direction, localDirection);
    -    //body.vectorToLocalFrame(direction, localDirection);
    -    Transform.pointToLocalFrame(position, quat, from, localFrom);
    -    //body.pointToLocalFrame(from, localFrom);
    -    Transform.pointToLocalFrame(position, quat, to, localTo);
    -    //body.pointToLocalFrame(to, localTo);
    -    var fromToDistanceSquared = localFrom.distanceSquared(localTo);
    -
    -    mesh.tree.rayQuery(this, treeTransform, triangles);
    -
    -    for (var i = 0, N = triangles.length; !this.result._shouldStop && i !== N; i++) {
    -        var trianglesIndex = triangles[i];
    -
    -        mesh.getNormal(trianglesIndex, normal);
    -
    -        // determine if ray intersects the plane of the face
    -        // note: this works regardless of the direction of the face normal
    -
    -        // Get plane point in world coordinates...
    -        mesh.getVertex(indices[trianglesIndex * 3], a);
    -
    -        // ...but make it relative to the ray from. We'll fix this later.
    -        a.vsub(localFrom,vector);
    -
    -        // Get plane normal
    -        // quat.vmult(normal, normal);
    -
    -        // If this dot product is negative, we have something interesting
    -        var dot = localDirection.dot(normal);
    -
    -        // Bail out if ray and plane are parallel
    -        // if (Math.abs( dot ) < this.precision){
    -        //     continue;
    -        // }
    -
    -        // calc distance to plane
    -        var scalar = normal.dot(vector) / dot;
    -
    -        // if negative distance, then plane is behind ray
    -        if (scalar < 0){
    -            continue;
    -        }
    -
    -        // Intersection point is from + direction * scalar
    -        localDirection.scale(scalar,intersectPoint);
    -        intersectPoint.vadd(localFrom,intersectPoint);
    -
    -        // Get triangle vertices
    -        mesh.getVertex(indices[trianglesIndex * 3 + 1], b);
    -        mesh.getVertex(indices[trianglesIndex * 3 + 2], c);
    -
    -        var squaredDistance = intersectPoint.distanceSquared(localFrom);
    -
    -        if(!(pointInTriangle(intersectPoint, b, a, c) || pointInTriangle(intersectPoint, a, b, c)) || squaredDistance > fromToDistanceSquared){
    -            continue;
    -        }
    -
    -        // transform intersectpoint and normal to world
    -        Transform.vectorToWorldFrame(quat, normal, worldNormal);
    -        //body.vectorToWorldFrame(normal, worldNormal);
    -        Transform.pointToWorldFrame(position, quat, intersectPoint, worldIntersectPoint);
    -        //body.pointToWorldFrame(intersectPoint, worldIntersectPoint);
    -        this.reportIntersection(worldNormal, worldIntersectPoint, mesh, body, trianglesIndex);
    -    }
    -    triangles.length = 0;
    -};
    -Ray.prototype[Shape.types.TRIMESH] = Ray.prototype.intersectTrimesh;
    -
    -
    -/**
    - * @method reportIntersection
    - * @private
    - * @param  {Vec3} normal
    - * @param  {Vec3} hitPointWorld
    - * @param  {Shape} shape
    - * @param  {Body} body
    - * @return {boolean} True if the intersections should continue
    - */
    -Ray.prototype.reportIntersection = function(normal, hitPointWorld, shape, body, hitFaceIndex){
    -    var from = this.from;
    -    var to = this.to;
    -    var distance = from.distanceTo(hitPointWorld);
    -    var result = this.result;
    -
    -    // Skip back faces?
    -    if(this.skipBackfaces && normal.dot(this._direction) > 0){
    -        return;
    -    }
    -
    -    result.hitFaceIndex = typeof(hitFaceIndex) !== 'undefined' ? hitFaceIndex : -1;
    -
    -    switch(this.mode){
    -    case Ray.ALL:
    -        this.hasHit = true;
    -        result.set(
    -            from,
    -            to,
    -            normal,
    -            hitPointWorld,
    -            shape,
    -            body,
    -            distance
    -        );
    -        result.hasHit = true;
    -        this.callback(result);
    -        break;
    -
    -    case Ray.CLOSEST:
    -
    -        // Store if closer than current closest
    -        if(distance < result.distance || !result.hasHit){
    -            this.hasHit = true;
    -            result.hasHit = true;
    -            result.set(
    -                from,
    -                to,
    -                normal,
    -                hitPointWorld,
    -                shape,
    -                body,
    -                distance
    -            );
    -        }
    -        break;
    -
    -    case Ray.ANY:
    -
    -        // Report and stop.
    -        this.hasHit = true;
    -        result.hasHit = true;
    -        result.set(
    -            from,
    -            to,
    -            normal,
    -            hitPointWorld,
    -            shape,
    -            body,
    -            distance
    -        );
    -        result._shouldStop = true;
    -        break;
    -    }
    -};
    -
    -var v0 = new Vec3(),
    -    intersect = new Vec3();
    -function distanceFromIntersection(from, direction, position) {
    -
    -    // v0 is vector from from to position
    -    position.vsub(from,v0);
    -    var dot = v0.dot(direction);
    -
    -    // intersect = direction*dot + from
    -    direction.mult(dot,intersect);
    -    intersect.vadd(from,intersect);
    -
    -    var distance = position.distanceTo(intersect);
    -
    -    return distance;
    -}
    -
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_collision_RaycastResult.js.html b/docs/files/src_collision_RaycastResult.js.html deleted file mode 100644 index df09646ee..000000000 --- a/docs/files/src_collision_RaycastResult.js.html +++ /dev/null @@ -1,275 +0,0 @@ - - - - - src/collision/RaycastResult.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/collision/RaycastResult.js

    - -
    -
    -var Vec3 = require('../math/Vec3');
    -
    -module.exports = RaycastResult;
    -
    -/**
    - * Storage for Ray casting data.
    - * @class RaycastResult
    - * @constructor
    - */
    -function RaycastResult(){
    -
    -	/**
    -	 * @property {Vec3} rayFromWorld
    -	 */
    -	this.rayFromWorld = new Vec3();
    -
    -	/**
    -	 * @property {Vec3} rayToWorld
    -	 */
    -	this.rayToWorld = new Vec3();
    -
    -	/**
    -	 * @property {Vec3} hitNormalWorld
    -	 */
    -	this.hitNormalWorld = new Vec3();
    -
    -	/**
    -	 * @property {Vec3} hitPointWorld
    -	 */
    -	this.hitPointWorld = new Vec3();
    -
    -	/**
    -	 * @property {boolean} hasHit
    -	 */
    -	this.hasHit = false;
    -
    -	/**
    -	 * The hit shape, or null.
    -	 * @property {Shape} shape
    -	 */
    -	this.shape = null;
    -
    -	/**
    -	 * The hit body, or null.
    -	 * @property {Body} body
    -	 */
    -	this.body = null;
    -
    -	/**
    -	 * The index of the hit triangle, if the hit shape was a trimesh.
    -	 * @property {number} hitFaceIndex
    -	 * @default -1
    -	 */
    -	this.hitFaceIndex = -1;
    -
    -	/**
    -	 * Distance to the hit. Will be set to -1 if there was no hit.
    -	 * @property {number} distance
    -	 * @default -1
    -	 */
    -	this.distance = -1;
    -
    -	/**
    -	 * If the ray should stop traversing the bodies.
    -	 * @private
    -	 * @property {Boolean} _shouldStop
    -	 * @default false
    -	 */
    -	this._shouldStop = false;
    -}
    -
    -/**
    - * Reset all result data.
    - * @method reset
    - */
    -RaycastResult.prototype.reset = function () {
    -	this.rayFromWorld.setZero();
    -	this.rayToWorld.setZero();
    -	this.hitNormalWorld.setZero();
    -	this.hitPointWorld.setZero();
    -	this.hasHit = false;
    -	this.shape = null;
    -	this.body = null;
    -	this.hitFaceIndex = -1;
    -	this.distance = -1;
    -	this._shouldStop = false;
    -};
    -
    -/**
    - * @method abort
    - */
    -RaycastResult.prototype.abort = function(){
    -	this._shouldStop = true;
    -};
    -
    -/**
    - * @method set
    - * @param {Vec3} rayFromWorld
    - * @param {Vec3} rayToWorld
    - * @param {Vec3} hitNormalWorld
    - * @param {Vec3} hitPointWorld
    - * @param {Shape} shape
    - * @param {Body} body
    - * @param {number} distance
    - */
    -RaycastResult.prototype.set = function(
    -	rayFromWorld,
    -	rayToWorld,
    -	hitNormalWorld,
    -	hitPointWorld,
    -	shape,
    -	body,
    -	distance
    -){
    -	this.rayFromWorld.copy(rayFromWorld);
    -	this.rayToWorld.copy(rayToWorld);
    -	this.hitNormalWorld.copy(hitNormalWorld);
    -	this.hitPointWorld.copy(hitPointWorld);
    -	this.shape = shape;
    -	this.body = body;
    -	this.distance = distance;
    -};
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_collision_SAPBroadphase.js.html b/docs/files/src_collision_SAPBroadphase.js.html deleted file mode 100644 index 4179038bb..000000000 --- a/docs/files/src_collision_SAPBroadphase.js.html +++ /dev/null @@ -1,476 +0,0 @@ - - - - - src/collision/SAPBroadphase.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/collision/SAPBroadphase.js

    - -
    -
    -var Shape = require('../shapes/Shape');
    -var Broadphase = require('../collision/Broadphase');
    -
    -module.exports = SAPBroadphase;
    -
    -/**
    - * Sweep and prune broadphase along one axis.
    - *
    - * @class SAPBroadphase
    - * @constructor
    - * @param {World} [world]
    - * @extends Broadphase
    - */
    -function SAPBroadphase(world){
    -    Broadphase.apply(this);
    -
    -    /**
    -     * List of bodies currently in the broadphase.
    -     * @property axisList
    -     * @type {Array}
    -     */
    -    this.axisList = [];
    -
    -    /**
    -     * The world to search in.
    -     * @property world
    -     * @type {World}
    -     */
    -    this.world = null;
    -
    -    /**
    -     * Axis to sort the bodies along. Set to 0 for x axis, and 1 for y axis. For best performance, choose an axis that the bodies are spread out more on.
    -     * @property axisIndex
    -     * @type {Number}
    -     */
    -    this.axisIndex = 0;
    -
    -    var axisList = this.axisList;
    -
    -    this._addBodyHandler = function(e){
    -        axisList.push(e.body);
    -    };
    -
    -    this._removeBodyHandler = function(e){
    -        var idx = axisList.indexOf(e.body);
    -        if(idx !== -1){
    -            axisList.splice(idx,1);
    -        }
    -    };
    -
    -    if(world){
    -        this.setWorld(world);
    -    }
    -}
    -SAPBroadphase.prototype = new Broadphase();
    -
    -/**
    - * Change the world
    - * @method setWorld
    - * @param  {World} world
    - */
    -SAPBroadphase.prototype.setWorld = function(world){
    -    // Clear the old axis array
    -    this.axisList.length = 0;
    -
    -    // Add all bodies from the new world
    -    for(var i=0; i<world.bodies.length; i++){
    -        this.axisList.push(world.bodies[i]);
    -    }
    -
    -    // Remove old handlers, if any
    -    world.removeEventListener("addBody", this._addBodyHandler);
    -    world.removeEventListener("removeBody", this._removeBodyHandler);
    -
    -    // Add handlers to update the list of bodies.
    -    world.addEventListener("addBody", this._addBodyHandler);
    -    world.addEventListener("removeBody", this._removeBodyHandler);
    -
    -    this.world = world;
    -    this.dirty = true;
    -};
    -
    -/**
    - * @static
    - * @method insertionSortX
    - * @param  {Array} a
    - * @return {Array}
    - */
    -SAPBroadphase.insertionSortX = function(a) {
    -    for(var i=1,l=a.length;i<l;i++) {
    -        var v = a[i];
    -        for(var j=i - 1;j>=0;j--) {
    -            if(a[j].aabb.lowerBound.x <= v.aabb.lowerBound.x){
    -                break;
    -            }
    -            a[j+1] = a[j];
    -        }
    -        a[j+1] = v;
    -    }
    -    return a;
    -};
    -
    -/**
    - * @static
    - * @method insertionSortY
    - * @param  {Array} a
    - * @return {Array}
    - */
    -SAPBroadphase.insertionSortY = function(a) {
    -    for(var i=1,l=a.length;i<l;i++) {
    -        var v = a[i];
    -        for(var j=i - 1;j>=0;j--) {
    -            if(a[j].aabb.lowerBound.y <= v.aabb.lowerBound.y){
    -                break;
    -            }
    -            a[j+1] = a[j];
    -        }
    -        a[j+1] = v;
    -    }
    -    return a;
    -};
    -
    -/**
    - * @static
    - * @method insertionSortZ
    - * @param  {Array} a
    - * @return {Array}
    - */
    -SAPBroadphase.insertionSortZ = function(a) {
    -    for(var i=1,l=a.length;i<l;i++) {
    -        var v = a[i];
    -        for(var j=i - 1;j>=0;j--) {
    -            if(a[j].aabb.lowerBound.z <= v.aabb.lowerBound.z){
    -                break;
    -            }
    -            a[j+1] = a[j];
    -        }
    -        a[j+1] = v;
    -    }
    -    return a;
    -};
    -
    -/**
    - * Collect all collision pairs
    - * @method collisionPairs
    - * @param  {World} world
    - * @param  {Array} p1
    - * @param  {Array} p2
    - */
    -SAPBroadphase.prototype.collisionPairs = function(world,p1,p2){
    -    var bodies = this.axisList,
    -        N = bodies.length,
    -        axisIndex = this.axisIndex,
    -        i, j;
    -
    -    if(this.dirty){
    -        this.sortList();
    -        this.dirty = false;
    -    }
    -
    -    // Look through the list
    -    for(i=0; i !== N; i++){
    -        var bi = bodies[i];
    -
    -        for(j=i+1; j < N; j++){
    -            var bj = bodies[j];
    -
    -            if(!this.needBroadphaseCollision(bi,bj)){
    -                continue;
    -            }
    -
    -            if(!SAPBroadphase.checkBounds(bi,bj,axisIndex)){
    -                break;
    -            }
    -
    -            this.intersectionTest(bi,bj,p1,p2);
    -        }
    -    }
    -};
    -
    -SAPBroadphase.prototype.sortList = function(){
    -    var axisList = this.axisList;
    -    var axisIndex = this.axisIndex;
    -    var N = axisList.length;
    -
    -    // Update AABBs
    -    for(var i = 0; i!==N; i++){
    -        var bi = axisList[i];
    -        if(bi.aabbNeedsUpdate){
    -            bi.computeAABB();
    -        }
    -    }
    -
    -    // Sort the list
    -    if(axisIndex === 0){
    -        SAPBroadphase.insertionSortX(axisList);
    -    } else if(axisIndex === 1){
    -        SAPBroadphase.insertionSortY(axisList);
    -    } else if(axisIndex === 2){
    -        SAPBroadphase.insertionSortZ(axisList);
    -    }
    -};
    -
    -/**
    - * Check if the bounds of two bodies overlap, along the given SAP axis.
    - * @static
    - * @method checkBounds
    - * @param  {Body} bi
    - * @param  {Body} bj
    - * @param  {Number} axisIndex
    - * @return {Boolean}
    - */
    -SAPBroadphase.checkBounds = function(bi, bj, axisIndex){
    -    var biPos;
    -    var bjPos;
    -
    -    if(axisIndex === 0){
    -        biPos = bi.position.x;
    -        bjPos = bj.position.x;
    -    } else if(axisIndex === 1){
    -        biPos = bi.position.y;
    -        bjPos = bj.position.y;
    -    } else if(axisIndex === 2){
    -        biPos = bi.position.z;
    -        bjPos = bj.position.z;
    -    }
    -
    -    var ri = bi.boundingRadius,
    -        rj = bj.boundingRadius,
    -        boundA1 = biPos - ri,
    -        boundA2 = biPos + ri,
    -        boundB1 = bjPos - rj,
    -        boundB2 = bjPos + rj;
    -
    -    return boundB1 < boundA2;
    -};
    -
    -/**
    - * Computes the variance of the body positions and estimates the best
    - * axis to use. Will automatically set property .axisIndex.
    - * @method autoDetectAxis
    - */
    -SAPBroadphase.prototype.autoDetectAxis = function(){
    -    var sumX=0,
    -        sumX2=0,
    -        sumY=0,
    -        sumY2=0,
    -        sumZ=0,
    -        sumZ2=0,
    -        bodies = this.axisList,
    -        N = bodies.length,
    -        invN=1/N;
    -
    -    for(var i=0; i!==N; i++){
    -        var b = bodies[i];
    -
    -        var centerX = b.position.x;
    -        sumX += centerX;
    -        sumX2 += centerX*centerX;
    -
    -        var centerY = b.position.y;
    -        sumY += centerY;
    -        sumY2 += centerY*centerY;
    -
    -        var centerZ = b.position.z;
    -        sumZ += centerZ;
    -        sumZ2 += centerZ*centerZ;
    -    }
    -
    -    var varianceX = sumX2 - sumX*sumX*invN,
    -        varianceY = sumY2 - sumY*sumY*invN,
    -        varianceZ = sumZ2 - sumZ*sumZ*invN;
    -
    -    if(varianceX > varianceY){
    -        if(varianceX > varianceZ){
    -            this.axisIndex = 0;
    -        } else{
    -            this.axisIndex = 2;
    -        }
    -    } else if(varianceY > varianceZ){
    -        this.axisIndex = 1;
    -    } else{
    -        this.axisIndex = 2;
    -    }
    -};
    -
    -/**
    - * Returns all the bodies within an AABB.
    - * @method aabbQuery
    - * @param  {World} world
    - * @param  {AABB} aabb
    - * @param {array} result An array to store resulting bodies in.
    - * @return {array}
    - */
    -SAPBroadphase.prototype.aabbQuery = function(world, aabb, result){
    -    result = result || [];
    -
    -    if(this.dirty){
    -        this.sortList();
    -        this.dirty = false;
    -    }
    -
    -    var axisIndex = this.axisIndex, axis = 'x';
    -    if(axisIndex === 1){ axis = 'y'; }
    -    if(axisIndex === 2){ axis = 'z'; }
    -
    -    var axisList = this.axisList;
    -    var lower = aabb.lowerBound[axis];
    -    var upper = aabb.upperBound[axis];
    -    for(var i = 0; i < axisList.length; i++){
    -        var b = axisList[i];
    -
    -        if(b.aabbNeedsUpdate){
    -            b.computeAABB();
    -        }
    -
    -        if(b.aabb.overlaps(aabb)){
    -            result.push(b);
    -        }
    -    }
    -
    -    return result;
    -};
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_constraints_ConeTwistConstraint.js.html b/docs/files/src_constraints_ConeTwistConstraint.js.html deleted file mode 100644 index 1f2199226..000000000 --- a/docs/files/src_constraints_ConeTwistConstraint.js.html +++ /dev/null @@ -1,243 +0,0 @@ - - - - - src/constraints/ConeTwistConstraint.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/constraints/ConeTwistConstraint.js

    - -
    -
    -module.exports = ConeTwistConstraint;
    -
    -var Constraint = require('./Constraint');
    -var PointToPointConstraint = require('./PointToPointConstraint');
    -var ConeEquation = require('../equations/ConeEquation');
    -var RotationalEquation = require('../equations/RotationalEquation');
    -var ContactEquation = require('../equations/ContactEquation');
    -var Vec3 = require('../math/Vec3');
    -
    -/**
    - * @class ConeTwistConstraint
    - * @constructor
    - * @author schteppe
    - * @param {Body} bodyA
    - * @param {Body} bodyB
    - * @param {object} [options]
    - * @param {Vec3} [options.pivotA]
    - * @param {Vec3} [options.pivotB]
    - * @param {Vec3} [options.axisA]
    - * @param {Vec3} [options.axisB]
    - * @param {Number} [options.maxForce=1e6]
    - * @extends PointToPointConstraint
    - */
    -function ConeTwistConstraint(bodyA, bodyB, options){
    -    options = options || {};
    -    var maxForce = typeof(options.maxForce) !== 'undefined' ? options.maxForce : 1e6;
    -
    -    // Set pivot point in between
    -    var pivotA = options.pivotA ? options.pivotA.clone() : new Vec3();
    -    var pivotB = options.pivotB ? options.pivotB.clone() : new Vec3();
    -    this.axisA = options.axisA ? options.axisA.clone() : new Vec3();
    -    this.axisB = options.axisB ? options.axisB.clone() : new Vec3();
    -
    -    PointToPointConstraint.call(this, bodyA, pivotA, bodyB, pivotB, maxForce);
    -
    -    this.collideConnected = !!options.collideConnected;
    -
    -    this.angle = typeof(options.angle) !== 'undefined' ? options.angle : 0;
    -
    -    /**
    -     * @property {ConeEquation} coneEquation
    -     */
    -    var c = this.coneEquation = new ConeEquation(bodyA,bodyB,options);
    -
    -    /**
    -     * @property {RotationalEquation} twistEquation
    -     */
    -    var t = this.twistEquation = new RotationalEquation(bodyA,bodyB,options);
    -    this.twistAngle = typeof(options.twistAngle) !== 'undefined' ? options.twistAngle : 0;
    -
    -    // Make the cone equation push the bodies toward the cone axis, not outward
    -    c.maxForce = 0;
    -    c.minForce = -maxForce;
    -
    -    // Make the twist equation add torque toward the initial position
    -    t.maxForce = 0;
    -    t.minForce = -maxForce;
    -
    -    this.equations.push(c, t);
    -}
    -ConeTwistConstraint.prototype = new PointToPointConstraint();
    -ConeTwistConstraint.constructor = ConeTwistConstraint;
    -
    -var ConeTwistConstraint_update_tmpVec1 = new Vec3();
    -var ConeTwistConstraint_update_tmpVec2 = new Vec3();
    -
    -ConeTwistConstraint.prototype.update = function(){
    -    var bodyA = this.bodyA,
    -        bodyB = this.bodyB,
    -        cone = this.coneEquation,
    -        twist = this.twistEquation;
    -
    -    PointToPointConstraint.prototype.update.call(this);
    -
    -    // Update the axes to the cone constraint
    -    bodyA.vectorToWorldFrame(this.axisA, cone.axisA);
    -    bodyB.vectorToWorldFrame(this.axisB, cone.axisB);
    -
    -    // Update the world axes in the twist constraint
    -    this.axisA.tangents(twist.axisA, twist.axisA);
    -    bodyA.vectorToWorldFrame(twist.axisA, twist.axisA);
    -
    -    this.axisB.tangents(twist.axisB, twist.axisB);
    -    bodyB.vectorToWorldFrame(twist.axisB, twist.axisB);
    -
    -    cone.angle = this.angle;
    -    twist.maxAngle = this.twistAngle;
    -};
    -
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_constraints_Constraint.js.html b/docs/files/src_constraints_Constraint.js.html deleted file mode 100644 index 1a1908e3a..000000000 --- a/docs/files/src_constraints_Constraint.js.html +++ /dev/null @@ -1,245 +0,0 @@ - - - - - src/constraints/Constraint.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/constraints/Constraint.js

    - -
    -
    -module.exports = Constraint;
    -
    -var Utils = require('../utils/Utils');
    -
    -/**
    - * Constraint base class
    - * @class Constraint
    - * @author schteppe
    - * @constructor
    - * @param {Body} bodyA
    - * @param {Body} bodyB
    - * @param {object} [options]
    - * @param {boolean} [options.collideConnected=true]
    - * @param {boolean} [options.wakeUpBodies=true]
    - */
    -function Constraint(bodyA, bodyB, options){
    -    options = Utils.defaults(options,{
    -        collideConnected : true,
    -        wakeUpBodies : true,
    -    });
    -
    -    /**
    -     * Equations to be solved in this constraint
    -     * @property equations
    -     * @type {Array}
    -     */
    -    this.equations = [];
    -
    -    /**
    -     * @property {Body} bodyA
    -     */
    -    this.bodyA = bodyA;
    -
    -    /**
    -     * @property {Body} bodyB
    -     */
    -    this.bodyB = bodyB;
    -
    -    /**
    -     * @property {Number} id
    -     */
    -    this.id = Constraint.idCounter++;
    -
    -    /**
    -     * Set to true if you want the bodies to collide when they are connected.
    -     * @property collideConnected
    -     * @type {boolean}
    -     */
    -    this.collideConnected = options.collideConnected;
    -
    -    if(options.wakeUpBodies){
    -        if(bodyA){
    -            bodyA.wakeUp();
    -        }
    -        if(bodyB){
    -            bodyB.wakeUp();
    -        }
    -    }
    -}
    -
    -/**
    - * Update all the equations with data.
    - * @method update
    - */
    -Constraint.prototype.update = function(){
    -    throw new Error("method update() not implmemented in this Constraint subclass!");
    -};
    -
    -/**
    - * Enables all equations in the constraint.
    - * @method enable
    - */
    -Constraint.prototype.enable = function(){
    -    var eqs = this.equations;
    -    for(var i=0; i<eqs.length; i++){
    -        eqs[i].enabled = true;
    -    }
    -};
    -
    -/**
    - * Disables all equations in the constraint.
    - * @method disable
    - */
    -Constraint.prototype.disable = function(){
    -    var eqs = this.equations;
    -    for(var i=0; i<eqs.length; i++){
    -        eqs[i].enabled = false;
    -    }
    -};
    -
    -Constraint.idCounter = 0;
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_constraints_DistanceConstraint.js.html b/docs/files/src_constraints_DistanceConstraint.js.html deleted file mode 100644 index 3e2710a3e..000000000 --- a/docs/files/src_constraints_DistanceConstraint.js.html +++ /dev/null @@ -1,209 +0,0 @@ - - - - - src/constraints/DistanceConstraint.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/constraints/DistanceConstraint.js

    - -
    -
    -module.exports = DistanceConstraint;
    -
    -var Constraint = require('./Constraint');
    -var ContactEquation = require('../equations/ContactEquation');
    -
    -/**
    - * Constrains two bodies to be at a constant distance from each others center of mass.
    - * @class DistanceConstraint
    - * @constructor
    - * @author schteppe
    - * @param {Body} bodyA
    - * @param {Body} bodyB
    - * @param {Number} [distance] The distance to keep. If undefined, it will be set to the current distance between bodyA and bodyB
    - * @param {Number} [maxForce=1e6]
    - * @extends Constraint
    - */
    -function DistanceConstraint(bodyA,bodyB,distance,maxForce){
    -    Constraint.call(this,bodyA,bodyB);
    -
    -    if(typeof(distance)==="undefined") {
    -        distance = bodyA.position.distanceTo(bodyB.position);
    -    }
    -
    -    if(typeof(maxForce)==="undefined") {
    -        maxForce = 1e6;
    -    }
    -
    -    /**
    -     * @property {number} distance
    -     */
    -    this.distance = distance;
    -
    -    /**
    -     * @property {ContactEquation} distanceEquation
    -     */
    -    var eq = this.distanceEquation = new ContactEquation(bodyA, bodyB);
    -    this.equations.push(eq);
    -
    -    // Make it bidirectional
    -    eq.minForce = -maxForce;
    -    eq.maxForce =  maxForce;
    -}
    -DistanceConstraint.prototype = new Constraint();
    -
    -DistanceConstraint.prototype.update = function(){
    -    var bodyA = this.bodyA;
    -    var bodyB = this.bodyB;
    -    var eq = this.distanceEquation;
    -    var halfDist = this.distance * 0.5;
    -    var normal = eq.ni;
    -
    -    bodyB.position.vsub(bodyA.position, normal);
    -    normal.normalize();
    -    normal.mult(halfDist, eq.ri);
    -    normal.mult(-halfDist, eq.rj);
    -};
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_constraints_HingeConstraint.js.html b/docs/files/src_constraints_HingeConstraint.js.html deleted file mode 100644 index f2e89f72a..000000000 --- a/docs/files/src_constraints_HingeConstraint.js.html +++ /dev/null @@ -1,288 +0,0 @@ - - - - - src/constraints/HingeConstraint.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/constraints/HingeConstraint.js

    - -
    -
    -module.exports = HingeConstraint;
    -
    -var Constraint = require('./Constraint');
    -var PointToPointConstraint = require('./PointToPointConstraint');
    -var RotationalEquation = require('../equations/RotationalEquation');
    -var RotationalMotorEquation = require('../equations/RotationalMotorEquation');
    -var ContactEquation = require('../equations/ContactEquation');
    -var Vec3 = require('../math/Vec3');
    -
    -/**
    - * Hinge constraint. Think of it as a door hinge. It tries to keep the door in the correct place and with the correct orientation.
    - * @class HingeConstraint
    - * @constructor
    - * @author schteppe
    - * @param {Body} bodyA
    - * @param {Body} bodyB
    - * @param {object} [options]
    - * @param {Vec3} [options.pivotA] A point defined locally in bodyA. This defines the offset of axisA.
    - * @param {Vec3} [options.axisA] An axis that bodyA can rotate around, defined locally in bodyA.
    - * @param {Vec3} [options.pivotB]
    - * @param {Vec3} [options.axisB]
    - * @param {Number} [options.maxForce=1e6]
    - * @extends PointToPointConstraint
    - */
    -function HingeConstraint(bodyA, bodyB, options){
    -    options = options || {};
    -    var maxForce = typeof(options.maxForce) !== 'undefined' ? options.maxForce : 1e6;
    -    var pivotA = options.pivotA ? options.pivotA.clone() : new Vec3();
    -    var pivotB = options.pivotB ? options.pivotB.clone() : new Vec3();
    -
    -    PointToPointConstraint.call(this, bodyA, pivotA, bodyB, pivotB, maxForce);
    -
    -    /**
    -     * Rotation axis, defined locally in bodyA.
    -     * @property {Vec3} axisA
    -     */
    -    var axisA = this.axisA = options.axisA ? options.axisA.clone() : new Vec3(1,0,0);
    -    axisA.normalize();
    -
    -    /**
    -     * Rotation axis, defined locally in bodyB.
    -     * @property {Vec3} axisB
    -     */
    -    var axisB = this.axisB = options.axisB ? options.axisB.clone() : new Vec3(1,0,0);
    -    axisB.normalize();
    -
    -    /**
    -     * @property {RotationalEquation} rotationalEquation1
    -     */
    -    var r1 = this.rotationalEquation1 = new RotationalEquation(bodyA,bodyB,options);
    -
    -    /**
    -     * @property {RotationalEquation} rotationalEquation2
    -     */
    -    var r2 = this.rotationalEquation2 = new RotationalEquation(bodyA,bodyB,options);
    -
    -    /**
    -     * @property {RotationalMotorEquation} motorEquation
    -     */
    -    var motor = this.motorEquation = new RotationalMotorEquation(bodyA,bodyB,maxForce);
    -    motor.enabled = false; // Not enabled by default
    -
    -    // Equations to be fed to the solver
    -    this.equations.push(
    -        r1, // rotational1
    -        r2, // rotational2
    -        motor
    -    );
    -}
    -HingeConstraint.prototype = new PointToPointConstraint();
    -HingeConstraint.constructor = HingeConstraint;
    -
    -/**
    - * @method enableMotor
    - */
    -HingeConstraint.prototype.enableMotor = function(){
    -    this.motorEquation.enabled = true;
    -};
    -
    -/**
    - * @method disableMotor
    - */
    -HingeConstraint.prototype.disableMotor = function(){
    -    this.motorEquation.enabled = false;
    -};
    -
    -/**
    - * @method setMotorSpeed
    - * @param {number} speed
    - */
    -HingeConstraint.prototype.setMotorSpeed = function(speed){
    -    this.motorEquation.targetVelocity = speed;
    -};
    -
    -/**
    - * @method setMotorMaxForce
    - * @param {number} maxForce
    - */
    -HingeConstraint.prototype.setMotorMaxForce = function(maxForce){
    -    this.motorEquation.maxForce = maxForce;
    -    this.motorEquation.minForce = -maxForce;
    -};
    -
    -var HingeConstraint_update_tmpVec1 = new Vec3();
    -var HingeConstraint_update_tmpVec2 = new Vec3();
    -
    -HingeConstraint.prototype.update = function(){
    -    var bodyA = this.bodyA,
    -        bodyB = this.bodyB,
    -        motor = this.motorEquation,
    -        r1 = this.rotationalEquation1,
    -        r2 = this.rotationalEquation2,
    -        worldAxisA = HingeConstraint_update_tmpVec1,
    -        worldAxisB = HingeConstraint_update_tmpVec2;
    -
    -    var axisA = this.axisA;
    -    var axisB = this.axisB;
    -
    -    PointToPointConstraint.prototype.update.call(this);
    -
    -    // Get world axes
    -    bodyA.quaternion.vmult(axisA, worldAxisA);
    -    bodyB.quaternion.vmult(axisB, worldAxisB);
    -
    -    worldAxisA.tangents(r1.axisA, r2.axisA);
    -    r1.axisB.copy(worldAxisB);
    -    r2.axisB.copy(worldAxisB);
    -
    -    if(this.motorEquation.enabled){
    -        bodyA.quaternion.vmult(this.axisA, motor.axisA);
    -        bodyB.quaternion.vmult(this.axisB, motor.axisB);
    -    }
    -};
    -
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_constraints_LockConstraint.js.html b/docs/files/src_constraints_LockConstraint.js.html deleted file mode 100644 index 39eff6c47..000000000 --- a/docs/files/src_constraints_LockConstraint.js.html +++ /dev/null @@ -1,233 +0,0 @@ - - - - - src/constraints/LockConstraint.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/constraints/LockConstraint.js

    - -
    -
    -module.exports = LockConstraint;
    -
    -var Constraint = require('./Constraint');
    -var PointToPointConstraint = require('./PointToPointConstraint');
    -var RotationalEquation = require('../equations/RotationalEquation');
    -var RotationalMotorEquation = require('../equations/RotationalMotorEquation');
    -var ContactEquation = require('../equations/ContactEquation');
    -var Vec3 = require('../math/Vec3');
    -
    -/**
    - * Lock constraint. Will remove all degrees of freedom between the bodies.
    - * @class LockConstraint
    - * @constructor
    - * @author schteppe
    - * @param {Body} bodyA
    - * @param {Body} bodyB
    - * @param {object} [options]
    - * @param {Number} [options.maxForce=1e6]
    - * @extends PointToPointConstraint
    - */
    -function LockConstraint(bodyA, bodyB, options){
    -    options = options || {};
    -    var maxForce = typeof(options.maxForce) !== 'undefined' ? options.maxForce : 1e6;
    -
    -    // Set pivot point in between
    -    var pivotA = new Vec3();
    -    var pivotB = new Vec3();
    -    var halfWay = new Vec3();
    -    bodyA.position.vadd(bodyB.position, halfWay);
    -    halfWay.scale(0.5, halfWay);
    -    bodyB.pointToLocalFrame(halfWay, pivotB);
    -    bodyA.pointToLocalFrame(halfWay, pivotA);
    -    PointToPointConstraint.call(this, bodyA, pivotA, bodyB, pivotB, maxForce);
    -
    -    /**
    -     * @property {RotationalEquation} rotationalEquation1
    -     */
    -    var r1 = this.rotationalEquation1 = new RotationalEquation(bodyA,bodyB,options);
    -
    -    /**
    -     * @property {RotationalEquation} rotationalEquation2
    -     */
    -    var r2 = this.rotationalEquation2 = new RotationalEquation(bodyA,bodyB,options);
    -
    -    /**
    -     * @property {RotationalEquation} rotationalEquation3
    -     */
    -    var r3 = this.rotationalEquation3 = new RotationalEquation(bodyA,bodyB,options);
    -
    -    this.equations.push(r1, r2, r3);
    -}
    -LockConstraint.prototype = new PointToPointConstraint();
    -LockConstraint.constructor = LockConstraint;
    -
    -var LockConstraint_update_tmpVec1 = new Vec3();
    -var LockConstraint_update_tmpVec2 = new Vec3();
    -
    -LockConstraint.prototype.update = function(){
    -    var bodyA = this.bodyA,
    -        bodyB = this.bodyB,
    -        motor = this.motorEquation,
    -        r1 = this.rotationalEquation1,
    -        r2 = this.rotationalEquation2,
    -        r3 = this.rotationalEquation3,
    -        worldAxisA = LockConstraint_update_tmpVec1,
    -        worldAxisB = LockConstraint_update_tmpVec2;
    -
    -    PointToPointConstraint.prototype.update.call(this);
    -
    -    bodyA.vectorToWorldFrame(Vec3.UNIT_X, r1.axisA);
    -    bodyB.vectorToWorldFrame(Vec3.UNIT_Y, r1.axisB);
    -
    -    bodyA.vectorToWorldFrame(Vec3.UNIT_Y, r2.axisA);
    -    bodyB.vectorToWorldFrame(Vec3.UNIT_Z, r2.axisB);
    -
    -    bodyA.vectorToWorldFrame(Vec3.UNIT_Z, r3.axisA);
    -    bodyB.vectorToWorldFrame(Vec3.UNIT_X, r3.axisB);
    -};
    -
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_constraints_PointToPointConstraint.js.html b/docs/files/src_constraints_PointToPointConstraint.js.html deleted file mode 100644 index 96d33c8fc..000000000 --- a/docs/files/src_constraints_PointToPointConstraint.js.html +++ /dev/null @@ -1,245 +0,0 @@ - - - - - src/constraints/PointToPointConstraint.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/constraints/PointToPointConstraint.js

    - -
    -
    -module.exports = PointToPointConstraint;
    -
    -var Constraint = require('./Constraint');
    -var ContactEquation = require('../equations/ContactEquation');
    -var Vec3 = require('../math/Vec3');
    -
    -/**
    - * Connects two bodies at given offset points.
    - * @class PointToPointConstraint
    - * @extends Constraint
    - * @constructor
    - * @param {Body} bodyA
    - * @param {Vec3} pivotA The point relative to the center of mass of bodyA which bodyA is constrained to.
    - * @param {Body} bodyB Body that will be constrained in a similar way to the same point as bodyA. We will therefore get a link between bodyA and bodyB. If not specified, bodyA will be constrained to a static point.
    - * @param {Vec3} pivotB See pivotA.
    - * @param {Number} maxForce The maximum force that should be applied to constrain the bodies.
    - *
    - * @example
    - *     var bodyA = new Body({ mass: 1 });
    - *     var bodyB = new Body({ mass: 1 });
    - *     bodyA.position.set(-1, 0, 0);
    - *     bodyB.position.set(1, 0, 0);
    - *     bodyA.addShape(shapeA);
    - *     bodyB.addShape(shapeB);
    - *     world.addBody(bodyA);
    - *     world.addBody(bodyB);
    - *     var localPivotA = new Vec3(1, 0, 0);
    - *     var localPivotB = new Vec3(-1, 0, 0);
    - *     var constraint = new PointToPointConstraint(bodyA, localPivotA, bodyB, localPivotB);
    - *     world.addConstraint(constraint);
    - */
    -function PointToPointConstraint(bodyA,pivotA,bodyB,pivotB,maxForce){
    -    Constraint.call(this,bodyA,bodyB);
    -
    -    maxForce = typeof(maxForce) !== 'undefined' ? maxForce : 1e6;
    -
    -    /**
    -     * Pivot, defined locally in bodyA.
    -     * @property {Vec3} pivotA
    -     */
    -    this.pivotA = pivotA ? pivotA.clone() : new Vec3();
    -
    -    /**
    -     * Pivot, defined locally in bodyB.
    -     * @property {Vec3} pivotB
    -     */
    -    this.pivotB = pivotB ? pivotB.clone() : new Vec3();
    -
    -    /**
    -     * @property {ContactEquation} equationX
    -     */
    -    var x = this.equationX = new ContactEquation(bodyA,bodyB);
    -
    -    /**
    -     * @property {ContactEquation} equationY
    -     */
    -    var y = this.equationY = new ContactEquation(bodyA,bodyB);
    -
    -    /**
    -     * @property {ContactEquation} equationZ
    -     */
    -    var z = this.equationZ = new ContactEquation(bodyA,bodyB);
    -
    -    // Equations to be fed to the solver
    -    this.equations.push(x, y, z);
    -
    -    // Make the equations bidirectional
    -    x.minForce = y.minForce = z.minForce = -maxForce;
    -    x.maxForce = y.maxForce = z.maxForce =  maxForce;
    -
    -    x.ni.set(1, 0, 0);
    -    y.ni.set(0, 1, 0);
    -    z.ni.set(0, 0, 1);
    -}
    -PointToPointConstraint.prototype = new Constraint();
    -
    -PointToPointConstraint.prototype.update = function(){
    -    var bodyA = this.bodyA;
    -    var bodyB = this.bodyB;
    -    var x = this.equationX;
    -    var y = this.equationY;
    -    var z = this.equationZ;
    -
    -    // Rotate the pivots to world space
    -    bodyA.quaternion.vmult(this.pivotA,x.ri);
    -    bodyB.quaternion.vmult(this.pivotB,x.rj);
    -
    -    y.ri.copy(x.ri);
    -    y.rj.copy(x.rj);
    -    z.ri.copy(x.ri);
    -    z.rj.copy(x.rj);
    -};
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_demo_Demo.js.html b/docs/files/src_demo_Demo.js.html deleted file mode 100644 index 7485915f7..000000000 --- a/docs/files/src_demo_Demo.js.html +++ /dev/null @@ -1,1279 +0,0 @@ - - - - - src/demo/Demo.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/demo/Demo.js

    - -
    -
    -/* global CANNON,THREE,Detector */
    -
    -CANNON = CANNON || {};
    -
    -/**
    - * Demo framework class. If you want to learn how to connect Cannon.js with Three.js, please look at the examples/ instead.
    - * @class Demo
    - * @constructor
    - * @param {Object} options
    - */
    -CANNON.Demo = function(options){
    -
    -    var that = this;
    -
    -    // API
    -    this.addScene = addScene;
    -    this.restartCurrentScene = restartCurrentScene;
    -    this.changeScene = changeScene;
    -    this.start = start;
    -
    -    var sceneFolder;
    -
    -    // Global settings
    -    var settings = this.settings = {
    -        stepFrequency: 60,
    -        quatNormalizeSkip: 2,
    -        quatNormalizeFast: true,
    -        gx: 0,
    -        gy: 0,
    -        gz: 0,
    -        iterations: 3,
    -        tolerance: 0.0001,
    -        k: 1e6,
    -        d: 3,
    -        scene: 0,
    -        paused: false,
    -        rendermode: "solid",
    -        constraints: false,
    -        contacts: false,  // Contact points
    -        cm2contact: false, // center of mass to contact points
    -        normals: false, // contact normals
    -        axes: false, // "local" frame axes
    -        particleSize: 0.1,
    -        shadows: false,
    -        aabbs: false,
    -        profiling: false,
    -        maxSubSteps:3
    -    };
    -
    -    // Extend settings with options
    -    options = options || {};
    -    for(var key in options){
    -        if(key in settings){
    -            settings[key] = options[key];
    -        }
    -    }
    -
    -    if(settings.stepFrequency % 60 !== 0){
    -        throw new Error("stepFrequency must be a multiple of 60.");
    -    }
    -
    -    var bodies = this.bodies = [];
    -    var visuals = this.visuals = [];
    -    var scenes = [];
    -    var gui = null;
    -    var smoothie = null;
    -    var smoothieCanvas = null;
    -    var scenePicker = {};
    -
    -    var three_contactpoint_geo = new THREE.SphereGeometry( 0.1, 6, 6);
    -    var particleGeo = this.particleGeo = new THREE.SphereGeometry( 1, 16, 8 );
    -
    -    // Material
    -    var materialColor = 0xdddddd;
    -    var solidMaterial = new THREE.MeshLambertMaterial( { color: materialColor } );
    -    //THREE.ColorUtils.adjustHSV( solidMaterial.color, 0, 0, 0.9 );
    -    var wireframeMaterial = new THREE.MeshLambertMaterial( { color: 0xffffff, wireframe:true } );
    -    this.currentMaterial = solidMaterial;
    -    var contactDotMaterial = new THREE.MeshLambertMaterial( { color: 0xff0000 } );
    -    var particleMaterial = this.particleMaterial = new THREE.MeshLambertMaterial( { color: 0xff0000 } );
    -
    -    // Geometry caches
    -    var contactMeshCache = new GeometryCache(function(){
    -        return new THREE.Mesh( three_contactpoint_geo, contactDotMaterial );
    -    });
    -    var cm2contactMeshCache = new GeometryCache(function(){
    -        var geometry = new THREE.Geometry();
    -        geometry.vertices.push(new THREE.Vector3(0,0,0));
    -        geometry.vertices.push(new THREE.Vector3(1,1,1));
    -        return new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0xff0000 } ) );
    -    });
    -    var bboxGeometry = new THREE.BoxGeometry(1,1,1);
    -    var bboxMaterial = new THREE.MeshBasicMaterial({
    -        color: materialColor,
    -        wireframe:true
    -    });
    -    var bboxMeshCache = new GeometryCache(function(){
    -        return new THREE.Mesh(bboxGeometry,bboxMaterial);
    -    });
    -    var distanceConstraintMeshCache = new GeometryCache(function(){
    -        var geometry = new THREE.Geometry();
    -        geometry.vertices.push(new THREE.Vector3(0,0,0));
    -        geometry.vertices.push(new THREE.Vector3(1,1,1));
    -        return new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0xff0000 } ) );
    -    });
    -    var p2pConstraintMeshCache = new GeometryCache(function(){
    -        var geometry = new THREE.Geometry();
    -        geometry.vertices.push(new THREE.Vector3(0,0,0));
    -        geometry.vertices.push(new THREE.Vector3(1,1,1));
    -        return new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0xff0000 } ) );
    -    });
    -    var normalMeshCache = new GeometryCache(function(){
    -        var geometry = new THREE.Geometry();
    -        geometry.vertices.push(new THREE.Vector3(0,0,0));
    -        geometry.vertices.push(new THREE.Vector3(1,1,1));
    -        return new THREE.Line( geometry, new THREE.LineBasicMaterial({color:0x00ff00}));
    -    });
    -    var axesMeshCache = new GeometryCache(function(){
    -        var mesh = new THREE.Object3D();
    -        //mesh.useQuaternion = true;
    -        var origin = new THREE.Vector3(0,0,0);
    -        var gX = new THREE.Geometry();
    -        var gY = new THREE.Geometry();
    -        var gZ = new THREE.Geometry();
    -        gX.vertices.push(origin);
    -        gY.vertices.push(origin);
    -        gZ.vertices.push(origin);
    -        gX.vertices.push(new THREE.Vector3(1,0,0));
    -        gY.vertices.push(new THREE.Vector3(0,1,0));
    -        gZ.vertices.push(new THREE.Vector3(0,0,1));
    -        var lineX = new THREE.Line( gX, new THREE.LineBasicMaterial({color:0xff0000}));
    -        var lineY = new THREE.Line( gY, new THREE.LineBasicMaterial({color:0x00ff00}));
    -        var lineZ = new THREE.Line( gZ, new THREE.LineBasicMaterial({color:0x0000ff}));
    -        mesh.add(lineX);
    -        mesh.add(lineY);
    -        mesh.add(lineZ);
    -        return mesh;
    -    });
    -    function restartGeometryCaches(){
    -        contactMeshCache.restart();
    -        contactMeshCache.hideCached();
    -
    -        cm2contactMeshCache.restart();
    -        cm2contactMeshCache.hideCached();
    -
    -        distanceConstraintMeshCache.restart();
    -        distanceConstraintMeshCache.hideCached();
    -
    -        normalMeshCache.restart();
    -        normalMeshCache.hideCached();
    -    }
    -
    -    // Create physics world
    -    var world = this.world = new CANNON.World();
    -    world.broadphase = new CANNON.NaiveBroadphase();
    -
    -    var renderModes = ["solid","wireframe"];
    -
    -    function updategui(){
    -        if(gui){
    -            // First level
    -            for (var i in gui.__controllers){
    -                gui.__controllers[i].updateDisplay();
    -            }
    -
    -            // Second level
    -            for (var f in gui.__folders){
    -                for (var i in gui.__folders[f].__controllers){
    -                    gui.__folders[f].__controllers[i].updateDisplay();
    -                }
    -            }
    -        }
    -    }
    -
    -    var light, scene, ambient, stats, info;
    -
    -    function setRenderMode(mode){
    -        if(renderModes.indexOf(mode) === -1){
    -            throw new Error("Render mode "+mode+" not found!");
    -        }
    -
    -        switch(mode){
    -        case "solid":
    -            that.currentMaterial = solidMaterial;
    -            light.intensity = 1;
    -            ambient.color.setHex(0x222222);
    -            break;
    -        case "wireframe":
    -            that.currentMaterial = wireframeMaterial;
    -            light.intensity = 0;
    -            ambient.color.setHex(0xffffff);
    -            break;
    -        }
    -
    -        function setMaterial(node,mat){
    -            if(node.material){
    -                node.material = mat;
    -            }
    -            for(var i=0; i<node.children.length; i++){
    -                setMaterial(node.children[i],mat);
    -            }
    -        }
    -        for(var i=0; i<visuals.length; i++){
    -            setMaterial(visuals[i],that.currentMaterial);
    -        }
    -        settings.rendermode = mode;
    -    }
    -
    -    /**
    -     * Add a scene to the demo app
    -     * @method addScene
    -     * @param {String} title Title of the scene
    -     * @param {Function} initfunc A function that takes one argument, app, and initializes a physics scene. The function runs app.setWorld(body), app.addVisual(body), app.removeVisual(body) etc.
    -     */
    -    function addScene(title,initfunc){
    -        if(typeof(title) !== "string"){
    -            throw new Error("1st argument of Demo.addScene(title,initfunc) must be a string!");
    -        }
    -        if(typeof(initfunc)!=="function"){
    -            throw new Error("2nd argument of Demo.addScene(title,initfunc) must be a function!");
    -        }
    -        scenes.push(initfunc);
    -        var idx = scenes.length-1;
    -        scenePicker[title] = function(){
    -            changeScene(idx);
    -        };
    -        sceneFolder.add(scenePicker,title);
    -    }
    -
    -    /**
    -     * Restarts the current scene
    -     * @method restartCurrentScene
    -     */
    -    function restartCurrentScene(){
    -        var N = bodies.length;
    -        for(var i=0; i<N; i++){
    -            var b = bodies[i];
    -            b.position.copy(b.initPosition);
    -            b.velocity.copy(b.initVelocity);
    -            if(b.initAngularVelocity){
    -                b.angularVelocity.copy(b.initAngularVelocity);
    -                b.quaternion.copy(b.initQuaternion);
    -            }
    -        }
    -    }
    -
    -    function makeSureNotZero(vec){
    -        if(vec.x===0.0){
    -            vec.x = 1e-6;
    -        }
    -        if(vec.y===0.0){
    -            vec.y = 1e-6;
    -        }
    -        if(vec.z===0.0){
    -            vec.z = 1e-6;
    -        }
    -    }
    -
    -
    -    function updateVisuals(){
    -        var N = bodies.length;
    -
    -        // Read position data into visuals
    -        for(var i=0; i<N; i++){
    -            var b = bodies[i], visual = visuals[i];
    -            visual.position.copy(b.position);
    -            if(b.quaternion){
    -                visual.quaternion.copy(b.quaternion);
    -            }
    -        }
    -
    -        // Render contacts
    -        contactMeshCache.restart();
    -        if(settings.contacts){
    -            // if ci is even - use body i, else j
    -            for(var ci=0; ci < world.contacts.length; ci++){
    -                for(var ij=0; ij < 2; ij++){
    -                    var  mesh = contactMeshCache.request(),
    -                    c = world.contacts[ci],
    -                    b = ij===0 ? c.bi : c.bj,
    -                    r = ij===0 ? c.ri : c.rj;
    -                    mesh.position.set( b.position.x + r.x , b.position.y + r.y , b.position.z + r.z );
    -                }
    -            }
    -        }
    -        contactMeshCache.hideCached();
    -
    -        // Lines from center of mass to contact point
    -        cm2contactMeshCache.restart();
    -        if(settings.cm2contact){
    -            for(var ci=0; ci<world.contacts.length; ci++){
    -                for(var ij=0; ij < 2; ij++){
    -                    var line = cm2contactMeshCache.request(),
    -                        c = world.contacts[ci],
    -                        b = ij===0 ? c.bi : c.bj,
    -                        r = ij===0 ? c.ri : c.rj;
    -                    line.scale.set( r.x, r.y, r.z);
    -                    makeSureNotZero(line.scale);
    -                    line.position.copy(b.position);
    -                }
    -            }
    -        }
    -        cm2contactMeshCache.hideCached();
    -
    -        distanceConstraintMeshCache.restart();
    -        p2pConstraintMeshCache.restart();
    -        if(settings.constraints){
    -            // Lines for distance constraints
    -            for(var ci=0; ci<world.constraints.length; ci++){
    -                var c = world.constraints[ci];
    -                if(!(c instanceof CANNON.DistanceConstraint)){
    -                    continue;
    -                }
    -
    -                var nc = c.equations.normal;
    -
    -                var bi=nc.bi, bj=nc.bj, line = distanceConstraintMeshCache.request();
    -                var i=bi.id, j=bj.id;
    -
    -                // Remember, bj is either a Vec3 or a Body.
    -                var v;
    -                if(bj.position){
    -                    v = bj.position;
    -                } else {
    -                    v = bj;
    -                }
    -                line.scale.set( v.x-bi.position.x,
    -                                v.y-bi.position.y,
    -                                v.z-bi.position.z );
    -                makeSureNotZero(line.scale);
    -                line.position.copy(bi.position);
    -            }
    -
    -
    -            // Lines for distance constraints
    -            for(var ci=0; ci<world.constraints.length; ci++){
    -                var c = world.constraints[ci];
    -                if(!(c instanceof CANNON.PointToPointConstraint)){
    -                    continue;
    -                }
    -                var n = c.equations.normal;
    -                var bi=n.bi, bj=n.bj, relLine1 = p2pConstraintMeshCache.request(), relLine2 = p2pConstraintMeshCache.request(), diffLine = p2pConstraintMeshCache.request();
    -                var i=bi.id, j=bj.id;
    -
    -                relLine1.scale.set( n.ri.x, n.ri.y, n.ri.z );
    -                relLine2.scale.set( n.rj.x, n.rj.y, n.rj.z );
    -                diffLine.scale.set( -n.penetrationVec.x, -n.penetrationVec.y, -n.penetrationVec.z );
    -                makeSureNotZero(relLine1.scale);
    -                makeSureNotZero(relLine2.scale);
    -                makeSureNotZero(diffLine.scale);
    -                relLine1.position.copy(bi.position);
    -                relLine2.position.copy(bj.position);
    -                n.bj.position.vadd(n.rj,diffLine.position);
    -            }
    -        }
    -        p2pConstraintMeshCache.hideCached();
    -        distanceConstraintMeshCache.hideCached();
    -
    -        // Normal lines
    -        normalMeshCache.restart();
    -        if(settings.normals){
    -            for(var ci=0; ci<world.contacts.length; ci++){
    -                var c = world.contacts[ci];
    -                var bi=c.bi, bj=c.bj, line=normalMeshCache.request();
    -                var i=bi.id, j=bj.id;
    -                var n = c.ni;
    -                var b = bi;
    -                line.scale.set(n.x,n.y,n.z);
    -                makeSureNotZero(line.scale);
    -                line.position.copy(b.position);
    -                c.ri.vadd(line.position,line.position);
    -            }
    -        }
    -        normalMeshCache.hideCached();
    -
    -        // Frame axes for each body
    -        axesMeshCache.restart();
    -        if(settings.axes){
    -            for(var bi=0; bi<bodies.length; bi++){
    -                var b = bodies[bi], mesh=axesMeshCache.request();
    -                mesh.position.copy(b.position);
    -                if(b.quaternion){
    -                    mesh.quaternion.copy(b.quaternion);
    -                }
    -            }
    -        }
    -        axesMeshCache.hideCached();
    -
    -        // AABBs
    -        bboxMeshCache.restart();
    -        if(settings.aabbs){
    -            for(var i=0; i<bodies.length; i++){
    -                var b = bodies[i];
    -                if(b.computeAABB){
    -
    -                    if(b.aabbNeedsUpdate){
    -                        b.computeAABB();
    -                    }
    -
    -                    // Todo: cap the infinite AABB to scene AABB, for now just dont render
    -                    if( isFinite(b.aabb.lowerBound.x) &&
    -                        isFinite(b.aabb.lowerBound.y) &&
    -                        isFinite(b.aabb.lowerBound.z) &&
    -                        isFinite(b.aabb.upperBound.x) &&
    -                        isFinite(b.aabb.upperBound.y) &&
    -                        isFinite(b.aabb.upperBound.z) &&
    -                        b.aabb.lowerBound.x - b.aabb.upperBound.x != 0 &&
    -                        b.aabb.lowerBound.y - b.aabb.upperBound.y != 0 &&
    -                        b.aabb.lowerBound.z - b.aabb.upperBound.z != 0){
    -                            var mesh = bboxMeshCache.request();
    -                            mesh.scale.set( b.aabb.lowerBound.x - b.aabb.upperBound.x,
    -                                            b.aabb.lowerBound.y - b.aabb.upperBound.y,
    -                                            b.aabb.lowerBound.z - b.aabb.upperBound.z);
    -                            mesh.position.set(  (b.aabb.lowerBound.x + b.aabb.upperBound.x)*0.5,
    -                                                (b.aabb.lowerBound.y + b.aabb.upperBound.y)*0.5,
    -                                                (b.aabb.lowerBound.z + b.aabb.upperBound.z)*0.5);
    -                        }
    -                }
    -            }
    -        }
    -        bboxMeshCache.hideCached();
    -    }
    -
    -    if (!Detector.webgl){
    -        Detector.addGetWebGLMessage();
    -    }
    -
    -    var SHADOW_MAP_WIDTH = 512;
    -    var SHADOW_MAP_HEIGHT = 512;
    -    var MARGIN = 0;
    -    var SCREEN_WIDTH = window.innerWidth;
    -    var SCREEN_HEIGHT = window.innerHeight - 2 * MARGIN;
    -    var camera, controls, renderer;
    -    var container;
    -    var NEAR = 5, FAR = 2000;
    -    var sceneHUD, cameraOrtho, hudMaterial;
    -
    -    var mouseX = 0, mouseY = 0;
    -
    -    var windowHalfX = window.innerWidth / 2;
    -    var windowHalfY = window.innerHeight / 2;
    -
    -    init();
    -    animate();
    -
    -    function init() {
    -
    -        container = document.createElement( 'div' );
    -        document.body.appendChild( container );
    -
    -        // Camera
    -        camera = new THREE.PerspectiveCamera( 24, SCREEN_WIDTH / SCREEN_HEIGHT, NEAR, FAR );
    -
    -        camera.up.set(0,0,1);
    -        camera.position.set(0,30,20);
    -
    -        // SCENE
    -        scene = that.scene = new THREE.Scene();
    -        scene.fog = new THREE.Fog( 0x222222, 1000, FAR );
    -
    -        // LIGHTS
    -        ambient = new THREE.AmbientLight( 0x222222 );
    -        scene.add( ambient );
    -
    -        light = new THREE.SpotLight( 0xffffff );
    -        light.position.set( 30, 30, 40 );
    -        light.target.position.set( 0, 0, 0 );
    -
    -        light.castShadow = true;
    -
    -        light.shadowCameraNear = 10;
    -        light.shadowCameraFar = 100;//camera.far;
    -        light.shadowCameraFov = 30;
    -
    -        light.shadowMapBias = 0.0039;
    -        light.shadowMapDarkness = 0.5;
    -        light.shadowMapWidth = SHADOW_MAP_WIDTH;
    -        light.shadowMapHeight = SHADOW_MAP_HEIGHT;
    -
    -        //light.shadowCameraVisible = true;
    -
    -        scene.add( light );
    -        scene.add( camera );
    -
    -        // RENDERER
    -        renderer = new THREE.WebGLRenderer( { clearColor: 0x000000, clearAlpha: 1, antialias: false } );
    -        renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
    -        renderer.domElement.style.position = "relative";
    -        renderer.domElement.style.top = MARGIN + 'px';
    -        container.appendChild( renderer.domElement );
    -
    -        // Add info
    -        info = document.createElement( 'div' );
    -        info.style.position = 'absolute';
    -        info.style.top = '10px';
    -        info.style.width = '100%';
    -        info.style.textAlign = 'center';
    -        info.innerHTML = '<a href="http://github.com/schteppe/cannon.js">cannon.js</a> - javascript 3d physics';
    -        container.appendChild( info );
    -
    -        document.addEventListener('mousemove',onDocumentMouseMove);
    -        window.addEventListener('resize',onWindowResize);
    -
    -        renderer.setClearColor( scene.fog.color, 1 );
    -        renderer.autoClear = false;
    -
    -        renderer.shadowMapEnabled = true;
    -        renderer.shadowMapSoft = true;
    -
    -        // Smoothie
    -        smoothieCanvas = document.createElement("canvas");
    -        smoothieCanvas.width = SCREEN_WIDTH;
    -        smoothieCanvas.height = SCREEN_HEIGHT;
    -        smoothieCanvas.style.opacity = 0.5;
    -        smoothieCanvas.style.position = 'absolute';
    -        smoothieCanvas.style.top = '0px';
    -        smoothieCanvas.style.zIndex = 90;
    -        container.appendChild( smoothieCanvas );
    -        smoothie = new SmoothieChart({
    -            labelOffsetY:50,
    -            maxDataSetLength:100,
    -            millisPerPixel:2,
    -            grid: {
    -                strokeStyle:'none',
    -                fillStyle:'none',
    -                lineWidth: 1,
    -                millisPerLine: 250,
    -                verticalSections: 6
    -            },
    -            labels: {
    -                fillStyle:'rgb(180, 180, 180)'
    -            }
    -        });
    -        smoothie.streamTo(smoothieCanvas);
    -        // Create time series for each profile label
    -        var lines = {};
    -        var colors = [[255, 0, 0],[0, 255, 0],[0, 0, 255],[255,255,0],[255,0,255],[0,255,255]];
    -        var i=0;
    -        for(var label in world.profile){
    -            var c = colors[i%colors.length];
    -            lines[label] = new TimeSeries({
    -                label : label,
    -                fillStyle : "rgb("+c[0]+","+c[1]+","+c[2]+")",
    -                maxDataLength : 500,
    -            });
    -            i++;
    -        }
    -
    -        // Add a random value to each line every second
    -        world.addEventListener("postStep",function(evt) {
    -            for(var label in world.profile)
    -                lines[label].append(world.time * 1000, world.profile[label]);
    -        });
    -
    -        // Add to SmoothieChart
    -        var i=0;
    -        for(var label in world.profile){
    -            var c = colors[i%colors.length];
    -            smoothie.addTimeSeries(lines[label],{
    -                strokeStyle : "rgb("+c[0]+","+c[1]+","+c[2]+")",
    -                //fillStyle:"rgba("+c[0]+","+c[1]+","+c[2]+",0.3)",
    -                lineWidth:2
    -            });
    -            i++;
    -        }
    -        world.doProfiling = false;
    -        smoothie.stop();
    -        smoothieCanvas.style.display = "none";
    -
    -        // STATS
    -        stats = new Stats();
    -        stats.domElement.style.position = 'absolute';
    -        stats.domElement.style.top = '0px';
    -        stats.domElement.style.zIndex = 100;
    -        container.appendChild( stats.domElement );
    -
    -        if(window.dat!=undefined){
    -            gui = new dat.GUI();
    -
    -            gui.domElement.parentNode.style.zIndex=120;
    -
    -            // Render mode
    -            var rf = gui.addFolder('Rendering');
    -            rf.add(settings,'rendermode',{Solid:"solid",Wireframe:"wireframe"}).onChange(function(mode){
    -                setRenderMode(mode);
    -            });
    -            rf.add(settings,'contacts');
    -            rf.add(settings,'cm2contact');
    -            rf.add(settings,'normals');
    -            rf.add(settings,'constraints');
    -            rf.add(settings,'axes');
    -            rf.add(settings,'particleSize').min(0).max(1).onChange(function(size){
    -                for(var i=0; i<visuals.length; i++){
    -                    if(bodies[i] instanceof CANNON.Particle)
    -                        visuals[i].scale.set(size,size,size);
    -                }
    -            });
    -            rf.add(settings,'shadows').onChange(function(shadows){
    -                if(shadows){
    -                    renderer.shadowMapAutoUpdate = true;
    -                } else {
    -                    renderer.shadowMapAutoUpdate = false;
    -                    renderer.clearTarget( light.shadowMap );
    -                }
    -            });
    -            rf.add(settings,'aabbs');
    -            rf.add(settings,'profiling').onChange(function(profiling){
    -                if(profiling){
    -                    world.doProfiling = true;
    -                    smoothie.start();
    -                    smoothieCanvas.style.display = "block";
    -                } else {
    -                    world.doProfiling = false;
    -                    smoothie.stop();
    -                    smoothieCanvas.style.display = "none";
    -                }
    -
    -            });
    -
    -            // World folder
    -            var wf = gui.addFolder('World');
    -            // Pause
    -            wf.add(settings, 'paused').onChange(function(p){
    -                /*if(p){
    -                    smoothie.stop();
    -                } else {
    -                    smoothie.start();
    -                }*/
    -            });
    -            wf.add(settings, 'stepFrequency',60,60*10).step(60);
    -            var maxg = 100;
    -            wf.add(settings, 'gx',-maxg,maxg).onChange(function(gx){
    -                if(!isNaN(gx)){
    -                    world.gravity.set(gx,settings.gy,settings.gz);
    -                }
    -            });
    -            wf.add(settings, 'gy',-maxg,maxg).onChange(function(gy){
    -                if(!isNaN(gy))
    -                    world.gravity.set(settings.gx,gy,settings.gz);
    -            });
    -            wf.add(settings, 'gz',-maxg,maxg).onChange(function(gz){
    -                if(!isNaN(gz))
    -                    world.gravity.set(settings.gx,settings.gy,gz);
    -            });
    -            wf.add(settings, 'quatNormalizeSkip',0,50).step(1).onChange(function(skip){
    -                if(!isNaN(skip)){
    -                    world.quatNormalizeSkip = skip;
    -                }
    -            });
    -            wf.add(settings, 'quatNormalizeFast').onChange(function(fast){
    -                world.quatNormalizeFast = !!fast;
    -            });
    -
    -            // Solver folder
    -            var sf = gui.addFolder('Solver');
    -            sf.add(settings, 'iterations',1,50).step(1).onChange(function(it){
    -                world.solver.iterations = it;
    -            });
    -            sf.add(settings, 'k',10,10000000).onChange(function(k){
    -                that.setGlobalSpookParams(settings.k,settings.d,1/settings.stepFrequency);
    -            });
    -            sf.add(settings, 'd',0,20).step(0.1).onChange(function(d){
    -                that.setGlobalSpookParams(settings.k,settings.d,1/settings.stepFrequency);
    -            });
    -            sf.add(settings, 'tolerance',0.0,10.0).step(0.01).onChange(function(t){
    -                world.solver.tolerance = t;
    -            });
    -
    -            // Scene picker
    -            sceneFolder = gui.addFolder('Scenes');
    -            sceneFolder.open();
    -        }
    -
    -        // Trackball controls
    -        controls = new THREE.TrackballControls( camera, renderer.domElement );
    -        controls.rotateSpeed = 1.0;
    -        controls.zoomSpeed = 1.2;
    -        controls.panSpeed = 0.2;
    -        controls.noZoom = false;
    -        controls.noPan = false;
    -        controls.staticMoving = false;
    -        controls.dynamicDampingFactor = 0.3;
    -        var radius = 100;
    -        controls.minDistance = 0.0;
    -        controls.maxDistance = radius * 1000;
    -        //controls.keys = [ 65, 83, 68 ]; // [ rotateKey, zoomKey, panKey ]
    -        controls.screen.width = SCREEN_WIDTH;
    -        controls.screen.height = SCREEN_HEIGHT;
    -    }
    -
    -    var t = 0, newTime, delta;
    -
    -    function animate(){
    -        requestAnimationFrame( animate );
    -        if(!settings.paused){
    -            updateVisuals();
    -            updatePhysics();
    -        }
    -        render();
    -        stats.update();
    -    }
    -
    -    var lastCallTime = 0;
    -    function updatePhysics(){
    -        // Step world
    -        var timeStep = 1 / settings.stepFrequency;
    -
    -        var now = Date.now() / 1000;
    -
    -        if(!lastCallTime){
    -            // last call time not saved, cant guess elapsed time. Take a simple step.
    -            world.step(timeStep);
    -            lastCallTime = now;
    -            return;
    -        }
    -
    -        var timeSinceLastCall = now - lastCallTime;
    -
    -        world.step(timeStep, timeSinceLastCall, settings.maxSubSteps);
    -
    -        lastCallTime = now;
    -    }
    -
    -    function onDocumentMouseMove( event ) {
    -        mouseX = ( event.clientX - windowHalfX );
    -        mouseY = ( event.clientY - windowHalfY );
    -    }
    -
    -    function onWindowResize( event ) {
    -        SCREEN_WIDTH = window.innerWidth;
    -        SCREEN_HEIGHT = window.innerHeight;
    -
    -        renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
    -
    -        camera.aspect = SCREEN_WIDTH / SCREEN_HEIGHT;
    -        camera.updateProjectionMatrix();
    -
    -        controls.screen.width = SCREEN_WIDTH;
    -        controls.screen.height = SCREEN_HEIGHT;
    -
    -        camera.radius = ( SCREEN_WIDTH + SCREEN_HEIGHT ) / 4;
    -    }
    -
    -    function render(){
    -        controls.update();
    -        renderer.clear();
    -        renderer.render( that.scene, camera );
    -    }
    -
    -    document.addEventListener('keypress',function(e){
    -
    -        if(e.keyCode){
    -            switch(e.keyCode){
    -            case 32: // Space - restart
    -                restartCurrentScene();
    -                break;
    -
    -            case 104: // h - toggle widgets
    -                if(stats.domElement.style.display=="none"){
    -                    stats.domElement.style.display = "block";
    -                    info.style.display = "block";
    -                } else {
    -                    stats.domElement.style.display = "none";
    -                    info.style.display = "none";
    -                }
    -                break;
    -
    -            case 97: // a - AABBs
    -                settings.aabbs = !settings.aabbs;
    -                updategui();
    -                break;
    -
    -            case 99: // c - constraints
    -                settings.constraints = !settings.constraints;
    -                updategui();
    -                break;
    -
    -            case 112: // p
    -                settings.paused = !settings.paused;
    -                updategui();
    -                break;
    -
    -            case 115: // s
    -                var timeStep = 1 / settings.stepFrequency;
    -                world.step(timeStep);
    -                updateVisuals();
    -                break;
    -
    -            case 109: // m - toggle materials
    -                var idx = renderModes.indexOf(settings.rendermode);
    -                idx++;
    -                idx = idx % renderModes.length; // begin at 0 if we exceeded number of modes
    -                setRenderMode(renderModes[idx]);
    -                updategui();
    -                break;
    -
    -            case 49:
    -            case 50:
    -            case 51:
    -            case 52:
    -            case 53:
    -            case 54:
    -            case 55:
    -            case 56:
    -            case 57:
    -                // Change scene
    -                // Only for numbers 1-9 and if no input field is active
    -                if(scenes.length > e.keyCode-49 && !document.activeElement.localName.match(/input/)){
    -                    changeScene(e.keyCode-49);
    -                }
    -                break;
    -            }
    -        }
    -    });
    -
    -
    -    function changeScene(n){
    -        that.dispatchEvent({ type: 'destroy' });
    -        settings.paused = false;
    -        updategui();
    -        buildScene(n);
    -    }
    -
    -
    -    function start(){
    -        buildScene(0);
    -    }
    -
    -    function buildScene(n){
    -        // Remove current bodies and visuals
    -        var num = visuals.length;
    -        for(var i=0; i<num; i++){
    -            world.remove(bodies.pop());
    -            var mesh = visuals.pop();
    -            that.scene.remove(mesh);
    -        }
    -        // Remove all constraints
    -        while(world.constraints.length){
    -            world.removeConstraint(world.constraints[0]);
    -        }
    -
    -        // Run the user defined "build scene" function
    -        scenes[n]();
    -
    -        // Read the newly set data to the gui
    -        settings.iterations = world.solver.iterations;
    -        settings.gx = world.gravity.x+0.0;
    -        settings.gy = world.gravity.y+0.0;
    -        settings.gz = world.gravity.z+0.0;
    -        settings.quatNormalizeSkip = world.quatNormalizeSkip;
    -        settings.quatNormalizeFast = world.quatNormalizeFast;
    -        updategui();
    -
    -        restartGeometryCaches();
    -    }
    -
    -
    -    function GeometryCache(createFunc){
    -        var that=this, geometries=[], gone=[];
    -        this.request = function(){
    -            if(geometries.length){
    -                geo = geometries.pop();
    -            } else{
    -                geo = createFunc();
    -            }
    -            scene.add(geo);
    -            gone.push(geo);
    -            return geo;
    -        };
    -
    -        this.restart = function(){
    -            while(gone.length){
    -                geometries.push(gone.pop());
    -            }
    -        };
    -
    -        this.hideCached = function(){
    -            for(var i=0; i<geometries.length; i++){
    -                scene.remove(geometries[i]);
    -            }
    -        };
    -    }
    -};
    -CANNON.Demo.prototype = new CANNON.EventTarget();
    -CANNON.Demo.constructor = CANNON.Demo;
    -
    -CANNON.Demo.prototype.setGlobalSpookParams = function(k,d,h){
    -    var world = this.world;
    -
    -    // Set for all constraints
    -    for(var i=0; i<world.constraints.length; i++){
    -        var c = world.constraints[i];
    -        for(var j=0; j<c.equations.length; j++){
    -            var eq = c.equations[j];
    -            eq.setSpookParams(k,d,h);
    -        }
    -    }
    -
    -    // Set for all contact materals
    -    for(var i=0; i<world.contactmaterials.length; i++){
    -        var cm = world.contactmaterials[i];
    -        cm.contactEquationStiffness = k;
    -        cm.frictionEquationStiffness = k;
    -        cm.contactEquationRelaxation = d;
    -        cm.frictionEquationRelaxation = d;
    -    }
    -
    -    world.defaultContactMaterial.contactEquationStiffness = k;
    -    world.defaultContactMaterial.frictionEquationStiffness = k;
    -    world.defaultContactMaterial.contactEquationRelaxation = d;
    -    world.defaultContactMaterial.frictionEquationRelaxation = d;
    -};
    -
    -CANNON.Demo.prototype.getWorld = function(){
    -    return this.world;
    -};
    -
    -CANNON.Demo.prototype.addVisual = function(body){
    -    var s = this.settings;
    -    // What geometry should be used?
    -    var mesh;
    -    if(body instanceof CANNON.Body){
    -        mesh = this.shape2mesh(body);
    -    }
    -    if(mesh) {
    -        // Add body
    -        this.bodies.push(body);
    -        this.visuals.push(mesh);
    -        body.visualref = mesh;
    -        body.visualref.visualId = this.bodies.length - 1;
    -        //mesh.useQuaternion = true;
    -        this.scene.add(mesh);
    -    }
    -};
    -
    -CANNON.Demo.prototype.addVisuals = function(bodies){
    -    for (var i = 0; i < bodies.length; i++) {
    -        this.addVisual(bodies[i]);
    -    }
    -};
    -
    -CANNON.Demo.prototype.removeVisual = function(body){
    -    if(body.visualref){
    -        var bodies = this.bodies,
    -            visuals = this.visuals,
    -            old_b = [],
    -            old_v = [],
    -            n = bodies.length;
    -
    -        for(var i=0; i<n; i++){
    -            old_b.unshift(bodies.pop());
    -            old_v.unshift(visuals.pop());
    -        }
    -
    -        var id = body.visualref.visualId;
    -        for(var j=0; j<old_b.length; j++){
    -            if(j !== id){
    -                var i = j>id ? j-1 : j;
    -                bodies[i] = old_b[j];
    -                visuals[i] = old_v[j];
    -                bodies[i].visualref = old_b[j].visualref;
    -                bodies[i].visualref.visualId = i;
    -            }
    -        }
    -        body.visualref.visualId = null;
    -        this.scene.remove(body.visualref);
    -        body.visualref = null;
    -    }
    -};
    -
    -CANNON.Demo.prototype.removeAllVisuals = function(){
    -    while(this.bodies.length) {
    -        this.removeVisual(this.bodies[0]);
    -    }
    -};
    -
    -CANNON.Demo.prototype.shape2mesh = function(body){
    -    var wireframe = this.settings.renderMode === "wireframe";
    -    var obj = new THREE.Object3D();
    -
    -    for (var l = 0; l < body.shapes.length; l++) {
    -        var shape = body.shapes[l];
    -
    -        var mesh;
    -
    -        switch(shape.type){
    -
    -        case CANNON.Shape.types.SPHERE:
    -            var sphere_geometry = new THREE.SphereGeometry( shape.radius, 8, 8);
    -            mesh = new THREE.Mesh( sphere_geometry, this.currentMaterial );
    -            break;
    -
    -        case CANNON.Shape.types.PARTICLE:
    -            mesh = new THREE.Mesh( this.particleGeo, this.particleMaterial );
    -            var s = this.settings;
    -            mesh.scale.set(s.particleSize,s.particleSize,s.particleSize);
    -            break;
    -
    -        case CANNON.Shape.types.PLANE:
    -            var geometry = new THREE.PlaneGeometry(10, 10, 4, 4);
    -            mesh = new THREE.Object3D();
    -            var submesh = new THREE.Object3D();
    -            var ground = new THREE.Mesh( geometry, this.currentMaterial );
    -            ground.scale.set(100, 100, 100);
    -            submesh.add(ground);
    -
    -            ground.castShadow = true;
    -            ground.receiveShadow = true;
    -
    -            mesh.add(submesh);
    -            break;
    -
    -        case CANNON.Shape.types.BOX:
    -            var box_geometry = new THREE.BoxGeometry(  shape.halfExtents.x*2,
    -                                                        shape.halfExtents.y*2,
    -                                                        shape.halfExtents.z*2 );
    -            mesh = new THREE.Mesh( box_geometry, this.currentMaterial );
    -            break;
    -
    -        case CANNON.Shape.types.CONVEXPOLYHEDRON:
    -            var geo = new THREE.Geometry();
    -
    -            // Add vertices
    -            for (var i = 0; i < shape.vertices.length; i++) {
    -                var v = shape.vertices[i];
    -                geo.vertices.push(new THREE.Vector3(v.x, v.y, v.z));
    -            }
    -
    -            for(var i=0; i < shape.faces.length; i++){
    -                var face = shape.faces[i];
    -
    -                // add triangles
    -                var a = face[0];
    -                for (var j = 1; j < face.length - 1; j++) {
    -                    var b = face[j];
    -                    var c = face[j + 1];
    -                    geo.faces.push(new THREE.Face3(a, b, c));
    -                }
    -            }
    -            geo.computeBoundingSphere();
    -            geo.computeFaceNormals();
    -            mesh = new THREE.Mesh( geo, this.currentMaterial );
    -            break;
    -
    -        case CANNON.Shape.types.HEIGHTFIELD:
    -            var geometry = new THREE.Geometry();
    -
    -            var v0 = new CANNON.Vec3();
    -            var v1 = new CANNON.Vec3();
    -            var v2 = new CANNON.Vec3();
    -            for (var xi = 0; xi < shape.data.length - 1; xi++) {
    -                for (var yi = 0; yi < shape.data[xi].length - 1; yi++) {
    -                    for (var k = 0; k < 2; k++) {
    -                        shape.getConvexTrianglePillar(xi, yi, k===0);
    -                        v0.copy(shape.pillarConvex.vertices[0]);
    -                        v1.copy(shape.pillarConvex.vertices[1]);
    -                        v2.copy(shape.pillarConvex.vertices[2]);
    -                        v0.vadd(shape.pillarOffset, v0);
    -                        v1.vadd(shape.pillarOffset, v1);
    -                        v2.vadd(shape.pillarOffset, v2);
    -                        geometry.vertices.push(
    -                            new THREE.Vector3(v0.x, v0.y, v0.z),
    -                            new THREE.Vector3(v1.x, v1.y, v1.z),
    -                            new THREE.Vector3(v2.x, v2.y, v2.z)
    -                        );
    -                        var i = geometry.vertices.length - 3;
    -                        geometry.faces.push(new THREE.Face3(i, i+1, i+2));
    -                    }
    -                }
    -            }
    -            geometry.computeBoundingSphere();
    -            geometry.computeFaceNormals();
    -            mesh = new THREE.Mesh(geometry, this.currentMaterial);
    -            break;
    -
    -        case CANNON.Shape.types.TRIMESH:
    -            var geometry = new THREE.Geometry();
    -
    -            var v0 = new CANNON.Vec3();
    -            var v1 = new CANNON.Vec3();
    -            var v2 = new CANNON.Vec3();
    -            for (var i = 0; i < shape.indices.length / 3; i++) {
    -                shape.getTriangleVertices(i, v0, v1, v2);
    -                geometry.vertices.push(
    -                    new THREE.Vector3(v0.x, v0.y, v0.z),
    -                    new THREE.Vector3(v1.x, v1.y, v1.z),
    -                    new THREE.Vector3(v2.x, v2.y, v2.z)
    -                );
    -                var j = geometry.vertices.length - 3;
    -                geometry.faces.push(new THREE.Face3(j, j+1, j+2));
    -            }
    -            geometry.computeBoundingSphere();
    -            geometry.computeFaceNormals();
    -            mesh = new THREE.Mesh(geometry, this.currentMaterial);
    -            break;
    -
    -        default:
    -            throw "Visual type not recognized: "+shape.type;
    -        }
    -
    -        mesh.receiveShadow = true;
    -        mesh.castShadow = true;
    -        if(mesh.children){
    -            for(var i=0; i<mesh.children.length; i++){
    -                mesh.children[i].castShadow = true;
    -                mesh.children[i].receiveShadow = true;
    -                if(mesh.children[i]){
    -                    for(var j=0; j<mesh.children[i].length; j++){
    -                        mesh.children[i].children[j].castShadow = true;
    -                        mesh.children[i].children[j].receiveShadow = true;
    -                    }
    -                }
    -            }
    -        }
    -
    -        var o = body.shapeOffsets[l];
    -        var q = body.shapeOrientations[l];
    -        mesh.position.set(o.x, o.y, o.z);
    -        mesh.quaternion.set(q.x, q.y, q.z, q.w);
    -
    -        obj.add(mesh);
    -    }
    -
    -    return obj;
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_equations_ConeEquation.js.html b/docs/files/src_equations_ConeEquation.js.html deleted file mode 100644 index 5006abcfc..000000000 --- a/docs/files/src_equations_ConeEquation.js.html +++ /dev/null @@ -1,231 +0,0 @@ - - - - - src/equations/ConeEquation.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/equations/ConeEquation.js

    - -
    -
    -module.exports = ConeEquation;
    -
    -var Vec3 = require('../math/Vec3');
    -var Mat3 = require('../math/Mat3');
    -var Equation = require('./Equation');
    -
    -/**
    - * Cone equation. Works to keep the given body world vectors aligned, or tilted within a given angle from each other.
    - * @class ConeEquation
    - * @constructor
    - * @author schteppe
    - * @param {Body} bodyA
    - * @param {Body} bodyB
    - * @param {Vec3} [options.axisA] Local axis in A
    - * @param {Vec3} [options.axisB] Local axis in B
    - * @param {Vec3} [options.angle] The "cone angle" to keep
    - * @param {number} [options.maxForce=1e6]
    - * @extends Equation
    - */
    -function ConeEquation(bodyA, bodyB, options){
    -    options = options || {};
    -    var maxForce = typeof(options.maxForce) !== 'undefined' ? options.maxForce : 1e6;
    -
    -    Equation.call(this,bodyA,bodyB,-maxForce, maxForce);
    -
    -    this.axisA = options.axisA ? options.axisA.clone() : new Vec3(1, 0, 0);
    -    this.axisB = options.axisB ? options.axisB.clone() : new Vec3(0, 1, 0);
    -
    -    /**
    -     * The cone angle to keep
    -     * @property {number} angle
    -     */
    -    this.angle = typeof(options.angle) !== 'undefined' ? options.angle : 0;
    -}
    -
    -ConeEquation.prototype = new Equation();
    -ConeEquation.prototype.constructor = ConeEquation;
    -
    -var tmpVec1 = new Vec3();
    -var tmpVec2 = new Vec3();
    -
    -ConeEquation.prototype.computeB = function(h){
    -    var a = this.a,
    -        b = this.b,
    -
    -        ni = this.axisA,
    -        nj = this.axisB,
    -
    -        nixnj = tmpVec1,
    -        njxni = tmpVec2,
    -
    -        GA = this.jacobianElementA,
    -        GB = this.jacobianElementB;
    -
    -    // Caluclate cross products
    -    ni.cross(nj, nixnj);
    -    nj.cross(ni, njxni);
    -
    -    // The angle between two vector is:
    -    // cos(theta) = a * b / (length(a) * length(b) = { len(a) = len(b) = 1 } = a * b
    -
    -    // g = a * b
    -    // gdot = (b x a) * wi + (a x b) * wj
    -    // G = [0 bxa 0 axb]
    -    // W = [vi wi vj wj]
    -    GA.rotational.copy(njxni);
    -    GB.rotational.copy(nixnj);
    -
    -    var g = Math.cos(this.angle) - ni.dot(nj),
    -        GW = this.computeGW(),
    -        GiMf = this.computeGiMf();
    -
    -    var B = - g * a - GW * b - h * GiMf;
    -
    -    return B;
    -};
    -
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_equations_ContactEquation.js.html b/docs/files/src_equations_ContactEquation.js.html deleted file mode 100644 index ffeac6af5..000000000 --- a/docs/files/src_equations_ContactEquation.js.html +++ /dev/null @@ -1,289 +0,0 @@ - - - - - src/equations/ContactEquation.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/equations/ContactEquation.js

    - -
    -
    -module.exports = ContactEquation;
    -
    -var Equation = require('./Equation');
    -var Vec3 = require('../math/Vec3');
    -var Mat3 = require('../math/Mat3');
    -
    -/**
    - * Contact/non-penetration constraint equation
    - * @class ContactEquation
    - * @constructor
    - * @author schteppe
    - * @param {Body} bodyA
    - * @param {Body} bodyB
    - * @extends Equation
    - */
    -function ContactEquation(bodyA, bodyB, maxForce){
    -    maxForce = typeof(maxForce) !== 'undefined' ? maxForce : 1e6;
    -    Equation.call(this, bodyA, bodyB, 0, maxForce);
    -
    -    /**
    -     * @property restitution
    -     * @type {Number}
    -     */
    -    this.restitution = 0.0; // "bounciness": u1 = -e*u0
    -
    -    /**
    -     * World-oriented vector that goes from the center of bi to the contact point.
    -     * @property {Vec3} ri
    -     */
    -    this.ri = new Vec3();
    -
    -    /**
    -     * World-oriented vector that starts in body j position and goes to the contact point.
    -     * @property {Vec3} rj
    -     */
    -    this.rj = new Vec3();
    -
    -    /**
    -     * Contact normal, pointing out of body i.
    -     * @property {Vec3} ni
    -     */
    -    this.ni = new Vec3();
    -}
    -
    -ContactEquation.prototype = new Equation();
    -ContactEquation.prototype.constructor = ContactEquation;
    -
    -var ContactEquation_computeB_temp1 = new Vec3(); // Temp vectors
    -var ContactEquation_computeB_temp2 = new Vec3();
    -var ContactEquation_computeB_temp3 = new Vec3();
    -ContactEquation.prototype.computeB = function(h){
    -    var a = this.a,
    -        b = this.b,
    -        bi = this.bi,
    -        bj = this.bj,
    -        ri = this.ri,
    -        rj = this.rj,
    -        rixn = ContactEquation_computeB_temp1,
    -        rjxn = ContactEquation_computeB_temp2,
    -
    -        vi = bi.velocity,
    -        wi = bi.angularVelocity,
    -        fi = bi.force,
    -        taui = bi.torque,
    -
    -        vj = bj.velocity,
    -        wj = bj.angularVelocity,
    -        fj = bj.force,
    -        tauj = bj.torque,
    -
    -        penetrationVec = ContactEquation_computeB_temp3,
    -
    -        GA = this.jacobianElementA,
    -        GB = this.jacobianElementB,
    -
    -        n = this.ni;
    -
    -    // Caluclate cross products
    -    ri.cross(n,rixn);
    -    rj.cross(n,rjxn);
    -
    -    // g = xj+rj -(xi+ri)
    -    // G = [ -ni  -rixn  ni  rjxn ]
    -    n.negate(GA.spatial);
    -    rixn.negate(GA.rotational);
    -    GB.spatial.copy(n);
    -    GB.rotational.copy(rjxn);
    -
    -    // Calculate the penetration vector
    -    penetrationVec.copy(bj.position);
    -    penetrationVec.vadd(rj,penetrationVec);
    -    penetrationVec.vsub(bi.position,penetrationVec);
    -    penetrationVec.vsub(ri,penetrationVec);
    -
    -    var g = n.dot(penetrationVec);
    -
    -    // Compute iteration
    -    var ePlusOne = this.restitution + 1;
    -    var GW = ePlusOne * vj.dot(n) - ePlusOne * vi.dot(n) + wj.dot(rjxn) - wi.dot(rixn);
    -    var GiMf = this.computeGiMf();
    -
    -    var B = - g * a - GW * b - h*GiMf;
    -
    -    return B;
    -};
    -
    -var ContactEquation_getImpactVelocityAlongNormal_vi = new Vec3();
    -var ContactEquation_getImpactVelocityAlongNormal_vj = new Vec3();
    -var ContactEquation_getImpactVelocityAlongNormal_xi = new Vec3();
    -var ContactEquation_getImpactVelocityAlongNormal_xj = new Vec3();
    -var ContactEquation_getImpactVelocityAlongNormal_relVel = new Vec3();
    -
    -/**
    - * Get the current relative velocity in the contact point.
    - * @method getImpactVelocityAlongNormal
    - * @return {number}
    - */
    -ContactEquation.prototype.getImpactVelocityAlongNormal = function(){
    -    var vi = ContactEquation_getImpactVelocityAlongNormal_vi;
    -    var vj = ContactEquation_getImpactVelocityAlongNormal_vj;
    -    var xi = ContactEquation_getImpactVelocityAlongNormal_xi;
    -    var xj = ContactEquation_getImpactVelocityAlongNormal_xj;
    -    var relVel = ContactEquation_getImpactVelocityAlongNormal_relVel;
    -
    -    this.bi.position.vadd(this.ri, xi);
    -    this.bj.position.vadd(this.rj, xj);
    -
    -    this.bi.getVelocityAtWorldPoint(xi, vi);
    -    this.bj.getVelocityAtWorldPoint(xj, vj);
    -
    -    vi.vsub(vj, relVel);
    -
    -    return this.ni.dot(relVel);
    -};
    -
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_equations_Equation.js.html b/docs/files/src_equations_Equation.js.html deleted file mode 100644 index 8022b1b8f..000000000 --- a/docs/files/src_equations_Equation.js.html +++ /dev/null @@ -1,424 +0,0 @@ - - - - - src/equations/Equation.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/equations/Equation.js

    - -
    -
    -module.exports = Equation;
    -
    -var JacobianElement = require('../math/JacobianElement'),
    -    Vec3 = require('../math/Vec3');
    -
    -/**
    - * Equation base class
    - * @class Equation
    - * @constructor
    - * @author schteppe
    - * @param {Body} bi
    - * @param {Body} bj
    - * @param {Number} minForce Minimum (read: negative max) force to be applied by the constraint.
    - * @param {Number} maxForce Maximum (read: positive max) force to be applied by the constraint.
    - */
    -function Equation(bi,bj,minForce,maxForce){
    -    this.id = Equation.id++;
    -
    -    /**
    -     * @property {number} minForce
    -     */
    -    this.minForce = typeof(minForce)==="undefined" ? -1e6 : minForce;
    -
    -    /**
    -     * @property {number} maxForce
    -     */
    -    this.maxForce = typeof(maxForce)==="undefined" ? 1e6 : maxForce;
    -
    -    /**
    -     * @property bi
    -     * @type {Body}
    -     */
    -    this.bi = bi;
    -
    -    /**
    -     * @property bj
    -     * @type {Body}
    -     */
    -    this.bj = bj;
    -
    -    /**
    -     * SPOOK parameter
    -     * @property {number} a
    -     */
    -    this.a = 0.0;
    -
    -    /**
    -     * SPOOK parameter
    -     * @property {number} b
    -     */
    -    this.b = 0.0;
    -
    -    /**
    -     * SPOOK parameter
    -     * @property {number} eps
    -     */
    -    this.eps = 0.0;
    -
    -    /**
    -     * @property {JacobianElement} jacobianElementA
    -     */
    -    this.jacobianElementA = new JacobianElement();
    -
    -    /**
    -     * @property {JacobianElement} jacobianElementB
    -     */
    -    this.jacobianElementB = new JacobianElement();
    -
    -    /**
    -     * @property {boolean} enabled
    -     * @default true
    -     */
    -    this.enabled = true;
    -
    -    // Set typical spook params
    -    this.setSpookParams(1e7,4,1/60);
    -}
    -Equation.prototype.constructor = Equation;
    -
    -Equation.id = 0;
    -
    -/**
    - * Recalculates a,b,eps.
    - * @method setSpookParams
    - */
    -Equation.prototype.setSpookParams = function(stiffness,relaxation,timeStep){
    -    var d = relaxation,
    -        k = stiffness,
    -        h = timeStep;
    -    this.a = 4.0 / (h * (1 + 4 * d));
    -    this.b = (4.0 * d) / (1 + 4 * d);
    -    this.eps = 4.0 / (h * h * k * (1 + 4 * d));
    -};
    -
    -/**
    - * Computes the RHS of the SPOOK equation
    - * @method computeB
    - * @return {Number}
    - */
    -Equation.prototype.computeB = function(a,b,h){
    -    var GW = this.computeGW(),
    -        Gq = this.computeGq(),
    -        GiMf = this.computeGiMf();
    -    return - Gq * a - GW * b - GiMf*h;
    -};
    -
    -/**
    - * Computes G*q, where q are the generalized body coordinates
    - * @method computeGq
    - * @return {Number}
    - */
    -Equation.prototype.computeGq = function(){
    -    var GA = this.jacobianElementA,
    -        GB = this.jacobianElementB,
    -        bi = this.bi,
    -        bj = this.bj,
    -        xi = bi.position,
    -        xj = bj.position;
    -    return GA.spatial.dot(xi) + GB.spatial.dot(xj);
    -};
    -
    -var zero = new Vec3();
    -
    -/**
    - * Computes G*W, where W are the body velocities
    - * @method computeGW
    - * @return {Number}
    - */
    -Equation.prototype.computeGW = function(){
    -    var GA = this.jacobianElementA,
    -        GB = this.jacobianElementB,
    -        bi = this.bi,
    -        bj = this.bj,
    -        vi = bi.velocity,
    -        vj = bj.velocity,
    -        wi = bi.angularVelocity || zero,
    -        wj = bj.angularVelocity || zero;
    -    return GA.multiplyVectors(vi,wi) + GB.multiplyVectors(vj,wj);
    -};
    -
    -
    -/**
    - * Computes G*Wlambda, where W are the body velocities
    - * @method computeGWlambda
    - * @return {Number}
    - */
    -Equation.prototype.computeGWlambda = function(){
    -    var GA = this.jacobianElementA,
    -        GB = this.jacobianElementB,
    -        bi = this.bi,
    -        bj = this.bj,
    -        vi = bi.vlambda,
    -        vj = bj.vlambda,
    -        wi = bi.wlambda || zero,
    -        wj = bj.wlambda || zero;
    -    return GA.multiplyVectors(vi,wi) + GB.multiplyVectors(vj,wj);
    -};
    -
    -/**
    - * Computes G*inv(M)*f, where M is the mass matrix with diagonal blocks for each body, and f are the forces on the bodies.
    - * @method computeGiMf
    - * @return {Number}
    - */
    -var iMfi = new Vec3(),
    -    iMfj = new Vec3(),
    -    invIi_vmult_taui = new Vec3(),
    -    invIj_vmult_tauj = new Vec3();
    -Equation.prototype.computeGiMf = function(){
    -    var GA = this.jacobianElementA,
    -        GB = this.jacobianElementB,
    -        bi = this.bi,
    -        bj = this.bj,
    -        fi = bi.force,
    -        ti = bi.torque,
    -        fj = bj.force,
    -        tj = bj.torque,
    -        invMassi = bi.invMassSolve,
    -        invMassj = bj.invMassSolve;
    -
    -    if(bi.invInertiaWorldSolve){ bi.invInertiaWorldSolve.vmult(ti,invIi_vmult_taui); }
    -    else { invIi_vmult_taui.set(0,0,0); }
    -    if(bj.invInertiaWorldSolve){ bj.invInertiaWorldSolve.vmult(tj,invIj_vmult_tauj); }
    -    else { invIj_vmult_tauj.set(0,0,0); }
    -
    -    fi.mult(invMassi,iMfi);
    -    fj.mult(invMassj,iMfj);
    -
    -    return GA.multiplyVectors(iMfi,invIi_vmult_taui) + GB.multiplyVectors(iMfj,invIj_vmult_tauj);
    -};
    -
    -/**
    - * Computes G*inv(M)*G'
    - * @method computeGiMGt
    - * @return {Number}
    - */
    -var tmp = new Vec3();
    -Equation.prototype.computeGiMGt = function(){
    -    var GA = this.jacobianElementA,
    -        GB = this.jacobianElementB,
    -        bi = this.bi,
    -        bj = this.bj,
    -        invMassi = bi.invMassSolve,
    -        invMassj = bj.invMassSolve,
    -        invIi = bi.invInertiaWorldSolve,
    -        invIj = bj.invInertiaWorldSolve,
    -        result = invMassi + invMassj;
    -
    -    if(invIi){
    -        invIi.vmult(GA.rotational,tmp);
    -        result += tmp.dot(GA.rotational);
    -    }
    -
    -    if(invIj){
    -        invIj.vmult(GB.rotational,tmp);
    -        result += tmp.dot(GB.rotational);
    -    }
    -
    -    return  result;
    -};
    -
    -var addToWlambda_temp = new Vec3(),
    -    addToWlambda_Gi = new Vec3(),
    -    addToWlambda_Gj = new Vec3(),
    -    addToWlambda_ri = new Vec3(),
    -    addToWlambda_rj = new Vec3(),
    -    addToWlambda_Mdiag = new Vec3();
    -
    -/**
    - * Add constraint velocity to the bodies.
    - * @method addToWlambda
    - * @param {Number} deltalambda
    - */
    -Equation.prototype.addToWlambda = function(deltalambda){
    -    var GA = this.jacobianElementA,
    -        GB = this.jacobianElementB,
    -        bi = this.bi,
    -        bj = this.bj,
    -        temp = addToWlambda_temp;
    -
    -    // Add to linear velocity
    -    // v_lambda += inv(M) * delta_lamba * G
    -    GA.spatial.mult(bi.invMassSolve * deltalambda,temp);
    -    bi.vlambda.vadd(temp, bi.vlambda);
    -
    -    GB.spatial.mult(bj.invMassSolve * deltalambda,temp);
    -    bj.vlambda.vadd(temp, bj.vlambda);
    -
    -    // Add to angular velocity
    -    if(bi.invInertiaWorldSolve){
    -        bi.invInertiaWorldSolve.vmult(GA.rotational,temp);
    -        temp.mult(deltalambda,temp);
    -        bi.wlambda.vadd(temp,bi.wlambda);
    -    }
    -
    -    if(bj.invInertiaWorldSolve){
    -        bj.invInertiaWorldSolve.vmult(GB.rotational,temp);
    -        temp.mult(deltalambda,temp);
    -        bj.wlambda.vadd(temp,bj.wlambda);
    -    }
    -};
    -
    -/**
    - * Compute the denominator part of the SPOOK equation: C = G*inv(M)*G' + eps
    - * @method computeInvC
    - * @param  {Number} eps
    - * @return {Number}
    - */
    -Equation.prototype.computeC = function(){
    -    return this.computeGiMGt() + this.eps;
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_equations_FrictionEquation.js.html b/docs/files/src_equations_FrictionEquation.js.html deleted file mode 100644 index 84eee1015..000000000 --- a/docs/files/src_equations_FrictionEquation.js.html +++ /dev/null @@ -1,213 +0,0 @@ - - - - - src/equations/FrictionEquation.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/equations/FrictionEquation.js

    - -
    -
    -module.exports = FrictionEquation;
    -
    -var Equation = require('./Equation');
    -var Vec3 = require('../math/Vec3');
    -var Mat3 = require('../math/Mat3');
    -
    -/**
    - * Constrains the slipping in a contact along a tangent
    - * @class FrictionEquation
    - * @constructor
    - * @author schteppe
    - * @param {Body} bodyA
    - * @param {Body} bodyB
    - * @param {Number} slipForce should be +-F_friction = +-mu * F_normal = +-mu * m * g
    - * @extends Equation
    - */
    -function FrictionEquation(bodyA, bodyB, slipForce){
    -    Equation.call(this,bodyA, bodyB, -slipForce, slipForce);
    -    this.ri = new Vec3();
    -    this.rj = new Vec3();
    -    this.t = new Vec3(); // tangent
    -}
    -
    -FrictionEquation.prototype = new Equation();
    -FrictionEquation.prototype.constructor = FrictionEquation;
    -
    -var FrictionEquation_computeB_temp1 = new Vec3();
    -var FrictionEquation_computeB_temp2 = new Vec3();
    -FrictionEquation.prototype.computeB = function(h){
    -    var a = this.a,
    -        b = this.b,
    -        bi = this.bi,
    -        bj = this.bj,
    -        ri = this.ri,
    -        rj = this.rj,
    -        rixt = FrictionEquation_computeB_temp1,
    -        rjxt = FrictionEquation_computeB_temp2,
    -        t = this.t;
    -
    -    // Caluclate cross products
    -    ri.cross(t,rixt);
    -    rj.cross(t,rjxt);
    -
    -    // G = [-t -rixt t rjxt]
    -    // And remember, this is a pure velocity constraint, g is always zero!
    -    var GA = this.jacobianElementA,
    -        GB = this.jacobianElementB;
    -    t.negate(GA.spatial);
    -    rixt.negate(GA.rotational);
    -    GB.spatial.copy(t);
    -    GB.rotational.copy(rjxt);
    -
    -    var GW = this.computeGW();
    -    var GiMf = this.computeGiMf();
    -
    -    var B = - GW * b - h * GiMf;
    -
    -    return B;
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_equations_RotationalEquation.js.html b/docs/files/src_equations_RotationalEquation.js.html deleted file mode 100644 index 1a182a8a3..000000000 --- a/docs/files/src_equations_RotationalEquation.js.html +++ /dev/null @@ -1,223 +0,0 @@ - - - - - src/equations/RotationalEquation.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/equations/RotationalEquation.js

    - -
    -
    -module.exports = RotationalEquation;
    -
    -var Vec3 = require('../math/Vec3');
    -var Mat3 = require('../math/Mat3');
    -var Equation = require('./Equation');
    -
    -/**
    - * Rotational constraint. Works to keep the local vectors orthogonal to each other in world space.
    - * @class RotationalEquation
    - * @constructor
    - * @author schteppe
    - * @param {Body} bodyA
    - * @param {Body} bodyB
    - * @param {Vec3} [options.axisA]
    - * @param {Vec3} [options.axisB]
    - * @param {number} [options.maxForce]
    - * @extends Equation
    - */
    -function RotationalEquation(bodyA, bodyB, options){
    -    options = options || {};
    -    var maxForce = typeof(options.maxForce) !== 'undefined' ? options.maxForce : 1e6;
    -
    -    Equation.call(this,bodyA,bodyB,-maxForce, maxForce);
    -
    -    this.axisA = options.axisA ? options.axisA.clone() : new Vec3(1, 0, 0);
    -    this.axisB = options.axisB ? options.axisB.clone() : new Vec3(0, 1, 0);
    -
    -    this.maxAngle = Math.PI / 2;
    -}
    -
    -RotationalEquation.prototype = new Equation();
    -RotationalEquation.prototype.constructor = RotationalEquation;
    -
    -var tmpVec1 = new Vec3();
    -var tmpVec2 = new Vec3();
    -
    -RotationalEquation.prototype.computeB = function(h){
    -    var a = this.a,
    -        b = this.b,
    -
    -        ni = this.axisA,
    -        nj = this.axisB,
    -
    -        nixnj = tmpVec1,
    -        njxni = tmpVec2,
    -
    -        GA = this.jacobianElementA,
    -        GB = this.jacobianElementB;
    -
    -    // Caluclate cross products
    -    ni.cross(nj, nixnj);
    -    nj.cross(ni, njxni);
    -
    -    // g = ni * nj
    -    // gdot = (nj x ni) * wi + (ni x nj) * wj
    -    // G = [0 njxni 0 nixnj]
    -    // W = [vi wi vj wj]
    -    GA.rotational.copy(njxni);
    -    GB.rotational.copy(nixnj);
    -
    -    var g = Math.cos(this.maxAngle) - ni.dot(nj),
    -        GW = this.computeGW(),
    -        GiMf = this.computeGiMf();
    -
    -    var B = - g * a - GW * b - h * GiMf;
    -
    -    return B;
    -};
    -
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_equations_RotationalMotorEquation.js.html b/docs/files/src_equations_RotationalMotorEquation.js.html deleted file mode 100644 index 3161316ef..000000000 --- a/docs/files/src_equations_RotationalMotorEquation.js.html +++ /dev/null @@ -1,224 +0,0 @@ - - - - - src/equations/RotationalMotorEquation.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/equations/RotationalMotorEquation.js

    - -
    -
    -module.exports = RotationalMotorEquation;
    -
    -var Vec3 = require('../math/Vec3');
    -var Mat3 = require('../math/Mat3');
    -var Equation = require('./Equation');
    -
    -/**
    - * Rotational motor constraint. Tries to keep the relative angular velocity of the bodies to a given value.
    - * @class RotationalMotorEquation
    - * @constructor
    - * @author schteppe
    - * @param {Body} bodyA
    - * @param {Body} bodyB
    - * @param {Number} maxForce
    - * @extends Equation
    - */
    -function RotationalMotorEquation(bodyA, bodyB, maxForce){
    -    maxForce = typeof(maxForce)!=='undefined' ? maxForce : 1e6;
    -    Equation.call(this,bodyA,bodyB,-maxForce,maxForce);
    -
    -    /**
    -     * World oriented rotational axis
    -     * @property {Vec3} axisA
    -     */
    -    this.axisA = new Vec3();
    -
    -    /**
    -     * World oriented rotational axis
    -     * @property {Vec3} axisB
    -     */
    -    this.axisB = new Vec3(); // World oriented rotational axis
    -
    -    /**
    -     * Motor velocity
    -     * @property {Number} targetVelocity
    -     */
    -    this.targetVelocity = 0;
    -}
    -
    -RotationalMotorEquation.prototype = new Equation();
    -RotationalMotorEquation.prototype.constructor = RotationalMotorEquation;
    -
    -RotationalMotorEquation.prototype.computeB = function(h){
    -    var a = this.a,
    -        b = this.b,
    -        bi = this.bi,
    -        bj = this.bj,
    -
    -        axisA = this.axisA,
    -        axisB = this.axisB,
    -
    -        GA = this.jacobianElementA,
    -        GB = this.jacobianElementB;
    -
    -    // g = 0
    -    // gdot = axisA * wi - axisB * wj
    -    // gdot = G * W = G * [vi wi vj wj]
    -    // =>
    -    // G = [0 axisA 0 -axisB]
    -
    -    GA.rotational.copy(axisA);
    -    axisB.negate(GB.rotational);
    -
    -    var GW = this.computeGW() - this.targetVelocity,
    -        GiMf = this.computeGiMf();
    -
    -    var B = - GW * b - h * GiMf;
    -
    -    return B;
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_material_ContactMaterial.js.html b/docs/files/src_material_ContactMaterial.js.html deleted file mode 100644 index 2c6a1a277..000000000 --- a/docs/files/src_material_ContactMaterial.js.html +++ /dev/null @@ -1,233 +0,0 @@ - - - - - src/material/ContactMaterial.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/material/ContactMaterial.js

    - -
    -
    -var Utils = require('../utils/Utils');
    -
    -module.exports = ContactMaterial;
    -
    -/**
    - * Defines what happens when two materials meet.
    - * @class ContactMaterial
    - * @constructor
    - * @param {Material} m1
    - * @param {Material} m2
    - * @param {object} [options]
    - * @param {Number} [options.friction=0.3]
    - * @param {Number} [options.restitution=0.3]
    - * @param {number} [options.contactEquationStiffness=1e7]
    - * @param {number} [options.contactEquationRelaxation=3]
    - * @param {number} [options.frictionEquationStiffness=1e7]
    - * @param {Number} [options.frictionEquationRelaxation=3]
    - */
    -function ContactMaterial(m1, m2, options){
    -    options = Utils.defaults(options, {
    -        friction: 0.3,
    -        restitution: 0.3,
    -        contactEquationStiffness: 1e7,
    -        contactEquationRelaxation: 3,
    -        frictionEquationStiffness: 1e7,
    -        frictionEquationRelaxation: 3
    -    });
    -
    -    /**
    -     * Identifier of this material
    -     * @property {Number} id
    -     */
    -    this.id = ContactMaterial.idCounter++;
    -
    -    /**
    -     * Participating materials
    -     * @property {Array} materials
    -     * @todo  Should be .materialA and .materialB instead
    -     */
    -    this.materials = [m1, m2];
    -
    -    /**
    -     * Friction coefficient
    -     * @property {Number} friction
    -     */
    -    this.friction = options.friction;
    -
    -    /**
    -     * Restitution coefficient
    -     * @property {Number} restitution
    -     */
    -    this.restitution = options.restitution;
    -
    -    /**
    -     * Stiffness of the produced contact equations
    -     * @property {Number} contactEquationStiffness
    -     */
    -    this.contactEquationStiffness = options.contactEquationStiffness;
    -
    -    /**
    -     * Relaxation time of the produced contact equations
    -     * @property {Number} contactEquationRelaxation
    -     */
    -    this.contactEquationRelaxation = options.contactEquationRelaxation;
    -
    -    /**
    -     * Stiffness of the produced friction equations
    -     * @property {Number} frictionEquationStiffness
    -     */
    -    this.frictionEquationStiffness = options.frictionEquationStiffness;
    -
    -    /**
    -     * Relaxation time of the produced friction equations
    -     * @property {Number} frictionEquationRelaxation
    -     */
    -    this.frictionEquationRelaxation = options.frictionEquationRelaxation;
    -}
    -
    -ContactMaterial.idCounter = 0;
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_material_Material.js.html b/docs/files/src_material_Material.js.html deleted file mode 100644 index 6b6833915..000000000 --- a/docs/files/src_material_Material.js.html +++ /dev/null @@ -1,202 +0,0 @@ - - - - - src/material/Material.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/material/Material.js

    - -
    -
    -module.exports = Material;
    -
    -/**
    - * Defines a physics material.
    - * @class Material
    - * @constructor
    - * @param {object} [options]
    - * @author schteppe
    - */
    -function Material(options){
    -    var name = '';
    -    options = options || {};
    -
    -    // Backwards compatibility fix
    -    if(typeof(options) === 'string'){
    -        name = options;
    -        options = {};
    -    } else if(typeof(options) === 'object') {
    -        name = '';
    -    }
    -
    -    /**
    -     * @property name
    -     * @type {String}
    -     */
    -    this.name = name;
    -
    -    /**
    -     * material id.
    -     * @property id
    -     * @type {number}
    -     */
    -    this.id = Material.idCounter++;
    -
    -    /**
    -     * Friction for this material. If non-negative, it will be used instead of the friction given by ContactMaterials. If there's no matching ContactMaterial, the value from .defaultContactMaterial in the World will be used.
    -     * @property {number} friction
    -     */
    -    this.friction = typeof(options.friction) !== 'undefined' ? options.friction : -1;
    -
    -    /**
    -     * Restitution for this material. If non-negative, it will be used instead of the restitution given by ContactMaterials. If there's no matching ContactMaterial, the value from .defaultContactMaterial in the World will be used.
    -     * @property {number} restitution
    -     */
    -    this.restitution = typeof(options.restitution) !== 'undefined' ? options.restitution : -1;
    -}
    -
    -Material.idCounter = 0;
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_math_JacobianElement.js.html b/docs/files/src_math_JacobianElement.js.html deleted file mode 100644 index ec838237b..000000000 --- a/docs/files/src_math_JacobianElement.js.html +++ /dev/null @@ -1,196 +0,0 @@ - - - - - src/math/JacobianElement.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/math/JacobianElement.js

    - -
    -
    -module.exports = JacobianElement;
    -
    -var Vec3 = require('./Vec3');
    -
    -/**
    - * An element containing 6 entries, 3 spatial and 3 rotational degrees of freedom.
    - * @class JacobianElement
    - * @constructor
    - */
    -function JacobianElement(){
    -
    -    /**
    -     * @property {Vec3} spatial
    -     */
    -    this.spatial = new Vec3();
    -
    -    /**
    -     * @property {Vec3} rotational
    -     */
    -    this.rotational = new Vec3();
    -}
    -
    -/**
    - * Multiply with other JacobianElement
    - * @method multiplyElement
    - * @param  {JacobianElement} element
    - * @return {Number}
    - */
    -JacobianElement.prototype.multiplyElement = function(element){
    -    return element.spatial.dot(this.spatial) + element.rotational.dot(this.rotational);
    -};
    -
    -/**
    - * Multiply with two vectors
    - * @method multiplyVectors
    - * @param  {Vec3} spatial
    - * @param  {Vec3} rotational
    - * @return {Number}
    - */
    -JacobianElement.prototype.multiplyVectors = function(spatial,rotational){
    -    return spatial.dot(this.spatial) + rotational.dot(this.rotational);
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_math_Mat3.js.html b/docs/files/src_math_Mat3.js.html deleted file mode 100644 index 69493d144..000000000 --- a/docs/files/src_math_Mat3.js.html +++ /dev/null @@ -1,576 +0,0 @@ - - - - - src/math/Mat3.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/math/Mat3.js

    - -
    -
    -module.exports = Mat3;
    -
    -var Vec3 = require('./Vec3');
    -
    -/**
    - * A 3x3 matrix.
    - * @class Mat3
    - * @constructor
    - * @param array elements Array of nine elements. Optional.
    - * @author schteppe / http://github.com/schteppe
    - */
    -function Mat3(elements){
    -    /**
    -     * A vector of length 9, containing all matrix elements
    -     * @property {Array} elements
    -     */
    -    if(elements){
    -        this.elements = elements;
    -    } else {
    -        this.elements = [0,0,0,0,0,0,0,0,0];
    -    }
    -}
    -
    -/**
    - * Sets the matrix to identity
    - * @method identity
    - * @todo Should perhaps be renamed to setIdentity() to be more clear.
    - * @todo Create another function that immediately creates an identity matrix eg. eye()
    - */
    -Mat3.prototype.identity = function(){
    -    var e = this.elements;
    -    e[0] = 1;
    -    e[1] = 0;
    -    e[2] = 0;
    -
    -    e[3] = 0;
    -    e[4] = 1;
    -    e[5] = 0;
    -
    -    e[6] = 0;
    -    e[7] = 0;
    -    e[8] = 1;
    -};
    -
    -/**
    - * Set all elements to zero
    - * @method setZero
    - */
    -Mat3.prototype.setZero = function(){
    -    var e = this.elements;
    -    e[0] = 0;
    -    e[1] = 0;
    -    e[2] = 0;
    -    e[3] = 0;
    -    e[4] = 0;
    -    e[5] = 0;
    -    e[6] = 0;
    -    e[7] = 0;
    -    e[8] = 0;
    -};
    -
    -/**
    - * Sets the matrix diagonal elements from a Vec3
    - * @method setTrace
    - * @param {Vec3} vec3
    - */
    -Mat3.prototype.setTrace = function(vec3){
    -    var e = this.elements;
    -    e[0] = vec3.x;
    -    e[4] = vec3.y;
    -    e[8] = vec3.z;
    -};
    -
    -/**
    - * Gets the matrix diagonal elements
    - * @method getTrace
    - * @return {Vec3}
    - */
    -Mat3.prototype.getTrace = function(target){
    -    var target = target || new Vec3();
    -    var e = this.elements;
    -    target.x = e[0];
    -    target.y = e[4];
    -    target.z = e[8];
    -};
    -
    -/**
    - * Matrix-Vector multiplication
    - * @method vmult
    - * @param {Vec3} v The vector to multiply with
    - * @param {Vec3} target Optional, target to save the result in.
    - */
    -Mat3.prototype.vmult = function(v,target){
    -    target = target || new Vec3();
    -
    -    var e = this.elements,
    -        x = v.x,
    -        y = v.y,
    -        z = v.z;
    -    target.x = e[0]*x + e[1]*y + e[2]*z;
    -    target.y = e[3]*x + e[4]*y + e[5]*z;
    -    target.z = e[6]*x + e[7]*y + e[8]*z;
    -
    -    return target;
    -};
    -
    -/**
    - * Matrix-scalar multiplication
    - * @method smult
    - * @param {Number} s
    - */
    -Mat3.prototype.smult = function(s){
    -    for(var i=0; i<this.elements.length; i++){
    -        this.elements[i] *= s;
    -    }
    -};
    -
    -/**
    - * Matrix multiplication
    - * @method mmult
    - * @param {Mat3} m Matrix to multiply with from left side.
    - * @return {Mat3} The result.
    - */
    -Mat3.prototype.mmult = function(m,target){
    -    var r = target || new Mat3();
    -    for(var i=0; i<3; i++){
    -        for(var j=0; j<3; j++){
    -            var sum = 0.0;
    -            for(var k=0; k<3; k++){
    -                sum += m.elements[i+k*3] * this.elements[k+j*3];
    -            }
    -            r.elements[i+j*3] = sum;
    -        }
    -    }
    -    return r;
    -};
    -
    -/**
    - * Scale each column of the matrix
    - * @method scale
    - * @param {Vec3} v
    - * @return {Mat3} The result.
    - */
    -Mat3.prototype.scale = function(v,target){
    -    target = target || new Mat3();
    -    var e = this.elements,
    -        t = target.elements;
    -    for(var i=0; i!==3; i++){
    -        t[3*i + 0] = v.x * e[3*i + 0];
    -        t[3*i + 1] = v.y * e[3*i + 1];
    -        t[3*i + 2] = v.z * e[3*i + 2];
    -    }
    -    return target;
    -};
    -
    -/**
    - * Solve Ax=b
    - * @method solve
    - * @param {Vec3} b The right hand side
    - * @param {Vec3} target Optional. Target vector to save in.
    - * @return {Vec3} The solution x
    - * @todo should reuse arrays
    - */
    -Mat3.prototype.solve = function(b,target){
    -    target = target || new Vec3();
    -
    -    // Construct equations
    -    var nr = 3; // num rows
    -    var nc = 4; // num cols
    -    var eqns = [];
    -    for(var i=0; i<nr*nc; i++){
    -        eqns.push(0);
    -    }
    -    var i,j;
    -    for(i=0; i<3; i++){
    -        for(j=0; j<3; j++){
    -            eqns[i+nc*j] = this.elements[i+3*j];
    -        }
    -    }
    -    eqns[3+4*0] = b.x;
    -    eqns[3+4*1] = b.y;
    -    eqns[3+4*2] = b.z;
    -
    -    // Compute right upper triangular version of the matrix - Gauss elimination
    -    var n = 3, k = n, np;
    -    var kp = 4; // num rows
    -    var p, els;
    -    do {
    -        i = k - n;
    -        if (eqns[i+nc*i] === 0) {
    -            // the pivot is null, swap lines
    -            for (j = i + 1; j < k; j++) {
    -                if (eqns[i+nc*j] !== 0) {
    -                    np = kp;
    -                    do {  // do ligne( i ) = ligne( i ) + ligne( k )
    -                        p = kp - np;
    -                        eqns[p+nc*i] += eqns[p+nc*j];
    -                    } while (--np);
    -                    break;
    -                }
    -            }
    -        }
    -        if (eqns[i+nc*i] !== 0) {
    -            for (j = i + 1; j < k; j++) {
    -                var multiplier = eqns[i+nc*j] / eqns[i+nc*i];
    -                np = kp;
    -                do {  // do ligne( k ) = ligne( k ) - multiplier * ligne( i )
    -                    p = kp - np;
    -                    eqns[p+nc*j] = p <= i ? 0 : eqns[p+nc*j] - eqns[p+nc*i] * multiplier ;
    -                } while (--np);
    -            }
    -        }
    -    } while (--n);
    -
    -    // Get the solution
    -    target.z = eqns[2*nc+3] / eqns[2*nc+2];
    -    target.y = (eqns[1*nc+3] - eqns[1*nc+2]*target.z) / eqns[1*nc+1];
    -    target.x = (eqns[0*nc+3] - eqns[0*nc+2]*target.z - eqns[0*nc+1]*target.y) / eqns[0*nc+0];
    -
    -    if(isNaN(target.x) || isNaN(target.y) || isNaN(target.z) || target.x===Infinity || target.y===Infinity || target.z===Infinity){
    -        throw "Could not solve equation! Got x=["+target.toString()+"], b=["+b.toString()+"], A=["+this.toString()+"]";
    -    }
    -
    -    return target;
    -};
    -
    -/**
    - * Get an element in the matrix by index. Index starts at 0, not 1!!!
    - * @method e
    - * @param {Number} row
    - * @param {Number} column
    - * @param {Number} value Optional. If provided, the matrix element will be set to this value.
    - * @return {Number}
    - */
    -Mat3.prototype.e = function( row , column ,value){
    -    if(value===undefined){
    -        return this.elements[column+3*row];
    -    } else {
    -        // Set value
    -        this.elements[column+3*row] = value;
    -    }
    -};
    -
    -/**
    - * Copy another matrix into this matrix object.
    - * @method copy
    - * @param {Mat3} source
    - * @return {Mat3} this
    - */
    -Mat3.prototype.copy = function(source){
    -    for(var i=0; i < source.elements.length; i++){
    -        this.elements[i] = source.elements[i];
    -    }
    -    return this;
    -};
    -
    -/**
    - * Returns a string representation of the matrix.
    - * @method toString
    - * @return string
    - */
    -Mat3.prototype.toString = function(){
    -    var r = "";
    -    var sep = ",";
    -    for(var i=0; i<9; i++){
    -        r += this.elements[i] + sep;
    -    }
    -    return r;
    -};
    -
    -/**
    - * reverse the matrix
    - * @method reverse
    - * @param {Mat3} target Optional. Target matrix to save in.
    - * @return {Mat3} The solution x
    - */
    -Mat3.prototype.reverse = function(target){
    -
    -    target = target || new Mat3();
    -
    -    // Construct equations
    -    var nr = 3; // num rows
    -    var nc = 6; // num cols
    -    var eqns = [];
    -    for(var i=0; i<nr*nc; i++){
    -        eqns.push(0);
    -    }
    -    var i,j;
    -    for(i=0; i<3; i++){
    -        for(j=0; j<3; j++){
    -            eqns[i+nc*j] = this.elements[i+3*j];
    -        }
    -    }
    -    eqns[3+6*0] = 1;
    -    eqns[3+6*1] = 0;
    -    eqns[3+6*2] = 0;
    -    eqns[4+6*0] = 0;
    -    eqns[4+6*1] = 1;
    -    eqns[4+6*2] = 0;
    -    eqns[5+6*0] = 0;
    -    eqns[5+6*1] = 0;
    -    eqns[5+6*2] = 1;
    -
    -    // Compute right upper triangular version of the matrix - Gauss elimination
    -    var n = 3, k = n, np;
    -    var kp = nc; // num rows
    -    var p;
    -    do {
    -        i = k - n;
    -        if (eqns[i+nc*i] === 0) {
    -            // the pivot is null, swap lines
    -            for (j = i + 1; j < k; j++) {
    -                if (eqns[i+nc*j] !== 0) {
    -                    np = kp;
    -                    do { // do line( i ) = line( i ) + line( k )
    -                        p = kp - np;
    -                        eqns[p+nc*i] += eqns[p+nc*j];
    -                    } while (--np);
    -                    break;
    -                }
    -            }
    -        }
    -        if (eqns[i+nc*i] !== 0) {
    -            for (j = i + 1; j < k; j++) {
    -                var multiplier = eqns[i+nc*j] / eqns[i+nc*i];
    -                np = kp;
    -                do { // do line( k ) = line( k ) - multiplier * line( i )
    -                    p = kp - np;
    -                    eqns[p+nc*j] = p <= i ? 0 : eqns[p+nc*j] - eqns[p+nc*i] * multiplier ;
    -                } while (--np);
    -            }
    -        }
    -    } while (--n);
    -
    -    // eliminate the upper left triangle of the matrix
    -    i = 2;
    -    do {
    -        j = i-1;
    -        do {
    -            var multiplier = eqns[i+nc*j] / eqns[i+nc*i];
    -            np = nc;
    -            do {
    -                p = nc - np;
    -                eqns[p+nc*j] =  eqns[p+nc*j] - eqns[p+nc*i] * multiplier ;
    -            } while (--np);
    -        } while (j--);
    -    } while (--i);
    -
    -    // operations on the diagonal
    -    i = 2;
    -    do {
    -        var multiplier = 1 / eqns[i+nc*i];
    -        np = nc;
    -        do {
    -            p = nc - np;
    -            eqns[p+nc*i] = eqns[p+nc*i] * multiplier ;
    -        } while (--np);
    -    } while (i--);
    -
    -    i = 2;
    -    do {
    -        j = 2;
    -        do {
    -            p = eqns[nr+j+nc*i];
    -            if( isNaN( p ) || p ===Infinity ){
    -                throw "Could not reverse! A=["+this.toString()+"]";
    -            }
    -            target.e( i , j , p );
    -        } while (j--);
    -    } while (i--);
    -
    -    return target;
    -};
    -
    -/**
    - * Set the matrix from a quaterion
    - * @method setRotationFromQuaternion
    - * @param {Quaternion} q
    - */
    -Mat3.prototype.setRotationFromQuaternion = function( q ) {
    -    var x = q.x, y = q.y, z = q.z, w = q.w,
    -        x2 = x + x, y2 = y + y, z2 = z + z,
    -        xx = x * x2, xy = x * y2, xz = x * z2,
    -        yy = y * y2, yz = y * z2, zz = z * z2,
    -        wx = w * x2, wy = w * y2, wz = w * z2,
    -        e = this.elements;
    -
    -    e[3*0 + 0] = 1 - ( yy + zz );
    -    e[3*0 + 1] = xy - wz;
    -    e[3*0 + 2] = xz + wy;
    -
    -    e[3*1 + 0] = xy + wz;
    -    e[3*1 + 1] = 1 - ( xx + zz );
    -    e[3*1 + 2] = yz - wx;
    -
    -    e[3*2 + 0] = xz - wy;
    -    e[3*2 + 1] = yz + wx;
    -    e[3*2 + 2] = 1 - ( xx + yy );
    -
    -    return this;
    -};
    -
    -/**
    - * Transpose the matrix
    - * @method transpose
    - * @param  {Mat3} target Where to store the result.
    - * @return {Mat3} The target Mat3, or a new Mat3 if target was omitted.
    - */
    -Mat3.prototype.transpose = function( target ) {
    -    target = target || new Mat3();
    -
    -    var Mt = target.elements,
    -        M = this.elements;
    -
    -    for(var i=0; i!==3; i++){
    -        for(var j=0; j!==3; j++){
    -            Mt[3*i + j] = M[3*j + i];
    -        }
    -    }
    -
    -    return target;
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_math_Quaternion.js.html b/docs/files/src_math_Quaternion.js.html deleted file mode 100644 index a773edac0..000000000 --- a/docs/files/src_math_Quaternion.js.html +++ /dev/null @@ -1,552 +0,0 @@ - - - - - src/math/Quaternion.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/math/Quaternion.js

    - -
    -
    -module.exports = Quaternion;
    -
    -var Vec3 = require('./Vec3');
    -
    -/**
    - * A Quaternion describes a rotation in 3D space. The Quaternion is mathematically defined as Q = x*i + y*j + z*k + w, where (i,j,k) are imaginary basis vectors. (x,y,z) can be seen as a vector related to the axis of rotation, while the real multiplier, w, is related to the amount of rotation.
    - * @class Quaternion
    - * @constructor
    - * @param {Number} x Multiplier of the imaginary basis vector i.
    - * @param {Number} y Multiplier of the imaginary basis vector j.
    - * @param {Number} z Multiplier of the imaginary basis vector k.
    - * @param {Number} w Multiplier of the real part.
    - * @see http://en.wikipedia.org/wiki/Quaternion
    - */
    -function Quaternion(x,y,z,w){
    -    /**
    -     * @property {Number} x
    -     */
    -    this.x = x!==undefined ? x : 0;
    -
    -    /**
    -     * @property {Number} y
    -     */
    -    this.y = y!==undefined ? y : 0;
    -
    -    /**
    -     * @property {Number} z
    -     */
    -    this.z = z!==undefined ? z : 0;
    -
    -    /**
    -     * The multiplier of the real quaternion basis vector.
    -     * @property {Number} w
    -     */
    -    this.w = w!==undefined ? w : 1;
    -}
    -
    -/**
    - * Set the value of the quaternion.
    - * @method set
    - * @param {Number} x
    - * @param {Number} y
    - * @param {Number} z
    - * @param {Number} w
    - */
    -Quaternion.prototype.set = function(x,y,z,w){
    -    this.x = x;
    -    this.y = y;
    -    this.z = z;
    -    this.w = w;
    -};
    -
    -/**
    - * Convert to a readable format
    - * @method toString
    - * @return string
    - */
    -Quaternion.prototype.toString = function(){
    -    return this.x+","+this.y+","+this.z+","+this.w;
    -};
    -
    -/**
    - * Convert to an Array
    - * @method toArray
    - * @return Array
    - */
    -Quaternion.prototype.toArray = function(){
    -    return [this.x, this.y, this.z, this.w];
    -};
    -
    -/**
    - * Set the quaternion components given an axis and an angle.
    - * @method setFromAxisAngle
    - * @param {Vec3} axis
    - * @param {Number} angle in radians
    - */
    -Quaternion.prototype.setFromAxisAngle = function(axis,angle){
    -    var s = Math.sin(angle*0.5);
    -    this.x = axis.x * s;
    -    this.y = axis.y * s;
    -    this.z = axis.z * s;
    -    this.w = Math.cos(angle*0.5);
    -};
    -
    -/**
    - * Converts the quaternion to axis/angle representation.
    - * @method toAxisAngle
    - * @param {Vec3} targetAxis Optional. A vector object to reuse for storing the axis.
    - * @return Array An array, first elemnt is the axis and the second is the angle in radians.
    - */
    -Quaternion.prototype.toAxisAngle = function(targetAxis){
    -    targetAxis = targetAxis || new Vec3();
    -    this.normalize(); // if w>1 acos and sqrt will produce errors, this cant happen if quaternion is normalised
    -    var angle = 2 * Math.acos(this.w);
    -    var s = Math.sqrt(1-this.w*this.w); // assuming quaternion normalised then w is less than 1, so term always positive.
    -    if (s < 0.001) { // test to avoid divide by zero, s is always positive due to sqrt
    -        // if s close to zero then direction of axis not important
    -        targetAxis.x = this.x; // if it is important that axis is normalised then replace with x=1; y=z=0;
    -        targetAxis.y = this.y;
    -        targetAxis.z = this.z;
    -    } else {
    -        targetAxis.x = this.x / s; // normalise axis
    -        targetAxis.y = this.y / s;
    -        targetAxis.z = this.z / s;
    -    }
    -    return [targetAxis,angle];
    -};
    -
    -var sfv_t1 = new Vec3(),
    -    sfv_t2 = new Vec3();
    -
    -/**
    - * Set the quaternion value given two vectors. The resulting rotation will be the needed rotation to rotate u to v.
    - * @method setFromVectors
    - * @param {Vec3} u
    - * @param {Vec3} v
    - */
    -Quaternion.prototype.setFromVectors = function(u,v){
    -    if(u.isAntiparallelTo(v)){
    -        var t1 = sfv_t1;
    -        var t2 = sfv_t2;
    -
    -        u.tangents(t1,t2);
    -        this.setFromAxisAngle(t1,Math.PI);
    -    } else {
    -        var a = u.cross(v);
    -        this.x = a.x;
    -        this.y = a.y;
    -        this.z = a.z;
    -        this.w = Math.sqrt(Math.pow(u.norm(),2) * Math.pow(v.norm(),2)) + u.dot(v);
    -        this.normalize();
    -    }
    -};
    -
    -/**
    - * Quaternion multiplication
    - * @method mult
    - * @param {Quaternion} q
    - * @param {Quaternion} target Optional.
    - * @return {Quaternion}
    - */
    -var Quaternion_mult_va = new Vec3();
    -var Quaternion_mult_vb = new Vec3();
    -var Quaternion_mult_vaxvb = new Vec3();
    -Quaternion.prototype.mult = function(q,target){
    -    target = target || new Quaternion();
    -    var w = this.w,
    -        va = Quaternion_mult_va,
    -        vb = Quaternion_mult_vb,
    -        vaxvb = Quaternion_mult_vaxvb;
    -
    -    va.set(this.x,this.y,this.z);
    -    vb.set(q.x,q.y,q.z);
    -    target.w = w*q.w - va.dot(vb);
    -    va.cross(vb,vaxvb);
    -
    -    target.x = w * vb.x + q.w*va.x + vaxvb.x;
    -    target.y = w * vb.y + q.w*va.y + vaxvb.y;
    -    target.z = w * vb.z + q.w*va.z + vaxvb.z;
    -
    -    return target;
    -};
    -
    -/**
    - * Get the inverse quaternion rotation.
    - * @method inverse
    - * @param {Quaternion} target
    - * @return {Quaternion}
    - */
    -Quaternion.prototype.inverse = function(target){
    -    var x = this.x, y = this.y, z = this.z, w = this.w;
    -    target = target || new Quaternion();
    -
    -    this.conjugate(target);
    -    var inorm2 = 1/(x*x + y*y + z*z + w*w);
    -    target.x *= inorm2;
    -    target.y *= inorm2;
    -    target.z *= inorm2;
    -    target.w *= inorm2;
    -
    -    return target;
    -};
    -
    -/**
    - * Get the quaternion conjugate
    - * @method conjugate
    - * @param {Quaternion} target
    - * @return {Quaternion}
    - */
    -Quaternion.prototype.conjugate = function(target){
    -    target = target || new Quaternion();
    -
    -    target.x = -this.x;
    -    target.y = -this.y;
    -    target.z = -this.z;
    -    target.w = this.w;
    -
    -    return target;
    -};
    -
    -/**
    - * Normalize the quaternion. Note that this changes the values of the quaternion.
    - * @method normalize
    - */
    -Quaternion.prototype.normalize = function(){
    -    var l = Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w);
    -    if ( l === 0 ) {
    -        this.x = 0;
    -        this.y = 0;
    -        this.z = 0;
    -        this.w = 0;
    -    } else {
    -        l = 1 / l;
    -        this.x *= l;
    -        this.y *= l;
    -        this.z *= l;
    -        this.w *= l;
    -    }
    -};
    -
    -/**
    - * Approximation of quaternion normalization. Works best when quat is already almost-normalized.
    - * @method normalizeFast
    - * @see http://jsperf.com/fast-quaternion-normalization
    - * @author unphased, https://github.com/unphased
    - */
    -Quaternion.prototype.normalizeFast = function () {
    -    var f = (3.0-(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w))/2.0;
    -    if ( f === 0 ) {
    -        this.x = 0;
    -        this.y = 0;
    -        this.z = 0;
    -        this.w = 0;
    -    } else {
    -        this.x *= f;
    -        this.y *= f;
    -        this.z *= f;
    -        this.w *= f;
    -    }
    -};
    -
    -/**
    - * Multiply the quaternion by a vector
    - * @method vmult
    - * @param {Vec3} v
    - * @param {Vec3} target Optional
    - * @return {Vec3}
    - */
    -Quaternion.prototype.vmult = function(v,target){
    -    target = target || new Vec3();
    -
    -    var x = v.x,
    -        y = v.y,
    -        z = v.z;
    -
    -    var qx = this.x,
    -        qy = this.y,
    -        qz = this.z,
    -        qw = this.w;
    -
    -    // q*v
    -    var ix =  qw * x + qy * z - qz * y,
    -    iy =  qw * y + qz * x - qx * z,
    -    iz =  qw * z + qx * y - qy * x,
    -    iw = -qx * x - qy * y - qz * z;
    -
    -    target.x = ix * qw + iw * -qx + iy * -qz - iz * -qy;
    -    target.y = iy * qw + iw * -qy + iz * -qx - ix * -qz;
    -    target.z = iz * qw + iw * -qz + ix * -qy - iy * -qx;
    -
    -    return target;
    -};
    -
    -/**
    - * Copies value of source to this quaternion.
    - * @method copy
    - * @param {Quaternion} source
    - * @return {Quaternion} this
    - */
    -Quaternion.prototype.copy = function(source){
    -    this.x = source.x;
    -    this.y = source.y;
    -    this.z = source.z;
    -    this.w = source.w;
    -    return this;
    -};
    -
    -/**
    - * Convert the quaternion to euler angle representation. Order: YZX, as this page describes: http://www.euclideanspace.com/maths/standards/index.htm
    - * @method toEuler
    - * @param {Vec3} target
    - * @param string order Three-character string e.g. "YZX", which also is default.
    - */
    -Quaternion.prototype.toEuler = function(target,order){
    -    order = order || "YZX";
    -
    -    var heading, attitude, bank;
    -    var x = this.x, y = this.y, z = this.z, w = this.w;
    -
    -    switch(order){
    -    case "YZX":
    -        var test = x*y + z*w;
    -        if (test > 0.499) { // singularity at north pole
    -            heading = 2 * Math.atan2(x,w);
    -            attitude = Math.PI/2;
    -            bank = 0;
    -        }
    -        if (test < -0.499) { // singularity at south pole
    -            heading = -2 * Math.atan2(x,w);
    -            attitude = - Math.PI/2;
    -            bank = 0;
    -        }
    -        if(isNaN(heading)){
    -            var sqx = x*x;
    -            var sqy = y*y;
    -            var sqz = z*z;
    -            heading = Math.atan2(2*y*w - 2*x*z , 1 - 2*sqy - 2*sqz); // Heading
    -            attitude = Math.asin(2*test); // attitude
    -            bank = Math.atan2(2*x*w - 2*y*z , 1 - 2*sqx - 2*sqz); // bank
    -        }
    -        break;
    -    default:
    -        throw new Error("Euler order "+order+" not supported yet.");
    -    }
    -
    -    target.y = heading;
    -    target.z = attitude;
    -    target.x = bank;
    -};
    -
    -/**
    - * See http://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/content/SpinCalc.m
    - * @method setFromEuler
    - * @param {Number} x
    - * @param {Number} y
    - * @param {Number} z
    - * @param {String} order The order to apply angles: 'XYZ' or 'YXZ' or any other combination
    - */
    -Quaternion.prototype.setFromEuler = function ( x, y, z, order ) {
    -    order = order || "XYZ";
    -
    -    var c1 = Math.cos( x / 2 );
    -    var c2 = Math.cos( y / 2 );
    -    var c3 = Math.cos( z / 2 );
    -    var s1 = Math.sin( x / 2 );
    -    var s2 = Math.sin( y / 2 );
    -    var s3 = Math.sin( z / 2 );
    -
    -    if ( order === 'XYZ' ) {
    -
    -        this.x = s1 * c2 * c3 + c1 * s2 * s3;
    -        this.y = c1 * s2 * c3 - s1 * c2 * s3;
    -        this.z = c1 * c2 * s3 + s1 * s2 * c3;
    -        this.w = c1 * c2 * c3 - s1 * s2 * s3;
    -
    -    } else if ( order === 'YXZ' ) {
    -
    -        this.x = s1 * c2 * c3 + c1 * s2 * s3;
    -        this.y = c1 * s2 * c3 - s1 * c2 * s3;
    -        this.z = c1 * c2 * s3 - s1 * s2 * c3;
    -        this.w = c1 * c2 * c3 + s1 * s2 * s3;
    -
    -    } else if ( order === 'ZXY' ) {
    -
    -        this.x = s1 * c2 * c3 - c1 * s2 * s3;
    -        this.y = c1 * s2 * c3 + s1 * c2 * s3;
    -        this.z = c1 * c2 * s3 + s1 * s2 * c3;
    -        this.w = c1 * c2 * c3 - s1 * s2 * s3;
    -
    -    } else if ( order === 'ZYX' ) {
    -
    -        this.x = s1 * c2 * c3 - c1 * s2 * s3;
    -        this.y = c1 * s2 * c3 + s1 * c2 * s3;
    -        this.z = c1 * c2 * s3 - s1 * s2 * c3;
    -        this.w = c1 * c2 * c3 + s1 * s2 * s3;
    -
    -    } else if ( order === 'YZX' ) {
    -
    -        this.x = s1 * c2 * c3 + c1 * s2 * s3;
    -        this.y = c1 * s2 * c3 + s1 * c2 * s3;
    -        this.z = c1 * c2 * s3 - s1 * s2 * c3;
    -        this.w = c1 * c2 * c3 - s1 * s2 * s3;
    -
    -    } else if ( order === 'XZY' ) {
    -
    -        this.x = s1 * c2 * c3 - c1 * s2 * s3;
    -        this.y = c1 * s2 * c3 - s1 * c2 * s3;
    -        this.z = c1 * c2 * s3 + s1 * s2 * c3;
    -        this.w = c1 * c2 * c3 + s1 * s2 * s3;
    -
    -    }
    -
    -    return this;
    -
    -};
    -
    -Quaternion.prototype.clone = function(){
    -    return new Quaternion(this.x, this.y, this.z, this.w);
    -};
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_math_Transform.js.html b/docs/files/src_math_Transform.js.html deleted file mode 100644 index be5e8c341..000000000 --- a/docs/files/src_math_Transform.js.html +++ /dev/null @@ -1,257 +0,0 @@ - - - - - src/math/Transform.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/math/Transform.js

    - -
    -
    -var Vec3 = require('./Vec3');
    -var Quaternion = require('./Quaternion');
    -
    -module.exports = Transform;
    -
    -/**
    - * @class Transform
    - * @constructor
    - */
    -function Transform(options) {
    -    options = options || {};
    -
    -	/**
    -	 * @property {Vec3} position
    -	 */
    -	this.position = new Vec3();
    -    if(options.position){
    -        this.position.copy(options.position);
    -    }
    -
    -	/**
    -	 * @property {Quaternion} quaternion
    -	 */
    -	this.quaternion = new Quaternion();
    -    if(options.quaternion){
    -        this.quaternion.copy(options.quaternion);
    -    }
    -}
    -
    -var tmpQuat = new Quaternion();
    -
    -/**
    - * @static
    - * @method pointToLocaFrame
    - * @param {Vec3} position
    - * @param {Quaternion} quaternion
    - * @param {Vec3} worldPoint
    - * @param {Vec3} result
    - */
    -Transform.pointToLocalFrame = function(position, quaternion, worldPoint, result){
    -    var result = result || new Vec3();
    -    worldPoint.vsub(position, result);
    -    quaternion.conjugate(tmpQuat);
    -    tmpQuat.vmult(result, result);
    -    return result;
    -};
    -
    -/**
    - * Get a global point in local transform coordinates.
    - * @method pointToLocal
    - * @param  {Vec3} point
    - * @param  {Vec3} result
    - * @return {Vec3} The "result" vector object
    - */
    -Transform.prototype.pointToLocal = function(worldPoint, result){
    -    return Transform.pointToLocalFrame(this.position, this.quaternion, worldPoint, result);
    -};
    -
    -/**
    - * @static
    - * @method pointToWorldFrame
    - * @param {Vec3} position
    - * @param {Vec3} quaternion
    - * @param {Vec3} localPoint
    - * @param {Vec3} result
    - */
    -Transform.pointToWorldFrame = function(position, quaternion, localPoint, result){
    -    var result = result || new Vec3();
    -    quaternion.vmult(localPoint, result);
    -    result.vadd(position, result);
    -    return result;
    -};
    -
    -/**
    - * Get a local point in global transform coordinates.
    - * @method pointToWorld
    - * @param  {Vec3} point
    - * @param  {Vec3} result
    - * @return {Vec3} The "result" vector object
    - */
    -Transform.prototype.pointToWorld = function(localPoint, result){
    -    return Transform.pointToWorldFrame(this.position, this.quaternion, localPoint, result);
    -};
    -
    -
    -Transform.prototype.vectorToWorldFrame = function(localVector, result){
    -    var result = result || new Vec3();
    -    this.quaternion.vmult(localVector, result);
    -    return result;
    -};
    -
    -Transform.vectorToWorldFrame = function(quaternion, localVector, result){
    -    quaternion.vmult(localVector, result);
    -    return result;
    -};
    -
    -Transform.vectorToLocalFrame = function(position, quaternion, worldVector, result){
    -    var result = result || new Vec3();
    -    quaternion.w *= -1;
    -    quaternion.vmult(worldVector, result);
    -    quaternion.w *= -1;
    -    return result;
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_math_Vec3.js.html b/docs/files/src_math_Vec3.js.html deleted file mode 100644 index 51ea06064..000000000 --- a/docs/files/src_math_Vec3.js.html +++ /dev/null @@ -1,605 +0,0 @@ - - - - - src/math/Vec3.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/math/Vec3.js

    - -
    -
    -module.exports = Vec3;
    -
    -var Mat3 = require('./Mat3');
    -
    -/**
    - * 3-dimensional vector
    - * @class Vec3
    - * @constructor
    - * @param {Number} x
    - * @param {Number} y
    - * @param {Number} z
    - * @author schteppe
    - * @example
    - *     var v = new Vec3(1, 2, 3);
    - *     console.log('x=' + v.x); // x=1
    - */
    -function Vec3(x,y,z){
    -    /**
    -     * @property x
    -     * @type {Number}
    -     */
    -    this.x = x||0.0;
    -
    -    /**
    -     * @property y
    -     * @type {Number}
    -     */
    -    this.y = y||0.0;
    -
    -    /**
    -     * @property z
    -     * @type {Number}
    -     */
    -    this.z = z||0.0;
    -}
    -
    -/**
    - * @static
    - * @property {Vec3} ZERO
    - */
    -Vec3.ZERO = new Vec3(0, 0, 0);
    -
    -/**
    - * @static
    - * @property {Vec3} UNIT_X
    - */
    -Vec3.UNIT_X = new Vec3(1, 0, 0);
    -
    -/**
    - * @static
    - * @property {Vec3} UNIT_Y
    - */
    -Vec3.UNIT_Y = new Vec3(0, 1, 0);
    -
    -/**
    - * @static
    - * @property {Vec3} UNIT_Z
    - */
    -Vec3.UNIT_Z = new Vec3(0, 0, 1);
    -
    -/**
    - * Vector cross product
    - * @method cross
    - * @param {Vec3} v
    - * @param {Vec3} target Optional. Target to save in.
    - * @return {Vec3}
    - */
    -Vec3.prototype.cross = function(v,target){
    -    var vx=v.x, vy=v.y, vz=v.z, x=this.x, y=this.y, z=this.z;
    -    target = target || new Vec3();
    -
    -    target.x = (y * vz) - (z * vy);
    -    target.y = (z * vx) - (x * vz);
    -    target.z = (x * vy) - (y * vx);
    -
    -    return target;
    -};
    -
    -/**
    - * Set the vectors' 3 elements
    - * @method set
    - * @param {Number} x
    - * @param {Number} y
    - * @param {Number} z
    - * @return Vec3
    - */
    -Vec3.prototype.set = function(x,y,z){
    -    this.x = x;
    -    this.y = y;
    -    this.z = z;
    -    return this;
    -};
    -
    -/**
    - * Set all components of the vector to zero.
    - * @method setZero
    - */
    -Vec3.prototype.setZero = function(){
    -    this.x = this.y = this.z = 0;
    -};
    -
    -/**
    - * Vector addition
    - * @method vadd
    - * @param {Vec3} v
    - * @param {Vec3} target Optional.
    - * @return {Vec3}
    - */
    -Vec3.prototype.vadd = function(v,target){
    -    if(target){
    -        target.x = v.x + this.x;
    -        target.y = v.y + this.y;
    -        target.z = v.z + this.z;
    -    } else {
    -        return new Vec3(this.x + v.x,
    -                               this.y + v.y,
    -                               this.z + v.z);
    -    }
    -};
    -
    -/**
    - * Vector subtraction
    - * @method vsub
    - * @param {Vec3} v
    - * @param {Vec3} target Optional. Target to save in.
    - * @return {Vec3}
    - */
    -Vec3.prototype.vsub = function(v,target){
    -    if(target){
    -        target.x = this.x - v.x;
    -        target.y = this.y - v.y;
    -        target.z = this.z - v.z;
    -    } else {
    -        return new Vec3(this.x-v.x,
    -                               this.y-v.y,
    -                               this.z-v.z);
    -    }
    -};
    -
    -/**
    - * Get the cross product matrix a_cross from a vector, such that a x b = a_cross * b = c
    - * @method crossmat
    - * @see http://www8.cs.umu.se/kurser/TDBD24/VT06/lectures/Lecture6.pdf
    - * @return {Mat3}
    - */
    -Vec3.prototype.crossmat = function(){
    -    return new Mat3([     0,  -this.z,   this.y,
    -                            this.z,        0,  -this.x,
    -                           -this.y,   this.x,        0]);
    -};
    -
    -/**
    - * Normalize the vector. Note that this changes the values in the vector.
    - * @method normalize
    - * @return {Number} Returns the norm of the vector
    - */
    -Vec3.prototype.normalize = function(){
    -    var x=this.x, y=this.y, z=this.z;
    -    var n = Math.sqrt(x*x + y*y + z*z);
    -    if(n>0.0){
    -        var invN = 1/n;
    -        this.x *= invN;
    -        this.y *= invN;
    -        this.z *= invN;
    -    } else {
    -        // Make something up
    -        this.x = 0;
    -        this.y = 0;
    -        this.z = 0;
    -    }
    -    return n;
    -};
    -
    -/**
    - * Get the version of this vector that is of length 1.
    - * @method unit
    - * @param {Vec3} target Optional target to save in
    - * @return {Vec3} Returns the unit vector
    - */
    -Vec3.prototype.unit = function(target){
    -    target = target || new Vec3();
    -    var x=this.x, y=this.y, z=this.z;
    -    var ninv = Math.sqrt(x*x + y*y + z*z);
    -    if(ninv>0.0){
    -        ninv = 1.0/ninv;
    -        target.x = x * ninv;
    -        target.y = y * ninv;
    -        target.z = z * ninv;
    -    } else {
    -        target.x = 1;
    -        target.y = 0;
    -        target.z = 0;
    -    }
    -    return target;
    -};
    -
    -/**
    - * Get the length of the vector
    - * @method norm
    - * @return {Number}
    - * @deprecated Use .length() instead
    - */
    -Vec3.prototype.norm = function(){
    -    var x=this.x, y=this.y, z=this.z;
    -    return Math.sqrt(x*x + y*y + z*z);
    -};
    -
    -/**
    - * Get the length of the vector
    - * @method length
    - * @return {Number}
    - */
    -Vec3.prototype.length = Vec3.prototype.norm;
    -
    -/**
    - * Get the squared length of the vector
    - * @method norm2
    - * @return {Number}
    - * @deprecated Use .lengthSquared() instead.
    - */
    -Vec3.prototype.norm2 = function(){
    -    return this.dot(this);
    -};
    -
    -/**
    - * Get the squared length of the vector.
    - * @method lengthSquared
    - * @return {Number}
    - */
    -Vec3.prototype.lengthSquared = Vec3.prototype.norm2;
    -
    -/**
    - * Get distance from this point to another point
    - * @method distanceTo
    - * @param  {Vec3} p
    - * @return {Number}
    - */
    -Vec3.prototype.distanceTo = function(p){
    -    var x=this.x, y=this.y, z=this.z;
    -    var px=p.x, py=p.y, pz=p.z;
    -    return Math.sqrt((px-x)*(px-x)+
    -                     (py-y)*(py-y)+
    -                     (pz-z)*(pz-z));
    -};
    -
    -/**
    - * Get squared distance from this point to another point
    - * @method distanceSquared
    - * @param  {Vec3} p
    - * @return {Number}
    - */
    -Vec3.prototype.distanceSquared = function(p){
    -    var x=this.x, y=this.y, z=this.z;
    -    var px=p.x, py=p.y, pz=p.z;
    -    return (px-x)*(px-x) + (py-y)*(py-y) + (pz-z)*(pz-z);
    -};
    -
    -/**
    - * Multiply all the components of the vector with a scalar.
    - * @deprecated Use .scale instead
    - * @method mult
    - * @param {Number} scalar
    - * @param {Vec3} target The vector to save the result in.
    - * @return {Vec3}
    - * @deprecated Use .scale() instead
    - */
    -Vec3.prototype.mult = function(scalar,target){
    -    target = target || new Vec3();
    -    var x = this.x,
    -        y = this.y,
    -        z = this.z;
    -    target.x = scalar * x;
    -    target.y = scalar * y;
    -    target.z = scalar * z;
    -    return target;
    -};
    -
    -/**
    - * Multiply the vector with a scalar.
    - * @method scale
    - * @param {Number} scalar
    - * @param {Vec3} target
    - * @return {Vec3}
    - */
    -Vec3.prototype.scale = Vec3.prototype.mult;
    -
    -/**
    - * Calculate dot product
    - * @method dot
    - * @param {Vec3} v
    - * @return {Number}
    - */
    -Vec3.prototype.dot = function(v){
    -    return this.x * v.x + this.y * v.y + this.z * v.z;
    -};
    -
    -/**
    - * @method isZero
    - * @return bool
    - */
    -Vec3.prototype.isZero = function(){
    -    return this.x===0 && this.y===0 && this.z===0;
    -};
    -
    -/**
    - * Make the vector point in the opposite direction.
    - * @method negate
    - * @param {Vec3} target Optional target to save in
    - * @return {Vec3}
    - */
    -Vec3.prototype.negate = function(target){
    -    target = target || new Vec3();
    -    target.x = -this.x;
    -    target.y = -this.y;
    -    target.z = -this.z;
    -    return target;
    -};
    -
    -/**
    - * Compute two artificial tangents to the vector
    - * @method tangents
    - * @param {Vec3} t1 Vector object to save the first tangent in
    - * @param {Vec3} t2 Vector object to save the second tangent in
    - */
    -var Vec3_tangents_n = new Vec3();
    -var Vec3_tangents_randVec = new Vec3();
    -Vec3.prototype.tangents = function(t1,t2){
    -    var norm = this.norm();
    -    if(norm>0.0){
    -        var n = Vec3_tangents_n;
    -        var inorm = 1/norm;
    -        n.set(this.x*inorm,this.y*inorm,this.z*inorm);
    -        var randVec = Vec3_tangents_randVec;
    -        if(Math.abs(n.x) < 0.9){
    -            randVec.set(1,0,0);
    -            n.cross(randVec,t1);
    -        } else {
    -            randVec.set(0,1,0);
    -            n.cross(randVec,t1);
    -        }
    -        n.cross(t1,t2);
    -    } else {
    -        // The normal length is zero, make something up
    -        t1.set(1, 0, 0);
    -        t2.set(0, 1, 0);
    -    }
    -};
    -
    -/**
    - * Converts to a more readable format
    - * @method toString
    - * @return string
    - */
    -Vec3.prototype.toString = function(){
    -    return this.x+","+this.y+","+this.z;
    -};
    -
    -/**
    - * Converts to an array
    - * @method toArray
    - * @return Array
    - */
    -Vec3.prototype.toArray = function(){
    -    return [this.x, this.y, this.z];
    -};
    -
    -/**
    - * Copies value of source to this vector.
    - * @method copy
    - * @param {Vec3} source
    - * @return {Vec3} this
    - */
    -Vec3.prototype.copy = function(source){
    -    this.x = source.x;
    -    this.y = source.y;
    -    this.z = source.z;
    -    return this;
    -};
    -
    -
    -/**
    - * Do a linear interpolation between two vectors
    - * @method lerp
    - * @param {Vec3} v
    - * @param {Number} t A number between 0 and 1. 0 will make this function return u, and 1 will make it return v. Numbers in between will generate a vector in between them.
    - * @param {Vec3} target
    - */
    -Vec3.prototype.lerp = function(v,t,target){
    -    var x=this.x, y=this.y, z=this.z;
    -    target.x = x + (v.x-x)*t;
    -    target.y = y + (v.y-y)*t;
    -    target.z = z + (v.z-z)*t;
    -};
    -
    -/**
    - * Check if a vector equals is almost equal to another one.
    - * @method almostEquals
    - * @param {Vec3} v
    - * @param {Number} precision
    - * @return bool
    - */
    -Vec3.prototype.almostEquals = function(v,precision){
    -    if(precision===undefined){
    -        precision = 1e-6;
    -    }
    -    if( Math.abs(this.x-v.x)>precision ||
    -        Math.abs(this.y-v.y)>precision ||
    -        Math.abs(this.z-v.z)>precision){
    -        return false;
    -    }
    -    return true;
    -};
    -
    -/**
    - * Check if a vector is almost zero
    - * @method almostZero
    - * @param {Number} precision
    - */
    -Vec3.prototype.almostZero = function(precision){
    -    if(precision===undefined){
    -        precision = 1e-6;
    -    }
    -    if( Math.abs(this.x)>precision ||
    -        Math.abs(this.y)>precision ||
    -        Math.abs(this.z)>precision){
    -        return false;
    -    }
    -    return true;
    -};
    -
    -var antip_neg = new Vec3();
    -
    -/**
    - * Check if the vector is anti-parallel to another vector.
    - * @method isAntiparallelTo
    - * @param  {Vec3}  v
    - * @param  {Number}  precision Set to zero for exact comparisons
    - * @return {Boolean}
    - */
    -Vec3.prototype.isAntiparallelTo = function(v,precision){
    -    this.negate(antip_neg);
    -    return antip_neg.almostEquals(v,precision);
    -};
    -
    -/**
    - * Clone the vector
    - * @method clone
    - * @return {Vec3}
    - */
    -Vec3.prototype.clone = function(){
    -    return new Vec3(this.x, this.y, this.z);
    -};
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_objects_Body.js.html b/docs/files/src_objects_Body.js.html deleted file mode 100644 index 0ca43c80a..000000000 --- a/docs/files/src_objects_Body.js.html +++ /dev/null @@ -1,949 +0,0 @@ - - - - - src/objects/Body.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/objects/Body.js

    - -
    -
    -module.exports = Body;
    -
    -var EventTarget = require('../utils/EventTarget');
    -var Shape = require('../shapes/Shape');
    -var Vec3 = require('../math/Vec3');
    -var Mat3 = require('../math/Mat3');
    -var Quaternion = require('../math/Quaternion');
    -var Material = require('../material/Material');
    -var AABB = require('../collision/AABB');
    -var Box = require('../shapes/Box');
    -
    -/**
    - * Base class for all body types.
    - * @class Body
    - * @constructor
    - * @extends EventTarget
    - * @param {object} [options]
    - * @param {Vec3} [options.position]
    - * @param {Vec3} [options.velocity]
    - * @param {Vec3} [options.angularVelocity]
    - * @param {Quaternion} [options.quaternion]
    - * @param {number} [options.mass]
    - * @param {Material} [options.material]
    - * @param {number} [options.type]
    - * @param {number} [options.linearDamping=0.01]
    - * @param {number} [options.angularDamping=0.01]
    - * @param {boolean} [options.allowSleep=true]
    - * @param {number} [options.sleepSpeedLimit=0.1]
    - * @param {number} [options.sleepTimeLimit=1]
    - * @param {number} [options.collisionFilterGroup=1]
    - * @param {number} [options.collisionFilterMask=1]
    - * @param {boolean} [options.fixedRotation=false]
    - * @param {Body} [options.shape]
    - * @example
    - *     var body = new Body({
    - *         mass: 1
    - *     });
    - *     var shape = new Sphere(1);
    - *     body.addShape(shape);
    - *     world.add(body);
    - */
    -function Body(options){
    -    options = options || {};
    -
    -    EventTarget.apply(this);
    -
    -    this.id = Body.idCounter++;
    -
    -    /**
    -     * Reference to the world the body is living in
    -     * @property world
    -     * @type {World}
    -     */
    -    this.world = null;
    -
    -    /**
    -     * Callback function that is used BEFORE stepping the system. Use it to apply forces, for example. Inside the function, "this" will refer to this Body object.
    -     * @property preStep
    -     * @type {Function}
    -     * @deprecated Use World events instead
    -     */
    -    this.preStep = null;
    -
    -    /**
    -     * Callback function that is used AFTER stepping the system. Inside the function, "this" will refer to this Body object.
    -     * @property postStep
    -     * @type {Function}
    -     * @deprecated Use World events instead
    -     */
    -    this.postStep = null;
    -
    -    this.vlambda = new Vec3();
    -
    -    /**
    -     * @property {Number} collisionFilterGroup
    -     */
    -    this.collisionFilterGroup = typeof(options.collisionFilterGroup) === 'number' ? options.collisionFilterGroup : 1;
    -
    -    /**
    -     * @property {Number} collisionFilterMask
    -     */
    -    this.collisionFilterMask = typeof(options.collisionFilterMask) === 'number' ? options.collisionFilterMask : 1;
    -
    -    /**
    -     * Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled.
    -     * @property {Number} collisionResponse
    -     */
    -	this.collisionResponse = true;
    -
    -    /**
    -     * @property position
    -     * @type {Vec3}
    -     */
    -    this.position = new Vec3();
    -
    -    if(options.position){
    -        this.position.copy(options.position);
    -    }
    -
    -    /**
    -     * @property {Vec3} previousPosition
    -     */
    -    this.previousPosition = new Vec3();
    -
    -    /**
    -     * Initial position of the body
    -     * @property initPosition
    -     * @type {Vec3}
    -     */
    -    this.initPosition = new Vec3();
    -
    -    /**
    -     * @property velocity
    -     * @type {Vec3}
    -     */
    -    this.velocity = new Vec3();
    -
    -    if(options.velocity){
    -        this.velocity.copy(options.velocity);
    -    }
    -
    -    /**
    -     * @property initVelocity
    -     * @type {Vec3}
    -     */
    -    this.initVelocity = new Vec3();
    -
    -    /**
    -     * Linear force on the body
    -     * @property force
    -     * @type {Vec3}
    -     */
    -    this.force = new Vec3();
    -
    -    var mass = typeof(options.mass) === 'number' ? options.mass : 0;
    -
    -    /**
    -     * @property mass
    -     * @type {Number}
    -     * @default 0
    -     */
    -    this.mass = mass;
    -
    -    /**
    -     * @property invMass
    -     * @type {Number}
    -     */
    -    this.invMass = mass > 0 ? 1.0 / mass : 0;
    -
    -    /**
    -     * @property material
    -     * @type {Material}
    -     */
    -    this.material = options.material || null;
    -
    -    /**
    -     * @property linearDamping
    -     * @type {Number}
    -     */
    -    this.linearDamping = typeof(options.linearDamping) === 'number' ? options.linearDamping : 0.01;
    -
    -    /**
    -     * One of: Body.DYNAMIC, Body.STATIC and Body.KINEMATIC.
    -     * @property type
    -     * @type {Number}
    -     */
    -    this.type = (mass <= 0.0 ? Body.STATIC : Body.DYNAMIC);
    -    if(typeof(options.type) === typeof(Body.STATIC)){
    -        this.type = options.type;
    -    }
    -
    -    /**
    -     * If true, the body will automatically fall to sleep.
    -     * @property allowSleep
    -     * @type {Boolean}
    -     * @default true
    -     */
    -    this.allowSleep = typeof(options.allowSleep) !== 'undefined' ? options.allowSleep : true;
    -
    -    /**
    -     * Current sleep state.
    -     * @property sleepState
    -     * @type {Number}
    -     */
    -    this.sleepState = 0;
    -
    -    /**
    -     * If the speed (the norm of the velocity) is smaller than this value, the body is considered sleepy.
    -     * @property sleepSpeedLimit
    -     * @type {Number}
    -     * @default 0.1
    -     */
    -    this.sleepSpeedLimit = typeof(options.sleepSpeedLimit) !== 'undefined' ? options.sleepSpeedLimit : 0.1;
    -
    -    /**
    -     * If the body has been sleepy for this sleepTimeLimit seconds, it is considered sleeping.
    -     * @property sleepTimeLimit
    -     * @type {Number}
    -     * @default 1
    -     */
    -    this.sleepTimeLimit = typeof(options.sleepTimeLimit) !== 'undefined' ? options.sleepTimeLimit : 1;
    -
    -    this.timeLastSleepy = 0;
    -
    -    this._wakeUpAfterNarrowphase = false;
    -
    -
    -    /**
    -     * Rotational force on the body, around center of mass
    -     * @property {Vec3} torque
    -     */
    -    this.torque = new Vec3();
    -
    -    /**
    -     * Orientation of the body
    -     * @property quaternion
    -     * @type {Quaternion}
    -     */
    -    this.quaternion = new Quaternion();
    -
    -    if(options.quaternion){
    -        this.quaternion.copy(options.quaternion);
    -    }
    -
    -    /**
    -     * @property initQuaternion
    -     * @type {Quaternion}
    -     */
    -    this.initQuaternion = new Quaternion();
    -
    -    /**
    -     * @property angularVelocity
    -     * @type {Vec3}
    -     */
    -    this.angularVelocity = new Vec3();
    -
    -    if(options.angularVelocity){
    -        this.angularVelocity.copy(options.angularVelocity);
    -    }
    -
    -    /**
    -     * @property initAngularVelocity
    -     * @type {Vec3}
    -     */
    -    this.initAngularVelocity = new Vec3();
    -
    -    this.interpolatedPosition = new Vec3();
    -    this.interpolatedQuaternion = new Quaternion();
    -
    -    /**
    -     * @property shapes
    -     * @type {array}
    -     */
    -    this.shapes = [];
    -
    -    /**
    -     * @property shapeOffsets
    -     * @type {array}
    -     */
    -    this.shapeOffsets = [];
    -
    -    /**
    -     * @property shapeOrientations
    -     * @type {array}
    -     */
    -    this.shapeOrientations = [];
    -
    -    /**
    -     * @property inertia
    -     * @type {Vec3}
    -     */
    -    this.inertia = new Vec3();
    -
    -    /**
    -     * @property {Vec3} invInertia
    -     */
    -    this.invInertia = new Vec3();
    -
    -    /**
    -     * @property {Mat3} invInertiaWorld
    -     */
    -    this.invInertiaWorld = new Mat3();
    -
    -    this.invMassSolve = 0;
    -
    -    /**
    -     * @property {Vec3} invInertiaSolve
    -     */
    -    this.invInertiaSolve = new Vec3();
    -
    -    /**
    -     * @property {Mat3} invInertiaWorldSolve
    -     */
    -    this.invInertiaWorldSolve = new Mat3();
    -
    -    /**
    -     * Set to true if you don't want the body to rotate. Make sure to run .updateMassProperties() after changing this.
    -     * @property {Boolean} fixedRotation
    -     * @default false
    -     */
    -    this.fixedRotation = typeof(options.fixedRotation) !== "undefined" ? options.fixedRotation : false;
    -
    -    /**
    -     * @property {Number} angularDamping
    -     */
    -    this.angularDamping = typeof(options.angularDamping) !== 'undefined' ? options.angularDamping : 0.01;
    -
    -    /**
    -     * @property aabb
    -     * @type {AABB}
    -     */
    -    this.aabb = new AABB();
    -
    -    /**
    -     * Indicates if the AABB needs to be updated before use.
    -     * @property aabbNeedsUpdate
    -     * @type {Boolean}
    -     */
    -    this.aabbNeedsUpdate = true;
    -
    -    this.wlambda = new Vec3();
    -
    -    if(options.shape){
    -        this.addShape(options.shape);
    -    }
    -
    -    this.updateMassProperties();
    -}
    -Body.prototype = new EventTarget();
    -Body.prototype.constructor = Body;
    -
    -/**
    - * A dynamic body is fully simulated. Can be moved manually by the user, but normally they move according to forces. A dynamic body can collide with all body types. A dynamic body always has finite, non-zero mass.
    - * @static
    - * @property DYNAMIC
    - * @type {Number}
    - */
    -Body.DYNAMIC = 1;
    -
    -/**
    - * A static body does not move during simulation and behaves as if it has infinite mass. Static bodies can be moved manually by setting the position of the body. The velocity of a static body is always zero. Static bodies do not collide with other static or kinematic bodies.
    - * @static
    - * @property STATIC
    - * @type {Number}
    - */
    -Body.STATIC = 2;
    -
    -/**
    - * A kinematic body moves under simulation according to its velocity. They do not respond to forces. They can be moved manually, but normally a kinematic body is moved by setting its velocity. A kinematic body behaves as if it has infinite mass. Kinematic bodies do not collide with other static or kinematic bodies.
    - * @static
    - * @property KINEMATIC
    - * @type {Number}
    - */
    -Body.KINEMATIC = 4;
    -
    -
    -
    -/**
    - * @static
    - * @property AWAKE
    - * @type {number}
    - */
    -Body.AWAKE = 0;
    -
    -/**
    - * @static
    - * @property SLEEPY
    - * @type {number}
    - */
    -Body.SLEEPY = 1;
    -
    -/**
    - * @static
    - * @property SLEEPING
    - * @type {number}
    - */
    -Body.SLEEPING = 2;
    -
    -Body.idCounter = 0;
    -
    -/**
    - * Wake the body up.
    - * @method wakeUp
    - */
    -Body.prototype.wakeUp = function(){
    -    var s = this.sleepState;
    -    this.sleepState = 0;
    -    if(s === Body.SLEEPING){
    -        this.dispatchEvent({type:"wakeup"});
    -    }
    -};
    -
    -/**
    - * Force body sleep
    - * @method sleep
    - */
    -Body.prototype.sleep = function(){
    -    this.sleepState = Body.SLEEPING;
    -    this.velocity.set(0,0,0);
    -    this.angularVelocity.set(0,0,0);
    -};
    -
    -Body.sleepyEvent = {
    -    type: "sleepy"
    -};
    -
    -Body.sleepEvent = {
    -    type: "sleep"
    -};
    -
    -/**
    - * Called every timestep to update internal sleep timer and change sleep state if needed.
    - * @method sleepTick
    - * @param {Number} time The world time in seconds
    - */
    -Body.prototype.sleepTick = function(time){
    -    if(this.allowSleep){
    -        var sleepState = this.sleepState;
    -        var speedSquared = this.velocity.norm2() + this.angularVelocity.norm2();
    -        var speedLimitSquared = Math.pow(this.sleepSpeedLimit,2);
    -        if(sleepState===Body.AWAKE && speedSquared < speedLimitSquared){
    -            this.sleepState = Body.SLEEPY; // Sleepy
    -            this.timeLastSleepy = time;
    -            this.dispatchEvent(Body.sleepyEvent);
    -        } else if(sleepState===Body.SLEEPY && speedSquared > speedLimitSquared){
    -            this.wakeUp(); // Wake up
    -        } else if(sleepState===Body.SLEEPY && (time - this.timeLastSleepy ) > this.sleepTimeLimit){
    -            this.sleep(); // Sleeping
    -            this.dispatchEvent(Body.sleepEvent);
    -        }
    -    }
    -};
    -
    -/**
    - * If the body is sleeping, it should be immovable / have infinite mass during solve. We solve it by having a separate "solve mass".
    - * @method updateSolveMassProperties
    - */
    -Body.prototype.updateSolveMassProperties = function(){
    -    if(this.sleepState === Body.SLEEPING || this.type === Body.KINEMATIC){
    -        this.invMassSolve = 0;
    -        this.invInertiaSolve.setZero();
    -        this.invInertiaWorldSolve.setZero();
    -    } else {
    -        this.invMassSolve = this.invMass;
    -        this.invInertiaSolve.copy(this.invInertia);
    -        this.invInertiaWorldSolve.copy(this.invInertiaWorld);
    -    }
    -};
    -
    -/**
    - * Convert a world point to local body frame.
    - * @method pointToLocalFrame
    - * @param  {Vec3} worldPoint
    - * @param  {Vec3} result
    - * @return {Vec3}
    - */
    -Body.prototype.pointToLocalFrame = function(worldPoint,result){
    -    var result = result || new Vec3();
    -    worldPoint.vsub(this.position,result);
    -    this.quaternion.conjugate().vmult(result,result);
    -    return result;
    -};
    -
    -/**
    - * Convert a world vector to local body frame.
    - * @method vectorToLocalFrame
    - * @param  {Vec3} worldPoint
    - * @param  {Vec3} result
    - * @return {Vec3}
    - */
    -Body.prototype.vectorToLocalFrame = function(worldVector, result){
    -    var result = result || new Vec3();
    -    this.quaternion.conjugate().vmult(worldVector,result);
    -    return result;
    -};
    -
    -/**
    - * Convert a local body point to world frame.
    - * @method pointToWorldFrame
    - * @param  {Vec3} localPoint
    - * @param  {Vec3} result
    - * @return {Vec3}
    - */
    -Body.prototype.pointToWorldFrame = function(localPoint,result){
    -    var result = result || new Vec3();
    -    this.quaternion.vmult(localPoint,result);
    -    result.vadd(this.position,result);
    -    return result;
    -};
    -
    -/**
    - * Convert a local body point to world frame.
    - * @method vectorToWorldFrame
    - * @param  {Vec3} localVector
    - * @param  {Vec3} result
    - * @return {Vec3}
    - */
    -Body.prototype.vectorToWorldFrame = function(localVector, result){
    -    var result = result || new Vec3();
    -    this.quaternion.vmult(localVector, result);
    -    return result;
    -};
    -
    -var tmpVec = new Vec3();
    -var tmpQuat = new Quaternion();
    -
    -/**
    - * Add a shape to the body with a local offset and orientation.
    - * @method addShape
    - * @param {Shape} shape
    - * @param {Vec3} offset
    - * @param {Quaternion} quaternion
    - * @return {Body} The body object, for chainability.
    - */
    -Body.prototype.addShape = function(shape, _offset, _orientation){
    -    var offset = new Vec3();
    -    var orientation = new Quaternion();
    -
    -    if(_offset){
    -        offset.copy(_offset);
    -    }
    -    if(_orientation){
    -        orientation.copy(_orientation);
    -    }
    -
    -    this.shapes.push(shape);
    -    this.shapeOffsets.push(offset);
    -    this.shapeOrientations.push(orientation);
    -    this.updateMassProperties();
    -    this.updateBoundingRadius();
    -
    -    this.aabbNeedsUpdate = true;
    -
    -    return this;
    -};
    -
    -/**
    - * Update the bounding radius of the body. Should be done if any of the shapes are changed.
    - * @method updateBoundingRadius
    - */
    -Body.prototype.updateBoundingRadius = function(){
    -    var shapes = this.shapes,
    -        shapeOffsets = this.shapeOffsets,
    -        N = shapes.length,
    -        radius = 0;
    -
    -    for(var i=0; i!==N; i++){
    -        var shape = shapes[i];
    -        shape.updateBoundingSphereRadius();
    -        var offset = shapeOffsets[i].norm(),
    -            r = shape.boundingSphereRadius;
    -        if(offset + r > radius){
    -            radius = offset + r;
    -        }
    -    }
    -
    -    this.boundingRadius = radius;
    -};
    -
    -var computeAABB_shapeAABB = new AABB();
    -
    -/**
    - * Updates the .aabb
    - * @method computeAABB
    - * @todo rename to updateAABB()
    - */
    -Body.prototype.computeAABB = function(){
    -    var shapes = this.shapes,
    -        shapeOffsets = this.shapeOffsets,
    -        shapeOrientations = this.shapeOrientations,
    -        N = shapes.length,
    -        offset = tmpVec,
    -        orientation = tmpQuat,
    -        bodyQuat = this.quaternion,
    -        aabb = this.aabb,
    -        shapeAABB = computeAABB_shapeAABB;
    -
    -    for(var i=0; i!==N; i++){
    -        var shape = shapes[i];
    -
    -        // Get shape world quaternion
    -        shapeOrientations[i].mult(bodyQuat, orientation);
    -
    -        // Get shape world position
    -        orientation.vmult(shapeOffsets[i], offset);
    -        offset.vadd(this.position, offset);
    -
    -        // vec2.rotate(offset, shapeOffsets[i], bodyAngle);
    -        // vec2.add(offset, offset, this.position);
    -
    -        // Get shape AABB
    -        shape.calculateWorldAABB(offset, orientation, shapeAABB.lowerBound, shapeAABB.upperBound);
    -
    -        if(i === 0){
    -            aabb.copy(shapeAABB);
    -        } else {
    -            aabb.extend(shapeAABB);
    -        }
    -    }
    -
    -    this.aabbNeedsUpdate = false;
    -};
    -
    -var uiw_m1 = new Mat3(),
    -    uiw_m2 = new Mat3(),
    -    uiw_m3 = new Mat3();
    -
    -/**
    - * Update .inertiaWorld and .invInertiaWorld
    - * @method updateInertiaWorld
    - */
    -Body.prototype.updateInertiaWorld = function(force){
    -    var I = this.invInertia;
    -    if (I.x === I.y && I.y === I.z && !force) {
    -        // If inertia M = s*I, where I is identity and s a scalar, then
    -        //    R*M*R' = R*(s*I)*R' = s*R*I*R' = s*R*R' = s*I = M
    -        // where R is the rotation matrix.
    -        // In other words, we don't have to transform the inertia if all
    -        // inertia diagonal entries are equal.
    -    } else {
    -        var m1 = uiw_m1,
    -            m2 = uiw_m2,
    -            m3 = uiw_m3;
    -        m1.setRotationFromQuaternion(this.quaternion);
    -        m1.transpose(m2);
    -        m1.scale(I,m1);
    -        m1.mmult(m2,this.invInertiaWorld);
    -        //m3.getTrace(this.invInertiaWorld);
    -    }
    -
    -    /*
    -    this.quaternion.vmult(this.inertia,this.inertiaWorld);
    -    this.quaternion.vmult(this.invInertia,this.invInertiaWorld);
    -    */
    -};
    -
    -/**
    - * Apply force to a world point. This could for example be a point on the Body surface. Applying force this way will add to Body.force and Body.torque.
    - * @method applyForce
    - * @param  {Vec3} force The amount of force to add.
    - * @param  {Vec3} worldPoint A world point to apply the force on.
    - */
    -var Body_applyForce_r = new Vec3();
    -var Body_applyForce_rotForce = new Vec3();
    -Body.prototype.applyForce = function(force,worldPoint){
    -    if(this.type !== Body.DYNAMIC){
    -        return;
    -    }
    -
    -    // Compute point position relative to the body center
    -    var r = Body_applyForce_r;
    -    worldPoint.vsub(this.position,r);
    -
    -    // Compute produced rotational force
    -    var rotForce = Body_applyForce_rotForce;
    -    r.cross(force,rotForce);
    -
    -    // Add linear force
    -    this.force.vadd(force,this.force);
    -
    -    // Add rotational force
    -    this.torque.vadd(rotForce,this.torque);
    -};
    -
    -/**
    - * Apply force to a local point in the body.
    - * @method applyLocalForce
    - * @param  {Vec3} force The force vector to apply, defined locally in the body frame.
    - * @param  {Vec3} localPoint A local point in the body to apply the force on.
    - */
    -var Body_applyLocalForce_worldForce = new Vec3();
    -var Body_applyLocalForce_worldPoint = new Vec3();
    -Body.prototype.applyLocalForce = function(localForce, localPoint){
    -    if(this.type !== Body.DYNAMIC){
    -        return;
    -    }
    -
    -    var worldForce = Body_applyLocalForce_worldForce;
    -    var worldPoint = Body_applyLocalForce_worldPoint;
    -
    -    // Transform the force vector to world space
    -    this.vectorToWorldFrame(localForce, worldForce);
    -    this.pointToWorldFrame(localPoint, worldPoint);
    -
    -    this.applyForce(worldForce, worldPoint);
    -};
    -
    -/**
    - * Apply impulse to a world point. This could for example be a point on the Body surface. An impulse is a force added to a body during a short period of time (impulse = force * time). Impulses will be added to Body.velocity and Body.angularVelocity.
    - * @method applyImpulse
    - * @param  {Vec3} impulse The amount of impulse to add.
    - * @param  {Vec3} worldPoint A world point to apply the force on.
    - */
    -var Body_applyImpulse_r = new Vec3();
    -var Body_applyImpulse_velo = new Vec3();
    -var Body_applyImpulse_rotVelo = new Vec3();
    -Body.prototype.applyImpulse = function(impulse, worldPoint){
    -    if(this.type !== Body.DYNAMIC){
    -        return;
    -    }
    -
    -    // Compute point position relative to the body center
    -    var r = Body_applyImpulse_r;
    -    worldPoint.vsub(this.position,r);
    -
    -    // Compute produced central impulse velocity
    -    var velo = Body_applyImpulse_velo;
    -    velo.copy(impulse);
    -    velo.mult(this.invMass,velo);
    -
    -    // Add linear impulse
    -    this.velocity.vadd(velo, this.velocity);
    -
    -    // Compute produced rotational impulse velocity
    -    var rotVelo = Body_applyImpulse_rotVelo;
    -    r.cross(impulse,rotVelo);
    -
    -    /*
    -    rotVelo.x *= this.invInertia.x;
    -    rotVelo.y *= this.invInertia.y;
    -    rotVelo.z *= this.invInertia.z;
    -    */
    -    this.invInertiaWorld.vmult(rotVelo,rotVelo);
    -
    -    // Add rotational Impulse
    -    this.angularVelocity.vadd(rotVelo, this.angularVelocity);
    -};
    -
    -/**
    - * Apply locally-defined impulse to a local point in the body.
    - * @method applyLocalImpulse
    - * @param  {Vec3} force The force vector to apply, defined locally in the body frame.
    - * @param  {Vec3} localPoint A local point in the body to apply the force on.
    - */
    -var Body_applyLocalImpulse_worldImpulse = new Vec3();
    -var Body_applyLocalImpulse_worldPoint = new Vec3();
    -Body.prototype.applyLocalImpulse = function(localImpulse, localPoint){
    -    if(this.type !== Body.DYNAMIC){
    -        return;
    -    }
    -
    -    var worldImpulse = Body_applyLocalImpulse_worldImpulse;
    -    var worldPoint = Body_applyLocalImpulse_worldPoint;
    -
    -    // Transform the force vector to world space
    -    this.vectorToWorldFrame(localImpulse, worldImpulse);
    -    this.pointToWorldFrame(localPoint, worldPoint);
    -
    -    this.applyImpulse(worldImpulse, worldPoint);
    -};
    -
    -var Body_updateMassProperties_halfExtents = new Vec3();
    -
    -/**
    - * Should be called whenever you change the body shape or mass.
    - * @method updateMassProperties
    - */
    -Body.prototype.updateMassProperties = function(){
    -    var halfExtents = Body_updateMassProperties_halfExtents;
    -
    -    this.invMass = this.mass > 0 ? 1.0 / this.mass : 0;
    -    var I = this.inertia;
    -    var fixed = this.fixedRotation;
    -
    -    // Approximate with AABB box
    -    this.computeAABB();
    -    halfExtents.set(
    -        (this.aabb.upperBound.x-this.aabb.lowerBound.x) / 2,
    -        (this.aabb.upperBound.y-this.aabb.lowerBound.y) / 2,
    -        (this.aabb.upperBound.z-this.aabb.lowerBound.z) / 2
    -    );
    -    Box.calculateInertia(halfExtents, this.mass, I);
    -
    -    this.invInertia.set(
    -        I.x > 0 && !fixed ? 1.0 / I.x : 0,
    -        I.y > 0 && !fixed ? 1.0 / I.y : 0,
    -        I.z > 0 && !fixed ? 1.0 / I.z : 0
    -    );
    -    this.updateInertiaWorld(true);
    -};
    -
    -/**
    - * Get world velocity of a point in the body.
    - * @method getVelocityAtWorldPoint
    - * @param  {Vec3} worldPoint
    - * @param  {Vec3} result
    - * @return {Vec3} The result vector.
    - */
    -Body.prototype.getVelocityAtWorldPoint = function(worldPoint, result){
    -    var r = new Vec3();
    -    worldPoint.vsub(this.position, r);
    -    this.angularVelocity.cross(r, result);
    -    this.velocity.vadd(result, result);
    -    return result;
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_objects_RaycastVehicle.js.html b/docs/files/src_objects_RaycastVehicle.js.html deleted file mode 100644 index f2bc31587..000000000 --- a/docs/files/src_objects_RaycastVehicle.js.html +++ /dev/null @@ -1,856 +0,0 @@ - - - - - src/objects/RaycastVehicle.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/objects/RaycastVehicle.js

    - -
    -
    -var Body = require('./Body');
    -var Vec3 = require('../math/Vec3');
    -var Quaternion = require('../math/Quaternion');
    -var RaycastResult = require('../collision/RaycastResult');
    -var Ray = require('../collision/Ray');
    -var WheelInfo = require('../objects/WheelInfo');
    -
    -module.exports = RaycastVehicle;
    -
    -/**
    - * Vehicle helper class that casts rays from the wheel positions towards the ground and applies forces.
    - * @class RaycastVehicle
    - * @constructor
    - * @param {object} [options]
    - * @param {Body} [options.chassisBody] The car chassis body.
    - * @param {integer} [options.indexRightAxis] Axis to use for right. x=0, y=1, z=2
    - * @param {integer} [options.indexLeftAxis]
    - * @param {integer} [options.indexUpAxis]
    - */
    -function RaycastVehicle(options){
    -
    -    /**
    -     * @property {Body} chassisBody
    -     */
    -    this.chassisBody = options.chassisBody;
    -
    -    /**
    -     * An array of WheelInfo objects.
    -     * @property {array} wheelInfos
    -     */
    -    this.wheelInfos = [];
    -
    -    /**
    -     * Will be set to true if the car is sliding.
    -     * @property {boolean} sliding
    -     */
    -    this.sliding = false;
    -
    -    /**
    -     * @property {World} world
    -     */
    -    this.world = null;
    -
    -    /**
    -     * Index of the right axis, 0=x, 1=y, 2=z
    -     * @property {integer} indexRightAxis
    -     * @default 1
    -     */
    -    this.indexRightAxis = typeof(options.indexRightAxis) !== 'undefined' ? options.indexRightAxis : 1;
    -
    -    /**
    -     * Index of the forward axis, 0=x, 1=y, 2=z
    -     * @property {integer} indexForwardAxis
    -     * @default 0
    -     */
    -    this.indexForwardAxis = typeof(options.indexForwardAxis) !== 'undefined' ? options.indexForwardAxis : 0;
    -
    -    /**
    -     * Index of the up axis, 0=x, 1=y, 2=z
    -     * @property {integer} indexUpAxis
    -     * @default 2
    -     */
    -    this.indexUpAxis = typeof(options.indexUpAxis) !== 'undefined' ? options.indexUpAxis : 2;
    -}
    -
    -var tmpVec1 = new Vec3();
    -var tmpVec2 = new Vec3();
    -var tmpVec3 = new Vec3();
    -var tmpVec4 = new Vec3();
    -var tmpVec5 = new Vec3();
    -var tmpVec6 = new Vec3();
    -var tmpRay = new Ray();
    -
    -/**
    - * Add a wheel. For information about the options, see WheelInfo.
    - * @method addWheel
    - * @param {object} [options]
    - */
    -RaycastVehicle.prototype.addWheel = function(options){
    -    options = options || {};
    -
    -    var info = new WheelInfo(options);
    -    var index = this.wheelInfos.length;
    -    this.wheelInfos.push(info);
    -
    -    return index;
    -};
    -
    -/**
    - * Set the steering value of a wheel.
    - * @method setSteeringValue
    - * @param {number} value
    - * @param {integer} wheelIndex
    - */
    -RaycastVehicle.prototype.setSteeringValue = function(value, wheelIndex){
    -    var wheel = this.wheelInfos[wheelIndex];
    -    wheel.steering = value;
    -};
    -
    -var torque = new Vec3();
    -
    -/**
    - * Set the wheel force to apply on one of the wheels each time step
    - * @method applyEngineForce
    - * @param  {number} value
    - * @param  {integer} wheelIndex
    - */
    -RaycastVehicle.prototype.applyEngineForce = function(value, wheelIndex){
    -    this.wheelInfos[wheelIndex].engineForce = value;
    -};
    -
    -/**
    - * Set the braking force of a wheel
    - * @method setBrake
    - * @param {number} brake
    - * @param {integer} wheelIndex
    - */
    -RaycastVehicle.prototype.setBrake = function(brake, wheelIndex){
    -    this.wheelInfos[wheelIndex].brake = brake;
    -};
    -
    -/**
    - * Add the vehicle including its constraints to the world.
    - * @method addToWorld
    - * @param {World} world
    - */
    -RaycastVehicle.prototype.addToWorld = function(world){
    -    var constraints = this.constraints;
    -    world.add(this.chassisBody);
    -    var that = this;
    -    this.preStepCallback = function(){
    -        that.updateVehicle(world.dt);
    -    };
    -    world.addEventListener('preStep', this.preStepCallback);
    -    this.world = world;
    -};
    -
    -/**
    - * Get one of the wheel axles, world-oriented.
    - * @private
    - * @method getVehicleAxisWorld
    - * @param  {integer} axisIndex
    - * @param  {Vec3} result
    - */
    -RaycastVehicle.prototype.getVehicleAxisWorld = function(axisIndex, result){
    -    result.set(
    -        axisIndex === 0 ? 1 : 0,
    -        axisIndex === 1 ? 1 : 0,
    -        axisIndex === 2 ? 1 : 0
    -    );
    -    this.chassisBody.vectorToWorldFrame(result, result);
    -};
    -
    -RaycastVehicle.prototype.updateVehicle = function(timeStep){
    -    var wheelInfos = this.wheelInfos;
    -    var numWheels = wheelInfos.length;
    -    var chassisBody = this.chassisBody;
    -
    -    for (var i = 0; i < numWheels; i++) {
    -        this.updateWheelTransform(i);
    -    }
    -
    -    this.currentVehicleSpeedKmHour = 3.6 * chassisBody.velocity.norm();
    -
    -    var forwardWorld = new Vec3();
    -    this.getVehicleAxisWorld(this.indexForwardAxis, forwardWorld);
    -
    -    if (forwardWorld.dot(chassisBody.velocity) < 0){
    -        this.currentVehicleSpeedKmHour *= -1;
    -    }
    -
    -    // simulate suspension
    -    for (var i = 0; i < numWheels; i++) {
    -        this.castRay(wheelInfos[i]);
    -    }
    -
    -    this.updateSuspension(timeStep);
    -
    -    var impulse = new Vec3();
    -    var relpos = new Vec3();
    -    for (var i = 0; i < numWheels; i++) {
    -        //apply suspension force
    -        var wheel = wheelInfos[i];
    -        var suspensionForce = wheel.suspensionForce;
    -        if (suspensionForce > wheel.maxSuspensionForce) {
    -            suspensionForce = wheel.maxSuspensionForce;
    -        }
    -        wheel.raycastResult.hitNormalWorld.scale(suspensionForce * timeStep, impulse);
    -
    -        wheel.raycastResult.hitPointWorld.vsub(chassisBody.position, relpos);
    -        chassisBody.applyImpulse(impulse, wheel.raycastResult.hitPointWorld/*relpos*/);
    -    }
    -
    -    this.updateFriction(timeStep);
    -
    -    var hitNormalWorldScaledWithProj = new Vec3();
    -    var fwd  = new Vec3();
    -    var vel = new Vec3();
    -    for (i = 0; i < numWheels; i++) {
    -        var wheel = wheelInfos[i];
    -        //var relpos = new Vec3();
    -        //wheel.chassisConnectionPointWorld.vsub(chassisBody.position, relpos);
    -        chassisBody.getVelocityAtWorldPoint(wheel.chassisConnectionPointWorld, vel);
    -
    -        // Hack to get the rotation in the correct direction
    -        var m = 1;
    -        switch(this.indexUpAxis){
    -        case 1:
    -            m = -1;
    -            break;
    -        }
    -
    -        if (wheel.isInContact) {
    -
    -            this.getVehicleAxisWorld(this.indexForwardAxis, fwd);
    -            var proj = fwd.dot(wheel.raycastResult.hitNormalWorld);
    -            wheel.raycastResult.hitNormalWorld.scale(proj, hitNormalWorldScaledWithProj);
    -
    -            fwd.vsub(hitNormalWorldScaledWithProj, fwd);
    -
    -            var proj2 = fwd.dot(vel);
    -            wheel.deltaRotation = m * proj2 * timeStep / wheel.radius;
    -        }
    -
    -        if((wheel.sliding || !wheel.isInContact) && wheel.engineForce !== 0 && wheel.useCustomSlidingRotationalSpeed){
    -            // Apply custom rotation when accelerating and sliding
    -            wheel.deltaRotation = (wheel.engineForce > 0 ? 1 : -1) * wheel.customSlidingRotationalSpeed * timeStep;
    -        }
    -
    -        // Lock wheels
    -        if(Math.abs(wheel.brake) > Math.abs(wheel.engineForce)){
    -            wheel.deltaRotation = 0;
    -        }
    -
    -        wheel.rotation += wheel.deltaRotation; // Use the old value
    -        wheel.deltaRotation *= 0.99; // damping of rotation when not in contact
    -    }
    -};
    -
    -RaycastVehicle.prototype.updateSuspension = function(deltaTime) {
    -    var chassisBody = this.chassisBody;
    -    var chassisMass = chassisBody.mass;
    -    var wheelInfos = this.wheelInfos;
    -    var numWheels = wheelInfos.length;
    -
    -    for (var w_it = 0; w_it < numWheels; w_it++){
    -        var wheel = wheelInfos[w_it];
    -
    -        if (wheel.isInContact){
    -            var force;
    -
    -            // Spring
    -            var susp_length = wheel.suspensionRestLength;
    -            var current_length = wheel.suspensionLength;
    -            var length_diff = (susp_length - current_length);
    -
    -            force = wheel.suspensionStiffness * length_diff * wheel.clippedInvContactDotSuspension;
    -
    -            // Damper
    -            var projected_rel_vel = wheel.suspensionRelativeVelocity;
    -            var susp_damping;
    -            if (projected_rel_vel < 0) {
    -                susp_damping = wheel.dampingCompression;
    -            } else {
    -                susp_damping = wheel.dampingRelaxation;
    -            }
    -            force -= susp_damping * projected_rel_vel;
    -
    -            wheel.suspensionForce = force * chassisMass;
    -            if (wheel.suspensionForce < 0) {
    -                wheel.suspensionForce = 0;
    -            }
    -        } else {
    -            wheel.suspensionForce = 0;
    -        }
    -    }
    -};
    -
    -/**
    - * Remove the vehicle including its constraints from the world.
    - * @method removeFromWorld
    - * @param {World} world
    - */
    -RaycastVehicle.prototype.removeFromWorld = function(world){
    -    var constraints = this.constraints;
    -    world.remove(this.chassisBody);
    -    world.removeEventListener('preStep', this.preStepCallback);
    -    this.world = null;
    -};
    -
    -var castRay_rayvector = new Vec3();
    -var castRay_target = new Vec3();
    -RaycastVehicle.prototype.castRay = function(wheel) {
    -    var rayvector = castRay_rayvector;
    -    var target = castRay_target;
    -
    -    this.updateWheelTransformWorld(wheel);
    -    var chassisBody = this.chassisBody;
    -
    -    var depth = -1;
    -
    -    var raylen = wheel.suspensionRestLength + wheel.radius;
    -
    -    wheel.directionWorld.scale(raylen, rayvector);
    -    var source = wheel.chassisConnectionPointWorld;
    -    source.vadd(rayvector, target);
    -    var raycastResult = wheel.raycastResult;
    -
    -    var param = 0;
    -
    -    raycastResult.reset();
    -    // Turn off ray collision with the chassis temporarily
    -    var oldState = chassisBody.collisionResponse;
    -    chassisBody.collisionResponse = false;
    -
    -    // Cast ray against world
    -    this.world.rayTest(source, target, raycastResult);
    -    chassisBody.collisionResponse = oldState;
    -
    -    var object = raycastResult.body;
    -
    -    wheel.raycastResult.groundObject = 0;
    -
    -    if (object) {
    -        depth = raycastResult.distance;
    -        wheel.raycastResult.hitNormalWorld  = raycastResult.hitNormalWorld;
    -        wheel.isInContact = true;
    -
    -        var hitDistance = raycastResult.distance;
    -        wheel.suspensionLength = hitDistance - wheel.radius;
    -
    -        // clamp on max suspension travel
    -        var minSuspensionLength = wheel.suspensionRestLength - wheel.maxSuspensionTravel;
    -        var maxSuspensionLength = wheel.suspensionRestLength + wheel.maxSuspensionTravel;
    -        if (wheel.suspensionLength < minSuspensionLength) {
    -            wheel.suspensionLength = minSuspensionLength;
    -        }
    -        if (wheel.suspensionLength > maxSuspensionLength) {
    -            wheel.suspensionLength = maxSuspensionLength;
    -            wheel.raycastResult.reset();
    -        }
    -
    -        var denominator = wheel.raycastResult.hitNormalWorld.dot(wheel.directionWorld);
    -
    -        var chassis_velocity_at_contactPoint = new Vec3();
    -        chassisBody.getVelocityAtWorldPoint(wheel.raycastResult.hitPointWorld, chassis_velocity_at_contactPoint);
    -
    -        var projVel = wheel.raycastResult.hitNormalWorld.dot( chassis_velocity_at_contactPoint );
    -
    -        if (denominator >= -0.1) {
    -            wheel.suspensionRelativeVelocity = 0;
    -            wheel.clippedInvContactDotSuspension = 1 / 0.1;
    -        } else {
    -            var inv = -1 / denominator;
    -            wheel.suspensionRelativeVelocity = projVel * inv;
    -            wheel.clippedInvContactDotSuspension = inv;
    -        }
    -
    -    } else {
    -
    -        //put wheel info as in rest position
    -        wheel.suspensionLength = wheel.suspensionRestLength + 0 * wheel.maxSuspensionTravel;
    -        wheel.suspensionRelativeVelocity = 0.0;
    -        wheel.directionWorld.scale(-1, wheel.raycastResult.hitNormalWorld);
    -        wheel.clippedInvContactDotSuspension = 1.0;
    -    }
    -
    -    return depth;
    -};
    -
    -RaycastVehicle.prototype.updateWheelTransformWorld = function(wheel){
    -    wheel.isInContact = false;
    -    var chassisBody = this.chassisBody;
    -    chassisBody.pointToWorldFrame(wheel.chassisConnectionPointLocal, wheel.chassisConnectionPointWorld);
    -    chassisBody.vectorToWorldFrame(wheel.directionLocal, wheel.directionWorld);
    -    chassisBody.vectorToWorldFrame(wheel.axleLocal, wheel.axleWorld);
    -};
    -
    -
    -/**
    - * Update one of the wheel transform.
    - * Note when rendering wheels: during each step, wheel transforms are updated BEFORE the chassis; ie. their position becomes invalid after the step. Thus when you render wheels, you must update wheel transforms before rendering them. See raycastVehicle demo for an example.
    - * @method updateWheelTransform
    - * @param {integer} wheelIndex The wheel index to update.
    - */
    -RaycastVehicle.prototype.updateWheelTransform = function(wheelIndex){
    -    var up = tmpVec4;
    -    var right = tmpVec5;
    -    var fwd = tmpVec6;
    -
    -    var wheel = this.wheelInfos[wheelIndex];
    -    this.updateWheelTransformWorld(wheel);
    -
    -    wheel.directionLocal.scale(-1, up);
    -    right.copy(wheel.axleLocal);
    -    up.cross(right, fwd);
    -    fwd.normalize();
    -    right.normalize();
    -
    -    // Rotate around steering over the wheelAxle
    -    var steering = wheel.steering;
    -    var steeringOrn = new Quaternion();
    -    steeringOrn.setFromAxisAngle(up, steering);
    -
    -    var rotatingOrn = new Quaternion();
    -    rotatingOrn.setFromAxisAngle(right, wheel.rotation);
    -
    -    // World rotation of the wheel
    -    var q = wheel.worldTransform.quaternion;
    -    this.chassisBody.quaternion.mult(steeringOrn, q);
    -    q.mult(rotatingOrn, q);
    -
    -    q.normalize();
    -
    -    // world position of the wheel
    -    var p = wheel.worldTransform.position;
    -    p.copy(wheel.directionWorld);
    -    p.scale(wheel.suspensionLength, p);
    -    p.vadd(wheel.chassisConnectionPointWorld, p);
    -};
    -
    -var directions = [
    -    new Vec3(1, 0, 0),
    -    new Vec3(0, 1, 0),
    -    new Vec3(0, 0, 1)
    -];
    -
    -/**
    - * Get the world transform of one of the wheels
    - * @method getWheelTransformWorld
    - * @param  {integer} wheelIndex
    - * @return {Transform}
    - */
    -RaycastVehicle.prototype.getWheelTransformWorld = function(wheelIndex) {
    -    return this.wheelInfos[wheelIndex].worldTransform;
    -};
    -
    -
    -var updateFriction_surfNormalWS_scaled_proj = new Vec3();
    -var updateFriction_axle = [];
    -var updateFriction_forwardWS = [];
    -var sideFrictionStiffness2 = 1;
    -RaycastVehicle.prototype.updateFriction = function(timeStep) {
    -    var surfNormalWS_scaled_proj = updateFriction_surfNormalWS_scaled_proj;
    -
    -    //calculate the impulse, so that the wheels don't move sidewards
    -    var wheelInfos = this.wheelInfos;
    -    var numWheels = wheelInfos.length;
    -    var chassisBody = this.chassisBody;
    -    var forwardWS = updateFriction_forwardWS;
    -    var axle = updateFriction_axle;
    -
    -    var numWheelsOnGround = 0;
    -
    -    for (var i = 0; i < numWheels; i++) {
    -        var wheel = wheelInfos[i];
    -
    -        var groundObject = wheel.raycastResult.body;
    -        if (groundObject){
    -            numWheelsOnGround++;
    -        }
    -
    -        wheel.sideImpulse = 0;
    -        wheel.forwardImpulse = 0;
    -        if(!forwardWS[i]){
    -            forwardWS[i] = new Vec3();
    -        }
    -        if(!axle[i]){
    -            axle[i] = new Vec3();
    -        }
    -    }
    -
    -    for (var i = 0; i < numWheels; i++){
    -        var wheel = wheelInfos[i];
    -
    -        var groundObject = wheel.raycastResult.body;
    -
    -        if (groundObject) {
    -            var axlei = axle[i];
    -            var wheelTrans = this.getWheelTransformWorld(i);
    -
    -            // Get world axle
    -            wheelTrans.vectorToWorldFrame(directions[this.indexRightAxis], axlei);
    -
    -            var surfNormalWS = wheel.raycastResult.hitNormalWorld;
    -            var proj = axlei.dot(surfNormalWS);
    -            surfNormalWS.scale(proj, surfNormalWS_scaled_proj);
    -            axlei.vsub(surfNormalWS_scaled_proj, axlei);
    -            axlei.normalize();
    -
    -            surfNormalWS.cross(axlei, forwardWS[i]);
    -            forwardWS[i].normalize();
    -
    -            wheel.sideImpulse = resolveSingleBilateral(
    -                chassisBody,
    -                wheel.raycastResult.hitPointWorld,
    -                groundObject,
    -                wheel.raycastResult.hitPointWorld,
    -                axlei
    -            );
    -
    -            wheel.sideImpulse *= sideFrictionStiffness2;
    -        }
    -    }
    -
    -    var sideFactor = 1;
    -    var fwdFactor = 0.5;
    -
    -    this.sliding = false;
    -    for (var i = 0; i < numWheels; i++) {
    -        var wheel = wheelInfos[i];
    -        var groundObject = wheel.raycastResult.body;
    -
    -        var rollingFriction = 0;
    -
    -        wheel.slipInfo = 1;
    -        if (groundObject) {
    -            var defaultRollingFrictionImpulse = 0;
    -            var maxImpulse = wheel.brake ? wheel.brake : defaultRollingFrictionImpulse;
    -
    -            // btWheelContactPoint contactPt(chassisBody,groundObject,wheelInfraycastInfo.hitPointWorld,forwardWS[wheel],maxImpulse);
    -            // rollingFriction = calcRollingFriction(contactPt);
    -            rollingFriction = calcRollingFriction(chassisBody, groundObject, wheel.raycastResult.hitPointWorld, forwardWS[i], maxImpulse);
    -
    -            rollingFriction += wheel.engineForce * timeStep;
    -
    -            // rollingFriction = 0;
    -            var factor = maxImpulse / rollingFriction;
    -            wheel.slipInfo *= factor;
    -        }
    -
    -        //switch between active rolling (throttle), braking and non-active rolling friction (nthrottle/break)
    -
    -        wheel.forwardImpulse = 0;
    -        wheel.skidInfo = 1;
    -
    -        if (groundObject) {
    -            wheel.skidInfo = 1;
    -
    -            var maximp = wheel.suspensionForce * timeStep * wheel.frictionSlip;
    -            var maximpSide = maximp;
    -
    -            var maximpSquared = maximp * maximpSide;
    -
    -            wheel.forwardImpulse = rollingFriction;//wheelInfo.engineForce* timeStep;
    -
    -            var x = wheel.forwardImpulse * fwdFactor;
    -            var y = wheel.sideImpulse * sideFactor;
    -
    -            var impulseSquared = x * x + y * y;
    -
    -            wheel.sliding = false;
    -            if (impulseSquared > maximpSquared) {
    -                this.sliding = true;
    -                wheel.sliding = true;
    -
    -                var factor = maximp / Math.sqrt(impulseSquared);
    -
    -                wheel.skidInfo *= factor;
    -            }
    -        }
    -    }
    -
    -    if (this.sliding) {
    -        for (var i = 0; i < numWheels; i++) {
    -            var wheel = wheelInfos[i];
    -            if (wheel.sideImpulse !== 0) {
    -                if (wheel.skidInfo < 1){
    -                    wheel.forwardImpulse *= wheel.skidInfo;
    -                    wheel.sideImpulse *= wheel.skidInfo;
    -                }
    -            }
    -        }
    -    }
    -
    -    // apply the impulses
    -    for (var i = 0; i < numWheels; i++) {
    -        var wheel = wheelInfos[i];
    -
    -        var rel_pos = new Vec3();
    -        //wheel.raycastResult.hitPointWorld.vsub(chassisBody.position, rel_pos);
    -        // cannons applyimpulse is using world coord for the position
    -        rel_pos.copy(wheel.raycastResult.hitPointWorld);
    -
    -        if (wheel.forwardImpulse !== 0) {
    -            var impulse = new Vec3();
    -            forwardWS[i].scale(wheel.forwardImpulse, impulse);
    -            chassisBody.applyImpulse(impulse, rel_pos);
    -        }
    -
    -        if (wheel.sideImpulse !== 0){
    -            var groundObject = wheel.raycastResult.body;
    -
    -            var rel_pos2 = new Vec3();
    -            //wheel.raycastResult.hitPointWorld.vsub(groundObject.position, rel_pos2);
    -            rel_pos2.copy(wheel.raycastResult.hitPointWorld);
    -            var sideImp = new Vec3();
    -            axle[i].scale(wheel.sideImpulse, sideImp);
    -
    -            // Scale the relative position in the up direction with rollInfluence.
    -            // If rollInfluence is 1, the impulse will be applied on the hitPoint (easy to roll over), if it is zero it will be applied in the same plane as the center of mass (not easy to roll over).
    -            chassisBody.pointToLocalFrame(rel_pos, rel_pos);
    -            rel_pos['xyz'[this.indexUpAxis]] *= wheel.rollInfluence;
    -            chassisBody.pointToWorldFrame(rel_pos, rel_pos);
    -            chassisBody.applyImpulse(sideImp, rel_pos);
    -
    -            //apply friction impulse on the ground
    -            sideImp.scale(-1, sideImp);
    -            groundObject.applyImpulse(sideImp, rel_pos2);
    -        }
    -    }
    -};
    -
    -var calcRollingFriction_vel1 = new Vec3();
    -var calcRollingFriction_vel2 = new Vec3();
    -var calcRollingFriction_vel = new Vec3();
    -
    -function calcRollingFriction(body0, body1, frictionPosWorld, frictionDirectionWorld, maxImpulse) {
    -    var j1 = 0;
    -    var contactPosWorld = frictionPosWorld;
    -
    -    // var rel_pos1 = new Vec3();
    -    // var rel_pos2 = new Vec3();
    -    var vel1 = calcRollingFriction_vel1;
    -    var vel2 = calcRollingFriction_vel2;
    -    var vel = calcRollingFriction_vel;
    -    // contactPosWorld.vsub(body0.position, rel_pos1);
    -    // contactPosWorld.vsub(body1.position, rel_pos2);
    -
    -    body0.getVelocityAtWorldPoint(contactPosWorld, vel1);
    -    body1.getVelocityAtWorldPoint(contactPosWorld, vel2);
    -    vel1.vsub(vel2, vel);
    -
    -    var vrel = frictionDirectionWorld.dot(vel);
    -
    -    var denom0 = computeImpulseDenominator(body0, frictionPosWorld, frictionDirectionWorld);
    -    var denom1 = computeImpulseDenominator(body1, frictionPosWorld, frictionDirectionWorld);
    -    var relaxation = 1;
    -    var jacDiagABInv = relaxation / (denom0 + denom1);
    -
    -    // calculate j that moves us to zero relative velocity
    -    j1 = -vrel * jacDiagABInv;
    -
    -    if (maxImpulse < j1) {
    -        j1 = maxImpulse;
    -    }
    -    if (j1 < -maxImpulse) {
    -        j1 = -maxImpulse;
    -    }
    -
    -    return j1;
    -}
    -
    -var computeImpulseDenominator_r0 = new Vec3();
    -var computeImpulseDenominator_c0 = new Vec3();
    -var computeImpulseDenominator_vec = new Vec3();
    -var computeImpulseDenominator_m = new Vec3();
    -function computeImpulseDenominator(body, pos, normal) {
    -    var r0 = computeImpulseDenominator_r0;
    -    var c0 = computeImpulseDenominator_c0;
    -    var vec = computeImpulseDenominator_vec;
    -    var m = computeImpulseDenominator_m;
    -
    -    pos.vsub(body.position, r0);
    -    r0.cross(normal, c0);
    -    body.invInertiaWorld.vmult(c0, m);
    -    m.cross(r0, vec);
    -
    -    return body.invMass + normal.dot(vec);
    -}
    -
    -
    -var resolveSingleBilateral_vel1 = new Vec3();
    -var resolveSingleBilateral_vel2 = new Vec3();
    -var resolveSingleBilateral_vel = new Vec3();
    -
    -//bilateral constraint between two dynamic objects
    -function resolveSingleBilateral(body1, pos1, body2, pos2, normal, impulse){
    -    var normalLenSqr = normal.norm2();
    -    if (normalLenSqr > 1.1){
    -        return 0; // no impulse
    -    }
    -    // var rel_pos1 = new Vec3();
    -    // var rel_pos2 = new Vec3();
    -    // pos1.vsub(body1.position, rel_pos1);
    -    // pos2.vsub(body2.position, rel_pos2);
    -
    -    var vel1 = resolveSingleBilateral_vel1;
    -    var vel2 = resolveSingleBilateral_vel2;
    -    var vel = resolveSingleBilateral_vel;
    -    body1.getVelocityAtWorldPoint(pos1, vel1);
    -    body2.getVelocityAtWorldPoint(pos2, vel2);
    -
    -    vel1.vsub(vel2, vel);
    -
    -    var rel_vel = normal.dot(vel);
    -
    -    var contactDamping = 0.2;
    -    var massTerm = 1 / (body1.invMass + body2.invMass);
    -    var impulse = - contactDamping * rel_vel * massTerm;
    -
    -    return impulse;
    -}
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_objects_RigidVehicle.js.html b/docs/files/src_objects_RigidVehicle.js.html deleted file mode 100644 index b2b5841ad..000000000 --- a/docs/files/src_objects_RigidVehicle.js.html +++ /dev/null @@ -1,374 +0,0 @@ - - - - - src/objects/RigidVehicle.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/objects/RigidVehicle.js

    - -
    -
    -var Body = require('./Body');
    -var Sphere = require('../shapes/Sphere');
    -var Box = require('../shapes/Box');
    -var Vec3 = require('../math/Vec3');
    -var HingeConstraint = require('../constraints/HingeConstraint');
    -
    -module.exports = RigidVehicle;
    -
    -/**
    - * Simple vehicle helper class with spherical rigid body wheels.
    - * @class RigidVehicle
    - * @constructor
    - * @param {Body} [options.chassisBody]
    - */
    -function RigidVehicle(options){
    -    this.wheelBodies = [];
    -
    -    /**
    -     * @property coordinateSystem
    -     * @type {Vec3}
    -     */
    -    this.coordinateSystem = typeof(options.coordinateSystem)==='undefined' ? new Vec3(1, 2, 3) : options.coordinateSystem.clone();
    -
    -    /**
    -     * @property {Body} chassisBody
    -     */
    -    this.chassisBody = options.chassisBody;
    -
    -    if(!this.chassisBody){
    -        // No chassis body given. Create it!
    -        var chassisShape = new Box(new Vec3(5, 2, 0.5));
    -        this.chassisBody = new Body(1, chassisShape);
    -    }
    -
    -    /**
    -     * @property constraints
    -     * @type {Array}
    -     */
    -    this.constraints = [];
    -
    -    this.wheelAxes = [];
    -    this.wheelForces = [];
    -}
    -
    -/**
    - * Add a wheel
    - * @method addWheel
    - * @param {object} options
    - * @param {boolean} [options.isFrontWheel]
    - * @param {Vec3} [options.position] Position of the wheel, locally in the chassis body.
    - * @param {Vec3} [options.direction] Slide direction of the wheel along the suspension.
    - * @param {Vec3} [options.axis] Axis of rotation of the wheel, locally defined in the chassis.
    - * @param {Body} [options.body] The wheel body.
    - */
    -RigidVehicle.prototype.addWheel = function(options){
    -    options = options || {};
    -    var wheelBody = options.body;
    -    if(!wheelBody){
    -        wheelBody =  new Body(1, new Sphere(1.2));
    -    }
    -    this.wheelBodies.push(wheelBody);
    -    this.wheelForces.push(0);
    -
    -    // Position constrain wheels
    -    var zero = new Vec3();
    -    var position = typeof(options.position) !== 'undefined' ? options.position.clone() : new Vec3();
    -
    -    // Set position locally to the chassis
    -    var worldPosition = new Vec3();
    -    this.chassisBody.pointToWorldFrame(position, worldPosition);
    -    wheelBody.position.set(worldPosition.x, worldPosition.y, worldPosition.z);
    -
    -    // Constrain wheel
    -    var axis = typeof(options.axis) !== 'undefined' ? options.axis.clone() : new Vec3(0, 1, 0);
    -    this.wheelAxes.push(axis);
    -
    -    var hingeConstraint = new HingeConstraint(this.chassisBody, wheelBody, {
    -        pivotA: position,
    -        axisA: axis,
    -        pivotB: Vec3.ZERO,
    -        axisB: axis,
    -        collideConnected: false
    -    });
    -    this.constraints.push(hingeConstraint);
    -
    -    return this.wheelBodies.length - 1;
    -};
    -
    -/**
    - * Set the steering value of a wheel.
    - * @method setSteeringValue
    - * @param {number} value
    - * @param {integer} wheelIndex
    - * @todo check coordinateSystem
    - */
    -RigidVehicle.prototype.setSteeringValue = function(value, wheelIndex){
    -    // Set angle of the hinge axis
    -    var axis = this.wheelAxes[wheelIndex];
    -
    -    var c = Math.cos(value),
    -        s = Math.sin(value),
    -        x = axis.x,
    -        y = axis.y;
    -    this.constraints[wheelIndex].axisA.set(
    -        c*x -s*y,
    -        s*x +c*y,
    -        0
    -    );
    -};
    -
    -/**
    - * Set the target rotational speed of the hinge constraint.
    - * @method setMotorSpeed
    - * @param {number} value
    - * @param {integer} wheelIndex
    - */
    -RigidVehicle.prototype.setMotorSpeed = function(value, wheelIndex){
    -    var hingeConstraint = this.constraints[wheelIndex];
    -    hingeConstraint.enableMotor();
    -    hingeConstraint.motorTargetVelocity = value;
    -};
    -
    -/**
    - * Set the target rotational speed of the hinge constraint.
    - * @method disableMotor
    - * @param {number} value
    - * @param {integer} wheelIndex
    - */
    -RigidVehicle.prototype.disableMotor = function(wheelIndex){
    -    var hingeConstraint = this.constraints[wheelIndex];
    -    hingeConstraint.disableMotor();
    -};
    -
    -var torque = new Vec3();
    -
    -/**
    - * Set the wheel force to apply on one of the wheels each time step
    - * @method setWheelForce
    - * @param  {number} value
    - * @param  {integer} wheelIndex
    - */
    -RigidVehicle.prototype.setWheelForce = function(value, wheelIndex){
    -    this.wheelForces[wheelIndex] = value;
    -};
    -
    -/**
    - * Apply a torque on one of the wheels.
    - * @method applyWheelForce
    - * @param  {number} value
    - * @param  {integer} wheelIndex
    - */
    -RigidVehicle.prototype.applyWheelForce = function(value, wheelIndex){
    -    var axis = this.wheelAxes[wheelIndex];
    -    var wheelBody = this.wheelBodies[wheelIndex];
    -    var bodyTorque = wheelBody.torque;
    -
    -    axis.scale(value, torque);
    -    wheelBody.vectorToWorldFrame(torque, torque);
    -    bodyTorque.vadd(torque, bodyTorque);
    -};
    -
    -/**
    - * Add the vehicle including its constraints to the world.
    - * @method addToWorld
    - * @param {World} world
    - */
    -RigidVehicle.prototype.addToWorld = function(world){
    -    var constraints = this.constraints;
    -    var bodies = this.wheelBodies.concat([this.chassisBody]);
    -
    -    for (var i = 0; i < bodies.length; i++) {
    -        world.add(bodies[i]);
    -    }
    -
    -    for (var i = 0; i < constraints.length; i++) {
    -        world.addConstraint(constraints[i]);
    -    }
    -
    -    world.addEventListener('preStep', this._update.bind(this));
    -};
    -
    -RigidVehicle.prototype._update = function(){
    -    var wheelForces = this.wheelForces;
    -    for (var i = 0; i < wheelForces.length; i++) {
    -        this.applyWheelForce(wheelForces[i], i);
    -    }
    -};
    -
    -/**
    - * Remove the vehicle including its constraints from the world.
    - * @method removeFromWorld
    - * @param {World} world
    - */
    -RigidVehicle.prototype.removeFromWorld = function(world){
    -    var constraints = this.constraints;
    -    var bodies = this.wheelBodies.concat([this.chassisBody]);
    -
    -    for (var i = 0; i < bodies.length; i++) {
    -        world.remove(bodies[i]);
    -    }
    -
    -    for (var i = 0; i < constraints.length; i++) {
    -        world.removeConstraint(constraints[i]);
    -    }
    -};
    -
    -var worldAxis = new Vec3();
    -
    -/**
    - * Get current rotational velocity of a wheel
    - * @method getWheelSpeed
    - * @param {integer} wheelIndex
    - */
    -RigidVehicle.prototype.getWheelSpeed = function(wheelIndex){
    -    var axis = this.wheelAxes[wheelIndex];
    -    var wheelBody = this.wheelBodies[wheelIndex];
    -    var w = wheelBody.angularVelocity;
    -    this.chassisBody.vectorToWorldFrame(axis, worldAxis);
    -    return w.dot(worldAxis);
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_objects_SPHSystem.js.html b/docs/files/src_objects_SPHSystem.js.html deleted file mode 100644 index 8d73ead30..000000000 --- a/docs/files/src_objects_SPHSystem.js.html +++ /dev/null @@ -1,367 +0,0 @@ - - - - - src/objects/SPHSystem.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/objects/SPHSystem.js

    - -
    -
    -module.exports = SPHSystem;
    -
    -var Shape = require('../shapes/Shape');
    -var Vec3 = require('../math/Vec3');
    -var Quaternion = require('../math/Quaternion');
    -var Particle = require('../shapes/Particle');
    -var Body = require('../objects/Body');
    -var Material = require('../material/Material');
    -
    -/**
    - * Smoothed-particle hydrodynamics system
    - * @class SPHSystem
    - * @constructor
    - */
    -function SPHSystem(){
    -    this.particles = [];
    -	
    -    /**
    -     * Density of the system (kg/m3).
    -     * @property {number} density
    -     */
    -    this.density = 1;
    -	
    -    /**
    -     * Distance below which two particles are considered to be neighbors.
    -     * It should be adjusted so there are about 15-20 neighbor particles within this radius.
    -     * @property {number} smoothingRadius
    -     */
    -    this.smoothingRadius = 1;
    -    this.speedOfSound = 1;
    -	
    -    /**
    -     * Viscosity of the system.
    -     * @property {number} viscosity
    -     */
    -    this.viscosity = 0.01;
    -    this.eps = 0.000001;
    -
    -    // Stuff Computed per particle
    -    this.pressures = [];
    -    this.densities = [];
    -    this.neighbors = [];
    -}
    -
    -/**
    - * Add a particle to the system.
    - * @method add
    - * @param {Body} particle
    - */
    -SPHSystem.prototype.add = function(particle){
    -    this.particles.push(particle);
    -    if(this.neighbors.length < this.particles.length){
    -        this.neighbors.push([]);
    -    }
    -};
    -
    -/**
    - * Remove a particle from the system.
    - * @method remove
    - * @param {Body} particle
    - */
    -SPHSystem.prototype.remove = function(particle){
    -    var idx = this.particles.indexOf(particle);
    -    if(idx !== -1){
    -        this.particles.splice(idx,1);
    -        if(this.neighbors.length > this.particles.length){
    -            this.neighbors.pop();
    -        }
    -    }
    -};
    -
    -/**
    - * Get neighbors within smoothing volume, save in the array neighbors
    - * @method getNeighbors
    - * @param {Body} particle
    - * @param {Array} neighbors
    - */
    -var SPHSystem_getNeighbors_dist = new Vec3();
    -SPHSystem.prototype.getNeighbors = function(particle,neighbors){
    -    var N = this.particles.length,
    -        id = particle.id,
    -        R2 = this.smoothingRadius * this.smoothingRadius,
    -        dist = SPHSystem_getNeighbors_dist;
    -    for(var i=0; i!==N; i++){
    -        var p = this.particles[i];
    -        p.position.vsub(particle.position,dist);
    -        if(id!==p.id && dist.norm2() < R2){
    -            neighbors.push(p);
    -        }
    -    }
    -};
    -
    -// Temp vectors for calculation
    -var SPHSystem_update_dist = new Vec3(),
    -    SPHSystem_update_a_pressure = new Vec3(),
    -    SPHSystem_update_a_visc = new Vec3(),
    -    SPHSystem_update_gradW = new Vec3(),
    -    SPHSystem_update_r_vec = new Vec3(),
    -    SPHSystem_update_u = new Vec3(); // Relative velocity
    -SPHSystem.prototype.update = function(){
    -    var N = this.particles.length,
    -        dist = SPHSystem_update_dist,
    -        cs = this.speedOfSound,
    -        eps = this.eps;
    -
    -    for(var i=0; i!==N; i++){
    -        var p = this.particles[i]; // Current particle
    -        var neighbors = this.neighbors[i];
    -
    -        // Get neighbors
    -        neighbors.length = 0;
    -        this.getNeighbors(p,neighbors);
    -        neighbors.push(this.particles[i]); // Add current too
    -        var numNeighbors = neighbors.length;
    -
    -        // Accumulate density for the particle
    -        var sum = 0.0;
    -        for(var j=0; j!==numNeighbors; j++){
    -
    -            //printf("Current particle has position %f %f %f\n",objects[id].pos.x(),objects[id].pos.y(),objects[id].pos.z());
    -            p.position.vsub(neighbors[j].position, dist);
    -            var len = dist.norm();
    -
    -            var weight = this.w(len);
    -            sum += neighbors[j].mass * weight;
    -        }
    -
    -        // Save
    -        this.densities[i] = sum;
    -        this.pressures[i] = cs * cs * (this.densities[i] - this.density);
    -    }
    -
    -    // Add forces
    -
    -    // Sum to these accelerations
    -    var a_pressure= SPHSystem_update_a_pressure;
    -    var a_visc =    SPHSystem_update_a_visc;
    -    var gradW =     SPHSystem_update_gradW;
    -    var r_vec =     SPHSystem_update_r_vec;
    -    var u =         SPHSystem_update_u;
    -
    -    for(var i=0; i!==N; i++){
    -
    -        var particle = this.particles[i];
    -
    -        a_pressure.set(0,0,0);
    -        a_visc.set(0,0,0);
    -
    -        // Init vars
    -        var Pij;
    -        var nabla;
    -        var Vij;
    -
    -        // Sum up for all other neighbors
    -        var neighbors = this.neighbors[i];
    -        var numNeighbors = neighbors.length;
    -
    -        //printf("Neighbors: ");
    -        for(var j=0; j!==numNeighbors; j++){
    -
    -            var neighbor = neighbors[j];
    -            //printf("%d ",nj);
    -
    -            // Get r once for all..
    -            particle.position.vsub(neighbor.position,r_vec);
    -            var r = r_vec.norm();
    -
    -            // Pressure contribution
    -            Pij = -neighbor.mass * (this.pressures[i] / (this.densities[i]*this.densities[i] + eps) + this.pressures[j] / (this.densities[j]*this.densities[j] + eps));
    -            this.gradw(r_vec, gradW);
    -            // Add to pressure acceleration
    -            gradW.mult(Pij , gradW);
    -            a_pressure.vadd(gradW, a_pressure);
    -
    -            // Viscosity contribution
    -            neighbor.velocity.vsub(particle.velocity, u);
    -            u.mult( 1.0 / (0.0001+this.densities[i] * this.densities[j]) * this.viscosity * neighbor.mass , u );
    -            nabla = this.nablaw(r);
    -            u.mult(nabla,u);
    -            // Add to viscosity acceleration
    -            a_visc.vadd( u, a_visc );
    -        }
    -
    -        // Calculate force
    -        a_visc.mult(particle.mass, a_visc);
    -        a_pressure.mult(particle.mass, a_pressure);
    -
    -        // Add force to particles
    -        particle.force.vadd(a_visc, particle.force);
    -        particle.force.vadd(a_pressure, particle.force);
    -    }
    -};
    -
    -// Calculate the weight using the W(r) weightfunction
    -SPHSystem.prototype.w = function(r){
    -    // 315
    -    var h = this.smoothingRadius;
    -    return 315.0/(64.0*Math.PI*Math.pow(h,9)) * Math.pow(h*h-r*r,3);
    -};
    -
    -// calculate gradient of the weight function
    -SPHSystem.prototype.gradw = function(rVec,resultVec){
    -    var r = rVec.norm(),
    -        h = this.smoothingRadius;
    -    rVec.mult(945.0/(32.0*Math.PI*Math.pow(h,9)) * Math.pow((h*h-r*r),2) , resultVec);
    -};
    -
    -// Calculate nabla(W)
    -SPHSystem.prototype.nablaw = function(r){
    -    var h = this.smoothingRadius;
    -    var nabla = 945.0/(32.0*Math.PI*Math.pow(h,9)) * (h*h-r*r)*(7*r*r - 3*h*h);
    -    return nabla;
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_objects_Spring.js.html b/docs/files/src_objects_Spring.js.html deleted file mode 100644 index 1a00bacfd..000000000 --- a/docs/files/src_objects_Spring.js.html +++ /dev/null @@ -1,347 +0,0 @@ - - - - - src/objects/Spring.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/objects/Spring.js

    - -
    -
    -var Vec3 = require('../math/Vec3');
    -
    -module.exports = Spring;
    -
    -/**
    - * A spring, connecting two bodies.
    - *
    - * @class Spring
    - * @constructor
    - * @param {Body} bodyA
    - * @param {Body} bodyB
    - * @param {Object} [options]
    - * @param {number} [options.restLength]   A number > 0. Default: 1
    - * @param {number} [options.stiffness]    A number >= 0. Default: 100
    - * @param {number} [options.damping]      A number >= 0. Default: 1
    - * @param {Vec3}  [options.worldAnchorA] Where to hook the spring to body A, in world coordinates.
    - * @param {Vec3}  [options.worldAnchorB]
    - * @param {Vec3}  [options.localAnchorA] Where to hook the spring to body A, in local body coordinates.
    - * @param {Vec3}  [options.localAnchorB]
    - */
    -function Spring(bodyA,bodyB,options){
    -    options = options || {};
    -
    -    /**
    -     * Rest length of the spring.
    -     * @property restLength
    -     * @type {number}
    -     */
    -    this.restLength = typeof(options.restLength) === "number" ? options.restLength : 1;
    -
    -    /**
    -     * Stiffness of the spring.
    -     * @property stiffness
    -     * @type {number}
    -     */
    -    this.stiffness = options.stiffness || 100;
    -
    -    /**
    -     * Damping of the spring.
    -     * @property damping
    -     * @type {number}
    -     */
    -    this.damping = options.damping || 1;
    -
    -    /**
    -     * First connected body.
    -     * @property bodyA
    -     * @type {Body}
    -     */
    -    this.bodyA = bodyA;
    -
    -    /**
    -     * Second connected body.
    -     * @property bodyB
    -     * @type {Body}
    -     */
    -    this.bodyB = bodyB;
    -
    -    /**
    -     * Anchor for bodyA in local bodyA coordinates.
    -     * @property localAnchorA
    -     * @type {Vec3}
    -     */
    -    this.localAnchorA = new Vec3();
    -
    -    /**
    -     * Anchor for bodyB in local bodyB coordinates.
    -     * @property localAnchorB
    -     * @type {Vec3}
    -     */
    -    this.localAnchorB = new Vec3();
    -
    -    if(options.localAnchorA){
    -        this.localAnchorA.copy(options.localAnchorA);
    -    }
    -    if(options.localAnchorB){
    -        this.localAnchorB.copy(options.localAnchorB);
    -    }
    -    if(options.worldAnchorA){
    -        this.setWorldAnchorA(options.worldAnchorA);
    -    }
    -    if(options.worldAnchorB){
    -        this.setWorldAnchorB(options.worldAnchorB);
    -    }
    -}
    -
    -/**
    - * Set the anchor point on body A, using world coordinates.
    - * @method setWorldAnchorA
    - * @param {Vec3} worldAnchorA
    - */
    -Spring.prototype.setWorldAnchorA = function(worldAnchorA){
    -    this.bodyA.pointToLocalFrame(worldAnchorA,this.localAnchorA);
    -};
    -
    -/**
    - * Set the anchor point on body B, using world coordinates.
    - * @method setWorldAnchorB
    - * @param {Vec3} worldAnchorB
    - */
    -Spring.prototype.setWorldAnchorB = function(worldAnchorB){
    -    this.bodyB.pointToLocalFrame(worldAnchorB,this.localAnchorB);
    -};
    -
    -/**
    - * Get the anchor point on body A, in world coordinates.
    - * @method getWorldAnchorA
    - * @param {Vec3} result The vector to store the result in.
    - */
    -Spring.prototype.getWorldAnchorA = function(result){
    -    this.bodyA.pointToWorldFrame(this.localAnchorA,result);
    -};
    -
    -/**
    - * Get the anchor point on body B, in world coordinates.
    - * @method getWorldAnchorB
    - * @param {Vec3} result The vector to store the result in.
    - */
    -Spring.prototype.getWorldAnchorB = function(result){
    -    this.bodyB.pointToWorldFrame(this.localAnchorB,result);
    -};
    -
    -var applyForce_r =              new Vec3(),
    -    applyForce_r_unit =         new Vec3(),
    -    applyForce_u =              new Vec3(),
    -    applyForce_f =              new Vec3(),
    -    applyForce_worldAnchorA =   new Vec3(),
    -    applyForce_worldAnchorB =   new Vec3(),
    -    applyForce_ri =             new Vec3(),
    -    applyForce_rj =             new Vec3(),
    -    applyForce_ri_x_f =         new Vec3(),
    -    applyForce_rj_x_f =         new Vec3(),
    -    applyForce_tmp =            new Vec3();
    -
    -/**
    - * Apply the spring force to the connected bodies.
    - * @method applyForce
    - */
    -Spring.prototype.applyForce = function(){
    -    var k = this.stiffness,
    -        d = this.damping,
    -        l = this.restLength,
    -        bodyA = this.bodyA,
    -        bodyB = this.bodyB,
    -        r = applyForce_r,
    -        r_unit = applyForce_r_unit,
    -        u = applyForce_u,
    -        f = applyForce_f,
    -        tmp = applyForce_tmp;
    -
    -    var worldAnchorA = applyForce_worldAnchorA,
    -        worldAnchorB = applyForce_worldAnchorB,
    -        ri = applyForce_ri,
    -        rj = applyForce_rj,
    -        ri_x_f = applyForce_ri_x_f,
    -        rj_x_f = applyForce_rj_x_f;
    -
    -    // Get world anchors
    -    this.getWorldAnchorA(worldAnchorA);
    -    this.getWorldAnchorB(worldAnchorB);
    -
    -    // Get offset points
    -    worldAnchorA.vsub(bodyA.position,ri);
    -    worldAnchorB.vsub(bodyB.position,rj);
    -
    -    // Compute distance vector between world anchor points
    -    worldAnchorB.vsub(worldAnchorA,r);
    -    var rlen = r.norm();
    -    r_unit.copy(r);
    -    r_unit.normalize();
    -
    -    // Compute relative velocity of the anchor points, u
    -    bodyB.velocity.vsub(bodyA.velocity,u);
    -    // Add rotational velocity
    -
    -    bodyB.angularVelocity.cross(rj,tmp);
    -    u.vadd(tmp,u);
    -    bodyA.angularVelocity.cross(ri,tmp);
    -    u.vsub(tmp,u);
    -
    -    // F = - k * ( x - L ) - D * ( u )
    -    r_unit.mult(-k*(rlen-l) - d*u.dot(r_unit), f);
    -
    -    // Add forces to bodies
    -    bodyA.force.vsub(f,bodyA.force);
    -    bodyB.force.vadd(f,bodyB.force);
    -
    -    // Angular force
    -    ri.cross(f,ri_x_f);
    -    rj.cross(f,rj_x_f);
    -    bodyA.torque.vsub(ri_x_f,bodyA.torque);
    -    bodyB.torque.vadd(rj_x_f,bodyB.torque);
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_objects_WheelInfo.js.html b/docs/files/src_objects_WheelInfo.js.html deleted file mode 100644 index ba8d53e8e..000000000 --- a/docs/files/src_objects_WheelInfo.js.html +++ /dev/null @@ -1,435 +0,0 @@ - - - - - src/objects/WheelInfo.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/objects/WheelInfo.js

    - -
    -
    -var Vec3 = require('../math/Vec3');
    -var Transform = require('../math/Transform');
    -var RaycastResult = require('../collision/RaycastResult');
    -var Utils = require('../utils/Utils');
    -
    -module.exports = WheelInfo;
    -
    -/**
    - * @class WheelInfo
    - * @constructor
    - * @param {Object} [options]
    - *
    - * @param {Vec3} [options.chassisConnectionPointLocal]
    - * @param {Vec3} [options.chassisConnectionPointWorld]
    - * @param {Vec3} [options.directionLocal]
    - * @param {Vec3} [options.directionWorld]
    - * @param {Vec3} [options.axleLocal]
    - * @param {Vec3} [options.axleWorld]
    - * @param {number} [options.suspensionRestLength=1]
    - * @param {number} [options.suspensionMaxLength=2]
    - * @param {number} [options.radius=1]
    - * @param {number} [options.suspensionStiffness=100]
    - * @param {number} [options.dampingCompression=10]
    - * @param {number} [options.dampingRelaxation=10]
    - * @param {number} [options.frictionSlip=10000]
    - * @param {number} [options.steering=0]
    - * @param {number} [options.rotation=0]
    - * @param {number} [options.deltaRotation=0]
    - * @param {number} [options.rollInfluence=0.01]
    - * @param {number} [options.maxSuspensionForce]
    - * @param {boolean} [options.isFrontWheel=true]
    - * @param {number} [options.clippedInvContactDotSuspension=1]
    - * @param {number} [options.suspensionRelativeVelocity=0]
    - * @param {number} [options.suspensionForce=0]
    - * @param {number} [options.skidInfo=0]
    - * @param {number} [options.suspensionLength=0]
    - * @param {number} [options.maxSuspensionTravel=1]
    - * @param {boolean} [options.useCustomSlidingRotationalSpeed=false]
    - * @param {number} [options.customSlidingRotationalSpeed=-0.1]
    - */
    -function WheelInfo(options){
    -    options = Utils.defaults(options, {
    -        chassisConnectionPointLocal: new Vec3(),
    -        chassisConnectionPointWorld: new Vec3(),
    -        directionLocal: new Vec3(),
    -        directionWorld: new Vec3(),
    -        axleLocal: new Vec3(),
    -        axleWorld: new Vec3(),
    -        suspensionRestLength: 1,
    -        suspensionMaxLength: 2,
    -        radius: 1,
    -        suspensionStiffness: 100,
    -        dampingCompression: 10,
    -        dampingRelaxation: 10,
    -        frictionSlip: 10000,
    -        steering: 0,
    -        rotation: 0,
    -        deltaRotation: 0,
    -        rollInfluence: 0.01,
    -        maxSuspensionForce: Number.MAX_VALUE,
    -        isFrontWheel: true,
    -        clippedInvContactDotSuspension: 1,
    -        suspensionRelativeVelocity: 0,
    -        suspensionForce: 0,
    -        skidInfo: 0,
    -        suspensionLength: 0,
    -        maxSuspensionTravel: 1,
    -        useCustomSlidingRotationalSpeed: false,
    -        customSlidingRotationalSpeed: -0.1
    -    });
    -
    -    /**
    -     * Max travel distance of the suspension, in meters.
    -     * @property {number} maxSuspensionTravel
    -     */
    -    this.maxSuspensionTravel = options.maxSuspensionTravel;
    -
    -    /**
    -     * Speed to apply to the wheel rotation when the wheel is sliding.
    -     * @property {number} customSlidingRotationalSpeed
    -     */
    -    this.customSlidingRotationalSpeed = options.customSlidingRotationalSpeed;
    -
    -    /**
    -     * If the customSlidingRotationalSpeed should be used.
    -     * @property {Boolean} useCustomSlidingRotationalSpeed
    -     */
    -    this.useCustomSlidingRotationalSpeed = options.useCustomSlidingRotationalSpeed;
    -
    -    /**
    -     * @property {Boolean} sliding
    -     */
    -    this.sliding = false;
    -
    -    /**
    -     * Connection point, defined locally in the chassis body frame.
    -     * @property {Vec3} chassisConnectionPointLocal
    -     */
    -    this.chassisConnectionPointLocal = options.chassisConnectionPointLocal.clone();
    -
    -    /**
    -     * @property {Vec3} chassisConnectionPointWorld
    -     */
    -    this.chassisConnectionPointWorld = options.chassisConnectionPointWorld.clone();
    -
    -    /**
    -     * @property {Vec3} directionLocal
    -     */
    -    this.directionLocal = options.directionLocal.clone();
    -
    -    /**
    -     * @property {Vec3} directionWorld
    -     */
    -    this.directionWorld = options.directionWorld.clone();
    -
    -    /**
    -     * @property {Vec3} axleLocal
    -     */
    -    this.axleLocal = options.axleLocal.clone();
    -
    -    /**
    -     * @property {Vec3} axleWorld
    -     */
    -    this.axleWorld = options.axleWorld.clone();
    -
    -    /**
    -     * @property {number} suspensionRestLength
    -     */
    -    this.suspensionRestLength = options.suspensionRestLength;
    -
    -    /**
    -     * @property {number} suspensionMaxLength
    -     */
    -    this.suspensionMaxLength = options.suspensionMaxLength;
    -
    -    /**
    -     * @property {number} radius
    -     */
    -    this.radius = options.radius;
    -
    -    /**
    -     * @property {number} suspensionStiffness
    -     */
    -    this.suspensionStiffness = options.suspensionStiffness;
    -
    -    /**
    -     * @property {number} dampingCompression
    -     */
    -    this.dampingCompression = options.dampingCompression;
    -
    -    /**
    -     * @property {number} dampingRelaxation
    -     */
    -    this.dampingRelaxation = options.dampingRelaxation;
    -
    -    /**
    -     * @property {number} frictionSlip
    -     */
    -    this.frictionSlip = options.frictionSlip;
    -
    -    /**
    -     * @property {number} steering
    -     */
    -    this.steering = 0;
    -
    -    /**
    -     * Rotation value, in radians.
    -     * @property {number} rotation
    -     */
    -    this.rotation = 0;
    -
    -    /**
    -     * @property {number} deltaRotation
    -     */
    -    this.deltaRotation = 0;
    -
    -    /**
    -     * @property {number} rollInfluence
    -     */
    -    this.rollInfluence = options.rollInfluence;
    -
    -    /**
    -     * @property {number} maxSuspensionForce
    -     */
    -    this.maxSuspensionForce = options.maxSuspensionForce;
    -
    -    /**
    -     * @property {number} engineForce
    -     */
    -    this.engineForce = 0;
    -
    -    /**
    -     * @property {number} brake
    -     */
    -    this.brake = 0;
    -
    -    /**
    -     * @property {number} isFrontWheel
    -     */
    -    this.isFrontWheel = options.isFrontWheel;
    -
    -    /**
    -     * @property {number} clippedInvContactDotSuspension
    -     */
    -    this.clippedInvContactDotSuspension = 1;
    -
    -    /**
    -     * @property {number} suspensionRelativeVelocity
    -     */
    -    this.suspensionRelativeVelocity = 0;
    -
    -    /**
    -     * @property {number} suspensionForce
    -     */
    -    this.suspensionForce = 0;
    -
    -    /**
    -     * @property {number} skidInfo
    -     */
    -    this.skidInfo = 0;
    -
    -    /**
    -     * @property {number} suspensionLength
    -     */
    -    this.suspensionLength = 0;
    -
    -    /**
    -     * @property {number} sideImpulse
    -     */
    -    this.sideImpulse = 0;
    -
    -    /**
    -     * @property {number} forwardImpulse
    -     */
    -    this.forwardImpulse = 0;
    -
    -    /**
    -     * The result from raycasting
    -     * @property {RaycastResult} raycastResult
    -     */
    -    this.raycastResult = new RaycastResult();
    -
    -    /**
    -     * Wheel world transform
    -     * @property {Transform} worldTransform
    -     */
    -    this.worldTransform = new Transform();
    -
    -    /**
    -     * @property {boolean} isInContact
    -     */
    -    this.isInContact = false;
    -}
    -
    -var chassis_velocity_at_contactPoint = new Vec3();
    -var relpos = new Vec3();
    -var chassis_velocity_at_contactPoint = new Vec3();
    -WheelInfo.prototype.updateWheel = function(chassis){
    -    var raycastResult = this.raycastResult;
    -
    -    if (this.isInContact){
    -        var project= raycastResult.hitNormalWorld.dot(raycastResult.directionWorld);
    -        raycastResult.hitPointWorld.vsub(chassis.position, relpos);
    -        chassis.getVelocityAtWorldPoint(relpos, chassis_velocity_at_contactPoint);
    -        var projVel = raycastResult.hitNormalWorld.dot( chassis_velocity_at_contactPoint );
    -        if (project >= -0.1) {
    -            this.suspensionRelativeVelocity = 0.0;
    -            this.clippedInvContactDotSuspension = 1.0 / 0.1;
    -        } else {
    -            var inv = -1 / project;
    -            this.suspensionRelativeVelocity = projVel * inv;
    -            this.clippedInvContactDotSuspension = inv;
    -        }
    -
    -    } else {
    -        // Not in contact : position wheel in a nice (rest length) position
    -        raycastResult.suspensionLength = this.suspensionRestLength;
    -        this.suspensionRelativeVelocity = 0.0;
    -        raycastResult.directionWorld.scale(-1, raycastResult.hitNormalWorld);
    -        this.clippedInvContactDotSuspension = 1.0;
    -    }
    -};
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_shapes_Box.js.html b/docs/files/src_shapes_Box.js.html deleted file mode 100644 index 9334d40b8..000000000 --- a/docs/files/src_shapes_Box.js.html +++ /dev/null @@ -1,389 +0,0 @@ - - - - - src/shapes/Box.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/shapes/Box.js

    - -
    -
    -module.exports = Box;
    -
    -var Shape = require('./Shape');
    -var Vec3 = require('../math/Vec3');
    -var ConvexPolyhedron = require('./ConvexPolyhedron');
    -
    -/**
    - * A 3d box shape.
    - * @class Box
    - * @constructor
    - * @param {Vec3} halfExtents
    - * @author schteppe
    - * @extends Shape
    - */
    -function Box(halfExtents){
    -    Shape.call(this);
    -
    -    this.type = Shape.types.BOX;
    -
    -    /**
    -     * @property halfExtents
    -     * @type {Vec3}
    -     */
    -    this.halfExtents = halfExtents;
    -
    -    /**
    -     * Used by the contact generator to make contacts with other convex polyhedra for example
    -     * @property convexPolyhedronRepresentation
    -     * @type {ConvexPolyhedron}
    -     */
    -    this.convexPolyhedronRepresentation = null;
    -
    -    this.updateConvexPolyhedronRepresentation();
    -    this.updateBoundingSphereRadius();
    -}
    -Box.prototype = new Shape();
    -Box.prototype.constructor = Box;
    -
    -/**
    - * Updates the local convex polyhedron representation used for some collisions.
    - * @method updateConvexPolyhedronRepresentation
    - */
    -Box.prototype.updateConvexPolyhedronRepresentation = function(){
    -    var sx = this.halfExtents.x;
    -    var sy = this.halfExtents.y;
    -    var sz = this.halfExtents.z;
    -    var V = Vec3;
    -
    -    var vertices = [
    -        new V(-sx,-sy,-sz),
    -        new V( sx,-sy,-sz),
    -        new V( sx, sy,-sz),
    -        new V(-sx, sy,-sz),
    -        new V(-sx,-sy, sz),
    -        new V( sx,-sy, sz),
    -        new V( sx, sy, sz),
    -        new V(-sx, sy, sz)
    -    ];
    -
    -    var indices = [
    -        [3,2,1,0], // -z
    -        [4,5,6,7], // +z
    -        [5,4,0,1], // -y
    -        [2,3,7,6], // +y
    -        [0,4,7,3], // -x
    -        [1,2,6,5], // +x
    -    ];
    -
    -    var axes = [
    -        new V(0, 0, 1),
    -        new V(0, 1, 0),
    -        new V(1, 0, 0)
    -    ];
    -
    -    var h = new ConvexPolyhedron(vertices, indices);
    -    this.convexPolyhedronRepresentation = h;
    -    h.material = this.material;
    -};
    -
    -/**
    - * @method calculateLocalInertia
    - * @param  {Number} mass
    - * @param  {Vec3} target
    - * @return {Vec3}
    - */
    -Box.prototype.calculateLocalInertia = function(mass,target){
    -    target = target || new Vec3();
    -    Box.calculateInertia(this.halfExtents, mass, target);
    -    return target;
    -};
    -
    -Box.calculateInertia = function(halfExtents,mass,target){
    -    var e = halfExtents;
    -    target.x = 1.0 / 12.0 * mass * (   2*e.y*2*e.y + 2*e.z*2*e.z );
    -    target.y = 1.0 / 12.0 * mass * (   2*e.x*2*e.x + 2*e.z*2*e.z );
    -    target.z = 1.0 / 12.0 * mass * (   2*e.y*2*e.y + 2*e.x*2*e.x );
    -};
    -
    -/**
    - * Get the box 6 side normals
    - * @method getSideNormals
    - * @param {array}      sixTargetVectors An array of 6 vectors, to store the resulting side normals in.
    - * @param {Quaternion} quat             Orientation to apply to the normal vectors. If not provided, the vectors will be in respect to the local frame.
    - * @return {array}
    - */
    -Box.prototype.getSideNormals = function(sixTargetVectors,quat){
    -    var sides = sixTargetVectors;
    -    var ex = this.halfExtents;
    -    sides[0].set(  ex.x,     0,     0);
    -    sides[1].set(     0,  ex.y,     0);
    -    sides[2].set(     0,     0,  ex.z);
    -    sides[3].set( -ex.x,     0,     0);
    -    sides[4].set(     0, -ex.y,     0);
    -    sides[5].set(     0,     0, -ex.z);
    -
    -    if(quat!==undefined){
    -        for(var i=0; i!==sides.length; i++){
    -            quat.vmult(sides[i],sides[i]);
    -        }
    -    }
    -
    -    return sides;
    -};
    -
    -Box.prototype.volume = function(){
    -    return 8.0 * this.halfExtents.x * this.halfExtents.y * this.halfExtents.z;
    -};
    -
    -Box.prototype.updateBoundingSphereRadius = function(){
    -    this.boundingSphereRadius = this.halfExtents.norm();
    -};
    -
    -var worldCornerTempPos = new Vec3();
    -var worldCornerTempNeg = new Vec3();
    -Box.prototype.forEachWorldCorner = function(pos,quat,callback){
    -
    -    var e = this.halfExtents;
    -    var corners = [[  e.x,  e.y,  e.z],
    -                   [ -e.x,  e.y,  e.z],
    -                   [ -e.x, -e.y,  e.z],
    -                   [ -e.x, -e.y, -e.z],
    -                   [  e.x, -e.y, -e.z],
    -                   [  e.x,  e.y, -e.z],
    -                   [ -e.x,  e.y, -e.z],
    -                   [  e.x, -e.y,  e.z]];
    -    for(var i=0; i<corners.length; i++){
    -        worldCornerTempPos.set(corners[i][0],corners[i][1],corners[i][2]);
    -        quat.vmult(worldCornerTempPos,worldCornerTempPos);
    -        pos.vadd(worldCornerTempPos,worldCornerTempPos);
    -        callback(worldCornerTempPos.x,
    -                 worldCornerTempPos.y,
    -                 worldCornerTempPos.z);
    -    }
    -};
    -
    -var worldCornersTemp = [
    -    new Vec3(),
    -    new Vec3(),
    -    new Vec3(),
    -    new Vec3(),
    -    new Vec3(),
    -    new Vec3(),
    -    new Vec3(),
    -    new Vec3()
    -];
    -Box.prototype.calculateWorldAABB = function(pos,quat,min,max){
    -
    -    var e = this.halfExtents;
    -    worldCornersTemp[0].set(e.x, e.y, e.z);
    -    worldCornersTemp[1].set(-e.x,  e.y, e.z);
    -    worldCornersTemp[2].set(-e.x, -e.y, e.z);
    -    worldCornersTemp[3].set(-e.x, -e.y, -e.z);
    -    worldCornersTemp[4].set(e.x, -e.y, -e.z);
    -    worldCornersTemp[5].set(e.x,  e.y, -e.z);
    -    worldCornersTemp[6].set(-e.x,  e.y, -e.z);
    -    worldCornersTemp[7].set(e.x, -e.y,  e.z);
    -
    -    var wc = worldCornersTemp[0];
    -    quat.vmult(wc, wc);
    -    pos.vadd(wc, wc);
    -    max.copy(wc);
    -    min.copy(wc);
    -    for(var i=1; i<8; i++){
    -        var wc = worldCornersTemp[i];
    -        quat.vmult(wc, wc);
    -        pos.vadd(wc, wc);
    -        var x = wc.x;
    -        var y = wc.y;
    -        var z = wc.z;
    -        if(x > max.x){
    -            max.x = x;
    -        }
    -        if(y > max.y){
    -            max.y = y;
    -        }
    -        if(z > max.z){
    -            max.z = z;
    -        }
    -
    -        if(x < min.x){
    -            min.x = x;
    -        }
    -        if(y < min.y){
    -            min.y = y;
    -        }
    -        if(z < min.z){
    -            min.z = z;
    -        }
    -    }
    -
    -    // Get each axis max
    -    // min.set(Infinity,Infinity,Infinity);
    -    // max.set(-Infinity,-Infinity,-Infinity);
    -    // this.forEachWorldCorner(pos,quat,function(x,y,z){
    -    //     if(x > max.x){
    -    //         max.x = x;
    -    //     }
    -    //     if(y > max.y){
    -    //         max.y = y;
    -    //     }
    -    //     if(z > max.z){
    -    //         max.z = z;
    -    //     }
    -
    -    //     if(x < min.x){
    -    //         min.x = x;
    -    //     }
    -    //     if(y < min.y){
    -    //         min.y = y;
    -    //     }
    -    //     if(z < min.z){
    -    //         min.z = z;
    -    //     }
    -    // });
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_shapes_ConvexPolyhedron.js.html b/docs/files/src_shapes_ConvexPolyhedron.js.html deleted file mode 100644 index 178656435..000000000 --- a/docs/files/src_shapes_ConvexPolyhedron.js.html +++ /dev/null @@ -1,1080 +0,0 @@ - - - - - src/shapes/ConvexPolyhedron.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/shapes/ConvexPolyhedron.js

    - -
    -
    -module.exports = ConvexPolyhedron;
    -
    -var Shape = require('./Shape');
    -var Vec3 = require('../math/Vec3');
    -var Quaternion = require('../math/Quaternion');
    -var Transform = require('../math/Transform');
    -
    -/**
    - * A set of polygons describing a convex shape.
    - * @class ConvexPolyhedron
    - * @constructor
    - * @extends Shape
    - * @description The shape MUST be convex for the code to work properly. No polygons may be coplanar (contained
    - * in the same 3D plane), instead these should be merged into one polygon.
    - *
    - * @param {array} points An array of Vec3's
    - * @param {array} faces Array of integer arrays, describing which vertices that is included in each face.
    - *
    - * @author qiao / https://github.com/qiao (original author, see https://github.com/qiao/three.js/commit/85026f0c769e4000148a67d45a9e9b9c5108836f)
    - * @author schteppe / https://github.com/schteppe
    - * @see http://www.altdevblogaday.com/2011/05/13/contact-generation-between-3d-convex-meshes/
    - * @see http://bullet.googlecode.com/svn/trunk/src/BulletCollision/NarrowPhaseCollision/btPolyhedralContactClipping.cpp
    - *
    - * @todo Move the clipping functions to ContactGenerator?
    - * @todo Automatically merge coplanar polygons in constructor.
    - */
    -function ConvexPolyhedron(points, faces, uniqueAxes) {
    -    var that = this;
    -    Shape.call(this);
    -    this.type = Shape.types.CONVEXPOLYHEDRON;
    -
    -    /**
    -     * Array of Vec3
    -     * @property vertices
    -     * @type {Array}
    -     */
    -    this.vertices = points||[];
    -
    -    this.worldVertices = []; // World transformed version of .vertices
    -    this.worldVerticesNeedsUpdate = true;
    -
    -    /**
    -     * Array of integer arrays, indicating which vertices each face consists of
    -     * @property faces
    -     * @type {Array}
    -     */
    -    this.faces = faces||[];
    -
    -    /**
    -     * Array of Vec3
    -     * @property faceNormals
    -     * @type {Array}
    -     */
    -    this.faceNormals = [];
    -    this.computeNormals();
    -
    -    this.worldFaceNormalsNeedsUpdate = true;
    -    this.worldFaceNormals = []; // World transformed version of .faceNormals
    -
    -    /**
    -     * Array of Vec3
    -     * @property uniqueEdges
    -     * @type {Array}
    -     */
    -    this.uniqueEdges = [];
    -
    -    /**
    -     * If given, these locally defined, normalized axes are the only ones being checked when doing separating axis check.
    -     * @property {Array} uniqueAxes
    -     */
    -    this.uniqueAxes = uniqueAxes ? uniqueAxes.slice() : null;
    -
    -    this.computeEdges();
    -    this.updateBoundingSphereRadius();
    -}
    -ConvexPolyhedron.prototype = new Shape();
    -ConvexPolyhedron.prototype.constructor = ConvexPolyhedron;
    -
    -var computeEdges_tmpEdge = new Vec3();
    -/**
    - * Computes uniqueEdges
    - * @method computeEdges
    - */
    -ConvexPolyhedron.prototype.computeEdges = function(){
    -    var faces = this.faces;
    -    var vertices = this.vertices;
    -    var nv = vertices.length;
    -    var edges = this.uniqueEdges;
    -
    -    edges.length = 0;
    -
    -    var edge = computeEdges_tmpEdge;
    -
    -    for(var i=0; i !== faces.length; i++){
    -        var face = faces[i];
    -        var numVertices = face.length;
    -        for(var j = 0; j !== numVertices; j++){
    -            var k = ( j+1 ) % numVertices;
    -            vertices[face[j]].vsub(vertices[face[k]], edge);
    -            edge.normalize();
    -            var found = false;
    -            for(var p=0; p !== edges.length; p++){
    -                if (edges[p].almostEquals(edge) || edges[p].almostEquals(edge)){
    -                    found = true;
    -                    break;
    -                }
    -            }
    -
    -            if (!found){
    -                edges.push(edge.clone());
    -            }
    -        }
    -    }
    -};
    -
    -/**
    - * Compute the normals of the faces. Will reuse existing Vec3 objects in the .faceNormals array if they exist.
    - * @method computeNormals
    - */
    -ConvexPolyhedron.prototype.computeNormals = function(){
    -    this.faceNormals.length = this.faces.length;
    -
    -    // Generate normals
    -    for(var i=0; i<this.faces.length; i++){
    -
    -        // Check so all vertices exists for this face
    -        for(var j=0; j<this.faces[i].length; j++){
    -            if(!this.vertices[this.faces[i][j]]){
    -                throw new Error("Vertex "+this.faces[i][j]+" not found!");
    -            }
    -        }
    -
    -        var n = this.faceNormals[i] || new Vec3();
    -        this.getFaceNormal(i,n);
    -        n.negate(n);
    -        this.faceNormals[i] = n;
    -        var vertex = this.vertices[this.faces[i][0]];
    -        if(n.dot(vertex) < 0){
    -            console.error(".faceNormals[" + i + "] = Vec3("+n.toString()+") looks like it points into the shape? The vertices follow. Make sure they are ordered CCW around the normal, using the right hand rule.");
    -            for(var j=0; j<this.faces[i].length; j++){
    -                console.warn(".vertices["+this.faces[i][j]+"] = Vec3("+this.vertices[this.faces[i][j]].toString()+")");
    -            }
    -        }
    -    }
    -};
    -
    -/**
    - * Get face normal given 3 vertices
    - * @static
    - * @method getFaceNormal
    - * @param {Vec3} va
    - * @param {Vec3} vb
    - * @param {Vec3} vc
    - * @param {Vec3} target
    - */
    -var cb = new Vec3();
    -var ab = new Vec3();
    -ConvexPolyhedron.computeNormal = function ( va, vb, vc, target ) {
    -    vb.vsub(va,ab);
    -    vc.vsub(vb,cb);
    -    cb.cross(ab,target);
    -    if ( !target.isZero() ) {
    -        target.normalize();
    -    }
    -};
    -
    -/**
    - * Compute the normal of a face from its vertices
    - * @method getFaceNormal
    - * @param  {Number} i
    - * @param  {Vec3} target
    - */
    -ConvexPolyhedron.prototype.getFaceNormal = function(i,target){
    -    var f = this.faces[i];
    -    var va = this.vertices[f[0]];
    -    var vb = this.vertices[f[1]];
    -    var vc = this.vertices[f[2]];
    -    return ConvexPolyhedron.computeNormal(va,vb,vc,target);
    -};
    -
    -/**
    - * @method clipAgainstHull
    - * @param {Vec3} posA
    - * @param {Quaternion} quatA
    - * @param {ConvexPolyhedron} hullB
    - * @param {Vec3} posB
    - * @param {Quaternion} quatB
    - * @param {Vec3} separatingNormal
    - * @param {Number} minDist Clamp distance
    - * @param {Number} maxDist
    - * @param {array} result The an array of contact point objects, see clipFaceAgainstHull
    - * @see http://bullet.googlecode.com/svn/trunk/src/BulletCollision/NarrowPhaseCollision/btPolyhedralContactClipping.cpp
    - */
    -var cah_WorldNormal = new Vec3();
    -ConvexPolyhedron.prototype.clipAgainstHull = function(posA,quatA,hullB,posB,quatB,separatingNormal,minDist,maxDist,result){
    -    var WorldNormal = cah_WorldNormal;
    -    var hullA = this;
    -    var curMaxDist = maxDist;
    -    var closestFaceB = -1;
    -    var dmax = -Number.MAX_VALUE;
    -    for(var face=0; face < hullB.faces.length; face++){
    -        WorldNormal.copy(hullB.faceNormals[face]);
    -        quatB.vmult(WorldNormal,WorldNormal);
    -        //posB.vadd(WorldNormal,WorldNormal);
    -        var d = WorldNormal.dot(separatingNormal);
    -        if (d > dmax){
    -            dmax = d;
    -            closestFaceB = face;
    -        }
    -    }
    -    var worldVertsB1 = [];
    -    var polyB = hullB.faces[closestFaceB];
    -    var numVertices = polyB.length;
    -    for(var e0=0; e0<numVertices; e0++){
    -        var b = hullB.vertices[polyB[e0]];
    -        var worldb = new Vec3();
    -        worldb.copy(b);
    -        quatB.vmult(worldb,worldb);
    -        posB.vadd(worldb,worldb);
    -        worldVertsB1.push(worldb);
    -    }
    -
    -    if (closestFaceB>=0){
    -        this.clipFaceAgainstHull(separatingNormal,
    -                                 posA,
    -                                 quatA,
    -                                 worldVertsB1,
    -                                 minDist,
    -                                 maxDist,
    -                                 result);
    -    }
    -};
    -
    -/**
    - * Find the separating axis between this hull and another
    - * @method findSeparatingAxis
    - * @param {ConvexPolyhedron} hullB
    - * @param {Vec3} posA
    - * @param {Quaternion} quatA
    - * @param {Vec3} posB
    - * @param {Quaternion} quatB
    - * @param {Vec3} target The target vector to save the axis in
    - * @return {bool} Returns false if a separation is found, else true
    - */
    -var fsa_faceANormalWS3 = new Vec3(),
    -    fsa_Worldnormal1 = new Vec3(),
    -    fsa_deltaC = new Vec3(),
    -    fsa_worldEdge0 = new Vec3(),
    -    fsa_worldEdge1 = new Vec3(),
    -    fsa_Cross = new Vec3();
    -ConvexPolyhedron.prototype.findSeparatingAxis = function(hullB,posA,quatA,posB,quatB,target, faceListA, faceListB){
    -    var faceANormalWS3 = fsa_faceANormalWS3,
    -        Worldnormal1 = fsa_Worldnormal1,
    -        deltaC = fsa_deltaC,
    -        worldEdge0 = fsa_worldEdge0,
    -        worldEdge1 = fsa_worldEdge1,
    -        Cross = fsa_Cross;
    -
    -    var dmin = Number.MAX_VALUE;
    -    var hullA = this;
    -    var curPlaneTests=0;
    -
    -    if(!hullA.uniqueAxes){
    -
    -        var numFacesA = faceListA ? faceListA.length : hullA.faces.length;
    -
    -        // Test face normals from hullA
    -        for(var i=0; i<numFacesA; i++){
    -            var fi = faceListA ? faceListA[i] : i;
    -
    -            // Get world face normal
    -            faceANormalWS3.copy(hullA.faceNormals[fi]);
    -            quatA.vmult(faceANormalWS3,faceANormalWS3);
    -
    -            var d = hullA.testSepAxis(faceANormalWS3, hullB, posA, quatA, posB, quatB);
    -            if(d===false){
    -                return false;
    -            }
    -
    -            if(d<dmin){
    -                dmin = d;
    -                target.copy(faceANormalWS3);
    -            }
    -        }
    -
    -    } else {
    -
    -        // Test unique axes
    -        for(var i = 0; i !== hullA.uniqueAxes.length; i++){
    -
    -            // Get world axis
    -            quatA.vmult(hullA.uniqueAxes[i],faceANormalWS3);
    -
    -            var d = hullA.testSepAxis(faceANormalWS3, hullB, posA, quatA, posB, quatB);
    -            if(d===false){
    -                return false;
    -            }
    -
    -            if(d<dmin){
    -                dmin = d;
    -                target.copy(faceANormalWS3);
    -            }
    -        }
    -    }
    -
    -    if(!hullB.uniqueAxes){
    -
    -        // Test face normals from hullB
    -        var numFacesB = faceListB ? faceListB.length : hullB.faces.length;
    -        for(var i=0;i<numFacesB;i++){
    -
    -            var fi = faceListB ? faceListB[i] : i;
    -
    -            Worldnormal1.copy(hullB.faceNormals[fi]);
    -            quatB.vmult(Worldnormal1,Worldnormal1);
    -            curPlaneTests++;
    -            var d = hullA.testSepAxis(Worldnormal1, hullB,posA,quatA,posB,quatB);
    -            if(d===false){
    -                return false;
    -            }
    -
    -            if(d<dmin){
    -                dmin = d;
    -                target.copy(Worldnormal1);
    -            }
    -        }
    -    } else {
    -
    -        // Test unique axes in B
    -        for(var i = 0; i !== hullB.uniqueAxes.length; i++){
    -            quatB.vmult(hullB.uniqueAxes[i],Worldnormal1);
    -
    -            curPlaneTests++;
    -            var d = hullA.testSepAxis(Worldnormal1, hullB,posA,quatA,posB,quatB);
    -            if(d===false){
    -                return false;
    -            }
    -
    -            if(d<dmin){
    -                dmin = d;
    -                target.copy(Worldnormal1);
    -            }
    -        }
    -    }
    -
    -    // Test edges
    -    for(var e0=0; e0 !== hullA.uniqueEdges.length; e0++){
    -
    -        // Get world edge
    -        quatA.vmult(hullA.uniqueEdges[e0],worldEdge0);
    -
    -        for(var e1=0; e1 !== hullB.uniqueEdges.length; e1++){
    -
    -            // Get world edge 2
    -            quatB.vmult(hullB.uniqueEdges[e1], worldEdge1);
    -            worldEdge0.cross(worldEdge1,Cross);
    -
    -            if(!Cross.almostZero()){
    -                Cross.normalize();
    -                var dist = hullA.testSepAxis(Cross, hullB, posA, quatA, posB, quatB);
    -                if(dist === false){
    -                    return false;
    -                }
    -                if(dist < dmin){
    -                    dmin = dist;
    -                    target.copy(Cross);
    -                }
    -            }
    -        }
    -    }
    -
    -    posB.vsub(posA,deltaC);
    -    if((deltaC.dot(target))>0.0){
    -        target.negate(target);
    -    }
    -
    -    return true;
    -};
    -
    -var maxminA=[], maxminB=[];
    -
    -/**
    - * Test separating axis against two hulls. Both hulls are projected onto the axis and the overlap size is returned if there is one.
    - * @method testSepAxis
    - * @param {Vec3} axis
    - * @param {ConvexPolyhedron} hullB
    - * @param {Vec3} posA
    - * @param {Quaternion} quatA
    - * @param {Vec3} posB
    - * @param {Quaternion} quatB
    - * @return {number} The overlap depth, or FALSE if no penetration.
    - */
    -ConvexPolyhedron.prototype.testSepAxis = function(axis, hullB, posA, quatA, posB, quatB){
    -    var hullA=this;
    -    ConvexPolyhedron.project(hullA, axis, posA, quatA, maxminA);
    -    ConvexPolyhedron.project(hullB, axis, posB, quatB, maxminB);
    -    var maxA = maxminA[0];
    -    var minA = maxminA[1];
    -    var maxB = maxminB[0];
    -    var minB = maxminB[1];
    -    if(maxA<minB || maxB<minA){
    -        return false; // Separated
    -    }
    -    var d0 = maxA - minB;
    -    var d1 = maxB - minA;
    -    var depth = d0<d1 ? d0:d1;
    -    return depth;
    -};
    -
    -var cli_aabbmin = new Vec3(),
    -    cli_aabbmax = new Vec3();
    -
    -/**
    - * @method calculateLocalInertia
    - * @param  {Number} mass
    - * @param  {Vec3} target
    - */
    -ConvexPolyhedron.prototype.calculateLocalInertia = function(mass,target){
    -    // Approximate with box inertia
    -    // Exact inertia calculation is overkill, but see http://geometrictools.com/Documentation/PolyhedralMassProperties.pdf for the correct way to do it
    -    this.computeLocalAABB(cli_aabbmin,cli_aabbmax);
    -    var x = cli_aabbmax.x - cli_aabbmin.x,
    -        y = cli_aabbmax.y - cli_aabbmin.y,
    -        z = cli_aabbmax.z - cli_aabbmin.z;
    -    target.x = 1.0 / 12.0 * mass * ( 2*y*2*y + 2*z*2*z );
    -    target.y = 1.0 / 12.0 * mass * ( 2*x*2*x + 2*z*2*z );
    -    target.z = 1.0 / 12.0 * mass * ( 2*y*2*y + 2*x*2*x );
    -};
    -
    -/**
    - * @method getPlaneConstantOfFace
    - * @param  {Number} face_i Index of the face
    - * @return {Number}
    - */
    -ConvexPolyhedron.prototype.getPlaneConstantOfFace = function(face_i){
    -    var f = this.faces[face_i];
    -    var n = this.faceNormals[face_i];
    -    var v = this.vertices[f[0]];
    -    var c = -n.dot(v);
    -    return c;
    -};
    -
    -/**
    - * Clip a face against a hull.
    - * @method clipFaceAgainstHull
    - * @param {Vec3} separatingNormal
    - * @param {Vec3} posA
    - * @param {Quaternion} quatA
    - * @param {Array} worldVertsB1 An array of Vec3 with vertices in the world frame.
    - * @param {Number} minDist Distance clamping
    - * @param {Number} maxDist
    - * @param Array result Array to store resulting contact points in. Will be objects with properties: point, depth, normal. These are represented in world coordinates.
    - */
    -var cfah_faceANormalWS = new Vec3(),
    -    cfah_edge0 = new Vec3(),
    -    cfah_WorldEdge0 = new Vec3(),
    -    cfah_worldPlaneAnormal1 = new Vec3(),
    -    cfah_planeNormalWS1 = new Vec3(),
    -    cfah_worldA1 = new Vec3(),
    -    cfah_localPlaneNormal = new Vec3(),
    -    cfah_planeNormalWS = new Vec3();
    -ConvexPolyhedron.prototype.clipFaceAgainstHull = function(separatingNormal, posA, quatA, worldVertsB1, minDist, maxDist,result){
    -    var faceANormalWS = cfah_faceANormalWS,
    -        edge0 = cfah_edge0,
    -        WorldEdge0 = cfah_WorldEdge0,
    -        worldPlaneAnormal1 = cfah_worldPlaneAnormal1,
    -        planeNormalWS1 = cfah_planeNormalWS1,
    -        worldA1 = cfah_worldA1,
    -        localPlaneNormal = cfah_localPlaneNormal,
    -        planeNormalWS = cfah_planeNormalWS;
    -
    -    var hullA = this;
    -    var worldVertsB2 = [];
    -    var pVtxIn = worldVertsB1;
    -    var pVtxOut = worldVertsB2;
    -    // Find the face with normal closest to the separating axis
    -    var closestFaceA = -1;
    -    var dmin = Number.MAX_VALUE;
    -    for(var face=0; face<hullA.faces.length; face++){
    -        faceANormalWS.copy(hullA.faceNormals[face]);
    -        quatA.vmult(faceANormalWS,faceANormalWS);
    -        //posA.vadd(faceANormalWS,faceANormalWS);
    -        var d = faceANormalWS.dot(separatingNormal);
    -        if (d < dmin){
    -            dmin = d;
    -            closestFaceA = face;
    -        }
    -    }
    -    if (closestFaceA < 0){
    -        // console.log("--- did not find any closest face... ---");
    -        return;
    -    }
    -    //console.log("closest A: ",closestFaceA);
    -    // Get the face and construct connected faces
    -    var polyA = hullA.faces[closestFaceA];
    -    polyA.connectedFaces = [];
    -    for(var i=0; i<hullA.faces.length; i++){
    -        for(var j=0; j<hullA.faces[i].length; j++){
    -            if(polyA.indexOf(hullA.faces[i][j])!==-1 /* Sharing a vertex*/ && i!==closestFaceA /* Not the one we are looking for connections from */ && polyA.connectedFaces.indexOf(i)===-1 /* Not already added */ ){
    -                polyA.connectedFaces.push(i);
    -            }
    -        }
    -    }
    -    // Clip the polygon to the back of the planes of all faces of hull A, that are adjacent to the witness face
    -    var numContacts = pVtxIn.length;
    -    var numVerticesA = polyA.length;
    -    var res = [];
    -    for(var e0=0; e0<numVerticesA; e0++){
    -        var a = hullA.vertices[polyA[e0]];
    -        var b = hullA.vertices[polyA[(e0+1)%numVerticesA]];
    -        a.vsub(b,edge0);
    -        WorldEdge0.copy(edge0);
    -        quatA.vmult(WorldEdge0,WorldEdge0);
    -        posA.vadd(WorldEdge0,WorldEdge0);
    -        worldPlaneAnormal1.copy(this.faceNormals[closestFaceA]);//transA.getBasis()* btVector3(polyA.m_plane[0],polyA.m_plane[1],polyA.m_plane[2]);
    -        quatA.vmult(worldPlaneAnormal1,worldPlaneAnormal1);
    -        posA.vadd(worldPlaneAnormal1,worldPlaneAnormal1);
    -        WorldEdge0.cross(worldPlaneAnormal1,planeNormalWS1);
    -        planeNormalWS1.negate(planeNormalWS1);
    -        worldA1.copy(a);
    -        quatA.vmult(worldA1,worldA1);
    -        posA.vadd(worldA1,worldA1);
    -        var planeEqWS1 = -worldA1.dot(planeNormalWS1);
    -        var planeEqWS;
    -        if(true){
    -            var otherFace = polyA.connectedFaces[e0];
    -            localPlaneNormal.copy(this.faceNormals[otherFace]);
    -            var localPlaneEq = this.getPlaneConstantOfFace(otherFace);
    -
    -            planeNormalWS.copy(localPlaneNormal);
    -            quatA.vmult(planeNormalWS,planeNormalWS);
    -            //posA.vadd(planeNormalWS,planeNormalWS);
    -            var planeEqWS = localPlaneEq - planeNormalWS.dot(posA);
    -        } else  {
    -            planeNormalWS.copy(planeNormalWS1);
    -            planeEqWS = planeEqWS1;
    -        }
    -
    -        // Clip face against our constructed plane
    -        this.clipFaceAgainstPlane(pVtxIn, pVtxOut, planeNormalWS, planeEqWS);
    -
    -        // Throw away all clipped points, but save the reamining until next clip
    -        while(pVtxIn.length){
    -            pVtxIn.shift();
    -        }
    -        while(pVtxOut.length){
    -            pVtxIn.push(pVtxOut.shift());
    -        }
    -    }
    -
    -    //console.log("Resulting points after clip:",pVtxIn);
    -
    -    // only keep contact points that are behind the witness face
    -    localPlaneNormal.copy(this.faceNormals[closestFaceA]);
    -
    -    var localPlaneEq = this.getPlaneConstantOfFace(closestFaceA);
    -    planeNormalWS.copy(localPlaneNormal);
    -    quatA.vmult(planeNormalWS,planeNormalWS);
    -
    -    var planeEqWS = localPlaneEq - planeNormalWS.dot(posA);
    -    for (var i=0; i<pVtxIn.length; i++){
    -        var depth = planeNormalWS.dot(pVtxIn[i]) + planeEqWS; //???
    -        /*console.log("depth calc from normal=",planeNormalWS.toString()," and constant "+planeEqWS+" and vertex ",pVtxIn[i].toString()," gives "+depth);*/
    -        if (depth <=minDist){
    -            console.log("clamped: depth="+depth+" to minDist="+(minDist+""));
    -            depth = minDist;
    -        }
    -
    -        if (depth <=maxDist){
    -            var point = pVtxIn[i];
    -            if(depth<=0){
    -                /*console.log("Got contact point ",point.toString(),
    -                  ", depth=",depth,
    -                  "contact normal=",separatingNormal.toString(),
    -                  "plane",planeNormalWS.toString(),
    -                  "planeConstant",planeEqWS);*/
    -                var p = {
    -                    point:point,
    -                    normal:planeNormalWS,
    -                    depth: depth,
    -                };
    -                result.push(p);
    -            }
    -        }
    -    }
    -};
    -
    -/**
    - * Clip a face in a hull against the back of a plane.
    - * @method clipFaceAgainstPlane
    - * @param {Array} inVertices
    - * @param {Array} outVertices
    - * @param {Vec3} planeNormal
    - * @param {Number} planeConstant The constant in the mathematical plane equation
    - */
    -ConvexPolyhedron.prototype.clipFaceAgainstPlane = function(inVertices,outVertices, planeNormal, planeConstant){
    -    var n_dot_first, n_dot_last;
    -    var numVerts = inVertices.length;
    -
    -    if(numVerts < 2){
    -        return outVertices;
    -    }
    -
    -    var firstVertex = inVertices[inVertices.length-1],
    -        lastVertex =   inVertices[0];
    -
    -    n_dot_first = planeNormal.dot(firstVertex) + planeConstant;
    -
    -    for(var vi = 0; vi < numVerts; vi++){
    -        lastVertex = inVertices[vi];
    -        n_dot_last = planeNormal.dot(lastVertex) + planeConstant;
    -        if(n_dot_first < 0){
    -            if(n_dot_last < 0){
    -                // Start < 0, end < 0, so output lastVertex
    -                var newv = new Vec3();
    -                newv.copy(lastVertex);
    -                outVertices.push(newv);
    -            } else {
    -                // Start < 0, end >= 0, so output intersection
    -                var newv = new Vec3();
    -                firstVertex.lerp(lastVertex,
    -                                 n_dot_first / (n_dot_first - n_dot_last),
    -                                 newv);
    -                outVertices.push(newv);
    -            }
    -        } else {
    -            if(n_dot_last<0){
    -                // Start >= 0, end < 0 so output intersection and end
    -                var newv = new Vec3();
    -                firstVertex.lerp(lastVertex,
    -                                 n_dot_first / (n_dot_first - n_dot_last),
    -                                 newv);
    -                outVertices.push(newv);
    -                outVertices.push(lastVertex);
    -            }
    -        }
    -        firstVertex = lastVertex;
    -        n_dot_first = n_dot_last;
    -    }
    -    return outVertices;
    -};
    -
    -// Updates .worldVertices and sets .worldVerticesNeedsUpdate to false.
    -ConvexPolyhedron.prototype.computeWorldVertices = function(position,quat){
    -    var N = this.vertices.length;
    -    while(this.worldVertices.length < N){
    -        this.worldVertices.push( new Vec3() );
    -    }
    -
    -    var verts = this.vertices,
    -        worldVerts = this.worldVertices;
    -    for(var i=0; i!==N; i++){
    -        quat.vmult( verts[i] , worldVerts[i] );
    -        position.vadd( worldVerts[i] , worldVerts[i] );
    -    }
    -
    -    this.worldVerticesNeedsUpdate = false;
    -};
    -
    -var computeLocalAABB_worldVert = new Vec3();
    -ConvexPolyhedron.prototype.computeLocalAABB = function(aabbmin,aabbmax){
    -    var n = this.vertices.length,
    -        vertices = this.vertices,
    -        worldVert = computeLocalAABB_worldVert;
    -
    -    aabbmin.set(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
    -    aabbmax.set(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
    -
    -    for(var i=0; i<n; i++){
    -        var v = vertices[i];
    -        if     (v.x < aabbmin.x){
    -            aabbmin.x = v.x;
    -        } else if(v.x > aabbmax.x){
    -            aabbmax.x = v.x;
    -        }
    -        if     (v.y < aabbmin.y){
    -            aabbmin.y = v.y;
    -        } else if(v.y > aabbmax.y){
    -            aabbmax.y = v.y;
    -        }
    -        if     (v.z < aabbmin.z){
    -            aabbmin.z = v.z;
    -        } else if(v.z > aabbmax.z){
    -            aabbmax.z = v.z;
    -        }
    -    }
    -};
    -
    -/**
    - * Updates .worldVertices and sets .worldVerticesNeedsUpdate to false.
    - * @method computeWorldFaceNormals
    - * @param  {Quaternion} quat
    - */
    -ConvexPolyhedron.prototype.computeWorldFaceNormals = function(quat){
    -    var N = this.faceNormals.length;
    -    while(this.worldFaceNormals.length < N){
    -        this.worldFaceNormals.push( new Vec3() );
    -    }
    -
    -    var normals = this.faceNormals,
    -        worldNormals = this.worldFaceNormals;
    -    for(var i=0; i!==N; i++){
    -        quat.vmult( normals[i] , worldNormals[i] );
    -    }
    -
    -    this.worldFaceNormalsNeedsUpdate = false;
    -};
    -
    -/**
    - * @method updateBoundingSphereRadius
    - */
    -ConvexPolyhedron.prototype.updateBoundingSphereRadius = function(){
    -    // Assume points are distributed with local (0,0,0) as center
    -    var max2 = 0;
    -    var verts = this.vertices;
    -    for(var i=0, N=verts.length; i!==N; i++) {
    -        var norm2 = verts[i].norm2();
    -        if(norm2 > max2){
    -            max2 = norm2;
    -        }
    -    }
    -    this.boundingSphereRadius = Math.sqrt(max2);
    -};
    -
    -var tempWorldVertex = new Vec3();
    -
    -/**
    - * @method calculateWorldAABB
    - * @param {Vec3}        pos
    - * @param {Quaternion}  quat
    - * @param {Vec3}        min
    - * @param {Vec3}        max
    - */
    -ConvexPolyhedron.prototype.calculateWorldAABB = function(pos,quat,min,max){
    -    var n = this.vertices.length, verts = this.vertices;
    -    var minx,miny,minz,maxx,maxy,maxz;
    -    for(var i=0; i<n; i++){
    -        tempWorldVertex.copy(verts[i]);
    -        quat.vmult(tempWorldVertex,tempWorldVertex);
    -        pos.vadd(tempWorldVertex,tempWorldVertex);
    -        var v = tempWorldVertex;
    -        if     (v.x < minx || minx===undefined){
    -            minx = v.x;
    -        } else if(v.x > maxx || maxx===undefined){
    -            maxx = v.x;
    -        }
    -
    -        if     (v.y < miny || miny===undefined){
    -            miny = v.y;
    -        } else if(v.y > maxy || maxy===undefined){
    -            maxy = v.y;
    -        }
    -
    -        if     (v.z < minz || minz===undefined){
    -            minz = v.z;
    -        } else if(v.z > maxz || maxz===undefined){
    -            maxz = v.z;
    -        }
    -    }
    -    min.set(minx,miny,minz);
    -    max.set(maxx,maxy,maxz);
    -};
    -
    -/**
    - * Get approximate convex volume
    - * @method volume
    - * @return {Number}
    - */
    -ConvexPolyhedron.prototype.volume = function(){
    -    return 4.0 * Math.PI * this.boundingSphereRadius / 3.0;
    -};
    -
    -/**
    - * Get an average of all the vertices positions
    - * @method getAveragePointLocal
    - * @param  {Vec3} target
    - * @return {Vec3}
    - */
    -ConvexPolyhedron.prototype.getAveragePointLocal = function(target){
    -    target = target || new Vec3();
    -    var n = this.vertices.length,
    -        verts = this.vertices;
    -    for(var i=0; i<n; i++){
    -        target.vadd(verts[i],target);
    -    }
    -    target.mult(1/n,target);
    -    return target;
    -};
    -
    -/**
    - * Transform all local points. Will change the .vertices
    - * @method transformAllPoints
    - * @param  {Vec3} offset
    - * @param  {Quaternion} quat
    - */
    -ConvexPolyhedron.prototype.transformAllPoints = function(offset,quat){
    -    var n = this.vertices.length,
    -        verts = this.vertices;
    -
    -    // Apply rotation
    -    if(quat){
    -        // Rotate vertices
    -        for(var i=0; i<n; i++){
    -            var v = verts[i];
    -            quat.vmult(v,v);
    -        }
    -        // Rotate face normals
    -        for(var i=0; i<this.faceNormals.length; i++){
    -            var v = this.faceNormals[i];
    -            quat.vmult(v,v);
    -        }
    -        /*
    -        // Rotate edges
    -        for(var i=0; i<this.uniqueEdges.length; i++){
    -            var v = this.uniqueEdges[i];
    -            quat.vmult(v,v);
    -        }*/
    -    }
    -
    -    // Apply offset
    -    if(offset){
    -        for(var i=0; i<n; i++){
    -            var v = verts[i];
    -            v.vadd(offset,v);
    -        }
    -    }
    -};
    -
    -/**
    - * Checks whether p is inside the polyhedra. Must be in local coords. The point lies outside of the convex hull of the other points if and only if the direction of all the vectors from it to those other points are on less than one half of a sphere around it.
    - * @method pointIsInside
    - * @param  {Vec3} p      A point given in local coordinates
    - * @return {Boolean}
    - */
    -var ConvexPolyhedron_pointIsInside = new Vec3();
    -var ConvexPolyhedron_vToP = new Vec3();
    -var ConvexPolyhedron_vToPointInside = new Vec3();
    -ConvexPolyhedron.prototype.pointIsInside = function(p){
    -    var n = this.vertices.length,
    -        verts = this.vertices,
    -        faces = this.faces,
    -        normals = this.faceNormals;
    -    var positiveResult = null;
    -    var N = this.faces.length;
    -    var pointInside = ConvexPolyhedron_pointIsInside;
    -    this.getAveragePointLocal(pointInside);
    -    for(var i=0; i<N; i++){
    -        var numVertices = this.faces[i].length;
    -        var n = normals[i];
    -        var v = verts[faces[i][0]]; // We only need one point in the face
    -
    -        // This dot product determines which side of the edge the point is
    -        var vToP = ConvexPolyhedron_vToP;
    -        p.vsub(v,vToP);
    -        var r1 = n.dot(vToP);
    -
    -        var vToPointInside = ConvexPolyhedron_vToPointInside;
    -        pointInside.vsub(v,vToPointInside);
    -        var r2 = n.dot(vToPointInside);
    -
    -        if((r1<0 && r2>0) || (r1>0 && r2<0)){
    -            return false; // Encountered some other sign. Exit.
    -        } else {
    -        }
    -    }
    -
    -    // If we got here, all dot products were of the same sign.
    -    return positiveResult ? 1 : -1;
    -};
    -
    -/**
    - * Get max and min dot product of a convex hull at position (pos,quat) projected onto an axis. Results are saved in the array maxmin.
    - * @static
    - * @method project
    - * @param {ConvexPolyhedron} hull
    - * @param {Vec3} axis
    - * @param {Vec3} pos
    - * @param {Quaternion} quat
    - * @param {array} result result[0] and result[1] will be set to maximum and minimum, respectively.
    - */
    -var project_worldVertex = new Vec3();
    -var project_localAxis = new Vec3();
    -var project_localOrigin = new Vec3();
    -ConvexPolyhedron.project = function(hull, axis, pos, quat, result){
    -    var n = hull.vertices.length,
    -        worldVertex = project_worldVertex,
    -        localAxis = project_localAxis,
    -        max = 0,
    -        min = 0,
    -        localOrigin = project_localOrigin,
    -        vs = hull.vertices;
    -
    -    localOrigin.setZero();
    -
    -    // Transform the axis to local
    -    Transform.vectorToLocalFrame(pos, quat, axis, localAxis);
    -    Transform.pointToLocalFrame(pos, quat, localOrigin, localOrigin);
    -    var add = localOrigin.dot(localAxis);
    -
    -    min = max = vs[0].dot(localAxis);
    -
    -    for(var i = 1; i < n; i++){
    -        var val = vs[i].dot(localAxis);
    -
    -        if(val > max){
    -            max = val;
    -        }
    -
    -        if(val < min){
    -            min = val;
    -        }
    -    }
    -
    -    min -= add;
    -    max -= add;
    -
    -    if(min > max){
    -        // Inconsistent - swap
    -        var temp = min;
    -        min = max;
    -        max = temp;
    -    }
    -    // Output
    -    result[0] = max;
    -    result[1] = min;
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_shapes_Cylinder.js.html b/docs/files/src_shapes_Cylinder.js.html deleted file mode 100644 index 5cc7235fb..000000000 --- a/docs/files/src_shapes_Cylinder.js.html +++ /dev/null @@ -1,234 +0,0 @@ - - - - - src/shapes/Cylinder.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/shapes/Cylinder.js

    - -
    -
    -module.exports = Cylinder;
    -
    -var Shape = require('./Shape');
    -var Vec3 = require('../math/Vec3');
    -var Quaternion = require('../math/Quaternion');
    -var ConvexPolyhedron = require('./ConvexPolyhedron');
    -
    -/**
    - * @class Cylinder
    - * @constructor
    - * @extends ConvexPolyhedron
    - * @author schteppe / https://github.com/schteppe
    - * @param {Number} radiusTop
    - * @param {Number} radiusBottom
    - * @param {Number} height
    - * @param {Number} numSegments The number of segments to build the cylinder out of
    - */
    -function Cylinder( radiusTop, radiusBottom, height , numSegments ) {
    -    var N = numSegments,
    -        verts = [],
    -        axes = [],
    -        faces = [],
    -        bottomface = [],
    -        topface = [],
    -        cos = Math.cos,
    -        sin = Math.sin;
    -
    -    // First bottom point
    -    verts.push(new Vec3(radiusBottom*cos(0),
    -                               radiusBottom*sin(0),
    -                               -height*0.5));
    -    bottomface.push(0);
    -
    -    // First top point
    -    verts.push(new Vec3(radiusTop*cos(0),
    -                               radiusTop*sin(0),
    -                               height*0.5));
    -    topface.push(1);
    -
    -    for(var i=0; i<N; i++){
    -        var theta = 2*Math.PI/N * (i+1);
    -        var thetaN = 2*Math.PI/N * (i+0.5);
    -        if(i<N-1){
    -            // Bottom
    -            verts.push(new Vec3(radiusBottom*cos(theta),
    -                                       radiusBottom*sin(theta),
    -                                       -height*0.5));
    -            bottomface.push(2*i+2);
    -            // Top
    -            verts.push(new Vec3(radiusTop*cos(theta),
    -                                       radiusTop*sin(theta),
    -                                       height*0.5));
    -            topface.push(2*i+3);
    -
    -            // Face
    -            faces.push([2*i+2, 2*i+3, 2*i+1,2*i]);
    -        } else {
    -            faces.push([0,1, 2*i+1, 2*i]); // Connect
    -        }
    -
    -        // Axis: we can cut off half of them if we have even number of segments
    -        if(N % 2 === 1 || i < N / 2){
    -            axes.push(new Vec3(cos(thetaN), sin(thetaN), 0));
    -        }
    -    }
    -    faces.push(topface);
    -    axes.push(new Vec3(0,0,1));
    -
    -    // Reorder bottom face
    -    var temp = [];
    -    for(var i=0; i<bottomface.length; i++){
    -        temp.push(bottomface[bottomface.length - i - 1]);
    -    }
    -    faces.push(temp);
    -
    -    this.type = Shape.types.CONVEXPOLYHEDRON;
    -    ConvexPolyhedron.call( this, verts, faces, axes );
    -}
    -
    -Cylinder.prototype = new ConvexPolyhedron();
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_shapes_Heightfield.js.html b/docs/files/src_shapes_Heightfield.js.html deleted file mode 100644 index f3829f6a7..000000000 --- a/docs/files/src_shapes_Heightfield.js.html +++ /dev/null @@ -1,638 +0,0 @@ - - - - - src/shapes/Heightfield.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/shapes/Heightfield.js

    - -
    -
    -var Shape = require('./Shape');
    -var ConvexPolyhedron = require('./ConvexPolyhedron');
    -var Vec3 = require('../math/Vec3');
    -var Utils = require('../utils/Utils');
    -
    -module.exports = Heightfield;
    -
    -/**
    - * Heightfield shape class. Height data is given as an array. These data points are spread out evenly with a given distance.
    - * @class Heightfield
    - * @extends Shape
    - * @constructor
    - * @param {Array} data An array of Y values that will be used to construct the terrain.
    - * @param {object} options
    - * @param {Number} [options.minValue] Minimum value of the data points in the data array. Will be computed automatically if not given.
    - * @param {Number} [options.maxValue] Maximum value.
    - * @param {Number} [options.elementSize=0.1] World spacing between the data points in X direction.
    - * @todo Should be possible to use along all axes, not just y
    - *
    - * @example
    - *     // Generate some height data (y-values).
    - *     var data = [];
    - *     for(var i = 0; i < 1000; i++){
    - *         var y = 0.5 * Math.cos(0.2 * i);
    - *         data.push(y);
    - *     }
    - *
    - *     // Create the heightfield shape
    - *     var heightfieldShape = new Heightfield(data, {
    - *         elementSize: 1 // Distance between the data points in X and Y directions
    - *     });
    - *     var heightfieldBody = new Body();
    - *     heightfieldBody.addShape(heightfieldShape);
    - *     world.addBody(heightfieldBody);
    - */
    -function Heightfield(data, options){
    -    options = Utils.defaults(options, {
    -        maxValue : null,
    -        minValue : null,
    -        elementSize : 1
    -    });
    -
    -    /**
    -     * An array of numbers, or height values, that are spread out along the x axis.
    -     * @property {array} data
    -     */
    -    this.data = data;
    -
    -    /**
    -     * Max value of the data
    -     * @property {number} maxValue
    -     */
    -    this.maxValue = options.maxValue;
    -
    -    /**
    -     * Max value of the data
    -     * @property {number} minValue
    -     */
    -    this.minValue = options.minValue;
    -
    -    /**
    -     * The width of each element
    -     * @property {number} elementSize
    -     * @todo elementSizeX and Y
    -     */
    -    this.elementSize = options.elementSize;
    -
    -    if(options.minValue === null){
    -        this.updateMinValue();
    -    }
    -    if(options.maxValue === null){
    -        this.updateMaxValue();
    -    }
    -
    -    this.cacheEnabled = true;
    -
    -    Shape.call(this);
    -
    -    this.pillarConvex = new ConvexPolyhedron();
    -    this.pillarOffset = new Vec3();
    -
    -    this.type = Shape.types.HEIGHTFIELD;
    -    this.updateBoundingSphereRadius();
    -
    -    // "i_j_isUpper" => { convex: ..., offset: ... }
    -    // for example:
    -    // _cachedPillars["0_2_1"]
    -    this._cachedPillars = {};
    -}
    -Heightfield.prototype = new Shape();
    -
    -/**
    - * Call whenever you change the data array.
    - * @method update
    - */
    -Heightfield.prototype.update = function(){
    -    this._cachedPillars = {};
    -};
    -
    -/**
    - * Update the .minValue property
    - * @method updateMinValue
    - */
    -Heightfield.prototype.updateMinValue = function(){
    -    var data = this.data;
    -    var minValue = data[0][0];
    -    for(var i=0; i !== data.length; i++){
    -        for(var j=0; j !== data[i].length; j++){
    -            var v = data[i][j];
    -            if(v < minValue){
    -                minValue = v;
    -            }
    -        }
    -    }
    -    this.minValue = minValue;
    -};
    -
    -/**
    - * Update the .maxValue property
    - * @method updateMaxValue
    - */
    -Heightfield.prototype.updateMaxValue = function(){
    -    var data = this.data;
    -    var maxValue = data[0][0];
    -    for(var i=0; i !== data.length; i++){
    -        for(var j=0; j !== data[i].length; j++){
    -            var v = data[i][j];
    -            if(v > maxValue){
    -                maxValue = v;
    -            }
    -        }
    -    }
    -    this.maxValue = maxValue;
    -};
    -
    -/**
    - * Set the height value at an index. Don't forget to update maxValue and minValue after you're done.
    - * @method setHeightValueAtIndex
    - * @param {integer} xi
    - * @param {integer} yi
    - * @param {number} value
    - */
    -Heightfield.prototype.setHeightValueAtIndex = function(xi, yi, value){
    -    var data = this.data;
    -    data[xi][yi] = value;
    -
    -    // Invalidate cache
    -    this.clearCachedConvexTrianglePillar(xi, yi, false);
    -    if(xi > 0){
    -        this.clearCachedConvexTrianglePillar(xi - 1, yi, true);
    -        this.clearCachedConvexTrianglePillar(xi - 1, yi, false);
    -    }
    -    if(yi > 0){
    -        this.clearCachedConvexTrianglePillar(xi, yi - 1, true);
    -        this.clearCachedConvexTrianglePillar(xi, yi - 1, false);
    -    }
    -    if(yi > 0 && xi > 0){
    -        this.clearCachedConvexTrianglePillar(xi - 1, yi - 1, true);
    -    }
    -};
    -
    -/**
    - * Get max/min in a rectangle in the matrix data
    - * @method getRectMinMax
    - * @param  {integer} iMinX
    - * @param  {integer} iMinY
    - * @param  {integer} iMaxX
    - * @param  {integer} iMaxY
    - * @param  {array} [result] An array to store the results in.
    - * @return {array} The result array, if it was passed in. Minimum will be at position 0 and max at 1.
    - */
    -Heightfield.prototype.getRectMinMax = function (iMinX, iMinY, iMaxX, iMaxY, result) {
    -    result = result || [];
    -
    -    // Get max and min of the data
    -    var data = this.data,
    -        max = this.minValue; // Set first value
    -    for(var i = iMinX; i <= iMaxX; i++){
    -        for(var j = iMinY; j <= iMaxY; j++){
    -            var height = data[i][j];
    -            if(height > max){
    -                max = height;
    -            }
    -        }
    -    }
    -
    -    result[0] = this.minValue;
    -    result[1] = max;
    -};
    -
    -/**
    - * Get the index of a local position on the heightfield. The indexes indicate the rectangles, so if your terrain is made of N x N height data points, you will have rectangle indexes ranging from 0 to N-1.
    - * @method getIndexOfPosition
    - * @param  {number} x
    - * @param  {number} y
    - * @param  {array} result Two-element array
    - * @param  {boolean} clamp If the position should be clamped to the heightfield edge.
    - * @return {boolean}
    - */
    -Heightfield.prototype.getIndexOfPosition = function (x, y, result, clamp) {
    -
    -    // Get the index of the data points to test against
    -    var w = this.elementSize;
    -    var data = this.data;
    -    var xi = Math.floor(x / w);
    -    var yi = Math.floor(y / w);
    -
    -    result[0] = xi;
    -    result[1] = yi;
    -
    -    if(clamp){
    -        // Clamp index to edges
    -        if(xi < 0){ xi = 0; }
    -        if(yi < 0){ yi = 0; }
    -        if(xi >= data.length - 1){ xi = data.length - 1; }
    -        if(yi >= data[0].length - 1){ yi = data[0].length - 1; }
    -    }
    -
    -    // Bail out if we are out of the terrain
    -    if(xi < 0 || yi < 0 || xi >= data.length-1 || yi >= data[0].length-1){
    -        return false;
    -    }
    -
    -    return true;
    -};
    -
    -Heightfield.prototype.getHeightAt = function(x, y, edgeClamp){
    -    var idx = [];
    -    this.getIndexOfPosition(x, y, idx, edgeClamp);
    -
    -    // TODO: get upper or lower triangle, then use barycentric interpolation to get the height in the triangle.
    -    var minmax = [];
    -    this.getRectMinMax(idx[0], idx[1] + 1, idx[0], idx[1] + 1, minmax);
    -
    -    return (minmax[0] + minmax[1]) / 2; // average
    -};
    -
    -Heightfield.prototype.getCacheConvexTrianglePillarKey = function(xi, yi, getUpperTriangle){
    -    return xi + '_' + yi + '_' + (getUpperTriangle ? 1 : 0);
    -};
    -
    -Heightfield.prototype.getCachedConvexTrianglePillar = function(xi, yi, getUpperTriangle){
    -    return this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi, yi, getUpperTriangle)];
    -};
    -
    -Heightfield.prototype.setCachedConvexTrianglePillar = function(xi, yi, getUpperTriangle, convex, offset){
    -    this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi, yi, getUpperTriangle)] = {
    -        convex: convex,
    -        offset: offset
    -    };
    -};
    -
    -Heightfield.prototype.clearCachedConvexTrianglePillar = function(xi, yi, getUpperTriangle){
    -    delete this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi, yi, getUpperTriangle)];
    -};
    -
    -/**
    - * Get a triangle in the terrain in the form of a triangular convex shape.
    - * @method getConvexTrianglePillar
    - * @param  {integer} i
    - * @param  {integer} j
    - * @param  {boolean} getUpperTriangle
    - */
    -Heightfield.prototype.getConvexTrianglePillar = function(xi, yi, getUpperTriangle){
    -    var result = this.pillarConvex;
    -    var offsetResult = this.pillarOffset;
    -
    -    if(this.cacheEnabled){
    -        var data = this.getCachedConvexTrianglePillar(xi, yi, getUpperTriangle);
    -        if(data){
    -            this.pillarConvex = data.convex;
    -            this.pillarOffset = data.offset;
    -            return;
    -        }
    -
    -        result = new ConvexPolyhedron();
    -        offsetResult = new Vec3();
    -
    -        this.pillarConvex = result;
    -        this.pillarOffset = offsetResult;
    -    }
    -
    -    var data = this.data;
    -    var elementSize = this.elementSize;
    -    var faces = result.faces;
    -
    -    // Reuse verts if possible
    -    result.vertices.length = 6;
    -    for (var i = 0; i < 6; i++) {
    -        if(!result.vertices[i]){
    -            result.vertices[i] = new Vec3();
    -        }
    -    }
    -
    -    // Reuse faces if possible
    -    faces.length = 5;
    -    for (var i = 0; i < 5; i++) {
    -        if(!faces[i]){
    -            faces[i] = [];
    -        }
    -    }
    -
    -    var verts = result.vertices;
    -
    -    var h = (Math.min(
    -        data[xi][yi],
    -        data[xi+1][yi],
    -        data[xi][yi+1],
    -        data[xi+1][yi+1]
    -    ) - this.minValue ) / 2 + this.minValue;
    -
    -    if (!getUpperTriangle) {
    -
    -        // Center of the triangle pillar - all polygons are given relative to this one
    -        offsetResult.set(
    -            (xi + 0.25) * elementSize, // sort of center of a triangle
    -            (yi + 0.25) * elementSize,
    -            h // vertical center
    -        );
    -
    -        // Top triangle verts
    -        verts[0].set(
    -            -0.25 * elementSize,
    -            -0.25 * elementSize,
    -            data[xi][yi] - h
    -        );
    -        verts[1].set(
    -            0.75 * elementSize,
    -            -0.25 * elementSize,
    -            data[xi + 1][yi] - h
    -        );
    -        verts[2].set(
    -            -0.25 * elementSize,
    -            0.75 * elementSize,
    -            data[xi][yi + 1] - h
    -        );
    -
    -        // bottom triangle verts
    -        verts[3].set(
    -            -0.25 * elementSize,
    -            -0.25 * elementSize,
    -            -h-1
    -        );
    -        verts[4].set(
    -            0.75 * elementSize,
    -            -0.25 * elementSize,
    -            -h-1
    -        );
    -        verts[5].set(
    -            -0.25 * elementSize,
    -            0.75  * elementSize,
    -            -h-1
    -        );
    -
    -        // top triangle
    -        faces[0][0] = 0;
    -        faces[0][1] = 1;
    -        faces[0][2] = 2;
    -
    -        // bottom triangle
    -        faces[1][0] = 5;
    -        faces[1][1] = 4;
    -        faces[1][2] = 3;
    -
    -        // -x facing quad
    -        faces[2][0] = 0;
    -        faces[2][1] = 2;
    -        faces[2][2] = 5;
    -        faces[2][3] = 3;
    -
    -        // -y facing quad
    -        faces[3][0] = 1;
    -        faces[3][1] = 0;
    -        faces[3][2] = 3;
    -        faces[3][3] = 4;
    -
    -        // +xy facing quad
    -        faces[4][0] = 4;
    -        faces[4][1] = 5;
    -        faces[4][2] = 2;
    -        faces[4][3] = 1;
    -
    -
    -    } else {
    -
    -        // Center of the triangle pillar - all polygons are given relative to this one
    -        offsetResult.set(
    -            (xi + 0.75) * elementSize, // sort of center of a triangle
    -            (yi + 0.75) * elementSize,
    -            h // vertical center
    -        );
    -
    -        // Top triangle verts
    -        verts[0].set(
    -            0.25 * elementSize,
    -            0.25 * elementSize,
    -            data[xi + 1][yi + 1] - h
    -        );
    -        verts[1].set(
    -            -0.75 * elementSize,
    -            0.25 * elementSize,
    -            data[xi][yi + 1] - h
    -        );
    -        verts[2].set(
    -            0.25 * elementSize,
    -            -0.75 * elementSize,
    -            data[xi + 1][yi] - h
    -        );
    -
    -        // bottom triangle verts
    -        verts[3].set(
    -            0.25 * elementSize,
    -            0.25 * elementSize,
    -            - h-1
    -        );
    -        verts[4].set(
    -            -0.75 * elementSize,
    -            0.25 * elementSize,
    -            - h-1
    -        );
    -        verts[5].set(
    -            0.25 * elementSize,
    -            -0.75 * elementSize,
    -            - h-1
    -        );
    -
    -        // Top triangle
    -        faces[0][0] = 0;
    -        faces[0][1] = 1;
    -        faces[0][2] = 2;
    -
    -        // bottom triangle
    -        faces[1][0] = 5;
    -        faces[1][1] = 4;
    -        faces[1][2] = 3;
    -
    -        // +x facing quad
    -        faces[2][0] = 2;
    -        faces[2][1] = 5;
    -        faces[2][2] = 3;
    -        faces[2][3] = 0;
    -
    -        // +y facing quad
    -        faces[3][0] = 3;
    -        faces[3][1] = 4;
    -        faces[3][2] = 1;
    -        faces[3][3] = 0;
    -
    -        // -xy facing quad
    -        faces[4][0] = 1;
    -        faces[4][1] = 4;
    -        faces[4][2] = 5;
    -        faces[4][3] = 2;
    -    }
    -
    -    result.computeNormals();
    -    result.computeEdges();
    -    result.updateBoundingSphereRadius();
    -
    -    this.setCachedConvexTrianglePillar(xi, yi, getUpperTriangle, result, offsetResult);
    -};
    -
    -Heightfield.prototype.calculateLocalInertia = function(mass, target){
    -    target = target || new Vec3();
    -    target.set(0, 0, 0);
    -    return target;
    -};
    -
    -Heightfield.prototype.volume = function(){
    -    return Number.MAX_VALUE; // The terrain is infinite
    -};
    -
    -Heightfield.prototype.calculateWorldAABB = function(pos, quat, min, max){
    -    // TODO: do it properly
    -    min.set(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
    -    max.set(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
    -};
    -
    -Heightfield.prototype.updateBoundingSphereRadius = function(){
    -    // Use the bounding box of the min/max values
    -    var data = this.data,
    -        s = this.elementSize;
    -    this.boundingSphereRadius = new Vec3(data.length * s, data[0].length * s, Math.max(Math.abs(this.maxValue), Math.abs(this.minValue))).norm();
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_shapes_Particle.js.html b/docs/files/src_shapes_Particle.js.html deleted file mode 100644 index 1cb4c384d..000000000 --- a/docs/files/src_shapes_Particle.js.html +++ /dev/null @@ -1,199 +0,0 @@ - - - - - src/shapes/Particle.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/shapes/Particle.js

    - -
    -
    -module.exports = Particle;
    -
    -var Shape = require('./Shape');
    -var Vec3 = require('../math/Vec3');
    -
    -/**
    - * Particle shape.
    - * @class Particle
    - * @constructor
    - * @author schteppe
    - * @extends Shape
    - */
    -function Particle(){
    -    Shape.call(this);
    -
    -    this.type = Shape.types.PARTICLE;
    -}
    -Particle.prototype = new Shape();
    -Particle.prototype.constructor = Particle;
    -
    -/**
    - * @method calculateLocalInertia
    - * @param  {Number} mass
    - * @param  {Vec3} target
    - * @return {Vec3}
    - */
    -Particle.prototype.calculateLocalInertia = function(mass,target){
    -    target = target || new Vec3();
    -    target.set(0, 0, 0);
    -    return target;
    -};
    -
    -Particle.prototype.volume = function(){
    -    return 0;
    -};
    -
    -Particle.prototype.updateBoundingSphereRadius = function(){
    -    this.boundingSphereRadius = 0;
    -};
    -
    -Particle.prototype.calculateWorldAABB = function(pos,quat,min,max){
    -    // Get each axis max
    -    min.copy(pos);
    -    max.copy(pos);
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_shapes_Plane.js.html b/docs/files/src_shapes_Plane.js.html deleted file mode 100644 index df40e3818..000000000 --- a/docs/files/src_shapes_Plane.js.html +++ /dev/null @@ -1,215 +0,0 @@ - - - - - src/shapes/Plane.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/shapes/Plane.js

    - -
    -
    -module.exports = Plane;
    -
    -var Shape = require('./Shape');
    -var Vec3 = require('../math/Vec3');
    -
    -/**
    - * A plane, facing in the Z direction. The plane has its surface at z=0 and everything below z=0 is assumed to be solid plane. To make the plane face in some other direction than z, you must put it inside a RigidBody and rotate that body. See the demos.
    - * @class Plane
    - * @constructor
    - * @extends Shape
    - * @author schteppe
    - */
    -function Plane(){
    -    Shape.call(this);
    -    this.type = Shape.types.PLANE;
    -
    -    // World oriented normal
    -    this.worldNormal = new Vec3();
    -    this.worldNormalNeedsUpdate = true;
    -
    -    this.boundingSphereRadius = Number.MAX_VALUE;
    -}
    -Plane.prototype = new Shape();
    -Plane.prototype.constructor = Plane;
    -
    -Plane.prototype.computeWorldNormal = function(quat){
    -    var n = this.worldNormal;
    -    n.set(0,0,1);
    -    quat.vmult(n,n);
    -    this.worldNormalNeedsUpdate = false;
    -};
    -
    -Plane.prototype.calculateLocalInertia = function(mass,target){
    -    target = target || new Vec3();
    -    return target;
    -};
    -
    -Plane.prototype.volume = function(){
    -    return Number.MAX_VALUE; // The plane is infinite...
    -};
    -
    -var tempNormal = new Vec3();
    -Plane.prototype.calculateWorldAABB = function(pos, quat, min, max){
    -    // The plane AABB is infinite, except if the normal is pointing along any axis
    -    tempNormal.set(0,0,1); // Default plane normal is z
    -    quat.vmult(tempNormal,tempNormal);
    -    var maxVal = Number.MAX_VALUE;
    -    min.set(-maxVal, -maxVal, -maxVal);
    -    max.set(maxVal, maxVal, maxVal);
    -
    -    if(tempNormal.x === 1){ max.x = pos.x; }
    -    if(tempNormal.y === 1){ max.y = pos.y; }
    -    if(tempNormal.z === 1){ max.z = pos.z; }
    -
    -    if(tempNormal.x === -1){ min.x = pos.x; }
    -    if(tempNormal.y === -1){ min.y = pos.y; }
    -    if(tempNormal.z === -1){ min.z = pos.z; }
    -};
    -
    -Plane.prototype.updateBoundingSphereRadius = function(){
    -    this.boundingSphereRadius = Number.MAX_VALUE;
    -};
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_shapes_Shape.js.html b/docs/files/src_shapes_Shape.js.html deleted file mode 100644 index ae9229e94..000000000 --- a/docs/files/src_shapes_Shape.js.html +++ /dev/null @@ -1,251 +0,0 @@ - - - - - src/shapes/Shape.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/shapes/Shape.js

    - -
    -
    -module.exports = Shape;
    -
    -var Shape = require('./Shape');
    -var Vec3 = require('../math/Vec3');
    -var Quaternion = require('../math/Quaternion');
    -var Material = require('../material/Material');
    -
    -/**
    - * Base class for shapes
    - * @class Shape
    - * @constructor
    - * @author schteppe
    - * @todo Should have a mechanism for caching bounding sphere radius instead of calculating it each time
    - */
    -function Shape(){
    -
    -    /**
    -     * Identifyer of the Shape.
    -     * @property {number} id
    -     */
    -    this.id = Shape.idCounter++;
    -
    -    /**
    -     * The type of this shape. Must be set to an int > 0 by subclasses.
    -     * @property type
    -     * @type {Number}
    -     * @see Shape.types
    -     */
    -    this.type = 0;
    -
    -    /**
    -     * The local bounding sphere radius of this shape.
    -     * @property {Number} boundingSphereRadius
    -     */
    -    this.boundingSphereRadius = 0;
    -
    -    /**
    -     * Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled.
    -     * @property {boolean} collisionResponse
    -     */
    -    this.collisionResponse = true;
    -
    -    /**
    -     * @property {Material} material
    -     */
    -    this.material = null;
    -}
    -Shape.prototype.constructor = Shape;
    -
    -/**
    - * Computes the bounding sphere radius. The result is stored in the property .boundingSphereRadius
    - * @method updateBoundingSphereRadius
    - * @return {Number}
    - */
    -Shape.prototype.updateBoundingSphereRadius = function(){
    -    throw "computeBoundingSphereRadius() not implemented for shape type "+this.type;
    -};
    -
    -/**
    - * Get the volume of this shape
    - * @method volume
    - * @return {Number}
    - */
    -Shape.prototype.volume = function(){
    -    throw "volume() not implemented for shape type "+this.type;
    -};
    -
    -/**
    - * Calculates the inertia in the local frame for this shape.
    - * @method calculateLocalInertia
    - * @return {Vec3}
    - * @see http://en.wikipedia.org/wiki/List_of_moments_of_inertia
    - */
    -Shape.prototype.calculateLocalInertia = function(mass,target){
    -    throw "calculateLocalInertia() not implemented for shape type "+this.type;
    -};
    -
    -Shape.idCounter = 0;
    -
    -/**
    - * The available shape types.
    - * @static
    - * @property types
    - * @type {Object}
    - */
    -Shape.types = {
    -    SPHERE:1,
    -    PLANE:2,
    -    BOX:4,
    -    COMPOUND:8,
    -    CONVEXPOLYHEDRON:16,
    -    HEIGHTFIELD:32,
    -    PARTICLE:64,
    -    CYLINDER:128,
    -    TRIMESH:256
    -};
    -
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_shapes_Sphere.js.html b/docs/files/src_shapes_Sphere.js.html deleted file mode 100644 index f0edd3bdb..000000000 --- a/docs/files/src_shapes_Sphere.js.html +++ /dev/null @@ -1,211 +0,0 @@ - - - - - src/shapes/Sphere.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/shapes/Sphere.js

    - -
    -
    -module.exports = Sphere;
    -
    -var Shape = require('./Shape');
    -var Vec3 = require('../math/Vec3');
    -
    -/**
    - * Spherical shape
    - * @class Sphere
    - * @constructor
    - * @extends Shape
    - * @param {Number} radius The radius of the sphere, a non-negative number.
    - * @author schteppe / http://github.com/schteppe
    - */
    -function Sphere(radius){
    -    Shape.call(this);
    -
    -    /**
    -     * @property {Number} radius
    -     */
    -    this.radius = radius!==undefined ? Number(radius) : 1.0;
    -    this.type = Shape.types.SPHERE;
    -
    -    if(this.radius < 0){
    -        throw new Error('The sphere radius cannot be negative.');
    -    }
    -
    -    this.updateBoundingSphereRadius();
    -}
    -Sphere.prototype = new Shape();
    -Sphere.prototype.constructor = Sphere;
    -
    -Sphere.prototype.calculateLocalInertia = function(mass,target){
    -    target = target || new Vec3();
    -    var I = 2.0*mass*this.radius*this.radius/5.0;
    -    target.x = I;
    -    target.y = I;
    -    target.z = I;
    -    return target;
    -};
    -
    -Sphere.prototype.volume = function(){
    -    return 4.0 * Math.PI * this.radius / 3.0;
    -};
    -
    -Sphere.prototype.updateBoundingSphereRadius = function(){
    -    this.boundingSphereRadius = this.radius;
    -};
    -
    -Sphere.prototype.calculateWorldAABB = function(pos,quat,min,max){
    -    var r = this.radius;
    -    var axes = ['x','y','z'];
    -    for(var i=0; i<axes.length; i++){
    -        var ax = axes[i];
    -        min[ax] = pos[ax] - r;
    -        max[ax] = pos[ax] + r;
    -    }
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_shapes_Trimesh.js.html b/docs/files/src_shapes_Trimesh.js.html deleted file mode 100644 index 3a8ebbb14..000000000 --- a/docs/files/src_shapes_Trimesh.js.html +++ /dev/null @@ -1,714 +0,0 @@ - - - - - src/shapes/Trimesh.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/shapes/Trimesh.js

    - -
    -
    -module.exports = Trimesh;
    -
    -var Shape = require('./Shape');
    -var Vec3 = require('../math/Vec3');
    -var Quaternion = require('../math/Quaternion');
    -var Transform = require('../math/Transform');
    -var AABB = require('../collision/AABB');
    -var Octree = require('../utils/Octree');
    -
    -/**
    - * @class Trimesh
    - * @constructor
    - * @param {array} vertices
    - * @param {array} indices
    - * @extends Shape
    - * @example
    - *     // How to make a mesh with a single triangle
    - *     var vertices = [
    - *         0, 0, 0, // vertex 0
    - *         1, 0, 0, // vertex 1
    - *         0, 1, 0  // vertex 2
    - *     ];
    - *     var indices = [
    - *         0, 1, 2  // triangle 0
    - *     ];
    - *     var trimeshShape = new Trimesh(vertices, indices);
    - */
    -function Trimesh(vertices, indices) {
    -    Shape.call(this);
    -    this.type = Shape.types.TRIMESH;
    -
    -    /**
    -     * @property vertices
    -     * @type {Array}
    -     */
    -    this.vertices = new Float32Array(vertices);
    -
    -    /**
    -     * Array of integers, indicating which vertices each triangle consists of. The length of this array is thus 3 times the number of triangles.
    -     * @property indices
    -     * @type {Array}
    -     */
    -    this.indices = new Int16Array(indices);
    -
    -    /**
    -     * The normals data.
    -     * @property normals
    -     * @type {Array}
    -     */
    -    this.normals = new Float32Array(indices.length);
    -
    -    /**
    -     * The local AABB of the mesh.
    -     * @property aabb
    -     * @type {Array}
    -     */
    -    this.aabb = new AABB();
    -
    -    /**
    -     * References to vertex pairs, making up all unique edges in the trimesh.
    -     * @property {array} edges
    -     */
    -    this.edges = null;
    -
    -    /**
    -     * Local scaling of the mesh. Use .setScale() to set it.
    -     * @property {Vec3} scale
    -     */
    -    this.scale = new Vec3(1, 1, 1);
    -
    -    /**
    -     * The indexed triangles. Use .updateTree() to update it.
    -     * @property {Octree} tree
    -     */
    -    this.tree = new Octree();
    -
    -    this.updateEdges();
    -    this.updateNormals();
    -    this.updateAABB();
    -    this.updateBoundingSphereRadius();
    -    this.updateTree();
    -}
    -Trimesh.prototype = new Shape();
    -Trimesh.prototype.constructor = Trimesh;
    -
    -var computeNormals_n = new Vec3();
    -
    -/**
    - * @method updateTree
    - */
    -Trimesh.prototype.updateTree = function(){
    -    var tree = this.tree;
    -
    -    tree.reset();
    -    tree.aabb.copy(this.aabb);
    -    var scale = this.scale; // The local mesh AABB is scaled, but the octree AABB should be unscaled
    -    tree.aabb.lowerBound.x *= 1 / scale.x;
    -    tree.aabb.lowerBound.y *= 1 / scale.y;
    -    tree.aabb.lowerBound.z *= 1 / scale.z;
    -    tree.aabb.upperBound.x *= 1 / scale.x;
    -    tree.aabb.upperBound.y *= 1 / scale.y;
    -    tree.aabb.upperBound.z *= 1 / scale.z;
    -
    -    // Insert all triangles
    -    var triangleAABB = new AABB();
    -    var a = new Vec3();
    -    var b = new Vec3();
    -    var c = new Vec3();
    -    var points = [a, b, c];
    -    for (var i = 0; i < this.indices.length / 3; i++) {
    -        //this.getTriangleVertices(i, a, b, c);
    -
    -        // Get unscaled triangle verts
    -        var i3 = i * 3;
    -        this._getUnscaledVertex(this.indices[i3], a);
    -        this._getUnscaledVertex(this.indices[i3 + 1], b);
    -        this._getUnscaledVertex(this.indices[i3 + 2], c);
    -
    -        triangleAABB.setFromPoints(points);
    -        tree.insert(triangleAABB, i);
    -    }
    -    tree.removeEmptyNodes();
    -};
    -
    -var unscaledAABB = new AABB();
    -
    -/**
    - * Get triangles in a local AABB from the trimesh.
    - * @method getTrianglesInAABB
    - * @param  {AABB} aabb
    - * @param  {array} result An array of integers, referencing the queried triangles.
    - */
    -Trimesh.prototype.getTrianglesInAABB = function(aabb, result){
    -    unscaledAABB.copy(aabb);
    -
    -    // Scale it to local
    -    var scale = this.scale;
    -    var isx = scale.x;
    -    var isy = scale.y;
    -    var isz = scale.z;
    -    var l = unscaledAABB.lowerBound;
    -    var u = unscaledAABB.upperBound;
    -    l.x /= isx;
    -    l.y /= isy;
    -    l.z /= isz;
    -    u.x /= isx;
    -    u.y /= isy;
    -    u.z /= isz;
    -
    -    return this.tree.aabbQuery(unscaledAABB, result);
    -};
    -
    -/**
    - * @method setScale
    - * @param {Vec3} scale
    - */
    -Trimesh.prototype.setScale = function(scale){
    -    var wasUniform = this.scale.x === this.scale.y === this.scale.z;
    -    var isUniform = scale.x === scale.y === scale.z;
    -
    -    if(!(wasUniform && isUniform)){
    -        // Non-uniform scaling. Need to update normals.
    -        this.updateNormals();
    -    }
    -    this.scale.copy(scale);
    -    this.updateAABB();
    -    this.updateBoundingSphereRadius();
    -};
    -
    -/**
    - * Compute the normals of the faces. Will save in the .normals array.
    - * @method updateNormals
    - */
    -Trimesh.prototype.updateNormals = function(){
    -    var n = computeNormals_n;
    -
    -    // Generate normals
    -    var normals = this.normals;
    -    for(var i=0; i < this.indices.length / 3; i++){
    -        var i3 = i * 3;
    -
    -        var a = this.indices[i3],
    -            b = this.indices[i3 + 1],
    -            c = this.indices[i3 + 2];
    -
    -        this.getVertex(a, va);
    -        this.getVertex(b, vb);
    -        this.getVertex(c, vc);
    -
    -        Trimesh.computeNormal(vb, va, vc, n);
    -
    -        normals[i3] = n.x;
    -        normals[i3 + 1] = n.y;
    -        normals[i3 + 2] = n.z;
    -    }
    -};
    -
    -/**
    - * Update the .edges property
    - * @method updateEdges
    - */
    -Trimesh.prototype.updateEdges = function(){
    -    var edges = {};
    -    var add = function(indexA, indexB){
    -        var key = a < b ? a + '_' + b : b + '_' + a;
    -        edges[key] = true;
    -    };
    -    for(var i=0; i < this.indices.length / 3; i++){
    -        var i3 = i * 3;
    -        var a = this.indices[i3],
    -            b = this.indices[i3 + 1],
    -            c = this.indices[i3 + 2];
    -        add(a,b);
    -        add(b,c);
    -        add(c,a);
    -    }
    -    var keys = Object.keys(edges);
    -    this.edges = new Int16Array(keys.length * 2);
    -    for (var i = 0; i < keys.length; i++) {
    -        var indices = keys[i].split('_');
    -        this.edges[2 * i] = parseInt(indices[0], 10);
    -        this.edges[2 * i + 1] = parseInt(indices[1], 10);
    -    }
    -};
    -
    -/**
    - * Get an edge vertex
    - * @method getEdgeVertex
    - * @param  {number} edgeIndex
    - * @param  {number} firstOrSecond 0 or 1, depending on which one of the vertices you need.
    - * @param  {Vec3} vertexStore Where to store the result
    - */
    -Trimesh.prototype.getEdgeVertex = function(edgeIndex, firstOrSecond, vertexStore){
    -    var vertexIndex = this.edges[edgeIndex * 2 + (firstOrSecond ? 1 : 0)];
    -    this.getVertex(vertexIndex, vertexStore);
    -};
    -
    -var getEdgeVector_va = new Vec3();
    -var getEdgeVector_vb = new Vec3();
    -
    -/**
    - * Get a vector along an edge.
    - * @method getEdgeVector
    - * @param  {number} edgeIndex
    - * @param  {Vec3} vectorStore
    - */
    -Trimesh.prototype.getEdgeVector = function(edgeIndex, vectorStore){
    -    var va = getEdgeVector_va;
    -    var vb = getEdgeVector_vb;
    -    this.getEdgeVertex(edgeIndex, 0, va);
    -    this.getEdgeVertex(edgeIndex, 1, vb);
    -    vb.vsub(va, vectorStore);
    -};
    -
    -/**
    - * Get face normal given 3 vertices
    - * @static
    - * @method computeNormal
    - * @param {Vec3} va
    - * @param {Vec3} vb
    - * @param {Vec3} vc
    - * @param {Vec3} target
    - */
    -var cb = new Vec3();
    -var ab = new Vec3();
    -Trimesh.computeNormal = function ( va, vb, vc, target ) {
    -    vb.vsub(va,ab);
    -    vc.vsub(vb,cb);
    -    cb.cross(ab,target);
    -    if ( !target.isZero() ) {
    -        target.normalize();
    -    }
    -};
    -
    -var va = new Vec3();
    -var vb = new Vec3();
    -var vc = new Vec3();
    -
    -/**
    - * Get vertex i.
    - * @method getVertex
    - * @param  {number} i
    - * @param  {Vec3} out
    - * @return {Vec3} The "out" vector object
    - */
    -Trimesh.prototype.getVertex = function(i, out){
    -    var scale = this.scale;
    -    this._getUnscaledVertex(i, out);
    -    out.x *= scale.x;
    -    out.y *= scale.y;
    -    out.z *= scale.z;
    -    return out;
    -};
    -
    -/**
    - * Get raw vertex i
    - * @private
    - * @method _getUnscaledVertex
    - * @param  {number} i
    - * @param  {Vec3} out
    - * @return {Vec3} The "out" vector object
    - */
    -Trimesh.prototype._getUnscaledVertex = function(i, out){
    -    var i3 = i * 3;
    -    var vertices = this.vertices;
    -    return out.set(
    -        vertices[i3],
    -        vertices[i3 + 1],
    -        vertices[i3 + 2]
    -    );
    -};
    -
    -/**
    - * Get a vertex from the trimesh,transformed by the given position and quaternion.
    - * @method getWorldVertex
    - * @param  {number} i
    - * @param  {Vec3} pos
    - * @param  {Quaternion} quat
    - * @param  {Vec3} out
    - * @return {Vec3} The "out" vector object
    - */
    -Trimesh.prototype.getWorldVertex = function(i, pos, quat, out){
    -    this.getVertex(i, out);
    -    Transform.pointToWorldFrame(pos, quat, out, out);
    -    return out;
    -};
    -
    -/**
    - * Get the three vertices for triangle i.
    - * @method getTriangleVertices
    - * @param  {number} i
    - * @param  {Vec3} a
    - * @param  {Vec3} b
    - * @param  {Vec3} c
    - */
    -Trimesh.prototype.getTriangleVertices = function(i, a, b, c){
    -    var i3 = i * 3;
    -    this.getVertex(this.indices[i3], a);
    -    this.getVertex(this.indices[i3 + 1], b);
    -    this.getVertex(this.indices[i3 + 2], c);
    -};
    -
    -/**
    - * Compute the normal of triangle i.
    - * @method getNormal
    - * @param  {Number} i
    - * @param  {Vec3} target
    - * @return {Vec3} The "target" vector object
    - */
    -Trimesh.prototype.getNormal = function(i, target){
    -    var i3 = i * 3;
    -    return target.set(
    -        this.normals[i3],
    -        this.normals[i3 + 1],
    -        this.normals[i3 + 2]
    -    );
    -};
    -
    -var cli_aabb = new AABB();
    -
    -/**
    - * @method calculateLocalInertia
    - * @param  {Number} mass
    - * @param  {Vec3} target
    - * @return {Vec3} The "target" vector object
    - */
    -Trimesh.prototype.calculateLocalInertia = function(mass,target){
    -    // Approximate with box inertia
    -    // Exact inertia calculation is overkill, but see http://geometrictools.com/Documentation/PolyhedralMassProperties.pdf for the correct way to do it
    -    this.computeLocalAABB(cli_aabb);
    -    var x = cli_aabb.upperBound.x - cli_aabb.lowerBound.x,
    -        y = cli_aabb.upperBound.y - cli_aabb.lowerBound.y,
    -        z = cli_aabb.upperBound.z - cli_aabb.lowerBound.z;
    -    return target.set(
    -        1.0 / 12.0 * mass * ( 2*y*2*y + 2*z*2*z ),
    -        1.0 / 12.0 * mass * ( 2*x*2*x + 2*z*2*z ),
    -        1.0 / 12.0 * mass * ( 2*y*2*y + 2*x*2*x )
    -    );
    -};
    -
    -var computeLocalAABB_worldVert = new Vec3();
    -
    -/**
    - * Compute the local AABB for the trimesh
    - * @method computeLocalAABB
    - * @param  {AABB} aabb
    - */
    -Trimesh.prototype.computeLocalAABB = function(aabb){
    -    var l = aabb.lowerBound,
    -        u = aabb.upperBound,
    -        n = this.vertices.length,
    -        vertices = this.vertices,
    -        v = computeLocalAABB_worldVert;
    -
    -    this.getVertex(0, v);
    -    l.copy(v);
    -    u.copy(v);
    -
    -    for(var i=0; i !== n; i++){
    -        this.getVertex(i, v);
    -
    -        if(v.x < l.x){
    -            l.x = v.x;
    -        } else if(v.x > u.x){
    -            u.x = v.x;
    -        }
    -
    -        if(v.y < l.y){
    -            l.y = v.y;
    -        } else if(v.y > u.y){
    -            u.y = v.y;
    -        }
    -
    -        if(v.z < l.z){
    -            l.z = v.z;
    -        } else if(v.z > u.z){
    -            u.z = v.z;
    -        }
    -    }
    -};
    -
    -
    -/**
    - * Update the .aabb property
    - * @method updateAABB
    - */
    -Trimesh.prototype.updateAABB = function(){
    -    this.computeLocalAABB(this.aabb);
    -};
    -
    -/**
    - * Will update the .boundingSphereRadius property
    - * @method updateBoundingSphereRadius
    - */
    -Trimesh.prototype.updateBoundingSphereRadius = function(){
    -    // Assume points are distributed with local (0,0,0) as center
    -    var max2 = 0;
    -    var vertices = this.vertices;
    -    var v = new Vec3();
    -    for(var i=0, N=vertices.length / 3; i !== N; i++) {
    -        this.getVertex(i, v);
    -        var norm2 = v.norm2();
    -        if(norm2 > max2){
    -            max2 = norm2;
    -        }
    -    }
    -    this.boundingSphereRadius = Math.sqrt(max2);
    -};
    -
    -var tempWorldVertex = new Vec3();
    -var calculateWorldAABB_frame = new Transform();
    -var calculateWorldAABB_aabb = new AABB();
    -
    -/**
    - * @method calculateWorldAABB
    - * @param {Vec3}        pos
    - * @param {Quaternion}  quat
    - * @param {Vec3}        min
    - * @param {Vec3}        max
    - */
    -Trimesh.prototype.calculateWorldAABB = function(pos,quat,min,max){
    -    /*
    -    var n = this.vertices.length / 3,
    -        verts = this.vertices;
    -    var minx,miny,minz,maxx,maxy,maxz;
    -
    -    var v = tempWorldVertex;
    -    for(var i=0; i<n; i++){
    -        this.getVertex(i, v);
    -        quat.vmult(v, v);
    -        pos.vadd(v, v);
    -        if (v.x < minx || minx===undefined){
    -            minx = v.x;
    -        } else if(v.x > maxx || maxx===undefined){
    -            maxx = v.x;
    -        }
    -
    -        if (v.y < miny || miny===undefined){
    -            miny = v.y;
    -        } else if(v.y > maxy || maxy===undefined){
    -            maxy = v.y;
    -        }
    -
    -        if (v.z < minz || minz===undefined){
    -            minz = v.z;
    -        } else if(v.z > maxz || maxz===undefined){
    -            maxz = v.z;
    -        }
    -    }
    -    min.set(minx,miny,minz);
    -    max.set(maxx,maxy,maxz);
    -    */
    -
    -    // Faster approximation using local AABB
    -    var frame = calculateWorldAABB_frame;
    -    var result = calculateWorldAABB_aabb;
    -    frame.position = pos;
    -    frame.quaternion = quat;
    -    this.aabb.toWorldFrame(frame, result);
    -    min.copy(result.lowerBound);
    -    max.copy(result.upperBound);
    -};
    -
    -/**
    - * Get approximate volume
    - * @method volume
    - * @return {Number}
    - */
    -Trimesh.prototype.volume = function(){
    -    return 4.0 * Math.PI * this.boundingSphereRadius / 3.0;
    -};
    -
    -/**
    - * Create a Trimesh instance, shaped as a torus.
    - * @static
    - * @method createTorus
    - * @param  {number} [radius=1]
    - * @param  {number} [tube=0.5]
    - * @param  {number} [radialSegments=8]
    - * @param  {number} [tubularSegments=6]
    - * @param  {number} [arc=6.283185307179586]
    - * @return {Trimesh} A torus
    - */
    -Trimesh.createTorus = function (radius, tube, radialSegments, tubularSegments, arc) {
    -    radius = radius || 1;
    -    tube = tube || 0.5;
    -    radialSegments = radialSegments || 8;
    -    tubularSegments = tubularSegments || 6;
    -    arc = arc || Math.PI * 2;
    -
    -    var vertices = [];
    -    var indices = [];
    -
    -    for ( var j = 0; j <= radialSegments; j ++ ) {
    -        for ( var i = 0; i <= tubularSegments; i ++ ) {
    -            var u = i / tubularSegments * arc;
    -            var v = j / radialSegments * Math.PI * 2;
    -
    -            var x = ( radius + tube * Math.cos( v ) ) * Math.cos( u );
    -            var y = ( radius + tube * Math.cos( v ) ) * Math.sin( u );
    -            var z = tube * Math.sin( v );
    -
    -            vertices.push( x, y, z );
    -        }
    -    }
    -
    -    for ( var j = 1; j <= radialSegments; j ++ ) {
    -        for ( var i = 1; i <= tubularSegments; i ++ ) {
    -            var a = ( tubularSegments + 1 ) * j + i - 1;
    -            var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1;
    -            var c = ( tubularSegments + 1 ) * ( j - 1 ) + i;
    -            var d = ( tubularSegments + 1 ) * j + i;
    -
    -            indices.push(a, b, d);
    -            indices.push(b, c, d);
    -        }
    -    }
    -
    -    return new Trimesh(vertices, indices);
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_solver_GSSolver.js.html b/docs/files/src_solver_GSSolver.js.html deleted file mode 100644 index c24b59c24..000000000 --- a/docs/files/src_solver_GSSolver.js.html +++ /dev/null @@ -1,287 +0,0 @@ - - - - - src/solver/GSSolver.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/solver/GSSolver.js

    - -
    -
    -module.exports = GSSolver;
    -
    -var Vec3 = require('../math/Vec3');
    -var Quaternion = require('../math/Quaternion');
    -var Solver = require('./Solver');
    -
    -/**
    - * Constraint equation Gauss-Seidel solver.
    - * @class GSSolver
    - * @constructor
    - * @todo The spook parameters should be specified for each constraint, not globally.
    - * @author schteppe / https://github.com/schteppe
    - * @see https://www8.cs.umu.se/kurser/5DV058/VT09/lectures/spooknotes.pdf
    - * @extends Solver
    - */
    -function GSSolver(){
    -    Solver.call(this);
    -
    -    /**
    -     * The number of solver iterations determines quality of the constraints in the world. The more iterations, the more correct simulation. More iterations need more computations though. If you have a large gravity force in your world, you will need more iterations.
    -     * @property iterations
    -     * @type {Number}
    -     * @todo write more about solver and iterations in the wiki
    -     */
    -    this.iterations = 10;
    -
    -    /**
    -     * When tolerance is reached, the system is assumed to be converged.
    -     * @property tolerance
    -     * @type {Number}
    -     */
    -    this.tolerance = 1e-7;
    -}
    -GSSolver.prototype = new Solver();
    -
    -var GSSolver_solve_lambda = []; // Just temporary number holders that we want to reuse each solve.
    -var GSSolver_solve_invCs = [];
    -var GSSolver_solve_Bs = [];
    -GSSolver.prototype.solve = function(dt,world){
    -    var iter = 0,
    -        maxIter = this.iterations,
    -        tolSquared = this.tolerance*this.tolerance,
    -        equations = this.equations,
    -        Neq = equations.length,
    -        bodies = world.bodies,
    -        Nbodies = bodies.length,
    -        h = dt,
    -        q, B, invC, deltalambda, deltalambdaTot, GWlambda, lambdaj;
    -
    -    // Update solve mass
    -    if(Neq !== 0){
    -        for(var i=0; i!==Nbodies; i++){
    -            bodies[i].updateSolveMassProperties();
    -        }
    -    }
    -
    -    // Things that does not change during iteration can be computed once
    -    var invCs = GSSolver_solve_invCs,
    -        Bs = GSSolver_solve_Bs,
    -        lambda = GSSolver_solve_lambda;
    -    invCs.length = Neq;
    -    Bs.length = Neq;
    -    lambda.length = Neq;
    -    for(var i=0; i!==Neq; i++){
    -        var c = equations[i];
    -        lambda[i] = 0.0;
    -        Bs[i] = c.computeB(h);
    -        invCs[i] = 1.0 / c.computeC();
    -    }
    -
    -    if(Neq !== 0){
    -
    -        // Reset vlambda
    -        for(var i=0; i!==Nbodies; i++){
    -            var b=bodies[i],
    -                vlambda=b.vlambda,
    -                wlambda=b.wlambda;
    -            vlambda.set(0,0,0);
    -            if(wlambda){
    -                wlambda.set(0,0,0);
    -            }
    -        }
    -
    -        // Iterate over equations
    -        for(iter=0; iter!==maxIter; iter++){
    -
    -            // Accumulate the total error for each iteration.
    -            deltalambdaTot = 0.0;
    -
    -            for(var j=0; j!==Neq; j++){
    -
    -                var c = equations[j];
    -
    -                // Compute iteration
    -                B = Bs[j];
    -                invC = invCs[j];
    -                lambdaj = lambda[j];
    -                GWlambda = c.computeGWlambda();
    -                deltalambda = invC * ( B - GWlambda - c.eps * lambdaj );
    -
    -                // Clamp if we are not within the min/max interval
    -                if(lambdaj + deltalambda < c.minForce){
    -                    deltalambda = c.minForce - lambdaj;
    -                } else if(lambdaj + deltalambda > c.maxForce){
    -                    deltalambda = c.maxForce - lambdaj;
    -                }
    -                lambda[j] += deltalambda;
    -
    -                deltalambdaTot += deltalambda > 0.0 ? deltalambda : -deltalambda; // abs(deltalambda)
    -
    -                c.addToWlambda(deltalambda);
    -            }
    -
    -            // If the total error is small enough - stop iterate
    -            if(deltalambdaTot*deltalambdaTot < tolSquared){
    -                break;
    -            }
    -        }
    -
    -        // Add result to velocity
    -        for(var i=0; i!==Nbodies; i++){
    -            var b=bodies[i],
    -                v=b.velocity,
    -                w=b.angularVelocity;
    -            v.vadd(b.vlambda, v);
    -            if(w){
    -                w.vadd(b.wlambda, w);
    -            }
    -        }
    -    }
    -
    -    return iter;
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_solver_Solver.js.html b/docs/files/src_solver_Solver.js.html deleted file mode 100644 index 68413119e..000000000 --- a/docs/files/src_solver_Solver.js.html +++ /dev/null @@ -1,213 +0,0 @@ - - - - - src/solver/Solver.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/solver/Solver.js

    - -
    -
    -module.exports = Solver;
    -
    -/**
    - * Constraint equation solver base class.
    - * @class Solver
    - * @constructor
    - * @author schteppe / https://github.com/schteppe
    - */
    -function Solver(){
    -    /**
    -     * All equations to be solved
    -     * @property {Array} equations
    -     */
    -    this.equations = [];
    -}
    -
    -/**
    - * Should be implemented in subclasses!
    - * @method solve
    - * @param  {Number} dt
    - * @param  {World} world
    - */
    -Solver.prototype.solve = function(dt,world){
    -    // Should return the number of iterations done!
    -    return 0;
    -};
    -
    -/**
    - * Add an equation
    - * @method addEquation
    - * @param {Equation} eq
    - */
    -Solver.prototype.addEquation = function(eq){
    -    if (eq.enabled) {
    -        this.equations.push(eq);
    -    }
    -};
    -
    -/**
    - * Remove an equation
    - * @method removeEquation
    - * @param {Equation} eq
    - */
    -Solver.prototype.removeEquation = function(eq){
    -    var eqs = this.equations;
    -    var i = eqs.indexOf(eq);
    -    if(i !== -1){
    -        eqs.splice(i,1);
    -    }
    -};
    -
    -/**
    - * Add all equations
    - * @method removeAllEquations
    - */
    -Solver.prototype.removeAllEquations = function(){
    -    this.equations.length = 0;
    -};
    -
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_solver_SplitSolver.js.html b/docs/files/src_solver_SplitSolver.js.html deleted file mode 100644 index 5befd8b6c..000000000 --- a/docs/files/src_solver_SplitSolver.js.html +++ /dev/null @@ -1,307 +0,0 @@ - - - - - src/solver/SplitSolver.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/solver/SplitSolver.js

    - -
    -
    -module.exports = SplitSolver;
    -
    -var Vec3 = require('../math/Vec3');
    -var Quaternion = require('../math/Quaternion');
    -var Solver = require('./Solver');
    -var Body = require('../objects/Body');
    -
    -/**
    - * Splits the equations into islands and solves them independently. Can improve performance.
    - * @class SplitSolver
    - * @constructor
    - * @extends Solver
    - * @param {Solver} subsolver
    - */
    -function SplitSolver(subsolver){
    -    Solver.call(this);
    -    this.iterations = 10;
    -    this.tolerance = 1e-7;
    -    this.subsolver = subsolver;
    -    this.nodes = [];
    -    this.nodePool = [];
    -
    -    // Create needed nodes, reuse if possible
    -    while(this.nodePool.length < 128){
    -        this.nodePool.push(this.createNode());
    -    }
    -}
    -SplitSolver.prototype = new Solver();
    -
    -// Returns the number of subsystems
    -var SplitSolver_solve_nodes = []; // All allocated node objects
    -var SplitSolver_solve_nodePool = []; // All allocated node objects
    -var SplitSolver_solve_eqs = [];   // Temp array
    -var SplitSolver_solve_bds = [];   // Temp array
    -var SplitSolver_solve_dummyWorld = {bodies:[]}; // Temp object
    -
    -var STATIC = Body.STATIC;
    -function getUnvisitedNode(nodes){
    -    var Nnodes = nodes.length;
    -    for(var i=0; i!==Nnodes; i++){
    -        var node = nodes[i];
    -        if(!node.visited && !(node.body.type & STATIC)){
    -            return node;
    -        }
    -    }
    -    return false;
    -}
    -
    -var queue = [];
    -function bfs(root,visitFunc,bds,eqs){
    -    queue.push(root);
    -    root.visited = true;
    -    visitFunc(root,bds,eqs);
    -    while(queue.length) {
    -        var node = queue.pop();
    -        // Loop over unvisited child nodes
    -        var child;
    -        while((child = getUnvisitedNode(node.children))) {
    -            child.visited = true;
    -            visitFunc(child,bds,eqs);
    -            queue.push(child);
    -        }
    -    }
    -}
    -
    -function visitFunc(node,bds,eqs){
    -    bds.push(node.body);
    -    var Neqs = node.eqs.length;
    -    for(var i=0; i!==Neqs; i++){
    -        var eq = node.eqs[i];
    -        if(eqs.indexOf(eq) === -1){
    -            eqs.push(eq);
    -        }
    -    }
    -}
    -
    -SplitSolver.prototype.createNode = function(){
    -    return { body:null, children:[], eqs:[], visited:false };
    -};
    -
    -/**
    - * Solve the subsystems
    - * @method solve
    - * @param  {Number} dt
    - * @param  {World} world
    - */
    -SplitSolver.prototype.solve = function(dt,world){
    -    var nodes=SplitSolver_solve_nodes,
    -        nodePool=this.nodePool,
    -        bodies=world.bodies,
    -        equations=this.equations,
    -        Neq=equations.length,
    -        Nbodies=bodies.length,
    -        subsolver=this.subsolver;
    -
    -    // Create needed nodes, reuse if possible
    -    while(nodePool.length < Nbodies){
    -        nodePool.push(this.createNode());
    -    }
    -    nodes.length = Nbodies;
    -    for (var i = 0; i < Nbodies; i++) {
    -        nodes[i] = nodePool[i];
    -    }
    -
    -    // Reset node values
    -    for(var i=0; i!==Nbodies; i++){
    -        var node = nodes[i];
    -        node.body = bodies[i];
    -        node.children.length = 0;
    -        node.eqs.length = 0;
    -        node.visited = false;
    -    }
    -    for(var k=0; k!==Neq; k++){
    -        var eq=equations[k],
    -            i=bodies.indexOf(eq.bi),
    -            j=bodies.indexOf(eq.bj),
    -            ni=nodes[i],
    -            nj=nodes[j];
    -        ni.children.push(nj);
    -        ni.eqs.push(eq);
    -        nj.children.push(ni);
    -        nj.eqs.push(eq);
    -    }
    -
    -    var child, n=0, eqs=SplitSolver_solve_eqs;
    -
    -    subsolver.tolerance = this.tolerance;
    -    subsolver.iterations = this.iterations;
    -
    -    var dummyWorld = SplitSolver_solve_dummyWorld;
    -    while((child = getUnvisitedNode(nodes))){
    -        eqs.length = 0;
    -        dummyWorld.bodies.length = 0;
    -        bfs(child, visitFunc, dummyWorld.bodies, eqs);
    -
    -        var Neqs = eqs.length;
    -
    -        eqs = eqs.sort(sortById);
    -
    -        for(var i=0; i!==Neqs; i++){
    -            subsolver.addEquation(eqs[i]);
    -        }
    -
    -        var iter = subsolver.solve(dt,dummyWorld);
    -        subsolver.removeAllEquations();
    -        n++;
    -    }
    -
    -    return n;
    -};
    -
    -function sortById(a, b){
    -    return b.id - a.id;
    -}
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_utils_EventTarget.js.html b/docs/files/src_utils_EventTarget.js.html deleted file mode 100644 index b79aed4ea..000000000 --- a/docs/files/src_utils_EventTarget.js.html +++ /dev/null @@ -1,241 +0,0 @@ - - - - - src/utils/EventTarget.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/utils/EventTarget.js

    - -
    -
    -/**
    - * Base class for objects that dispatches events.
    - * @class EventTarget
    - * @constructor
    - */
    -var EventTarget = function () {
    -
    -};
    -
    -module.exports = EventTarget;
    -
    -EventTarget.prototype = {
    -    constructor: EventTarget,
    -
    -    /**
    -     * Add an event listener
    -     * @method addEventListener
    -     * @param  {String} type
    -     * @param  {Function} listener
    -     * @return {EventTarget} The self object, for chainability.
    -     */
    -    addEventListener: function ( type, listener ) {
    -        if ( this._listeners === undefined ){ this._listeners = {}; }
    -        var listeners = this._listeners;
    -        if ( listeners[ type ] === undefined ) {
    -            listeners[ type ] = [];
    -        }
    -        if ( listeners[ type ].indexOf( listener ) === - 1 ) {
    -            listeners[ type ].push( listener );
    -        }
    -        return this;
    -    },
    -
    -    /**
    -     * Check if an event listener is added
    -     * @method hasEventListener
    -     * @param  {String} type
    -     * @param  {Function} listener
    -     * @return {Boolean}
    -     */
    -    hasEventListener: function ( type, listener ) {
    -        if ( this._listeners === undefined ){ return false; }
    -        var listeners = this._listeners;
    -        if ( listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1 ) {
    -            return true;
    -        }
    -        return false;
    -    },
    -
    -    /**
    -     * Remove an event listener
    -     * @method removeEventListener
    -     * @param  {String} type
    -     * @param  {Function} listener
    -     * @return {EventTarget} The self object, for chainability.
    -     */
    -    removeEventListener: function ( type, listener ) {
    -        if ( this._listeners === undefined ){ return this; }
    -        var listeners = this._listeners;
    -        if ( listeners[type] === undefined ){ return this; }
    -        var index = listeners[ type ].indexOf( listener );
    -        if ( index !== - 1 ) {
    -            listeners[ type ].splice( index, 1 );
    -        }
    -        return this;
    -    },
    -
    -    /**
    -     * Emit an event.
    -     * @method dispatchEvent
    -     * @param  {Object} event
    -     * @param  {String} event.type
    -     * @return {EventTarget} The self object, for chainability.
    -     */
    -    dispatchEvent: function ( event ) {
    -        if ( this._listeners === undefined ){ return this; }
    -        var listeners = this._listeners;
    -        var listenerArray = listeners[ event.type ];
    -        if ( listenerArray !== undefined ) {
    -            event.target = this;
    -            for ( var i = 0, l = listenerArray.length; i < l; i ++ ) {
    -                listenerArray[ i ].call( this, event );
    -            }
    -        }
    -        return this;
    -    }
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_utils_Octree.js.html b/docs/files/src_utils_Octree.js.html deleted file mode 100644 index e2feb5a57..000000000 --- a/docs/files/src_utils_Octree.js.html +++ /dev/null @@ -1,387 +0,0 @@ - - - - - src/utils/Octree.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/utils/Octree.js

    - -
    -
    -var AABB = require('../collision/AABB');
    -var Vec3 = require('../math/Vec3');
    -
    -module.exports = Octree;
    -
    -/**
    - * @class OctreeNode
    - * @param {object} [options]
    - * @param {Octree} [options.root]
    - * @param {AABB} [options.aabb]
    - */
    -function OctreeNode(options){
    -    options = options || {};
    -
    -    /**
    -     * The root node
    -     * @property {OctreeNode} root
    -     */
    -    this.root = options.root || null;
    -
    -    /**
    -     * Boundary of this node
    -     * @property {AABB} aabb
    -     */
    -    this.aabb = options.aabb ? options.aabb.clone() : new AABB();
    -
    -    /**
    -     * Contained data at the current node level.
    -     * @property {Array} data
    -     */
    -    this.data = [];
    -
    -    /**
    -     * Children to this node
    -     * @property {Array} children
    -     */
    -    this.children = [];
    -}
    -
    -/**
    - * @class Octree
    - * @param {AABB} aabb The total AABB of the tree
    - * @param {object} [options]
    - * @param {number} [options.maxDepth=8]
    - * @extends OctreeNode
    - */
    -function Octree(aabb, options){
    -    options = options || {};
    -    options.root = null;
    -    options.aabb = aabb;
    -    OctreeNode.call(this, options);
    -
    -    /**
    -     * Maximum subdivision depth
    -     * @property {number} maxDepth
    -     */
    -    this.maxDepth = typeof(options.maxDepth) !== 'undefined' ? options.maxDepth : 8;
    -}
    -Octree.prototype = new OctreeNode();
    -
    -OctreeNode.prototype.reset = function(aabb, options){
    -    this.children.length = this.data.length = 0;
    -};
    -
    -/**
    - * Insert data into this node
    - * @method insert
    - * @param  {AABB} aabb
    - * @param  {object} elementData
    - * @return {boolean} True if successful, otherwise false
    - */
    -OctreeNode.prototype.insert = function(aabb, elementData, level){
    -    var nodeData = this.data;
    -    level = level || 0;
    -
    -    // Ignore objects that do not belong in this node
    -    if (!this.aabb.contains(aabb)){
    -        return false; // object cannot be added
    -    }
    -
    -    var children = this.children;
    -
    -    if(level < (this.maxDepth || this.root.maxDepth)){
    -        // Subdivide if there are no children yet
    -        var subdivided = false;
    -        if (!children.length){
    -            this.subdivide();
    -            subdivided = true;
    -        }
    -
    -        // add to whichever node will accept it
    -        for (var i = 0; i !== 8; i++) {
    -            if (children[i].insert(aabb, elementData, level + 1)){
    -                return true;
    -            }
    -        }
    -
    -        if(subdivided){
    -            // No children accepted! Might as well just remove em since they contain none
    -            children.length = 0;
    -        }
    -    }
    -
    -    // Too deep, or children didnt want it. add it in current node
    -    nodeData.push(elementData);
    -
    -    return true;
    -};
    -
    -var halfDiagonal = new Vec3();
    -
    -/**
    - * Create 8 equally sized children nodes and put them in the .children array.
    - * @method subdivide
    - */
    -OctreeNode.prototype.subdivide = function() {
    -    var aabb = this.aabb;
    -    var l = aabb.lowerBound;
    -    var u = aabb.upperBound;
    -
    -    var children = this.children;
    -
    -    children.push(
    -        new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(0,0,0) }) }),
    -        new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(1,0,0) }) }),
    -        new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(1,1,0) }) }),
    -        new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(1,1,1) }) }),
    -        new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(0,1,1) }) }),
    -        new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(0,0,1) }) }),
    -        new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(1,0,1) }) }),
    -        new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(0,1,0) }) })
    -    );
    -
    -    u.vsub(l, halfDiagonal);
    -    halfDiagonal.scale(0.5, halfDiagonal);
    -
    -    var root = this.root || this;
    -
    -    for (var i = 0; i !== 8; i++) {
    -        var child = children[i];
    -
    -        // Set current node as root
    -        child.root = root;
    -
    -        // Compute bounds
    -        var lowerBound = child.aabb.lowerBound;
    -        lowerBound.x *= halfDiagonal.x;
    -        lowerBound.y *= halfDiagonal.y;
    -        lowerBound.z *= halfDiagonal.z;
    -
    -        lowerBound.vadd(l, lowerBound);
    -
    -        // Upper bound is always lower bound + halfDiagonal
    -        lowerBound.vadd(halfDiagonal, child.aabb.upperBound);
    -    }
    -};
    -
    -/**
    - * Get all data, potentially within an AABB
    - * @method aabbQuery
    - * @param  {AABB} aabb
    - * @param  {array} result
    - * @return {array} The "result" object
    - */
    -OctreeNode.prototype.aabbQuery = function(aabb, result) {
    -
    -    var nodeData = this.data;
    -
    -    // abort if the range does not intersect this node
    -    // if (!this.aabb.overlaps(aabb)){
    -    //     return result;
    -    // }
    -
    -    // Add objects at this level
    -    // Array.prototype.push.apply(result, nodeData);
    -
    -    // Add child data
    -    // @todo unwrap recursion into a queue / loop, that's faster in JS
    -    var children = this.children;
    -
    -
    -    // for (var i = 0, N = this.children.length; i !== N; i++) {
    -    //     children[i].aabbQuery(aabb, result);
    -    // }
    -
    -    var queue = [this];
    -    while (queue.length) {
    -        var node = queue.pop();
    -        if (node.aabb.overlaps(aabb)){
    -            Array.prototype.push.apply(result, node.data);
    -        }
    -        Array.prototype.push.apply(queue, node.children);
    -    }
    -
    -    return result;
    -};
    -
    -var tmpAABB = new AABB();
    -
    -/**
    - * Get all data, potentially intersected by a ray.
    - * @method rayQuery
    - * @param  {Ray} ray
    - * @param  {Transform} treeTransform
    - * @param  {array} result
    - * @return {array} The "result" object
    - */
    -OctreeNode.prototype.rayQuery = function(ray, treeTransform, result) {
    -
    -    // Use aabb query for now.
    -    // @todo implement real ray query which needs less lookups
    -    ray.getAABB(tmpAABB);
    -    tmpAABB.toLocalFrame(treeTransform, tmpAABB);
    -    this.aabbQuery(tmpAABB, result);
    -
    -    return result;
    -};
    -
    -/**
    - * @method removeEmptyNodes
    - */
    -OctreeNode.prototype.removeEmptyNodes = function() {
    -    var queue = [this];
    -    while (queue.length) {
    -        var node = queue.pop();
    -        for (var i = node.children.length - 1; i >= 0; i--) {
    -            if(!node.children[i].data.length){
    -                node.children.splice(i, 1);
    -            }
    -        }
    -        Array.prototype.push.apply(queue, node.children);
    -    }
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_utils_Pool.js.html b/docs/files/src_utils_Pool.js.html deleted file mode 100644 index 11a40eb1d..000000000 --- a/docs/files/src_utils_Pool.js.html +++ /dev/null @@ -1,208 +0,0 @@ - - - - - src/utils/Pool.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/utils/Pool.js

    - -
    -
    -module.exports = Pool;
    -
    -/**
    - * For pooling objects that can be reused.
    - * @class Pool
    - * @constructor
    - */
    -function Pool(){
    -    /**
    -     * The pooled objects
    -     * @property {Array} objects
    -     */
    -    this.objects = [];
    -
    -    /**
    -     * Constructor of the objects
    -     * @property {mixed} type
    -     */
    -    this.type = Object;
    -}
    -
    -/**
    - * Release an object after use
    - * @method release
    - * @param {Object} obj
    - */
    -Pool.prototype.release = function(){
    -    var Nargs = arguments.length;
    -    for(var i=0; i!==Nargs; i++){
    -        this.objects.push(arguments[i]);
    -    }
    -};
    -
    -/**
    - * Get an object
    - * @method get
    - * @return {mixed}
    - */
    -Pool.prototype.get = function(){
    -    if(this.objects.length===0){
    -        return this.constructObject();
    -    } else {
    -        return this.objects.pop();
    -    }
    -};
    -
    -/**
    - * Construct an object. Should be implmented in each subclass.
    - * @method constructObject
    - * @return {mixed}
    - */
    -Pool.prototype.constructObject = function(){
    -    throw new Error("constructObject() not implemented in this Pool subclass yet!");
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_utils_TupleDictionary.js.html b/docs/files/src_utils_TupleDictionary.js.html deleted file mode 100644 index 877b433fb..000000000 --- a/docs/files/src_utils_TupleDictionary.js.html +++ /dev/null @@ -1,219 +0,0 @@ - - - - - src/utils/TupleDictionary.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/utils/TupleDictionary.js

    - -
    -
    -module.exports = TupleDictionary;
    -
    -/**
    - * @class TupleDictionary
    - * @constructor
    - */
    -function TupleDictionary() {
    -
    -    /**
    -     * The data storage
    -     * @property data
    -     * @type {Object}
    -     */
    -    this.data = { keys:[] };
    -}
    -
    -/**
    - * @method get
    - * @param  {Number} i
    - * @param  {Number} j
    - * @return {Number}
    - */
    -TupleDictionary.prototype.get = function(i, j) {
    -    if (i > j) {
    -        // swap
    -        var temp = j;
    -        j = i;
    -        i = temp;
    -    }
    -    return this.data[i+'-'+j];
    -};
    -
    -/**
    - * @method set
    - * @param  {Number} i
    - * @param  {Number} j
    - * @param {Number} value
    - */
    -TupleDictionary.prototype.set = function(i, j, value) {
    -    if (i > j) {
    -        var temp = j;
    -        j = i;
    -        i = temp;
    -    }
    -    var key = i+'-'+j;
    -
    -    // Check if key already exists
    -    if(!this.get(i,j)){
    -        this.data.keys.push(key);
    -    }
    -
    -    this.data[key] = value;
    -};
    -
    -/**
    - * @method reset
    - */
    -TupleDictionary.prototype.reset = function() {
    -    var data = this.data,
    -        keys = data.keys;
    -    while(keys.length > 0){
    -        var key = keys.pop();
    -        delete data[key];
    -    }
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_utils_Utils.js.html b/docs/files/src_utils_Utils.js.html deleted file mode 100644 index 3f2349507..000000000 --- a/docs/files/src_utils_Utils.js.html +++ /dev/null @@ -1,177 +0,0 @@ - - - - - src/utils/Utils.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/utils/Utils.js

    - -
    -
    -function Utils(){}
    -
    -module.exports = Utils;
    -
    -/**
    - * Extend an options object with default values.
    - * @static
    - * @method defaults
    - * @param  {object} options The options object. May be falsy: in this case, a new object is created and returned.
    - * @param  {object} defaults An object containing default values.
    - * @return {object} The modified options object.
    - */
    -Utils.defaults = function(options, defaults){
    -    options = options || {};
    -
    -    for(var key in defaults){
    -        if(!(key in options)){
    -            options[key] = defaults[key];
    -        }
    -    }
    -
    -    return options;
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_utils_Vec3Pool.js.html b/docs/files/src_utils_Vec3Pool.js.html deleted file mode 100644 index 71be0ff53..000000000 --- a/docs/files/src_utils_Vec3Pool.js.html +++ /dev/null @@ -1,178 +0,0 @@ - - - - - src/utils/Vec3Pool.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/utils/Vec3Pool.js

    - -
    -
    -module.exports = Vec3Pool;
    -
    -var Vec3 = require('../math/Vec3');
    -var Pool = require('./Pool');
    -
    -/**
    - * @class Vec3Pool
    - * @constructor
    - * @extends Pool
    - */
    -function Vec3Pool(){
    -    Pool.call(this);
    -    this.type = Vec3;
    -}
    -Vec3Pool.prototype = new Pool();
    -
    -/**
    - * Construct a vector
    - * @method constructObject
    - * @return {Vec3}
    - */
    -Vec3Pool.prototype.constructObject = function(){
    -    return new Vec3();
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_world_Narrowphase.js.html b/docs/files/src_world_Narrowphase.js.html deleted file mode 100644 index 07cd1bd0c..000000000 --- a/docs/files/src_world_Narrowphase.js.html +++ /dev/null @@ -1,1892 +0,0 @@ - - - - - src/world/Narrowphase.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/world/Narrowphase.js

    - -
    -
    -module.exports = Narrowphase;
    -
    -var AABB = require('../collision/AABB');
    -var Shape = require('../shapes/Shape');
    -var Ray = require('../collision/Ray');
    -var Vec3 = require('../math/Vec3');
    -var Transform = require('../math/Transform');
    -var ConvexPolyhedron = require('../shapes/ConvexPolyhedron');
    -var Quaternion = require('../math/Quaternion');
    -var Solver = require('../solver/Solver');
    -var Vec3Pool = require('../utils/Vec3Pool');
    -var ContactEquation = require('../equations/ContactEquation');
    -var FrictionEquation = require('../equations/FrictionEquation');
    -
    -/**
    - * Helper class for the World. Generates ContactEquations.
    - * @class Narrowphase
    - * @constructor
    - * @todo Sphere-ConvexPolyhedron contacts
    - * @todo Contact reduction
    - * @todo  should move methods to prototype
    - */
    -function Narrowphase(world){
    -
    -    /**
    -     * Internal storage of pooled contact points.
    -     * @property {Array} contactPointPool
    -     */
    -    this.contactPointPool = [];
    -
    -    this.frictionEquationPool = [];
    -
    -    this.result = [];
    -    this.frictionResult = [];
    -
    -    /**
    -     * Pooled vectors.
    -     * @property {Vec3Pool} v3pool
    -     */
    -    this.v3pool = new Vec3Pool();
    -
    -    this.world = world;
    -    this.currentContactMaterial = null;
    -
    -    /**
    -     * @property {Boolean} enableFrictionReduction
    -     */
    -    this.enableFrictionReduction = false;
    -}
    -
    -/**
    - * Make a contact object, by using the internal pool or creating a new one.
    - * @method createContactEquation
    - * @return {ContactEquation}
    - */
    -Narrowphase.prototype.createContactEquation = function(bi, bj, si, sj, rsi, rsj){
    -    var c;
    -    if(this.contactPointPool.length){
    -        c = this.contactPointPool.pop();
    -        c.bi = bi;
    -        c.bj = bj;
    -    } else {
    -        c = new ContactEquation(bi, bj);
    -    }
    -
    -    c.enabled = bi.collisionResponse && bj.collisionResponse && si.collisionResponse && sj.collisionResponse;
    -
    -    var cm = this.currentContactMaterial;
    -
    -    c.restitution = cm.restitution;
    -
    -    c.setSpookParams(
    -        cm.contactEquationStiffness,
    -        cm.contactEquationRelaxation,
    -        this.world.dt
    -    );
    -
    -    var matA = si.material || bi.material;
    -    var matB = sj.material || bj.material;
    -    if(matA && matB && matA.restitution >= 0 && matB.restitution >= 0){
    -        c.restitution = matA.restitution * matB.restitution;
    -    }
    -
    -    c.si = rsi || si;
    -    c.sj = rsj || sj;
    -
    -    return c;
    -};
    -
    -Narrowphase.prototype.createFrictionEquationsFromContact = function(contactEquation, outArray){
    -    var bodyA = contactEquation.bi;
    -    var bodyB = contactEquation.bj;
    -    var shapeA = contactEquation.si;
    -    var shapeB = contactEquation.sj;
    -
    -    var world = this.world;
    -    var cm = this.currentContactMaterial;
    -
    -    // If friction or restitution were specified in the material, use them
    -    var friction = cm.friction;
    -    var matA = shapeA.material || bodyA.material;
    -    var matB = shapeB.material || bodyB.material;
    -    if(matA && matB && matA.friction >= 0 && matB.friction >= 0){
    -        friction = matA.friction * matB.friction;
    -    }
    -
    -    if(friction > 0){
    -
    -        // Create 2 tangent equations
    -        var mug = friction * world.gravity.length();
    -        var reducedMass = (bodyA.invMass + bodyB.invMass);
    -        if(reducedMass > 0){
    -            reducedMass = 1/reducedMass;
    -        }
    -        var pool = this.frictionEquationPool;
    -        var c1 = pool.length ? pool.pop() : new FrictionEquation(bodyA,bodyB,mug*reducedMass);
    -        var c2 = pool.length ? pool.pop() : new FrictionEquation(bodyA,bodyB,mug*reducedMass);
    -
    -        c1.bi = c2.bi = bodyA;
    -        c1.bj = c2.bj = bodyB;
    -        c1.minForce = c2.minForce = -mug*reducedMass;
    -        c1.maxForce = c2.maxForce = mug*reducedMass;
    -
    -        // Copy over the relative vectors
    -        c1.ri.copy(contactEquation.ri);
    -        c1.rj.copy(contactEquation.rj);
    -        c2.ri.copy(contactEquation.ri);
    -        c2.rj.copy(contactEquation.rj);
    -
    -        // Construct tangents
    -        contactEquation.ni.tangents(c1.t, c2.t);
    -
    -        // Set spook params
    -        c1.setSpookParams(cm.frictionEquationStiffness, cm.frictionEquationRelaxation, world.dt);
    -        c2.setSpookParams(cm.frictionEquationStiffness, cm.frictionEquationRelaxation, world.dt);
    -
    -        c1.enabled = c2.enabled = contactEquation.enabled;
    -
    -        outArray.push(c1, c2);
    -
    -        return true;
    -    }
    -
    -    return false;
    -};
    -
    -var averageNormal = new Vec3();
    -var averageContactPointA = new Vec3();
    -var averageContactPointB = new Vec3();
    -
    -// Take the average N latest contact point on the plane.
    -Narrowphase.prototype.createFrictionFromAverage = function(numContacts){
    -    // The last contactEquation
    -    var c = this.result[this.result.length - 1];
    -
    -    // Create the result: two "average" friction equations
    -    if (!this.createFrictionEquationsFromContact(c, this.frictionResult) || numContacts === 1) {
    -        return;
    -    }
    -
    -    var f1 = this.frictionResult[this.frictionResult.length - 2];
    -    var f2 = this.frictionResult[this.frictionResult.length - 1];
    -
    -    averageNormal.setZero();
    -    averageContactPointA.setZero();
    -    averageContactPointB.setZero();
    -
    -    var bodyA = c.bi;
    -    var bodyB = c.bj;
    -    for(var i=0; i!==numContacts; i++){
    -        c = this.result[this.result.length - 1 - i];
    -        if(c.bodyA !== bodyA){
    -            averageNormal.vadd(c.ni, averageNormal); // vec2.add(eq.t, eq.t, c.normalA);
    -            averageContactPointA.vadd(c.ri, averageContactPointA); // vec2.add(eq.contactPointA, eq.contactPointA, c.contactPointA);
    -            averageContactPointB.vadd(c.rj, averageContactPointB);
    -        } else {
    -            averageNormal.vsub(c.ni, averageNormal); // vec2.sub(eq.t, eq.t, c.normalA);
    -            averageContactPointA.vadd(c.rj, averageContactPointA); // vec2.add(eq.contactPointA, eq.contactPointA, c.contactPointA);
    -            averageContactPointB.vadd(c.ri, averageContactPointB);
    -        }
    -    }
    -
    -    var invNumContacts = 1 / numContacts;
    -    averageContactPointA.scale(invNumContacts, f1.ri); // vec2.scale(eq.contactPointA, eq.contactPointA, invNumContacts);
    -    averageContactPointB.scale(invNumContacts, f1.rj); // vec2.scale(eq.contactPointB, eq.contactPointB, invNumContacts);
    -    f2.ri.copy(f1.ri); // Should be the same
    -    f2.rj.copy(f1.rj);
    -    averageNormal.normalize();
    -    averageNormal.tangents(f1.t, f2.t);
    -    // return eq;
    -};
    -
    -
    -var tmpVec1 = new Vec3();
    -var tmpVec2 = new Vec3();
    -var tmpQuat1 = new Quaternion();
    -var tmpQuat2 = new Quaternion();
    -
    -/**
    - * Generate all contacts between a list of body pairs
    - * @method getContacts
    - * @param {array} p1 Array of body indices
    - * @param {array} p2 Array of body indices
    - * @param {World} world
    - * @param {array} result Array to store generated contacts
    - * @param {array} oldcontacts Optional. Array of reusable contact objects
    - */
    -Narrowphase.prototype.getContacts = function(p1, p2, world, result, oldcontacts, frictionResult, frictionPool){
    -    // Save old contact objects
    -    this.contactPointPool = oldcontacts;
    -    this.frictionEquationPool = frictionPool;
    -    this.result = result;
    -    this.frictionResult = frictionResult;
    -
    -    var qi = tmpQuat1;
    -    var qj = tmpQuat2;
    -    var xi = tmpVec1;
    -    var xj = tmpVec2;
    -
    -    for(var k=0, N=p1.length; k!==N; k++){
    -
    -        // Get current collision bodies
    -        var bi = p1[k],
    -            bj = p2[k];
    -
    -        // Get contact material
    -        var bodyContactMaterial = null;
    -        if(bi.material && bj.material){
    -            bodyContactMaterial = world.getContactMaterial(bi.material,bj.material) || null;
    -        }
    -
    -        for (var i = 0; i < bi.shapes.length; i++) {
    -            bi.quaternion.mult(bi.shapeOrientations[i], qi);
    -            bi.quaternion.vmult(bi.shapeOffsets[i], xi);
    -            xi.vadd(bi.position, xi);
    -            var si = bi.shapes[i];
    -
    -            for (var j = 0; j < bj.shapes.length; j++) {
    -
    -                // Compute world transform of shapes
    -                bj.quaternion.mult(bj.shapeOrientations[j], qj);
    -                bj.quaternion.vmult(bj.shapeOffsets[j], xj);
    -                xj.vadd(bj.position, xj);
    -                var sj = bj.shapes[j];
    -
    -                if(xi.distanceTo(xj) > si.boundingSphereRadius + sj.boundingSphereRadius){
    -                    continue;
    -                }
    -
    -                // Get collision material
    -                var shapeContactMaterial = null;
    -                if(si.material && sj.material){
    -                    shapeContactMaterial = world.getContactMaterial(si.material,sj.material) || null;
    -                }
    -
    -                this.currentContactMaterial = shapeContactMaterial || bodyContactMaterial || world.defaultContactMaterial;
    -
    -                // Get contacts
    -                var resolver = this[si.type | sj.type];
    -                if(resolver){
    -                    if (si.type < sj.type) {
    -                        resolver.call(this, si, sj, xi, xj, qi, qj, bi, bj, si, sj);
    -                    } else {
    -                        resolver.call(this, sj, si, xj, xi, qj, qi, bj, bi, si, sj);
    -                    }
    -                }
    -            }
    -        }
    -    }
    -};
    -
    -var numWarnings = 0;
    -var maxWarnings = 10;
    -
    -function warn(msg){
    -    if(numWarnings > maxWarnings){
    -        return;
    -    }
    -
    -    numWarnings++;
    -
    -    console.warn(msg);
    -}
    -
    -Narrowphase.prototype[Shape.types.BOX | Shape.types.BOX] =
    -Narrowphase.prototype.boxBox = function(si,sj,xi,xj,qi,qj,bi,bj){
    -    si.convexPolyhedronRepresentation.material = si.material;
    -    sj.convexPolyhedronRepresentation.material = sj.material;
    -    si.convexPolyhedronRepresentation.collisionResponse = si.collisionResponse;
    -    sj.convexPolyhedronRepresentation.collisionResponse = sj.collisionResponse;
    -    this.convexConvex(si.convexPolyhedronRepresentation,sj.convexPolyhedronRepresentation,xi,xj,qi,qj,bi,bj,si,sj);
    -};
    -
    -Narrowphase.prototype[Shape.types.BOX | Shape.types.CONVEXPOLYHEDRON] =
    -Narrowphase.prototype.boxConvex = function(si,sj,xi,xj,qi,qj,bi,bj){
    -    si.convexPolyhedronRepresentation.material = si.material;
    -    si.convexPolyhedronRepresentation.collisionResponse = si.collisionResponse;
    -    this.convexConvex(si.convexPolyhedronRepresentation,sj,xi,xj,qi,qj,bi,bj,si,sj);
    -};
    -
    -Narrowphase.prototype[Shape.types.BOX | Shape.types.PARTICLE] =
    -Narrowphase.prototype.boxParticle = function(si,sj,xi,xj,qi,qj,bi,bj){
    -    si.convexPolyhedronRepresentation.material = si.material;
    -    si.convexPolyhedronRepresentation.collisionResponse = si.collisionResponse;
    -    this.convexParticle(si.convexPolyhedronRepresentation,sj,xi,xj,qi,qj,bi,bj,si,sj);
    -};
    -
    -/**
    - * @method sphereSphere
    - * @param  {Shape}      si
    - * @param  {Shape}      sj
    - * @param  {Vec3}       xi
    - * @param  {Vec3}       xj
    - * @param  {Quaternion} qi
    - * @param  {Quaternion} qj
    - * @param  {Body}       bi
    - * @param  {Body}       bj
    - */
    -Narrowphase.prototype[Shape.types.SPHERE] =
    -Narrowphase.prototype.sphereSphere = function(si,sj,xi,xj,qi,qj,bi,bj){
    -    // We will have only one contact in this case
    -    var r = this.createContactEquation(bi,bj,si,sj);
    -
    -    // Contact normal
    -    xj.vsub(xi, r.ni);
    -    r.ni.normalize();
    -
    -    // Contact point locations
    -    r.ri.copy(r.ni);
    -    r.rj.copy(r.ni);
    -    r.ri.mult(si.radius, r.ri);
    -    r.rj.mult(-sj.radius, r.rj);
    -
    -    r.ri.vadd(xi, r.ri);
    -    r.ri.vsub(bi.position, r.ri);
    -
    -    r.rj.vadd(xj, r.rj);
    -    r.rj.vsub(bj.position, r.rj);
    -
    -    this.result.push(r);
    -
    -    this.createFrictionEquationsFromContact(r, this.frictionResult);
    -};
    -
    -/**
    - * @method planeTrimesh
    - * @param  {Shape}      si
    - * @param  {Shape}      sj
    - * @param  {Vec3}       xi
    - * @param  {Vec3}       xj
    - * @param  {Quaternion} qi
    - * @param  {Quaternion} qj
    - * @param  {Body}       bi
    - * @param  {Body}       bj
    - */
    -var planeTrimesh_normal = new Vec3();
    -var planeTrimesh_relpos = new Vec3();
    -var planeTrimesh_projected = new Vec3();
    -Narrowphase.prototype[Shape.types.PLANE | Shape.types.TRIMESH] =
    -Narrowphase.prototype.planeTrimesh = function(
    -    planeShape,
    -    trimeshShape,
    -    planePos,
    -    trimeshPos,
    -    planeQuat,
    -    trimeshQuat,
    -    planeBody,
    -    trimeshBody
    -){
    -    // Make contacts!
    -    var v = new Vec3();
    -
    -    var normal = planeTrimesh_normal;
    -    normal.set(0,0,1);
    -    planeQuat.vmult(normal,normal); // Turn normal according to plane
    -
    -    for(var i=0; i<trimeshShape.vertices.length / 3; i++){
    -
    -        // Get world vertex from trimesh
    -        trimeshShape.getVertex(i, v);
    -
    -        // Safe up
    -        var v2 = new Vec3();
    -        v2.copy(v);
    -        Transform.pointToWorldFrame(trimeshPos, trimeshQuat, v2, v);
    -
    -        // Check plane side
    -        var relpos = planeTrimesh_relpos;
    -        v.vsub(planePos, relpos);
    -        var dot = normal.dot(relpos);
    -
    -        if(dot <= 0.0){
    -            var r = this.createContactEquation(planeBody,trimeshBody,planeShape,trimeshShape);
    -
    -            r.ni.copy(normal); // Contact normal is the plane normal
    -
    -            // Get vertex position projected on plane
    -            var projected = planeTrimesh_projected;
    -            normal.scale(relpos.dot(normal), projected);
    -            v.vsub(projected,projected);
    -
    -            // ri is the projected world position minus plane position
    -            r.ri.copy(projected);
    -            r.ri.vsub(planeBody.position, r.ri);
    -
    -            r.rj.copy(v);
    -            r.rj.vsub(trimeshBody.position, r.rj);
    -
    -            // Store result
    -            this.result.push(r);
    -            this.createFrictionEquationsFromContact(r, this.frictionResult);
    -        }
    -    }
    -};
    -
    -/**
    - * @method sphereTrimesh
    - * @param  {Shape}      sphereShape
    - * @param  {Shape}      trimeshShape
    - * @param  {Vec3}       spherePos
    - * @param  {Vec3}       trimeshPos
    - * @param  {Quaternion} sphereQuat
    - * @param  {Quaternion} trimeshQuat
    - * @param  {Body}       sphereBody
    - * @param  {Body}       trimeshBody
    - */
    -var sphereTrimesh_normal = new Vec3();
    -var sphereTrimesh_relpos = new Vec3();
    -var sphereTrimesh_projected = new Vec3();
    -var sphereTrimesh_v = new Vec3();
    -var sphereTrimesh_v2 = new Vec3();
    -var sphereTrimesh_edgeVertexA = new Vec3();
    -var sphereTrimesh_edgeVertexB = new Vec3();
    -var sphereTrimesh_edgeVector = new Vec3();
    -var sphereTrimesh_edgeVectorUnit = new Vec3();
    -var sphereTrimesh_localSpherePos = new Vec3();
    -var sphereTrimesh_tmp = new Vec3();
    -var sphereTrimesh_va = new Vec3();
    -var sphereTrimesh_vb = new Vec3();
    -var sphereTrimesh_vc = new Vec3();
    -var sphereTrimesh_localSphereAABB = new AABB();
    -var sphereTrimesh_triangles = [];
    -Narrowphase.prototype[Shape.types.SPHERE | Shape.types.TRIMESH] =
    -Narrowphase.prototype.sphereTrimesh = function (
    -    sphereShape,
    -    trimeshShape,
    -    spherePos,
    -    trimeshPos,
    -    sphereQuat,
    -    trimeshQuat,
    -    sphereBody,
    -    trimeshBody
    -) {
    -
    -    var edgeVertexA = sphereTrimesh_edgeVertexA;
    -    var edgeVertexB = sphereTrimesh_edgeVertexB;
    -    var edgeVector = sphereTrimesh_edgeVector;
    -    var edgeVectorUnit = sphereTrimesh_edgeVectorUnit;
    -    var localSpherePos = sphereTrimesh_localSpherePos;
    -    var tmp = sphereTrimesh_tmp;
    -    var localSphereAABB = sphereTrimesh_localSphereAABB;
    -    var v2 = sphereTrimesh_v2;
    -    var relpos = sphereTrimesh_relpos;
    -    var triangles = sphereTrimesh_triangles;
    -
    -    // Convert sphere position to local in the trimesh
    -    Transform.pointToLocalFrame(trimeshPos, trimeshQuat, spherePos, localSpherePos);
    -
    -    // Get the aabb of the sphere locally in the trimesh
    -    var sphereRadius = sphereShape.radius;
    -    localSphereAABB.lowerBound.set(
    -        localSpherePos.x - sphereRadius,
    -        localSpherePos.y - sphereRadius,
    -        localSpherePos.z - sphereRadius
    -    );
    -    localSphereAABB.upperBound.set(
    -        localSpherePos.x + sphereRadius,
    -        localSpherePos.y + sphereRadius,
    -        localSpherePos.z + sphereRadius
    -    );
    -
    -    trimeshShape.getTrianglesInAABB(localSphereAABB, triangles);
    -    //for (var i = 0; i < trimeshShape.indices.length / 3; i++) triangles.push(i); // All
    -
    -    // Vertices
    -    var v = sphereTrimesh_v;
    -    var radiusSquared = sphereShape.radius * sphereShape.radius;
    -    for(var i=0; i<triangles.length; i++){
    -        for (var j = 0; j < 3; j++) {
    -
    -            trimeshShape.getVertex(trimeshShape.indices[triangles[i] * 3 + j], v);
    -
    -            // Check vertex overlap in sphere
    -            v.vsub(localSpherePos, relpos);
    -
    -            if(relpos.norm2() <= radiusSquared){
    -
    -                // Safe up
    -                v2.copy(v);
    -                Transform.pointToWorldFrame(trimeshPos, trimeshQuat, v2, v);
    -
    -                v.vsub(spherePos, relpos);
    -
    -                var r = this.createContactEquation(sphereBody,trimeshBody,sphereShape,trimeshShape);
    -                r.ni.copy(relpos);
    -                r.ni.normalize();
    -
    -                // ri is the vector from sphere center to the sphere surface
    -                r.ri.copy(r.ni);
    -                r.ri.scale(sphereShape.radius, r.ri);
    -                r.ri.vadd(spherePos, r.ri);
    -                r.ri.vsub(sphereBody.position, r.ri);
    -
    -                r.rj.copy(v);
    -                r.rj.vsub(trimeshBody.position, r.rj);
    -
    -                // Store result
    -                this.result.push(r);
    -                this.createFrictionEquationsFromContact(r, this.frictionResult);
    -            }
    -        }
    -    }
    -
    -    // Check all edges
    -    for(var i=0; i<triangles.length; i++){
    -        for (var j = 0; j < 3; j++) {
    -
    -            trimeshShape.getVertex(trimeshShape.indices[triangles[i] * 3 + j], edgeVertexA);
    -            trimeshShape.getVertex(trimeshShape.indices[triangles[i] * 3 + ((j+1)%3)], edgeVertexB);
    -            edgeVertexB.vsub(edgeVertexA, edgeVector);
    -
    -            // Project sphere position to the edge
    -            localSpherePos.vsub(edgeVertexB, tmp);
    -            var positionAlongEdgeB = tmp.dot(edgeVector);
    -
    -            localSpherePos.vsub(edgeVertexA, tmp);
    -            var positionAlongEdgeA = tmp.dot(edgeVector);
    -
    -            if(positionAlongEdgeA > 0 && positionAlongEdgeB < 0){
    -
    -                // Now check the orthogonal distance from edge to sphere center
    -                localSpherePos.vsub(edgeVertexA, tmp);
    -
    -                edgeVectorUnit.copy(edgeVector);
    -                edgeVectorUnit.normalize();
    -                positionAlongEdgeA = tmp.dot(edgeVectorUnit);
    -
    -                edgeVectorUnit.scale(positionAlongEdgeA, tmp);
    -                tmp.vadd(edgeVertexA, tmp);
    -
    -                // tmp is now the sphere center position projected to the edge, defined locally in the trimesh frame
    -                var dist = tmp.distanceTo(localSpherePos);
    -                if(dist < sphereShape.radius){
    -                    var r = this.createContactEquation(sphereBody, trimeshBody, sphereShape, trimeshShape);
    -
    -                    tmp.vsub(localSpherePos, r.ni);
    -                    r.ni.normalize();
    -                    r.ni.scale(sphereShape.radius, r.ri);
    -
    -                    Transform.pointToWorldFrame(trimeshPos, trimeshQuat, tmp, tmp);
    -                    tmp.vsub(trimeshBody.position, r.rj);
    -
    -                    Transform.vectorToWorldFrame(trimeshQuat, r.ni, r.ni);
    -                    Transform.vectorToWorldFrame(trimeshQuat, r.ri, r.ri);
    -
    -                    this.result.push(r);
    -                    this.createFrictionEquationsFromContact(r, this.frictionResult);
    -                }
    -            }
    -        }
    -    }
    -
    -    // Triangle faces
    -    var va = sphereTrimesh_va;
    -    var vb = sphereTrimesh_vb;
    -    var vc = sphereTrimesh_vc;
    -    var normal = sphereTrimesh_normal;
    -    for(var i=0, N = triangles.length; i !== N; i++){
    -        trimeshShape.getTriangleVertices(triangles[i], va, vb, vc);
    -        trimeshShape.getNormal(triangles[i], normal);
    -        localSpherePos.vsub(va, tmp);
    -        var dist = tmp.dot(normal);
    -        normal.scale(dist, tmp);
    -        localSpherePos.vsub(tmp, tmp);
    -
    -        // tmp is now the sphere position projected to the triangle plane
    -        dist = tmp.distanceTo(localSpherePos);
    -        if(Ray.pointInTriangle(tmp, va, vb, vc) && dist < sphereShape.radius){
    -            var r = this.createContactEquation(sphereBody, trimeshBody, sphereShape, trimeshShape);
    -
    -            tmp.vsub(localSpherePos, r.ni);
    -            r.ni.normalize();
    -            r.ni.scale(sphereShape.radius, r.ri);
    -
    -            Transform.pointToWorldFrame(trimeshPos, trimeshQuat, tmp, tmp);
    -            tmp.vsub(trimeshBody.position, r.rj);
    -
    -            Transform.vectorToWorldFrame(trimeshQuat, r.ni, r.ni);
    -            Transform.vectorToWorldFrame(trimeshQuat, r.ri, r.ri);
    -
    -            this.result.push(r);
    -            this.createFrictionEquationsFromContact(r, this.frictionResult);
    -        }
    -    }
    -
    -    triangles.length = 0;
    -};
    -
    -var point_on_plane_to_sphere = new Vec3();
    -var plane_to_sphere_ortho = new Vec3();
    -
    -/**
    - * @method spherePlane
    - * @param  {Shape}      si
    - * @param  {Shape}      sj
    - * @param  {Vec3}       xi
    - * @param  {Vec3}       xj
    - * @param  {Quaternion} qi
    - * @param  {Quaternion} qj
    - * @param  {Body}       bi
    - * @param  {Body}       bj
    - */
    -Narrowphase.prototype[Shape.types.SPHERE | Shape.types.PLANE] =
    -Narrowphase.prototype.spherePlane = function(si,sj,xi,xj,qi,qj,bi,bj){
    -    // We will have one contact in this case
    -    var r = this.createContactEquation(bi,bj,si,sj);
    -
    -    // Contact normal
    -    r.ni.set(0,0,1);
    -    qj.vmult(r.ni, r.ni);
    -    r.ni.negate(r.ni); // body i is the sphere, flip normal
    -    r.ni.normalize(); // Needed?
    -
    -    // Vector from sphere center to contact point
    -    r.ni.mult(si.radius, r.ri);
    -
    -    // Project down sphere on plane
    -    xi.vsub(xj, point_on_plane_to_sphere);
    -    r.ni.mult(r.ni.dot(point_on_plane_to_sphere), plane_to_sphere_ortho);
    -    point_on_plane_to_sphere.vsub(plane_to_sphere_ortho,r.rj); // The sphere position projected to plane
    -
    -    if(-point_on_plane_to_sphere.dot(r.ni) <= si.radius){
    -
    -        // Make it relative to the body
    -        var ri = r.ri;
    -        var rj = r.rj;
    -        ri.vadd(xi, ri);
    -        ri.vsub(bi.position, ri);
    -        rj.vadd(xj, rj);
    -        rj.vsub(bj.position, rj);
    -
    -        this.result.push(r);
    -        this.createFrictionEquationsFromContact(r, this.frictionResult);
    -    }
    -};
    -
    -// See http://bulletphysics.com/Bullet/BulletFull/SphereTriangleDetector_8cpp_source.html
    -var pointInPolygon_edge = new Vec3();
    -var pointInPolygon_edge_x_normal = new Vec3();
    -var pointInPolygon_vtp = new Vec3();
    -function pointInPolygon(verts, normal, p){
    -    var positiveResult = null;
    -    var N = verts.length;
    -    for(var i=0; i!==N; i++){
    -        var v = verts[i];
    -
    -        // Get edge to the next vertex
    -        var edge = pointInPolygon_edge;
    -        verts[(i+1) % (N)].vsub(v,edge);
    -
    -        // Get cross product between polygon normal and the edge
    -        var edge_x_normal = pointInPolygon_edge_x_normal;
    -        //var edge_x_normal = new Vec3();
    -        edge.cross(normal,edge_x_normal);
    -
    -        // Get vector between point and current vertex
    -        var vertex_to_p = pointInPolygon_vtp;
    -        p.vsub(v,vertex_to_p);
    -
    -        // This dot product determines which side of the edge the point is
    -        var r = edge_x_normal.dot(vertex_to_p);
    -
    -        // If all such dot products have same sign, we are inside the polygon.
    -        if(positiveResult===null || (r>0 && positiveResult===true) || (r<=0 && positiveResult===false)){
    -            if(positiveResult===null){
    -                positiveResult = r>0;
    -            }
    -            continue;
    -        } else {
    -            return false; // Encountered some other sign. Exit.
    -        }
    -    }
    -
    -    // If we got here, all dot products were of the same sign.
    -    return true;
    -}
    -
    -var box_to_sphere = new Vec3();
    -var sphereBox_ns = new Vec3();
    -var sphereBox_ns1 = new Vec3();
    -var sphereBox_ns2 = new Vec3();
    -var sphereBox_sides = [new Vec3(),new Vec3(),new Vec3(),new Vec3(),new Vec3(),new Vec3()];
    -var sphereBox_sphere_to_corner = new Vec3();
    -var sphereBox_side_ns = new Vec3();
    -var sphereBox_side_ns1 = new Vec3();
    -var sphereBox_side_ns2 = new Vec3();
    -
    -/**
    - * @method sphereBox
    - * @param  {Shape}      si
    - * @param  {Shape}      sj
    - * @param  {Vec3}       xi
    - * @param  {Vec3}       xj
    - * @param  {Quaternion} qi
    - * @param  {Quaternion} qj
    - * @param  {Body}       bi
    - * @param  {Body}       bj
    - */
    -Narrowphase.prototype[Shape.types.SPHERE | Shape.types.BOX] =
    -Narrowphase.prototype.sphereBox = function(si,sj,xi,xj,qi,qj,bi,bj){
    -    var v3pool = this.v3pool;
    -
    -    // we refer to the box as body j
    -    var sides = sphereBox_sides;
    -    xi.vsub(xj,box_to_sphere);
    -    sj.getSideNormals(sides,qj);
    -    var R =     si.radius;
    -    var penetrating_sides = [];
    -
    -    // Check side (plane) intersections
    -    var found = false;
    -
    -    // Store the resulting side penetration info
    -    var side_ns = sphereBox_side_ns;
    -    var side_ns1 = sphereBox_side_ns1;
    -    var side_ns2 = sphereBox_side_ns2;
    -    var side_h = null;
    -    var side_penetrations = 0;
    -    var side_dot1 = 0;
    -    var side_dot2 = 0;
    -    var side_distance = null;
    -    for(var idx=0,nsides=sides.length; idx!==nsides && found===false; idx++){
    -        // Get the plane side normal (ns)
    -        var ns = sphereBox_ns;
    -        ns.copy(sides[idx]);
    -
    -        var h = ns.norm();
    -        ns.normalize();
    -
    -        // The normal/distance dot product tells which side of the plane we are
    -        var dot = box_to_sphere.dot(ns);
    -
    -        if(dot<h+R && dot>0){
    -            // Intersects plane. Now check the other two dimensions
    -            var ns1 = sphereBox_ns1;
    -            var ns2 = sphereBox_ns2;
    -            ns1.copy(sides[(idx+1)%3]);
    -            ns2.copy(sides[(idx+2)%3]);
    -            var h1 = ns1.norm();
    -            var h2 = ns2.norm();
    -            ns1.normalize();
    -            ns2.normalize();
    -            var dot1 = box_to_sphere.dot(ns1);
    -            var dot2 = box_to_sphere.dot(ns2);
    -            if(dot1<h1 && dot1>-h1 && dot2<h2 && dot2>-h2){
    -                var dist = Math.abs(dot-h-R);
    -                if(side_distance===null || dist < side_distance){
    -                    side_distance = dist;
    -                    side_dot1 = dot1;
    -                    side_dot2 = dot2;
    -                    side_h = h;
    -                    side_ns.copy(ns);
    -                    side_ns1.copy(ns1);
    -                    side_ns2.copy(ns2);
    -                    side_penetrations++;
    -                }
    -            }
    -        }
    -    }
    -    if(side_penetrations){
    -        found = true;
    -        var r = this.createContactEquation(bi,bj,si,sj);
    -        side_ns.mult(-R,r.ri); // Sphere r
    -        r.ni.copy(side_ns);
    -        r.ni.negate(r.ni); // Normal should be out of sphere
    -        side_ns.mult(side_h,side_ns);
    -        side_ns1.mult(side_dot1,side_ns1);
    -        side_ns.vadd(side_ns1,side_ns);
    -        side_ns2.mult(side_dot2,side_ns2);
    -        side_ns.vadd(side_ns2,r.rj);
    -
    -        // Make relative to bodies
    -        r.ri.vadd(xi, r.ri);
    -        r.ri.vsub(bi.position, r.ri);
    -        r.rj.vadd(xj, r.rj);
    -        r.rj.vsub(bj.position, r.rj);
    -
    -        this.result.push(r);
    -        this.createFrictionEquationsFromContact(r, this.frictionResult);
    -    }
    -
    -    // Check corners
    -    var rj = v3pool.get();
    -    var sphere_to_corner = sphereBox_sphere_to_corner;
    -    for(var j=0; j!==2 && !found; j++){
    -        for(var k=0; k!==2 && !found; k++){
    -            for(var l=0; l!==2 && !found; l++){
    -                rj.set(0,0,0);
    -                if(j){
    -                    rj.vadd(sides[0],rj);
    -                } else {
    -                    rj.vsub(sides[0],rj);
    -                }
    -                if(k){
    -                    rj.vadd(sides[1],rj);
    -                } else {
    -                    rj.vsub(sides[1],rj);
    -                }
    -                if(l){
    -                    rj.vadd(sides[2],rj);
    -                } else {
    -                    rj.vsub(sides[2],rj);
    -                }
    -
    -                // World position of corner
    -                xj.vadd(rj,sphere_to_corner);
    -                sphere_to_corner.vsub(xi,sphere_to_corner);
    -
    -                if(sphere_to_corner.norm2() < R*R){
    -                    found = true;
    -                    var r = this.createContactEquation(bi,bj,si,sj);
    -                    r.ri.copy(sphere_to_corner);
    -                    r.ri.normalize();
    -                    r.ni.copy(r.ri);
    -                    r.ri.mult(R,r.ri);
    -                    r.rj.copy(rj);
    -
    -                    // Make relative to bodies
    -                    r.ri.vadd(xi, r.ri);
    -                    r.ri.vsub(bi.position, r.ri);
    -                    r.rj.vadd(xj, r.rj);
    -                    r.rj.vsub(bj.position, r.rj);
    -
    -                    this.result.push(r);
    -                    this.createFrictionEquationsFromContact(r, this.frictionResult);
    -                }
    -            }
    -        }
    -    }
    -    v3pool.release(rj);
    -    rj = null;
    -
    -    // Check edges
    -    var edgeTangent = v3pool.get();
    -    var edgeCenter = v3pool.get();
    -    var r = v3pool.get(); // r = edge center to sphere center
    -    var orthogonal = v3pool.get();
    -    var dist = v3pool.get();
    -    var Nsides = sides.length;
    -    for(var j=0; j!==Nsides && !found; j++){
    -        for(var k=0; k!==Nsides && !found; k++){
    -            if(j%3 !== k%3){
    -                // Get edge tangent
    -                sides[k].cross(sides[j],edgeTangent);
    -                edgeTangent.normalize();
    -                sides[j].vadd(sides[k], edgeCenter);
    -                r.copy(xi);
    -                r.vsub(edgeCenter,r);
    -                r.vsub(xj,r);
    -                var orthonorm = r.dot(edgeTangent); // distance from edge center to sphere center in the tangent direction
    -                edgeTangent.mult(orthonorm,orthogonal); // Vector from edge center to sphere center in the tangent direction
    -
    -                // Find the third side orthogonal to this one
    -                var l = 0;
    -                while(l===j%3 || l===k%3){
    -                    l++;
    -                }
    -
    -                // vec from edge center to sphere projected to the plane orthogonal to the edge tangent
    -                dist.copy(xi);
    -                dist.vsub(orthogonal,dist);
    -                dist.vsub(edgeCenter,dist);
    -                dist.vsub(xj,dist);
    -
    -                // Distances in tangent direction and distance in the plane orthogonal to it
    -                var tdist = Math.abs(orthonorm);
    -                var ndist = dist.norm();
    -
    -                if(tdist < sides[l].norm() && ndist<R){
    -                    found = true;
    -                    var res = this.createContactEquation(bi,bj,si,sj);
    -                    edgeCenter.vadd(orthogonal,res.rj); // box rj
    -                    res.rj.copy(res.rj);
    -                    dist.negate(res.ni);
    -                    res.ni.normalize();
    -
    -                    res.ri.copy(res.rj);
    -                    res.ri.vadd(xj,res.ri);
    -                    res.ri.vsub(xi,res.ri);
    -                    res.ri.normalize();
    -                    res.ri.mult(R,res.ri);
    -
    -                    // Make relative to bodies
    -                    res.ri.vadd(xi, res.ri);
    -                    res.ri.vsub(bi.position, res.ri);
    -                    res.rj.vadd(xj, res.rj);
    -                    res.rj.vsub(bj.position, res.rj);
    -
    -                    this.result.push(res);
    -                    this.createFrictionEquationsFromContact(res, this.frictionResult);
    -                }
    -            }
    -        }
    -    }
    -    v3pool.release(edgeTangent,edgeCenter,r,orthogonal,dist);
    -};
    -
    -var convex_to_sphere = new Vec3();
    -var sphereConvex_edge = new Vec3();
    -var sphereConvex_edgeUnit = new Vec3();
    -var sphereConvex_sphereToCorner = new Vec3();
    -var sphereConvex_worldCorner = new Vec3();
    -var sphereConvex_worldNormal = new Vec3();
    -var sphereConvex_worldPoint = new Vec3();
    -var sphereConvex_worldSpherePointClosestToPlane = new Vec3();
    -var sphereConvex_penetrationVec = new Vec3();
    -var sphereConvex_sphereToWorldPoint = new Vec3();
    -
    -/**
    - * @method sphereConvex
    - * @param  {Shape}      si
    - * @param  {Shape}      sj
    - * @param  {Vec3}       xi
    - * @param  {Vec3}       xj
    - * @param  {Quaternion} qi
    - * @param  {Quaternion} qj
    - * @param  {Body}       bi
    - * @param  {Body}       bj
    - */
    -Narrowphase.prototype[Shape.types.SPHERE | Shape.types.CONVEXPOLYHEDRON] =
    -Narrowphase.prototype.sphereConvex = function(si,sj,xi,xj,qi,qj,bi,bj){
    -    var v3pool = this.v3pool;
    -    xi.vsub(xj,convex_to_sphere);
    -    var normals = sj.faceNormals;
    -    var faces = sj.faces;
    -    var verts = sj.vertices;
    -    var R =     si.radius;
    -    var penetrating_sides = [];
    -
    -    // if(convex_to_sphere.norm2() > si.boundingSphereRadius + sj.boundingSphereRadius){
    -    //     return;
    -    // }
    -
    -    // Check corners
    -    for(var i=0; i!==verts.length; i++){
    -        var v = verts[i];
    -
    -        // World position of corner
    -        var worldCorner = sphereConvex_worldCorner;
    -        qj.vmult(v,worldCorner);
    -        xj.vadd(worldCorner,worldCorner);
    -        var sphere_to_corner = sphereConvex_sphereToCorner;
    -        worldCorner.vsub(xi, sphere_to_corner);
    -        if(sphere_to_corner.norm2() < R * R){
    -            found = true;
    -            var r = this.createContactEquation(bi,bj,si,sj);
    -            r.ri.copy(sphere_to_corner);
    -            r.ri.normalize();
    -            r.ni.copy(r.ri);
    -            r.ri.mult(R,r.ri);
    -            worldCorner.vsub(xj,r.rj);
    -
    -            // Should be relative to the body.
    -            r.ri.vadd(xi, r.ri);
    -            r.ri.vsub(bi.position, r.ri);
    -
    -            // Should be relative to the body.
    -            r.rj.vadd(xj, r.rj);
    -            r.rj.vsub(bj.position, r.rj);
    -
    -            this.result.push(r);
    -            this.createFrictionEquationsFromContact(r, this.frictionResult);
    -            return;
    -        }
    -    }
    -
    -    // Check side (plane) intersections
    -    var found = false;
    -    for(var i=0, nfaces=faces.length; i!==nfaces && found===false; i++){
    -        var normal = normals[i];
    -        var face = faces[i];
    -
    -        // Get world-transformed normal of the face
    -        var worldNormal = sphereConvex_worldNormal;
    -        qj.vmult(normal,worldNormal);
    -
    -        // Get a world vertex from the face
    -        var worldPoint = sphereConvex_worldPoint;
    -        qj.vmult(verts[face[0]],worldPoint);
    -        worldPoint.vadd(xj,worldPoint);
    -
    -        // Get a point on the sphere, closest to the face normal
    -        var worldSpherePointClosestToPlane = sphereConvex_worldSpherePointClosestToPlane;
    -        worldNormal.mult(-R, worldSpherePointClosestToPlane);
    -        xi.vadd(worldSpherePointClosestToPlane, worldSpherePointClosestToPlane);
    -
    -        // Vector from a face point to the closest point on the sphere
    -        var penetrationVec = sphereConvex_penetrationVec;
    -        worldSpherePointClosestToPlane.vsub(worldPoint,penetrationVec);
    -
    -        // The penetration. Negative value means overlap.
    -        var penetration = penetrationVec.dot(worldNormal);
    -
    -        var worldPointToSphere = sphereConvex_sphereToWorldPoint;
    -        xi.vsub(worldPoint, worldPointToSphere);
    -
    -        if(penetration < 0 && worldPointToSphere.dot(worldNormal)>0){
    -            // Intersects plane. Now check if the sphere is inside the face polygon
    -            var faceVerts = []; // Face vertices, in world coords
    -            for(var j=0, Nverts=face.length; j!==Nverts; j++){
    -                var worldVertex = v3pool.get();
    -                qj.vmult(verts[face[j]], worldVertex);
    -                xj.vadd(worldVertex,worldVertex);
    -                faceVerts.push(worldVertex);
    -            }
    -
    -            if(pointInPolygon(faceVerts,worldNormal,xi)){ // Is the sphere center in the face polygon?
    -                found = true;
    -                var r = this.createContactEquation(bi,bj,si,sj);
    -
    -                worldNormal.mult(-R, r.ri); // Contact offset, from sphere center to contact
    -                worldNormal.negate(r.ni); // Normal pointing out of sphere
    -
    -                var penetrationVec2 = v3pool.get();
    -                worldNormal.mult(-penetration, penetrationVec2);
    -                var penetrationSpherePoint = v3pool.get();
    -                worldNormal.mult(-R, penetrationSpherePoint);
    -
    -                //xi.vsub(xj).vadd(penetrationSpherePoint).vadd(penetrationVec2 , r.rj);
    -                xi.vsub(xj,r.rj);
    -                r.rj.vadd(penetrationSpherePoint,r.rj);
    -                r.rj.vadd(penetrationVec2 , r.rj);
    -
    -                // Should be relative to the body.
    -                r.rj.vadd(xj, r.rj);
    -                r.rj.vsub(bj.position, r.rj);
    -
    -                // Should be relative to the body.
    -                r.ri.vadd(xi, r.ri);
    -                r.ri.vsub(bi.position, r.ri);
    -
    -                v3pool.release(penetrationVec2);
    -                v3pool.release(penetrationSpherePoint);
    -
    -                this.result.push(r);
    -                this.createFrictionEquationsFromContact(r, this.frictionResult);
    -
    -                // Release world vertices
    -                for(var j=0, Nfaceverts=faceVerts.length; j!==Nfaceverts; j++){
    -                    v3pool.release(faceVerts[j]);
    -                }
    -
    -                return; // We only expect *one* face contact
    -            } else {
    -                // Edge?
    -                for(var j=0; j!==face.length; j++){
    -
    -                    // Get two world transformed vertices
    -                    var v1 = v3pool.get();
    -                    var v2 = v3pool.get();
    -                    qj.vmult(verts[face[(j+1)%face.length]], v1);
    -                    qj.vmult(verts[face[(j+2)%face.length]], v2);
    -                    xj.vadd(v1, v1);
    -                    xj.vadd(v2, v2);
    -
    -                    // Construct edge vector
    -                    var edge = sphereConvex_edge;
    -                    v2.vsub(v1,edge);
    -
    -                    // Construct the same vector, but normalized
    -                    var edgeUnit = sphereConvex_edgeUnit;
    -                    edge.unit(edgeUnit);
    -
    -                    // p is xi projected onto the edge
    -                    var p = v3pool.get();
    -                    var v1_to_xi = v3pool.get();
    -                    xi.vsub(v1, v1_to_xi);
    -                    var dot = v1_to_xi.dot(edgeUnit);
    -                    edgeUnit.mult(dot, p);
    -                    p.vadd(v1, p);
    -
    -                    // Compute a vector from p to the center of the sphere
    -                    var xi_to_p = v3pool.get();
    -                    p.vsub(xi, xi_to_p);
    -
    -                    // Collision if the edge-sphere distance is less than the radius
    -                    // AND if p is in between v1 and v2
    -                    if(dot > 0 && dot*dot<edge.norm2() && xi_to_p.norm2() < R*R){ // Collision if the edge-sphere distance is less than the radius
    -                        // Edge contact!
    -                        var r = this.createContactEquation(bi,bj,si,sj);
    -                        p.vsub(xj,r.rj);
    -
    -                        p.vsub(xi,r.ni);
    -                        r.ni.normalize();
    -
    -                        r.ni.mult(R,r.ri);
    -
    -                        // Should be relative to the body.
    -                        r.rj.vadd(xj, r.rj);
    -                        r.rj.vsub(bj.position, r.rj);
    -
    -                        // Should be relative to the body.
    -                        r.ri.vadd(xi, r.ri);
    -                        r.ri.vsub(bi.position, r.ri);
    -
    -                        this.result.push(r);
    -                        this.createFrictionEquationsFromContact(r, this.frictionResult);
    -
    -                        // Release world vertices
    -                        for(var j=0, Nfaceverts=faceVerts.length; j!==Nfaceverts; j++){
    -                            v3pool.release(faceVerts[j]);
    -                        }
    -
    -                        v3pool.release(v1);
    -                        v3pool.release(v2);
    -                        v3pool.release(p);
    -                        v3pool.release(xi_to_p);
    -                        v3pool.release(v1_to_xi);
    -
    -                        return;
    -                    }
    -
    -                    v3pool.release(v1);
    -                    v3pool.release(v2);
    -                    v3pool.release(p);
    -                    v3pool.release(xi_to_p);
    -                    v3pool.release(v1_to_xi);
    -                }
    -            }
    -
    -            // Release world vertices
    -            for(var j=0, Nfaceverts=faceVerts.length; j!==Nfaceverts; j++){
    -                v3pool.release(faceVerts[j]);
    -            }
    -        }
    -    }
    -};
    -
    -var planeBox_normal = new Vec3();
    -var plane_to_corner = new Vec3();
    -
    -/**
    - * @method planeBox
    - * @param  {Array}      result
    - * @param  {Shape}      si
    - * @param  {Shape}      sj
    - * @param  {Vec3}       xi
    - * @param  {Vec3}       xj
    - * @param  {Quaternion} qi
    - * @param  {Quaternion} qj
    - * @param  {Body}       bi
    - * @param  {Body}       bj
    - */
    -Narrowphase.prototype[Shape.types.PLANE | Shape.types.BOX] =
    -Narrowphase.prototype.planeBox = function(si,sj,xi,xj,qi,qj,bi,bj){
    -    sj.convexPolyhedronRepresentation.material = sj.material;
    -    sj.convexPolyhedronRepresentation.collisionResponse = sj.collisionResponse;
    -    this.planeConvex(si,sj.convexPolyhedronRepresentation,xi,xj,qi,qj,bi,bj);
    -};
    -
    -var planeConvex_v = new Vec3();
    -var planeConvex_normal = new Vec3();
    -var planeConvex_relpos = new Vec3();
    -var planeConvex_projected = new Vec3();
    -
    -/**
    - * @method planeConvex
    - * @param  {Shape}      si
    - * @param  {Shape}      sj
    - * @param  {Vec3}       xi
    - * @param  {Vec3}       xj
    - * @param  {Quaternion} qi
    - * @param  {Quaternion} qj
    - * @param  {Body}       bi
    - * @param  {Body}       bj
    - */
    -Narrowphase.prototype[Shape.types.PLANE | Shape.types.CONVEXPOLYHEDRON] =
    -Narrowphase.prototype.planeConvex = function(
    -    planeShape,
    -    convexShape,
    -    planePosition,
    -    convexPosition,
    -    planeQuat,
    -    convexQuat,
    -    planeBody,
    -    convexBody
    -){
    -    // Simply return the points behind the plane.
    -    var worldVertex = planeConvex_v,
    -        worldNormal = planeConvex_normal;
    -    worldNormal.set(0,0,1);
    -    planeQuat.vmult(worldNormal,worldNormal); // Turn normal according to plane orientation
    -
    -    var numContacts = 0;
    -    var relpos = planeConvex_relpos;
    -    for(var i = 0; i !== convexShape.vertices.length; i++){
    -
    -        // Get world convex vertex
    -        worldVertex.copy(convexShape.vertices[i]);
    -        convexQuat.vmult(worldVertex, worldVertex);
    -        convexPosition.vadd(worldVertex, worldVertex);
    -        worldVertex.vsub(planePosition, relpos);
    -
    -        var dot = worldNormal.dot(relpos);
    -        if(dot <= 0.0){
    -
    -            var r = this.createContactEquation(planeBody, convexBody, planeShape, convexShape);
    -
    -            // Get vertex position projected on plane
    -            var projected = planeConvex_projected;
    -            worldNormal.mult(worldNormal.dot(relpos),projected);
    -            worldVertex.vsub(projected, projected);
    -            projected.vsub(planePosition, r.ri); // From plane to vertex projected on plane
    -
    -            r.ni.copy(worldNormal); // Contact normal is the plane normal out from plane
    -
    -            // rj is now just the vector from the convex center to the vertex
    -            worldVertex.vsub(convexPosition, r.rj);
    -
    -            // Make it relative to the body
    -            r.ri.vadd(planePosition, r.ri);
    -            r.ri.vsub(planeBody.position, r.ri);
    -            r.rj.vadd(convexPosition, r.rj);
    -            r.rj.vsub(convexBody.position, r.rj);
    -
    -            this.result.push(r);
    -            numContacts++;
    -            if(!this.enableFrictionReduction){
    -                this.createFrictionEquationsFromContact(r, this.frictionResult);
    -            }
    -        }
    -    }
    -
    -    if(this.enableFrictionReduction && numContacts){
    -        this.createFrictionFromAverage(numContacts);
    -    }
    -};
    -
    -var convexConvex_sepAxis = new Vec3();
    -var convexConvex_q = new Vec3();
    -
    -/**
    - * @method convexConvex
    - * @param  {Shape}      si
    - * @param  {Shape}      sj
    - * @param  {Vec3}       xi
    - * @param  {Vec3}       xj
    - * @param  {Quaternion} qi
    - * @param  {Quaternion} qj
    - * @param  {Body}       bi
    - * @param  {Body}       bj
    - */
    -Narrowphase.prototype[Shape.types.CONVEXPOLYHEDRON] =
    -Narrowphase.prototype.convexConvex = function(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,faceListA,faceListB){
    -    var sepAxis = convexConvex_sepAxis;
    -
    -    if(xi.distanceTo(xj) > si.boundingSphereRadius + sj.boundingSphereRadius){
    -        return;
    -    }
    -
    -    if(si.findSeparatingAxis(sj,xi,qi,xj,qj,sepAxis,faceListA,faceListB)){
    -        var res = [];
    -        var q = convexConvex_q;
    -        si.clipAgainstHull(xi,qi,sj,xj,qj,sepAxis,-100,100,res);
    -        var numContacts = 0;
    -        for(var j = 0; j !== res.length; j++){
    -            var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj),
    -                ri = r.ri,
    -                rj = r.rj;
    -            sepAxis.negate(r.ni);
    -            res[j].normal.negate(q);
    -            q.mult(res[j].depth, q);
    -            res[j].point.vadd(q, ri);
    -            rj.copy(res[j].point);
    -
    -            // Contact points are in world coordinates. Transform back to relative
    -            ri.vsub(xi,ri);
    -            rj.vsub(xj,rj);
    -
    -            // Make relative to bodies
    -            ri.vadd(xi, ri);
    -            ri.vsub(bi.position, ri);
    -            rj.vadd(xj, rj);
    -            rj.vsub(bj.position, rj);
    -
    -            this.result.push(r);
    -            numContacts++;
    -            if(!this.enableFrictionReduction){
    -                this.createFrictionEquationsFromContact(r, this.frictionResult);
    -            }
    -        }
    -        if(this.enableFrictionReduction && numContacts){
    -            this.createFrictionFromAverage(numContacts);
    -        }
    -    }
    -};
    -
    -
    -/**
    - * @method convexTrimesh
    - * @param  {Array}      result
    - * @param  {Shape}      si
    - * @param  {Shape}      sj
    - * @param  {Vec3}       xi
    - * @param  {Vec3}       xj
    - * @param  {Quaternion} qi
    - * @param  {Quaternion} qj
    - * @param  {Body}       bi
    - * @param  {Body}       bj
    - */
    -// Narrowphase.prototype[Shape.types.CONVEXPOLYHEDRON | Shape.types.TRIMESH] =
    -// Narrowphase.prototype.convexTrimesh = function(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,faceListA,faceListB){
    -//     var sepAxis = convexConvex_sepAxis;
    -
    -//     if(xi.distanceTo(xj) > si.boundingSphereRadius + sj.boundingSphereRadius){
    -//         return;
    -//     }
    -
    -//     // Construct a temp hull for each triangle
    -//     var hullB = new ConvexPolyhedron();
    -
    -//     hullB.faces = [[0,1,2]];
    -//     var va = new Vec3();
    -//     var vb = new Vec3();
    -//     var vc = new Vec3();
    -//     hullB.vertices = [
    -//         va,
    -//         vb,
    -//         vc
    -//     ];
    -
    -//     for (var i = 0; i < sj.indices.length / 3; i++) {
    -
    -//         var triangleNormal = new Vec3();
    -//         sj.getNormal(i, triangleNormal);
    -//         hullB.faceNormals = [triangleNormal];
    -
    -//         sj.getTriangleVertices(i, va, vb, vc);
    -
    -//         var d = si.testSepAxis(triangleNormal, hullB, xi, qi, xj, qj);
    -//         if(!d){
    -//             triangleNormal.scale(-1, triangleNormal);
    -//             d = si.testSepAxis(triangleNormal, hullB, xi, qi, xj, qj);
    -
    -//             if(!d){
    -//                 continue;
    -//             }
    -//         }
    -
    -//         var res = [];
    -//         var q = convexConvex_q;
    -//         si.clipAgainstHull(xi,qi,hullB,xj,qj,triangleNormal,-100,100,res);
    -//         for(var j = 0; j !== res.length; j++){
    -//             var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj),
    -//                 ri = r.ri,
    -//                 rj = r.rj;
    -//             r.ni.copy(triangleNormal);
    -//             r.ni.negate(r.ni);
    -//             res[j].normal.negate(q);
    -//             q.mult(res[j].depth, q);
    -//             res[j].point.vadd(q, ri);
    -//             rj.copy(res[j].point);
    -
    -//             // Contact points are in world coordinates. Transform back to relative
    -//             ri.vsub(xi,ri);
    -//             rj.vsub(xj,rj);
    -
    -//             // Make relative to bodies
    -//             ri.vadd(xi, ri);
    -//             ri.vsub(bi.position, ri);
    -//             rj.vadd(xj, rj);
    -//             rj.vsub(bj.position, rj);
    -
    -//             result.push(r);
    -//         }
    -//     }
    -// };
    -
    -var particlePlane_normal = new Vec3();
    -var particlePlane_relpos = new Vec3();
    -var particlePlane_projected = new Vec3();
    -
    -/**
    - * @method particlePlane
    - * @param  {Array}      result
    - * @param  {Shape}      si
    - * @param  {Shape}      sj
    - * @param  {Vec3}       xi
    - * @param  {Vec3}       xj
    - * @param  {Quaternion} qi
    - * @param  {Quaternion} qj
    - * @param  {Body}       bi
    - * @param  {Body}       bj
    - */
    -Narrowphase.prototype[Shape.types.PLANE | Shape.types.PARTICLE] =
    -Narrowphase.prototype.planeParticle = function(sj,si,xj,xi,qj,qi,bj,bi){
    -    var normal = particlePlane_normal;
    -    normal.set(0,0,1);
    -    bj.quaternion.vmult(normal,normal); // Turn normal according to plane orientation
    -    var relpos = particlePlane_relpos;
    -    xi.vsub(bj.position,relpos);
    -    var dot = normal.dot(relpos);
    -    if(dot <= 0.0){
    -        var r = this.createContactEquation(bi,bj,si,sj);
    -        r.ni.copy(normal); // Contact normal is the plane normal
    -        r.ni.negate(r.ni);
    -        r.ri.set(0,0,0); // Center of particle
    -
    -        // Get particle position projected on plane
    -        var projected = particlePlane_projected;
    -        normal.mult(normal.dot(xi),projected);
    -        xi.vsub(projected,projected);
    -        //projected.vadd(bj.position,projected);
    -
    -        // rj is now the projected world position minus plane position
    -        r.rj.copy(projected);
    -        this.result.push(r);
    -        this.createFrictionEquationsFromContact(r, this.frictionResult);
    -    }
    -};
    -
    -var particleSphere_normal = new Vec3();
    -
    -/**
    - * @method particleSphere
    - * @param  {Array}      result
    - * @param  {Shape}      si
    - * @param  {Shape}      sj
    - * @param  {Vec3}       xi
    - * @param  {Vec3}       xj
    - * @param  {Quaternion} qi
    - * @param  {Quaternion} qj
    - * @param  {Body}       bi
    - * @param  {Body}       bj
    - */
    -Narrowphase.prototype[Shape.types.PARTICLE | Shape.types.SPHERE] =
    -Narrowphase.prototype.sphereParticle = function(sj,si,xj,xi,qj,qi,bj,bi){
    -    // The normal is the unit vector from sphere center to particle center
    -    var normal = particleSphere_normal;
    -    normal.set(0,0,1);
    -    xi.vsub(xj,normal);
    -    var lengthSquared = normal.norm2();
    -
    -    if(lengthSquared <= sj.radius * sj.radius){
    -        var r = this.createContactEquation(bi,bj,si,sj);
    -        normal.normalize();
    -        r.rj.copy(normal);
    -        r.rj.mult(sj.radius,r.rj);
    -        r.ni.copy(normal); // Contact normal
    -        r.ni.negate(r.ni);
    -        r.ri.set(0,0,0); // Center of particle
    -        this.result.push(r);
    -        this.createFrictionEquationsFromContact(r, this.frictionResult);
    -    }
    -};
    -
    -// WIP
    -var cqj = new Quaternion();
    -var convexParticle_local = new Vec3();
    -var convexParticle_normal = new Vec3();
    -var convexParticle_penetratedFaceNormal = new Vec3();
    -var convexParticle_vertexToParticle = new Vec3();
    -var convexParticle_worldPenetrationVec = new Vec3();
    -
    -/**
    - * @method convexParticle
    - * @param  {Array}      result
    - * @param  {Shape}      si
    - * @param  {Shape}      sj
    - * @param  {Vec3}       xi
    - * @param  {Vec3}       xj
    - * @param  {Quaternion} qi
    - * @param  {Quaternion} qj
    - * @param  {Body}       bi
    - * @param  {Body}       bj
    - */
    -Narrowphase.prototype[Shape.types.PARTICLE | Shape.types.CONVEXPOLYHEDRON] =
    -Narrowphase.prototype.convexParticle = function(sj,si,xj,xi,qj,qi,bj,bi){
    -    var penetratedFaceIndex = -1;
    -    var penetratedFaceNormal = convexParticle_penetratedFaceNormal;
    -    var worldPenetrationVec = convexParticle_worldPenetrationVec;
    -    var minPenetration = null;
    -    var numDetectedFaces = 0;
    -
    -    // Convert particle position xi to local coords in the convex
    -    var local = convexParticle_local;
    -    local.copy(xi);
    -    local.vsub(xj,local); // Convert position to relative the convex origin
    -    qj.conjugate(cqj);
    -    cqj.vmult(local,local);
    -
    -    if(sj.pointIsInside(local)){
    -
    -        if(sj.worldVerticesNeedsUpdate){
    -            sj.computeWorldVertices(xj,qj);
    -        }
    -        if(sj.worldFaceNormalsNeedsUpdate){
    -            sj.computeWorldFaceNormals(qj);
    -        }
    -
    -        // For each world polygon in the polyhedra
    -        for(var i=0,nfaces=sj.faces.length; i!==nfaces; i++){
    -
    -            // Construct world face vertices
    -            var verts = [ sj.worldVertices[ sj.faces[i][0] ] ];
    -            var normal = sj.worldFaceNormals[i];
    -
    -            // Check how much the particle penetrates the polygon plane.
    -            xi.vsub(verts[0],convexParticle_vertexToParticle);
    -            var penetration = -normal.dot(convexParticle_vertexToParticle);
    -            if(minPenetration===null || Math.abs(penetration)<Math.abs(minPenetration)){
    -                minPenetration = penetration;
    -                penetratedFaceIndex = i;
    -                penetratedFaceNormal.copy(normal);
    -                numDetectedFaces++;
    -            }
    -        }
    -
    -        if(penetratedFaceIndex!==-1){
    -            // Setup contact
    -            var r = this.createContactEquation(bi,bj,si,sj);
    -            penetratedFaceNormal.mult(minPenetration, worldPenetrationVec);
    -
    -            // rj is the particle position projected to the face
    -            worldPenetrationVec.vadd(xi,worldPenetrationVec);
    -            worldPenetrationVec.vsub(xj,worldPenetrationVec);
    -            r.rj.copy(worldPenetrationVec);
    -            //var projectedToFace = xi.vsub(xj).vadd(worldPenetrationVec);
    -            //projectedToFace.copy(r.rj);
    -
    -            //qj.vmult(r.rj,r.rj);
    -            penetratedFaceNormal.negate( r.ni ); // Contact normal
    -            r.ri.set(0,0,0); // Center of particle
    -
    -            var ri = r.ri,
    -                rj = r.rj;
    -
    -            // Make relative to bodies
    -            ri.vadd(xi, ri);
    -            ri.vsub(bi.position, ri);
    -            rj.vadd(xj, rj);
    -            rj.vsub(bj.position, rj);
    -
    -            this.result.push(r);
    -            this.createFrictionEquationsFromContact(r, this.frictionResult);
    -        } else {
    -            console.warn("Point found inside convex, but did not find penetrating face!");
    -        }
    -    }
    -};
    -
    -Narrowphase.prototype[Shape.types.BOX | Shape.types.HEIGHTFIELD] =
    -Narrowphase.prototype.boxHeightfield = function (si,sj,xi,xj,qi,qj,bi,bj){
    -    si.convexPolyhedronRepresentation.material = si.material;
    -    si.convexPolyhedronRepresentation.collisionResponse = si.collisionResponse;
    -    this.convexHeightfield(si.convexPolyhedronRepresentation,sj,xi,xj,qi,qj,bi,bj);
    -};
    -
    -var convexHeightfield_tmp1 = new Vec3();
    -var convexHeightfield_tmp2 = new Vec3();
    -var convexHeightfield_faceList = [0];
    -
    -/**
    - * @method convexHeightfield
    - */
    -Narrowphase.prototype[Shape.types.CONVEXPOLYHEDRON | Shape.types.HEIGHTFIELD] =
    -Narrowphase.prototype.convexHeightfield = function (
    -    convexShape,
    -    hfShape,
    -    convexPos,
    -    hfPos,
    -    convexQuat,
    -    hfQuat,
    -    convexBody,
    -    hfBody
    -){
    -    var data = hfShape.data,
    -        w = hfShape.elementSize,
    -        radius = convexShape.boundingSphereRadius,
    -        worldPillarOffset = convexHeightfield_tmp2,
    -        faceList = convexHeightfield_faceList;
    -
    -    // Get sphere position to heightfield local!
    -    var localConvexPos = convexHeightfield_tmp1;
    -    Transform.pointToLocalFrame(hfPos, hfQuat, convexPos, localConvexPos);
    -
    -    // Get the index of the data points to test against
    -    var iMinX = Math.floor((localConvexPos.x - radius) / w) - 1,
    -        iMaxX = Math.ceil((localConvexPos.x + radius) / w) + 1,
    -        iMinY = Math.floor((localConvexPos.y - radius) / w) - 1,
    -        iMaxY = Math.ceil((localConvexPos.y + radius) / w) + 1;
    -
    -    // Bail out if we are out of the terrain
    -    if(iMaxX < 0 || iMaxY < 0 || iMinX > data.length || iMinY > data[0].length){
    -        return;
    -    }
    -
    -    // Clamp index to edges
    -    if(iMinX < 0){ iMinX = 0; }
    -    if(iMaxX < 0){ iMaxX = 0; }
    -    if(iMinY < 0){ iMinY = 0; }
    -    if(iMaxY < 0){ iMaxY = 0; }
    -    if(iMinX >= data.length){ iMinX = data.length - 1; }
    -    if(iMaxX >= data.length){ iMaxX = data.length - 1; }
    -    if(iMaxY >= data[0].length){ iMaxY = data[0].length - 1; }
    -    if(iMinY >= data[0].length){ iMinY = data[0].length - 1; }
    -
    -    var minMax = [];
    -    hfShape.getRectMinMax(iMinX, iMinY, iMaxX, iMaxY, minMax);
    -    var min = minMax[0];
    -    var max = minMax[1];
    -
    -    // Bail out if we're cant touch the bounding height box
    -    if(localConvexPos.z - radius > max || localConvexPos.z + radius < min){
    -        return;
    -    }
    -
    -    for(var i = iMinX; i < iMaxX; i++){
    -        for(var j = iMinY; j < iMaxY; j++){
    -
    -            // Lower triangle
    -            hfShape.getConvexTrianglePillar(i, j, false);
    -            Transform.pointToWorldFrame(hfPos, hfQuat, hfShape.pillarOffset, worldPillarOffset);
    -            if (convexPos.distanceTo(worldPillarOffset) < hfShape.pillarConvex.boundingSphereRadius + convexShape.boundingSphereRadius) {
    -                this.convexConvex(convexShape, hfShape.pillarConvex, convexPos, worldPillarOffset, convexQuat, hfQuat, convexBody, hfBody, null, null, faceList, null);
    -            }
    -
    -            // Upper triangle
    -            hfShape.getConvexTrianglePillar(i, j, true);
    -            Transform.pointToWorldFrame(hfPos, hfQuat, hfShape.pillarOffset, worldPillarOffset);
    -            if (convexPos.distanceTo(worldPillarOffset) < hfShape.pillarConvex.boundingSphereRadius + convexShape.boundingSphereRadius) {
    -                this.convexConvex(convexShape, hfShape.pillarConvex, convexPos, worldPillarOffset, convexQuat, hfQuat, convexBody, hfBody, null, null, faceList, null);
    -            }
    -        }
    -    }
    -};
    -
    -var sphereHeightfield_tmp1 = new Vec3();
    -var sphereHeightfield_tmp2 = new Vec3();
    -
    -/**
    - * @method sphereHeightfield
    - */
    -Narrowphase.prototype[Shape.types.SPHERE | Shape.types.HEIGHTFIELD] =
    -Narrowphase.prototype.sphereHeightfield = function (
    -    sphereShape,
    -    hfShape,
    -    spherePos,
    -    hfPos,
    -    sphereQuat,
    -    hfQuat,
    -    sphereBody,
    -    hfBody
    -){
    -    var data = hfShape.data,
    -        radius = sphereShape.radius,
    -        w = hfShape.elementSize,
    -        worldPillarOffset = sphereHeightfield_tmp2;
    -
    -    // Get sphere position to heightfield local!
    -    var localSpherePos = sphereHeightfield_tmp1;
    -    Transform.pointToLocalFrame(hfPos, hfQuat, spherePos, localSpherePos);
    -
    -    // Get the index of the data points to test against
    -    var iMinX = Math.floor((localSpherePos.x - radius) / w) - 1,
    -        iMaxX = Math.ceil((localSpherePos.x + radius) / w) + 1,
    -        iMinY = Math.floor((localSpherePos.y - radius) / w) - 1,
    -        iMaxY = Math.ceil((localSpherePos.y + radius) / w) + 1;
    -
    -    // Bail out if we are out of the terrain
    -    if(iMaxX < 0 || iMaxY < 0 || iMinX > data.length || iMaxY > data[0].length){
    -        return;
    -    }
    -
    -    // Clamp index to edges
    -    if(iMinX < 0){ iMinX = 0; }
    -    if(iMaxX < 0){ iMaxX = 0; }
    -    if(iMinY < 0){ iMinY = 0; }
    -    if(iMaxY < 0){ iMaxY = 0; }
    -    if(iMinX >= data.length){ iMinX = data.length - 1; }
    -    if(iMaxX >= data.length){ iMaxX = data.length - 1; }
    -    if(iMaxY >= data[0].length){ iMaxY = data[0].length - 1; }
    -    if(iMinY >= data[0].length){ iMinY = data[0].length - 1; }
    -
    -    var minMax = [];
    -    hfShape.getRectMinMax(iMinX, iMinY, iMaxX, iMaxY, minMax);
    -    var min = minMax[0];
    -    var max = minMax[1];
    -
    -    // Bail out if we're cant touch the bounding height box
    -    if(localSpherePos.z - radius > max || localSpherePos.z + radius < min){
    -        return;
    -    }
    -
    -    var result = this.result;
    -    for(var i = iMinX; i < iMaxX; i++){
    -        for(var j = iMinY; j < iMaxY; j++){
    -
    -            var numContactsBefore = result.length;
    -
    -            // Lower triangle
    -            hfShape.getConvexTrianglePillar(i, j, false);
    -            Transform.pointToWorldFrame(hfPos, hfQuat, hfShape.pillarOffset, worldPillarOffset);
    -            if (spherePos.distanceTo(worldPillarOffset) < hfShape.pillarConvex.boundingSphereRadius + sphereShape.boundingSphereRadius) {
    -                this.sphereConvex(sphereShape, hfShape.pillarConvex, spherePos, worldPillarOffset, sphereQuat, hfQuat, sphereBody, hfBody);
    -            }
    -
    -            // Upper triangle
    -            hfShape.getConvexTrianglePillar(i, j, true);
    -            Transform.pointToWorldFrame(hfPos, hfQuat, hfShape.pillarOffset, worldPillarOffset);
    -            if (spherePos.distanceTo(worldPillarOffset) < hfShape.pillarConvex.boundingSphereRadius + sphereShape.boundingSphereRadius) {
    -                this.sphereConvex(sphereShape, hfShape.pillarConvex, spherePos, worldPillarOffset, sphereQuat, hfQuat, sphereBody, hfBody);
    -            }
    -
    -            var numContacts = result.length - numContactsBefore;
    -
    -            if(numContacts > 2){
    -                return;
    -            }
    -            /*
    -            // Skip all but 1
    -            for (var k = 0; k < numContacts - 1; k++) {
    -                result.pop();
    -            }
    -            */
    -        }
    -    }
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/files/src_world_World.js.html b/docs/files/src_world_World.js.html deleted file mode 100644 index fc2b4bdc9..000000000 --- a/docs/files/src_world_World.js.html +++ /dev/null @@ -1,1105 +0,0 @@ - - - - - src/world/World.js - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: src/world/World.js

    - -
    -
    -/* global performance */
    -
    -module.exports = World;
    -
    -var Shape = require('../shapes/Shape');
    -var Vec3 = require('../math/Vec3');
    -var Quaternion = require('../math/Quaternion');
    -var GSSolver = require('../solver/GSSolver');
    -var Vec3Pool = require('../utils/Vec3Pool');
    -var ContactEquation = require('../equations/ContactEquation');
    -var FrictionEquation = require('../equations/FrictionEquation');
    -var Narrowphase = require('./Narrowphase');
    -var EventTarget = require('../utils/EventTarget');
    -var ArrayCollisionMatrix = require('../collision/ArrayCollisionMatrix');
    -var Material = require('../material/Material');
    -var ContactMaterial = require('../material/ContactMaterial');
    -var Body = require('../objects/Body');
    -var TupleDictionary = require('../utils/TupleDictionary');
    -var RaycastResult = require('../collision/RaycastResult');
    -var AABB = require('../collision/AABB');
    -var Ray = require('../collision/Ray');
    -var NaiveBroadphase = require('../collision/NaiveBroadphase');
    -
    -/**
    - * The physics world
    - * @class World
    - * @constructor
    - * @extends EventTarget
    - */
    -function World(){
    -    EventTarget.apply(this);
    -
    -    /**
    -     * Currently / last used timestep. Is set to -1 if not available. This value is updated before each internal step, which means that it is "fresh" inside event callbacks.
    -     * @property {Number} dt
    -     */
    -    this.dt = -1;
    -
    -    /**
    -     * Makes bodies go to sleep when they've been inactive
    -     * @property allowSleep
    -     * @type {Boolean}
    -     */
    -    this.allowSleep = false;
    -
    -    /**
    -     * All the current contacts (instances of ContactEquation) in the world.
    -     * @property contacts
    -     * @type {Array}
    -     */
    -    this.contacts = [];
    -    this.frictionEquations = [];
    -
    -    /**
    -     * How often to normalize quaternions. Set to 0 for every step, 1 for every second etc.. A larger value increases performance. If bodies tend to explode, set to a smaller value (zero to be sure nothing can go wrong).
    -     * @property quatNormalizeSkip
    -     * @type {Number}
    -     */
    -    this.quatNormalizeSkip = 0;
    -
    -    /**
    -     * Set to true to use fast quaternion normalization. It is often enough accurate to use. If bodies tend to explode, set to false.
    -     * @property quatNormalizeFast
    -     * @type {Boolean}
    -     * @see Quaternion.normalizeFast
    -     * @see Quaternion.normalize
    -     */
    -    this.quatNormalizeFast = false;
    -
    -    /**
    -     * The wall-clock time since simulation start
    -     * @property time
    -     * @type {Number}
    -     */
    -    this.time = 0.0;
    -
    -    /**
    -     * Number of timesteps taken since start
    -     * @property stepnumber
    -     * @type {Number}
    -     */
    -    this.stepnumber = 0;
    -
    -    /// Default and last timestep sizes
    -    this.default_dt = 1/60;
    -
    -    this.nextId = 0;
    -    /**
    -     * @property gravity
    -     * @type {Vec3}
    -     */
    -    this.gravity = new Vec3();
    -
    -    /**
    -     * @property broadphase
    -     * @type {Broadphase}
    -     */
    -    this.broadphase = new NaiveBroadphase();
    -
    -    /**
    -     * @property bodies
    -     * @type {Array}
    -     */
    -    this.bodies = [];
    -
    -    /**
    -     * @property solver
    -     * @type {Solver}
    -     */
    -    this.solver = new GSSolver();
    -
    -    /**
    -     * @property constraints
    -     * @type {Array}
    -     */
    -    this.constraints = [];
    -
    -    /**
    -     * @property narrowphase
    -     * @type {Narrowphase}
    -     */
    -    this.narrowphase = new Narrowphase(this);
    -
    -    /**
    -     * @property {ArrayCollisionMatrix} collisionMatrix
    -	 * @type {ArrayCollisionMatrix}
    -	 */
    -	this.collisionMatrix = new ArrayCollisionMatrix();
    -
    -    /**
    -     * CollisionMatrix from the previous step.
    -     * @property {ArrayCollisionMatrix} collisionMatrixPrevious
    -	 * @type {ArrayCollisionMatrix}
    -	 */
    -	this.collisionMatrixPrevious = new ArrayCollisionMatrix();
    -
    -    /**
    -     * All added materials
    -     * @property materials
    -     * @type {Array}
    -     */
    -    this.materials = [];
    -
    -    /**
    -     * @property contactmaterials
    -     * @type {Array}
    -     */
    -    this.contactmaterials = [];
    -
    -    /**
    -     * Used to look up a ContactMaterial given two instances of Material.
    -     * @property {TupleDictionary} contactMaterialTable
    -     */
    -    this.contactMaterialTable = new TupleDictionary();
    -
    -    this.defaultMaterial = new Material("default");
    -
    -    /**
    -     * This contact material is used if no suitable contactmaterial is found for a contact.
    -     * @property defaultContactMaterial
    -     * @type {ContactMaterial}
    -     */
    -    this.defaultContactMaterial = new ContactMaterial(this.defaultMaterial, this.defaultMaterial, { friction: 0.3, restitution: 0.0 });
    -
    -    /**
    -     * @property doProfiling
    -     * @type {Boolean}
    -     */
    -    this.doProfiling = false;
    -
    -    /**
    -     * @property profile
    -     * @type {Object}
    -     */
    -    this.profile = {
    -        solve:0,
    -        makeContactConstraints:0,
    -        broadphase:0,
    -        integrate:0,
    -        narrowphase:0,
    -    };
    -
    -    /**
    -     * @property subsystems
    -     * @type {Array}
    -     */
    -    this.subsystems = [];
    -
    -    this.addBodyEvent = {
    -        type:"addBody",
    -        body : null,
    -    };
    -
    -    this.removeBodyEvent = {
    -        type:"removeBody",
    -        body : null,
    -    };
    -}
    -World.prototype = new EventTarget();
    -
    -// Temp stuff
    -var tmpAABB1 = new AABB();
    -var tmpArray1 = [];
    -var tmpRay = new Ray();
    -
    -/**
    - * Get the contact material between materials m1 and m2
    - * @method getContactMaterial
    - * @param {Material} m1
    - * @param {Material} m2
    - * @return {ContactMaterial} The contact material if it was found.
    - */
    -World.prototype.getContactMaterial = function(m1,m2){
    -    return this.contactMaterialTable.get(m1.id,m2.id); //this.contactmaterials[this.mats2cmat[i+j*this.materials.length]];
    -};
    -
    -/**
    - * Get number of objects in the world.
    - * @method numObjects
    - * @return {Number}
    - * @deprecated
    - */
    -World.prototype.numObjects = function(){
    -    return this.bodies.length;
    -};
    -
    -/**
    - * Store old collision state info
    - * @method collisionMatrixTick
    - */
    -World.prototype.collisionMatrixTick = function(){
    -	var temp = this.collisionMatrixPrevious;
    -	this.collisionMatrixPrevious = this.collisionMatrix;
    -	this.collisionMatrix = temp;
    -	this.collisionMatrix.reset();
    -};
    -
    -/**
    - * Add a rigid body to the simulation.
    - * @method add
    - * @param {Body} body
    - * @todo If the simulation has not yet started, why recrete and copy arrays for each body? Accumulate in dynamic arrays in this case.
    - * @todo Adding an array of bodies should be possible. This would save some loops too
    - * @deprecated Use .addBody instead
    - */
    -World.prototype.add = World.prototype.addBody = function(body){
    -    if(this.bodies.indexOf(body) !== -1){
    -        return;
    -    }
    -    body.index = this.bodies.length;
    -    this.bodies.push(body);
    -    body.world = this;
    -    body.initPosition.copy(body.position);
    -    body.initVelocity.copy(body.velocity);
    -    body.timeLastSleepy = this.time;
    -    if(body instanceof Body){
    -        body.initAngularVelocity.copy(body.angularVelocity);
    -        body.initQuaternion.copy(body.quaternion);
    -    }
    -	this.collisionMatrix.setNumObjects(this.bodies.length);
    -    this.addBodyEvent.body = body;
    -    this.dispatchEvent(this.addBodyEvent);
    -};
    -
    -/**
    - * Add a constraint to the simulation.
    - * @method addConstraint
    - * @param {Constraint} c
    - */
    -World.prototype.addConstraint = function(c){
    -    this.constraints.push(c);
    -};
    -
    -/**
    - * Removes a constraint
    - * @method removeConstraint
    - * @param {Constraint} c
    - */
    -World.prototype.removeConstraint = function(c){
    -    var idx = this.constraints.indexOf(c);
    -    if(idx!==-1){
    -        this.constraints.splice(idx,1);
    -    }
    -};
    -
    -/**
    - * Raycast test
    - * @method rayTest
    - * @param {Vec3} from
    - * @param {Vec3} to
    - * @param {Function|RaycastResult} result
    - * @deprecated Use .raycastAll, .raycastClosest or .raycastAny instead.
    - */
    -World.prototype.rayTest = function(from, to, result){
    -    if(result instanceof RaycastResult){
    -        // Do raycastclosest
    -        this.raycastClosest(from, to, {
    -            skipBackfaces: true
    -        }, result);
    -    } else {
    -        // Do raycastAll
    -        this.raycastAll(from, to, {
    -            skipBackfaces: true
    -        }, result);
    -    }
    -};
    -
    -/**
    - * Ray cast against all bodies. The provided callback will be executed for each hit with a RaycastResult as single argument.
    - * @method raycastAll
    - * @param  {Vec3} from
    - * @param  {Vec3} to
    - * @param  {Object} options
    - * @param  {number} [options.collisionFilterMask=-1]
    - * @param  {number} [options.collisionFilterGroup=-1]
    - * @param  {boolean} [options.skipBackfaces=false]
    - * @param  {boolean} [options.checkCollisionResponse=true]
    - * @param  {Function} callback
    - * @return {boolean} True if any body was hit.
    - */
    -World.prototype.raycastAll = function(from, to, options, callback){
    -    options.mode = Ray.ALL;
    -    options.from = from;
    -    options.to = to;
    -    options.callback = callback;
    -    return tmpRay.intersectWorld(this, options);
    -};
    -
    -/**
    - * Ray cast, and stop at the first result. Note that the order is random - but the method is fast.
    - * @method raycastAny
    - * @param  {Vec3} from
    - * @param  {Vec3} to
    - * @param  {Object} options
    - * @param  {number} [options.collisionFilterMask=-1]
    - * @param  {number} [options.collisionFilterGroup=-1]
    - * @param  {boolean} [options.skipBackfaces=false]
    - * @param  {boolean} [options.checkCollisionResponse=true]
    - * @param  {RaycastResult} result
    - * @return {boolean} True if any body was hit.
    - */
    -World.prototype.raycastAny = function(from, to, options, result){
    -    options.mode = Ray.ANY;
    -    options.from = from;
    -    options.to = to;
    -    options.result = result;
    -    return tmpRay.intersectWorld(this, options);
    -};
    -
    -/**
    - * Ray cast, and return information of the closest hit.
    - * @method raycastClosest
    - * @param  {Vec3} from
    - * @param  {Vec3} to
    - * @param  {Object} options
    - * @param  {number} [options.collisionFilterMask=-1]
    - * @param  {number} [options.collisionFilterGroup=-1]
    - * @param  {boolean} [options.skipBackfaces=false]
    - * @param  {boolean} [options.checkCollisionResponse=true]
    - * @param  {RaycastResult} result
    - * @return {boolean} True if any body was hit.
    - */
    -World.prototype.raycastClosest = function(from, to, options, result){
    -    options.mode = Ray.CLOSEST;
    -    options.from = from;
    -    options.to = to;
    -    options.result = result;
    -    return tmpRay.intersectWorld(this, options);
    -};
    -
    -/**
    - * Remove a rigid body from the simulation.
    - * @method remove
    - * @param {Body} body
    - * @deprecated Use .removeBody instead
    - */
    -World.prototype.remove = function(body){
    -    body.world = null;
    -    var n = this.bodies.length-1,
    -        bodies = this.bodies,
    -        idx = bodies.indexOf(body);
    -    if(idx !== -1){
    -        bodies.splice(idx, 1); // Todo: should use a garbage free method
    -
    -        // Recompute index
    -        for(var i=0; i!==bodies.length; i++){
    -            bodies[i].index = i;
    -        }
    -
    -        this.collisionMatrix.setNumObjects(n);
    -        this.removeBodyEvent.body = body;
    -        this.dispatchEvent(this.removeBodyEvent);
    -    }
    -};
    -
    -/**
    - * Remove a rigid body from the simulation.
    - * @method removeBody
    - * @param {Body} body
    - */
    -World.prototype.removeBody = World.prototype.remove;
    -
    -/**
    - * Adds a material to the World.
    - * @method addMaterial
    - * @param {Material} m
    - * @todo Necessary?
    - */
    -World.prototype.addMaterial = function(m){
    -    this.materials.push(m);
    -};
    -
    -/**
    - * Adds a contact material to the World
    - * @method addContactMaterial
    - * @param {ContactMaterial} cmat
    - */
    -World.prototype.addContactMaterial = function(cmat) {
    -
    -    // Add contact material
    -    this.contactmaterials.push(cmat);
    -
    -    // Add current contact material to the material table
    -    this.contactMaterialTable.set(cmat.materials[0].id,cmat.materials[1].id,cmat);
    -};
    -
    -// performance.now()
    -if(typeof performance === 'undefined'){
    -    performance = {};
    -}
    -if(!performance.now){
    -    var nowOffset = Date.now();
    -    if (performance.timing && performance.timing.navigationStart){
    -        nowOffset = performance.timing.navigationStart;
    -    }
    -    performance.now = function(){
    -        return Date.now() - nowOffset;
    -    };
    -}
    -
    -var step_tmp1 = new Vec3();
    -
    -/**
    - * Step the physics world forward in time.
    - *
    - * There are two modes. The simple mode is fixed timestepping without interpolation. In this case you only use the first argument. The second case uses interpolation. In that you also provide the time since the function was last used, as well as the maximum fixed timesteps to take.
    - *
    - * @method step
    - * @param {Number} dt                       The fixed time step size to use.
    - * @param {Number} [timeSinceLastCalled]    The time elapsed since the function was last called.
    - * @param {Number} [maxSubSteps=10]         Maximum number of fixed steps to take per function call.
    - *
    - * @example
    - *     // fixed timestepping without interpolation
    - *     world.step(1/60);
    - *
    - * @see http://bulletphysics.org/mediawiki-1.5.8/index.php/Stepping_The_World
    - */
    -World.prototype.step = function(dt, timeSinceLastCalled, maxSubSteps){
    -    maxSubSteps = maxSubSteps || 10;
    -    timeSinceLastCalled = timeSinceLastCalled || 0;
    -
    -    if(timeSinceLastCalled === 0){ // Fixed, simple stepping
    -
    -        this.internalStep(dt);
    -
    -        // Increment time
    -        this.time += dt;
    -
    -    } else {
    -
    -        // Compute the number of fixed steps we should have taken since the last step
    -        var internalSteps = Math.floor((this.time + timeSinceLastCalled) / dt) - Math.floor(this.time / dt);
    -        internalSteps = Math.min(internalSteps,maxSubSteps);
    -
    -        // Do some fixed steps to catch up
    -        var t0 = performance.now();
    -        for(var i=0; i!==internalSteps; i++){
    -            this.internalStep(dt);
    -            if(performance.now() - t0 > dt * 1000){
    -                // We are slower than real-time. Better bail out.
    -                break;
    -            }
    -        }
    -
    -        // Increment internal clock
    -        this.time += timeSinceLastCalled;
    -
    -        // Compute "Left over" time step
    -        var h = this.time % dt;
    -        var h_div_dt = h / dt;
    -        var interpvelo = step_tmp1;
    -        var bodies = this.bodies;
    -
    -        for(var j=0; j !== bodies.length; j++){
    -            var b = bodies[j];
    -            if(b.type !== Body.STATIC && b.sleepState !== Body.SLEEPING){
    -
    -                // Interpolate
    -                b.position.vsub(b.previousPosition, interpvelo);
    -                interpvelo.scale(h_div_dt, interpvelo);
    -                b.position.vadd(interpvelo, b.interpolatedPosition);
    -
    -                // TODO: interpolate quaternion
    -                // b.interpolatedAngle = b.angle + (b.angle - b.previousAngle) * h_div_dt;
    -
    -            } else {
    -
    -                // For static bodies, just copy. Who else will do it?
    -                b.interpolatedPosition.copy(b.position);
    -                b.interpolatedQuaternion.copy(b.quaternion);
    -            }
    -        }
    -    }
    -};
    -
    -/**
    - * Step the simulation
    - * @method step
    - * @param {Number} dt
    - */
    -var World_step_postStepEvent = {type:"postStep"}, // Reusable event objects to save memory
    -    World_step_preStepEvent = {type:"preStep"},
    -    World_step_collideEvent = {type:"collide", body:null, contact:null },
    -    World_step_oldContacts = [], // Pools for unused objects
    -    World_step_frictionEquationPool = [],
    -    World_step_p1 = [], // Reusable arrays for collision pairs
    -    World_step_p2 = [],
    -    World_step_gvec = new Vec3(), // Temporary vectors and quats
    -    World_step_vi = new Vec3(),
    -    World_step_vj = new Vec3(),
    -    World_step_wi = new Vec3(),
    -    World_step_wj = new Vec3(),
    -    World_step_t1 = new Vec3(),
    -    World_step_t2 = new Vec3(),
    -    World_step_rixn = new Vec3(),
    -    World_step_rjxn = new Vec3(),
    -    World_step_step_q = new Quaternion(),
    -    World_step_step_w = new Quaternion(),
    -    World_step_step_wq = new Quaternion(),
    -    invI_tau_dt = new Vec3();
    -World.prototype.internalStep = function(dt){
    -    this.dt = dt;
    -
    -    var world = this,
    -        that = this,
    -        contacts = this.contacts,
    -        p1 = World_step_p1,
    -        p2 = World_step_p2,
    -        N = this.numObjects(),
    -        bodies = this.bodies,
    -        solver = this.solver,
    -        gravity = this.gravity,
    -        doProfiling = this.doProfiling,
    -        profile = this.profile,
    -        DYNAMIC = Body.DYNAMIC,
    -        profilingStart,
    -        constraints = this.constraints,
    -        frictionEquationPool = World_step_frictionEquationPool,
    -        gnorm = gravity.norm(),
    -        gx = gravity.x,
    -        gy = gravity.y,
    -        gz = gravity.z,
    -        i=0;
    -
    -    if(doProfiling){
    -        profilingStart = performance.now();
    -    }
    -
    -    // Add gravity to all objects
    -    for(i=0; i!==N; i++){
    -        var bi = bodies[i];
    -        if(bi.type & DYNAMIC){ // Only for dynamic bodies
    -            var f = bi.force, m = bi.mass;
    -            f.x += m*gx;
    -            f.y += m*gy;
    -            f.z += m*gz;
    -        }
    -    }
    -
    -    // Update subsystems
    -    for(var i=0, Nsubsystems=this.subsystems.length; i!==Nsubsystems; i++){
    -        this.subsystems[i].update();
    -    }
    -
    -    // Collision detection
    -    if(doProfiling){ profilingStart = performance.now(); }
    -    p1.length = 0; // Clean up pair arrays from last step
    -    p2.length = 0;
    -    this.broadphase.collisionPairs(this,p1,p2);
    -    if(doProfiling){ profile.broadphase = performance.now() - profilingStart; }
    -
    -    // Remove constrained pairs with collideConnected == false
    -    var Nconstraints = constraints.length;
    -    for(i=0; i!==Nconstraints; i++){
    -        var c = constraints[i];
    -        if(!c.collideConnected){
    -            for(var j = p1.length-1; j>=0; j-=1){
    -                if( (c.bodyA === p1[j] && c.bodyB === p2[j]) ||
    -                    (c.bodyB === p1[j] && c.bodyA === p2[j])){
    -                    p1.splice(j, 1);
    -                    p2.splice(j, 1);
    -                }
    -            }
    -        }
    -    }
    -
    -    this.collisionMatrixTick();
    -
    -    // Generate contacts
    -    if(doProfiling){ profilingStart = performance.now(); }
    -    var oldcontacts = World_step_oldContacts;
    -    var NoldContacts = contacts.length;
    -
    -    for(i=0; i!==NoldContacts; i++){
    -        oldcontacts.push(contacts[i]);
    -    }
    -    contacts.length = 0;
    -
    -    // Transfer FrictionEquation from current list to the pool for reuse
    -    var NoldFrictionEquations = this.frictionEquations.length;
    -    for(i=0; i!==NoldFrictionEquations; i++){
    -        frictionEquationPool.push(this.frictionEquations[i]);
    -    }
    -    this.frictionEquations.length = 0;
    -
    -    this.narrowphase.getContacts(
    -        p1,
    -        p2,
    -        this,
    -        contacts,
    -        oldcontacts, // To be reused
    -        this.frictionEquations,
    -        frictionEquationPool
    -    );
    -
    -    if(doProfiling){
    -        profile.narrowphase = performance.now() - profilingStart;
    -    }
    -
    -    // Loop over all collisions
    -    if(doProfiling){
    -        profilingStart = performance.now();
    -    }
    -
    -    // Add all friction eqs
    -    for (var i = 0; i < this.frictionEquations.length; i++) {
    -        solver.addEquation(this.frictionEquations[i]);
    -    }
    -
    -    var ncontacts = contacts.length;
    -    for(var k=0; k!==ncontacts; k++){
    -
    -        // Current contact
    -        var c = contacts[k];
    -
    -        // Get current collision indeces
    -        var bi = c.bi,
    -            bj = c.bj,
    -            si = c.si,
    -            sj = c.sj;
    -
    -        // Get collision properties
    -        var cm;
    -        if(bi.material && bj.material){
    -            cm = this.getContactMaterial(bi.material,bj.material) || this.defaultContactMaterial;
    -        } else {
    -            cm = this.defaultContactMaterial;
    -        }
    -
    -        // c.enabled = bi.collisionResponse && bj.collisionResponse && si.collisionResponse && sj.collisionResponse;
    -
    -        var mu = cm.friction;
    -        // c.restitution = cm.restitution;
    -
    -        // If friction or restitution were specified in the material, use them
    -        if(bi.material && bj.material){
    -            if(bi.material.friction >= 0 && bj.material.friction >= 0){
    -                mu = bi.material.friction * bj.material.friction;
    -            }
    -
    -            if(bi.material.restitution >= 0 && bj.material.restitution >= 0){
    -                c.restitution = bi.material.restitution * bj.material.restitution;
    -            }
    -        }
    -
    -		// c.setSpookParams(
    -  //           cm.contactEquationStiffness,
    -  //           cm.contactEquationRelaxation,
    -  //           dt
    -  //       );
    -
    -		solver.addEquation(c);
    -
    -		// // Add friction constraint equation
    -		// if(mu > 0){
    -
    -		// 	// Create 2 tangent equations
    -		// 	var mug = mu * gnorm;
    -		// 	var reducedMass = (bi.invMass + bj.invMass);
    -		// 	if(reducedMass > 0){
    -		// 		reducedMass = 1/reducedMass;
    -		// 	}
    -		// 	var pool = frictionEquationPool;
    -		// 	var c1 = pool.length ? pool.pop() : new FrictionEquation(bi,bj,mug*reducedMass);
    -		// 	var c2 = pool.length ? pool.pop() : new FrictionEquation(bi,bj,mug*reducedMass);
    -		// 	this.frictionEquations.push(c1, c2);
    -
    -		// 	c1.bi = c2.bi = bi;
    -		// 	c1.bj = c2.bj = bj;
    -		// 	c1.minForce = c2.minForce = -mug*reducedMass;
    -		// 	c1.maxForce = c2.maxForce = mug*reducedMass;
    -
    -		// 	// Copy over the relative vectors
    -		// 	c1.ri.copy(c.ri);
    -		// 	c1.rj.copy(c.rj);
    -		// 	c2.ri.copy(c.ri);
    -		// 	c2.rj.copy(c.rj);
    -
    -		// 	// Construct tangents
    -		// 	c.ni.tangents(c1.t, c2.t);
    -
    -  //           // Set spook params
    -  //           c1.setSpookParams(cm.frictionEquationStiffness, cm.frictionEquationRelaxation, dt);
    -  //           c2.setSpookParams(cm.frictionEquationStiffness, cm.frictionEquationRelaxation, dt);
    -
    -  //           c1.enabled = c2.enabled = c.enabled;
    -
    -		// 	// Add equations to solver
    -		// 	solver.addEquation(c1);
    -		// 	solver.addEquation(c2);
    -		// }
    -
    -        if( bi.allowSleep &&
    -            bi.type === Body.DYNAMIC &&
    -            bi.sleepState  === Body.SLEEPING &&
    -            bj.sleepState  === Body.AWAKE &&
    -            bj.type !== Body.STATIC
    -        ){
    -            var speedSquaredB = bj.velocity.norm2() + bj.angularVelocity.norm2();
    -            var speedLimitSquaredB = Math.pow(bj.sleepSpeedLimit,2);
    -            if(speedSquaredB >= speedLimitSquaredB*2){
    -                bi._wakeUpAfterNarrowphase = true;
    -            }
    -        }
    -
    -        if( bj.allowSleep &&
    -            bj.type === Body.DYNAMIC &&
    -            bj.sleepState  === Body.SLEEPING &&
    -            bi.sleepState  === Body.AWAKE &&
    -            bi.type !== Body.STATIC
    -        ){
    -            var speedSquaredA = bi.velocity.norm2() + bi.angularVelocity.norm2();
    -            var speedLimitSquaredA = Math.pow(bi.sleepSpeedLimit,2);
    -            if(speedSquaredA >= speedLimitSquaredA*2){
    -                bj._wakeUpAfterNarrowphase = true;
    -            }
    -        }
    -
    -        // Now we know that i and j are in contact. Set collision matrix state
    -		this.collisionMatrix.set(bi, bj, true);
    -
    -        if (!this.collisionMatrixPrevious.get(bi, bj)) {
    -            // First contact!
    -            // We reuse the collideEvent object, otherwise we will end up creating new objects for each new contact, even if there's no event listener attached.
    -            World_step_collideEvent.body = bj;
    -            World_step_collideEvent.contact = c;
    -            bi.dispatchEvent(World_step_collideEvent);
    -
    -            World_step_collideEvent.body = bi;
    -            bj.dispatchEvent(World_step_collideEvent);
    -        }
    -    }
    -    if(doProfiling){
    -        profile.makeContactConstraints = performance.now() - profilingStart;
    -        profilingStart = performance.now();
    -    }
    -
    -    // Wake up bodies
    -    for(i=0; i!==N; i++){
    -        var bi = bodies[i];
    -        if(bi._wakeUpAfterNarrowphase){
    -            bi.wakeUp();
    -            bi._wakeUpAfterNarrowphase = false;
    -        }
    -    }
    -
    -    // Add user-added constraints
    -    var Nconstraints = constraints.length;
    -    for(i=0; i!==Nconstraints; i++){
    -        var c = constraints[i];
    -        c.update();
    -        for(var j=0, Neq=c.equations.length; j!==Neq; j++){
    -            var eq = c.equations[j];
    -            solver.addEquation(eq);
    -        }
    -    }
    -
    -    // Solve the constrained system
    -    solver.solve(dt,this);
    -
    -    if(doProfiling){
    -        profile.solve = performance.now() - profilingStart;
    -    }
    -
    -    // Remove all contacts from solver
    -    solver.removeAllEquations();
    -
    -    // Apply damping, see http://code.google.com/p/bullet/issues/detail?id=74 for details
    -    var pow = Math.pow;
    -    for(i=0; i!==N; i++){
    -        var bi = bodies[i];
    -        if(bi.type & DYNAMIC){ // Only for dynamic bodies
    -            var ld = pow(1.0 - bi.linearDamping,dt);
    -            var v = bi.velocity;
    -            v.mult(ld,v);
    -            var av = bi.angularVelocity;
    -            if(av){
    -                var ad = pow(1.0 - bi.angularDamping,dt);
    -                av.mult(ad,av);
    -            }
    -        }
    -    }
    -
    -    this.dispatchEvent(World_step_preStepEvent);
    -
    -    // Invoke pre-step callbacks
    -    for(i=0; i!==N; i++){
    -        var bi = bodies[i];
    -        if(bi.preStep){
    -            bi.preStep.call(bi);
    -        }
    -    }
    -
    -    // Leap frog
    -    // vnew = v + h*f/m
    -    // xnew = x + h*vnew
    -    if(doProfiling){
    -        profilingStart = performance.now();
    -    }
    -    var q = World_step_step_q;
    -    var w = World_step_step_w;
    -    var wq = World_step_step_wq;
    -    var stepnumber = this.stepnumber;
    -    var DYNAMIC_OR_KINEMATIC = Body.DYNAMIC | Body.KINEMATIC;
    -    var quatNormalize = stepnumber % (this.quatNormalizeSkip+1) === 0;
    -    var quatNormalizeFast = this.quatNormalizeFast;
    -    var half_dt = dt * 0.5;
    -    var PLANE = Shape.types.PLANE,
    -        CONVEX = Shape.types.CONVEXPOLYHEDRON;
    -
    -    for(i=0; i!==N; i++){
    -        var b = bodies[i],
    -            force = b.force,
    -            tau = b.torque;
    -        if((b.type & DYNAMIC_OR_KINEMATIC) && b.sleepState !== Body.SLEEPING){ // Only for dynamic
    -            var velo = b.velocity,
    -                angularVelo = b.angularVelocity,
    -                pos = b.position,
    -                quat = b.quaternion,
    -                invMass = b.invMass,
    -                invInertia = b.invInertiaWorld;
    -
    -            velo.x += force.x * invMass * dt;
    -            velo.y += force.y * invMass * dt;
    -            velo.z += force.z * invMass * dt;
    -
    -            if(b.angularVelocity){
    -                invInertia.vmult(tau,invI_tau_dt);
    -                invI_tau_dt.mult(dt,invI_tau_dt);
    -                invI_tau_dt.vadd(angularVelo,angularVelo);
    -            }
    -
    -            // Use new velocity  - leap frog
    -            pos.x += velo.x * dt;
    -            pos.y += velo.y * dt;
    -            pos.z += velo.z * dt;
    -
    -            if(b.angularVelocity){
    -                w.set(angularVelo.x, angularVelo.y, angularVelo.z, 0);
    -                w.mult(quat,wq);
    -                quat.x += half_dt * wq.x;
    -                quat.y += half_dt * wq.y;
    -                quat.z += half_dt * wq.z;
    -                quat.w += half_dt * wq.w;
    -                if(quatNormalize){
    -                    if(quatNormalizeFast){
    -                        quat.normalizeFast();
    -                    } else {
    -                        quat.normalize();
    -                    }
    -                }
    -            }
    -
    -            if(b.aabb){
    -                b.aabbNeedsUpdate = true;
    -            }
    -
    -            // Update world inertia
    -            if(b.updateInertiaWorld){
    -                b.updateInertiaWorld();
    -            }
    -        }
    -    }
    -    this.clearForces();
    -
    -    this.broadphase.dirty = true;
    -
    -    if(doProfiling){
    -        profile.integrate = performance.now() - profilingStart;
    -    }
    -
    -    // Update world time
    -    this.time += dt;
    -    this.stepnumber += 1;
    -
    -    this.dispatchEvent(World_step_postStepEvent);
    -
    -    // Invoke post-step callbacks
    -    for(i=0; i!==N; i++){
    -        var bi = bodies[i];
    -        var postStep = bi.postStep;
    -        if(postStep){
    -            postStep.call(bi);
    -        }
    -    }
    -
    -    // Sleeping update
    -    if(this.allowSleep){
    -        for(i=0; i!==N; i++){
    -            bodies[i].sleepTick(this.time);
    -        }
    -    }
    -};
    -
    -/**
    - * Sets all body forces in the world to zero.
    - * @method clearForces
    - */
    -World.prototype.clearForces = function(){
    -    var bodies = this.bodies;
    -    var N = bodies.length;
    -    for(var i=0; i !== N; i++){
    -        var b = bodies[i],
    -            force = b.force,
    -            tau = b.torque;
    -
    -        b.force.set(0,0,0);
    -        b.torque.set(0,0,0);
    -    }
    -};
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index 4d5af6ae3..000000000 --- a/docs/index.html +++ /dev/null @@ -1,168 +0,0 @@ - - - - - cannon - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: 0.6.1 -
    -
    -
    - - -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -
    -
    -

    - Browse to a module or class using the sidebar to view its API documentation. -

    - -

    Keyboard Shortcuts

    - -
      -
    • Press s to focus the API search box.

    • - -
    • Use Up and Down to select classes, modules, and search results.

    • - -
    • With the API search box or sidebar focused, use -Left or -Right to switch sidebar tabs.

    • - -
    • With the API search box or sidebar focused, use Ctrl+Left and Ctrl+Right to switch sidebar tabs.

    • -
    -
    -
    - - -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/docs/modules/index.html b/docs/modules/index.html deleted file mode 100644 index 487fe15b2..000000000 --- a/docs/modules/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - Redirector - - - - Click here to redirect - - diff --git a/examples/canvas_interpolation.html b/examples/canvas_interpolation.html index 7fc14de47..29a4d0201 100644 --- a/examples/canvas_interpolation.html +++ b/examples/canvas_interpolation.html @@ -1,106 +1,115 @@ - - Canvas interpolation example - Cannon.js - - - - - - - - - - - - + + + Canvas interpolation example - Cannon.js + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/js/PointerLockControls.js b/examples/js/PointerLockControls.js index ef6db5a1b..2e797993b 100644 --- a/examples/js/PointerLockControls.js +++ b/examples/js/PointerLockControls.js @@ -2,7 +2,8 @@ * @author mrdoob / http://mrdoob.com/ * @author schteppe / https://github.com/schteppe */ - var PointerLockControls = function ( camera, cannonBody ) { +var PointerLockControls = function (camera, cannonBody) +{ var eyeYPos = 2; // eyes are 2 meters above the ground var velocityFactor = 0.2; @@ -10,11 +11,11 @@ var scope = this; var pitchObject = new THREE.Object3D(); - pitchObject.add( camera ); + pitchObject.add(camera); var yawObject = new THREE.Object3D(); yawObject.position.y = 2; - yawObject.add( pitchObject ); + yawObject.add(pitchObject); var quat = new THREE.Quaternion(); @@ -25,20 +26,21 @@ var canJump = false; - var contactNormal = new CANNON.Vec3(); // Normal in the contact, pointing *out* of whatever the player touched - var upAxis = new CANNON.Vec3(0,1,0); - cannonBody.addEventListener("collide",function(e){ + var contactNormal = new CANNON.Vector3(); // Normal in the contact, pointing *out* of whatever the player touched + var upAxis = new CANNON.Vector3(0, 1, 0); + cannonBody.addEventListener("collide", function (e) + { var contact = e.contact; // contact.bi and contact.bj are the colliding bodies, and contact.ni is the collision normal. // We do not yet know which one is which! Let's check. - if(contact.bi.id == cannonBody.id) // bi is the player body, flip the contact normal + if (contact.bi.id == cannonBody.id) // bi is the player body, flip the contact normal contact.ni.negate(contactNormal); else contactNormal.copy(contact.ni); // bi is something else. Keep the normal as it is // If contactNormal.dot(upAxis) is between 0 and 1, we know that the contact normal is somewhat in the up direction. - if(contactNormal.dot(upAxis) > 0.5) // Use a "good" threshold value between 0 and 1 here! + if (contactNormal.dot(upAxis) > 0.5) // Use a "good" threshold value between 0 and 1 here! canJump = true; }); @@ -46,9 +48,10 @@ var PI_2 = Math.PI / 2; - var onMouseMove = function ( event ) { + var onMouseMove = function (event) + { - if ( scope.enabled === false ) return; + if (scope.enabled === false) return; var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0; var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0; @@ -56,12 +59,14 @@ yawObject.rotation.y -= movementX * 0.002; pitchObject.rotation.x -= movementY * 0.002; - pitchObject.rotation.x = Math.max( - PI_2, Math.min( PI_2, pitchObject.rotation.x ) ); + pitchObject.rotation.x = Math.max(- PI_2, Math.min(PI_2, pitchObject.rotation.x)); }; - var onKeyDown = function ( event ) { + var onKeyDown = function (event) + { - switch ( event.keyCode ) { + switch (event.keyCode) + { case 38: // up case 87: // w @@ -83,7 +88,8 @@ break; case 32: // space - if ( canJump === true ){ + if (canJump === true) + { velocity.y = jumpVelocity; } canJump = false; @@ -92,9 +98,11 @@ }; - var onKeyUp = function ( event ) { + var onKeyUp = function (event) + { - switch( event.keyCode ) { + switch (event.keyCode) + { case 38: // up case 87: // w @@ -120,43 +128,50 @@ }; - document.addEventListener( 'mousemove', onMouseMove, false ); - document.addEventListener( 'keydown', onKeyDown, false ); - document.addEventListener( 'keyup', onKeyUp, false ); + document.addEventListener('mousemove', onMouseMove, false); + document.addEventListener('keydown', onKeyDown, false); + document.addEventListener('keyup', onKeyUp, false); this.enabled = false; - this.getObject = function () { + this.getObject = function () + { return yawObject; }; - this.getDirection = function(targetVec){ - targetVec.set(0,0,-1); + this.getDirection = function (targetVec) + { + targetVec.set(0, 0, -1); quat.multiplyVector3(targetVec); } // Moves the camera to the Cannon.js object position and adds velocity to the object if the run key is down var inputVelocity = new THREE.Vector3(); var euler = new THREE.Euler(); - this.update = function ( delta ) { + this.update = function (delta) + { - if ( scope.enabled === false ) return; + if (scope.enabled === false) return; delta *= 0.1; - inputVelocity.set(0,0,0); + inputVelocity.set(0, 0, 0); - if ( moveForward ){ + if (moveForward) + { inputVelocity.z = -velocityFactor * delta; } - if ( moveBackward ){ + if (moveBackward) + { inputVelocity.z = velocityFactor * delta; } - if ( moveLeft ){ + if (moveLeft) + { inputVelocity.x = -velocityFactor * delta; } - if ( moveRight ){ + if (moveRight) + { inputVelocity.x = velocityFactor * delta; } diff --git a/examples/js/VoxelLandscape.js b/examples/js/VoxelLandscape.js index bba735ed8..9b5673275 100644 --- a/examples/js/VoxelLandscape.js +++ b/examples/js/VoxelLandscape.js @@ -1,7 +1,8 @@ /** * @author schteppe / https://github.com/schteppe */ -var VoxelLandscape = function ( world, nx, ny, nz, sx, sy, sz ) { +var VoxelLandscape = function (world, nx, ny, nz, sx, sy, sz) +{ this.nx = nx; this.ny = ny; this.nz = nz; @@ -14,16 +15,19 @@ var VoxelLandscape = function ( world, nx, ny, nz, sx, sy, sz ) { this.map = []; this.boxified = []; this.boxes = []; - this.boxShape = new CANNON.Box(new CANNON.Vec3(sx*0.5,sy*0.5,sz*0.5)); + this.boxShape = new CANNON.Box(new CANNON.Vector3(sx * 0.5, sy * 0.5, sz * 0.5)); var map = this.map, boxes = this.boxes, boxified = this.boxified; // Prepare map - for(var i=0; i!==nx; i++){ - for(var j=0; j!==ny; j++){ - for(var k=0; k!==nz; k++){ + for (var i = 0; i !== nx; i++) + { + for (var j = 0; j !== ny; j++) + { + for (var k = 0; k !== nz; k++) + { map.push(true); boxified.push(false); } @@ -33,46 +37,52 @@ var VoxelLandscape = function ( world, nx, ny, nz, sx, sy, sz ) { // User must manually update the map for the first time. }; -VoxelLandscape.prototype.getBoxIndex = function(xi,yi,zi){ +VoxelLandscape.prototype.getBoxIndex = function (xi, yi, zi) +{ var nx = this.nx, ny = this.ny, nz = this.nz; - if( xi>=0 && xi=0 && yi=0 && zi= 0 && xi < nx && + yi >= 0 && yi < ny && + zi >= 0 && zi < nz) return xi + nx * yi + nx * ny * zi; else return -1; }; -VoxelLandscape.prototype.setFilled = function(xi,yi,zi,filled){ - var i = this.getBoxIndex(xi,yi,zi); - if(i!==-1) - this.map[ i ] = !!filled; +VoxelLandscape.prototype.setFilled = function (xi, yi, zi, filled) +{ + var i = this.getBoxIndex(xi, yi, zi); + if (i !== -1) + this.map[i] = !!filled; }; -VoxelLandscape.prototype.isFilled = function(xi,yi,zi){ - var i = this.getBoxIndex(xi,yi,zi); - if(i!==-1) - return this.map[ i ]; +VoxelLandscape.prototype.isFilled = function (xi, yi, zi) +{ + var i = this.getBoxIndex(xi, yi, zi); + if (i !== -1) + return this.map[i]; else return false; }; -VoxelLandscape.prototype.isBoxified = function(xi,yi,zi){ - var i = this.getBoxIndex(xi,yi,zi); - if(i!==-1) - return this.boxified[ i ]; +VoxelLandscape.prototype.isBoxified = function (xi, yi, zi) +{ + var i = this.getBoxIndex(xi, yi, zi); + if (i !== -1) + return this.boxified[i]; else return false; }; -VoxelLandscape.prototype.setBoxified = function(xi,yi,zi,boxified){ - return this.boxified[ this.getBoxIndex(xi,yi,zi) ] = !!boxified; +VoxelLandscape.prototype.setBoxified = function (xi, yi, zi, boxified) +{ + return this.boxified[this.getBoxIndex(xi, yi, zi)] = !!boxified; }; // Updates "boxes" -VoxelLandscape.prototype.update = function(){ +VoxelLandscape.prototype.update = function () +{ var map = this.map, boxes = this.boxes, world = this.world, @@ -82,24 +92,31 @@ VoxelLandscape.prototype.update = function(){ nz = this.nz; // Remove all old boxes - for(var i=0; i!==boxes.length; i++){ + for (var i = 0; i !== boxes.length; i++) + { world.remove(boxes[i]); } boxes.length = 0; // Set whole map to unboxified - for(var i=0; i!==boxified.length; i++){ + for (var i = 0; i !== boxified.length; i++) + { boxified[i] = false; } - while(true){ + while (true) + { var box; // 1. Get a filled box that we haven't boxified yet - for(var i=0; !box && ij-yi) box.ny = j-yi; + if (box.ny > j - yi) box.ny = j - yi; } } } // Merge in z found = false; - for(var i=xi; !found && ik-zi) box.nz = k-zi; + if (box.nz > k - zi) box.nz = k - zi; } } } } - if(box.nx==0) box.nx = 1; - if(box.ny==0) box.ny = 1; - if(box.nz==0) box.nz = 1; + if (box.nx == 0) box.nx = 1; + if (box.ny == 0) box.ny = 1; + if (box.nz == 0) box.nz = 1; // Set the merged boxes as boxified - for(var i=xi; i= xi && i<=xi+box.nx && - j >= yi && j<=yi+box.ny && - k >= zi && k<=zi+box.nz){ - this.setBoxified(i,j,k,true); + for (var i = xi; i < xi + box.nx; i++) + { + for (var j = yi; j < yi + box.ny; j++) + { + for (var k = zi; k < zi + box.nz; k++) + { + if (i >= xi && i <= xi + box.nx && + j >= yi && j <= yi + box.ny && + k >= zi && k <= zi + box.nz) + { + this.setBoxified(i, j, k, true); } } } } box = false; - } else { + } else + { break; } } @@ -184,16 +216,17 @@ VoxelLandscape.prototype.update = function(){ var sx = this.sx, sy = this.sy, sz = this.sz; - for(var i=0; i - + + SceneJS / cannon.js example - - + + + - + + - + - - + + + \ No newline at end of file diff --git a/examples/threejs.html b/examples/threejs.html index 03bd5e16d..cc219371f 100644 --- a/examples/threejs.html +++ b/examples/threejs.html @@ -1,96 +1,105 @@ - - three.js / cannon.js example - - - - - - - + + + - - + renderer.render(scene, camera); + + } + + + + + \ No newline at end of file diff --git a/examples/threejs_cloth.html b/examples/threejs_cloth.html index e0242b11e..d897c2d0a 100644 --- a/examples/threejs_cloth.html +++ b/examples/threejs_cloth.html @@ -1,308 +1,327 @@ - - cannon.js cloth simulation - - - - - - - - - - - + + + + + + - - - + \ No newline at end of file diff --git a/examples/threejs_fps.html b/examples/threejs_fps.html index fefa38eeb..446817925 100644 --- a/examples/threejs_fps.html +++ b/examples/threejs_fps.html @@ -1,407 +1,437 @@ - - - cannon.js + three.js physics shooter - - - - - - + } + + -
    + + + + + -
    - Click to play -
    - (W,A,S,D = Move, SPACE = Jump, MOUSE = Look, CLICK = Shoot) -
    +
    +
    + Click to play +
    + (W,A,S,D = Move, SPACE = Jump, MOUSE = Look, CLICK = Shoot)
    - + - - - + \ No newline at end of file diff --git a/examples/threejs_mousepick.html b/examples/threejs_mousepick.html index b0a3d13ed..2555f6e52 100644 --- a/examples/threejs_mousepick.html +++ b/examples/threejs_mousepick.html @@ -1,32 +1,36 @@ - - - cannon.js mouse pick demo - - - - - - - + + + + + - - + + + + \ No newline at end of file diff --git a/examples/threejs_voxel_fps.html b/examples/threejs_voxel_fps.html index ff8a60f3e..3daa26464 100644 --- a/examples/threejs_voxel_fps.html +++ b/examples/threejs_voxel_fps.html @@ -1,408 +1,438 @@ - - - cannon.js + three.js physics shooter - - - - - - - + } + + -
    - Cannon.js voxel landscape example -
    + + + + + + -
    +
    + Cannon.js voxel landscape example +
    -
    - Click to play -
    - (W,A,S,D = Move, SPACE = Jump, MOUSE = Look, CLICK = Shoot) -
    +
    +
    + Click to play +
    + (W,A,S,D = Move, SPACE = Jump, MOUSE = Look, CLICK = Shoot)
    - + - - - + \ No newline at end of file diff --git a/examples/worker.html b/examples/worker.html index 807fa6940..25733ec7f 100644 --- a/examples/worker.html +++ b/examples/worker.html @@ -1,5 +1,6 @@ + cannon.js worker example @@ -12,26 +13,30 @@ margin: 0px; overflow: hidden; } + #info { text-align: center; padding: 10px; z-index: 10; width: 100%; position: absolute; - color:white; + color: white; } + a { text-decoration: underline; cursor: pointer; } +
    Cannon.js web worker example
    + @@ -53,11 +58,11 @@ var plane = new CANNON.Plane(); var groundBody = new CANNON.Body({ mass: 0 }); groundBody.addShape(plane); - groundBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1,0,0),-Math.PI/2); + groundBody.quaternion.fromAxisAngle(new CANNON.Vector3(1,0,0),-Math.PI/2); world.addBody(groundBody); // Create N cubes - var shape = new CANNON.Box(new CANNON.Vec3(0.5,0.5,0.5)); + var shape = new CANNON.Box(new CANNON.Vector3(0.5,0.5,0.5)); for(var i=0; i!==e.data.N; i++){ var body = new CANNON.Body({ mass: 1 }); body.addShape(shape); @@ -96,84 +101,89 @@ + - + + \ No newline at end of file diff --git a/libs/Three.js b/libs/Three.js index dfe37ad49..d98a124a1 100644 --- a/libs/Three.js +++ b/libs/Three.js @@ -1,827 +1,35842 @@ -// threejs.org/license -'use strict';var THREE={REVISION:"68"};"object"===typeof module&&(module.exports=THREE);THREE.CullFaceNone=0;THREE.CullFaceBack=1;THREE.CullFaceFront=2;THREE.CullFaceFrontBack=3;THREE.FrontFaceDirectionCW=0;THREE.FrontFaceDirectionCCW=1;THREE.BasicShadowMap=0;THREE.PCFShadowMap=1;THREE.PCFSoftShadowMap=2;THREE.FrontSide=0;THREE.BackSide=1;THREE.DoubleSide=2;THREE.NoShading=0;THREE.FlatShading=1;THREE.SmoothShading=2;THREE.NoColors=0;THREE.FaceColors=1;THREE.VertexColors=2;THREE.NoBlending=0; -THREE.NormalBlending=1;THREE.AdditiveBlending=2;THREE.SubtractiveBlending=3;THREE.MultiplyBlending=4;THREE.CustomBlending=5;THREE.AddEquation=100;THREE.SubtractEquation=101;THREE.ReverseSubtractEquation=102;THREE.ZeroFactor=200;THREE.OneFactor=201;THREE.SrcColorFactor=202;THREE.OneMinusSrcColorFactor=203;THREE.SrcAlphaFactor=204;THREE.OneMinusSrcAlphaFactor=205;THREE.DstAlphaFactor=206;THREE.OneMinusDstAlphaFactor=207;THREE.DstColorFactor=208;THREE.OneMinusDstColorFactor=209; -THREE.SrcAlphaSaturateFactor=210;THREE.MultiplyOperation=0;THREE.MixOperation=1;THREE.AddOperation=2;THREE.UVMapping=function(){};THREE.CubeReflectionMapping=function(){};THREE.CubeRefractionMapping=function(){};THREE.SphericalReflectionMapping=function(){};THREE.SphericalRefractionMapping=function(){};THREE.RepeatWrapping=1E3;THREE.ClampToEdgeWrapping=1001;THREE.MirroredRepeatWrapping=1002;THREE.NearestFilter=1003;THREE.NearestMipMapNearestFilter=1004;THREE.NearestMipMapLinearFilter=1005; -THREE.LinearFilter=1006;THREE.LinearMipMapNearestFilter=1007;THREE.LinearMipMapLinearFilter=1008;THREE.UnsignedByteType=1009;THREE.ByteType=1010;THREE.ShortType=1011;THREE.UnsignedShortType=1012;THREE.IntType=1013;THREE.UnsignedIntType=1014;THREE.FloatType=1015;THREE.UnsignedShort4444Type=1016;THREE.UnsignedShort5551Type=1017;THREE.UnsignedShort565Type=1018;THREE.AlphaFormat=1019;THREE.RGBFormat=1020;THREE.RGBAFormat=1021;THREE.LuminanceFormat=1022;THREE.LuminanceAlphaFormat=1023; -THREE.RGB_S3TC_DXT1_Format=2001;THREE.RGBA_S3TC_DXT1_Format=2002;THREE.RGBA_S3TC_DXT3_Format=2003;THREE.RGBA_S3TC_DXT5_Format=2004;THREE.Color=function(a){return 3===arguments.length?this.setRGB(arguments[0],arguments[1],arguments[2]):this.set(a)}; -THREE.Color.prototype={constructor:THREE.Color,r:1,g:1,b:1,set:function(a){a instanceof THREE.Color?this.copy(a):"number"===typeof a?this.setHex(a):"string"===typeof a&&this.setStyle(a);return this},setHex:function(a){a=Math.floor(a);this.r=(a>>16&255)/255;this.g=(a>>8&255)/255;this.b=(a&255)/255;return this},setRGB:function(a,b,c){this.r=a;this.g=b;this.b=c;return this},setHSL:function(a,b,c){if(0===b)this.r=this.g=this.b=c;else{var d=function(a,b,c){0>c&&(c+=1);1c?b:c<2/3?a+6*(b-a)*(2/3-c):a};b=0.5>=c?c*(1+b):c+b-c*b;c=2*c-b;this.r=d(c,b,a+1/3);this.g=d(c,b,a);this.b=d(c,b,a-1/3)}return this},setStyle:function(a){if(/^rgb\((\d+), ?(\d+), ?(\d+)\)$/i.test(a))return a=/^rgb\((\d+), ?(\d+), ?(\d+)\)$/i.exec(a),this.r=Math.min(255,parseInt(a[1],10))/255,this.g=Math.min(255,parseInt(a[2],10))/255,this.b=Math.min(255,parseInt(a[3],10))/255,this;if(/^rgb\((\d+)\%, ?(\d+)\%, ?(\d+)\%\)$/i.test(a))return a=/^rgb\((\d+)\%, ?(\d+)\%, ?(\d+)\%\)$/i.exec(a),this.r= -Math.min(100,parseInt(a[1],10))/100,this.g=Math.min(100,parseInt(a[2],10))/100,this.b=Math.min(100,parseInt(a[3],10))/100,this;if(/^\#([0-9a-f]{6})$/i.test(a))return a=/^\#([0-9a-f]{6})$/i.exec(a),this.setHex(parseInt(a[1],16)),this;if(/^\#([0-9a-f])([0-9a-f])([0-9a-f])$/i.test(a))return a=/^\#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(a),this.setHex(parseInt(a[1]+a[1]+a[2]+a[2]+a[3]+a[3],16)),this;if(/^(\w+)$/i.test(a))return this.setHex(THREE.ColorKeywords[a]),this},copy:function(a){this.r=a.r;this.g= -a.g;this.b=a.b;return this},copyGammaToLinear:function(a){this.r=a.r*a.r;this.g=a.g*a.g;this.b=a.b*a.b;return this},copyLinearToGamma:function(a){this.r=Math.sqrt(a.r);this.g=Math.sqrt(a.g);this.b=Math.sqrt(a.b);return this},convertGammaToLinear:function(){var a=this.r,b=this.g,c=this.b;this.r=a*a;this.g=b*b;this.b=c*c;return this},convertLinearToGamma:function(){this.r=Math.sqrt(this.r);this.g=Math.sqrt(this.g);this.b=Math.sqrt(this.b);return this},getHex:function(){return 255*this.r<<16^255*this.g<< -8^255*this.b<<0},getHexString:function(){return("000000"+this.getHex().toString(16)).slice(-6)},getHSL:function(a){a=a||{h:0,s:0,l:0};var b=this.r,c=this.g,d=this.b,e=Math.max(b,c,d),f=Math.min(b,c,d),g,h=(f+e)/2;if(f===e)f=g=0;else{var k=e-f,f=0.5>=h?k/(e+f):k/(2-e-f);switch(e){case b:g=(c-d)/k+(cf&&c>b?(c=2*Math.sqrt(1+c-f-b),this._w=(k-g)/c,this._x=0.25*c,this._y=(a+e)/c,this._z=(d+h)/c):f>b?(c=2*Math.sqrt(1+f-c-b),this._w=(d-h)/c,this._x=(a+e)/c,this._y= -0.25*c,this._z=(g+k)/c):(c=2*Math.sqrt(1+b-c-f),this._w=(e-a)/c,this._x=(d+h)/c,this._y=(g+k)/c,this._z=0.25*c);this.onChangeCallback();return this},setFromUnitVectors:function(){var a,b;return function(c,d){void 0===a&&(a=new THREE.Vector3);b=c.dot(d)+1;1E-6>b?(b=0,Math.abs(c.x)>Math.abs(c.z)?a.set(-c.y,c.x,0):a.set(0,-c.z,c.y)):a.crossVectors(c,d);this._x=a.x;this._y=a.y;this._z=a.z;this._w=b;this.normalize();return this}}(),inverse:function(){this.conjugate().normalize();return this},conjugate:function(){this._x*= --1;this._y*=-1;this._z*=-1;this.onChangeCallback();return this},dot:function(a){return this._x*a._x+this._y*a._y+this._z*a._z+this._w*a._w},lengthSq:function(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w},length:function(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)},normalize:function(){var a=this.length();0===a?(this._z=this._y=this._x=0,this._w=1):(a=1/a,this._x*=a,this._y*=a,this._z*=a,this._w*=a);this.onChangeCallback();return this}, -multiply:function(a,b){return void 0!==b?(console.warn("THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead."),this.multiplyQuaternions(a,b)):this.multiplyQuaternions(this,a)},multiplyQuaternions:function(a,b){var c=a._x,d=a._y,e=a._z,f=a._w,g=b._x,h=b._y,k=b._z,l=b._w;this._x=c*l+f*g+d*k-e*h;this._y=d*l+f*h+e*g-c*k;this._z=e*l+f*k+c*h-d*g;this._w=f*l-c*g-d*h-e*k;this.onChangeCallback();return this},multiplyVector3:function(a){console.warn("THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead."); -return a.applyQuaternion(this)},slerp:function(a,b){var c=this._x,d=this._y,e=this._z,f=this._w,g=f*a._w+c*a._x+d*a._y+e*a._z;0>g?(this._w=-a._w,this._x=-a._x,this._y=-a._y,this._z=-a._z,g=-g):this.copy(a);if(1<=g)return this._w=f,this._x=c,this._y=d,this._z=e,this;var h=Math.acos(g),k=Math.sqrt(1-g*g);if(0.001>Math.abs(k))return this._w=0.5*(f+this._w),this._x=0.5*(c+this._x),this._y=0.5*(d+this._y),this._z=0.5*(e+this._z),this;g=Math.sin((1-b)*h)/k;h=Math.sin(b*h)/k;this._w=f*g+this._w*h;this._x= -c*g+this._x*h;this._y=d*g+this._y*h;this._z=e*g+this._z*h;this.onChangeCallback();return this},equals:function(a){return a._x===this._x&&a._y===this._y&&a._z===this._z&&a._w===this._w},fromArray:function(a){this._x=a[0];this._y=a[1];this._z=a[2];this._w=a[3];this.onChangeCallback();return this},toArray:function(){return[this._x,this._y,this._z,this._w]},onChange:function(a){this.onChangeCallback=a;return this},onChangeCallback:function(){},clone:function(){return new THREE.Quaternion(this._x,this._y, -this._z,this._w)}};THREE.Quaternion.slerp=function(a,b,c,d){return c.copy(a).slerp(b,d)};THREE.Vector2=function(a,b){this.x=a||0;this.y=b||0}; -THREE.Vector2.prototype={constructor:THREE.Vector2,set:function(a,b){this.x=a;this.y=b;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;default:throw Error("index is out of range: "+a);}},getComponent:function(a){switch(a){case 0:return this.x;case 1:return this.y;default:throw Error("index is out of range: "+a);}},copy:function(a){this.x=a.x;this.y=a.y;return this},add:function(a, -b){if(void 0!==b)return console.warn("THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(a,b);this.x+=a.x;this.y+=a.y;return this},addVectors:function(a,b){this.x=a.x+b.x;this.y=a.y+b.y;return this},addScalar:function(a){this.x+=a;this.y+=a;return this},sub:function(a,b){if(void 0!==b)return console.warn("THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(a,b);this.x-=a.x;this.y-=a.y;return this}, -subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;return this},multiply:function(a){this.x*=a.x;this.y*=a.y;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;return this},divide:function(a){this.x/=a.x;this.y/=a.y;return this},divideScalar:function(a){0!==a?(a=1/a,this.x*=a,this.y*=a):this.y=this.x=0;return this},min:function(a){this.x>a.x&&(this.x=a.x);this.y>a.y&&(this.y=a.y);return this},max:function(a){this.xb.x&&(this.x=b.x);this.yb.y&&(this.y=b.y);return this},clampScalar:function(){var a,b;return function(c,d){void 0===a&&(a=new THREE.Vector2,b=new THREE.Vector2);a.set(c,c);b.set(d,d);return this.clamp(a,b)}}(),floor:function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);return this},ceil:function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);return this},round:function(){this.x=Math.round(this.x);this.y=Math.round(this.y);return this}, -roundToZero:function(){this.x=0>this.x?Math.ceil(this.x):Math.floor(this.x);this.y=0>this.y?Math.ceil(this.y):Math.floor(this.y);return this},negate:function(){this.x=-this.x;this.y=-this.y;return this},dot:function(a){return this.x*a.x+this.y*a.y},lengthSq:function(){return this.x*this.x+this.y*this.y},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y)},normalize:function(){return this.divideScalar(this.length())},distanceTo:function(a){return Math.sqrt(this.distanceToSquared(a))},distanceToSquared:function(a){var b= -this.x-a.x;a=this.y-a.y;return b*b+a*a},setLength:function(a){var b=this.length();0!==b&&a!==b&&this.multiplyScalar(a/b);return this},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;return this},equals:function(a){return a.x===this.x&&a.y===this.y},fromArray:function(a){this.x=a[0];this.y=a[1];return this},toArray:function(){return[this.x,this.y]},clone:function(){return new THREE.Vector2(this.x,this.y)}};THREE.Vector3=function(a,b,c){this.x=a||0;this.y=b||0;this.z=c||0}; -THREE.Vector3.prototype={constructor:THREE.Vector3,set:function(a,b,c){this.x=a;this.y=b;this.z=c;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setZ:function(a){this.z=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;case 2:this.z=b;break;default:throw Error("index is out of range: "+a);}},getComponent:function(a){switch(a){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw Error("index is out of range: "+ -a);}},copy:function(a){this.x=a.x;this.y=a.y;this.z=a.z;return this},add:function(a,b){if(void 0!==b)return console.warn("THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(a,b);this.x+=a.x;this.y+=a.y;this.z+=a.z;return this},addScalar:function(a){this.x+=a;this.y+=a;this.z+=a;return this},addVectors:function(a,b){this.x=a.x+b.x;this.y=a.y+b.y;this.z=a.z+b.z;return this},sub:function(a,b){if(void 0!==b)return console.warn("THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."), -this.subVectors(a,b);this.x-=a.x;this.y-=a.y;this.z-=a.z;return this},subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;this.z=a.z-b.z;return this},multiply:function(a,b){if(void 0!==b)return console.warn("THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."),this.multiplyVectors(a,b);this.x*=a.x;this.y*=a.y;this.z*=a.z;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;this.z*=a;return this},multiplyVectors:function(a,b){this.x=a.x*b.x;this.y= -a.y*b.y;this.z=a.z*b.z;return this},applyEuler:function(){var a;return function(b){!1===b instanceof THREE.Euler&&console.error("THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order.");void 0===a&&(a=new THREE.Quaternion);this.applyQuaternion(a.setFromEuler(b));return this}}(),applyAxisAngle:function(){var a;return function(b,c){void 0===a&&(a=new THREE.Quaternion);this.applyQuaternion(a.setFromAxisAngle(b,c));return this}}(),applyMatrix3:function(a){var b=this.x, -c=this.y,d=this.z;a=a.elements;this.x=a[0]*b+a[3]*c+a[6]*d;this.y=a[1]*b+a[4]*c+a[7]*d;this.z=a[2]*b+a[5]*c+a[8]*d;return this},applyMatrix4:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d+a[12];this.y=a[1]*b+a[5]*c+a[9]*d+a[13];this.z=a[2]*b+a[6]*c+a[10]*d+a[14];return this},applyProjection:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;var e=1/(a[3]*b+a[7]*c+a[11]*d+a[15]);this.x=(a[0]*b+a[4]*c+a[8]*d+a[12])*e;this.y=(a[1]*b+a[5]*c+a[9]*d+a[13])*e;this.z= -(a[2]*b+a[6]*c+a[10]*d+a[14])*e;return this},applyQuaternion:function(a){var b=this.x,c=this.y,d=this.z,e=a.x,f=a.y,g=a.z;a=a.w;var h=a*b+f*d-g*c,k=a*c+g*b-e*d,l=a*d+e*c-f*b,b=-e*b-f*c-g*d;this.x=h*a+b*-e+k*-g-l*-f;this.y=k*a+b*-f+l*-e-h*-g;this.z=l*a+b*-g+h*-f-k*-e;return this},transformDirection:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d;this.y=a[1]*b+a[5]*c+a[9]*d;this.z=a[2]*b+a[6]*c+a[10]*d;this.normalize();return this},divide:function(a){this.x/=a.x; -this.y/=a.y;this.z/=a.z;return this},divideScalar:function(a){0!==a?(a=1/a,this.x*=a,this.y*=a,this.z*=a):this.z=this.y=this.x=0;return this},min:function(a){this.x>a.x&&(this.x=a.x);this.y>a.y&&(this.y=a.y);this.z>a.z&&(this.z=a.z);return this},max:function(a){this.xb.x&&(this.x=b.x);this.yb.y&&(this.y=b.y);this.zb.z&&(this.z= -b.z);return this},clampScalar:function(){var a,b;return function(c,d){void 0===a&&(a=new THREE.Vector3,b=new THREE.Vector3);a.set(c,c,c);b.set(d,d,d);return this.clamp(a,b)}}(),floor:function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);this.z=Math.floor(this.z);return this},ceil:function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);this.z=Math.ceil(this.z);return this},round:function(){this.x=Math.round(this.x);this.y=Math.round(this.y);this.z=Math.round(this.z);return this},roundToZero:function(){this.x= -0>this.x?Math.ceil(this.x):Math.floor(this.x);this.y=0>this.y?Math.ceil(this.y):Math.floor(this.y);this.z=0>this.z?Math.ceil(this.z):Math.floor(this.z);return this},negate:function(){this.x=-this.x;this.y=-this.y;this.z=-this.z;return this},dot:function(a){return this.x*a.x+this.y*a.y+this.z*a.z},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)+ -Math.abs(this.z)},normalize:function(){return this.divideScalar(this.length())},setLength:function(a){var b=this.length();0!==b&&a!==b&&this.multiplyScalar(a/b);return this},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;this.z+=(a.z-this.z)*b;return this},cross:function(a,b){if(void 0!==b)return console.warn("THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead."),this.crossVectors(a,b);var c=this.x,d=this.y,e=this.z;this.x=d*a.z-e*a.y;this.y= -e*a.x-c*a.z;this.z=c*a.y-d*a.x;return this},crossVectors:function(a,b){var c=a.x,d=a.y,e=a.z,f=b.x,g=b.y,h=b.z;this.x=d*h-e*g;this.y=e*f-c*h;this.z=c*g-d*f;return this},projectOnVector:function(){var a,b;return function(c){void 0===a&&(a=new THREE.Vector3);a.copy(c).normalize();b=this.dot(a);return this.copy(a).multiplyScalar(b)}}(),projectOnPlane:function(){var a;return function(b){void 0===a&&(a=new THREE.Vector3);a.copy(this).projectOnVector(b);return this.sub(a)}}(),reflect:function(){var a;return function(b){void 0=== -a&&(a=new THREE.Vector3);return this.sub(a.copy(b).multiplyScalar(2*this.dot(b)))}}(),angleTo:function(a){a=this.dot(a)/(this.length()*a.length());return Math.acos(THREE.Math.clamp(a,-1,1))},distanceTo:function(a){return Math.sqrt(this.distanceToSquared(a))},distanceToSquared:function(a){var b=this.x-a.x,c=this.y-a.y;a=this.z-a.z;return b*b+c*c+a*a},setEulerFromRotationMatrix:function(a,b){console.error("THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.")}, -setEulerFromQuaternion:function(a,b){console.error("THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.")},getPositionFromMatrix:function(a){console.warn("THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().");return this.setFromMatrixPosition(a)},getScaleFromMatrix:function(a){console.warn("THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().");return this.setFromMatrixScale(a)},getColumnFromMatrix:function(a, -b){console.warn("THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().");return this.setFromMatrixColumn(a,b)},setFromMatrixPosition:function(a){this.x=a.elements[12];this.y=a.elements[13];this.z=a.elements[14];return this},setFromMatrixScale:function(a){var b=this.set(a.elements[0],a.elements[1],a.elements[2]).length(),c=this.set(a.elements[4],a.elements[5],a.elements[6]).length();a=this.set(a.elements[8],a.elements[9],a.elements[10]).length();this.x=b;this.y=c;this.z= -a;return this},setFromMatrixColumn:function(a,b){var c=4*a,d=b.elements;this.x=d[c];this.y=d[c+1];this.z=d[c+2];return this},equals:function(a){return a.x===this.x&&a.y===this.y&&a.z===this.z},fromArray:function(a){this.x=a[0];this.y=a[1];this.z=a[2];return this},toArray:function(){return[this.x,this.y,this.z]},clone:function(){return new THREE.Vector3(this.x,this.y,this.z)}};THREE.Vector4=function(a,b,c,d){this.x=a||0;this.y=b||0;this.z=c||0;this.w=void 0!==d?d:1}; -THREE.Vector4.prototype={constructor:THREE.Vector4,set:function(a,b,c,d){this.x=a;this.y=b;this.z=c;this.w=d;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setZ:function(a){this.z=a;return this},setW:function(a){this.w=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;case 2:this.z=b;break;case 3:this.w=b;break;default:throw Error("index is out of range: "+a);}},getComponent:function(a){switch(a){case 0:return this.x; -case 1:return this.y;case 2:return this.z;case 3:return this.w;default:throw Error("index is out of range: "+a);}},copy:function(a){this.x=a.x;this.y=a.y;this.z=a.z;this.w=void 0!==a.w?a.w:1;return this},add:function(a,b){if(void 0!==b)return console.warn("THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(a,b);this.x+=a.x;this.y+=a.y;this.z+=a.z;this.w+=a.w;return this},addScalar:function(a){this.x+=a;this.y+=a;this.z+=a;this.w+=a;return this}, -addVectors:function(a,b){this.x=a.x+b.x;this.y=a.y+b.y;this.z=a.z+b.z;this.w=a.w+b.w;return this},sub:function(a,b){if(void 0!==b)return console.warn("THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(a,b);this.x-=a.x;this.y-=a.y;this.z-=a.z;this.w-=a.w;return this},subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;this.z=a.z-b.z;this.w=a.w-b.w;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;this.z*=a;this.w*=a;return this},applyMatrix4:function(a){var b= -this.x,c=this.y,d=this.z,e=this.w;a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d+a[12]*e;this.y=a[1]*b+a[5]*c+a[9]*d+a[13]*e;this.z=a[2]*b+a[6]*c+a[10]*d+a[14]*e;this.w=a[3]*b+a[7]*c+a[11]*d+a[15]*e;return this},divideScalar:function(a){0!==a?(a=1/a,this.x*=a,this.y*=a,this.z*=a,this.w*=a):(this.z=this.y=this.x=0,this.w=1);return this},setAxisAngleFromQuaternion:function(a){this.w=2*Math.acos(a.w);var b=Math.sqrt(1-a.w*a.w);1E-4>b?(this.x=1,this.z=this.y=0):(this.x=a.x/b,this.y=a.y/b,this.z=a.z/b);return this}, -setAxisAngleFromRotationMatrix:function(a){var b,c,d;a=a.elements;var e=a[0];d=a[4];var f=a[8],g=a[1],h=a[5],k=a[9];c=a[2];b=a[6];var l=a[10];if(0.01>Math.abs(d-g)&&0.01>Math.abs(f-c)&&0.01>Math.abs(k-b)){if(0.1>Math.abs(d+g)&&0.1>Math.abs(f+c)&&0.1>Math.abs(k+b)&&0.1>Math.abs(e+h+l-3))return this.set(1,0,0,0),this;a=Math.PI;e=(e+1)/2;h=(h+1)/2;l=(l+1)/2;d=(d+g)/4;f=(f+c)/4;k=(k+b)/4;e>h&&e>l?0.01>e?(b=0,d=c=0.707106781):(b=Math.sqrt(e),c=d/b,d=f/b):h>l?0.01>h?(b=0.707106781,c=0,d=0.707106781):(c= -Math.sqrt(h),b=d/c,d=k/c):0.01>l?(c=b=0.707106781,d=0):(d=Math.sqrt(l),b=f/d,c=k/d);this.set(b,c,d,a);return this}a=Math.sqrt((b-k)*(b-k)+(f-c)*(f-c)+(g-d)*(g-d));0.001>Math.abs(a)&&(a=1);this.x=(b-k)/a;this.y=(f-c)/a;this.z=(g-d)/a;this.w=Math.acos((e+h+l-1)/2);return this},min:function(a){this.x>a.x&&(this.x=a.x);this.y>a.y&&(this.y=a.y);this.z>a.z&&(this.z=a.z);this.w>a.w&&(this.w=a.w);return this},max:function(a){this.xb.x&&(this.x=b.x);this.yb.y&&(this.y=b.y);this.zb.z&&(this.z=b.z);this.wb.w&&(this.w=b.w);return this},clampScalar:function(){var a,b;return function(c,d){void 0===a&&(a=new THREE.Vector4,b=new THREE.Vector4);a.set(c,c,c,c);b.set(d,d,d,d);return this.clamp(a,b)}}(),floor:function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);this.z=Math.floor(this.z); -this.w=Math.floor(this.w);return this},ceil:function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);this.z=Math.ceil(this.z);this.w=Math.ceil(this.w);return this},round:function(){this.x=Math.round(this.x);this.y=Math.round(this.y);this.z=Math.round(this.z);this.w=Math.round(this.w);return this},roundToZero:function(){this.x=0>this.x?Math.ceil(this.x):Math.floor(this.x);this.y=0>this.y?Math.ceil(this.y):Math.floor(this.y);this.z=0>this.z?Math.ceil(this.z):Math.floor(this.z);this.w=0>this.w?Math.ceil(this.w): -Math.floor(this.w);return this},negate:function(){this.x=-this.x;this.y=-this.y;this.z=-this.z;this.w=-this.w;return this},dot:function(a){return this.x*a.x+this.y*a.y+this.z*a.z+this.w*a.w},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)+Math.abs(this.w)},normalize:function(){return this.divideScalar(this.length())}, -setLength:function(a){var b=this.length();0!==b&&a!==b&&this.multiplyScalar(a/b);return this},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;this.z+=(a.z-this.z)*b;this.w+=(a.w-this.w)*b;return this},equals:function(a){return a.x===this.x&&a.y===this.y&&a.z===this.z&&a.w===this.w},fromArray:function(a){this.x=a[0];this.y=a[1];this.z=a[2];this.w=a[3];return this},toArray:function(){return[this.x,this.y,this.z,this.w]},clone:function(){return new THREE.Vector4(this.x,this.y,this.z, -this.w)}};THREE.Euler=function(a,b,c,d){this._x=a||0;this._y=b||0;this._z=c||0;this._order=d||THREE.Euler.DefaultOrder};THREE.Euler.RotationOrders="XYZ YZX ZXY XZY YXZ ZYX".split(" ");THREE.Euler.DefaultOrder="XYZ"; -THREE.Euler.prototype={constructor:THREE.Euler,_x:0,_y:0,_z:0,_order:THREE.Euler.DefaultOrder,get x(){return this._x},set x(a){this._x=a;this.onChangeCallback()},get y(){return this._y},set y(a){this._y=a;this.onChangeCallback()},get z(){return this._z},set z(a){this._z=a;this.onChangeCallback()},get order(){return this._order},set order(a){this._order=a;this.onChangeCallback()},set:function(a,b,c,d){this._x=a;this._y=b;this._z=c;this._order=d||this._order;this.onChangeCallback();return this},copy:function(a){this._x= -a._x;this._y=a._y;this._z=a._z;this._order=a._order;this.onChangeCallback();return this},setFromRotationMatrix:function(a,b){var c=THREE.Math.clamp,d=a.elements,e=d[0],f=d[4],g=d[8],h=d[1],k=d[5],l=d[9],n=d[2],q=d[6],d=d[10];b=b||this._order;"XYZ"===b?(this._y=Math.asin(c(g,-1,1)),0.99999>Math.abs(g)?(this._x=Math.atan2(-l,d),this._z=Math.atan2(-f,e)):(this._x=Math.atan2(q,k),this._z=0)):"YXZ"===b?(this._x=Math.asin(-c(l,-1,1)),0.99999>Math.abs(l)?(this._y=Math.atan2(g,d),this._z=Math.atan2(h,k)): -(this._y=Math.atan2(-n,e),this._z=0)):"ZXY"===b?(this._x=Math.asin(c(q,-1,1)),0.99999>Math.abs(q)?(this._y=Math.atan2(-n,d),this._z=Math.atan2(-f,k)):(this._y=0,this._z=Math.atan2(h,e))):"ZYX"===b?(this._y=Math.asin(-c(n,-1,1)),0.99999>Math.abs(n)?(this._x=Math.atan2(q,d),this._z=Math.atan2(h,e)):(this._x=0,this._z=Math.atan2(-f,k))):"YZX"===b?(this._z=Math.asin(c(h,-1,1)),0.99999>Math.abs(h)?(this._x=Math.atan2(-l,k),this._y=Math.atan2(-n,e)):(this._x=0,this._y=Math.atan2(g,d))):"XZY"===b?(this._z= -Math.asin(-c(f,-1,1)),0.99999>Math.abs(f)?(this._x=Math.atan2(q,k),this._y=Math.atan2(g,e)):(this._x=Math.atan2(-l,d),this._y=0)):console.warn("THREE.Euler: .setFromRotationMatrix() given unsupported order: "+b);this._order=b;this.onChangeCallback();return this},setFromQuaternion:function(a,b,c){var d=THREE.Math.clamp,e=a.x*a.x,f=a.y*a.y,g=a.z*a.z,h=a.w*a.w;b=b||this._order;"XYZ"===b?(this._x=Math.atan2(2*(a.x*a.w-a.y*a.z),h-e-f+g),this._y=Math.asin(d(2*(a.x*a.z+a.y*a.w),-1,1)),this._z=Math.atan2(2* -(a.z*a.w-a.x*a.y),h+e-f-g)):"YXZ"===b?(this._x=Math.asin(d(2*(a.x*a.w-a.y*a.z),-1,1)),this._y=Math.atan2(2*(a.x*a.z+a.y*a.w),h-e-f+g),this._z=Math.atan2(2*(a.x*a.y+a.z*a.w),h-e+f-g)):"ZXY"===b?(this._x=Math.asin(d(2*(a.x*a.w+a.y*a.z),-1,1)),this._y=Math.atan2(2*(a.y*a.w-a.z*a.x),h-e-f+g),this._z=Math.atan2(2*(a.z*a.w-a.x*a.y),h-e+f-g)):"ZYX"===b?(this._x=Math.atan2(2*(a.x*a.w+a.z*a.y),h-e-f+g),this._y=Math.asin(d(2*(a.y*a.w-a.x*a.z),-1,1)),this._z=Math.atan2(2*(a.x*a.y+a.z*a.w),h+e-f-g)):"YZX"=== -b?(this._x=Math.atan2(2*(a.x*a.w-a.z*a.y),h-e+f-g),this._y=Math.atan2(2*(a.y*a.w-a.x*a.z),h+e-f-g),this._z=Math.asin(d(2*(a.x*a.y+a.z*a.w),-1,1))):"XZY"===b?(this._x=Math.atan2(2*(a.x*a.w+a.y*a.z),h-e+f-g),this._y=Math.atan2(2*(a.x*a.z+a.y*a.w),h+e-f-g),this._z=Math.asin(d(2*(a.z*a.w-a.x*a.y),-1,1))):console.warn("THREE.Euler: .setFromQuaternion() given unsupported order: "+b);this._order=b;if(!1!==c)this.onChangeCallback();return this},reorder:function(){var a=new THREE.Quaternion;return function(b){a.setFromEuler(this); -this.setFromQuaternion(a,b)}}(),equals:function(a){return a._x===this._x&&a._y===this._y&&a._z===this._z&&a._order===this._order},fromArray:function(a){this._x=a[0];this._y=a[1];this._z=a[2];void 0!==a[3]&&(this._order=a[3]);this.onChangeCallback();return this},toArray:function(){return[this._x,this._y,this._z,this._order]},onChange:function(a){this.onChangeCallback=a;return this},onChangeCallback:function(){},clone:function(){return new THREE.Euler(this._x,this._y,this._z,this._order)}}; -THREE.Line3=function(a,b){this.start=void 0!==a?a:new THREE.Vector3;this.end=void 0!==b?b:new THREE.Vector3}; -THREE.Line3.prototype={constructor:THREE.Line3,set:function(a,b){this.start.copy(a);this.end.copy(b);return this},copy:function(a){this.start.copy(a.start);this.end.copy(a.end);return this},center:function(a){return(a||new THREE.Vector3).addVectors(this.start,this.end).multiplyScalar(0.5)},delta:function(a){return(a||new THREE.Vector3).subVectors(this.end,this.start)},distanceSq:function(){return this.start.distanceToSquared(this.end)},distance:function(){return this.start.distanceTo(this.end)},at:function(a, -b){var c=b||new THREE.Vector3;return this.delta(c).multiplyScalar(a).add(this.start)},closestPointToPointParameter:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c,d){a.subVectors(c,this.start);b.subVectors(this.end,this.start);var e=b.dot(b),e=b.dot(a)/e;d&&(e=THREE.Math.clamp(e,0,1));return e}}(),closestPointToPoint:function(a,b,c){a=this.closestPointToPointParameter(a,b);c=c||new THREE.Vector3;return this.delta(c).multiplyScalar(a).add(this.start)},applyMatrix4:function(a){this.start.applyMatrix4(a); -this.end.applyMatrix4(a);return this},equals:function(a){return a.start.equals(this.start)&&a.end.equals(this.end)},clone:function(){return(new THREE.Line3).copy(this)}};THREE.Box2=function(a,b){this.min=void 0!==a?a:new THREE.Vector2(Infinity,Infinity);this.max=void 0!==b?b:new THREE.Vector2(-Infinity,-Infinity)}; -THREE.Box2.prototype={constructor:THREE.Box2,set:function(a,b){this.min.copy(a);this.max.copy(b);return this},setFromPoints:function(a){this.makeEmpty();for(var b=0,c=a.length;bthis.max.x||a.ythis.max.y?!1:!0},containsBox:function(a){return this.min.x<=a.min.x&&a.max.x<=this.max.x&&this.min.y<=a.min.y&&a.max.y<=this.max.y?!0:!1},getParameter:function(a,b){return(b||new THREE.Vector2).set((a.x-this.min.x)/(this.max.x-this.min.x),(a.y-this.min.y)/(this.max.y-this.min.y))},isIntersectionBox:function(a){return a.max.xthis.max.x||a.max.y -this.max.y?!1:!0},clampPoint:function(a,b){return(b||new THREE.Vector2).copy(a).clamp(this.min,this.max)},distanceToPoint:function(){var a=new THREE.Vector2;return function(b){return a.copy(b).clamp(this.min,this.max).sub(b).length()}}(),intersect:function(a){this.min.max(a.min);this.max.min(a.max);return this},union:function(a){this.min.min(a.min);this.max.max(a.max);return this},translate:function(a){this.min.add(a);this.max.add(a);return this},equals:function(a){return a.min.equals(this.min)&& -a.max.equals(this.max)},clone:function(){return(new THREE.Box2).copy(this)}};THREE.Box3=function(a,b){this.min=void 0!==a?a:new THREE.Vector3(Infinity,Infinity,Infinity);this.max=void 0!==b?b:new THREE.Vector3(-Infinity,-Infinity,-Infinity)}; -THREE.Box3.prototype={constructor:THREE.Box3,set:function(a,b){this.min.copy(a);this.max.copy(b);return this},setFromPoints:function(a){this.makeEmpty();for(var b=0,c=a.length;bthis.max.x||a.ythis.max.y|| -a.zthis.max.z?!1:!0},containsBox:function(a){return this.min.x<=a.min.x&&a.max.x<=this.max.x&&this.min.y<=a.min.y&&a.max.y<=this.max.y&&this.min.z<=a.min.z&&a.max.z<=this.max.z?!0:!1},getParameter:function(a,b){return(b||new THREE.Vector3).set((a.x-this.min.x)/(this.max.x-this.min.x),(a.y-this.min.y)/(this.max.y-this.min.y),(a.z-this.min.z)/(this.max.z-this.min.z))},isIntersectionBox:function(a){return a.max.xthis.max.x||a.max.ythis.max.y|| -a.max.zthis.max.z?!1:!0},clampPoint:function(a,b){return(b||new THREE.Vector3).copy(a).clamp(this.min,this.max)},distanceToPoint:function(){var a=new THREE.Vector3;return function(b){return a.copy(b).clamp(this.min,this.max).sub(b).length()}}(),getBoundingSphere:function(){var a=new THREE.Vector3;return function(b){b=b||new THREE.Sphere;b.center=this.center();b.radius=0.5*this.size(a).length();return b}}(),intersect:function(a){this.min.max(a.min);this.max.min(a.max);return this}, -union:function(a){this.min.min(a.min);this.max.max(a.max);return this},applyMatrix4:function(){var a=[new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3,new THREE.Vector3];return function(b){a[0].set(this.min.x,this.min.y,this.min.z).applyMatrix4(b);a[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(b);a[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(b);a[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(b); -a[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(b);a[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(b);a[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(b);a[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(b);this.makeEmpty();this.setFromPoints(a);return this}}(),translate:function(a){this.min.add(a);this.max.add(a);return this},equals:function(a){return a.min.equals(this.min)&&a.max.equals(this.max)},clone:function(){return(new THREE.Box3).copy(this)}}; -THREE.Matrix3=function(a,b,c,d,e,f,g,h,k){var l=this.elements=new Float32Array(9);l[0]=void 0!==a?a:1;l[3]=b||0;l[6]=c||0;l[1]=d||0;l[4]=void 0!==e?e:1;l[7]=f||0;l[2]=g||0;l[5]=h||0;l[8]=void 0!==k?k:1}; -THREE.Matrix3.prototype={constructor:THREE.Matrix3,set:function(a,b,c,d,e,f,g,h,k){var l=this.elements;l[0]=a;l[3]=b;l[6]=c;l[1]=d;l[4]=e;l[7]=f;l[2]=g;l[5]=h;l[8]=k;return this},identity:function(){this.set(1,0,0,0,1,0,0,0,1);return this},copy:function(a){a=a.elements;this.set(a[0],a[3],a[6],a[1],a[4],a[7],a[2],a[5],a[8]);return this},multiplyVector3:function(a){console.warn("THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.");return a.applyMatrix3(this)}, -multiplyVector3Array:function(a){console.warn("THREE.Matrix3: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.");return this.applyToVector3Array(a)},applyToVector3Array:function(){var a=new THREE.Vector3;return function(b,c,d){void 0===c&&(c=0);void 0===d&&(d=b.length);for(var e=0;ethis.determinant()&&(g=-g);c.x=f[12];c.y=f[13];c.z=f[14];b.elements.set(this.elements);c=1/g;var f=1/h,l=1/k;b.elements[0]*=c;b.elements[1]*= -c;b.elements[2]*=c;b.elements[4]*=f;b.elements[5]*=f;b.elements[6]*=f;b.elements[8]*=l;b.elements[9]*=l;b.elements[10]*=l;d.setFromRotationMatrix(b);e.x=g;e.y=h;e.z=k;return this}}(),makeFrustum:function(a,b,c,d,e,f){var g=this.elements;g[0]=2*e/(b-a);g[4]=0;g[8]=(b+a)/(b-a);g[12]=0;g[1]=0;g[5]=2*e/(d-c);g[9]=(d+c)/(d-c);g[13]=0;g[2]=0;g[6]=0;g[10]=-(f+e)/(f-e);g[14]=-2*f*e/(f-e);g[3]=0;g[7]=0;g[11]=-1;g[15]=0;return this},makePerspective:function(a,b,c,d){a=c*Math.tan(THREE.Math.degToRad(0.5*a)); -var e=-a;return this.makeFrustum(e*b,a*b,e,a,c,d)},makeOrthographic:function(a,b,c,d,e,f){var g=this.elements,h=b-a,k=c-d,l=f-e;g[0]=2/h;g[4]=0;g[8]=0;g[12]=-((b+a)/h);g[1]=0;g[5]=2/k;g[9]=0;g[13]=-((c+d)/k);g[2]=0;g[6]=0;g[10]=-2/l;g[14]=-((f+e)/l);g[3]=0;g[7]=0;g[11]=0;g[15]=1;return this},fromArray:function(a){this.elements.set(a);return this},toArray:function(){var a=this.elements;return[a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9],a[10],a[11],a[12],a[13],a[14],a[15]]},clone:function(){var a= -this.elements;return new THREE.Matrix4(a[0],a[4],a[8],a[12],a[1],a[5],a[9],a[13],a[2],a[6],a[10],a[14],a[3],a[7],a[11],a[15])}};THREE.Ray=function(a,b){this.origin=void 0!==a?a:new THREE.Vector3;this.direction=void 0!==b?b:new THREE.Vector3}; -THREE.Ray.prototype={constructor:THREE.Ray,set:function(a,b){this.origin.copy(a);this.direction.copy(b);return this},copy:function(a){this.origin.copy(a.origin);this.direction.copy(a.direction);return this},at:function(a,b){return(b||new THREE.Vector3).copy(this.direction).multiplyScalar(a).add(this.origin)},recast:function(){var a=new THREE.Vector3;return function(b){this.origin.copy(this.at(b,a));return this}}(),closestPointToPoint:function(a,b){var c=b||new THREE.Vector3;c.subVectors(a,this.origin); -var d=c.dot(this.direction);return 0>d?c.copy(this.origin):c.copy(this.direction).multiplyScalar(d).add(this.origin)},distanceToPoint:function(){var a=new THREE.Vector3;return function(b){var c=a.subVectors(b,this.origin).dot(this.direction);if(0>c)return this.origin.distanceTo(b);a.copy(this.direction).multiplyScalar(c).add(this.origin);return a.distanceTo(b)}}(),distanceSqToSegment:function(a,b,c,d){var e=a.clone().add(b).multiplyScalar(0.5),f=b.clone().sub(a).normalize(),g=0.5*a.distanceTo(b), -h=this.origin.clone().sub(e);a=-this.direction.dot(f);b=h.dot(this.direction);var k=-h.dot(f),l=h.lengthSq(),n=Math.abs(1-a*a),q,r;0<=n?(h=a*k-b,q=a*b-k,r=g*n,0<=h?q>=-r?q<=r?(g=1/n,h*=g,q*=g,a=h*(h+a*q+2*b)+q*(a*h+q+2*k)+l):(q=g,h=Math.max(0,-(a*q+b)),a=-h*h+q*(q+2*k)+l):(q=-g,h=Math.max(0,-(a*q+b)),a=-h*h+q*(q+2*k)+l):q<=-r?(h=Math.max(0,-(-a*g+b)),q=0f)return null;f=Math.sqrt(f-e);e=d-f; -d+=f;return 0>e&&0>d?null:0>e?this.at(d,c):this.at(e,c)}}(),isIntersectionPlane:function(a){var b=a.distanceToPoint(this.origin);return 0===b||0>a.normal.dot(this.direction)*b?!0:!1},distanceToPlane:function(a){var b=a.normal.dot(this.direction);if(0==b)return 0==a.distanceToPoint(this.origin)?0:null;a=-(this.origin.dot(a.normal)+a.constant)/b;return 0<=a?a:null},intersectPlane:function(a,b){var c=this.distanceToPlane(a);return null===c?null:this.at(c,b)},isIntersectionBox:function(){var a=new THREE.Vector3; -return function(b){return null!==this.intersectBox(b,a)}}(),intersectBox:function(a,b){var c,d,e,f,g;d=1/this.direction.x;f=1/this.direction.y;g=1/this.direction.z;var h=this.origin;0<=d?(c=(a.min.x-h.x)*d,d*=a.max.x-h.x):(c=(a.max.x-h.x)*d,d*=a.min.x-h.x);0<=f?(e=(a.min.y-h.y)*f,f*=a.max.y-h.y):(e=(a.max.y-h.y)*f,f*=a.min.y-h.y);if(c>f||e>d)return null;if(e>c||c!==c)c=e;if(fg||e>d)return null;if(e>c||c!== -c)c=e;if(gd?null:this.at(0<=c?c:d,b)},intersectTriangle:function(){var a=new THREE.Vector3,b=new THREE.Vector3,c=new THREE.Vector3,d=new THREE.Vector3;return function(e,f,g,h,k){b.subVectors(f,e);c.subVectors(g,e);d.crossVectors(b,c);f=this.direction.dot(d);if(0f)h=-1,f=-f;else return null;a.subVectors(this.origin,e);e=h*this.direction.dot(c.crossVectors(a,c));if(0>e)return null;g=h*this.direction.dot(b.cross(a));if(0>g||e+g>f)return null; -e=-h*a.dot(d);return 0>e?null:this.at(e/f,k)}}(),applyMatrix4:function(a){this.direction.add(this.origin).applyMatrix4(a);this.origin.applyMatrix4(a);this.direction.sub(this.origin);this.direction.normalize();return this},equals:function(a){return a.origin.equals(this.origin)&&a.direction.equals(this.direction)},clone:function(){return(new THREE.Ray).copy(this)}};THREE.Sphere=function(a,b){this.center=void 0!==a?a:new THREE.Vector3;this.radius=void 0!==b?b:0}; -THREE.Sphere.prototype={constructor:THREE.Sphere,set:function(a,b){this.center.copy(a);this.radius=b;return this},setFromPoints:function(){var a=new THREE.Box3;return function(b,c){var d=this.center;void 0!==c?d.copy(c):a.setFromPoints(b).center(d);for(var e=0,f=0,g=b.length;f=this.radius},containsPoint:function(a){return a.distanceToSquared(this.center)<= -this.radius*this.radius},distanceToPoint:function(a){return a.distanceTo(this.center)-this.radius},intersectsSphere:function(a){var b=this.radius+a.radius;return a.center.distanceToSquared(this.center)<=b*b},clampPoint:function(a,b){var c=this.center.distanceToSquared(a),d=b||new THREE.Vector3;d.copy(a);c>this.radius*this.radius&&(d.sub(this.center).normalize(),d.multiplyScalar(this.radius).add(this.center));return d},getBoundingBox:function(a){a=a||new THREE.Box3;a.set(this.center,this.center);a.expandByScalar(this.radius); -return a},applyMatrix4:function(a){this.center.applyMatrix4(a);this.radius*=a.getMaxScaleOnAxis();return this},translate:function(a){this.center.add(a);return this},equals:function(a){return a.center.equals(this.center)&&a.radius===this.radius},clone:function(){return(new THREE.Sphere).copy(this)}}; -THREE.Frustum=function(a,b,c,d,e,f){this.planes=[void 0!==a?a:new THREE.Plane,void 0!==b?b:new THREE.Plane,void 0!==c?c:new THREE.Plane,void 0!==d?d:new THREE.Plane,void 0!==e?e:new THREE.Plane,void 0!==f?f:new THREE.Plane]}; -THREE.Frustum.prototype={constructor:THREE.Frustum,set:function(a,b,c,d,e,f){var g=this.planes;g[0].copy(a);g[1].copy(b);g[2].copy(c);g[3].copy(d);g[4].copy(e);g[5].copy(f);return this},copy:function(a){for(var b=this.planes,c=0;6>c;c++)b[c].copy(a.planes[c]);return this},setFromMatrix:function(a){var b=this.planes,c=a.elements;a=c[0];var d=c[1],e=c[2],f=c[3],g=c[4],h=c[5],k=c[6],l=c[7],n=c[8],q=c[9],r=c[10],t=c[11],s=c[12],p=c[13],v=c[14],c=c[15];b[0].setComponents(f-a,l-g,t-n,c-s).normalize();b[1].setComponents(f+ -a,l+g,t+n,c+s).normalize();b[2].setComponents(f+d,l+h,t+q,c+p).normalize();b[3].setComponents(f-d,l-h,t-q,c-p).normalize();b[4].setComponents(f-e,l-k,t-r,c-v).normalize();b[5].setComponents(f+e,l+k,t+r,c+v).normalize();return this},intersectsObject:function(){var a=new THREE.Sphere;return function(b){var c=b.geometry;null===c.boundingSphere&&c.computeBoundingSphere();a.copy(c.boundingSphere);a.applyMatrix4(b.matrixWorld);return this.intersectsSphere(a)}}(),intersectsSphere:function(a){var b=this.planes, -c=a.center;a=-a.radius;for(var d=0;6>d;d++)if(b[d].distanceToPoint(c)e;e++){var f=d[e];a.x=0g&&0>f)return!1}return!0}}(), -containsPoint:function(a){for(var b=this.planes,c=0;6>c;c++)if(0>b[c].distanceToPoint(a))return!1;return!0},clone:function(){return(new THREE.Frustum).copy(this)}};THREE.Plane=function(a,b){this.normal=void 0!==a?a:new THREE.Vector3(1,0,0);this.constant=void 0!==b?b:0}; -THREE.Plane.prototype={constructor:THREE.Plane,set:function(a,b){this.normal.copy(a);this.constant=b;return this},setComponents:function(a,b,c,d){this.normal.set(a,b,c);this.constant=d;return this},setFromNormalAndCoplanarPoint:function(a,b){this.normal.copy(a);this.constant=-b.dot(this.normal);return this},setFromCoplanarPoints:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c,d,e){d=a.subVectors(e,d).cross(b.subVectors(c,d)).normalize();this.setFromNormalAndCoplanarPoint(d, -c);return this}}(),copy:function(a){this.normal.copy(a.normal);this.constant=a.constant;return this},normalize:function(){var a=1/this.normal.length();this.normal.multiplyScalar(a);this.constant*=a;return this},negate:function(){this.constant*=-1;this.normal.negate();return this},distanceToPoint:function(a){return this.normal.dot(a)+this.constant},distanceToSphere:function(a){return this.distanceToPoint(a.center)-a.radius},projectPoint:function(a,b){return this.orthoPoint(a,b).sub(a).negate()},orthoPoint:function(a, -b){var c=this.distanceToPoint(a);return(b||new THREE.Vector3).copy(this.normal).multiplyScalar(c)},isIntersectionLine:function(a){var b=this.distanceToPoint(a.start);a=this.distanceToPoint(a.end);return 0>b&&0a&&0f||1e;e++)8==e||13==e||18==e||23==e?b[e]="-":14==e?b[e]="4":(2>=c&&(c=33554432+16777216*Math.random()|0),d=c&15,c>>=4,b[e]=a[19==e?d&3|8:d]);return b.join("")}}(),clamp:function(a,b,c){return ac?c:a},clampBottom:function(a,b){return a=c)return 1;a=(a-b)/(c-b);return a*a*(3-2*a)},smootherstep:function(a,b,c){if(a<=b)return 0;if(a>=c)return 1;a=(a-b)/(c-b);return a*a*a*(a*(6*a-15)+10)},random16:function(){return(65280*Math.random()+255*Math.random())/65535},randInt:function(a,b){return a+Math.floor(Math.random()*(b-a+1))},randFloat:function(a,b){return a+Math.random()*(b-a)},randFloatSpread:function(a){return a*(0.5-Math.random())},sign:function(a){return 0>a?-1:0this.points.length-2?this.points.length-1:f+1;c[3]=f>this.points.length-3?this.points.length-1: -f+2;l=this.points[c[0]];n=this.points[c[1]];q=this.points[c[2]];r=this.points[c[3]];h=g*g;k=g*h;d.x=b(l.x,n.x,q.x,r.x,g,h,k);d.y=b(l.y,n.y,q.y,r.y,g,h,k);d.z=b(l.z,n.z,q.z,r.z,g,h,k);return d};this.getControlPointsArray=function(){var a,b,c=this.points.length,d=[];for(a=0;a=b.x+b.y}}(); -THREE.Triangle.prototype={constructor:THREE.Triangle,set:function(a,b,c){this.a.copy(a);this.b.copy(b);this.c.copy(c);return this},setFromPointsAndIndices:function(a,b,c,d){this.a.copy(a[b]);this.b.copy(a[c]);this.c.copy(a[d]);return this},copy:function(a){this.a.copy(a.a);this.b.copy(a.b);this.c.copy(a.c);return this},area:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(){a.subVectors(this.c,this.b);b.subVectors(this.a,this.b);return 0.5*a.cross(b).length()}}(),midpoint:function(a){return(a|| -new THREE.Vector3).addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)},normal:function(a){return THREE.Triangle.normal(this.a,this.b,this.c,a)},plane:function(a){return(a||new THREE.Plane).setFromCoplanarPoints(this.a,this.b,this.c)},barycoordFromPoint:function(a,b){return THREE.Triangle.barycoordFromPoint(a,this.a,this.b,this.c,b)},containsPoint:function(a){return THREE.Triangle.containsPoint(a,this.a,this.b,this.c)},equals:function(a){return a.a.equals(this.a)&&a.b.equals(this.b)&&a.c.equals(this.c)}, -clone:function(){return(new THREE.Triangle).copy(this)}};THREE.Clock=function(a){this.autoStart=void 0!==a?a:!0;this.elapsedTime=this.oldTime=this.startTime=0;this.running=!1}; -THREE.Clock.prototype={constructor:THREE.Clock,start:function(){this.oldTime=this.startTime=void 0!==self.performance&&void 0!==self.performance.now?self.performance.now():Date.now();this.running=!0},stop:function(){this.getElapsedTime();this.running=!1},getElapsedTime:function(){this.getDelta();return this.elapsedTime},getDelta:function(){var a=0;this.autoStart&&!this.running&&this.start();if(this.running){var b=void 0!==self.performance&&void 0!==self.performance.now?self.performance.now():Date.now(), -a=0.001*(b-this.oldTime);this.oldTime=b;this.elapsedTime+=a}return a}};THREE.EventDispatcher=function(){}; -THREE.EventDispatcher.prototype={constructor:THREE.EventDispatcher,apply:function(a){a.addEventListener=THREE.EventDispatcher.prototype.addEventListener;a.hasEventListener=THREE.EventDispatcher.prototype.hasEventListener;a.removeEventListener=THREE.EventDispatcher.prototype.removeEventListener;a.dispatchEvent=THREE.EventDispatcher.prototype.dispatchEvent},addEventListener:function(a,b){void 0===this._listeners&&(this._listeners={});var c=this._listeners;void 0===c[a]&&(c[a]=[]);-1===c[a].indexOf(b)&& -c[a].push(b)},hasEventListener:function(a,b){if(void 0===this._listeners)return!1;var c=this._listeners;return void 0!==c[a]&&-1!==c[a].indexOf(b)?!0:!1},removeEventListener:function(a,b){if(void 0!==this._listeners){var c=this._listeners[a];if(void 0!==c){var d=c.indexOf(b);-1!==d&&c.splice(d,1)}}},dispatchEvent:function(a){if(void 0!==this._listeners){var b=this._listeners[a.type];if(void 0!==b){a.target=this;for(var c=[],d=b.length,e=0;ee&&0>f||0>g&&0>h)return!1;0>e?c=Math.max(c,e/(e-f)):0>f&&(d=Math.min(d,e/(e-f)));0>g?c=Math.max(c,g/(g-h)):0>h&&(d=Math.min(d,g/(g-h)));if(d=c.x&&-1<=c.y&&1>=c.y&&-1<=c.z&&1>=c.z},l=function(a, -b,c){if(!0===a.visible||!0===b.visible||!0===c.visible)return!0;L[0]=a.positionScreen;L[1]=b.positionScreen;L[2]=c.positionScreen;return E.isIntersectionBox(Q.setFromPoints(L))},q=function(a,b,c){return 0>(c.positionScreen.x-a.positionScreen.x)*(b.positionScreen.y-a.positionScreen.y)-(c.positionScreen.y-a.positionScreen.y)*(b.positionScreen.x-a.positionScreen.x)};return{setObject:function(a){f=a;g=f.material;h.getNormalMatrix(f.matrixWorld);d.length=0;e.length=0},projectVertex:k,checkTriangleVisibility:l, -checkBackfaceCulling:q,pushVertex:function(b,c,d){n=a();n.position.set(b,c,d);k(n)},pushNormal:function(a,b,c){d.push(a,b,c)},pushUv:function(a,b){e.push(a,b)},pushLine:function(a,b){var d=r[a],e=r[b];u=c();u.id=f.id;u.v1.copy(d);u.v2.copy(e);u.z=(d.positionScreen.z+e.positionScreen.z)/2;u.material=f.material;K.elements.push(u)},pushTriangle:function(a,c,k){var n=r[a],p=r[c],t=r[k];if(!1!==l(n,p,t)&&(g.side===THREE.DoubleSide||!0===q(n,p,t))){s=b();s.id=f.id;s.v1.copy(n);s.v2.copy(p);s.v3.copy(t); -s.z=(n.positionScreen.z+p.positionScreen.z+t.positionScreen.z)/3;for(n=0;3>n;n++)p=3*arguments[n],t=s.vertexNormalsModel[n],t.set(d[p],d[p+1],d[p+2]),t.applyMatrix3(h).normalize(),p=2*arguments[n],s.uvs[n].set(e[p],e[p+1]);s.vertexNormalsLength=3;s.material=f.material;K.elements.push(s)}}}};this.projectScene=function(n,t,w,v){I=D=p=0;K.elements.length=0;!0===n.autoUpdate&&n.updateMatrixWorld();void 0===t.parent&&t.updateMatrixWorld();R.copy(t.matrixWorldInverse.getInverse(t.matrixWorld));B.multiplyMatrices(t.projectionMatrix, -R);H.setFromMatrix(B);h=0;K.objects.length=0;K.lights.length=0;n.traverseVisible(function(a){if(a instanceof THREE.Light)K.lights.push(a);else if(a instanceof THREE.Mesh||a instanceof THREE.Line||a instanceof THREE.Sprite)if(!1===a.frustumCulled||!0===H.intersectsObject(a)){if(h===l){var b=new THREE.RenderableObject;k.push(b);l++;h++;g=b}else g=k[h++];g.id=a.id;g.object=a;null!==a.renderDepth?g.z=a.renderDepth:(O.setFromMatrixPosition(a.matrixWorld),O.applyProjection(B),g.z=O.z);K.objects.push(g)}}); -!0===w&&K.objects.sort(e);n=0;for(w=K.objects.length;nva;va++)s.uvs[va].copy(ua[va]); -s.color=ca.color;s.material=qa;s.z=(la.positionScreen.z+ja.positionScreen.z+Fa.positionScreen.z)/3;K.elements.push(s)}}}}}else if(A instanceof THREE.Line)if(x instanceof THREE.BufferGeometry){if(z=x.attributes,void 0!==z.position){G=z.position.array;x=0;for(y=G.length;x=J.z&&(C=d(),C.id=A.id,C.x=J.x*x,C.y=J.y*x,C.z=J.z,C.object=A,C.rotation=A.rotation,C.scale.x=A.scale.x*Math.abs(C.x-(J.x+t.projectionMatrix.elements[0])/(J.w+t.projectionMatrix.elements[12])),C.scale.y=A.scale.y*Math.abs(C.y-(J.y+t.projectionMatrix.elements[5])/ -(J.w+t.projectionMatrix.elements[13])),C.material=A.material,K.elements.push(C)))}!0===v&&K.elements.sort(e);return K}};THREE.Face3=function(a,b,c,d,e,f){this.a=a;this.b=b;this.c=c;this.normal=d instanceof THREE.Vector3?d:new THREE.Vector3;this.vertexNormals=d instanceof Array?d:[];this.color=e instanceof THREE.Color?e:new THREE.Color;this.vertexColors=e instanceof Array?e:[];this.vertexTangents=[];this.materialIndex=void 0!==f?f:0}; -THREE.Face3.prototype={constructor:THREE.Face3,clone:function(){var a=new THREE.Face3(this.a,this.b,this.c);a.normal.copy(this.normal);a.color.copy(this.color);a.materialIndex=this.materialIndex;for(var b=0,c=this.vertexNormals.length;bb.max.x&&(b.max.x=e);fb.max.y&& -(b.max.y=f);gb.max.z&&(b.max.z=g)}}if(void 0===a||0===a.length)this.boundingBox.min.set(0,0,0),this.boundingBox.max.set(0,0,0);(isNaN(this.boundingBox.min.x)||isNaN(this.boundingBox.min.y)||isNaN(this.boundingBox.min.z))&&console.error('THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.')},computeBoundingSphere:function(){var a=new THREE.Box3,b=new THREE.Vector3;return function(){null===this.boundingSphere&& -(this.boundingSphere=new THREE.Sphere);var c=this.attributes.position.array;if(c){a.makeEmpty();for(var d=this.boundingSphere.center,e=0,f=c.length;eBa?-1:1;h[4*a]=Ea.x;h[4*a+1]=Ea.y;h[4*a+2]=Ea.z;h[4*a+3]=Pa}if(void 0===this.attributes.index||void 0===this.attributes.position||void 0===this.attributes.normal||void 0===this.attributes.uv)console.warn("Missing required attributes (index, position, normal or uv) in BufferGeometry.computeTangents()");else{var c=this.attributes.index.array,d=this.attributes.position.array,e=this.attributes.normal.array,f=this.attributes.uv.array,g=d.length/3;void 0===this.attributes.tangent&&(this.attributes.tangent= -{itemSize:4,array:new Float32Array(4*g)});for(var h=this.attributes.tangent.array,k=[],l=[],n=0;np;p++)s=a[3*c+p],-1==r[s]?(q[2*p]=s,q[2*p+1]=-1,n++):r[s]k.index+b)for(k={start:f,count:0,index:g},h.push(k),n=0;6>n;n+=2)p=q[n+1],-1n;n+=2)s=q[n],p=q[n+1],-1===p&&(p=g++),r[s]=p,t[p]=s,e[f++]=p-k.index,k.count++}this.reorderBuffers(e,t,g);return this.offsets=h},merge:function(){console.log("BufferGeometry.merge(): TODO")},normalizeNormals:function(){for(var a=this.attributes.normal.array, -b,c,d,e=0,f=a.length;ed?-1:1,e.vertexTangents[c]=new THREE.Vector4(A.x,A.y,A.z,d);this.hasTangents=!0},computeLineDistances:function(){for(var a=0,b=this.vertices,c=0,d=b.length;cd;d++)if(e[d]==e[(d+1)%3]){a.push(f);break}for(f=a.length-1;0<=f;f--)for(e=a[f],this.faces.splice(e,1),c=0,g=this.faceVertexUvs.length;cc&&(h[f].counter+=1,g=h[f].hash+"_"+h[f].counter,g in this.geometryGroups||(k={id:a++,faces3:[],materialIndex:f,vertices:0,numMorphTargets:l,numMorphNormals:n},this.geometryGroups[g]=k,this.geometryGroupsList.push(k))),this.geometryGroups[g].faces3.push(d),this.geometryGroups[g].vertices+=3}}(),clone:function(){for(var a=new THREE.Geometry,b=this.vertices,c=0,d=b.length;ca.opacity)h.transparent=a.transparent;void 0!==a.depthTest&&(h.depthTest=a.depthTest);void 0!==a.depthWrite&&(h.depthWrite=a.depthWrite);void 0!==a.visible&&(h.visible=a.visible);void 0!==a.flipSided&&(h.side=THREE.BackSide);void 0!==a.doubleSided&&(h.side=THREE.DoubleSide);void 0!==a.wireframe&&(h.wireframe=a.wireframe);void 0!==a.vertexColors&&("face"=== -a.vertexColors?h.vertexColors=THREE.FaceColors:a.vertexColors&&(h.vertexColors=THREE.VertexColors));a.colorDiffuse?h.color=e(a.colorDiffuse):a.DbgColor&&(h.color=a.DbgColor);a.colorSpecular&&(h.specular=e(a.colorSpecular));a.colorAmbient&&(h.ambient=e(a.colorAmbient));a.colorEmissive&&(h.emissive=e(a.colorEmissive));a.transparency&&(h.opacity=a.transparency);a.specularCoef&&(h.shininess=a.specularCoef);a.mapDiffuse&&b&&d(h,"map",a.mapDiffuse,a.mapDiffuseRepeat,a.mapDiffuseOffset,a.mapDiffuseWrap, -a.mapDiffuseAnisotropy);a.mapLight&&b&&d(h,"lightMap",a.mapLight,a.mapLightRepeat,a.mapLightOffset,a.mapLightWrap,a.mapLightAnisotropy);a.mapBump&&b&&d(h,"bumpMap",a.mapBump,a.mapBumpRepeat,a.mapBumpOffset,a.mapBumpWrap,a.mapBumpAnisotropy);a.mapNormal&&b&&d(h,"normalMap",a.mapNormal,a.mapNormalRepeat,a.mapNormalOffset,a.mapNormalWrap,a.mapNormalAnisotropy);a.mapSpecular&&b&&d(h,"specularMap",a.mapSpecular,a.mapSpecularRepeat,a.mapSpecularOffset,a.mapSpecularWrap,a.mapSpecularAnisotropy);a.mapAlpha&& -b&&d(h,"alphaMap",a.mapAlpha,a.mapAlphaRepeat,a.mapAlphaOffset,a.mapAlphaWrap,a.mapAlphaAnisotropy);a.mapBumpScale&&(h.bumpScale=a.mapBumpScale);a.mapNormal?(g=THREE.ShaderLib.normalmap,k=THREE.UniformsUtils.clone(g.uniforms),k.tNormal.value=h.normalMap,a.mapNormalFactor&&k.uNormalScale.value.set(a.mapNormalFactor,a.mapNormalFactor),h.map&&(k.tDiffuse.value=h.map,k.enableDiffuse.value=!0),h.specularMap&&(k.tSpecular.value=h.specularMap,k.enableSpecular.value=!0),h.lightMap&&(k.tAO.value=h.lightMap, -k.enableAO.value=!0),k.diffuse.value.setHex(h.color),k.specular.value.setHex(h.specular),k.ambient.value.setHex(h.ambient),k.shininess.value=h.shininess,void 0!==h.opacity&&(k.opacity.value=h.opacity),g=new THREE.ShaderMaterial({fragmentShader:g.fragmentShader,vertexShader:g.vertexShader,uniforms:k,lights:!0,fog:!0}),h.transparent&&(g.transparent=!0)):g=new THREE[g](h);void 0!==a.DbgName&&(g.name=a.DbgName);return g}}; -THREE.Loader.Handlers={handlers:[],add:function(a,b){this.handlers.push(a,b)},get:function(a){for(var b=0,c=this.handlers.length;bg;g++)r=u[k++],w=v[2*r],r=v[2*r+1],w=new THREE.Vector2(w,r),2!==g&&c.faceVertexUvs[d][h].push(w),0!==g&&c.faceVertexUvs[d][h+1].push(w);q&&(q=3*u[k++],t.normal.set(D[q++],D[q++],D[q]),p.normal.copy(t.normal));if(s)for(d=0;4>d;d++)q=3*u[k++],s=new THREE.Vector3(D[q++], -D[q++],D[q]),2!==d&&t.vertexNormals.push(s),0!==d&&p.vertexNormals.push(s);n&&(n=u[k++],n=A[n],t.color.setHex(n),p.color.setHex(n));if(b)for(d=0;4>d;d++)n=u[k++],n=A[n],2!==d&&t.vertexColors.push(new THREE.Color(n)),0!==d&&p.vertexColors.push(new THREE.Color(n));c.faces.push(t);c.faces.push(p)}else{t=new THREE.Face3;t.a=u[k++];t.b=u[k++];t.c=u[k++];h&&(h=u[k++],t.materialIndex=h);h=c.faces.length;if(d)for(d=0;dg;g++)r=u[k++],w=v[2*r],r=v[2*r+1], -w=new THREE.Vector2(w,r),c.faceVertexUvs[d][h].push(w);q&&(q=3*u[k++],t.normal.set(D[q++],D[q++],D[q]));if(s)for(d=0;3>d;d++)q=3*u[k++],s=new THREE.Vector3(D[q++],D[q++],D[q]),t.vertexNormals.push(s);n&&(n=u[k++],t.color.setHex(A[n]));if(b)for(d=0;3>d;d++)n=u[k++],t.vertexColors.push(new THREE.Color(A[n]));c.faces.push(t)}})(d);(function(){var b=void 0!==a.influencesPerVertex?a.influencesPerVertex:2;if(a.skinWeights)for(var d=0,g=a.skinWeights.length;df)){var r=b.origin.distanceTo(l);rd.far||e.push({distance:r,point:k.clone().applyMatrix4(this.matrixWorld),face:null,faceIndex:null,object:this})}}}();THREE.Line.prototype.clone=function(a){void 0===a&&(a=new THREE.Line(this.geometry,this.material,this.type));THREE.Object3D.prototype.clone.call(this,a);return a}; -THREE.Mesh=function(a,b){THREE.Object3D.call(this);this.geometry=void 0!==a?a:new THREE.Geometry;this.material=void 0!==b?b:new THREE.MeshBasicMaterial({color:16777215*Math.random()});this.updateMorphTargets()};THREE.Mesh.prototype=Object.create(THREE.Object3D.prototype); -THREE.Mesh.prototype.updateMorphTargets=function(){if(void 0!==this.geometry.morphTargets&&0g.far||h.push({distance:C,point:x,indices:[n,q,r],face:null,faceIndex:null,object:this})}}}else for(p=n.position.array,s=k=0,A=p.length;kg.far||h.push({distance:C,point:x,indices:[n,q,r],face:null,faceIndex:null,object:this}))}}else if(k instanceof THREE.Geometry)for(s=this.material instanceof THREE.MeshFaceMaterial,p=!0===s?this.material.materials:null,t=g.precision,v=k.vertices,w=0,u=k.faces.length;wg.far||h.push({distance:C,point:x,face:D,faceIndex:w,object:this}))}}}();THREE.Mesh.prototype.clone=function(a,b){void 0===a&&(a=new THREE.Mesh(this.geometry,this.material));THREE.Object3D.prototype.clone.call(this,a,b);return a};THREE.Bone=function(a){THREE.Object3D.call(this);this.skin=a;this.accumulatedSclWeight=this.accumulatedPosWeight=this.accumulatedRotWeight=0};THREE.Bone.prototype=Object.create(THREE.Object3D.prototype); -THREE.Bone.prototype.updateMatrixWorld=function(a){THREE.Object3D.prototype.updateMatrixWorld.call(this,a);this.accumulatedSclWeight=this.accumulatedPosWeight=this.accumulatedRotWeight=0}; -THREE.Skeleton=function(a,b,c){this.useVertexTexture=void 0!==c?c:!0;this.identityMatrix=new THREE.Matrix4;a=a||[];this.bones=a.slice(0);this.useVertexTexture?(this.boneTextureHeight=this.boneTextureWidth=a=256h.end&&(h.end=e);b||(b=g)}}a.firstAnimation=b}; -THREE.MorphAnimMesh.prototype.setAnimationLabel=function(a,b,c){this.geometry.animations||(this.geometry.animations={});this.geometry.animations[a]={start:b,end:c}};THREE.MorphAnimMesh.prototype.playAnimation=function(a,b){var c=this.geometry.animations[a];c?(this.setFrameRange(c.start,c.end),this.duration=(c.end-c.start)/b*1E3,this.time=0):console.warn("animation["+a+"] undefined")}; -THREE.MorphAnimMesh.prototype.updateAnimation=function(a){var b=this.duration/this.length;this.time+=this.direction*a;if(this.mirroredLoop){if(this.time>this.duration||0>this.time)this.direction*=-1,this.time>this.duration&&(this.time=this.duration,this.directionBackwards=!0),0>this.time&&(this.time=0,this.directionBackwards=!1)}else this.time%=this.duration,0>this.time&&(this.time+=this.duration);a=this.startKeyframe+THREE.Math.clamp(Math.floor(this.time/b),0,this.length-1);a!==this.currentKeyframe&& -(this.morphTargetInfluences[this.lastKeyframe]=0,this.morphTargetInfluences[this.currentKeyframe]=1,this.morphTargetInfluences[a]=0,this.lastKeyframe=this.currentKeyframe,this.currentKeyframe=a);b=this.time%b/b;this.directionBackwards&&(b=1-b);this.morphTargetInfluences[this.currentKeyframe]=b;this.morphTargetInfluences[this.lastKeyframe]=1-b}; -THREE.MorphAnimMesh.prototype.interpolateTargets=function(a,b,c){for(var d=this.morphTargetInfluences,e=0,f=d.length;e=this.objects[d].distance)this.objects[d-1].object.visible=!1,this.objects[d].object.visible=!0;else break;for(;dthis.scale.x||c.push({distance:d,point:this.position,face:null,object:this})}}();THREE.Sprite.prototype.updateMatrix=function(){this.matrix.compose(this.position,this.quaternion,this.scale);this.matrixWorldNeedsUpdate=!0}; -THREE.Sprite.prototype.clone=function(a){void 0===a&&(a=new THREE.Sprite(this.material));THREE.Object3D.prototype.clone.call(this,a);return a};THREE.Particle=THREE.Sprite;THREE.Scene=function(){THREE.Object3D.call(this);this.overrideMaterial=this.fog=null;this.autoUpdate=!0;this.matrixAutoUpdate=!1;this.__lights=[];this.__objectsAdded=[];this.__objectsRemoved=[]};THREE.Scene.prototype=Object.create(THREE.Object3D.prototype); -THREE.Scene.prototype.__addObject=function(a){if(a instanceof THREE.Light)-1===this.__lights.indexOf(a)&&this.__lights.push(a),a.target&&void 0===a.target.parent&&this.add(a.target);else if(!(a instanceof THREE.Camera||a instanceof THREE.Bone)){this.__objectsAdded.push(a);var b=this.__objectsRemoved.indexOf(a);-1!==b&&this.__objectsRemoved.splice(b,1)}this.dispatchEvent({type:"objectAdded",object:a});a.dispatchEvent({type:"addedToScene",scene:this});for(b=0;bE&&O.clearRect(aa.min.x|0,aa.min.y|0,aa.max.x-aa.min.x|0,aa.max.y-aa.min.y|0),0$.positionScreen.z||1<$.positionScreen.z)continue;if(-1>X.positionScreen.z||1T.positionScreen.z||1=S||(S*=V.intensity,R.add(Za.multiplyScalar(S)))):V instanceof THREE.PointLight&&(da=Ga.setFromMatrixPosition(V.matrixWorld),S=ea.dot(Ga.subVectors(da,H).normalize()),0>=S||(S*=0==V.distance?1:1-Math.min(H.distanceTo(da)/V.distance,1),0!=S&&(S*=V.intensity,R.add(Za.multiplyScalar(S)))));G.multiply(Ba).add(Ya);!0===E.wireframe?b(G,E.wireframeLinewidth,E.wireframeLinecap,E.wireframeLinejoin):c(G)}else E instanceof THREE.MeshBasicMaterial||E instanceof THREE.MeshLambertMaterial||E instanceof -THREE.MeshPhongMaterial?null!==E.map?E.map.mapping instanceof THREE.UVMapping&&(ca=J.uvs,f(ya,Ea,Aa,za,Oa,Pa,ca[0].x,ca[0].y,ca[1].x,ca[1].y,ca[2].x,ca[2].y,E.map)):null!==E.envMap?E.envMap.mapping instanceof THREE.SphericalReflectionMapping?(ma.copy(J.vertexNormalsModel[0]).applyMatrix3(Ja),la=0.5*ma.x+0.5,qa=0.5*ma.y+0.5,ma.copy(J.vertexNormalsModel[1]).applyMatrix3(Ja),ua=0.5*ma.x+0.5,ja=0.5*ma.y+0.5,ma.copy(J.vertexNormalsModel[2]).applyMatrix3(Ja),Fa=0.5*ma.x+0.5,va=0.5*ma.y+0.5,f(ya,Ea,Aa,za, -Oa,Pa,la,qa,ua,ja,Fa,va,E.envMap)):E.envMap.mapping instanceof THREE.SphericalRefractionMapping&&(ma.copy(J.vertexNormalsModel[0]).applyMatrix3(Ja),la=-0.5*ma.x+0.5,qa=-0.5*ma.y+0.5,ma.copy(J.vertexNormalsModel[1]).applyMatrix3(Ja),ua=-0.5*ma.x+0.5,ja=-0.5*ma.y+0.5,ma.copy(J.vertexNormalsModel[2]).applyMatrix3(Ja),Fa=-0.5*ma.x+0.5,va=-0.5*ma.y+0.5,f(ya,Ea,Aa,za,Oa,Pa,la,qa,ua,ja,Fa,va,E.envMap)):(G.copy(E.color),E.vertexColors===THREE.FaceColors&&G.multiply(J.color),!0===E.wireframe?b(G,E.wireframeLinewidth, -E.wireframeLinecap,E.wireframeLinejoin):c(G)):(E instanceof THREE.MeshDepthMaterial?G.r=G.g=G.b=1-p(H.positionScreen.z*H.positionScreen.w,oa.near,oa.far):E instanceof THREE.MeshNormalMaterial?(ma.copy(J.normalModel).applyMatrix3(Ja),G.setRGB(ma.x,ma.y,ma.z).multiplyScalar(0.5).addScalar(0.5)):G.setRGB(1,1,1),!0===E.wireframe?b(G,E.wireframeLinewidth,E.wireframeLinecap,E.wireframeLinejoin):c(G))}}aa.union(ra)}}O.setTransform(1,0,0,1,0,0)}}};THREE.ShaderChunk={}; -THREE.ShaderChunk.alphatest_fragment="#ifdef ALPHATEST\n\n\tif ( gl_FragColor.a < ALPHATEST ) discard;\n\n#endif\n";THREE.ShaderChunk.lights_lambert_vertex="vLightFront = vec3( 0.0 );\n\n#ifdef DOUBLE_SIDED\n\n\tvLightBack = vec3( 0.0 );\n\n#endif\n\ntransformedNormal = normalize( transformedNormal );\n\n#if MAX_DIR_LIGHTS > 0\n\nfor( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n\tvec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\n\tvec3 dirVector = normalize( lDirection.xyz );\n\n\tfloat dotProduct = dot( transformedNormal, dirVector );\n\tvec3 directionalLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n\t#ifdef DOUBLE_SIDED\n\n\t\tvec3 directionalLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n\t\t#ifdef WRAP_AROUND\n\n\t\t\tvec3 directionalLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n\t\t#endif\n\n\t#endif\n\n\t#ifdef WRAP_AROUND\n\n\t\tvec3 directionalLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n\t\tdirectionalLightWeighting = mix( directionalLightWeighting, directionalLightWeightingHalf, wrapRGB );\n\n\t\t#ifdef DOUBLE_SIDED\n\n\t\t\tdirectionalLightWeightingBack = mix( directionalLightWeightingBack, directionalLightWeightingHalfBack, wrapRGB );\n\n\t\t#endif\n\n\t#endif\n\n\tvLightFront += directionalLightColor[ i ] * directionalLightWeighting;\n\n\t#ifdef DOUBLE_SIDED\n\n\t\tvLightBack += directionalLightColor[ i ] * directionalLightWeightingBack;\n\n\t#endif\n\n}\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n\tfor( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n\t\tvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\n\t\tvec3 lVector = lPosition.xyz - mvPosition.xyz;\n\n\t\tfloat lDistance = 1.0;\n\t\tif ( pointLightDistance[ i ] > 0.0 )\n\t\t\tlDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\n\n\t\tlVector = normalize( lVector );\n\t\tfloat dotProduct = dot( transformedNormal, lVector );\n\n\t\tvec3 pointLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n\t\t#ifdef DOUBLE_SIDED\n\n\t\t\tvec3 pointLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n\t\t\t#ifdef WRAP_AROUND\n\n\t\t\t\tvec3 pointLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n\t\t\t#endif\n\n\t\t#endif\n\n\t\t#ifdef WRAP_AROUND\n\n\t\t\tvec3 pointLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n\t\t\tpointLightWeighting = mix( pointLightWeighting, pointLightWeightingHalf, wrapRGB );\n\n\t\t\t#ifdef DOUBLE_SIDED\n\n\t\t\t\tpointLightWeightingBack = mix( pointLightWeightingBack, pointLightWeightingHalfBack, wrapRGB );\n\n\t\t\t#endif\n\n\t\t#endif\n\n\t\tvLightFront += pointLightColor[ i ] * pointLightWeighting * lDistance;\n\n\t\t#ifdef DOUBLE_SIDED\n\n\t\t\tvLightBack += pointLightColor[ i ] * pointLightWeightingBack * lDistance;\n\n\t\t#endif\n\n\t}\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n\tfor( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n\t\tvec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\n\t\tvec3 lVector = lPosition.xyz - mvPosition.xyz;\n\n\t\tfloat spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - worldPosition.xyz ) );\n\n\t\tif ( spotEffect > spotLightAngleCos[ i ] ) {\n\n\t\t\tspotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );\n\n\t\t\tfloat lDistance = 1.0;\n\t\t\tif ( spotLightDistance[ i ] > 0.0 )\n\t\t\t\tlDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );\n\n\t\t\tlVector = normalize( lVector );\n\n\t\t\tfloat dotProduct = dot( transformedNormal, lVector );\n\t\t\tvec3 spotLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n\t\t\t#ifdef DOUBLE_SIDED\n\n\t\t\t\tvec3 spotLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n\t\t\t\t#ifdef WRAP_AROUND\n\n\t\t\t\t\tvec3 spotLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n\t\t\t\t#endif\n\n\t\t\t#endif\n\n\t\t\t#ifdef WRAP_AROUND\n\n\t\t\t\tvec3 spotLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n\t\t\t\tspotLightWeighting = mix( spotLightWeighting, spotLightWeightingHalf, wrapRGB );\n\n\t\t\t\t#ifdef DOUBLE_SIDED\n\n\t\t\t\t\tspotLightWeightingBack = mix( spotLightWeightingBack, spotLightWeightingHalfBack, wrapRGB );\n\n\t\t\t\t#endif\n\n\t\t\t#endif\n\n\t\t\tvLightFront += spotLightColor[ i ] * spotLightWeighting * lDistance * spotEffect;\n\n\t\t\t#ifdef DOUBLE_SIDED\n\n\t\t\t\tvLightBack += spotLightColor[ i ] * spotLightWeightingBack * lDistance * spotEffect;\n\n\t\t\t#endif\n\n\t\t}\n\n\t}\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n\tfor( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n\t\tvec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );\n\t\tvec3 lVector = normalize( lDirection.xyz );\n\n\t\tfloat dotProduct = dot( transformedNormal, lVector );\n\n\t\tfloat hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\t\tfloat hemiDiffuseWeightBack = -0.5 * dotProduct + 0.5;\n\n\t\tvLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n\t\t#ifdef DOUBLE_SIDED\n\n\t\t\tvLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );\n\n\t\t#endif\n\n\t}\n\n#endif\n\nvLightFront = vLightFront * diffuse + ambient * ambientLightColor + emissive;\n\n#ifdef DOUBLE_SIDED\n\n\tvLightBack = vLightBack * diffuse + ambient * ambientLightColor + emissive;\n\n#endif"; -THREE.ShaderChunk.map_particle_pars_fragment="#ifdef USE_MAP\n\n\tuniform sampler2D map;\n\n#endif";THREE.ShaderChunk.default_vertex="vec4 mvPosition;\n\n#ifdef USE_SKINNING\n\n\tmvPosition = modelViewMatrix * skinned;\n\n#endif\n\n#if !defined( USE_SKINNING ) && defined( USE_MORPHTARGETS )\n\n\tmvPosition = modelViewMatrix * vec4( morphed, 1.0 );\n\n#endif\n\n#if !defined( USE_SKINNING ) && ! defined( USE_MORPHTARGETS )\n\n\tmvPosition = modelViewMatrix * vec4( position, 1.0 );\n\n#endif\n\ngl_Position = projectionMatrix * mvPosition;"; -THREE.ShaderChunk.map_pars_fragment="#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n\tvarying vec2 vUv;\n\n#endif\n\n#ifdef USE_MAP\n\n\tuniform sampler2D map;\n\n#endif";THREE.ShaderChunk.skinnormal_vertex="#ifdef USE_SKINNING\n\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\n\t#ifdef USE_MORPHNORMALS\n\n\tvec4 skinnedNormal = skinMatrix * vec4( morphedNormal, 0.0 );\n\n\t#else\n\n\tvec4 skinnedNormal = skinMatrix * vec4( normal, 0.0 );\n\n\t#endif\n\n#endif\n"; -THREE.ShaderChunk.logdepthbuf_pars_vertex="#ifdef USE_LOGDEPTHBUF\n\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\n\t\tvarying float vFragDepth;\n\n\t#endif\n\n\tuniform float logDepthBufFC;\n\n#endif";THREE.ShaderChunk.lightmap_pars_vertex="#ifdef USE_LIGHTMAP\n\n\tvarying vec2 vUv2;\n\n#endif";THREE.ShaderChunk.lights_phong_fragment="vec3 normal = normalize( vNormal );\nvec3 viewPosition = normalize( vViewPosition );\n\n#ifdef DOUBLE_SIDED\n\n\tnormal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n\n#endif\n\n#ifdef USE_NORMALMAP\n\n\tnormal = perturbNormal2Arb( -vViewPosition, normal );\n\n#elif defined( USE_BUMPMAP )\n\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n\tvec3 pointDiffuse = vec3( 0.0 );\n\tvec3 pointSpecular = vec3( 0.0 );\n\n\tfor ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n\t\tvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\n\t\tvec3 lVector = lPosition.xyz + vViewPosition.xyz;\n\n\t\tfloat lDistance = 1.0;\n\t\tif ( pointLightDistance[ i ] > 0.0 )\n\t\t\tlDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\n\n\t\tlVector = normalize( lVector );\n\n\t\t\t\t// diffuse\n\n\t\tfloat dotProduct = dot( normal, lVector );\n\n\t\t#ifdef WRAP_AROUND\n\n\t\t\tfloat pointDiffuseWeightFull = max( dotProduct, 0.0 );\n\t\t\tfloat pointDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n\t\t\tvec3 pointDiffuseWeight = mix( vec3( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );\n\n\t\t#else\n\n\t\t\tfloat pointDiffuseWeight = max( dotProduct, 0.0 );\n\n\t\t#endif\n\n\t\tpointDiffuse += diffuse * pointLightColor[ i ] * pointDiffuseWeight * lDistance;\n\n\t\t\t\t// specular\n\n\t\tvec3 pointHalfVector = normalize( lVector + viewPosition );\n\t\tfloat pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );\n\t\tfloat pointSpecularWeight = specularStrength * max( pow( pointDotNormalHalf, shininess ), 0.0 );\n\n\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n\t\tvec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, pointHalfVector ), 0.0 ), 5.0 );\n\t\tpointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance * specularNormalization;\n\n\t}\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n\tvec3 spotDiffuse = vec3( 0.0 );\n\tvec3 spotSpecular = vec3( 0.0 );\n\n\tfor ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n\t\tvec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\n\t\tvec3 lVector = lPosition.xyz + vViewPosition.xyz;\n\n\t\tfloat lDistance = 1.0;\n\t\tif ( spotLightDistance[ i ] > 0.0 )\n\t\t\tlDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );\n\n\t\tlVector = normalize( lVector );\n\n\t\tfloat spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );\n\n\t\tif ( spotEffect > spotLightAngleCos[ i ] ) {\n\n\t\t\tspotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );\n\n\t\t\t\t\t// diffuse\n\n\t\t\tfloat dotProduct = dot( normal, lVector );\n\n\t\t\t#ifdef WRAP_AROUND\n\n\t\t\t\tfloat spotDiffuseWeightFull = max( dotProduct, 0.0 );\n\t\t\t\tfloat spotDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n\t\t\t\tvec3 spotDiffuseWeight = mix( vec3( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );\n\n\t\t\t#else\n\n\t\t\t\tfloat spotDiffuseWeight = max( dotProduct, 0.0 );\n\n\t\t\t#endif\n\n\t\t\tspotDiffuse += diffuse * spotLightColor[ i ] * spotDiffuseWeight * lDistance * spotEffect;\n\n\t\t\t\t\t// specular\n\n\t\t\tvec3 spotHalfVector = normalize( lVector + viewPosition );\n\t\t\tfloat spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );\n\t\t\tfloat spotSpecularWeight = specularStrength * max( pow( spotDotNormalHalf, shininess ), 0.0 );\n\n\t\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n\t\t\tvec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, spotHalfVector ), 0.0 ), 5.0 );\n\t\t\tspotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * specularNormalization * spotEffect;\n\n\t\t}\n\n\t}\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n\tvec3 dirDiffuse = vec3( 0.0 );\n\tvec3 dirSpecular = vec3( 0.0 );\n\n\tfor( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n\t\tvec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\n\t\tvec3 dirVector = normalize( lDirection.xyz );\n\n\t\t\t\t// diffuse\n\n\t\tfloat dotProduct = dot( normal, dirVector );\n\n\t\t#ifdef WRAP_AROUND\n\n\t\t\tfloat dirDiffuseWeightFull = max( dotProduct, 0.0 );\n\t\t\tfloat dirDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n\t\t\tvec3 dirDiffuseWeight = mix( vec3( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), wrapRGB );\n\n\t\t#else\n\n\t\t\tfloat dirDiffuseWeight = max( dotProduct, 0.0 );\n\n\t\t#endif\n\n\t\tdirDiffuse += diffuse * directionalLightColor[ i ] * dirDiffuseWeight;\n\n\t\t// specular\n\n\t\tvec3 dirHalfVector = normalize( dirVector + viewPosition );\n\t\tfloat dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );\n\t\tfloat dirSpecularWeight = specularStrength * max( pow( dirDotNormalHalf, shininess ), 0.0 );\n\n\t\t/*\n\t\t// fresnel term from skin shader\n\t\tconst float F0 = 0.128;\n\n\t\tfloat base = 1.0 - dot( viewPosition, dirHalfVector );\n\t\tfloat exponential = pow( base, 5.0 );\n\n\t\tfloat fresnel = exponential + F0 * ( 1.0 - exponential );\n\t\t*/\n\n\t\t/*\n\t\t// fresnel term from fresnel shader\n\t\tconst float mFresnelBias = 0.08;\n\t\tconst float mFresnelScale = 0.3;\n\t\tconst float mFresnelPower = 5.0;\n\n\t\tfloat fresnel = mFresnelBias + mFresnelScale * pow( 1.0 + dot( normalize( -viewPosition ), normal ), mFresnelPower );\n\t\t*/\n\n\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n\t\t// \t\tdirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization * fresnel;\n\n\t\tvec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( dirVector, dirHalfVector ), 0.0 ), 5.0 );\n\t\tdirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;\n\n\n\t}\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n\tvec3 hemiDiffuse = vec3( 0.0 );\n\tvec3 hemiSpecular = vec3( 0.0 );\n\n\tfor( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n\t\tvec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );\n\t\tvec3 lVector = normalize( lDirection.xyz );\n\n\t\t// diffuse\n\n\t\tfloat dotProduct = dot( normal, lVector );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n\t\tvec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n\t\themiDiffuse += diffuse * hemiColor;\n\n\t\t// specular (sky light)\n\n\t\tvec3 hemiHalfVectorSky = normalize( lVector + viewPosition );\n\t\tfloat hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;\n\t\tfloat hemiSpecularWeightSky = specularStrength * max( pow( max( hemiDotNormalHalfSky, 0.0 ), shininess ), 0.0 );\n\n\t\t// specular (ground light)\n\n\t\tvec3 lVectorGround = -lVector;\n\n\t\tvec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );\n\t\tfloat hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;\n\t\tfloat hemiSpecularWeightGround = specularStrength * max( pow( max( hemiDotNormalHalfGround, 0.0 ), shininess ), 0.0 );\n\n\t\tfloat dotProductGround = dot( normal, lVectorGround );\n\n\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n\t\tvec3 schlickSky = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, hemiHalfVectorSky ), 0.0 ), 5.0 );\n\t\tvec3 schlickGround = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 0.0 ), 5.0 );\n\t\themiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );\n\n\t}\n\n#endif\n\nvec3 totalDiffuse = vec3( 0.0 );\nvec3 totalSpecular = vec3( 0.0 );\n\n#if MAX_DIR_LIGHTS > 0\n\n\ttotalDiffuse += dirDiffuse;\n\ttotalSpecular += dirSpecular;\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n\ttotalDiffuse += hemiDiffuse;\n\ttotalSpecular += hemiSpecular;\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n\ttotalDiffuse += pointDiffuse;\n\ttotalSpecular += pointSpecular;\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n\ttotalDiffuse += spotDiffuse;\n\ttotalSpecular += spotSpecular;\n\n#endif\n\n#ifdef METAL\n\n\tgl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient + totalSpecular );\n\n#else\n\n\tgl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient ) + totalSpecular;\n\n#endif"; -THREE.ShaderChunk.fog_pars_fragment="#ifdef USE_FOG\n\n\tuniform vec3 fogColor;\n\n\t#ifdef FOG_EXP2\n\n\t\tuniform float fogDensity;\n\n\t#else\n\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n\n#endif";THREE.ShaderChunk.morphnormal_vertex="#ifdef USE_MORPHNORMALS\n\n\tvec3 morphedNormal = vec3( 0.0 );\n\n\tmorphedNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n\tmorphedNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n\tmorphedNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n\tmorphedNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n\n\tmorphedNormal += normal;\n\n#endif"; -THREE.ShaderChunk.envmap_pars_fragment="#ifdef USE_ENVMAP\n\n\tuniform float reflectivity;\n\tuniform samplerCube envMap;\n\tuniform float flipEnvMap;\n\tuniform int combine;\n\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\n\n\t\tuniform bool useRefract;\n\t\tuniform float refractionRatio;\n\n\t#else\n\n\t\tvarying vec3 vReflect;\n\n\t#endif\n\n#endif";THREE.ShaderChunk.logdepthbuf_fragment="#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n\n\tgl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n\n#endif"; -THREE.ShaderChunk.normalmap_pars_fragment="#ifdef USE_NORMALMAP\n\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n\n\t\t\t// Per-Pixel Tangent Space Normal Mapping\n\t\t\t// http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html\n\n\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\n\t\tvec3 q0 = dFdx( eye_pos.xyz );\n\t\tvec3 q1 = dFdy( eye_pos.xyz );\n\t\tvec2 st0 = dFdx( vUv.st );\n\t\tvec2 st1 = dFdy( vUv.st );\n\n\t\tvec3 S = normalize( q0 * st1.t - q1 * st0.t );\n\t\tvec3 T = normalize( -q0 * st1.s + q1 * st0.s );\n\t\tvec3 N = normalize( surf_norm );\n\n\t\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t\tmapN.xy = normalScale * mapN.xy;\n\t\tmat3 tsn = mat3( S, T, N );\n\t\treturn normalize( tsn * mapN );\n\n\t}\n\n#endif\n"; -THREE.ShaderChunk.lights_phong_pars_vertex="#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n\tvarying vec3 vWorldPosition;\n\n#endif\n";THREE.ShaderChunk.lightmap_pars_fragment="#ifdef USE_LIGHTMAP\n\n\tvarying vec2 vUv2;\n\tuniform sampler2D lightMap;\n\n#endif";THREE.ShaderChunk.shadowmap_vertex="#ifdef USE_SHADOWMAP\n\n\tfor( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n\t\tvShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n\n\t}\n\n#endif"; -THREE.ShaderChunk.lights_phong_vertex="#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n\tvWorldPosition = worldPosition.xyz;\n\n#endif";THREE.ShaderChunk.map_fragment="#ifdef USE_MAP\n\n\tvec4 texelColor = texture2D( map, vUv );\n\n\t#ifdef GAMMA_INPUT\n\n\t\ttexelColor.xyz *= texelColor.xyz;\n\n\t#endif\n\n\tgl_FragColor = gl_FragColor * texelColor;\n\n#endif";THREE.ShaderChunk.lightmap_vertex="#ifdef USE_LIGHTMAP\n\n\tvUv2 = uv2;\n\n#endif"; -THREE.ShaderChunk.map_particle_fragment="#ifdef USE_MAP\n\n\tgl_FragColor = gl_FragColor * texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) );\n\n#endif";THREE.ShaderChunk.color_pars_fragment="#ifdef USE_COLOR\n\n\tvarying vec3 vColor;\n\n#endif\n";THREE.ShaderChunk.color_vertex="#ifdef USE_COLOR\n\n\t#ifdef GAMMA_INPUT\n\n\t\tvColor = color * color;\n\n\t#else\n\n\t\tvColor = color;\n\n\t#endif\n\n#endif";THREE.ShaderChunk.skinning_vertex="#ifdef USE_SKINNING\n\n\t#ifdef USE_MORPHTARGETS\n\n\tvec4 skinVertex = bindMatrix * vec4( morphed, 1.0 );\n\n\t#else\n\n\tvec4 skinVertex = bindMatrix * vec4( position, 1.0 );\n\n\t#endif\n\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\tskinned = bindMatrixInverse * skinned;\n\n#endif\n"; -THREE.ShaderChunk.envmap_pars_vertex="#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP )\n\n\tvarying vec3 vReflect;\n\n\tuniform float refractionRatio;\n\tuniform bool useRefract;\n\n#endif\n";THREE.ShaderChunk.linear_to_gamma_fragment="#ifdef GAMMA_OUTPUT\n\n\tgl_FragColor.xyz = sqrt( gl_FragColor.xyz );\n\n#endif";THREE.ShaderChunk.color_pars_vertex="#ifdef USE_COLOR\n\n\tvarying vec3 vColor;\n\n#endif";THREE.ShaderChunk.lights_lambert_pars_vertex="uniform vec3 ambient;\nuniform vec3 diffuse;\nuniform vec3 emissive;\n\nuniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n\tuniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n\tuniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n\tuniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n\tuniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n\tuniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n\tuniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n\tuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n\tuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n\tuniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n\tuniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n\tuniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#ifdef WRAP_AROUND\n\n\tuniform vec3 wrapRGB;\n\n#endif\n"; -THREE.ShaderChunk.map_pars_vertex="#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n\tvarying vec2 vUv;\n\tuniform vec4 offsetRepeat;\n\n#endif\n";THREE.ShaderChunk.envmap_fragment="#ifdef USE_ENVMAP\n\n\tvec3 reflectVec;\n\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\n\n\t\tvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\n\t\t// http://en.wikibooks.org/wiki/GLSL_Programming/Applying_Matrix_Transformations\n\t\t// Transforming Normal Vectors with the Inverse Transformation\n\n\t\tvec3 worldNormal = normalize( vec3( vec4( normal, 0.0 ) * viewMatrix ) );\n\n\t\tif ( useRefract ) {\n\n\t\t\treflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\n\t\t} else { \n\n\t\t\treflectVec = reflect( cameraToVertex, worldNormal );\n\n\t\t}\n\n\t#else\n\n\t\treflectVec = vReflect;\n\n\t#endif\n\n\t#ifdef DOUBLE_SIDED\n\n\t\tfloat flipNormal = ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n\t\tvec4 cubeColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n\t#else\n\n\t\tvec4 cubeColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n\t#endif\n\n\t#ifdef GAMMA_INPUT\n\n\t\tcubeColor.xyz *= cubeColor.xyz;\n\n\t#endif\n\n\tif ( combine == 1 ) {\n\n\t\tgl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularStrength * reflectivity );\n\n\t} else if ( combine == 2 ) {\n\n\t\tgl_FragColor.xyz += cubeColor.xyz * specularStrength * reflectivity;\n\n\t} else {\n\n\t\tgl_FragColor.xyz = mix( gl_FragColor.xyz, gl_FragColor.xyz * cubeColor.xyz, specularStrength * reflectivity );\n\n\t}\n\n#endif"; -THREE.ShaderChunk.specularmap_pars_fragment="#ifdef USE_SPECULARMAP\n\n\tuniform sampler2D specularMap;\n\n#endif";THREE.ShaderChunk.logdepthbuf_vertex="#ifdef USE_LOGDEPTHBUF\n\n\tgl_Position.z = log2(max(1e-6, gl_Position.w + 1.0)) * logDepthBufFC;\n\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\n#else\n\n\t\tgl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;\n\n\t#endif\n\n#endif";THREE.ShaderChunk.morphtarget_pars_vertex="#ifdef USE_MORPHTARGETS\n\n\t#ifndef USE_MORPHNORMALS\n\n\tuniform float morphTargetInfluences[ 8 ];\n\n\t#else\n\n\tuniform float morphTargetInfluences[ 4 ];\n\n\t#endif\n\n#endif"; -THREE.ShaderChunk.specularmap_fragment="float specularStrength;\n\n#ifdef USE_SPECULARMAP\n\n\tvec4 texelSpecular = texture2D( specularMap, vUv );\n\tspecularStrength = texelSpecular.r;\n\n#else\n\n\tspecularStrength = 1.0;\n\n#endif";THREE.ShaderChunk.fog_fragment="#ifdef USE_FOG\n\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\n\t\tfloat depth = gl_FragDepthEXT / gl_FragCoord.w;\n\n\t#else\n\n\t\tfloat depth = gl_FragCoord.z / gl_FragCoord.w;\n\n\t#endif\n\n\t#ifdef FOG_EXP2\n\n\t\tconst float LOG2 = 1.442695;\n\t\tfloat fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );\n\t\tfogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );\n\n\t#else\n\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, depth );\n\n\t#endif\n\t\n\tgl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );\n\n#endif"; -THREE.ShaderChunk.bumpmap_pars_fragment="#ifdef USE_BUMPMAP\n\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\n\t\t\t// Derivative maps - bump mapping unparametrized surfaces by Morten Mikkelsen\n\t\t\t//\thttp://mmikkelsen3d.blogspot.sk/2011/07/derivative-maps.html\n\n\t\t\t// Evaluate the derivative of the height w.r.t. screen-space using forward differencing (listing 2)\n\n\tvec2 dHdxy_fwd() {\n\n\t\tvec2 dSTdx = dFdx( vUv );\n\t\tvec2 dSTdy = dFdy( vUv );\n\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\n\t\treturn vec2( dBx, dBy );\n\n\t}\n\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\n\t\tvec3 vSigmaX = dFdx( surf_pos );\n\t\tvec3 vSigmaY = dFdy( surf_pos );\n\t\tvec3 vN = surf_norm;\t\t// normalized\n\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\n\t\tfloat fDet = dot( vSigmaX, R1 );\n\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\n\t}\n\n#endif"; -THREE.ShaderChunk.defaultnormal_vertex="vec3 objectNormal;\n\n#ifdef USE_SKINNING\n\n\tobjectNormal = skinnedNormal.xyz;\n\n#endif\n\n#if !defined( USE_SKINNING ) && defined( USE_MORPHNORMALS )\n\n\tobjectNormal = morphedNormal;\n\n#endif\n\n#if !defined( USE_SKINNING ) && ! defined( USE_MORPHNORMALS )\n\n\tobjectNormal = normal;\n\n#endif\n\n#ifdef FLIP_SIDED\n\n\tobjectNormal = -objectNormal;\n\n#endif\n\nvec3 transformedNormal = normalMatrix * objectNormal;"; -THREE.ShaderChunk.lights_phong_pars_fragment="uniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n\tuniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n\tuniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n\tuniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n\tuniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n\tuniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n\tuniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n\n\tuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n\tuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n\tuniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n\tuniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n\tuniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n\n\tuniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n\tvarying vec3 vWorldPosition;\n\n#endif\n\n#ifdef WRAP_AROUND\n\n\tuniform vec3 wrapRGB;\n\n#endif\n\nvarying vec3 vViewPosition;\nvarying vec3 vNormal;"; -THREE.ShaderChunk.skinbase_vertex="#ifdef USE_SKINNING\n\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n\n#endif";THREE.ShaderChunk.map_vertex="#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n\tvUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n\n#endif"; -THREE.ShaderChunk.lightmap_fragment="#ifdef USE_LIGHTMAP\n\n\tgl_FragColor = gl_FragColor * texture2D( lightMap, vUv2 );\n\n#endif";THREE.ShaderChunk.shadowmap_pars_vertex="#ifdef USE_SHADOWMAP\n\n\tvarying vec4 vShadowCoord[ MAX_SHADOWS ];\n\tuniform mat4 shadowMatrix[ MAX_SHADOWS ];\n\n#endif";THREE.ShaderChunk.color_fragment="#ifdef USE_COLOR\n\n\tgl_FragColor = gl_FragColor * vec4( vColor, 1.0 );\n\n#endif";THREE.ShaderChunk.morphtarget_vertex="#ifdef USE_MORPHTARGETS\n\n\tvec3 morphed = vec3( 0.0 );\n\tmorphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n\tmorphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n\tmorphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n\tmorphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\n\t#ifndef USE_MORPHNORMALS\n\n\tmorphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n\tmorphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n\tmorphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n\tmorphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\n\t#endif\n\n\tmorphed += position;\n\n#endif"; -THREE.ShaderChunk.envmap_vertex="#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP )\n\n\tvec3 worldNormal = mat3( modelMatrix[ 0 ].xyz, modelMatrix[ 1 ].xyz, modelMatrix[ 2 ].xyz ) * objectNormal;\n\tworldNormal = normalize( worldNormal );\n\n\tvec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\n\tif ( useRefract ) {\n\n\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\n\t} else {\n\n\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\n\t}\n\n#endif"; -THREE.ShaderChunk.shadowmap_fragment="#ifdef USE_SHADOWMAP\n\n\t#ifdef SHADOWMAP_DEBUG\n\n\t\tvec3 frustumColors[3];\n\t\tfrustumColors[0] = vec3( 1.0, 0.5, 0.0 );\n\t\tfrustumColors[1] = vec3( 0.0, 1.0, 0.8 );\n\t\tfrustumColors[2] = vec3( 0.0, 0.5, 1.0 );\n\n\t#endif\n\n\t#ifdef SHADOWMAP_CASCADE\n\n\t\tint inFrustumCount = 0;\n\n\t#endif\n\n\tfloat fDepth;\n\tvec3 shadowColor = vec3( 1.0 );\n\n\tfor( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n\t\tvec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;\n\n\t\t\t\t// if ( something && something ) breaks ATI OpenGL shader compiler\n\t\t\t\t// if ( all( something, something ) ) using this instead\n\n\t\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n\t\tbool inFrustum = all( inFrustumVec );\n\n\t\t\t\t// don't shadow pixels outside of light frustum\n\t\t\t\t// use just first frustum (for cascades)\n\t\t\t\t// don't shadow pixels behind far plane of light frustum\n\n\t\t#ifdef SHADOWMAP_CASCADE\n\n\t\t\tinFrustumCount += int( inFrustum );\n\t\t\tbvec3 frustumTestVec = bvec3( inFrustum, inFrustumCount == 1, shadowCoord.z <= 1.0 );\n\n\t\t#else\n\n\t\t\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\n\t\t#endif\n\n\t\tbool frustumTest = all( frustumTestVec );\n\n\t\tif ( frustumTest ) {\n\n\t\t\tshadowCoord.z += shadowBias[ i ];\n\n\t\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\n\t\t\t\t\t\t// Percentage-close filtering\n\t\t\t\t\t\t// (9 pixel kernel)\n\t\t\t\t\t\t// http://fabiensanglard.net/shadowmappingPCF/\n\n\t\t\t\tfloat shadow = 0.0;\n\n\t\t/*\n\t\t\t\t\t\t// nested loops breaks shader compiler / validator on some ATI cards when using OpenGL\n\t\t\t\t\t\t// must enroll loop manually\n\n\t\t\t\tfor ( float y = -1.25; y <= 1.25; y += 1.25 )\n\t\t\t\t\tfor ( float x = -1.25; x <= 1.25; x += 1.25 ) {\n\n\t\t\t\t\t\tvec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );\n\n\t\t\t\t\t\t\t\t// doesn't seem to produce any noticeable visual difference compared to simple texture2D lookup\n\t\t\t\t\t\t\t\t//vec4 rgbaDepth = texture2DProj( shadowMap[ i ], vec4( vShadowCoord[ i ].w * ( vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy ), 0.05, vShadowCoord[ i ].w ) );\n\n\t\t\t\t\t\tfloat fDepth = unpackDepth( rgbaDepth );\n\n\t\t\t\t\t\tif ( fDepth < shadowCoord.z )\n\t\t\t\t\t\t\tshadow += 1.0;\n\n\t\t\t\t}\n\n\t\t\t\tshadow /= 9.0;\n\n\t\t*/\n\n\t\t\t\tconst float shadowDelta = 1.0 / 9.0;\n\n\t\t\t\tfloat xPixelOffset = 1.0 / shadowMapSize[ i ].x;\n\t\t\t\tfloat yPixelOffset = 1.0 / shadowMapSize[ i ].y;\n\n\t\t\t\tfloat dx0 = -1.25 * xPixelOffset;\n\t\t\t\tfloat dy0 = -1.25 * yPixelOffset;\n\t\t\t\tfloat dx1 = 1.25 * xPixelOffset;\n\t\t\t\tfloat dy1 = 1.25 * yPixelOffset;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n\t\t\t\tif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n\t\t\t\tshadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n\n\t\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n\t\t\t\t\t\t// Percentage-close filtering\n\t\t\t\t\t\t// (9 pixel kernel)\n\t\t\t\t\t\t// http://fabiensanglard.net/shadowmappingPCF/\n\n\t\t\t\tfloat shadow = 0.0;\n\n\t\t\t\tfloat xPixelOffset = 1.0 / shadowMapSize[ i ].x;\n\t\t\t\tfloat yPixelOffset = 1.0 / shadowMapSize[ i ].y;\n\n\t\t\t\tfloat dx0 = -1.0 * xPixelOffset;\n\t\t\t\tfloat dy0 = -1.0 * yPixelOffset;\n\t\t\t\tfloat dx1 = 1.0 * xPixelOffset;\n\t\t\t\tfloat dy1 = 1.0 * yPixelOffset;\n\n\t\t\t\tmat3 shadowKernel;\n\t\t\t\tmat3 depthKernel;\n\n\t\t\t\tdepthKernel[0][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n\t\t\t\tdepthKernel[0][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n\t\t\t\tdepthKernel[0][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n\t\t\t\tdepthKernel[1][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n\t\t\t\tdepthKernel[1][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n\t\t\t\tdepthKernel[1][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n\t\t\t\tdepthKernel[2][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n\t\t\t\tdepthKernel[2][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n\t\t\t\tdepthKernel[2][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n\n\t\t\t\tvec3 shadowZ = vec3( shadowCoord.z );\n\t\t\t\tshadowKernel[0] = vec3(lessThan(depthKernel[0], shadowZ ));\n\t\t\t\tshadowKernel[0] *= vec3(0.25);\n\n\t\t\t\tshadowKernel[1] = vec3(lessThan(depthKernel[1], shadowZ ));\n\t\t\t\tshadowKernel[1] *= vec3(0.25);\n\n\t\t\t\tshadowKernel[2] = vec3(lessThan(depthKernel[2], shadowZ ));\n\t\t\t\tshadowKernel[2] *= vec3(0.25);\n\n\t\t\t\tvec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[i].xy );\n\n\t\t\t\tshadowKernel[0] = mix( shadowKernel[1], shadowKernel[0], fractionalCoord.x );\n\t\t\t\tshadowKernel[1] = mix( shadowKernel[2], shadowKernel[1], fractionalCoord.x );\n\n\t\t\t\tvec4 shadowValues;\n\t\t\t\tshadowValues.x = mix( shadowKernel[0][1], shadowKernel[0][0], fractionalCoord.y );\n\t\t\t\tshadowValues.y = mix( shadowKernel[0][2], shadowKernel[0][1], fractionalCoord.y );\n\t\t\t\tshadowValues.z = mix( shadowKernel[1][1], shadowKernel[1][0], fractionalCoord.y );\n\t\t\t\tshadowValues.w = mix( shadowKernel[1][2], shadowKernel[1][1], fractionalCoord.y );\n\n\t\t\t\tshadow = dot( shadowValues, vec4( 1.0 ) );\n\n\t\t\t\tshadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n\n\t\t\t#else\n\n\t\t\t\tvec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );\n\t\t\t\tfloat fDepth = unpackDepth( rgbaDepth );\n\n\t\t\t\tif ( fDepth < shadowCoord.z )\n\n\t\t// spot with multiple shadows is darker\n\n\t\t\t\t\tshadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] );\n\n\t\t// spot with multiple shadows has the same color as single shadow spot\n\n\t\t// \t\t\t\t\tshadowColor = min( shadowColor, vec3( shadowDarkness[ i ] ) );\n\n\t\t\t#endif\n\n\t\t}\n\n\n\t\t#ifdef SHADOWMAP_DEBUG\n\n\t\t\t#ifdef SHADOWMAP_CASCADE\n\n\t\t\t\tif ( inFrustum && inFrustumCount == 1 ) gl_FragColor.xyz *= frustumColors[ i ];\n\n\t\t\t#else\n\n\t\t\t\tif ( inFrustum ) gl_FragColor.xyz *= frustumColors[ i ];\n\n\t\t\t#endif\n\n\t\t#endif\n\n\t}\n\n\t#ifdef GAMMA_OUTPUT\n\n\t\tshadowColor *= shadowColor;\n\n\t#endif\n\n\tgl_FragColor.xyz = gl_FragColor.xyz * shadowColor;\n\n#endif\n"; -THREE.ShaderChunk.worldpos_vertex="#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n\n\t#ifdef USE_SKINNING\n\n\t\tvec4 worldPosition = modelMatrix * skinned;\n\n\t#endif\n\n\t#if defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )\n\n\t\tvec4 worldPosition = modelMatrix * vec4( morphed, 1.0 );\n\n\t#endif\n\n\t#if ! defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )\n\n\t\tvec4 worldPosition = modelMatrix * vec4( position, 1.0 );\n\n\t#endif\n\n#endif"; -THREE.ShaderChunk.shadowmap_pars_fragment="#ifdef USE_SHADOWMAP\n\n\tuniform sampler2D shadowMap[ MAX_SHADOWS ];\n\tuniform vec2 shadowMapSize[ MAX_SHADOWS ];\n\n\tuniform float shadowDarkness[ MAX_SHADOWS ];\n\tuniform float shadowBias[ MAX_SHADOWS ];\n\n\tvarying vec4 vShadowCoord[ MAX_SHADOWS ];\n\n\tfloat unpackDepth( const in vec4 rgba_depth ) {\n\n\t\tconst vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n\t\tfloat depth = dot( rgba_depth, bit_shift );\n\t\treturn depth;\n\n\t}\n\n#endif"; -THREE.ShaderChunk.skinning_pars_vertex="#ifdef USE_SKINNING\n\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\n\t#ifdef BONE_TEXTURE\n\n\t\tuniform sampler2D boneTexture;\n\t\tuniform int boneTextureWidth;\n\t\tuniform int boneTextureHeight;\n\n\t\tmat4 getBoneMatrix( const in float i ) {\n\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureWidth ) );\n\t\t\tfloat y = floor( j / float( boneTextureWidth ) );\n\n\t\t\tfloat dx = 1.0 / float( boneTextureWidth );\n\t\t\tfloat dy = 1.0 / float( boneTextureHeight );\n\n\t\t\ty = dy * ( y + 0.5 );\n\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\n\t\t\treturn bone;\n\n\t\t}\n\n\t#else\n\n\t\tuniform mat4 boneGlobalMatrices[ MAX_BONES ];\n\n\t\tmat4 getBoneMatrix( const in float i ) {\n\n\t\t\tmat4 bone = boneGlobalMatrices[ int(i) ];\n\t\t\treturn bone;\n\n\t\t}\n\n\t#endif\n\n#endif\n"; -THREE.ShaderChunk.logdepthbuf_pars_fragment="#ifdef USE_LOGDEPTHBUF\n\n\tuniform float logDepthBufFC;\n\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\n\t\t#extension GL_EXT_frag_depth : enable\n\t\tvarying float vFragDepth;\n\n\t#endif\n\n#endif";THREE.ShaderChunk.alphamap_fragment="#ifdef USE_ALPHAMAP\n\n\tgl_FragColor.a *= texture2D( alphaMap, vUv ).g;\n\n#endif\n";THREE.ShaderChunk.alphamap_pars_fragment="#ifdef USE_ALPHAMAP\n\n\tuniform sampler2D alphaMap;\n\n#endif\n"; -THREE.UniformsUtils={merge:function(a){var b,c,d,e={};for(b=0;b dashSize ) {\n\t\tdiscard;\n\t}\n\tgl_FragColor = vec4( diffuse, opacity );",THREE.ShaderChunk.logdepthbuf_fragment,THREE.ShaderChunk.color_fragment,THREE.ShaderChunk.fog_fragment, -"}"].join("\n")},depth:{uniforms:{mNear:{type:"f",value:1},mFar:{type:"f",value:2E3},opacity:{type:"f",value:1}},vertexShader:[THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.default_vertex,THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:["uniform float mNear;\nuniform float mFar;\nuniform float opacity;",THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {",THREE.ShaderChunk.logdepthbuf_fragment, -"\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tfloat depth = gl_FragDepthEXT / gl_FragCoord.w;\n\t#else\n\t\tfloat depth = gl_FragCoord.z / gl_FragCoord.w;\n\t#endif\n\tfloat color = 1.0 - smoothstep( mNear, mFar, depth );\n\tgl_FragColor = vec4( vec3( color ), opacity );\n}"].join("\n")},normal:{uniforms:{opacity:{type:"f",value:1}},vertexShader:["varying vec3 vNormal;",THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {\n\tvNormal = normalize( normalMatrix * normal );", -THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.default_vertex,THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:["uniform float opacity;\nvarying vec3 vNormal;",THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {\n\tgl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );",THREE.ShaderChunk.logdepthbuf_fragment,"}"].join("\n")},normalmap:{uniforms:THREE.UniformsUtils.merge([THREE.UniformsLib.fog,THREE.UniformsLib.lights,THREE.UniformsLib.shadowmap,{enableAO:{type:"i", -value:0},enableDiffuse:{type:"i",value:0},enableSpecular:{type:"i",value:0},enableReflection:{type:"i",value:0},enableDisplacement:{type:"i",value:0},tDisplacement:{type:"t",value:null},tDiffuse:{type:"t",value:null},tCube:{type:"t",value:null},tNormal:{type:"t",value:null},tSpecular:{type:"t",value:null},tAO:{type:"t",value:null},uNormalScale:{type:"v2",value:new THREE.Vector2(1,1)},uDisplacementBias:{type:"f",value:0},uDisplacementScale:{type:"f",value:1},diffuse:{type:"c",value:new THREE.Color(16777215)}, -specular:{type:"c",value:new THREE.Color(1118481)},ambient:{type:"c",value:new THREE.Color(16777215)},shininess:{type:"f",value:30},opacity:{type:"f",value:1},useRefract:{type:"i",value:0},refractionRatio:{type:"f",value:0.98},reflectivity:{type:"f",value:0.5},uOffset:{type:"v2",value:new THREE.Vector2(0,0)},uRepeat:{type:"v2",value:new THREE.Vector2(1,1)},wrapRGB:{type:"v3",value:new THREE.Vector3(1,1,1)}}]),fragmentShader:["uniform vec3 ambient;\nuniform vec3 diffuse;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\nuniform bool enableDiffuse;\nuniform bool enableSpecular;\nuniform bool enableAO;\nuniform bool enableReflection;\nuniform sampler2D tDiffuse;\nuniform sampler2D tNormal;\nuniform sampler2D tSpecular;\nuniform sampler2D tAO;\nuniform samplerCube tCube;\nuniform vec2 uNormalScale;\nuniform bool useRefract;\nuniform float refractionRatio;\nuniform float reflectivity;\nvarying vec3 vTangent;\nvarying vec3 vBinormal;\nvarying vec3 vNormal;\nvarying vec2 vUv;\nuniform vec3 ambientLightColor;\n#if MAX_DIR_LIGHTS > 0\n\tuniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n\tuniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n#endif\n#if MAX_HEMI_LIGHTS > 0\n\tuniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n\tuniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n\tuniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n#endif\n#if MAX_POINT_LIGHTS > 0\n\tuniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n\tuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n\tuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n#endif\n#if MAX_SPOT_LIGHTS > 0\n\tuniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n\tuniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n\tuniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n\tuniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n#endif\n#ifdef WRAP_AROUND\n\tuniform vec3 wrapRGB;\n#endif\nvarying vec3 vWorldPosition;\nvarying vec3 vViewPosition;", -THREE.ShaderChunk.shadowmap_pars_fragment,THREE.ShaderChunk.fog_pars_fragment,THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {",THREE.ShaderChunk.logdepthbuf_fragment,"\tgl_FragColor = vec4( vec3( 1.0 ), opacity );\n\tvec3 specularTex = vec3( 1.0 );\n\tvec3 normalTex = texture2D( tNormal, vUv ).xyz * 2.0 - 1.0;\n\tnormalTex.xy *= uNormalScale;\n\tnormalTex = normalize( normalTex );\n\tif( enableDiffuse ) {\n\t\t#ifdef GAMMA_INPUT\n\t\t\tvec4 texelColor = texture2D( tDiffuse, vUv );\n\t\t\ttexelColor.xyz *= texelColor.xyz;\n\t\t\tgl_FragColor = gl_FragColor * texelColor;\n\t\t#else\n\t\t\tgl_FragColor = gl_FragColor * texture2D( tDiffuse, vUv );\n\t\t#endif\n\t}\n\tif( enableAO ) {\n\t\t#ifdef GAMMA_INPUT\n\t\t\tvec4 aoColor = texture2D( tAO, vUv );\n\t\t\taoColor.xyz *= aoColor.xyz;\n\t\t\tgl_FragColor.xyz = gl_FragColor.xyz * aoColor.xyz;\n\t\t#else\n\t\t\tgl_FragColor.xyz = gl_FragColor.xyz * texture2D( tAO, vUv ).xyz;\n\t\t#endif\n\t}", -THREE.ShaderChunk.alphatest_fragment,"\tif( enableSpecular )\n\t\tspecularTex = texture2D( tSpecular, vUv ).xyz;\n\tmat3 tsb = mat3( normalize( vTangent ), normalize( vBinormal ), normalize( vNormal ) );\n\tvec3 finalNormal = tsb * normalTex;\n\t#ifdef FLIP_SIDED\n\t\tfinalNormal = -finalNormal;\n\t#endif\n\tvec3 normal = normalize( finalNormal );\n\tvec3 viewPosition = normalize( vViewPosition );\n\t#if MAX_POINT_LIGHTS > 0\n\t\tvec3 pointDiffuse = vec3( 0.0 );\n\t\tvec3 pointSpecular = vec3( 0.0 );\n\t\tfor ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\t\t\tvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\n\t\t\tvec3 pointVector = lPosition.xyz + vViewPosition.xyz;\n\t\t\tfloat pointDistance = 1.0;\n\t\t\tif ( pointLightDistance[ i ] > 0.0 )\n\t\t\t\tpointDistance = 1.0 - min( ( length( pointVector ) / pointLightDistance[ i ] ), 1.0 );\n\t\t\tpointVector = normalize( pointVector );\n\t\t\t#ifdef WRAP_AROUND\n\t\t\t\tfloat pointDiffuseWeightFull = max( dot( normal, pointVector ), 0.0 );\n\t\t\t\tfloat pointDiffuseWeightHalf = max( 0.5 * dot( normal, pointVector ) + 0.5, 0.0 );\n\t\t\t\tvec3 pointDiffuseWeight = mix( vec3( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );\n\t\t\t#else\n\t\t\t\tfloat pointDiffuseWeight = max( dot( normal, pointVector ), 0.0 );\n\t\t\t#endif\n\t\t\tpointDiffuse += pointDistance * pointLightColor[ i ] * diffuse * pointDiffuseWeight;\n\t\t\tvec3 pointHalfVector = normalize( pointVector + viewPosition );\n\t\t\tfloat pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );\n\t\t\tfloat pointSpecularWeight = specularTex.r * max( pow( pointDotNormalHalf, shininess ), 0.0 );\n\t\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\t\t\tvec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( pointVector, pointHalfVector ), 0.0 ), 5.0 );\n\t\t\tpointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * pointDistance * specularNormalization;\n\t\t}\n\t#endif\n\t#if MAX_SPOT_LIGHTS > 0\n\t\tvec3 spotDiffuse = vec3( 0.0 );\n\t\tvec3 spotSpecular = vec3( 0.0 );\n\t\tfor ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\t\t\tvec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\n\t\t\tvec3 spotVector = lPosition.xyz + vViewPosition.xyz;\n\t\t\tfloat spotDistance = 1.0;\n\t\t\tif ( spotLightDistance[ i ] > 0.0 )\n\t\t\t\tspotDistance = 1.0 - min( ( length( spotVector ) / spotLightDistance[ i ] ), 1.0 );\n\t\t\tspotVector = normalize( spotVector );\n\t\t\tfloat spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );\n\t\t\tif ( spotEffect > spotLightAngleCos[ i ] ) {\n\t\t\t\tspotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );\n\t\t\t\t#ifdef WRAP_AROUND\n\t\t\t\t\tfloat spotDiffuseWeightFull = max( dot( normal, spotVector ), 0.0 );\n\t\t\t\t\tfloat spotDiffuseWeightHalf = max( 0.5 * dot( normal, spotVector ) + 0.5, 0.0 );\n\t\t\t\t\tvec3 spotDiffuseWeight = mix( vec3( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );\n\t\t\t\t#else\n\t\t\t\t\tfloat spotDiffuseWeight = max( dot( normal, spotVector ), 0.0 );\n\t\t\t\t#endif\n\t\t\t\tspotDiffuse += spotDistance * spotLightColor[ i ] * diffuse * spotDiffuseWeight * spotEffect;\n\t\t\t\tvec3 spotHalfVector = normalize( spotVector + viewPosition );\n\t\t\t\tfloat spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );\n\t\t\t\tfloat spotSpecularWeight = specularTex.r * max( pow( spotDotNormalHalf, shininess ), 0.0 );\n\t\t\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\t\t\t\tvec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( spotVector, spotHalfVector ), 0.0 ), 5.0 );\n\t\t\t\tspotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * spotDistance * specularNormalization * spotEffect;\n\t\t\t}\n\t\t}\n\t#endif\n\t#if MAX_DIR_LIGHTS > 0\n\t\tvec3 dirDiffuse = vec3( 0.0 );\n\t\tvec3 dirSpecular = vec3( 0.0 );\n\t\tfor( int i = 0; i < MAX_DIR_LIGHTS; i++ ) {\n\t\t\tvec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\n\t\t\tvec3 dirVector = normalize( lDirection.xyz );\n\t\t\t#ifdef WRAP_AROUND\n\t\t\t\tfloat directionalLightWeightingFull = max( dot( normal, dirVector ), 0.0 );\n\t\t\t\tfloat directionalLightWeightingHalf = max( 0.5 * dot( normal, dirVector ) + 0.5, 0.0 );\n\t\t\t\tvec3 dirDiffuseWeight = mix( vec3( directionalLightWeightingFull ), vec3( directionalLightWeightingHalf ), wrapRGB );\n\t\t\t#else\n\t\t\t\tfloat dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );\n\t\t\t#endif\n\t\t\tdirDiffuse += directionalLightColor[ i ] * diffuse * dirDiffuseWeight;\n\t\t\tvec3 dirHalfVector = normalize( dirVector + viewPosition );\n\t\t\tfloat dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );\n\t\t\tfloat dirSpecularWeight = specularTex.r * max( pow( dirDotNormalHalf, shininess ), 0.0 );\n\t\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\t\t\tvec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( dirVector, dirHalfVector ), 0.0 ), 5.0 );\n\t\t\tdirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;\n\t\t}\n\t#endif\n\t#if MAX_HEMI_LIGHTS > 0\n\t\tvec3 hemiDiffuse = vec3( 0.0 );\n\t\tvec3 hemiSpecular = vec3( 0.0 );\n\t\tfor( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\t\t\tvec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );\n\t\t\tvec3 lVector = normalize( lDirection.xyz );\n\t\t\tfloat dotProduct = dot( normal, lVector );\n\t\t\tfloat hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\t\t\tvec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\t\t\themiDiffuse += diffuse * hemiColor;\n\t\t\tvec3 hemiHalfVectorSky = normalize( lVector + viewPosition );\n\t\t\tfloat hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;\n\t\t\tfloat hemiSpecularWeightSky = specularTex.r * max( pow( max( hemiDotNormalHalfSky, 0.0 ), shininess ), 0.0 );\n\t\t\tvec3 lVectorGround = -lVector;\n\t\t\tvec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );\n\t\t\tfloat hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;\n\t\t\tfloat hemiSpecularWeightGround = specularTex.r * max( pow( max( hemiDotNormalHalfGround, 0.0 ), shininess ), 0.0 );\n\t\t\tfloat dotProductGround = dot( normal, lVectorGround );\n\t\t\tfloat specularNormalization = ( shininess + 2.0 ) / 8.0;\n\t\t\tvec3 schlickSky = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, hemiHalfVectorSky ), 0.0 ), 5.0 );\n\t\t\tvec3 schlickGround = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 0.0 ), 5.0 );\n\t\t\themiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );\n\t\t}\n\t#endif\n\tvec3 totalDiffuse = vec3( 0.0 );\n\tvec3 totalSpecular = vec3( 0.0 );\n\t#if MAX_DIR_LIGHTS > 0\n\t\ttotalDiffuse += dirDiffuse;\n\t\ttotalSpecular += dirSpecular;\n\t#endif\n\t#if MAX_HEMI_LIGHTS > 0\n\t\ttotalDiffuse += hemiDiffuse;\n\t\ttotalSpecular += hemiSpecular;\n\t#endif\n\t#if MAX_POINT_LIGHTS > 0\n\t\ttotalDiffuse += pointDiffuse;\n\t\ttotalSpecular += pointSpecular;\n\t#endif\n\t#if MAX_SPOT_LIGHTS > 0\n\t\ttotalDiffuse += spotDiffuse;\n\t\ttotalSpecular += spotSpecular;\n\t#endif\n\t#ifdef METAL\n\t\tgl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * ambient + totalSpecular );\n\t#else\n\t\tgl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * ambient ) + totalSpecular;\n\t#endif\n\tif ( enableReflection ) {\n\t\tvec3 vReflect;\n\t\tvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\t\tif ( useRefract ) {\n\t\t\tvReflect = refract( cameraToVertex, normal, refractionRatio );\n\t\t} else {\n\t\t\tvReflect = reflect( cameraToVertex, normal );\n\t\t}\n\t\tvec4 cubeColor = textureCube( tCube, vec3( -vReflect.x, vReflect.yz ) );\n\t\t#ifdef GAMMA_INPUT\n\t\t\tcubeColor.xyz *= cubeColor.xyz;\n\t\t#endif\n\t\tgl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularTex.r * reflectivity );\n\t}", -THREE.ShaderChunk.shadowmap_fragment,THREE.ShaderChunk.linear_to_gamma_fragment,THREE.ShaderChunk.fog_fragment,"}"].join("\n"),vertexShader:["attribute vec4 tangent;\nuniform vec2 uOffset;\nuniform vec2 uRepeat;\nuniform bool enableDisplacement;\n#ifdef VERTEX_TEXTURES\n\tuniform sampler2D tDisplacement;\n\tuniform float uDisplacementScale;\n\tuniform float uDisplacementBias;\n#endif\nvarying vec3 vTangent;\nvarying vec3 vBinormal;\nvarying vec3 vNormal;\nvarying vec2 vUv;\nvarying vec3 vWorldPosition;\nvarying vec3 vViewPosition;", -THREE.ShaderChunk.skinning_pars_vertex,THREE.ShaderChunk.shadowmap_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {",THREE.ShaderChunk.skinbase_vertex,THREE.ShaderChunk.skinnormal_vertex,"\t#ifdef USE_SKINNING\n\t\tvNormal = normalize( normalMatrix * skinnedNormal.xyz );\n\t\tvec4 skinnedTangent = skinMatrix * vec4( tangent.xyz, 0.0 );\n\t\tvTangent = normalize( normalMatrix * skinnedTangent.xyz );\n\t#else\n\t\tvNormal = normalize( normalMatrix * normal );\n\t\tvTangent = normalize( normalMatrix * tangent.xyz );\n\t#endif\n\tvBinormal = normalize( cross( vNormal, vTangent ) * tangent.w );\n\tvUv = uv * uRepeat + uOffset;\n\tvec3 displacedPosition;\n\t#ifdef VERTEX_TEXTURES\n\t\tif ( enableDisplacement ) {\n\t\t\tvec3 dv = texture2D( tDisplacement, uv ).xyz;\n\t\t\tfloat df = uDisplacementScale * dv.x + uDisplacementBias;\n\t\t\tdisplacedPosition = position + normalize( normal ) * df;\n\t\t} else {\n\t\t\t#ifdef USE_SKINNING\n\t\t\t\tvec4 skinVertex = bindMatrix * vec4( position, 1.0 );\n\t\t\t\tvec4 skinned = vec4( 0.0 );\n\t\t\t\tskinned += boneMatX * skinVertex * skinWeight.x;\n\t\t\t\tskinned += boneMatY * skinVertex * skinWeight.y;\n\t\t\t\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\t\t\t\tskinned += boneMatW * skinVertex * skinWeight.w;\n\t\t\t\tskinned = bindMatrixInverse * skinned;\n\t\t\t\tdisplacedPosition = skinned.xyz;\n\t\t\t#else\n\t\t\t\tdisplacedPosition = position;\n\t\t\t#endif\n\t\t}\n\t#else\n\t\t#ifdef USE_SKINNING\n\t\t\tvec4 skinVertex = bindMatrix * vec4( position, 1.0 );\n\t\t\tvec4 skinned = vec4( 0.0 );\n\t\t\tskinned += boneMatX * skinVertex * skinWeight.x;\n\t\t\tskinned += boneMatY * skinVertex * skinWeight.y;\n\t\t\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\t\t\tskinned += boneMatW * skinVertex * skinWeight.w;\n\t\t\tskinned = bindMatrixInverse * skinned;\n\t\t\tdisplacedPosition = skinned.xyz;\n\t\t#else\n\t\t\tdisplacedPosition = position;\n\t\t#endif\n\t#endif\n\tvec4 mvPosition = modelViewMatrix * vec4( displacedPosition, 1.0 );\n\tvec4 worldPosition = modelMatrix * vec4( displacedPosition, 1.0 );\n\tgl_Position = projectionMatrix * mvPosition;", -THREE.ShaderChunk.logdepthbuf_vertex,"\tvWorldPosition = worldPosition.xyz;\n\tvViewPosition = -mvPosition.xyz;\n\t#ifdef USE_SHADOWMAP\n\t\tfor( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\t\t\tvShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n\t\t}\n\t#endif\n}"].join("\n")},cube:{uniforms:{tCube:{type:"t",value:null},tFlip:{type:"f",value:-1}},vertexShader:["varying vec3 vWorldPosition;",THREE.ShaderChunk.logdepthbuf_pars_vertex,"void main() {\n\tvec4 worldPosition = modelMatrix * vec4( position, 1.0 );\n\tvWorldPosition = worldPosition.xyz;\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", -THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:["uniform samplerCube tCube;\nuniform float tFlip;\nvarying vec3 vWorldPosition;",THREE.ShaderChunk.logdepthbuf_pars_fragment,"void main() {\n\tgl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );",THREE.ShaderChunk.logdepthbuf_fragment,"}"].join("\n")},depthRGBA:{uniforms:{},vertexShader:[THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.skinning_pars_vertex,THREE.ShaderChunk.logdepthbuf_pars_vertex, -"void main() {",THREE.ShaderChunk.skinbase_vertex,THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.skinning_vertex,THREE.ShaderChunk.default_vertex,THREE.ShaderChunk.logdepthbuf_vertex,"}"].join("\n"),fragmentShader:[THREE.ShaderChunk.logdepthbuf_pars_fragment,"vec4 pack_depth( const in float depth ) {\n\tconst vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );\n\tconst vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );\n\tvec4 res = mod( depth * bit_shift * vec4( 255 ), vec4( 256 ) ) / vec4( 255 );\n\tres -= res.xxyz * bit_mask;\n\treturn res;\n}\nvoid main() {", -THREE.ShaderChunk.logdepthbuf_fragment,"\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tgl_FragData[ 0 ] = pack_depth( gl_FragDepthEXT );\n\t#else\n\t\tgl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );\n\t#endif\n}"].join("\n")}}; -THREE.WebGLRenderer=function(a){function b(a,b){var c=a.vertices.length,d=b.material;if(d.attributes){void 0===a.__webglCustomAttributesList&&(a.__webglCustomAttributesList=[]);for(var e in d.attributes){var f=d.attributes[e];if(!f.__webglInitialized||f.createUniqueBuffers){f.__webglInitialized=!0;var g=1;"v2"===f.type?g=2:"v3"===f.type?g=3:"v4"===f.type?g=4:"c"===f.type&&(g=3);f.size=g;f.array=new Float32Array(c*g);f.buffer=m.createBuffer();f.buffer.belongsToAttribute=e;f.needsUpdate=!0}a.__webglCustomAttributesList.push(f)}}} -function c(a,b){var c=b.geometry,g=a.faces3,h=3*g.length,k=1*g.length,l=3*g.length,g=d(b,a),n=f(g),r=e(g),p=g.vertexColors?g.vertexColors:!1;a.__vertexArray=new Float32Array(3*h);r&&(a.__normalArray=new Float32Array(3*h));c.hasTangents&&(a.__tangentArray=new Float32Array(4*h));p&&(a.__colorArray=new Float32Array(3*h));n&&(0ja;ja++)Da=R[ja], -yb[gb]=Da.x,yb[gb+1]=Da.y,yb[gb+2]=Da.z,gb+=3;else for(ja=0;3>ja;ja++)yb[gb]=ba.x,yb[gb+1]=ba.y,yb[gb+2]=ba.z,gb+=3;m.bindBuffer(m.ARRAY_BUFFER,y.__webglNormalBuffer);m.bufferData(m.ARRAY_BUFFER,yb,I)}if(xb&&Hb&&Q){B=0;for(N=ka.length;Bja;ja++)Ga=V[ja],cb[Sa]=Ga.x,cb[Sa+1]=Ga.y,Sa+=2;0ja;ja++)Ya=za[ja],fb[Za]=Ya.x,fb[Za+1]=Ya.y,Za+=2;0c;c++)G.autoScaleCubemaps&&!U?(g=h,f=c,r=a.image[c],u=sc,r.width<=u&&r.height<=u||(v=Math.max(r.width,r.height),q=Math.floor(r.width*u/v),u=Math.floor(r.height*u/v),v=document.createElement("canvas"),v.width=q,v.height=u,v.getContext("2d").drawImage(r,0,0,r.width,r.height,0,0,q,u),r=v),g[f]=r):h[c]=a.image[c];c=h[0];g=THREE.Math.isPowerOfTwo(c.width)&&THREE.Math.isPowerOfTwo(c.height); -f=B(a.format);r=B(a.type);E(m.TEXTURE_CUBE_MAP,a,g);for(c=0;6>c;c++)if(U)for(u=h[c].mipmaps,v=0,x=u.length;v=Qb&&console.warn("WebGLRenderer: trying to use "+a+" texture units while this GPU supports only "+Qb);qa+=1;return a}function N(a,b){a._modelViewMatrix.multiplyMatrices(b.matrixWorldInverse,a.matrixWorld);a._normalMatrix.getNormalMatrix(a._modelViewMatrix)} -function ba(a,b,c,d){a[b]=c.r*c.r*d;a[b+1]=c.g*c.g*d;a[b+2]=c.b*c.b*d}function P(a,b,c,d){a[b]=c.r*d;a[b+1]=c.g*d;a[b+2]=c.b*d}function O(a){a!==xa&&(m.lineWidth(a),xa=a)}function J(a,b,c){Qa!==a&&(a?m.enable(m.POLYGON_OFFSET_FILL):m.disable(m.POLYGON_OFFSET_FILL),Qa=a);!a||cb===b&&Ga===c||(m.polygonOffset(b,c),cb=b,Ga=c)}function E(a,b,c){c?(m.texParameteri(a,m.TEXTURE_WRAP_S,B(b.wrapS)),m.texParameteri(a,m.TEXTURE_WRAP_T,B(b.wrapT)),m.texParameteri(a,m.TEXTURE_MAG_FILTER,B(b.magFilter)),m.texParameteri(a, -m.TEXTURE_MIN_FILTER,B(b.minFilter))):(m.texParameteri(a,m.TEXTURE_WRAP_S,m.CLAMP_TO_EDGE),m.texParameteri(a,m.TEXTURE_WRAP_T,m.CLAMP_TO_EDGE),m.texParameteri(a,m.TEXTURE_MAG_FILTER,R(b.magFilter)),m.texParameteri(a,m.TEXTURE_MIN_FILTER,R(b.minFilter)));Ta&&b.type!==THREE.FloatType&&(1b;b++)m.deleteFramebuffer(a.__webglFramebuffer[b]),m.deleteRenderbuffer(a.__webglRenderbuffer[b]);else m.deleteFramebuffer(a.__webglFramebuffer),m.deleteRenderbuffer(a.__webglRenderbuffer); -G.info.memory.textures--},ic=function(a){a=a.target;a.removeEventListener("dispose",ic);Nb(a)},Tb=function(a){void 0!==a.__webglVertexBuffer&&m.deleteBuffer(a.__webglVertexBuffer);void 0!==a.__webglNormalBuffer&&m.deleteBuffer(a.__webglNormalBuffer);void 0!==a.__webglTangentBuffer&&m.deleteBuffer(a.__webglTangentBuffer);void 0!==a.__webglColorBuffer&&m.deleteBuffer(a.__webglColorBuffer);void 0!==a.__webglUVBuffer&&m.deleteBuffer(a.__webglUVBuffer);void 0!==a.__webglUV2Buffer&&m.deleteBuffer(a.__webglUV2Buffer); -void 0!==a.__webglSkinIndicesBuffer&&m.deleteBuffer(a.__webglSkinIndicesBuffer);void 0!==a.__webglSkinWeightsBuffer&&m.deleteBuffer(a.__webglSkinWeightsBuffer);void 0!==a.__webglFaceBuffer&&m.deleteBuffer(a.__webglFaceBuffer);void 0!==a.__webglLineBuffer&&m.deleteBuffer(a.__webglLineBuffer);void 0!==a.__webglLineDistanceBuffer&&m.deleteBuffer(a.__webglLineDistanceBuffer);if(void 0!==a.__webglCustomAttributesList)for(var b in a.__webglCustomAttributesList)m.deleteBuffer(a.__webglCustomAttributesList[b].buffer); -G.info.memory.geometries--},Nb=function(a){var b=a.program.program;if(void 0!==b){a.program=void 0;var c,d,e=!1;a=0;for(c=Ba.length;ad.numSupportedMorphTargets?(h.sort(t),h.length=d.numSupportedMorphTargets):h.length>d.numSupportedMorphNormals?h.sort(t):0===h.length&&h.push([0,0]);for(p=0;pf;f++){a.__webglFramebuffer[f]=m.createFramebuffer();a.__webglRenderbuffer[f]=m.createRenderbuffer(); -m.texImage2D(m.TEXTURE_CUBE_MAP_POSITIVE_X+f,0,d,a.width,a.height,0,d,e,null);var g=a,h=m.TEXTURE_CUBE_MAP_POSITIVE_X+f;m.bindFramebuffer(m.FRAMEBUFFER,a.__webglFramebuffer[f]);m.framebufferTexture2D(m.FRAMEBUFFER,m.COLOR_ATTACHMENT0,h,g.__webglTexture,0);Q(a.__webglRenderbuffer[f],a)}c&&m.generateMipmap(m.TEXTURE_CUBE_MAP)}else a.__webglFramebuffer=m.createFramebuffer(),a.__webglRenderbuffer=a.shareDepthFrom?a.shareDepthFrom.__webglRenderbuffer:m.createRenderbuffer(),m.bindTexture(m.TEXTURE_2D,a.__webglTexture), -E(m.TEXTURE_2D,a,c),m.texImage2D(m.TEXTURE_2D,0,d,a.width,a.height,0,d,e,null),d=m.TEXTURE_2D,m.bindFramebuffer(m.FRAMEBUFFER,a.__webglFramebuffer),m.framebufferTexture2D(m.FRAMEBUFFER,m.COLOR_ATTACHMENT0,d,a.__webglTexture,0),a.shareDepthFrom?a.depthBuffer&&!a.stencilBuffer?m.framebufferRenderbuffer(m.FRAMEBUFFER,m.DEPTH_ATTACHMENT,m.RENDERBUFFER,a.__webglRenderbuffer):a.depthBuffer&&a.stencilBuffer&&m.framebufferRenderbuffer(m.FRAMEBUFFER,m.DEPTH_STENCIL_ATTACHMENT,m.RENDERBUFFER,a.__webglRenderbuffer): -Q(a.__webglRenderbuffer,a),c&&m.generateMipmap(m.TEXTURE_2D);b?m.bindTexture(m.TEXTURE_CUBE_MAP,null):m.bindTexture(m.TEXTURE_2D,null);m.bindRenderbuffer(m.RENDERBUFFER,null);m.bindFramebuffer(m.FRAMEBUFFER,null)}a?(b=b?a.__webglFramebuffer[a.activeCubeFace]:a.__webglFramebuffer,c=a.width,a=a.height,e=d=0):(b=null,c=wb,a=sb,d=ma,e=Ja);b!==Za&&(m.bindFramebuffer(m.FRAMEBUFFER,b),m.viewport(d,e,c,a),Za=b);Hb=c;Ib=a};this.shadowMapPlugin=new THREE.ShadowMapPlugin;this.addPrePlugin(this.shadowMapPlugin); -this.addPostPlugin(new THREE.SpritePlugin);this.addPostPlugin(new THREE.LensFlarePlugin)}; -THREE.WebGLRenderTarget=function(a,b,c){this.width=a;this.height=b;c=c||{};this.wrapS=void 0!==c.wrapS?c.wrapS:THREE.ClampToEdgeWrapping;this.wrapT=void 0!==c.wrapT?c.wrapT:THREE.ClampToEdgeWrapping;this.magFilter=void 0!==c.magFilter?c.magFilter:THREE.LinearFilter;this.minFilter=void 0!==c.minFilter?c.minFilter:THREE.LinearMipMapLinearFilter;this.anisotropy=void 0!==c.anisotropy?c.anisotropy:1;this.offset=new THREE.Vector2(0,0);this.repeat=new THREE.Vector2(1,1);this.format=void 0!==c.format?c.format: -THREE.RGBAFormat;this.type=void 0!==c.type?c.type:THREE.UnsignedByteType;this.depthBuffer=void 0!==c.depthBuffer?c.depthBuffer:!0;this.stencilBuffer=void 0!==c.stencilBuffer?c.stencilBuffer:!0;this.generateMipmaps=!0;this.shareDepthFrom=null}; -THREE.WebGLRenderTarget.prototype={constructor:THREE.WebGLRenderTarget,setSize:function(a,b){this.width=a;this.height=b},clone:function(){var a=new THREE.WebGLRenderTarget(this.width,this.height);a.wrapS=this.wrapS;a.wrapT=this.wrapT;a.magFilter=this.magFilter;a.minFilter=this.minFilter;a.anisotropy=this.anisotropy;a.offset.copy(this.offset);a.repeat.copy(this.repeat);a.format=this.format;a.type=this.type;a.depthBuffer=this.depthBuffer;a.stencilBuffer=this.stencilBuffer;a.generateMipmaps=this.generateMipmaps; -a.shareDepthFrom=this.shareDepthFrom;return a},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.WebGLRenderTarget.prototype);THREE.WebGLRenderTargetCube=function(a,b,c){THREE.WebGLRenderTarget.call(this,a,b,c);this.activeCubeFace=0};THREE.WebGLRenderTargetCube.prototype=Object.create(THREE.WebGLRenderTarget.prototype); -THREE.WebGLProgram=function(){var a=0;return function(b,c,d,e){var f=b.context,g=d.defines,h=d.__webglShader.uniforms,k=d.attributes,l=d.__webglShader.vertexShader,n=d.__webglShader.fragmentShader,q=d.index0AttributeName;void 0===q&&!0===e.morphTargets&&(q="position");var r="SHADOWMAP_TYPE_BASIC";e.shadowMapType===THREE.PCFShadowMap?r="SHADOWMAP_TYPE_PCF":e.shadowMapType===THREE.PCFSoftShadowMap&&(r="SHADOWMAP_TYPE_PCF_SOFT");var t,s;t=[];for(var p in g)s=g[p],!1!==s&&(s="#define "+p+" "+s,t.push(s)); -t=t.join("\n");g=f.createProgram();d instanceof THREE.RawShaderMaterial?b=d="":(d=["precision "+e.precision+" float;","precision "+e.precision+" int;",t,e.supportsVertexTextures?"#define VERTEX_TEXTURES":"",b.gammaInput?"#define GAMMA_INPUT":"",b.gammaOutput?"#define GAMMA_OUTPUT":"","#define MAX_DIR_LIGHTS "+e.maxDirLights,"#define MAX_POINT_LIGHTS "+e.maxPointLights,"#define MAX_SPOT_LIGHTS "+e.maxSpotLights,"#define MAX_HEMI_LIGHTS "+e.maxHemiLights,"#define MAX_SHADOWS "+e.maxShadows,"#define MAX_BONES "+ -e.maxBones,e.map?"#define USE_MAP":"",e.envMap?"#define USE_ENVMAP":"",e.lightMap?"#define USE_LIGHTMAP":"",e.bumpMap?"#define USE_BUMPMAP":"",e.normalMap?"#define USE_NORMALMAP":"",e.specularMap?"#define USE_SPECULARMAP":"",e.alphaMap?"#define USE_ALPHAMAP":"",e.vertexColors?"#define USE_COLOR":"",e.skinning?"#define USE_SKINNING":"",e.useVertexTexture?"#define BONE_TEXTURE":"",e.morphTargets?"#define USE_MORPHTARGETS":"",e.morphNormals?"#define USE_MORPHNORMALS":"",e.wrapAround?"#define WRAP_AROUND": -"",e.doubleSided?"#define DOUBLE_SIDED":"",e.flipSided?"#define FLIP_SIDED":"",e.shadowMapEnabled?"#define USE_SHADOWMAP":"",e.shadowMapEnabled?"#define "+r:"",e.shadowMapDebug?"#define SHADOWMAP_DEBUG":"",e.shadowMapCascade?"#define SHADOWMAP_CASCADE":"",e.sizeAttenuation?"#define USE_SIZEATTENUATION":"",e.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"","uniform mat4 modelMatrix;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform mat4 viewMatrix;\nuniform mat3 normalMatrix;\nuniform vec3 cameraPosition;\nattribute vec3 position;\nattribute vec3 normal;\nattribute vec2 uv;\nattribute vec2 uv2;\n#ifdef USE_COLOR\n\tattribute vec3 color;\n#endif\n#ifdef USE_MORPHTARGETS\n\tattribute vec3 morphTarget0;\n\tattribute vec3 morphTarget1;\n\tattribute vec3 morphTarget2;\n\tattribute vec3 morphTarget3;\n\t#ifdef USE_MORPHNORMALS\n\t\tattribute vec3 morphNormal0;\n\t\tattribute vec3 morphNormal1;\n\t\tattribute vec3 morphNormal2;\n\t\tattribute vec3 morphNormal3;\n\t#else\n\t\tattribute vec3 morphTarget4;\n\t\tattribute vec3 morphTarget5;\n\t\tattribute vec3 morphTarget6;\n\t\tattribute vec3 morphTarget7;\n\t#endif\n#endif\n#ifdef USE_SKINNING\n\tattribute vec4 skinIndex;\n\tattribute vec4 skinWeight;\n#endif\n"].join("\n"), -b=["precision "+e.precision+" float;","precision "+e.precision+" int;",e.bumpMap||e.normalMap?"#extension GL_OES_standard_derivatives : enable":"",t,"#define MAX_DIR_LIGHTS "+e.maxDirLights,"#define MAX_POINT_LIGHTS "+e.maxPointLights,"#define MAX_SPOT_LIGHTS "+e.maxSpotLights,"#define MAX_HEMI_LIGHTS "+e.maxHemiLights,"#define MAX_SHADOWS "+e.maxShadows,e.alphaTest?"#define ALPHATEST "+e.alphaTest:"",b.gammaInput?"#define GAMMA_INPUT":"",b.gammaOutput?"#define GAMMA_OUTPUT":"",e.useFog&&e.fog?"#define USE_FOG": -"",e.useFog&&e.fogExp?"#define FOG_EXP2":"",e.map?"#define USE_MAP":"",e.envMap?"#define USE_ENVMAP":"",e.lightMap?"#define USE_LIGHTMAP":"",e.bumpMap?"#define USE_BUMPMAP":"",e.normalMap?"#define USE_NORMALMAP":"",e.specularMap?"#define USE_SPECULARMAP":"",e.alphaMap?"#define USE_ALPHAMAP":"",e.vertexColors?"#define USE_COLOR":"",e.metal?"#define METAL":"",e.wrapAround?"#define WRAP_AROUND":"",e.doubleSided?"#define DOUBLE_SIDED":"",e.flipSided?"#define FLIP_SIDED":"",e.shadowMapEnabled?"#define USE_SHADOWMAP": -"",e.shadowMapEnabled?"#define "+r:"",e.shadowMapDebug?"#define SHADOWMAP_DEBUG":"",e.shadowMapCascade?"#define SHADOWMAP_CASCADE":"",e.logarithmicDepthBuffer?"#define USE_LOGDEPTHBUF":"","uniform mat4 viewMatrix;\nuniform vec3 cameraPosition;\n"].join("\n"));l=new THREE.WebGLShader(f,f.VERTEX_SHADER,d+l);n=new THREE.WebGLShader(f,f.FRAGMENT_SHADER,b+n);f.attachShader(g,l);f.attachShader(g,n);void 0!==q&&f.bindAttribLocation(g,0,q);f.linkProgram(g);!1===f.getProgramParameter(g,f.LINK_STATUS)&&(console.error("THREE.WebGLProgram: Could not initialise shader."), -console.error("gl.VALIDATE_STATUS",f.getProgramParameter(g,f.VALIDATE_STATUS)),console.error("gl.getError()",f.getError()));""!==f.getProgramInfoLog(g)&&console.warn("THREE.WebGLProgram: gl.getProgramInfoLog()",f.getProgramInfoLog(g));f.deleteShader(l);f.deleteShader(n);q="viewMatrix modelViewMatrix projectionMatrix normalMatrix modelMatrix cameraPosition morphTargetInfluences bindMatrix bindMatrixInverse".split(" ");e.useVertexTexture?(q.push("boneTexture"),q.push("boneTextureWidth"),q.push("boneTextureHeight")): -q.push("boneGlobalMatrices");e.logarithmicDepthBuffer&&q.push("logDepthBufFC");for(var v in h)q.push(v);h=q;v={};q=0;for(b=h.length;qq-1?0:q-1,t=q+1>e-1?e-1:q+1,s=0>n-1?0:n-1,p=n+1>d-1?d-1:n+1,v=[],w=[0,0,h[4*(q*d+n)]/255*b];v.push([-1,0,h[4*(q*d+s)]/255*b]);v.push([-1,-1,h[4*(r*d+s)]/255*b]);v.push([0,-1,h[4*(r*d+n)]/255*b]);v.push([1,-1,h[4*(r*d+p)]/255*b]);v.push([1,0,h[4*(q*d+p)]/255*b]);v.push([1,1,h[4*(t*d+p)]/255*b]);v.push([0,1,h[4*(t*d+n)]/255* -b]);v.push([-1,1,h[4*(t*d+s)]/255*b]);r=[];s=v.length;for(t=0;te)return null;var f=[],g=[],h=[],k,l,n;if(0=q--){console.log("Warning, unable to triangulate polygon!");break}k=l;e<=k&&(k=0);l=k+1;e<=l&&(l=0);n=l+1;e<=n&&(n=0);var r;a:{var t=r=void 0,s=void 0,p=void 0,v=void 0,w=void 0,u=void 0,D=void 0,A= -void 0,t=a[g[k]].x,s=a[g[k]].y,p=a[g[l]].x,v=a[g[l]].y,w=a[g[n]].x,u=a[g[n]].y;if(1E-10>(p-t)*(u-s)-(v-s)*(w-t))r=!1;else{var x=void 0,C=void 0,I=void 0,z=void 0,y=void 0,K=void 0,N=void 0,ba=void 0,P=void 0,O=void 0,P=ba=N=A=D=void 0,x=w-p,C=u-v,I=t-w,z=s-u,y=p-t,K=v-s;for(r=0;rk)g=d+1;else if(0b&&(b=0);1=b)return b=c[a]-b,a=this.curves[a],b=1-b/a.getLength(),a.getPointAt(b);a++}return null};THREE.CurvePath.prototype.getLength=function(){var a=this.getCurveLengths();return a[a.length-1]}; -THREE.CurvePath.prototype.getCurveLengths=function(){if(this.cacheLengths&&this.cacheLengths.length==this.curves.length)return this.cacheLengths;var a=[],b=0,c,d=this.curves.length;for(c=0;cb?b=h.x:h.xc?c=h.y:h.yd?d=h.z:h.zMath.abs(d.x-c[0].x)&&1E-10>Math.abs(d.y-c[0].y)&&c.splice(c.length-1,1);b&&c.push(c[0]);return c}; -THREE.Path.prototype.toShapes=function(a,b){function c(a){for(var b=[],c=0,d=a.length;cl&&(g=b[f],k=-k,h=b[e],l=-l),!(a.yh.y))if(a.y==g.y){if(a.x==g.x)return!0}else{e=l*(a.x-g.x)-k*(a.y-g.y);if(0==e)return!0;0>e||(d=!d)}}else if(a.y==g.y&&(h.x<=a.x&&a.x<=g.x||g.x<=a.x&&a.x<= -h.x))return!0}return d}var e=function(a){var b,c,d,e,f=[],g=new THREE.Path;b=0;for(c=a.length;bz||z>I)return[];k=l*n-k*q;if(0>k||k>I)return[]}else{if(0d?[]:k==d?f?[]:[g]:a<=d?[g,h]: -[g,l]}function e(a,b,c,d){var e=b.x-a.x,f=b.y-a.y;b=c.x-a.x;c=c.y-a.y;var g=d.x-a.x;d=d.y-a.y;a=e*c-f*b;e=e*d-f*g;return 1E-10f&&(f=d);var g=a+1;g>d&&(g=0);d=e(h[a],h[f],h[g],k[b]);if(!d)return!1; -d=k.length-1;f=b-1;0>f&&(f=d);g=b+1;g>d&&(g=0);return(d=e(k[b],k[f],k[g],h[a]))?!0:!1}function f(a,b){var c,e;for(c=0;cO){console.log("Infinite Loop! Holes left:"+ -l.length+", Probably Hole outside Shape!");break}for(q=K;qh;h++)l=k[h].x+":"+k[h].y, -l=n[l],void 0!==l&&(k[h]=l);return q.concat()},isClockWise:function(a){return 0>THREE.FontUtils.Triangulate.area(a)},b2p0:function(a,b){var c=1-a;return c*c*b},b2p1:function(a,b){return 2*(1-a)*a*b},b2p2:function(a,b){return a*a*b},b2:function(a,b,c,d){return this.b2p0(a,b)+this.b2p1(a,c)+this.b2p2(a,d)},b3p0:function(a,b){var c=1-a;return c*c*c*b},b3p1:function(a,b){var c=1-a;return 3*c*c*a*b},b3p2:function(a,b){return 3*(1-a)*a*a*b},b3p3:function(a,b){return a*a*a*b},b3:function(a,b,c,d,e){return this.b3p0(a, -b)+this.b3p1(a,c)+this.b3p2(a,d)+this.b3p3(a,e)}};THREE.LineCurve=function(a,b){this.v1=a;this.v2=b};THREE.LineCurve.prototype=Object.create(THREE.Curve.prototype);THREE.LineCurve.prototype.getPoint=function(a){var b=this.v2.clone().sub(this.v1);b.multiplyScalar(a).add(this.v1);return b};THREE.LineCurve.prototype.getPointAt=function(a){return this.getPoint(a)};THREE.LineCurve.prototype.getTangent=function(a){return this.v2.clone().sub(this.v1).normalize()}; -THREE.QuadraticBezierCurve=function(a,b,c){this.v0=a;this.v1=b;this.v2=c};THREE.QuadraticBezierCurve.prototype=Object.create(THREE.Curve.prototype);THREE.QuadraticBezierCurve.prototype.getPoint=function(a){var b;b=THREE.Shape.Utils.b2(a,this.v0.x,this.v1.x,this.v2.x);a=THREE.Shape.Utils.b2(a,this.v0.y,this.v1.y,this.v2.y);return new THREE.Vector2(b,a)}; -THREE.QuadraticBezierCurve.prototype.getTangent=function(a){var b;b=THREE.Curve.Utils.tangentQuadraticBezier(a,this.v0.x,this.v1.x,this.v2.x);a=THREE.Curve.Utils.tangentQuadraticBezier(a,this.v0.y,this.v1.y,this.v2.y);b=new THREE.Vector2(b,a);b.normalize();return b};THREE.CubicBezierCurve=function(a,b,c,d){this.v0=a;this.v1=b;this.v2=c;this.v3=d};THREE.CubicBezierCurve.prototype=Object.create(THREE.Curve.prototype); -THREE.CubicBezierCurve.prototype.getPoint=function(a){var b;b=THREE.Shape.Utils.b3(a,this.v0.x,this.v1.x,this.v2.x,this.v3.x);a=THREE.Shape.Utils.b3(a,this.v0.y,this.v1.y,this.v2.y,this.v3.y);return new THREE.Vector2(b,a)};THREE.CubicBezierCurve.prototype.getTangent=function(a){var b;b=THREE.Curve.Utils.tangentCubicBezier(a,this.v0.x,this.v1.x,this.v2.x,this.v3.x);a=THREE.Curve.Utils.tangentCubicBezier(a,this.v0.y,this.v1.y,this.v2.y,this.v3.y);b=new THREE.Vector2(b,a);b.normalize();return b}; -THREE.SplineCurve=function(a){this.points=void 0==a?[]:a};THREE.SplineCurve.prototype=Object.create(THREE.Curve.prototype);THREE.SplineCurve.prototype.getPoint=function(a){var b=new THREE.Vector2,c=[],d=this.points,e;e=(d.length-1)*a;a=Math.floor(e);e-=a;c[0]=0==a?a:a-1;c[1]=a;c[2]=a>d.length-2?d.length-1:a+1;c[3]=a>d.length-3?d.length-1:a+2;b.x=THREE.Curve.Utils.interpolate(d[c[0]].x,d[c[1]].x,d[c[2]].x,d[c[3]].x,e);b.y=THREE.Curve.Utils.interpolate(d[c[0]].y,d[c[1]].y,d[c[2]].y,d[c[3]].y,e);return b}; -THREE.EllipseCurve=function(a,b,c,d,e,f,g){this.aX=a;this.aY=b;this.xRadius=c;this.yRadius=d;this.aStartAngle=e;this.aEndAngle=f;this.aClockwise=g};THREE.EllipseCurve.prototype=Object.create(THREE.Curve.prototype); -THREE.EllipseCurve.prototype.getPoint=function(a){var b;b=this.aEndAngle-this.aStartAngle;0>b&&(b+=2*Math.PI);b>2*Math.PI&&(b-=2*Math.PI);b=!0===this.aClockwise?this.aEndAngle+(1-a)*(2*Math.PI-b):this.aStartAngle+a*b;a=this.aX+this.xRadius*Math.cos(b);b=this.aY+this.yRadius*Math.sin(b);return new THREE.Vector2(a,b)};THREE.ArcCurve=function(a,b,c,d,e,f){THREE.EllipseCurve.call(this,a,b,c,c,d,e,f)};THREE.ArcCurve.prototype=Object.create(THREE.EllipseCurve.prototype); -THREE.LineCurve3=THREE.Curve.create(function(a,b){this.v1=a;this.v2=b},function(a){var b=new THREE.Vector3;b.subVectors(this.v2,this.v1);b.multiplyScalar(a);b.add(this.v1);return b});THREE.QuadraticBezierCurve3=THREE.Curve.create(function(a,b,c){this.v0=a;this.v1=b;this.v2=c},function(a){var b,c;b=THREE.Shape.Utils.b2(a,this.v0.x,this.v1.x,this.v2.x);c=THREE.Shape.Utils.b2(a,this.v0.y,this.v1.y,this.v2.y);a=THREE.Shape.Utils.b2(a,this.v0.z,this.v1.z,this.v2.z);return new THREE.Vector3(b,c,a)}); -THREE.CubicBezierCurve3=THREE.Curve.create(function(a,b,c,d){this.v0=a;this.v1=b;this.v2=c;this.v3=d},function(a){var b,c;b=THREE.Shape.Utils.b3(a,this.v0.x,this.v1.x,this.v2.x,this.v3.x);c=THREE.Shape.Utils.b3(a,this.v0.y,this.v1.y,this.v2.y,this.v3.y);a=THREE.Shape.Utils.b3(a,this.v0.z,this.v1.z,this.v2.z,this.v3.z);return new THREE.Vector3(b,c,a)}); -THREE.SplineCurve3=THREE.Curve.create(function(a){this.points=void 0==a?[]:a},function(a){var b=new THREE.Vector3,c=[],d=this.points,e;a*=d.length-1;e=Math.floor(a);a-=e;c[0]=0==e?e:e-1;c[1]=e;c[2]=e>d.length-2?d.length-1:e+1;c[3]=e>d.length-3?d.length-1:e+2;e=d[c[0]];var f=d[c[1]],g=d[c[2]],c=d[c[3]];b.x=THREE.Curve.Utils.interpolate(e.x,f.x,g.x,c.x,a);b.y=THREE.Curve.Utils.interpolate(e.y,f.y,g.y,c.y,a);b.z=THREE.Curve.Utils.interpolate(e.z,f.z,g.z,c.z,a);return b}); -THREE.ClosedSplineCurve3=THREE.Curve.create(function(a){this.points=void 0==a?[]:a},function(a){var b=new THREE.Vector3,c=[],d=this.points,e;e=(d.length-0)*a;a=Math.floor(e);e-=a;a+=0a.hierarchy[b].keys[c].time&&(a.hierarchy[b].keys[c].time= -0),void 0!==a.hierarchy[b].keys[c].rot&&!(a.hierarchy[b].keys[c].rot instanceof THREE.Quaternion)){var d=a.hierarchy[b].keys[c].rot;a.hierarchy[b].keys[c].rot=(new THREE.Quaternion).fromArray(d)}if(a.hierarchy[b].keys.length&&void 0!==a.hierarchy[b].keys[0].morphTargets){d={};for(c=0;cd;d++){for(var e= -this.keyTypes[d],f=this.data.hierarchy[a].keys[0],g=this.getNextKeyWith(e,a,1);g.timef.index;)f=g,g=this.getNextKeyWith(e,a,g.index+1);c.prevKey[e]=f;c.nextKey[e]=g}}}; -THREE.Animation.prototype.update=function(){var a=[],b=new THREE.Vector3,c=new THREE.Vector3,d=new THREE.Quaternion,e=function(a,b){var c=[],d=[],e,q,r,t,s,p;e=(a.length-1)*b;q=Math.floor(e);e-=q;c[0]=0===q?q:q-1;c[1]=q;c[2]=q>a.length-2?q:q+1;c[3]=q>a.length-3?q:q+2;q=a[c[0]];t=a[c[1]];s=a[c[2]];p=a[c[3]];c=e*e;r=e*c;d[0]=f(q[0],t[0],s[0],p[0],e,c,r);d[1]=f(q[1],t[1],s[1],p[1],e,c,r);d[2]=f(q[2],t[2],s[2],p[2],e,c,r);return d},f=function(a,b,c,d,e,f,r){a=0.5*(c-a);d=0.5*(d-b);return(2*(b-c)+a+d)* -r+(-3*(b-c)-2*a-d)*f+a*e+b};return function(f){if(!1!==this.isPlaying&&(this.currentTime+=f*this.timeScale,0!==this.weight)){f=this.data.length;if(!0===this.loop&&this.currentTime>f)this.currentTime%=f,this.reset();else if(!1===this.loop&&this.currentTime>f){this.stop();return}f=0;for(var h=this.hierarchy.length;fn;n++){var q=this.keyTypes[n],r=l.prevKey[q],t=l.nextKey[q];if(t.time<=this.currentTime){r=this.data.hierarchy[f].keys[0]; -for(t=this.getNextKeyWith(q,f,1);t.timer.index;)r=t,t=this.getNextKeyWith(q,f,t.index+1);l.prevKey[q]=r;l.nextKey[q]=t}k.matrixAutoUpdate=!0;k.matrixWorldNeedsUpdate=!0;var s=(this.currentTime-r.time)/(t.time-r.time),p=r[q],v=t[q];0>s&&(s=0);1a&&(this.currentTime%=a);this.currentTime=Math.min(this.currentTime,a);a=0;for(var b=this.hierarchy.length;af.index;)f=g,g=e[f.index+1];d.prevKey= -f;d.nextKey=g}g.time>=this.currentTime?f.interpolate(g,this.currentTime):f.interpolate(g,g.time);this.data.hierarchy[a].node.updateMatrix();c.matrixWorldNeedsUpdate=!0}}}};THREE.KeyFrameAnimation.prototype.getNextKeyWith=function(a,b,c){b=this.data.hierarchy[b].keys;for(c%=b.length;cthis.duration&&(this.currentTime%=this.duration);this.currentTime=Math.min(this.currentTime,this.duration);c=this.duration/this.frames;var d=Math.floor(this.currentTime/c);d!=b&&(this.mesh.morphTargetInfluences[a]=0,this.mesh.morphTargetInfluences[b]=1,this.mesh.morphTargetInfluences[d]= -0,a=b,b=d);this.mesh.morphTargetInfluences[d]=this.currentTime%c/c;this.mesh.morphTargetInfluences[a]=1-this.mesh.morphTargetInfluences[d]}}}()}; -THREE.BoxGeometry=function(a,b,c,d,e,f){function g(a,b,c,d,e,f,g,p){var v,w=h.widthSegments,u=h.heightSegments,D=e/2,A=f/2,x=h.vertices.length;if("x"===a&&"y"===b||"y"===a&&"x"===b)v="z";else if("x"===a&&"z"===b||"z"===a&&"x"===b)v="y",u=h.depthSegments;else if("z"===a&&"y"===b||"y"===a&&"z"===b)v="x",w=h.depthSegments;var C=w+1,I=u+1,z=e/w,y=f/u,K=new THREE.Vector3;K[v]=0=e)return new THREE.Vector2(c,a);e=Math.sqrt(e/2)}else a=!1,1E-10e?-1E-10>g&& -(a=!0):d(f)==d(h)&&(a=!0),a?(c=-f,a=e,e=Math.sqrt(k)):(c=e,a=f,e=Math.sqrt(k/2));return new THREE.Vector2(c/e,a/e)}function e(c,d){var e,f;for(H=c.length;0<=--H;){e=H;f=H-1;0>f&&(f=c.length-1);for(var g=0,h=t+2*n,g=0;gMath.abs(c-k)?[new THREE.Vector2(b,1-e),new THREE.Vector2(d,1-f),new THREE.Vector2(l,1-g),new THREE.Vector2(q,1-a)]:[new THREE.Vector2(c,1-e),new THREE.Vector2(k,1-f),new THREE.Vector2(n,1-g),new THREE.Vector2(r,1-a)]}};THREE.ExtrudeGeometry.__v1=new THREE.Vector2;THREE.ExtrudeGeometry.__v2=new THREE.Vector2;THREE.ExtrudeGeometry.__v3=new THREE.Vector2;THREE.ExtrudeGeometry.__v4=new THREE.Vector2; -THREE.ExtrudeGeometry.__v5=new THREE.Vector2;THREE.ExtrudeGeometry.__v6=new THREE.Vector2;THREE.ShapeGeometry=function(a,b){THREE.Geometry.call(this);!1===a instanceof Array&&(a=[a]);this.addShapeList(a,b);this.computeFaceNormals()};THREE.ShapeGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.ShapeGeometry.prototype.addShapeList=function(a,b){for(var c=0,d=a.length;cc&&1===a.x&&(a=new THREE.Vector2(a.x-1,a.y));0===b.x&&0===b.z&&(a=new THREE.Vector2(c/2/Math.PI+0.5, -a.y));return a.clone()}THREE.Geometry.call(this);c=c||1;d=d||0;for(var k=this,l=0,n=a.length;lt&&(0.2>d&&(b[0].x+=1),0.2>a&&(b[1].x+=1),0.2>q&&(b[2].x+=1));l=0;for(n=this.vertices.length;lc.y?this.quaternion.set(1,0,0,0):(a.set(c.z,0,-c.x).normalize(),b=Math.acos(c.y),this.quaternion.setFromAxisAngle(a,b))}}(); -THREE.ArrowHelper.prototype.setLength=function(a,b,c){void 0===b&&(b=0.2*a);void 0===c&&(c=0.2*b);this.line.scale.set(1,a,1);this.line.updateMatrix();this.cone.scale.set(c,b,c);this.cone.position.y=a;this.cone.updateMatrix()};THREE.ArrowHelper.prototype.setColor=function(a){this.line.material.color.set(a);this.cone.material.color.set(a)}; -THREE.BoxHelper=function(a){var b=new THREE.BufferGeometry;b.addAttribute("position",new THREE.BufferAttribute(new Float32Array(72),3));THREE.Line.call(this,b,new THREE.LineBasicMaterial({color:16776960}),THREE.LinePieces);void 0!==a&&this.update(a)};THREE.BoxHelper.prototype=Object.create(THREE.Line.prototype); -THREE.BoxHelper.prototype.update=function(a){var b=a.geometry;null===b.boundingBox&&b.computeBoundingBox();var c=b.boundingBox.min,b=b.boundingBox.max,d=this.geometry.attributes.position.array;d[0]=b.x;d[1]=b.y;d[2]=b.z;d[3]=c.x;d[4]=b.y;d[5]=b.z;d[6]=c.x;d[7]=b.y;d[8]=b.z;d[9]=c.x;d[10]=c.y;d[11]=b.z;d[12]=c.x;d[13]=c.y;d[14]=b.z;d[15]=b.x;d[16]=c.y;d[17]=b.z;d[18]=b.x;d[19]=c.y;d[20]=b.z;d[21]=b.x;d[22]=b.y;d[23]=b.z;d[24]=b.x;d[25]=b.y;d[26]=c.z;d[27]=c.x;d[28]=b.y;d[29]=c.z;d[30]=c.x;d[31]=b.y; -d[32]=c.z;d[33]=c.x;d[34]=c.y;d[35]=c.z;d[36]=c.x;d[37]=c.y;d[38]=c.z;d[39]=b.x;d[40]=c.y;d[41]=c.z;d[42]=b.x;d[43]=c.y;d[44]=c.z;d[45]=b.x;d[46]=b.y;d[47]=c.z;d[48]=b.x;d[49]=b.y;d[50]=b.z;d[51]=b.x;d[52]=b.y;d[53]=c.z;d[54]=c.x;d[55]=b.y;d[56]=b.z;d[57]=c.x;d[58]=b.y;d[59]=c.z;d[60]=c.x;d[61]=c.y;d[62]=b.z;d[63]=c.x;d[64]=c.y;d[65]=c.z;d[66]=b.x;d[67]=c.y;d[68]=b.z;d[69]=b.x;d[70]=c.y;d[71]=c.z;this.geometry.attributes.position.needsUpdate=!0;this.geometry.computeBoundingSphere();this.matrixAutoUpdate= -!1;this.matrixWorld=a.matrixWorld};THREE.BoundingBoxHelper=function(a,b){var c=void 0!==b?b:8947848;this.object=a;this.box=new THREE.Box3;THREE.Mesh.call(this,new THREE.BoxGeometry(1,1,1),new THREE.MeshBasicMaterial({color:c,wireframe:!0}))};THREE.BoundingBoxHelper.prototype=Object.create(THREE.Mesh.prototype);THREE.BoundingBoxHelper.prototype.update=function(){this.box.setFromObject(this.object);this.box.size(this.scale);this.box.center(this.position)}; -THREE.CameraHelper=function(a){function b(a,b,d){c(a,d);c(b,d)}function c(a,b){d.vertices.push(new THREE.Vector3);d.colors.push(new THREE.Color(b));void 0===f[a]&&(f[a]=[]);f[a].push(d.vertices.length-1)}var d=new THREE.Geometry,e=new THREE.LineBasicMaterial({color:16777215,vertexColors:THREE.FaceColors}),f={};b("n1","n2",16755200);b("n2","n4",16755200);b("n4","n3",16755200);b("n3","n1",16755200);b("f1","f2",16755200);b("f2","f4",16755200);b("f4","f3",16755200);b("f3","f1",16755200);b("n1","f1",16755200); -b("n2","f2",16755200);b("n3","f3",16755200);b("n4","f4",16755200);b("p","n1",16711680);b("p","n2",16711680);b("p","n3",16711680);b("p","n4",16711680);b("u1","u2",43775);b("u2","u3",43775);b("u3","u1",43775);b("c","t",16777215);b("p","c",3355443);b("cn1","cn2",3355443);b("cn3","cn4",3355443);b("cf1","cf2",3355443);b("cf3","cf4",3355443);THREE.Line.call(this,d,e,THREE.LinePieces);this.camera=a;this.matrixWorld=a.matrixWorld;this.matrixAutoUpdate=!1;this.pointMap=f;this.update()}; -THREE.CameraHelper.prototype=Object.create(THREE.Line.prototype); -THREE.CameraHelper.prototype.update=function(){var a=new THREE.Vector3,b=new THREE.Camera,c=new THREE.Projector;return function(){function d(d,g,h,k){a.set(g,h,k);c.unprojectVector(a,b);d=e.pointMap[d];if(void 0!==d)for(g=0,h=d.length;gs;s++){d[0]=t[g[s]];d[1]=t[g[(s+1)%3]];d.sort(f);var p=d.toString();void 0===e[p]?(e[p]={vert1:d[0],vert2:d[1],face1:q,face2:void 0},n++):e[p].face2=q}h.addAttribute("position",new THREE.Float32Attribute(6*n,3));d=h.attributes.position.array; -f=0;for(p in e)if(g=e[p],void 0===g.face2||0.9999>k[g.face1].normal.dot(k[g.face2].normal))n=l[g.vert1],d[f++]=n.x,d[f++]=n.y,d[f++]=n.z,n=l[g.vert2],d[f++]=n.x,d[f++]=n.y,d[f++]=n.z;THREE.Line.call(this,h,new THREE.LineBasicMaterial({color:c}),THREE.LinePieces);this.matrixAutoUpdate=!1;this.matrixWorld=a.matrixWorld};THREE.EdgesHelper.prototype=Object.create(THREE.Line.prototype); -THREE.FaceNormalsHelper=function(a,b,c,d){this.object=a;this.size=void 0!==b?b:1;a=void 0!==c?c:16776960;d=void 0!==d?d:1;b=new THREE.Geometry;c=0;for(var e=this.object.geometry.faces.length;cb;b++)a.faces[b].color=this.colors[4>b?0:1];b=new THREE.MeshBasicMaterial({vertexColors:THREE.FaceColors,wireframe:!0});this.lightSphere=new THREE.Mesh(a,b);this.add(this.lightSphere); -this.update()};THREE.HemisphereLightHelper.prototype=Object.create(THREE.Object3D.prototype);THREE.HemisphereLightHelper.prototype.dispose=function(){this.lightSphere.geometry.dispose();this.lightSphere.material.dispose()}; -THREE.HemisphereLightHelper.prototype.update=function(){var a=new THREE.Vector3;return function(){this.colors[0].copy(this.light.color).multiplyScalar(this.light.intensity);this.colors[1].copy(this.light.groundColor).multiplyScalar(this.light.intensity);this.lightSphere.lookAt(a.setFromMatrixPosition(this.light.matrixWorld).negate());this.lightSphere.geometry.colorsNeedUpdate=!0}}(); -THREE.PointLightHelper=function(a,b){this.light=a;this.light.updateMatrixWorld();var c=new THREE.SphereGeometry(b,4,2),d=new THREE.MeshBasicMaterial({wireframe:!0,fog:!1});d.color.copy(this.light.color).multiplyScalar(this.light.intensity);THREE.Mesh.call(this,c,d);this.matrixWorld=this.light.matrixWorld;this.matrixAutoUpdate=!1};THREE.PointLightHelper.prototype=Object.create(THREE.Mesh.prototype);THREE.PointLightHelper.prototype.dispose=function(){this.geometry.dispose();this.material.dispose()}; -THREE.PointLightHelper.prototype.update=function(){this.material.color.copy(this.light.color).multiplyScalar(this.light.intensity)}; -THREE.SkeletonHelper=function(a){this.bones=this.getBoneList(a);for(var b=new THREE.Geometry,c=0;cp;p++){d[0]=s[g[p]];d[1]=s[g[(p+1)%3]];d.sort(f);var v=d.toString();void 0===e[v]&&(q[2*n]=d[0],q[2*n+1]=d[1],e[v]=!0,n++)}d=new Float32Array(6*n);r=0;for(t=n;rp;p++)n= -k[q[2*r+p]],g=6*r+3*p,d[g+0]=n.x,d[g+1]=n.y,d[g+2]=n.z;h.addAttribute("position",new THREE.BufferAttribute(d,3))}else if(a.geometry instanceof THREE.BufferGeometry){if(void 0!==a.geometry.attributes.index){for(var k=a.geometry.attributes.position.array,t=a.geometry.attributes.index.array,l=a.geometry.offsets,n=0,q=new Uint32Array(2*t.length),s=0,w=l.length;sp;p++)d[0]=g+t[r+p],d[1]=g+t[r+(p+1)%3],d.sort(f),v=d.toString(), -void 0===e[v]&&(q[2*n]=d[0],q[2*n+1]=d[1],e[v]=!0,n++);d=new Float32Array(6*n);r=0;for(t=n;rp;p++)g=6*r+3*p,n=3*q[2*r+p],d[g+0]=k[n],d[g+1]=k[n+1],d[g+2]=k[n+2]}else for(k=a.geometry.attributes.position.array,n=k.length/3,q=n/3,d=new Float32Array(6*n),r=0,t=q;rp;p++)g=18*r+6*p,q=9*r+3*p,d[g+0]=k[q],d[g+1]=k[q+1],d[g+2]=k[q+2],n=9*r+(p+1)%3*3,d[g+3]=k[n],d[g+4]=k[n+1],d[g+5]=k[n+2];h.addAttribute("position",new THREE.BufferAttribute(d,3))}THREE.Line.call(this,h,new THREE.LineBasicMaterial({color:c}), -THREE.LinePieces);this.matrixAutoUpdate=!1;this.matrixWorld=a.matrixWorld};THREE.WireframeHelper.prototype=Object.create(THREE.Line.prototype);THREE.ImmediateRenderObject=function(){THREE.Object3D.call(this);this.render=function(a){}};THREE.ImmediateRenderObject.prototype=Object.create(THREE.Object3D.prototype);THREE.LensFlare=function(a,b,c,d,e){THREE.Object3D.call(this);this.lensFlares=[];this.positionScreen=new THREE.Vector3;this.customUpdateCallback=void 0;void 0!==a&&this.add(a,b,c,d,e)}; -THREE.LensFlare.prototype=Object.create(THREE.Object3D.prototype);THREE.LensFlare.prototype.add=function(a,b,c,d,e,f){void 0===b&&(b=-1);void 0===c&&(c=0);void 0===f&&(f=1);void 0===e&&(e=new THREE.Color(16777215));void 0===d&&(d=THREE.NormalBlending);c=Math.min(c,Math.max(0,c));this.lensFlares.push({texture:a,size:b,distance:c,x:0,y:0,z:0,scale:1,rotation:1,opacity:f,color:e,blending:d})}; -THREE.LensFlare.prototype.updateLensFlares=function(){var a,b=this.lensFlares.length,c,d=2*-this.positionScreen.x,e=2*-this.positionScreen.y;for(a=0;ah.end&&(h.end=f);c||(c=k)}}for(k in d)h=d[k],this.createAnimation(k,h.start,h.end,a);this.firstAnimation=c}; -THREE.MorphBlendMesh.prototype.setAnimationDirectionForward=function(a){if(a=this.animationsMap[a])a.direction=1,a.directionBackwards=!1};THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward=function(a){if(a=this.animationsMap[a])a.direction=-1,a.directionBackwards=!0};THREE.MorphBlendMesh.prototype.setAnimationFPS=function(a,b){var c=this.animationsMap[a];c&&(c.fps=b,c.duration=(c.end-c.start)/c.fps)}; -THREE.MorphBlendMesh.prototype.setAnimationDuration=function(a,b){var c=this.animationsMap[a];c&&(c.duration=b,c.fps=(c.end-c.start)/c.duration)};THREE.MorphBlendMesh.prototype.setAnimationWeight=function(a,b){var c=this.animationsMap[a];c&&(c.weight=b)};THREE.MorphBlendMesh.prototype.setAnimationTime=function(a,b){var c=this.animationsMap[a];c&&(c.time=b)};THREE.MorphBlendMesh.prototype.getAnimationTime=function(a){var b=0;if(a=this.animationsMap[a])b=a.time;return b}; -THREE.MorphBlendMesh.prototype.getAnimationDuration=function(a){var b=-1;if(a=this.animationsMap[a])b=a.duration;return b};THREE.MorphBlendMesh.prototype.playAnimation=function(a){var b=this.animationsMap[a];b?(b.time=0,b.active=!0):console.warn("animation["+a+"] undefined")};THREE.MorphBlendMesh.prototype.stopAnimation=function(a){if(a=this.animationsMap[a])a.active=!1}; -THREE.MorphBlendMesh.prototype.update=function(a){for(var b=0,c=this.animationsList.length;bd.duration||0>d.time)d.direction*=-1,d.time>d.duration&&(d.time=d.duration,d.directionBackwards=!0),0>d.time&&(d.time=0,d.directionBackwards=!1)}else d.time%=d.duration,0>d.time&&(d.time+=d.duration);var f=d.startFrame+THREE.Math.clamp(Math.floor(d.time/e),0,d.length-1),g=d.weight; -f!==d.currentFrame&&(this.morphTargetInfluences[d.lastFrame]=0,this.morphTargetInfluences[d.currentFrame]=1*g,this.morphTargetInfluences[f]=0,d.lastFrame=d.currentFrame,d.currentFrame=f);e=d.time%e/e;d.directionBackwards&&(e=1-e);this.morphTargetInfluences[d.currentFrame]=e*g;this.morphTargetInfluences[d.lastFrame]=(1-e)*g}}}; -THREE.LensFlarePlugin=function(){function a(a,b){var d=c.createProgram(),e=c.createShader(c.FRAGMENT_SHADER),f=c.createShader(c.VERTEX_SHADER),g="precision "+b+" float;\n";c.shaderSource(e,g+a.fragmentShader);c.shaderSource(f,g+a.vertexShader);c.compileShader(e);c.compileShader(f);c.attachShader(d,e);c.attachShader(d,f);c.linkProgram(d);return d}var b=[],c,d,e,f,g,h,k,l,n,q,r,t,s;this.init=function(b){c=b.context;d=b;e=b.getPrecision();f=new Float32Array(16);g=new Uint16Array(6);b=0;f[b++]=-1;f[b++]= --1;f[b++]=0;f[b++]=0;f[b++]=1;f[b++]=-1;f[b++]=1;f[b++]=0;f[b++]=1;f[b++]=1;f[b++]=1;f[b++]=1;f[b++]=-1;f[b++]=1;f[b++]=0;f[b++]=1;b=0;g[b++]=0;g[b++]=1;g[b++]=2;g[b++]=0;g[b++]=2;g[b++]=3;h=c.createBuffer();k=c.createBuffer();c.bindBuffer(c.ARRAY_BUFFER,h);c.bufferData(c.ARRAY_BUFFER,f,c.STATIC_DRAW);c.bindBuffer(c.ELEMENT_ARRAY_BUFFER,k);c.bufferData(c.ELEMENT_ARRAY_BUFFER,g,c.STATIC_DRAW);l=c.createTexture();n=c.createTexture();c.bindTexture(c.TEXTURE_2D,l);c.texImage2D(c.TEXTURE_2D,0,c.RGB,16, -16,0,c.RGB,c.UNSIGNED_BYTE,null);c.texParameteri(c.TEXTURE_2D,c.TEXTURE_WRAP_S,c.CLAMP_TO_EDGE);c.texParameteri(c.TEXTURE_2D,c.TEXTURE_WRAP_T,c.CLAMP_TO_EDGE);c.texParameteri(c.TEXTURE_2D,c.TEXTURE_MAG_FILTER,c.NEAREST);c.texParameteri(c.TEXTURE_2D,c.TEXTURE_MIN_FILTER,c.NEAREST);c.bindTexture(c.TEXTURE_2D,n);c.texImage2D(c.TEXTURE_2D,0,c.RGBA,16,16,0,c.RGBA,c.UNSIGNED_BYTE,null);c.texParameteri(c.TEXTURE_2D,c.TEXTURE_WRAP_S,c.CLAMP_TO_EDGE);c.texParameteri(c.TEXTURE_2D,c.TEXTURE_WRAP_T,c.CLAMP_TO_EDGE); -c.texParameteri(c.TEXTURE_2D,c.TEXTURE_MAG_FILTER,c.NEAREST);c.texParameteri(c.TEXTURE_2D,c.TEXTURE_MIN_FILTER,c.NEAREST);0>=c.getParameter(c.MAX_VERTEX_TEXTURE_IMAGE_UNITS)?(q=!1,r=a(THREE.ShaderFlares.lensFlare,e)):(q=!0,r=a(THREE.ShaderFlares.lensFlareVertexTexture,e));t={};s={};t.vertex=c.getAttribLocation(r,"position");t.uv=c.getAttribLocation(r,"uv");s.renderType=c.getUniformLocation(r,"renderType");s.map=c.getUniformLocation(r,"map");s.occlusionMap=c.getUniformLocation(r,"occlusionMap");s.opacity= -c.getUniformLocation(r,"opacity");s.color=c.getUniformLocation(r,"color");s.scale=c.getUniformLocation(r,"scale");s.rotation=c.getUniformLocation(r,"rotation");s.screenPosition=c.getUniformLocation(r,"screenPosition")};this.render=function(a,e,f,g){b.length=0;a.traverseVisible(function(a){a instanceof THREE.LensFlare&&b.push(a)});if(0!==b.length){a=new THREE.Vector3;var D=g/f,A=0.5*f,x=0.5*g,C=16/g,I=new THREE.Vector2(C*D,C),z=new THREE.Vector3(1,1,0),y=new THREE.Vector2(1,1),K=s,C=t;c.useProgram(r); -c.enableVertexAttribArray(t.vertex);c.enableVertexAttribArray(t.uv);c.uniform1i(K.occlusionMap,0);c.uniform1i(K.map,1);c.bindBuffer(c.ARRAY_BUFFER,h);c.vertexAttribPointer(C.vertex,2,c.FLOAT,!1,16,0);c.vertexAttribPointer(C.uv,2,c.FLOAT,!1,16,8);c.bindBuffer(c.ELEMENT_ARRAY_BUFFER,k);c.disable(c.CULL_FACE);c.depthMask(!1);for(var N=0,ba=b.length;NK;K++)C[K]=new THREE.Vector3,A[K]=new THREE.Vector3;C=x.shadowCascadeNearZ[y];x=x.shadowCascadeFarZ[y];A[0].set(-1,-1,C);A[1].set(1,-1,C);A[2].set(-1,1,C);A[3].set(1,1,C);A[4].set(-1,-1,x);A[5].set(1,-1,x);A[6].set(-1,1,x);A[7].set(1,1,x);z.originalCamera=s;A=new THREE.Gyroscope; -A.position.copy(w.shadowCascadeOffset);A.add(z);A.add(z.target);s.add(A);w.shadowCascadeArray[D]=z;console.log("Created virtualLight",z)}y=w;C=D;x=y.shadowCascadeArray[C];x.position.copy(y.position);x.target.position.copy(y.target.position);x.lookAt(x.target);x.shadowCameraVisible=y.shadowCameraVisible;x.shadowDarkness=y.shadowDarkness;x.shadowBias=y.shadowCascadeBias[C];A=y.shadowCascadeNearZ[C];y=y.shadowCascadeFarZ[C];x=x.pointsFrustum;x[0].z=A;x[1].z=A;x[2].z=A;x[3].z=A;x[4].z=y;x[5].z=y;x[6].z= -y;x[7].z=y;I[u]=z;u++}else I[u]=w,u++;p=0;for(v=I.length;py;y++)C=x[y],C.copy(A[y]),THREE.ShadowMapPlugin.__projector.unprojectVector(C,D),C.applyMatrix4(u.matrixWorldInverse),C.xn.x&&(n.x=C.x),C.yn.y&&(n.y=C.y),C.zn.z&&(n.z=C.z);u.left=l.x;u.right=n.x;u.top=n.y;u.bottom=l.y;u.updateProjectionMatrix()}u=w.shadowMap;A=w.shadowMatrix; -D=w.shadowCamera;D.position.setFromMatrixPosition(w.matrixWorld);q.setFromMatrixPosition(w.target.matrixWorld);D.lookAt(q);D.updateMatrixWorld();D.matrixWorldInverse.getInverse(D.matrixWorld);w.cameraHelper&&(w.cameraHelper.visible=w.shadowCameraVisible);w.shadowCameraVisible&&w.cameraHelper.update();A.set(0.5,0,0,0.5,0,0.5,0,0.5,0,0,0.5,0.5,0,0,0,1);A.multiply(D.projectionMatrix);A.multiply(D.matrixWorldInverse);k.multiplyMatrices(D.projectionMatrix,D.matrixWorldInverse);h.setFromMatrix(k);c.setRenderTarget(u); -c.clear();r.length=0;a(t,t,D);w=0;for(u=r.length;w 0 ) {\nfloat depth = gl_FragCoord.z / gl_FragCoord.w;\nfloat fogFactor = 0.0;\nif ( fogType == 1 ) {\nfogFactor = smoothstep( fogNear, fogFar, depth );\n} else {\nconst float LOG2 = 1.442695;\nfloat fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );\nfogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );\n}\ngl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );\n}\n}"].join("\n")); -u.compileShader(x);u.compileShader(P);u.attachShader(w,x);u.attachShader(w,P);u.linkProgram(w);K=w;p=u.getAttribLocation(K,"position");v=u.getAttribLocation(K,"uv");a=u.getUniformLocation(K,"uvOffset");b=u.getUniformLocation(K,"uvScale");c=u.getUniformLocation(K,"rotation");d=u.getUniformLocation(K,"scale");e=u.getUniformLocation(K,"color");f=u.getUniformLocation(K,"map");g=u.getUniformLocation(K,"opacity");h=u.getUniformLocation(K,"modelViewMatrix");k=u.getUniformLocation(K,"projectionMatrix");l= -u.getUniformLocation(K,"fogType");n=u.getUniformLocation(K,"fogDensity");q=u.getUniformLocation(K,"fogNear");r=u.getUniformLocation(K,"fogFar");t=u.getUniformLocation(K,"fogColor");s=u.getUniformLocation(K,"alphaTest");w=document.createElement("canvas");w.width=8;w.height=8;x=w.getContext("2d");x.fillStyle="white";x.fillRect(0,0,8,8);A=new THREE.Texture(w);A.needsUpdate=!0};this.render=function(C,I,P,O){x.length=0;C.traverseVisible(function(a){a instanceof THREE.Sprite&&x.push(a)});if(0!==x.length){u.useProgram(K); -u.enableVertexAttribArray(p);u.enableVertexAttribArray(v);u.disable(u.CULL_FACE);u.enable(u.BLEND);u.bindBuffer(u.ARRAY_BUFFER,z);u.vertexAttribPointer(p,2,u.FLOAT,!1,16,0);u.vertexAttribPointer(v,2,u.FLOAT,!1,16,8);u.bindBuffer(u.ELEMENT_ARRAY_BUFFER,y);u.uniformMatrix4fv(k,!1,I.projectionMatrix.elements);u.activeTexture(u.TEXTURE0);u.uniform1i(f,0);O=P=0;var J=C.fog;J?(u.uniform3f(t,J.color.r,J.color.g,J.color.b),J instanceof THREE.Fog?(u.uniform1f(q,J.near),u.uniform1f(r,J.far),u.uniform1i(l,1), -O=P=1):J instanceof THREE.FogExp2&&(u.uniform1f(n,J.density),u.uniform1i(l,2),O=P=2)):(u.uniform1i(l,0),O=P=0);for(var J=0,E=x.length;J> 16 & 255 ) / 255; + this.g = ( hex >> 8 & 255 ) / 255; + this.b = ( hex & 255 ) / 255; + + return this; + + }, + + setRGB: function ( r, g, b ) { + + this.r = r; + this.g = g; + this.b = b; + + return this; + + }, + + setHSL: function ( h, s, l ) { + + // h,s,l ranges are in 0.0 - 1.0 + + if ( s === 0 ) { + + this.r = this.g = this.b = l; + + } else { + + var hue2rgb = function ( p, q, t ) { + + if ( t < 0 ) t += 1; + if ( t > 1 ) t -= 1; + if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t; + if ( t < 1 / 2 ) return q; + if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t ); + return p; + + }; + + var p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); + var q = ( 2 * l ) - p; + + this.r = hue2rgb( q, p, h + 1 / 3 ); + this.g = hue2rgb( q, p, h ); + this.b = hue2rgb( q, p, h - 1 / 3 ); + + } + + return this; + + }, + + setStyle: function ( style ) { + + // rgb(255,0,0) + + if ( /^rgb\((\d+), ?(\d+), ?(\d+)\)$/i.test( style ) ) { + + var color = /^rgb\((\d+), ?(\d+), ?(\d+)\)$/i.exec( style ); + + this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255; + this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255; + this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255; + + return this; + + } + + // rgb(100%,0%,0%) + + if ( /^rgb\((\d+)\%, ?(\d+)\%, ?(\d+)\%\)$/i.test( style ) ) { + + var color = /^rgb\((\d+)\%, ?(\d+)\%, ?(\d+)\%\)$/i.exec( style ); + + this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100; + this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100; + this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100; + + return this; + + } + + // #ff0000 + + if ( /^\#([0-9a-f]{6})$/i.test( style ) ) { + + var color = /^\#([0-9a-f]{6})$/i.exec( style ); + + this.setHex( parseInt( color[ 1 ], 16 ) ); + + return this; + + } + + // #f00 + + if ( /^\#([0-9a-f])([0-9a-f])([0-9a-f])$/i.test( style ) ) { + + var color = /^\#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec( style ); + + this.setHex( parseInt( color[ 1 ] + color[ 1 ] + color[ 2 ] + color[ 2 ] + color[ 3 ] + color[ 3 ], 16 ) ); + + return this; + + } + + // red + + if ( /^(\w+)$/i.test( style ) ) { + + this.setHex( THREE.ColorKeywords[ style ] ); + + return this; + + } + + + }, + + copy: function ( color ) { + + this.r = color.r; + this.g = color.g; + this.b = color.b; + + return this; + + }, + + copyGammaToLinear: function ( color ) { + + this.r = color.r * color.r; + this.g = color.g * color.g; + this.b = color.b * color.b; + + return this; + + }, + + copyLinearToGamma: function ( color ) { + + this.r = Math.sqrt( color.r ); + this.g = Math.sqrt( color.g ); + this.b = Math.sqrt( color.b ); + + return this; + + }, + + convertGammaToLinear: function () { + + var r = this.r, g = this.g, b = this.b; + + this.r = r * r; + this.g = g * g; + this.b = b * b; + + return this; + + }, + + convertLinearToGamma: function () { + + this.r = Math.sqrt( this.r ); + this.g = Math.sqrt( this.g ); + this.b = Math.sqrt( this.b ); + + return this; + + }, + + getHex: function () { + + return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0; + + }, + + getHexString: function () { + + return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 ); + + }, + + getHSL: function ( optionalTarget ) { + + // h,s,l ranges are in 0.0 - 1.0 + + var hsl = optionalTarget || { h: 0, s: 0, l: 0 }; + + var r = this.r, g = this.g, b = this.b; + + var max = Math.max( r, g, b ); + var min = Math.min( r, g, b ); + + var hue, saturation; + var lightness = ( min + max ) / 2.0; + + if ( min === max ) { + + hue = 0; + saturation = 0; + + } else { + + var delta = max - min; + + saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); + + switch ( max ) { + + case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break; + case g: hue = ( b - r ) / delta + 2; break; + case b: hue = ( r - g ) / delta + 4; break; + + } + + hue /= 6; + + } + + hsl.h = hue; + hsl.s = saturation; + hsl.l = lightness; + + return hsl; + + }, + + getStyle: function () { + + return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')'; + + }, + + offsetHSL: function ( h, s, l ) { + + var hsl = this.getHSL(); + + hsl.h += h; hsl.s += s; hsl.l += l; + + this.setHSL( hsl.h, hsl.s, hsl.l ); + + return this; + + }, + + add: function ( color ) { + + this.r += color.r; + this.g += color.g; + this.b += color.b; + + return this; + + }, + + addColors: function ( color1, color2 ) { + + this.r = color1.r + color2.r; + this.g = color1.g + color2.g; + this.b = color1.b + color2.b; + + return this; + + }, + + addScalar: function ( s ) { + + this.r += s; + this.g += s; + this.b += s; + + return this; + + }, + + multiply: function ( color ) { + + this.r *= color.r; + this.g *= color.g; + this.b *= color.b; + + return this; + + }, + + multiplyScalar: function ( s ) { + + this.r *= s; + this.g *= s; + this.b *= s; + + return this; + + }, + + lerp: function ( color, alpha ) { + + this.r += ( color.r - this.r ) * alpha; + this.g += ( color.g - this.g ) * alpha; + this.b += ( color.b - this.b ) * alpha; + + return this; + + }, + + equals: function ( c ) { + + return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); + + }, + + fromArray: function ( array ) { + + this.r = array[ 0 ]; + this.g = array[ 1 ]; + this.b = array[ 2 ]; + + return this; + + }, + + toArray: function () { + + return [ this.r, this.g, this.b ]; + + }, + + clone: function () { + + return new THREE.Color().setRGB( this.r, this.g, this.b ); + + } + +}; + +THREE.ColorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF, +'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2, +'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50, +'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B, +'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B, +'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F, +'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3, +'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222, +'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700, +'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4, +'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00, +'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3, +'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA, +'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32, +'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3, +'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC, +'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD, +'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6, +'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9, +'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F, +'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE, +'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA, +'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0, +'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 }; + +// File:src/math/Quaternion.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author WestLangley / http://github.com/WestLangley + * @author bhouston / http://exocortex.com + */ + +THREE.Quaternion = function ( x, y, z, w ) { + + this._x = x || 0; + this._y = y || 0; + this._z = z || 0; + this._w = ( w !== undefined ) ? w : 1; + +}; + +THREE.Quaternion.prototype = { + + constructor: THREE.Quaternion, + + _x: 0,_y: 0, _z: 0, _w: 0, + + get x () { + + return this._x; + + }, + + set x ( value ) { + + this._x = value; + this.onChangeCallback(); + + }, + + get y () { + + return this._y; + + }, + + set y ( value ) { + + this._y = value; + this.onChangeCallback(); + + }, + + get z () { + + return this._z; + + }, + + set z ( value ) { + + this._z = value; + this.onChangeCallback(); + + }, + + get w () { + + return this._w; + + }, + + set w ( value ) { + + this._w = value; + this.onChangeCallback(); + + }, + + set: function ( x, y, z, w ) { + + this._x = x; + this._y = y; + this._z = z; + this._w = w; + + this.onChangeCallback(); + + return this; + + }, + + copy: function ( quaternion ) { + + this._x = quaternion.x; + this._y = quaternion.y; + this._z = quaternion.z; + this._w = quaternion.w; + + this.onChangeCallback(); + + return this; + + }, + + setFromEuler: function ( euler, update ) { + + if ( euler instanceof THREE.Euler === false ) { + + throw new Error( 'THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); + } + + // http://www.mathworks.com/matlabcentral/fileexchange/ + // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ + // content/SpinCalc.m + + var c1 = Math.cos( euler._x / 2 ); + var c2 = Math.cos( euler._y / 2 ); + var c3 = Math.cos( euler._z / 2 ); + var s1 = Math.sin( euler._x / 2 ); + var s2 = Math.sin( euler._y / 2 ); + var s3 = Math.sin( euler._z / 2 ); + + if ( euler.order === 'XYZ' ) { + + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + + } else if ( euler.order === 'YXZ' ) { + + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + + } else if ( euler.order === 'ZXY' ) { + + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + + } else if ( euler.order === 'ZYX' ) { + + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + + } else if ( euler.order === 'YZX' ) { + + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + + } else if ( euler.order === 'XZY' ) { + + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + + } + + if ( update !== false ) this.onChangeCallback(); + + return this; + + }, + + setFromAxisAngle: function ( axis, angle ) { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm + + // assumes axis is normalized + + var halfAngle = angle / 2, s = Math.sin( halfAngle ); + + this._x = axis.x * s; + this._y = axis.y * s; + this._z = axis.z * s; + this._w = Math.cos( halfAngle ); + + this.onChangeCallback(); + + return this; + + }, + + setFromRotationMatrix: function ( m ) { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + var te = m.elements, + + m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], + m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], + m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ], + + trace = m11 + m22 + m33, + s; + + if ( trace > 0 ) { + + s = 0.5 / Math.sqrt( trace + 1.0 ); + + this._w = 0.25 / s; + this._x = ( m32 - m23 ) * s; + this._y = ( m13 - m31 ) * s; + this._z = ( m21 - m12 ) * s; + + } else if ( m11 > m22 && m11 > m33 ) { + + s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); + + this._w = ( m32 - m23 ) / s; + this._x = 0.25 * s; + this._y = ( m12 + m21 ) / s; + this._z = ( m13 + m31 ) / s; + + } else if ( m22 > m33 ) { + + s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); + + this._w = ( m13 - m31 ) / s; + this._x = ( m12 + m21 ) / s; + this._y = 0.25 * s; + this._z = ( m23 + m32 ) / s; + + } else { + + s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); + + this._w = ( m21 - m12 ) / s; + this._x = ( m13 + m31 ) / s; + this._y = ( m23 + m32 ) / s; + this._z = 0.25 * s; + + } + + this.onChangeCallback(); + + return this; + + }, + + setFromUnitVectors: function () { + + // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final + + // assumes direction vectors vFrom and vTo are normalized + + var v1, r; + + var EPS = 0.000001; + + return function ( vFrom, vTo ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + r = vFrom.dot( vTo ) + 1; + + if ( r < EPS ) { + + r = 0; + + if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { + + v1.set( - vFrom.y, vFrom.x, 0 ); + + } else { + + v1.set( 0, - vFrom.z, vFrom.y ); + + } + + } else { + + v1.crossVectors( vFrom, vTo ); + + } + + this._x = v1.x; + this._y = v1.y; + this._z = v1.z; + this._w = r; + + this.normalize(); + + return this; + + } + + }(), + + inverse: function () { + + this.conjugate().normalize(); + + return this; + + }, + + conjugate: function () { + + this._x *= - 1; + this._y *= - 1; + this._z *= - 1; + + this.onChangeCallback(); + + return this; + + }, + + dot: function ( v ) { + + return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; + + }, + + lengthSq: function () { + + return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; + + }, + + length: function () { + + return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); + + }, + + normalize: function () { + + var l = this.length(); + + if ( l === 0 ) { + + this._x = 0; + this._y = 0; + this._z = 0; + this._w = 1; + + } else { + + l = 1 / l; + + this._x = this._x * l; + this._y = this._y * l; + this._z = this._z * l; + this._w = this._w * l; + + } + + this.onChangeCallback(); + + return this; + + }, + + multiply: function ( q, p ) { + + if ( p !== undefined ) { + + console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' ); + return this.multiplyQuaternions( q, p ); + + } + + return this.multiplyQuaternions( this, q ); + + }, + + multiplyQuaternions: function ( a, b ) { + + // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm + + var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; + var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; + + this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; + this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; + this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; + this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; + + this.onChangeCallback(); + + return this; + + }, + + multiplyVector3: function ( vector ) { + + console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' ); + return vector.applyQuaternion( this ); + + }, + + slerp: function ( qb, t ) { + + var x = this._x, y = this._y, z = this._z, w = this._w; + + // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ + + var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; + + if ( cosHalfTheta < 0 ) { + + this._w = - qb._w; + this._x = - qb._x; + this._y = - qb._y; + this._z = - qb._z; + + cosHalfTheta = - cosHalfTheta; + + } else { + + this.copy( qb ); + + } + + if ( cosHalfTheta >= 1.0 ) { + + this._w = w; + this._x = x; + this._y = y; + this._z = z; + + return this; + + } + + var halfTheta = Math.acos( cosHalfTheta ); + var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta ); + + if ( Math.abs( sinHalfTheta ) < 0.001 ) { + + this._w = 0.5 * ( w + this._w ); + this._x = 0.5 * ( x + this._x ); + this._y = 0.5 * ( y + this._y ); + this._z = 0.5 * ( z + this._z ); + + return this; + + } + + var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, + ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; + + this._w = ( w * ratioA + this._w * ratioB ); + this._x = ( x * ratioA + this._x * ratioB ); + this._y = ( y * ratioA + this._y * ratioB ); + this._z = ( z * ratioA + this._z * ratioB ); + + this.onChangeCallback(); + + return this; + + }, + + equals: function ( quaternion ) { + + return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); + + }, + + fromArray: function ( array ) { + + this._x = array[ 0 ]; + this._y = array[ 1 ]; + this._z = array[ 2 ]; + this._w = array[ 3 ]; + + this.onChangeCallback(); + + return this; + + }, + + toArray: function () { + + return [ this._x, this._y, this._z, this._w ]; + + }, + + onChange: function ( callback ) { + + this.onChangeCallback = callback; + + return this; + + }, + + onChangeCallback: function () {}, + + clone: function () { + + return new THREE.Quaternion( this._x, this._y, this._z, this._w ); + + } + +}; + +THREE.Quaternion.slerp = function ( qa, qb, qm, t ) { + + return qm.copy( qa ).slerp( qb, t ); + +} + +// File:src/math/Vector2.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author philogb / http://blog.thejit.org/ + * @author egraether / http://egraether.com/ + * @author zz85 / http://www.lab4games.net/zz85/blog + */ + +THREE.Vector2 = function ( x, y ) { + + this.x = x || 0; + this.y = y || 0; + +}; + +THREE.Vector2.prototype = { + + constructor: THREE.Vector2, + + set: function ( x, y ) { + + this.x = x; + this.y = y; + + return this; + + }, + + setX: function ( x ) { + + this.x = x; + + return this; + + }, + + setY: function ( y ) { + + this.y = y; + + return this; + + }, + + setComponent: function ( index, value ) { + + switch ( index ) { + + case 0: this.x = value; break; + case 1: this.y = value; break; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + getComponent: function ( index ) { + + switch ( index ) { + + case 0: return this.x; + case 1: return this.y; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + copy: function ( v ) { + + this.x = v.x; + this.y = v.y; + + return this; + + }, + + add: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); + return this.addVectors( v, w ); + + } + + this.x += v.x; + this.y += v.y; + + return this; + + }, + + addVectors: function ( a, b ) { + + this.x = a.x + b.x; + this.y = a.y + b.y; + + return this; + + }, + + addScalar: function ( s ) { + + this.x += s; + this.y += s; + + return this; + + }, + + sub: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); + return this.subVectors( v, w ); + + } + + this.x -= v.x; + this.y -= v.y; + + return this; + + }, + + subVectors: function ( a, b ) { + + this.x = a.x - b.x; + this.y = a.y - b.y; + + return this; + + }, + + multiply: function ( v ) { + + this.x *= v.x; + this.y *= v.y; + + return this; + + }, + + multiplyScalar: function ( s ) { + + this.x *= s; + this.y *= s; + + return this; + + }, + + divide: function ( v ) { + + this.x /= v.x; + this.y /= v.y; + + return this; + + }, + + divideScalar: function ( scalar ) { + + if ( scalar !== 0 ) { + + var invScalar = 1 / scalar; + + this.x *= invScalar; + this.y *= invScalar; + + } else { + + this.x = 0; + this.y = 0; + + } + + return this; + + }, + + min: function ( v ) { + + if ( this.x > v.x ) { + + this.x = v.x; + + } + + if ( this.y > v.y ) { + + this.y = v.y; + + } + + return this; + + }, + + max: function ( v ) { + + if ( this.x < v.x ) { + + this.x = v.x; + + } + + if ( this.y < v.y ) { + + this.y = v.y; + + } + + return this; + + }, + + clamp: function ( min, max ) { + + // This function assumes min < max, if this assumption isn't true it will not operate correctly + + if ( this.x < min.x ) { + + this.x = min.x; + + } else if ( this.x > max.x ) { + + this.x = max.x; + + } + + if ( this.y < min.y ) { + + this.y = min.y; + + } else if ( this.y > max.y ) { + + this.y = max.y; + + } + + return this; + }, + + clampScalar: ( function () { + + var min, max; + + return function ( minVal, maxVal ) { + + if ( min === undefined ) { + + min = new THREE.Vector2(); + max = new THREE.Vector2(); + + } + + min.set( minVal, minVal ); + max.set( maxVal, maxVal ); + + return this.clamp( min, max ); + + }; + + } )(), + + floor: function () { + + this.x = Math.floor( this.x ); + this.y = Math.floor( this.y ); + + return this; + + }, + + ceil: function () { + + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); + + return this; + + }, + + round: function () { + + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); + + return this; + + }, + + roundToZero: function () { + + this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); + this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); + + return this; + + }, + + negate: function () { + + this.x = - this.x; + this.y = - this.y; + + return this; + + }, + + dot: function ( v ) { + + return this.x * v.x + this.y * v.y; + + }, + + lengthSq: function () { + + return this.x * this.x + this.y * this.y; + + }, + + length: function () { + + return Math.sqrt( this.x * this.x + this.y * this.y ); + + }, + + normalize: function () { + + return this.divideScalar( this.length() ); + + }, + + distanceTo: function ( v ) { + + return Math.sqrt( this.distanceToSquared( v ) ); + + }, + + distanceToSquared: function ( v ) { + + var dx = this.x - v.x, dy = this.y - v.y; + return dx * dx + dy * dy; + + }, + + setLength: function ( l ) { + + var oldLength = this.length(); + + if ( oldLength !== 0 && l !== oldLength ) { + + this.multiplyScalar( l / oldLength ); + } + + return this; + + }, + + lerp: function ( v, alpha ) { + + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + + return this; + + }, + + equals: function ( v ) { + + return ( ( v.x === this.x ) && ( v.y === this.y ) ); + + }, + + fromArray: function ( array ) { + + this.x = array[ 0 ]; + this.y = array[ 1 ]; + + return this; + + }, + + toArray: function () { + + return [ this.x, this.y ]; + + }, + + clone: function () { + + return new THREE.Vector2( this.x, this.y ); + + } + +}; + +// File:src/math/Vector3.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author *kile / http://kile.stravaganza.org/ + * @author philogb / http://blog.thejit.org/ + * @author mikael emtinger / http://gomo.se/ + * @author egraether / http://egraether.com/ + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.Vector3 = function ( x, y, z ) { + + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; + +}; + +THREE.Vector3.prototype = { + + constructor: THREE.Vector3, + + set: function ( x, y, z ) { + + this.x = x; + this.y = y; + this.z = z; + + return this; + + }, + + setX: function ( x ) { + + this.x = x; + + return this; + + }, + + setY: function ( y ) { + + this.y = y; + + return this; + + }, + + setZ: function ( z ) { + + this.z = z; + + return this; + + }, + + setComponent: function ( index, value ) { + + switch ( index ) { + + case 0: this.x = value; break; + case 1: this.y = value; break; + case 2: this.z = value; break; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + getComponent: function ( index ) { + + switch ( index ) { + + case 0: return this.x; + case 1: return this.y; + case 2: return this.z; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + copy: function ( v ) { + + this.x = v.x; + this.y = v.y; + this.z = v.z; + + return this; + + }, + + add: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); + return this.addVectors( v, w ); + + } + + this.x += v.x; + this.y += v.y; + this.z += v.z; + + return this; + + }, + + addScalar: function ( s ) { + + this.x += s; + this.y += s; + this.z += s; + + return this; + + }, + + addVectors: function ( a, b ) { + + this.x = a.x + b.x; + this.y = a.y + b.y; + this.z = a.z + b.z; + + return this; + + }, + + sub: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); + return this.subVectors( v, w ); + + } + + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + + return this; + + }, + + subVectors: function ( a, b ) { + + this.x = a.x - b.x; + this.y = a.y - b.y; + this.z = a.z - b.z; + + return this; + + }, + + multiply: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' ); + return this.multiplyVectors( v, w ); + + } + + this.x *= v.x; + this.y *= v.y; + this.z *= v.z; + + return this; + + }, + + multiplyScalar: function ( scalar ) { + + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + + return this; + + }, + + multiplyVectors: function ( a, b ) { + + this.x = a.x * b.x; + this.y = a.y * b.y; + this.z = a.z * b.z; + + return this; + + }, + + applyEuler: function () { + + var quaternion; + + return function ( euler ) { + + if ( euler instanceof THREE.Euler === false ) { + + console.error( 'THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order.' ); + + } + + if ( quaternion === undefined ) quaternion = new THREE.Quaternion(); + + this.applyQuaternion( quaternion.setFromEuler( euler ) ); + + return this; + + }; + + }(), + + applyAxisAngle: function () { + + var quaternion; + + return function ( axis, angle ) { + + if ( quaternion === undefined ) quaternion = new THREE.Quaternion(); + + this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) ); + + return this; + + }; + + }(), + + applyMatrix3: function ( m ) { + + var x = this.x; + var y = this.y; + var z = this.z; + + var e = m.elements; + + this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z; + this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z; + this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z; + + return this; + + }, + + applyMatrix4: function ( m ) { + + // input: THREE.Matrix4 affine matrix + + var x = this.x, y = this.y, z = this.z; + + var e = m.elements; + + this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ]; + this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ]; + this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ]; + + return this; + + }, + + applyProjection: function ( m ) { + + // input: THREE.Matrix4 projection matrix + + var x = this.x, y = this.y, z = this.z; + + var e = m.elements; + var d = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); // perspective divide + + this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * d; + this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * d; + this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * d; + + return this; + + }, + + applyQuaternion: function ( q ) { + + var x = this.x; + var y = this.y; + var z = this.z; + + var qx = q.x; + var qy = q.y; + var qz = q.z; + var qw = q.w; + + // calculate quat * vector + + var ix = qw * x + qy * z - qz * y; + var iy = qw * y + qz * x - qx * z; + var iz = qw * z + qx * y - qy * x; + var iw = - qx * x - qy * y - qz * z; + + // calculate result * inverse quat + + this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy; + this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz; + this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx; + + return this; + + }, + + transformDirection: function ( m ) { + + // input: THREE.Matrix4 affine matrix + // vector interpreted as a direction + + var x = this.x, y = this.y, z = this.z; + + var e = m.elements; + + this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z; + this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z; + this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z; + + this.normalize(); + + return this; + + }, + + divide: function ( v ) { + + this.x /= v.x; + this.y /= v.y; + this.z /= v.z; + + return this; + + }, + + divideScalar: function ( scalar ) { + + if ( scalar !== 0 ) { + + var invScalar = 1 / scalar; + + this.x *= invScalar; + this.y *= invScalar; + this.z *= invScalar; + + } else { + + this.x = 0; + this.y = 0; + this.z = 0; + + } + + return this; + + }, + + min: function ( v ) { + + if ( this.x > v.x ) { + + this.x = v.x; + + } + + if ( this.y > v.y ) { + + this.y = v.y; + + } + + if ( this.z > v.z ) { + + this.z = v.z; + + } + + return this; + + }, + + max: function ( v ) { + + if ( this.x < v.x ) { + + this.x = v.x; + + } + + if ( this.y < v.y ) { + + this.y = v.y; + + } + + if ( this.z < v.z ) { + + this.z = v.z; + + } + + return this; + + }, + + clamp: function ( min, max ) { + + // This function assumes min < max, if this assumption isn't true it will not operate correctly + + if ( this.x < min.x ) { + + this.x = min.x; + + } else if ( this.x > max.x ) { + + this.x = max.x; + + } + + if ( this.y < min.y ) { + + this.y = min.y; + + } else if ( this.y > max.y ) { + + this.y = max.y; + + } + + if ( this.z < min.z ) { + + this.z = min.z; + + } else if ( this.z > max.z ) { + + this.z = max.z; + + } + + return this; + + }, + + clampScalar: ( function () { + + var min, max; + + return function ( minVal, maxVal ) { + + if ( min === undefined ) { + + min = new THREE.Vector3(); + max = new THREE.Vector3(); + + } + + min.set( minVal, minVal, minVal ); + max.set( maxVal, maxVal, maxVal ); + + return this.clamp( min, max ); + + }; + + } )(), + + floor: function () { + + this.x = Math.floor( this.x ); + this.y = Math.floor( this.y ); + this.z = Math.floor( this.z ); + + return this; + + }, + + ceil: function () { + + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); + this.z = Math.ceil( this.z ); + + return this; + + }, + + round: function () { + + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); + this.z = Math.round( this.z ); + + return this; + + }, + + roundToZero: function () { + + this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); + this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); + this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); + + return this; + + }, + + negate: function () { + + this.x = - this.x; + this.y = - this.y; + this.z = - this.z; + + return this; + + }, + + dot: function ( v ) { + + return this.x * v.x + this.y * v.y + this.z * v.z; + + }, + + lengthSq: function () { + + return this.x * this.x + this.y * this.y + this.z * this.z; + + }, + + length: function () { + + return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); + + }, + + lengthManhattan: function () { + + return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); + + }, + + normalize: function () { + + return this.divideScalar( this.length() ); + + }, + + setLength: function ( l ) { + + var oldLength = this.length(); + + if ( oldLength !== 0 && l !== oldLength ) { + + this.multiplyScalar( l / oldLength ); + } + + return this; + + }, + + lerp: function ( v, alpha ) { + + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + this.z += ( v.z - this.z ) * alpha; + + return this; + + }, + + cross: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' ); + return this.crossVectors( v, w ); + + } + + var x = this.x, y = this.y, z = this.z; + + this.x = y * v.z - z * v.y; + this.y = z * v.x - x * v.z; + this.z = x * v.y - y * v.x; + + return this; + + }, + + crossVectors: function ( a, b ) { + + var ax = a.x, ay = a.y, az = a.z; + var bx = b.x, by = b.y, bz = b.z; + + this.x = ay * bz - az * by; + this.y = az * bx - ax * bz; + this.z = ax * by - ay * bx; + + return this; + + }, + + projectOnVector: function () { + + var v1, dot; + + return function ( vector ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + v1.copy( vector ).normalize(); + + dot = this.dot( v1 ); + + return this.copy( v1 ).multiplyScalar( dot ); + + }; + + }(), + + projectOnPlane: function () { + + var v1; + + return function ( planeNormal ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + v1.copy( this ).projectOnVector( planeNormal ); + + return this.sub( v1 ); + + } + + }(), + + reflect: function () { + + // reflect incident vector off plane orthogonal to normal + // normal is assumed to have unit length + + var v1; + + return function ( normal ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); + + } + + }(), + + angleTo: function ( v ) { + + var theta = this.dot( v ) / ( this.length() * v.length() ); + + // clamp, to handle numerical problems + + return Math.acos( THREE.Math.clamp( theta, - 1, 1 ) ); + + }, + + distanceTo: function ( v ) { + + return Math.sqrt( this.distanceToSquared( v ) ); + + }, + + distanceToSquared: function ( v ) { + + var dx = this.x - v.x; + var dy = this.y - v.y; + var dz = this.z - v.z; + + return dx * dx + dy * dy + dz * dz; + + }, + + setEulerFromRotationMatrix: function ( m, order ) { + + console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' ); + + }, + + setEulerFromQuaternion: function ( q, order ) { + + console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' ); + + }, + + getPositionFromMatrix: function ( m ) { + + console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' ); + + return this.setFromMatrixPosition( m ); + + }, + + getScaleFromMatrix: function ( m ) { + + console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' ); + + return this.setFromMatrixScale( m ); + }, + + getColumnFromMatrix: function ( index, matrix ) { + + console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' ); + + return this.setFromMatrixColumn( index, matrix ); + + }, + + setFromMatrixPosition: function ( m ) { + + this.x = m.elements[ 12 ]; + this.y = m.elements[ 13 ]; + this.z = m.elements[ 14 ]; + + return this; + + }, + + setFromMatrixScale: function ( m ) { + + var sx = this.set( m.elements[ 0 ], m.elements[ 1 ], m.elements[ 2 ] ).length(); + var sy = this.set( m.elements[ 4 ], m.elements[ 5 ], m.elements[ 6 ] ).length(); + var sz = this.set( m.elements[ 8 ], m.elements[ 9 ], m.elements[ 10 ] ).length(); + + this.x = sx; + this.y = sy; + this.z = sz; + + return this; + }, + + setFromMatrixColumn: function ( index, matrix ) { + + var offset = index * 4; + + var me = matrix.elements; + + this.x = me[ offset ]; + this.y = me[ offset + 1 ]; + this.z = me[ offset + 2 ]; + + return this; + + }, + + equals: function ( v ) { + + return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); + + }, + + fromArray: function ( array ) { + + this.x = array[ 0 ]; + this.y = array[ 1 ]; + this.z = array[ 2 ]; + + return this; + + }, + + toArray: function () { + + return [ this.x, this.y, this.z ]; + + }, + + clone: function () { + + return new THREE.Vector3( this.x, this.y, this.z ); + + } + +}; + +// File:src/math/Vector4.js + +/** + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author philogb / http://blog.thejit.org/ + * @author mikael emtinger / http://gomo.se/ + * @author egraether / http://egraether.com/ + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.Vector4 = function ( x, y, z, w ) { + + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; + this.w = ( w !== undefined ) ? w : 1; + +}; + +THREE.Vector4.prototype = { + + constructor: THREE.Vector4, + + set: function ( x, y, z, w ) { + + this.x = x; + this.y = y; + this.z = z; + this.w = w; + + return this; + + }, + + setX: function ( x ) { + + this.x = x; + + return this; + + }, + + setY: function ( y ) { + + this.y = y; + + return this; + + }, + + setZ: function ( z ) { + + this.z = z; + + return this; + + }, + + setW: function ( w ) { + + this.w = w; + + return this; + + }, + + setComponent: function ( index, value ) { + + switch ( index ) { + + case 0: this.x = value; break; + case 1: this.y = value; break; + case 2: this.z = value; break; + case 3: this.w = value; break; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + getComponent: function ( index ) { + + switch ( index ) { + + case 0: return this.x; + case 1: return this.y; + case 2: return this.z; + case 3: return this.w; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + copy: function ( v ) { + + this.x = v.x; + this.y = v.y; + this.z = v.z; + this.w = ( v.w !== undefined ) ? v.w : 1; + + return this; + + }, + + add: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); + return this.addVectors( v, w ); + + } + + this.x += v.x; + this.y += v.y; + this.z += v.z; + this.w += v.w; + + return this; + + }, + + addScalar: function ( s ) { + + this.x += s; + this.y += s; + this.z += s; + this.w += s; + + return this; + + }, + + addVectors: function ( a, b ) { + + this.x = a.x + b.x; + this.y = a.y + b.y; + this.z = a.z + b.z; + this.w = a.w + b.w; + + return this; + + }, + + sub: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); + return this.subVectors( v, w ); + + } + + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + this.w -= v.w; + + return this; + + }, + + subVectors: function ( a, b ) { + + this.x = a.x - b.x; + this.y = a.y - b.y; + this.z = a.z - b.z; + this.w = a.w - b.w; + + return this; + + }, + + multiplyScalar: function ( scalar ) { + + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + this.w *= scalar; + + return this; + + }, + + applyMatrix4: function ( m ) { + + var x = this.x; + var y = this.y; + var z = this.z; + var w = this.w; + + var e = m.elements; + + this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w; + this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w; + this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w; + this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w; + + return this; + + }, + + divideScalar: function ( scalar ) { + + if ( scalar !== 0 ) { + + var invScalar = 1 / scalar; + + this.x *= invScalar; + this.y *= invScalar; + this.z *= invScalar; + this.w *= invScalar; + + } else { + + this.x = 0; + this.y = 0; + this.z = 0; + this.w = 1; + + } + + return this; + + }, + + setAxisAngleFromQuaternion: function ( q ) { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm + + // q is assumed to be normalized + + this.w = 2 * Math.acos( q.w ); + + var s = Math.sqrt( 1 - q.w * q.w ); + + if ( s < 0.0001 ) { + + this.x = 1; + this.y = 0; + this.z = 0; + + } else { + + this.x = q.x / s; + this.y = q.y / s; + this.z = q.z / s; + + } + + return this; + + }, + + setAxisAngleFromRotationMatrix: function ( m ) { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + var angle, x, y, z, // variables for result + epsilon = 0.01, // margin to allow for rounding errors + epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees + + te = m.elements, + + m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], + m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], + m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; + + if ( ( Math.abs( m12 - m21 ) < epsilon ) + && ( Math.abs( m13 - m31 ) < epsilon ) + && ( Math.abs( m23 - m32 ) < epsilon ) ) { + + // singularity found + // first check for identity matrix which must have +1 for all terms + // in leading diagonal and zero in other terms + + if ( ( Math.abs( m12 + m21 ) < epsilon2 ) + && ( Math.abs( m13 + m31 ) < epsilon2 ) + && ( Math.abs( m23 + m32 ) < epsilon2 ) + && ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { + + // this singularity is identity matrix so angle = 0 + + this.set( 1, 0, 0, 0 ); + + return this; // zero angle, arbitrary axis + + } + + // otherwise this singularity is angle = 180 + + angle = Math.PI; + + var xx = ( m11 + 1 ) / 2; + var yy = ( m22 + 1 ) / 2; + var zz = ( m33 + 1 ) / 2; + var xy = ( m12 + m21 ) / 4; + var xz = ( m13 + m31 ) / 4; + var yz = ( m23 + m32 ) / 4; + + if ( ( xx > yy ) && ( xx > zz ) ) { // m11 is the largest diagonal term + + if ( xx < epsilon ) { + + x = 0; + y = 0.707106781; + z = 0.707106781; + + } else { + + x = Math.sqrt( xx ); + y = xy / x; + z = xz / x; + + } + + } else if ( yy > zz ) { // m22 is the largest diagonal term + + if ( yy < epsilon ) { + + x = 0.707106781; + y = 0; + z = 0.707106781; + + } else { + + y = Math.sqrt( yy ); + x = xy / y; + z = yz / y; + + } + + } else { // m33 is the largest diagonal term so base result on this + + if ( zz < epsilon ) { + + x = 0.707106781; + y = 0.707106781; + z = 0; + + } else { + + z = Math.sqrt( zz ); + x = xz / z; + y = yz / z; + + } + + } + + this.set( x, y, z, angle ); + + return this; // return 180 deg rotation + + } + + // as we have reached here there are no singularities so we can handle normally + + var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + + ( m13 - m31 ) * ( m13 - m31 ) + + ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize + + if ( Math.abs( s ) < 0.001 ) s = 1; + + // prevent divide by zero, should not happen if matrix is orthogonal and should be + // caught by singularity test above, but I've left it in just in case + + this.x = ( m32 - m23 ) / s; + this.y = ( m13 - m31 ) / s; + this.z = ( m21 - m12 ) / s; + this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); + + return this; + + }, + + min: function ( v ) { + + if ( this.x > v.x ) { + + this.x = v.x; + + } + + if ( this.y > v.y ) { + + this.y = v.y; + + } + + if ( this.z > v.z ) { + + this.z = v.z; + + } + + if ( this.w > v.w ) { + + this.w = v.w; + + } + + return this; + + }, + + max: function ( v ) { + + if ( this.x < v.x ) { + + this.x = v.x; + + } + + if ( this.y < v.y ) { + + this.y = v.y; + + } + + if ( this.z < v.z ) { + + this.z = v.z; + + } + + if ( this.w < v.w ) { + + this.w = v.w; + + } + + return this; + + }, + + clamp: function ( min, max ) { + + // This function assumes min < max, if this assumption isn't true it will not operate correctly + + if ( this.x < min.x ) { + + this.x = min.x; + + } else if ( this.x > max.x ) { + + this.x = max.x; + + } + + if ( this.y < min.y ) { + + this.y = min.y; + + } else if ( this.y > max.y ) { + + this.y = max.y; + + } + + if ( this.z < min.z ) { + + this.z = min.z; + + } else if ( this.z > max.z ) { + + this.z = max.z; + + } + + if ( this.w < min.w ) { + + this.w = min.w; + + } else if ( this.w > max.w ) { + + this.w = max.w; + + } + + return this; + + }, + + clampScalar: ( function () { + + var min, max; + + return function ( minVal, maxVal ) { + + if ( min === undefined ) { + + min = new THREE.Vector4(); + max = new THREE.Vector4(); + + } + + min.set( minVal, minVal, minVal, minVal ); + max.set( maxVal, maxVal, maxVal, maxVal ); + + return this.clamp( min, max ); + + }; + + } )(), + + floor: function () { + + this.x = Math.floor( this.x ); + this.y = Math.floor( this.y ); + this.z = Math.floor( this.z ); + this.w = Math.floor( this.w ); + + return this; + + }, + + ceil: function () { + + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); + this.z = Math.ceil( this.z ); + this.w = Math.ceil( this.w ); + + return this; + + }, + + round: function () { + + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); + this.z = Math.round( this.z ); + this.w = Math.round( this.w ); + + return this; + + }, + + roundToZero: function () { + + this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); + this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); + this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); + this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w ); + + return this; + + }, + + negate: function () { + + this.x = - this.x; + this.y = - this.y; + this.z = - this.z; + this.w = - this.w; + + return this; + + }, + + dot: function ( v ) { + + return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; + + }, + + lengthSq: function () { + + return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; + + }, + + length: function () { + + return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); + + }, + + lengthManhattan: function () { + + return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); + + }, + + normalize: function () { + + return this.divideScalar( this.length() ); + + }, + + setLength: function ( l ) { + + var oldLength = this.length(); + + if ( oldLength !== 0 && l !== oldLength ) { + + this.multiplyScalar( l / oldLength ); + + } + + return this; + + }, + + lerp: function ( v, alpha ) { + + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + this.z += ( v.z - this.z ) * alpha; + this.w += ( v.w - this.w ) * alpha; + + return this; + + }, + + equals: function ( v ) { + + return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); + + }, + + fromArray: function ( array ) { + + this.x = array[ 0 ]; + this.y = array[ 1 ]; + this.z = array[ 2 ]; + this.w = array[ 3 ]; + + return this; + + }, + + toArray: function () { + + return [ this.x, this.y, this.z, this.w ]; + + }, + + clone: function () { + + return new THREE.Vector4( this.x, this.y, this.z, this.w ); + + } + +}; + +// File:src/math/Euler.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley + * @author bhouston / http://exocortex.com + */ + +THREE.Euler = function ( x, y, z, order ) { + + this._x = x || 0; + this._y = y || 0; + this._z = z || 0; + this._order = order || THREE.Euler.DefaultOrder; + +}; + +THREE.Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ]; + +THREE.Euler.DefaultOrder = 'XYZ'; + +THREE.Euler.prototype = { + + constructor: THREE.Euler, + + _x: 0, _y: 0, _z: 0, _order: THREE.Euler.DefaultOrder, + + get x () { + + return this._x; + + }, + + set x ( value ) { + + this._x = value; + this.onChangeCallback(); + + }, + + get y () { + + return this._y; + + }, + + set y ( value ) { + + this._y = value; + this.onChangeCallback(); + + }, + + get z () { + + return this._z; + + }, + + set z ( value ) { + + this._z = value; + this.onChangeCallback(); + + }, + + get order () { + + return this._order; + + }, + + set order ( value ) { + + this._order = value; + this.onChangeCallback(); + + }, + + set: function ( x, y, z, order ) { + + this._x = x; + this._y = y; + this._z = z; + this._order = order || this._order; + + this.onChangeCallback(); + + return this; + + }, + + copy: function ( euler ) { + + this._x = euler._x; + this._y = euler._y; + this._z = euler._z; + this._order = euler._order; + + this.onChangeCallback(); + + return this; + + }, + + setFromRotationMatrix: function ( m, order ) { + + var clamp = THREE.Math.clamp; + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + var te = m.elements; + var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; + var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; + var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; + + order = order || this._order; + + if ( order === 'XYZ' ) { + + this._y = Math.asin( clamp( m13, - 1, 1 ) ); + + if ( Math.abs( m13 ) < 0.99999 ) { + + this._x = Math.atan2( - m23, m33 ); + this._z = Math.atan2( - m12, m11 ); + + } else { + + this._x = Math.atan2( m32, m22 ); + this._z = 0; + + } + + } else if ( order === 'YXZ' ) { + + this._x = Math.asin( - clamp( m23, - 1, 1 ) ); + + if ( Math.abs( m23 ) < 0.99999 ) { + + this._y = Math.atan2( m13, m33 ); + this._z = Math.atan2( m21, m22 ); + + } else { + + this._y = Math.atan2( - m31, m11 ); + this._z = 0; + + } + + } else if ( order === 'ZXY' ) { + + this._x = Math.asin( clamp( m32, - 1, 1 ) ); + + if ( Math.abs( m32 ) < 0.99999 ) { + + this._y = Math.atan2( - m31, m33 ); + this._z = Math.atan2( - m12, m22 ); + + } else { + + this._y = 0; + this._z = Math.atan2( m21, m11 ); + + } + + } else if ( order === 'ZYX' ) { + + this._y = Math.asin( - clamp( m31, - 1, 1 ) ); + + if ( Math.abs( m31 ) < 0.99999 ) { + + this._x = Math.atan2( m32, m33 ); + this._z = Math.atan2( m21, m11 ); + + } else { + + this._x = 0; + this._z = Math.atan2( - m12, m22 ); + + } + + } else if ( order === 'YZX' ) { + + this._z = Math.asin( clamp( m21, - 1, 1 ) ); + + if ( Math.abs( m21 ) < 0.99999 ) { + + this._x = Math.atan2( - m23, m22 ); + this._y = Math.atan2( - m31, m11 ); + + } else { + + this._x = 0; + this._y = Math.atan2( m13, m33 ); + + } + + } else if ( order === 'XZY' ) { + + this._z = Math.asin( - clamp( m12, - 1, 1 ) ); + + if ( Math.abs( m12 ) < 0.99999 ) { + + this._x = Math.atan2( m32, m22 ); + this._y = Math.atan2( m13, m11 ); + + } else { + + this._x = Math.atan2( - m23, m33 ); + this._y = 0; + + } + + } else { + + console.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order ) + + } + + this._order = order; + + this.onChangeCallback(); + + return this; + + }, + + setFromQuaternion: function ( q, order, update ) { + + var clamp = THREE.Math.clamp; + + // q is assumed to be normalized + + // http://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/content/SpinCalc.m + + var sqx = q.x * q.x; + var sqy = q.y * q.y; + var sqz = q.z * q.z; + var sqw = q.w * q.w; + + order = order || this._order; + + if ( order === 'XYZ' ) { + + this._x = Math.atan2( 2 * ( q.x * q.w - q.y * q.z ), ( sqw - sqx - sqy + sqz ) ); + this._y = Math.asin( clamp( 2 * ( q.x * q.z + q.y * q.w ), - 1, 1 ) ); + this._z = Math.atan2( 2 * ( q.z * q.w - q.x * q.y ), ( sqw + sqx - sqy - sqz ) ); + + } else if ( order === 'YXZ' ) { + + this._x = Math.asin( clamp( 2 * ( q.x * q.w - q.y * q.z ), - 1, 1 ) ); + this._y = Math.atan2( 2 * ( q.x * q.z + q.y * q.w ), ( sqw - sqx - sqy + sqz ) ); + this._z = Math.atan2( 2 * ( q.x * q.y + q.z * q.w ), ( sqw - sqx + sqy - sqz ) ); + + } else if ( order === 'ZXY' ) { + + this._x = Math.asin( clamp( 2 * ( q.x * q.w + q.y * q.z ), - 1, 1 ) ); + this._y = Math.atan2( 2 * ( q.y * q.w - q.z * q.x ), ( sqw - sqx - sqy + sqz ) ); + this._z = Math.atan2( 2 * ( q.z * q.w - q.x * q.y ), ( sqw - sqx + sqy - sqz ) ); + + } else if ( order === 'ZYX' ) { + + this._x = Math.atan2( 2 * ( q.x * q.w + q.z * q.y ), ( sqw - sqx - sqy + sqz ) ); + this._y = Math.asin( clamp( 2 * ( q.y * q.w - q.x * q.z ), - 1, 1 ) ); + this._z = Math.atan2( 2 * ( q.x * q.y + q.z * q.w ), ( sqw + sqx - sqy - sqz ) ); + + } else if ( order === 'YZX' ) { + + this._x = Math.atan2( 2 * ( q.x * q.w - q.z * q.y ), ( sqw - sqx + sqy - sqz ) ); + this._y = Math.atan2( 2 * ( q.y * q.w - q.x * q.z ), ( sqw + sqx - sqy - sqz ) ); + this._z = Math.asin( clamp( 2 * ( q.x * q.y + q.z * q.w ), - 1, 1 ) ); + + } else if ( order === 'XZY' ) { + + this._x = Math.atan2( 2 * ( q.x * q.w + q.y * q.z ), ( sqw - sqx + sqy - sqz ) ); + this._y = Math.atan2( 2 * ( q.x * q.z + q.y * q.w ), ( sqw + sqx - sqy - sqz ) ); + this._z = Math.asin( clamp( 2 * ( q.z * q.w - q.x * q.y ), - 1, 1 ) ); + + } else { + + console.warn( 'THREE.Euler: .setFromQuaternion() given unsupported order: ' + order ) + + } + + this._order = order; + + if ( update !== false ) this.onChangeCallback(); + + return this; + + }, + + reorder: function () { + + // WARNING: this discards revolution information -bhouston + + var q = new THREE.Quaternion(); + + return function ( newOrder ) { + + q.setFromEuler( this ); + this.setFromQuaternion( q, newOrder ); + + }; + + + }(), + + equals: function ( euler ) { + + return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); + + }, + + fromArray: function ( array ) { + + this._x = array[ 0 ]; + this._y = array[ 1 ]; + this._z = array[ 2 ]; + if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; + + this.onChangeCallback(); + + return this; + + }, + + toArray: function () { + + return [ this._x, this._y, this._z, this._order ]; + + }, + + onChange: function ( callback ) { + + this.onChangeCallback = callback; + + return this; + + }, + + onChangeCallback: function () {}, + + clone: function () { + + return new THREE.Euler( this._x, this._y, this._z, this._order ); + + } + +}; + +// File:src/math/Line3.js + +/** + * @author bhouston / http://exocortex.com + */ + +THREE.Line3 = function ( start, end ) { + + this.start = ( start !== undefined ) ? start : new THREE.Vector3(); + this.end = ( end !== undefined ) ? end : new THREE.Vector3(); + +}; + +THREE.Line3.prototype = { + + constructor: THREE.Line3, + + set: function ( start, end ) { + + this.start.copy( start ); + this.end.copy( end ); + + return this; + + }, + + copy: function ( line ) { + + this.start.copy( line.start ); + this.end.copy( line.end ); + + return this; + + }, + + center: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.addVectors( this.start, this.end ).multiplyScalar( 0.5 ); + + }, + + delta: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.subVectors( this.end, this.start ); + + }, + + distanceSq: function () { + + return this.start.distanceToSquared( this.end ); + + }, + + distance: function () { + + return this.start.distanceTo( this.end ); + + }, + + at: function ( t, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + return this.delta( result ).multiplyScalar( t ).add( this.start ); + + }, + + closestPointToPointParameter: function () { + + var startP = new THREE.Vector3(); + var startEnd = new THREE.Vector3(); + + return function ( point, clampToLine ) { + + startP.subVectors( point, this.start ); + startEnd.subVectors( this.end, this.start ); + + var startEnd2 = startEnd.dot( startEnd ); + var startEnd_startP = startEnd.dot( startP ); + + var t = startEnd_startP / startEnd2; + + if ( clampToLine ) { + + t = THREE.Math.clamp( t, 0, 1 ); + + } + + return t; + + }; + + }(), + + closestPointToPoint: function ( point, clampToLine, optionalTarget ) { + + var t = this.closestPointToPointParameter( point, clampToLine ); + + var result = optionalTarget || new THREE.Vector3(); + + return this.delta( result ).multiplyScalar( t ).add( this.start ); + + }, + + applyMatrix4: function ( matrix ) { + + this.start.applyMatrix4( matrix ); + this.end.applyMatrix4( matrix ); + + return this; + + }, + + equals: function ( line ) { + + return line.start.equals( this.start ) && line.end.equals( this.end ); + + }, + + clone: function () { + + return new THREE.Line3().copy( this ); + + } + +}; + +// File:src/math/Box2.js + +/** + * @author bhouston / http://exocortex.com + */ + +THREE.Box2 = function ( min, max ) { + + this.min = ( min !== undefined ) ? min : new THREE.Vector2( Infinity, Infinity ); + this.max = ( max !== undefined ) ? max : new THREE.Vector2( - Infinity, - Infinity ); + +}; + +THREE.Box2.prototype = { + + constructor: THREE.Box2, + + set: function ( min, max ) { + + this.min.copy( min ); + this.max.copy( max ); + + return this; + + }, + + setFromPoints: function ( points ) { + + this.makeEmpty(); + + for ( var i = 0, il = points.length; i < il; i ++ ) { + + this.expandByPoint( points[ i ] ) + + } + + return this; + + }, + + setFromCenterAndSize: function () { + + var v1 = new THREE.Vector2(); + + return function ( center, size ) { + + var halfSize = v1.copy( size ).multiplyScalar( 0.5 ); + this.min.copy( center ).sub( halfSize ); + this.max.copy( center ).add( halfSize ); + + return this; + + }; + + }(), + + copy: function ( box ) { + + this.min.copy( box.min ); + this.max.copy( box.max ); + + return this; + + }, + + makeEmpty: function () { + + this.min.x = this.min.y = Infinity; + this.max.x = this.max.y = - Infinity; + + return this; + + }, + + empty: function () { + + // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes + + return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ); + + }, + + center: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector2(); + return result.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); + + }, + + size: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector2(); + return result.subVectors( this.max, this.min ); + + }, + + expandByPoint: function ( point ) { + + this.min.min( point ); + this.max.max( point ); + + return this; + }, + + expandByVector: function ( vector ) { + + this.min.sub( vector ); + this.max.add( vector ); + + return this; + }, + + expandByScalar: function ( scalar ) { + + this.min.addScalar( - scalar ); + this.max.addScalar( scalar ); + + return this; + }, + + containsPoint: function ( point ) { + + if ( point.x < this.min.x || point.x > this.max.x || + point.y < this.min.y || point.y > this.max.y ) { + + return false; + + } + + return true; + + }, + + containsBox: function ( box ) { + + if ( ( this.min.x <= box.min.x ) && ( box.max.x <= this.max.x ) && + ( this.min.y <= box.min.y ) && ( box.max.y <= this.max.y ) ) { + + return true; + + } + + return false; + + }, + + getParameter: function ( point, optionalTarget ) { + + // This can potentially have a divide by zero if the box + // has a size dimension of 0. + + var result = optionalTarget || new THREE.Vector2(); + + return result.set( + ( point.x - this.min.x ) / ( this.max.x - this.min.x ), + ( point.y - this.min.y ) / ( this.max.y - this.min.y ) + ); + + }, + + isIntersectionBox: function ( box ) { + + // using 6 splitting planes to rule out intersections. + + if ( box.max.x < this.min.x || box.min.x > this.max.x || + box.max.y < this.min.y || box.min.y > this.max.y ) { + + return false; + + } + + return true; + + }, + + clampPoint: function ( point, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector2(); + return result.copy( point ).clamp( this.min, this.max ); + + }, + + distanceToPoint: function () { + + var v1 = new THREE.Vector2(); + + return function ( point ) { + + var clampedPoint = v1.copy( point ).clamp( this.min, this.max ); + return clampedPoint.sub( point ).length(); + + }; + + }(), + + intersect: function ( box ) { + + this.min.max( box.min ); + this.max.min( box.max ); + + return this; + + }, + + union: function ( box ) { + + this.min.min( box.min ); + this.max.max( box.max ); + + return this; + + }, + + translate: function ( offset ) { + + this.min.add( offset ); + this.max.add( offset ); + + return this; + + }, + + equals: function ( box ) { + + return box.min.equals( this.min ) && box.max.equals( this.max ); + + }, + + clone: function () { + + return new THREE.Box2().copy( this ); + + } + +}; + +// File:src/math/Box3.js + +/** + * @author bhouston / http://exocortex.com + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.Box3 = function ( min, max ) { + + this.min = ( min !== undefined ) ? min : new THREE.Vector3( Infinity, Infinity, Infinity ); + this.max = ( max !== undefined ) ? max : new THREE.Vector3( - Infinity, - Infinity, - Infinity ); + +}; + +THREE.Box3.prototype = { + + constructor: THREE.Box3, + + set: function ( min, max ) { + + this.min.copy( min ); + this.max.copy( max ); + + return this; + + }, + + setFromPoints: function ( points ) { + + this.makeEmpty(); + + for ( var i = 0, il = points.length; i < il; i ++ ) { + + this.expandByPoint( points[ i ] ) + + } + + return this; + + }, + + setFromCenterAndSize: function () { + + var v1 = new THREE.Vector3(); + + return function ( center, size ) { + + var halfSize = v1.copy( size ).multiplyScalar( 0.5 ); + + this.min.copy( center ).sub( halfSize ); + this.max.copy( center ).add( halfSize ); + + return this; + + }; + + }(), + + setFromObject: function () { + + // Computes the world-axis-aligned bounding box of an object (including its children), + // accounting for both the object's, and childrens', world transforms + + var v1 = new THREE.Vector3(); + + return function ( object ) { + + var scope = this; + + object.updateMatrixWorld( true ); + + this.makeEmpty(); + + object.traverse( function ( node ) { + + if ( node.geometry !== undefined && node.geometry.vertices !== undefined ) { + + var vertices = node.geometry.vertices; + + for ( var i = 0, il = vertices.length; i < il; i ++ ) { + + v1.copy( vertices[ i ] ); + + v1.applyMatrix4( node.matrixWorld ); + + scope.expandByPoint( v1 ); + + } + + } + + } ); + + return this; + + }; + + }(), + + copy: function ( box ) { + + this.min.copy( box.min ); + this.max.copy( box.max ); + + return this; + + }, + + makeEmpty: function () { + + this.min.x = this.min.y = this.min.z = Infinity; + this.max.x = this.max.y = this.max.z = - Infinity; + + return this; + + }, + + empty: function () { + + // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes + + return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); + + }, + + center: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); + + }, + + size: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.subVectors( this.max, this.min ); + + }, + + expandByPoint: function ( point ) { + + this.min.min( point ); + this.max.max( point ); + + return this; + + }, + + expandByVector: function ( vector ) { + + this.min.sub( vector ); + this.max.add( vector ); + + return this; + + }, + + expandByScalar: function ( scalar ) { + + this.min.addScalar( - scalar ); + this.max.addScalar( scalar ); + + return this; + + }, + + containsPoint: function ( point ) { + + if ( point.x < this.min.x || point.x > this.max.x || + point.y < this.min.y || point.y > this.max.y || + point.z < this.min.z || point.z > this.max.z ) { + + return false; + + } + + return true; + + }, + + containsBox: function ( box ) { + + if ( ( this.min.x <= box.min.x ) && ( box.max.x <= this.max.x ) && + ( this.min.y <= box.min.y ) && ( box.max.y <= this.max.y ) && + ( this.min.z <= box.min.z ) && ( box.max.z <= this.max.z ) ) { + + return true; + + } + + return false; + + }, + + getParameter: function ( point, optionalTarget ) { + + // This can potentially have a divide by zero if the box + // has a size dimension of 0. + + var result = optionalTarget || new THREE.Vector3(); + + return result.set( + ( point.x - this.min.x ) / ( this.max.x - this.min.x ), + ( point.y - this.min.y ) / ( this.max.y - this.min.y ), + ( point.z - this.min.z ) / ( this.max.z - this.min.z ) + ); + + }, + + isIntersectionBox: function ( box ) { + + // using 6 splitting planes to rule out intersections. + + if ( box.max.x < this.min.x || box.min.x > this.max.x || + box.max.y < this.min.y || box.min.y > this.max.y || + box.max.z < this.min.z || box.min.z > this.max.z ) { + + return false; + + } + + return true; + + }, + + clampPoint: function ( point, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.copy( point ).clamp( this.min, this.max ); + + }, + + distanceToPoint: function () { + + var v1 = new THREE.Vector3(); + + return function ( point ) { + + var clampedPoint = v1.copy( point ).clamp( this.min, this.max ); + return clampedPoint.sub( point ).length(); + + }; + + }(), + + getBoundingSphere: function () { + + var v1 = new THREE.Vector3(); + + return function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Sphere(); + + result.center = this.center(); + result.radius = this.size( v1 ).length() * 0.5; + + return result; + + }; + + }(), + + intersect: function ( box ) { + + this.min.max( box.min ); + this.max.min( box.max ); + + return this; + + }, + + union: function ( box ) { + + this.min.min( box.min ); + this.max.max( box.max ); + + return this; + + }, + + applyMatrix4: function () { + + var points = [ + new THREE.Vector3(), + new THREE.Vector3(), + new THREE.Vector3(), + new THREE.Vector3(), + new THREE.Vector3(), + new THREE.Vector3(), + new THREE.Vector3(), + new THREE.Vector3() + ]; + + return function ( matrix ) { + + // NOTE: I am using a binary pattern to specify all 2^3 combinations below + points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000 + points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001 + points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010 + points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011 + points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100 + points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101 + points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110 + points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111 + + this.makeEmpty(); + this.setFromPoints( points ); + + return this; + + }; + + }(), + + translate: function ( offset ) { + + this.min.add( offset ); + this.max.add( offset ); + + return this; + + }, + + equals: function ( box ) { + + return box.min.equals( this.min ) && box.max.equals( this.max ); + + }, + + clone: function () { + + return new THREE.Box3().copy( this ); + + } + +}; + +// File:src/math/Matrix3.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author WestLangley / http://github.com/WestLangley + * @author bhouston / http://exocortex.com + */ + +THREE.Matrix3 = function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { + + this.elements = new Float32Array( 9 ); + + var te = this.elements; + + te[ 0 ] = ( n11 !== undefined ) ? n11 : 1; te[ 3 ] = n12 || 0; te[ 6 ] = n13 || 0; + te[ 1 ] = n21 || 0; te[ 4 ] = ( n22 !== undefined ) ? n22 : 1; te[ 7 ] = n23 || 0; + te[ 2 ] = n31 || 0; te[ 5 ] = n32 || 0; te[ 8 ] = ( n33 !== undefined ) ? n33 : 1; + +}; + +THREE.Matrix3.prototype = { + + constructor: THREE.Matrix3, + + set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { + + var te = this.elements; + + te[ 0 ] = n11; te[ 3 ] = n12; te[ 6 ] = n13; + te[ 1 ] = n21; te[ 4 ] = n22; te[ 7 ] = n23; + te[ 2 ] = n31; te[ 5 ] = n32; te[ 8 ] = n33; + + return this; + + }, + + identity: function () { + + this.set( + + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 + + ); + + return this; + + }, + + copy: function ( m ) { + + var me = m.elements; + + this.set( + + me[ 0 ], me[ 3 ], me[ 6 ], + me[ 1 ], me[ 4 ], me[ 7 ], + me[ 2 ], me[ 5 ], me[ 8 ] + + ); + + return this; + + }, + + multiplyVector3: function ( vector ) { + + console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' ); + return vector.applyMatrix3( this ); + + }, + + multiplyVector3Array: function ( a ) { + + console.warn( 'THREE.Matrix3: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.' ); + return this.applyToVector3Array( a ); + + }, + + applyToVector3Array: function () { + + var v1 = new THREE.Vector3(); + + return function ( array, offset, length ) { + + if ( offset === undefined ) offset = 0; + if ( length === undefined ) length = array.length; + + for ( var i = 0, j = offset, il; i < length; i += 3, j += 3 ) { + + v1.x = array[ j ]; + v1.y = array[ j + 1 ]; + v1.z = array[ j + 2 ]; + + v1.applyMatrix3( this ); + + array[ j ] = v1.x; + array[ j + 1 ] = v1.y; + array[ j + 2 ] = v1.z; + + } + + return array; + + }; + + }(), + + multiplyScalar: function ( s ) { + + var te = this.elements; + + te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s; + te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s; + te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s; + + return this; + + }, + + determinant: function () { + + var te = this.elements; + + var a = te[ 0 ], b = te[ 1 ], c = te[ 2 ], + d = te[ 3 ], e = te[ 4 ], f = te[ 5 ], + g = te[ 6 ], h = te[ 7 ], i = te[ 8 ]; + + return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; + + }, + + getInverse: function ( matrix, throwOnInvertible ) { + + // input: THREE.Matrix4 + // ( based on http://code.google.com/p/webgl-mjs/ ) + + var me = matrix.elements; + var te = this.elements; + + te[ 0 ] = me[ 10 ] * me[ 5 ] - me[ 6 ] * me[ 9 ]; + te[ 1 ] = - me[ 10 ] * me[ 1 ] + me[ 2 ] * me[ 9 ]; + te[ 2 ] = me[ 6 ] * me[ 1 ] - me[ 2 ] * me[ 5 ]; + te[ 3 ] = - me[ 10 ] * me[ 4 ] + me[ 6 ] * me[ 8 ]; + te[ 4 ] = me[ 10 ] * me[ 0 ] - me[ 2 ] * me[ 8 ]; + te[ 5 ] = - me[ 6 ] * me[ 0 ] + me[ 2 ] * me[ 4 ]; + te[ 6 ] = me[ 9 ] * me[ 4 ] - me[ 5 ] * me[ 8 ]; + te[ 7 ] = - me[ 9 ] * me[ 0 ] + me[ 1 ] * me[ 8 ]; + te[ 8 ] = me[ 5 ] * me[ 0 ] - me[ 1 ] * me[ 4 ]; + + var det = me[ 0 ] * te[ 0 ] + me[ 1 ] * te[ 3 ] + me[ 2 ] * te[ 6 ]; + + // no inverse + + if ( det === 0 ) { + + var msg = "Matrix3.getInverse(): can't invert matrix, determinant is 0"; + + if ( throwOnInvertible || false ) { + + throw new Error( msg ); + + } else { + + console.warn( msg ); + + } + + this.identity(); + + return this; + + } + + this.multiplyScalar( 1.0 / det ); + + return this; + + }, + + transpose: function () { + + var tmp, m = this.elements; + + tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp; + tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp; + tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp; + + return this; + + }, + + flattenToArrayOffset: function ( array, offset ) { + + var te = this.elements; + + array[ offset ] = te[ 0 ]; + array[ offset + 1 ] = te[ 1 ]; + array[ offset + 2 ] = te[ 2 ]; + + array[ offset + 3 ] = te[ 3 ]; + array[ offset + 4 ] = te[ 4 ]; + array[ offset + 5 ] = te[ 5 ]; + + array[ offset + 6 ] = te[ 6 ]; + array[ offset + 7 ] = te[ 7 ]; + array[ offset + 8 ] = te[ 8 ]; + + return array; + + }, + + getNormalMatrix: function ( m ) { + + // input: THREE.Matrix4 + + this.getInverse( m ).transpose(); + + return this; + + }, + + transposeIntoArray: function ( r ) { + + var m = this.elements; + + r[ 0 ] = m[ 0 ]; + r[ 1 ] = m[ 3 ]; + r[ 2 ] = m[ 6 ]; + r[ 3 ] = m[ 1 ]; + r[ 4 ] = m[ 4 ]; + r[ 5 ] = m[ 7 ]; + r[ 6 ] = m[ 2 ]; + r[ 7 ] = m[ 5 ]; + r[ 8 ] = m[ 8 ]; + + return this; + + }, + + fromArray: function ( array ) { + + this.elements.set( array ); + + return this; + + }, + + toArray: function () { + + var te = this.elements; + + return [ + te[ 0 ], te[ 1 ], te[ 2 ], + te[ 3 ], te[ 4 ], te[ 5 ], + te[ 6 ], te[ 7 ], te[ 8 ] + ]; + + }, + + clone: function () { + + var te = this.elements; + + return new THREE.Matrix3( + + te[ 0 ], te[ 3 ], te[ 6 ], + te[ 1 ], te[ 4 ], te[ 7 ], + te[ 2 ], te[ 5 ], te[ 8 ] + + ); + + } + +}; + +// File:src/math/Matrix4.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author philogb / http://blog.thejit.org/ + * @author jordi_ros / http://plattsoft.com + * @author D1plo1d / http://github.com/D1plo1d + * @author alteredq / http://alteredqualia.com/ + * @author mikael emtinger / http://gomo.se/ + * @author timknip / http://www.floorplanner.com/ + * @author bhouston / http://exocortex.com + * @author WestLangley / http://github.com/WestLangley + */ + + +THREE.Matrix4 = function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { + + this.elements = new Float32Array( 16 ); + + // TODO: if n11 is undefined, then just set to identity, otherwise copy all other values into matrix + // we should not support semi specification of Matrix4, it is just weird. + + var te = this.elements; + + te[ 0 ] = ( n11 !== undefined ) ? n11 : 1; te[ 4 ] = n12 || 0; te[ 8 ] = n13 || 0; te[ 12 ] = n14 || 0; + te[ 1 ] = n21 || 0; te[ 5 ] = ( n22 !== undefined ) ? n22 : 1; te[ 9 ] = n23 || 0; te[ 13 ] = n24 || 0; + te[ 2 ] = n31 || 0; te[ 6 ] = n32 || 0; te[ 10 ] = ( n33 !== undefined ) ? n33 : 1; te[ 14 ] = n34 || 0; + te[ 3 ] = n41 || 0; te[ 7 ] = n42 || 0; te[ 11 ] = n43 || 0; te[ 15 ] = ( n44 !== undefined ) ? n44 : 1; + +}; + +THREE.Matrix4.prototype = { + + constructor: THREE.Matrix4, + + set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { + + var te = this.elements; + + te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14; + te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24; + te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34; + te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44; + + return this; + + }, + + identity: function () { + + this.set( + + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + copy: function ( m ) { + + this.elements.set( m.elements ); + + return this; + + }, + + extractPosition: function ( m ) { + + console.warn( 'THREEMatrix4: .extractPosition() has been renamed to .copyPosition().' ); + return this.copyPosition( m ); + + }, + + copyPosition: function ( m ) { + + var te = this.elements; + var me = m.elements; + + te[ 12 ] = me[ 12 ]; + te[ 13 ] = me[ 13 ]; + te[ 14 ] = me[ 14 ]; + + return this; + + }, + + extractRotation: function () { + + var v1 = new THREE.Vector3(); + + return function ( m ) { + + var te = this.elements; + var me = m.elements; + + var scaleX = 1 / v1.set( me[ 0 ], me[ 1 ], me[ 2 ] ).length(); + var scaleY = 1 / v1.set( me[ 4 ], me[ 5 ], me[ 6 ] ).length(); + var scaleZ = 1 / v1.set( me[ 8 ], me[ 9 ], me[ 10 ] ).length(); + + te[ 0 ] = me[ 0 ] * scaleX; + te[ 1 ] = me[ 1 ] * scaleX; + te[ 2 ] = me[ 2 ] * scaleX; + + te[ 4 ] = me[ 4 ] * scaleY; + te[ 5 ] = me[ 5 ] * scaleY; + te[ 6 ] = me[ 6 ] * scaleY; + + te[ 8 ] = me[ 8 ] * scaleZ; + te[ 9 ] = me[ 9 ] * scaleZ; + te[ 10 ] = me[ 10 ] * scaleZ; + + return this; + + }; + + }(), + + makeRotationFromEuler: function ( euler ) { + + if ( euler instanceof THREE.Euler === false ) { + + console.error( 'THREE.Matrix: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); + + } + + var te = this.elements; + + var x = euler.x, y = euler.y, z = euler.z; + var a = Math.cos( x ), b = Math.sin( x ); + var c = Math.cos( y ), d = Math.sin( y ); + var e = Math.cos( z ), f = Math.sin( z ); + + if ( euler.order === 'XYZ' ) { + + var ae = a * e, af = a * f, be = b * e, bf = b * f; + + te[ 0 ] = c * e; + te[ 4 ] = - c * f; + te[ 8 ] = d; + + te[ 1 ] = af + be * d; + te[ 5 ] = ae - bf * d; + te[ 9 ] = - b * c; + + te[ 2 ] = bf - ae * d; + te[ 6 ] = be + af * d; + te[ 10 ] = a * c; + + } else if ( euler.order === 'YXZ' ) { + + var ce = c * e, cf = c * f, de = d * e, df = d * f; + + te[ 0 ] = ce + df * b; + te[ 4 ] = de * b - cf; + te[ 8 ] = a * d; + + te[ 1 ] = a * f; + te[ 5 ] = a * e; + te[ 9 ] = - b; + + te[ 2 ] = cf * b - de; + te[ 6 ] = df + ce * b; + te[ 10 ] = a * c; + + } else if ( euler.order === 'ZXY' ) { + + var ce = c * e, cf = c * f, de = d * e, df = d * f; + + te[ 0 ] = ce - df * b; + te[ 4 ] = - a * f; + te[ 8 ] = de + cf * b; + + te[ 1 ] = cf + de * b; + te[ 5 ] = a * e; + te[ 9 ] = df - ce * b; + + te[ 2 ] = - a * d; + te[ 6 ] = b; + te[ 10 ] = a * c; + + } else if ( euler.order === 'ZYX' ) { + + var ae = a * e, af = a * f, be = b * e, bf = b * f; + + te[ 0 ] = c * e; + te[ 4 ] = be * d - af; + te[ 8 ] = ae * d + bf; + + te[ 1 ] = c * f; + te[ 5 ] = bf * d + ae; + te[ 9 ] = af * d - be; + + te[ 2 ] = - d; + te[ 6 ] = b * c; + te[ 10 ] = a * c; + + } else if ( euler.order === 'YZX' ) { + + var ac = a * c, ad = a * d, bc = b * c, bd = b * d; + + te[ 0 ] = c * e; + te[ 4 ] = bd - ac * f; + te[ 8 ] = bc * f + ad; + + te[ 1 ] = f; + te[ 5 ] = a * e; + te[ 9 ] = - b * e; + + te[ 2 ] = - d * e; + te[ 6 ] = ad * f + bc; + te[ 10 ] = ac - bd * f; + + } else if ( euler.order === 'XZY' ) { + + var ac = a * c, ad = a * d, bc = b * c, bd = b * d; + + te[ 0 ] = c * e; + te[ 4 ] = - f; + te[ 8 ] = d * e; + + te[ 1 ] = ac * f + bd; + te[ 5 ] = a * e; + te[ 9 ] = ad * f - bc; + + te[ 2 ] = bc * f - ad; + te[ 6 ] = b * e; + te[ 10 ] = bd * f + ac; + + } + + // last column + te[ 3 ] = 0; + te[ 7 ] = 0; + te[ 11 ] = 0; + + // bottom row + te[ 12 ] = 0; + te[ 13 ] = 0; + te[ 14 ] = 0; + te[ 15 ] = 1; + + return this; + + }, + + setRotationFromQuaternion: function ( q ) { + + console.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' ); + + return this.makeRotationFromQuaternion( q ); + + }, + + makeRotationFromQuaternion: function ( q ) { + + var te = this.elements; + + var x = q.x, y = q.y, z = q.z, w = q.w; + var x2 = x + x, y2 = y + y, z2 = z + z; + var xx = x * x2, xy = x * y2, xz = x * z2; + var yy = y * y2, yz = y * z2, zz = z * z2; + var wx = w * x2, wy = w * y2, wz = w * z2; + + te[ 0 ] = 1 - ( yy + zz ); + te[ 4 ] = xy - wz; + te[ 8 ] = xz + wy; + + te[ 1 ] = xy + wz; + te[ 5 ] = 1 - ( xx + zz ); + te[ 9 ] = yz - wx; + + te[ 2 ] = xz - wy; + te[ 6 ] = yz + wx; + te[ 10 ] = 1 - ( xx + yy ); + + // last column + te[ 3 ] = 0; + te[ 7 ] = 0; + te[ 11 ] = 0; + + // bottom row + te[ 12 ] = 0; + te[ 13 ] = 0; + te[ 14 ] = 0; + te[ 15 ] = 1; + + return this; + + }, + + lookAt: function () { + + var x = new THREE.Vector3(); + var y = new THREE.Vector3(); + var z = new THREE.Vector3(); + + return function ( eye, target, up ) { + + var te = this.elements; + + z.subVectors( eye, target ).normalize(); + + if ( z.length() === 0 ) { + + z.z = 1; + + } + + x.crossVectors( up, z ).normalize(); + + if ( x.length() === 0 ) { + + z.x += 0.0001; + x.crossVectors( up, z ).normalize(); + + } + + y.crossVectors( z, x ); + + + te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x; + te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y; + te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z; + + return this; + + }; + + }(), + + multiply: function ( m, n ) { + + if ( n !== undefined ) { + + console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' ); + return this.multiplyMatrices( m, n ); + + } + + return this.multiplyMatrices( this, m ); + + }, + + multiplyMatrices: function ( a, b ) { + + var ae = a.elements; + var be = b.elements; + var te = this.elements; + + var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; + var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; + var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; + var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; + + var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; + var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; + var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; + var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ]; + + te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; + te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; + te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; + te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; + + te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; + te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; + te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; + te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; + + te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; + te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; + te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; + te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; + + te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; + te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; + te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; + te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; + + return this; + + }, + + multiplyToArray: function ( a, b, r ) { + + var te = this.elements; + + this.multiplyMatrices( a, b ); + + r[ 0 ] = te[ 0 ]; r[ 1 ] = te[ 1 ]; r[ 2 ] = te[ 2 ]; r[ 3 ] = te[ 3 ]; + r[ 4 ] = te[ 4 ]; r[ 5 ] = te[ 5 ]; r[ 6 ] = te[ 6 ]; r[ 7 ] = te[ 7 ]; + r[ 8 ] = te[ 8 ]; r[ 9 ] = te[ 9 ]; r[ 10 ] = te[ 10 ]; r[ 11 ] = te[ 11 ]; + r[ 12 ] = te[ 12 ]; r[ 13 ] = te[ 13 ]; r[ 14 ] = te[ 14 ]; r[ 15 ] = te[ 15 ]; + + return this; + + }, + + multiplyScalar: function ( s ) { + + var te = this.elements; + + te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s; + te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s; + te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s; + te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s; + + return this; + + }, + + multiplyVector3: function ( vector ) { + + console.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) or vector.applyProjection( matrix ) instead.' ); + return vector.applyProjection( this ); + + }, + + multiplyVector4: function ( vector ) { + + console.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); + return vector.applyMatrix4( this ); + + }, + + multiplyVector3Array: function ( a ) { + + console.warn( 'THREE.Matrix4: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.' ); + return this.applyToVector3Array( a ); + + }, + + applyToVector3Array: function () { + + var v1 = new THREE.Vector3(); + + return function ( array, offset, length ) { + + if ( offset === undefined ) offset = 0; + if ( length === undefined ) length = array.length; + + for ( var i = 0, j = offset, il; i < length; i += 3, j += 3 ) { + + v1.x = array[ j ]; + v1.y = array[ j + 1 ]; + v1.z = array[ j + 2 ]; + + v1.applyMatrix4( this ); + + array[ j ] = v1.x; + array[ j + 1 ] = v1.y; + array[ j + 2 ] = v1.z; + + } + + return array; + + }; + + }(), + + rotateAxis: function ( v ) { + + console.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' ); + + v.transformDirection( this ); + + }, + + crossVector: function ( vector ) { + + console.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); + return vector.applyMatrix4( this ); + + }, + + determinant: function () { + + var te = this.elements; + + var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; + var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; + var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; + var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; + + //TODO: make this more efficient + //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) + + return ( + n41 * ( + + n14 * n23 * n32 + - n13 * n24 * n32 + - n14 * n22 * n33 + + n12 * n24 * n33 + + n13 * n22 * n34 + - n12 * n23 * n34 + ) + + n42 * ( + + n11 * n23 * n34 + - n11 * n24 * n33 + + n14 * n21 * n33 + - n13 * n21 * n34 + + n13 * n24 * n31 + - n14 * n23 * n31 + ) + + n43 * ( + + n11 * n24 * n32 + - n11 * n22 * n34 + - n14 * n21 * n32 + + n12 * n21 * n34 + + n14 * n22 * n31 + - n12 * n24 * n31 + ) + + n44 * ( + - n13 * n22 * n31 + - n11 * n23 * n32 + + n11 * n22 * n33 + + n13 * n21 * n32 + - n12 * n21 * n33 + + n12 * n23 * n31 + ) + + ); + + }, + + transpose: function () { + + var te = this.elements; + var tmp; + + tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp; + tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp; + tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp; + + tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp; + tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp; + tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp; + + return this; + + }, + + flattenToArrayOffset: function ( array, offset ) { + + var te = this.elements; + + array[ offset ] = te[ 0 ]; + array[ offset + 1 ] = te[ 1 ]; + array[ offset + 2 ] = te[ 2 ]; + array[ offset + 3 ] = te[ 3 ]; + + array[ offset + 4 ] = te[ 4 ]; + array[ offset + 5 ] = te[ 5 ]; + array[ offset + 6 ] = te[ 6 ]; + array[ offset + 7 ] = te[ 7 ]; + + array[ offset + 8 ] = te[ 8 ]; + array[ offset + 9 ] = te[ 9 ]; + array[ offset + 10 ] = te[ 10 ]; + array[ offset + 11 ] = te[ 11 ]; + + array[ offset + 12 ] = te[ 12 ]; + array[ offset + 13 ] = te[ 13 ]; + array[ offset + 14 ] = te[ 14 ]; + array[ offset + 15 ] = te[ 15 ]; + + return array; + + }, + + getPosition: function () { + + var v1 = new THREE.Vector3(); + + return function () { + + console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' ); + + var te = this.elements; + return v1.set( te[ 12 ], te[ 13 ], te[ 14 ] ); + + }; + + }(), + + setPosition: function ( v ) { + + var te = this.elements; + + te[ 12 ] = v.x; + te[ 13 ] = v.y; + te[ 14 ] = v.z; + + return this; + + }, + + getInverse: function ( m, throwOnInvertible ) { + + // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm + var te = this.elements; + var me = m.elements; + + var n11 = me[ 0 ], n12 = me[ 4 ], n13 = me[ 8 ], n14 = me[ 12 ]; + var n21 = me[ 1 ], n22 = me[ 5 ], n23 = me[ 9 ], n24 = me[ 13 ]; + var n31 = me[ 2 ], n32 = me[ 6 ], n33 = me[ 10 ], n34 = me[ 14 ]; + var n41 = me[ 3 ], n42 = me[ 7 ], n43 = me[ 11 ], n44 = me[ 15 ]; + + te[ 0 ] = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44; + te[ 4 ] = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44; + te[ 8 ] = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44; + te[ 12 ] = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; + te[ 1 ] = n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44; + te[ 5 ] = n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44; + te[ 9 ] = n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44; + te[ 13 ] = n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34; + te[ 2 ] = n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44; + te[ 6 ] = n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44; + te[ 10 ] = n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44; + te[ 14 ] = n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34; + te[ 3 ] = n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43; + te[ 7 ] = n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43; + te[ 11 ] = n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43; + te[ 15 ] = n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33; + + var det = n11 * te[ 0 ] + n21 * te[ 4 ] + n31 * te[ 8 ] + n41 * te[ 12 ]; + + if ( det == 0 ) { + + var msg = "Matrix4.getInverse(): can't invert matrix, determinant is 0"; + + if ( throwOnInvertible || false ) { + + throw new Error( msg ); + + } else { + + console.warn( msg ); + + } + + this.identity(); + + return this; + } + + this.multiplyScalar( 1 / det ); + + return this; + + }, + + translate: function ( v ) { + + console.warn( 'THREE.Matrix4: .translate() has been removed.' ); + + }, + + rotateX: function ( angle ) { + + console.warn( 'THREE.Matrix4: .rotateX() has been removed.' ); + + }, + + rotateY: function ( angle ) { + + console.warn( 'THREE.Matrix4: .rotateY() has been removed.' ); + + }, + + rotateZ: function ( angle ) { + + console.warn( 'THREE.Matrix4: .rotateZ() has been removed.' ); + + }, + + rotateByAxis: function ( axis, angle ) { + + console.warn( 'THREE.Matrix4: .rotateByAxis() has been removed.' ); + + }, + + scale: function ( v ) { + + var te = this.elements; + var x = v.x, y = v.y, z = v.z; + + te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z; + te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z; + te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z; + te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z; + + return this; + + }, + + getMaxScaleOnAxis: function () { + + var te = this.elements; + + var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; + var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; + var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; + + return Math.sqrt( Math.max( scaleXSq, Math.max( scaleYSq, scaleZSq ) ) ); + + }, + + makeTranslation: function ( x, y, z ) { + + this.set( + + 1, 0, 0, x, + 0, 1, 0, y, + 0, 0, 1, z, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeRotationX: function ( theta ) { + + var c = Math.cos( theta ), s = Math.sin( theta ); + + this.set( + + 1, 0, 0, 0, + 0, c, - s, 0, + 0, s, c, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeRotationY: function ( theta ) { + + var c = Math.cos( theta ), s = Math.sin( theta ); + + this.set( + + c, 0, s, 0, + 0, 1, 0, 0, + - s, 0, c, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeRotationZ: function ( theta ) { + + var c = Math.cos( theta ), s = Math.sin( theta ); + + this.set( + + c, - s, 0, 0, + s, c, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeRotationAxis: function ( axis, angle ) { + + // Based on http://www.gamedev.net/reference/articles/article1199.asp + + var c = Math.cos( angle ); + var s = Math.sin( angle ); + var t = 1 - c; + var x = axis.x, y = axis.y, z = axis.z; + var tx = t * x, ty = t * y; + + this.set( + + tx * x + c, tx * y - s * z, tx * z + s * y, 0, + tx * y + s * z, ty * y + c, ty * z - s * x, 0, + tx * z - s * y, ty * z + s * x, t * z * z + c, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeScale: function ( x, y, z ) { + + this.set( + + x, 0, 0, 0, + 0, y, 0, 0, + 0, 0, z, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + compose: function ( position, quaternion, scale ) { + + this.makeRotationFromQuaternion( quaternion ); + this.scale( scale ); + this.setPosition( position ); + + return this; + + }, + + decompose: function () { + + var vector = new THREE.Vector3(); + var matrix = new THREE.Matrix4(); + + return function ( position, quaternion, scale ) { + + var te = this.elements; + + var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); + var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); + var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); + + // if determine is negative, we need to invert one scale + var det = this.determinant(); + if ( det < 0 ) { + sx = - sx; + } + + position.x = te[ 12 ]; + position.y = te[ 13 ]; + position.z = te[ 14 ]; + + // scale the rotation part + + matrix.elements.set( this.elements ); // at this point matrix is incomplete so we can't use .copy() + + var invSX = 1 / sx; + var invSY = 1 / sy; + var invSZ = 1 / sz; + + matrix.elements[ 0 ] *= invSX; + matrix.elements[ 1 ] *= invSX; + matrix.elements[ 2 ] *= invSX; + + matrix.elements[ 4 ] *= invSY; + matrix.elements[ 5 ] *= invSY; + matrix.elements[ 6 ] *= invSY; + + matrix.elements[ 8 ] *= invSZ; + matrix.elements[ 9 ] *= invSZ; + matrix.elements[ 10 ] *= invSZ; + + quaternion.setFromRotationMatrix( matrix ); + + scale.x = sx; + scale.y = sy; + scale.z = sz; + + return this; + + }; + + }(), + + makeFrustum: function ( left, right, bottom, top, near, far ) { + + var te = this.elements; + var x = 2 * near / ( right - left ); + var y = 2 * near / ( top - bottom ); + + var a = ( right + left ) / ( right - left ); + var b = ( top + bottom ) / ( top - bottom ); + var c = - ( far + near ) / ( far - near ); + var d = - 2 * far * near / ( far - near ); + + te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0; + te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0; + te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; + te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0; + + return this; + + }, + + makePerspective: function ( fov, aspect, near, far ) { + + var ymax = near * Math.tan( THREE.Math.degToRad( fov * 0.5 ) ); + var ymin = - ymax; + var xmin = ymin * aspect; + var xmax = ymax * aspect; + + return this.makeFrustum( xmin, xmax, ymin, ymax, near, far ); + + }, + + makeOrthographic: function ( left, right, top, bottom, near, far ) { + + var te = this.elements; + var w = right - left; + var h = top - bottom; + var p = far - near; + + var x = ( right + left ) / w; + var y = ( top + bottom ) / h; + var z = ( far + near ) / p; + + te[ 0 ] = 2 / w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x; + te[ 1 ] = 0; te[ 5 ] = 2 / h; te[ 9 ] = 0; te[ 13 ] = - y; + te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 / p; te[ 14 ] = - z; + te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1; + + return this; + + }, + + fromArray: function ( array ) { + + this.elements.set( array ); + + return this; + + }, + + toArray: function () { + + var te = this.elements; + + return [ + te[ 0 ], te[ 1 ], te[ 2 ], te[ 3 ], + te[ 4 ], te[ 5 ], te[ 6 ], te[ 7 ], + te[ 8 ], te[ 9 ], te[ 10 ], te[ 11 ], + te[ 12 ], te[ 13 ], te[ 14 ], te[ 15 ] + ]; + + }, + + clone: function () { + + var te = this.elements; + + return new THREE.Matrix4( + + te[ 0 ], te[ 4 ], te[ 8 ], te[ 12 ], + te[ 1 ], te[ 5 ], te[ 9 ], te[ 13 ], + te[ 2 ], te[ 6 ], te[ 10 ], te[ 14 ], + te[ 3 ], te[ 7 ], te[ 11 ], te[ 15 ] + + ); + + } + +}; + +// File:src/math/Ray.js + +/** + * @author bhouston / http://exocortex.com + */ + +THREE.Ray = function ( origin, direction ) { + + this.origin = ( origin !== undefined ) ? origin : new THREE.Vector3(); + this.direction = ( direction !== undefined ) ? direction : new THREE.Vector3(); + +}; + +THREE.Ray.prototype = { + + constructor: THREE.Ray, + + set: function ( origin, direction ) { + + this.origin.copy( origin ); + this.direction.copy( direction ); + + return this; + + }, + + copy: function ( ray ) { + + this.origin.copy( ray.origin ); + this.direction.copy( ray.direction ); + + return this; + + }, + + at: function ( t, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + return result.copy( this.direction ).multiplyScalar( t ).add( this.origin ); + + }, + + recast: function () { + + var v1 = new THREE.Vector3(); + + return function ( t ) { + + this.origin.copy( this.at( t, v1 ) ); + + return this; + + }; + + }(), + + closestPointToPoint: function ( point, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + result.subVectors( point, this.origin ); + var directionDistance = result.dot( this.direction ); + + if ( directionDistance < 0 ) { + + return result.copy( this.origin ); + + } + + return result.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); + + }, + + distanceToPoint: function () { + + var v1 = new THREE.Vector3(); + + return function ( point ) { + + var directionDistance = v1.subVectors( point, this.origin ).dot( this.direction ); + + // point behind the ray + + if ( directionDistance < 0 ) { + + return this.origin.distanceTo( point ); + + } + + v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); + + return v1.distanceTo( point ); + + }; + + }(), + + distanceSqToSegment: function ( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { + + // from http://www.geometrictools.com/LibMathematics/Distance/Wm5DistRay3Segment3.cpp + // It returns the min distance between the ray and the segment + // defined by v0 and v1 + // It can also set two optional targets : + // - The closest point on the ray + // - The closest point on the segment + + var segCenter = v0.clone().add( v1 ).multiplyScalar( 0.5 ); + var segDir = v1.clone().sub( v0 ).normalize(); + var segExtent = v0.distanceTo( v1 ) * 0.5; + var diff = this.origin.clone().sub( segCenter ); + var a01 = - this.direction.dot( segDir ); + var b0 = diff.dot( this.direction ); + var b1 = - diff.dot( segDir ); + var c = diff.lengthSq(); + var det = Math.abs( 1 - a01 * a01 ); + var s0, s1, sqrDist, extDet; + + if ( det >= 0 ) { + + // The ray and segment are not parallel. + + s0 = a01 * b1 - b0; + s1 = a01 * b0 - b1; + extDet = segExtent * det; + + if ( s0 >= 0 ) { + + if ( s1 >= - extDet ) { + + if ( s1 <= extDet ) { + + // region 0 + // Minimum at interior points of ray and segment. + + var invDet = 1 / det; + s0 *= invDet; + s1 *= invDet; + sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; + + } else { + + // region 1 + + s1 = segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + + } + + } else { + + // region 5 + + s1 = - segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + + } + + } else { + + if ( s1 <= - extDet ) { + + // region 4 + + s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); + s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + + } else if ( s1 <= extDet ) { + + // region 3 + + s0 = 0; + s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = s1 * ( s1 + 2 * b1 ) + c; + + } else { + + // region 2 + + s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); + s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + + } + + } + + } else { + + // Ray and segment are parallel. + + s1 = ( a01 > 0 ) ? - segExtent : segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + + } + + if ( optionalPointOnRay ) { + + optionalPointOnRay.copy( this.direction.clone().multiplyScalar( s0 ).add( this.origin ) ); + + } + + if ( optionalPointOnSegment ) { + + optionalPointOnSegment.copy( segDir.clone().multiplyScalar( s1 ).add( segCenter ) ); + + } + + return sqrDist; + + }, + + isIntersectionSphere: function ( sphere ) { + + return this.distanceToPoint( sphere.center ) <= sphere.radius; + + }, + + intersectSphere: function () { + + // from http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-sphere-intersection/ + + var v1 = new THREE.Vector3(); + + return function ( sphere, optionalTarget ) { + + v1.subVectors( sphere.center, this.origin ); + + var tca = v1.dot( this.direction ); + + var d2 = v1.dot( v1 ) - tca * tca; + + var radius2 = sphere.radius * sphere.radius; + + if ( d2 > radius2 ) return null; + + var thc = Math.sqrt( radius2 - d2 ); + + // t0 = first intersect point - entrance on front of sphere + var t0 = tca - thc; + + // t1 = second intersect point - exit point on back of sphere + var t1 = tca + thc; + + // test to see if both t0 and t1 are behind the ray - if so, return null + if ( t0 < 0 && t1 < 0 ) return null; + + // test to see if t0 is behind the ray: + // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, + // in order to always return an intersect point that is in front of the ray. + if ( t0 < 0 ) return this.at( t1, optionalTarget ); + + // else t0 is in front of the ray, so return the first collision point scaled by t0 + return this.at( t0, optionalTarget ); + + } + + }(), + + isIntersectionPlane: function ( plane ) { + + // check if the ray lies on the plane first + + var distToPoint = plane.distanceToPoint( this.origin ); + + if ( distToPoint === 0 ) { + + return true; + + } + + var denominator = plane.normal.dot( this.direction ); + + if ( denominator * distToPoint < 0 ) { + + return true; + + } + + // ray origin is behind the plane (and is pointing behind it) + + return false; + + }, + + distanceToPlane: function ( plane ) { + + var denominator = plane.normal.dot( this.direction ); + if ( denominator == 0 ) { + + // line is coplanar, return origin + if ( plane.distanceToPoint( this.origin ) == 0 ) { + + return 0; + + } + + // Null is preferable to undefined since undefined means.... it is undefined + + return null; + + } + + var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; + + // Return if the ray never intersects the plane + + return t >= 0 ? t : null; + + }, + + intersectPlane: function ( plane, optionalTarget ) { + + var t = this.distanceToPlane( plane ); + + if ( t === null ) { + + return null; + } + + return this.at( t, optionalTarget ); + + }, + + isIntersectionBox: function () { + + var v = new THREE.Vector3(); + + return function ( box ) { + + return this.intersectBox( box, v ) !== null; + + }; + + }(), + + intersectBox: function ( box , optionalTarget ) { + + // http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-box-intersection/ + + var tmin,tmax,tymin,tymax,tzmin,tzmax; + + var invdirx = 1 / this.direction.x, + invdiry = 1 / this.direction.y, + invdirz = 1 / this.direction.z; + + var origin = this.origin; + + if ( invdirx >= 0 ) { + + tmin = ( box.min.x - origin.x ) * invdirx; + tmax = ( box.max.x - origin.x ) * invdirx; + + } else { + + tmin = ( box.max.x - origin.x ) * invdirx; + tmax = ( box.min.x - origin.x ) * invdirx; + } + + if ( invdiry >= 0 ) { + + tymin = ( box.min.y - origin.y ) * invdiry; + tymax = ( box.max.y - origin.y ) * invdiry; + + } else { + + tymin = ( box.max.y - origin.y ) * invdiry; + tymax = ( box.min.y - origin.y ) * invdiry; + } + + if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; + + // These lines also handle the case where tmin or tmax is NaN + // (result of 0 * Infinity). x !== x returns true if x is NaN + + if ( tymin > tmin || tmin !== tmin ) tmin = tymin; + + if ( tymax < tmax || tmax !== tmax ) tmax = tymax; + + if ( invdirz >= 0 ) { + + tzmin = ( box.min.z - origin.z ) * invdirz; + tzmax = ( box.max.z - origin.z ) * invdirz; + + } else { + + tzmin = ( box.max.z - origin.z ) * invdirz; + tzmax = ( box.min.z - origin.z ) * invdirz; + } + + if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; + + if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; + + if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; + + //return point closest to the ray (positive side) + + if ( tmax < 0 ) return null; + + return this.at( tmin >= 0 ? tmin : tmax, optionalTarget ); + + }, + + intersectTriangle: function () { + + // Compute the offset origin, edges, and normal. + var diff = new THREE.Vector3(); + var edge1 = new THREE.Vector3(); + var edge2 = new THREE.Vector3(); + var normal = new THREE.Vector3(); + + return function ( a, b, c, backfaceCulling, optionalTarget ) { + + // from http://www.geometrictools.com/LibMathematics/Intersection/Wm5IntrRay3Triangle3.cpp + + edge1.subVectors( b, a ); + edge2.subVectors( c, a ); + normal.crossVectors( edge1, edge2 ); + + // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, + // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by + // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) + // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) + // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) + var DdN = this.direction.dot( normal ); + var sign; + + if ( DdN > 0 ) { + + if ( backfaceCulling ) return null; + sign = 1; + + } else if ( DdN < 0 ) { + + sign = - 1; + DdN = - DdN; + + } else { + + return null; + + } + + diff.subVectors( this.origin, a ); + var DdQxE2 = sign * this.direction.dot( edge2.crossVectors( diff, edge2 ) ); + + // b1 < 0, no intersection + if ( DdQxE2 < 0 ) { + + return null; + + } + + var DdE1xQ = sign * this.direction.dot( edge1.cross( diff ) ); + + // b2 < 0, no intersection + if ( DdE1xQ < 0 ) { + + return null; + + } + + // b1+b2 > 1, no intersection + if ( DdQxE2 + DdE1xQ > DdN ) { + + return null; + + } + + // Line intersects triangle, check if ray does. + var QdN = - sign * diff.dot( normal ); + + // t < 0, no intersection + if ( QdN < 0 ) { + + return null; + + } + + // Ray intersects triangle. + return this.at( QdN / DdN, optionalTarget ); + + }; + + }(), + + applyMatrix4: function ( matrix4 ) { + + this.direction.add( this.origin ).applyMatrix4( matrix4 ); + this.origin.applyMatrix4( matrix4 ); + this.direction.sub( this.origin ); + this.direction.normalize(); + + return this; + }, + + equals: function ( ray ) { + + return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); + + }, + + clone: function () { + + return new THREE.Ray().copy( this ); + + } + +}; + +// File:src/math/Sphere.js + +/** + * @author bhouston / http://exocortex.com + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Sphere = function ( center, radius ) { + + this.center = ( center !== undefined ) ? center : new THREE.Vector3(); + this.radius = ( radius !== undefined ) ? radius : 0; + +}; + +THREE.Sphere.prototype = { + + constructor: THREE.Sphere, + + set: function ( center, radius ) { + + this.center.copy( center ); + this.radius = radius; + + return this; + }, + + setFromPoints: function () { + + var box = new THREE.Box3(); + + return function ( points, optionalCenter ) { + + var center = this.center; + + if ( optionalCenter !== undefined ) { + + center.copy( optionalCenter ); + + } else { + + box.setFromPoints( points ).center( center ); + + } + + var maxRadiusSq = 0; + + for ( var i = 0, il = points.length; i < il; i ++ ) { + + maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); + + } + + this.radius = Math.sqrt( maxRadiusSq ); + + return this; + + }; + + }(), + + copy: function ( sphere ) { + + this.center.copy( sphere.center ); + this.radius = sphere.radius; + + return this; + + }, + + empty: function () { + + return ( this.radius <= 0 ); + + }, + + containsPoint: function ( point ) { + + return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); + + }, + + distanceToPoint: function ( point ) { + + return ( point.distanceTo( this.center ) - this.radius ); + + }, + + intersectsSphere: function ( sphere ) { + + var radiusSum = this.radius + sphere.radius; + + return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); + + }, + + clampPoint: function ( point, optionalTarget ) { + + var deltaLengthSq = this.center.distanceToSquared( point ); + + var result = optionalTarget || new THREE.Vector3(); + result.copy( point ); + + if ( deltaLengthSq > ( this.radius * this.radius ) ) { + + result.sub( this.center ).normalize(); + result.multiplyScalar( this.radius ).add( this.center ); + + } + + return result; + + }, + + getBoundingBox: function ( optionalTarget ) { + + var box = optionalTarget || new THREE.Box3(); + + box.set( this.center, this.center ); + box.expandByScalar( this.radius ); + + return box; + + }, + + applyMatrix4: function ( matrix ) { + + this.center.applyMatrix4( matrix ); + this.radius = this.radius * matrix.getMaxScaleOnAxis(); + + return this; + + }, + + translate: function ( offset ) { + + this.center.add( offset ); + + return this; + + }, + + equals: function ( sphere ) { + + return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); + + }, + + clone: function () { + + return new THREE.Sphere().copy( this ); + + } + +}; + +// File:src/math/Frustum.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author bhouston / http://exocortex.com + */ + +THREE.Frustum = function ( p0, p1, p2, p3, p4, p5 ) { + + this.planes = [ + + ( p0 !== undefined ) ? p0 : new THREE.Plane(), + ( p1 !== undefined ) ? p1 : new THREE.Plane(), + ( p2 !== undefined ) ? p2 : new THREE.Plane(), + ( p3 !== undefined ) ? p3 : new THREE.Plane(), + ( p4 !== undefined ) ? p4 : new THREE.Plane(), + ( p5 !== undefined ) ? p5 : new THREE.Plane() + + ]; + +}; + +THREE.Frustum.prototype = { + + constructor: THREE.Frustum, + + set: function ( p0, p1, p2, p3, p4, p5 ) { + + var planes = this.planes; + + planes[ 0 ].copy( p0 ); + planes[ 1 ].copy( p1 ); + planes[ 2 ].copy( p2 ); + planes[ 3 ].copy( p3 ); + planes[ 4 ].copy( p4 ); + planes[ 5 ].copy( p5 ); + + return this; + + }, + + copy: function ( frustum ) { + + var planes = this.planes; + + for ( var i = 0; i < 6; i ++ ) { + + planes[ i ].copy( frustum.planes[ i ] ); + + } + + return this; + + }, + + setFromMatrix: function ( m ) { + + var planes = this.planes; + var me = m.elements; + var me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ]; + var me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ]; + var me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ]; + var me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ]; + + planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize(); + planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize(); + planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize(); + planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize(); + planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize(); + planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize(); + + return this; + + }, + + intersectsObject: function () { + + var sphere = new THREE.Sphere(); + + return function ( object ) { + + var geometry = object.geometry; + + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + + sphere.copy( geometry.boundingSphere ); + sphere.applyMatrix4( object.matrixWorld ); + + return this.intersectsSphere( sphere ); + + }; + + }(), + + intersectsSphere: function ( sphere ) { + + var planes = this.planes; + var center = sphere.center; + var negRadius = - sphere.radius; + + for ( var i = 0; i < 6; i ++ ) { + + var distance = planes[ i ].distanceToPoint( center ); + + if ( distance < negRadius ) { + + return false; + + } + + } + + return true; + + }, + + intersectsBox: function () { + + var p1 = new THREE.Vector3(), + p2 = new THREE.Vector3(); + + return function ( box ) { + + var planes = this.planes; + + for ( var i = 0; i < 6 ; i ++ ) { + + var plane = planes[ i ]; + + p1.x = plane.normal.x > 0 ? box.min.x : box.max.x; + p2.x = plane.normal.x > 0 ? box.max.x : box.min.x; + p1.y = plane.normal.y > 0 ? box.min.y : box.max.y; + p2.y = plane.normal.y > 0 ? box.max.y : box.min.y; + p1.z = plane.normal.z > 0 ? box.min.z : box.max.z; + p2.z = plane.normal.z > 0 ? box.max.z : box.min.z; + + var d1 = plane.distanceToPoint( p1 ); + var d2 = plane.distanceToPoint( p2 ); + + // if both outside plane, no intersection + + if ( d1 < 0 && d2 < 0 ) { + + return false; + + } + } + + return true; + }; + + }(), + + + containsPoint: function ( point ) { + + var planes = this.planes; + + for ( var i = 0; i < 6; i ++ ) { + + if ( planes[ i ].distanceToPoint( point ) < 0 ) { + + return false; + + } + + } + + return true; + + }, + + clone: function () { + + return new THREE.Frustum().copy( this ); + + } + +}; + +// File:src/math/Plane.js + +/** + * @author bhouston / http://exocortex.com + */ + +THREE.Plane = function ( normal, constant ) { + + this.normal = ( normal !== undefined ) ? normal : new THREE.Vector3( 1, 0, 0 ); + this.constant = ( constant !== undefined ) ? constant : 0; + +}; + +THREE.Plane.prototype = { + + constructor: THREE.Plane, + + set: function ( normal, constant ) { + + this.normal.copy( normal ); + this.constant = constant; + + return this; + + }, + + setComponents: function ( x, y, z, w ) { + + this.normal.set( x, y, z ); + this.constant = w; + + return this; + + }, + + setFromNormalAndCoplanarPoint: function ( normal, point ) { + + this.normal.copy( normal ); + this.constant = - point.dot( this.normal ); // must be this.normal, not normal, as this.normal is normalized + + return this; + + }, + + setFromCoplanarPoints: function () { + + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + + return function ( a, b, c ) { + + var normal = v1.subVectors( c, b ).cross( v2.subVectors( a, b ) ).normalize(); + + // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? + + this.setFromNormalAndCoplanarPoint( normal, a ); + + return this; + + }; + + }(), + + + copy: function ( plane ) { + + this.normal.copy( plane.normal ); + this.constant = plane.constant; + + return this; + + }, + + normalize: function () { + + // Note: will lead to a divide by zero if the plane is invalid. + + var inverseNormalLength = 1.0 / this.normal.length(); + this.normal.multiplyScalar( inverseNormalLength ); + this.constant *= inverseNormalLength; + + return this; + + }, + + negate: function () { + + this.constant *= - 1; + this.normal.negate(); + + return this; + + }, + + distanceToPoint: function ( point ) { + + return this.normal.dot( point ) + this.constant; + + }, + + distanceToSphere: function ( sphere ) { + + return this.distanceToPoint( sphere.center ) - sphere.radius; + + }, + + projectPoint: function ( point, optionalTarget ) { + + return this.orthoPoint( point, optionalTarget ).sub( point ).negate(); + + }, + + orthoPoint: function ( point, optionalTarget ) { + + var perpendicularMagnitude = this.distanceToPoint( point ); + + var result = optionalTarget || new THREE.Vector3(); + return result.copy( this.normal ).multiplyScalar( perpendicularMagnitude ); + + }, + + isIntersectionLine: function ( line ) { + + // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. + + var startSign = this.distanceToPoint( line.start ); + var endSign = this.distanceToPoint( line.end ); + + return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); + + }, + + intersectLine: function () { + + var v1 = new THREE.Vector3(); + + return function ( line, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + var direction = line.delta( v1 ); + + var denominator = this.normal.dot( direction ); + + if ( denominator == 0 ) { + + // line is coplanar, return origin + if ( this.distanceToPoint( line.start ) == 0 ) { + + return result.copy( line.start ); + + } + + // Unsure if this is the correct method to handle this case. + return undefined; + + } + + var t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; + + if ( t < 0 || t > 1 ) { + + return undefined; + + } + + return result.copy( direction ).multiplyScalar( t ).add( line.start ); + + }; + + }(), + + + coplanarPoint: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.copy( this.normal ).multiplyScalar( - this.constant ); + + }, + + applyMatrix4: function () { + + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + var m1 = new THREE.Matrix3(); + + return function ( matrix, optionalNormalMatrix ) { + + // compute new normal based on theory here: + // http://www.songho.ca/opengl/gl_normaltransform.html + var normalMatrix = optionalNormalMatrix || m1.getNormalMatrix( matrix ); + var newNormal = v1.copy( this.normal ).applyMatrix3( normalMatrix ); + + var newCoplanarPoint = this.coplanarPoint( v2 ); + newCoplanarPoint.applyMatrix4( matrix ); + + this.setFromNormalAndCoplanarPoint( newNormal, newCoplanarPoint ); + + return this; + + }; + + }(), + + translate: function ( offset ) { + + this.constant = this.constant - offset.dot( this.normal ); + + return this; + + }, + + equals: function ( plane ) { + + return plane.normal.equals( this.normal ) && ( plane.constant == this.constant ); + + }, + + clone: function () { + + return new THREE.Plane().copy( this ); + + } + +}; + +// File:src/math/Math.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Math = { + + generateUUID: function () { + + // http://www.broofa.com/Tools/Math.uuid.htm + + var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( '' ); + var uuid = new Array( 36 ); + var rnd = 0, r; + + return function () { + + for ( var i = 0; i < 36; i ++ ) { + + if ( i == 8 || i == 13 || i == 18 || i == 23 ) { + + uuid[ i ] = '-'; + + } else if ( i == 14 ) { + + uuid[ i ] = '4'; + + } else { + + if ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0; + r = rnd & 0xf; + rnd = rnd >> 4; + uuid[ i ] = chars[ ( i == 19 ) ? ( r & 0x3 ) | 0x8 : r ]; + + } + } + + return uuid.join( '' ); + + }; + + }(), + + // Clamp value to range + + clamp: function ( x, a, b ) { + + return ( x < a ) ? a : ( ( x > b ) ? b : x ); + + }, + + // Clamp value to range to range + + mapLinear: function ( x, a1, a2, b1, b2 ) { + + return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); + + }, + + // http://en.wikipedia.org/wiki/Smoothstep + + smoothstep: function ( x, min, max ) { + + if ( x <= min ) return 0; + if ( x >= max ) return 1; + + x = ( x - min ) / ( max - min ); + + return x * x * ( 3 - 2 * x ); + + }, + + smootherstep: function ( x, min, max ) { + + if ( x <= min ) return 0; + if ( x >= max ) return 1; + + x = ( x - min ) / ( max - min ); + + return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); + + }, + + // Random float from <0, 1> with 16 bits of randomness + // (standard Math.random() creates repetitive patterns when applied over larger space) + + random16: function () { + + return ( 65280 * Math.random() + 255 * Math.random() ) / 65535; + + }, + + // Random integer from interval + + randInt: function ( low, high ) { + + return low + Math.floor( Math.random() * ( high - low + 1 ) ); + + }, + + // Random float from interval + + randFloat: function ( low, high ) { + + return low + Math.random() * ( high - low ); + + }, + + // Random float from <-range/2, range/2> interval + + randFloatSpread: function ( range ) { + + return range * ( 0.5 - Math.random() ); + + }, + + sign: function ( x ) { + + return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : 0; + + }, + + degToRad: function () { + + var degreeToRadiansFactor = Math.PI / 180; + + return function ( degrees ) { + + return degrees * degreeToRadiansFactor; + + }; + + }(), + + radToDeg: function () { + + var radianToDegreesFactor = 180 / Math.PI; + + return function ( radians ) { + + return radians * radianToDegreesFactor; + + }; + + }(), + + isPowerOfTwo: function ( value ) { + + return ( value & ( value - 1 ) ) === 0 && value !== 0; + + } + +}; + +// File:src/math/Spline.js + +/** + * Spline from Tween.js, slightly optimized (and trashed) + * http://sole.github.com/tween.js/examples/05_spline.html + * + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Spline = function ( points ) { + + this.points = points; + + var c = [], v3 = { x: 0, y: 0, z: 0 }, + point, intPoint, weight, w2, w3, + pa, pb, pc, pd; + + this.initFromArray = function ( a ) { + + this.points = []; + + for ( var i = 0; i < a.length; i ++ ) { + + this.points[ i ] = { x: a[ i ][ 0 ], y: a[ i ][ 1 ], z: a[ i ][ 2 ] }; + + } + + }; + + this.getPoint = function ( k ) { + + point = ( this.points.length - 1 ) * k; + intPoint = Math.floor( point ); + weight = point - intPoint; + + c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1; + c[ 1 ] = intPoint; + c[ 2 ] = intPoint > this.points.length - 2 ? this.points.length - 1 : intPoint + 1; + c[ 3 ] = intPoint > this.points.length - 3 ? this.points.length - 1 : intPoint + 2; + + pa = this.points[ c[ 0 ] ]; + pb = this.points[ c[ 1 ] ]; + pc = this.points[ c[ 2 ] ]; + pd = this.points[ c[ 3 ] ]; + + w2 = weight * weight; + w3 = weight * w2; + + v3.x = interpolate( pa.x, pb.x, pc.x, pd.x, weight, w2, w3 ); + v3.y = interpolate( pa.y, pb.y, pc.y, pd.y, weight, w2, w3 ); + v3.z = interpolate( pa.z, pb.z, pc.z, pd.z, weight, w2, w3 ); + + return v3; + + }; + + this.getControlPointsArray = function () { + + var i, p, l = this.points.length, + coords = []; + + for ( i = 0; i < l; i ++ ) { + + p = this.points[ i ]; + coords[ i ] = [ p.x, p.y, p.z ]; + + } + + return coords; + + }; + + // approximate length by summing linear segments + + this.getLength = function ( nSubDivisions ) { + + var i, index, nSamples, position, + point = 0, intPoint = 0, oldIntPoint = 0, + oldPosition = new THREE.Vector3(), + tmpVec = new THREE.Vector3(), + chunkLengths = [], + totalLength = 0; + + // first point has 0 length + + chunkLengths[ 0 ] = 0; + + if ( ! nSubDivisions ) nSubDivisions = 100; + + nSamples = this.points.length * nSubDivisions; + + oldPosition.copy( this.points[ 0 ] ); + + for ( i = 1; i < nSamples; i ++ ) { + + index = i / nSamples; + + position = this.getPoint( index ); + tmpVec.copy( position ); + + totalLength += tmpVec.distanceTo( oldPosition ); + + oldPosition.copy( position ); + + point = ( this.points.length - 1 ) * index; + intPoint = Math.floor( point ); + + if ( intPoint != oldIntPoint ) { + + chunkLengths[ intPoint ] = totalLength; + oldIntPoint = intPoint; + + } + + } + + // last point ends with total length + + chunkLengths[ chunkLengths.length ] = totalLength; + + return { chunks: chunkLengths, total: totalLength }; + + }; + + this.reparametrizeByArcLength = function ( samplingCoef ) { + + var i, j, + index, indexCurrent, indexNext, + linearDistance, realDistance, + sampling, position, + newpoints = [], + tmpVec = new THREE.Vector3(), + sl = this.getLength(); + + newpoints.push( tmpVec.copy( this.points[ 0 ] ).clone() ); + + for ( i = 1; i < this.points.length; i ++ ) { + + //tmpVec.copy( this.points[ i - 1 ] ); + //linearDistance = tmpVec.distanceTo( this.points[ i ] ); + + realDistance = sl.chunks[ i ] - sl.chunks[ i - 1 ]; + + sampling = Math.ceil( samplingCoef * realDistance / sl.total ); + + indexCurrent = ( i - 1 ) / ( this.points.length - 1 ); + indexNext = i / ( this.points.length - 1 ); + + for ( j = 1; j < sampling - 1; j ++ ) { + + index = indexCurrent + j * ( 1 / sampling ) * ( indexNext - indexCurrent ); + + position = this.getPoint( index ); + newpoints.push( tmpVec.copy( position ).clone() ); + + } + + newpoints.push( tmpVec.copy( this.points[ i ] ).clone() ); + + } + + this.points = newpoints; + + }; + + // Catmull-Rom + + function interpolate( p0, p1, p2, p3, t, t2, t3 ) { + + var v0 = ( p2 - p0 ) * 0.5, + v1 = ( p3 - p1 ) * 0.5; + + return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1; + + }; + +}; + +// File:src/math/Triangle.js + +/** + * @author bhouston / http://exocortex.com + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Triangle = function ( a, b, c ) { + + this.a = ( a !== undefined ) ? a : new THREE.Vector3(); + this.b = ( b !== undefined ) ? b : new THREE.Vector3(); + this.c = ( c !== undefined ) ? c : new THREE.Vector3(); + +}; + +THREE.Triangle.normal = function () { + + var v0 = new THREE.Vector3(); + + return function ( a, b, c, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + result.subVectors( c, b ); + v0.subVectors( a, b ); + result.cross( v0 ); + + var resultLengthSq = result.lengthSq(); + if ( resultLengthSq > 0 ) { + + return result.multiplyScalar( 1 / Math.sqrt( resultLengthSq ) ); + + } + + return result.set( 0, 0, 0 ); + + }; + +}(); + +// static/instance method to calculate barycoordinates +// based on: http://www.blackpawn.com/texts/pointinpoly/default.html +THREE.Triangle.barycoordFromPoint = function () { + + var v0 = new THREE.Vector3(); + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + + return function ( point, a, b, c, optionalTarget ) { + + v0.subVectors( c, a ); + v1.subVectors( b, a ); + v2.subVectors( point, a ); + + var dot00 = v0.dot( v0 ); + var dot01 = v0.dot( v1 ); + var dot02 = v0.dot( v2 ); + var dot11 = v1.dot( v1 ); + var dot12 = v1.dot( v2 ); + + var denom = ( dot00 * dot11 - dot01 * dot01 ); + + var result = optionalTarget || new THREE.Vector3(); + + // colinear or singular triangle + if ( denom == 0 ) { + // arbitrary location outside of triangle? + // not sure if this is the best idea, maybe should be returning undefined + return result.set( - 2, - 1, - 1 ); + } + + var invDenom = 1 / denom; + var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; + var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; + + // barycoordinates must always sum to 1 + return result.set( 1 - u - v, v, u ); + + }; + +}(); + +THREE.Triangle.containsPoint = function () { + + var v1 = new THREE.Vector3(); + + return function ( point, a, b, c ) { + + var result = THREE.Triangle.barycoordFromPoint( point, a, b, c, v1 ); + + return ( result.x >= 0 ) && ( result.y >= 0 ) && ( ( result.x + result.y ) <= 1 ); + + }; + +}(); + +THREE.Triangle.prototype = { + + constructor: THREE.Triangle, + + set: function ( a, b, c ) { + + this.a.copy( a ); + this.b.copy( b ); + this.c.copy( c ); + + return this; + + }, + + setFromPointsAndIndices: function ( points, i0, i1, i2 ) { + + this.a.copy( points[ i0 ] ); + this.b.copy( points[ i1 ] ); + this.c.copy( points[ i2 ] ); + + return this; + + }, + + copy: function ( triangle ) { + + this.a.copy( triangle.a ); + this.b.copy( triangle.b ); + this.c.copy( triangle.c ); + + return this; + + }, + + area: function () { + + var v0 = new THREE.Vector3(); + var v1 = new THREE.Vector3(); + + return function () { + + v0.subVectors( this.c, this.b ); + v1.subVectors( this.a, this.b ); + + return v0.cross( v1 ).length() * 0.5; + + }; + + }(), + + midpoint: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); + + }, + + normal: function ( optionalTarget ) { + + return THREE.Triangle.normal( this.a, this.b, this.c, optionalTarget ); + + }, + + plane: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Plane(); + + return result.setFromCoplanarPoints( this.a, this.b, this.c ); + + }, + + barycoordFromPoint: function ( point, optionalTarget ) { + + return THREE.Triangle.barycoordFromPoint( point, this.a, this.b, this.c, optionalTarget ); + + }, + + containsPoint: function ( point ) { + + return THREE.Triangle.containsPoint( point, this.a, this.b, this.c ); + + }, + + equals: function ( triangle ) { + + return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); + + }, + + clone: function () { + + return new THREE.Triangle().copy( this ); + + } + +}; + +// File:src/core/Clock.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Clock = function ( autoStart ) { + + this.autoStart = ( autoStart !== undefined ) ? autoStart : true; + + this.startTime = 0; + this.oldTime = 0; + this.elapsedTime = 0; + + this.running = false; + +}; + +THREE.Clock.prototype = { + + constructor: THREE.Clock, + + start: function () { + + this.startTime = self.performance !== undefined && self.performance.now !== undefined + ? self.performance.now() + : Date.now(); + + this.oldTime = this.startTime; + this.running = true; + }, + + stop: function () { + + this.getElapsedTime(); + this.running = false; + + }, + + getElapsedTime: function () { + + this.getDelta(); + return this.elapsedTime; + + }, + + getDelta: function () { + + var diff = 0; + + if ( this.autoStart && ! this.running ) { + + this.start(); + + } + + if ( this.running ) { + + var newTime = self.performance !== undefined && self.performance.now !== undefined + ? self.performance.now() + : Date.now(); + + diff = 0.001 * ( newTime - this.oldTime ); + this.oldTime = newTime; + + this.elapsedTime += diff; + + } + + return diff; + + } + +}; + +// File:src/core/EventDispatcher.js + +/** + * https://github.com/mrdoob/eventdispatcher.js/ + */ + +THREE.EventDispatcher = function () {} + +THREE.EventDispatcher.prototype = { + + constructor: THREE.EventDispatcher, + + apply: function ( object ) { + + object.addEventListener = THREE.EventDispatcher.prototype.addEventListener; + object.hasEventListener = THREE.EventDispatcher.prototype.hasEventListener; + object.removeEventListener = THREE.EventDispatcher.prototype.removeEventListener; + object.dispatchEvent = THREE.EventDispatcher.prototype.dispatchEvent; + + }, + + addEventListener: function ( type, listener ) { + + if ( this._listeners === undefined ) this._listeners = {}; + + var listeners = this._listeners; + + if ( listeners[ type ] === undefined ) { + + listeners[ type ] = []; + + } + + if ( listeners[ type ].indexOf( listener ) === - 1 ) { + + listeners[ type ].push( listener ); + + } + + }, + + hasEventListener: function ( type, listener ) { + + if ( this._listeners === undefined ) return false; + + var listeners = this._listeners; + + if ( listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1 ) { + + return true; + + } + + return false; + + }, + + removeEventListener: function ( type, listener ) { + + if ( this._listeners === undefined ) return; + + var listeners = this._listeners; + var listenerArray = listeners[ type ]; + + if ( listenerArray !== undefined ) { + + var index = listenerArray.indexOf( listener ); + + if ( index !== - 1 ) { + + listenerArray.splice( index, 1 ); + + } + + } + + }, + + dispatchEvent: function ( event ) { + + if ( this._listeners === undefined ) return; + + var listeners = this._listeners; + var listenerArray = listeners[ event.type ]; + + if ( listenerArray !== undefined ) { + + event.target = this; + + var array = []; + var length = listenerArray.length; + + for ( var i = 0; i < length; i ++ ) { + + array[ i ] = listenerArray[ i ]; + + } + + for ( var i = 0; i < length; i ++ ) { + + array[ i ].call( this, event ); + + } + + } + + } + +}; + +// File:src/core/Raycaster.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author bhouston / http://exocortex.com/ + * @author stephomi / http://stephaneginier.com/ + */ + +( function ( THREE ) { + + THREE.Raycaster = function ( origin, direction, near, far ) { + + this.ray = new THREE.Ray( origin, direction ); + // direction is assumed to be normalized (for accurate distance calculations) + + this.near = near || 0; + this.far = far || Infinity; + + this.params = { + Sprite: {}, + Mesh: {}, + PointCloud: { threshold: 1 }, + LOD: {}, + Line: {} + }; + + }; + + var descSort = function ( a, b ) { + + return a.distance - b.distance; + + }; + + var intersectObject = function ( object, raycaster, intersects, recursive ) { + + object.raycast( raycaster, intersects ); + + if ( recursive === true ) { + + var children = object.children; + + for ( var i = 0, l = children.length; i < l; i ++ ) { + + intersectObject( children[ i ], raycaster, intersects, true ); + + } + + } + + }; + + // + + THREE.Raycaster.prototype = { + + constructor: THREE.Raycaster, + + precision: 0.0001, + linePrecision: 1, + + set: function ( origin, direction ) { + + this.ray.set( origin, direction ); + // direction is assumed to be normalized (for accurate distance calculations) + + }, + + intersectObject: function ( object, recursive ) { + + var intersects = []; + + intersectObject( object, this, intersects, recursive ); + + intersects.sort( descSort ); + + return intersects; + + }, + + intersectObjects: function ( objects, recursive ) { + + var intersects = []; + + for ( var i = 0, l = objects.length; i < l; i ++ ) { + + intersectObject( objects[ i ], this, intersects, recursive ); + + } + + intersects.sort( descSort ); + + return intersects; + + } + + }; + +}( THREE ) ); + +// File:src/core/Object3D.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.Object3D = function () { + + this.id = THREE.Object3DIdCount ++; + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + + this.parent = undefined; + this.children = []; + + this.up = THREE.Object3D.DefaultUp.clone(); + + var scope = this; + + var position = new THREE.Vector3(); + var rotation = new THREE.Euler(); + var quaternion = new THREE.Quaternion(); + var scale = new THREE.Vector3( 1, 1, 1 ); + + rotation.onChange( function () { + quaternion.setFromEuler( rotation, false ); + } ); + + quaternion.onChange( function () { + rotation.setFromQuaternion( quaternion, undefined, false ); + } ); + + Object.defineProperties( this, { + position: { + enumerable: true, + value: position + }, + rotation: { + enumerable: true, + value: rotation + }, + quaternion: { + enumerable: true, + value: quaternion + }, + scale: { + enumerable: true, + value: scale + }, + } ); + + this.renderDepth = null; + + this.rotationAutoUpdate = true; + + this.matrix = new THREE.Matrix4(); + this.matrixWorld = new THREE.Matrix4(); + + this.matrixAutoUpdate = true; + this.matrixWorldNeedsUpdate = false; + + this.visible = true; + + this.castShadow = false; + this.receiveShadow = false; + + this.frustumCulled = true; + + this.userData = {}; + +}; + +THREE.Object3D.DefaultUp = new THREE.Vector3( 0, 1, 0 ); + +THREE.Object3D.prototype = { + + constructor: THREE.Object3D, + + get eulerOrder () { + + console.warn( 'THREE.Object3D: .eulerOrder has been moved to .rotation.order.' ); + + return this.rotation.order; + + }, + + set eulerOrder ( value ) { + + console.warn( 'THREE.Object3D: .eulerOrder has been moved to .rotation.order.' ); + + this.rotation.order = value; + + }, + + get useQuaternion () { + + console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); + + }, + + set useQuaternion ( value ) { + + console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); + + }, + + applyMatrix: function ( matrix ) { + + this.matrix.multiplyMatrices( matrix, this.matrix ); + + this.matrix.decompose( this.position, this.quaternion, this.scale ); + + }, + + setRotationFromAxisAngle: function ( axis, angle ) { + + // assumes axis is normalized + + this.quaternion.setFromAxisAngle( axis, angle ); + + }, + + setRotationFromEuler: function ( euler ) { + + this.quaternion.setFromEuler( euler, true ); + + }, + + setRotationFromMatrix: function ( m ) { + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + this.quaternion.setFromRotationMatrix( m ); + + }, + + setRotationFromQuaternion: function ( q ) { + + // assumes q is normalized + + this.quaternion.copy( q ); + + }, + + rotateOnAxis: function () { + + // rotate object on axis in object space + // axis is assumed to be normalized + + var q1 = new THREE.Quaternion(); + + return function ( axis, angle ) { + + q1.setFromAxisAngle( axis, angle ); + + this.quaternion.multiply( q1 ); + + return this; + + } + + }(), + + rotateX: function () { + + var v1 = new THREE.Vector3( 1, 0, 0 ); + + return function ( angle ) { + + return this.rotateOnAxis( v1, angle ); + + }; + + }(), + + rotateY: function () { + + var v1 = new THREE.Vector3( 0, 1, 0 ); + + return function ( angle ) { + + return this.rotateOnAxis( v1, angle ); + + }; + + }(), + + rotateZ: function () { + + var v1 = new THREE.Vector3( 0, 0, 1 ); + + return function ( angle ) { + + return this.rotateOnAxis( v1, angle ); + + }; + + }(), + + translateOnAxis: function () { + + // translate object by distance along axis in object space + // axis is assumed to be normalized + + var v1 = new THREE.Vector3(); + + return function ( axis, distance ) { + + v1.copy( axis ).applyQuaternion( this.quaternion ); + + this.position.add( v1.multiplyScalar( distance ) ); + + return this; + + } + + }(), + + translate: function ( distance, axis ) { + + console.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' ); + return this.translateOnAxis( axis, distance ); + + }, + + translateX: function () { + + var v1 = new THREE.Vector3( 1, 0, 0 ); + + return function ( distance ) { + + return this.translateOnAxis( v1, distance ); + + }; + + }(), + + translateY: function () { + + var v1 = new THREE.Vector3( 0, 1, 0 ); + + return function ( distance ) { + + return this.translateOnAxis( v1, distance ); + + }; + + }(), + + translateZ: function () { + + var v1 = new THREE.Vector3( 0, 0, 1 ); + + return function ( distance ) { + + return this.translateOnAxis( v1, distance ); + + }; + + }(), + + localToWorld: function ( vector ) { + + return vector.applyMatrix4( this.matrixWorld ); + + }, + + worldToLocal: function () { + + var m1 = new THREE.Matrix4(); + + return function ( vector ) { + + return vector.applyMatrix4( m1.getInverse( this.matrixWorld ) ); + + }; + + }(), + + lookAt: function () { + + // This routine does not support objects with rotated and/or translated parent(s) + + var m1 = new THREE.Matrix4(); + + return function ( vector ) { + + m1.lookAt( vector, this.position, this.up ); + + this.quaternion.setFromRotationMatrix( m1 ); + + }; + + }(), + + add: function ( object ) { + + if ( arguments.length > 1 ) { + + for ( var i = 0; i < arguments.length; i++ ) { + + this.add( arguments[ i ] ); + + } + + return this; + + }; + + if ( object === this ) { + + console.error( "THREE.Object3D.add:", object, "can't be added as a child of itself." ); + return this; + + } + + if ( object instanceof THREE.Object3D ) { + + if ( object.parent !== undefined ) { + + object.parent.remove( object ); + + } + + object.parent = this; + object.dispatchEvent( { type: 'added' } ); + + this.children.push( object ); + + // add to scene + + var scene = this; + + while ( scene.parent !== undefined ) { + + scene = scene.parent; + + } + + if ( scene !== undefined && scene instanceof THREE.Scene ) { + + scene.__addObject( object ); + + } + + } else { + + console.error( "THREE.Object3D.add:", object, "is not an instance of THREE.Object3D." ); + + } + + return this; + + }, + + remove: function ( object ) { + + if ( arguments.length > 1 ) { + + for ( var i = 0; i < arguments.length; i++ ) { + + this.remove( arguments[ i ] ); + + } + + }; + + var index = this.children.indexOf( object ); + + if ( index !== - 1 ) { + + object.parent = undefined; + object.dispatchEvent( { type: 'removed' } ); + + this.children.splice( index, 1 ); + + // remove from scene + + var scene = this; + + while ( scene.parent !== undefined ) { + + scene = scene.parent; + + } + + if ( scene !== undefined && scene instanceof THREE.Scene ) { + + scene.__removeObject( object ); + + } + + } + + }, + + raycast: function () {}, + + traverse: function ( callback ) { + + callback( this ); + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + this.children[ i ].traverse( callback ); + + } + + }, + + traverseVisible: function ( callback ) { + + if ( this.visible === false ) return; + + callback( this ); + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + this.children[ i ].traverseVisible( callback ); + + } + + }, + + getObjectById: function ( id, recursive ) { + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + var child = this.children[ i ]; + + if ( child.id === id ) { + + return child; + + } + + if ( recursive === true ) { + + child = child.getObjectById( id, recursive ); + + if ( child !== undefined ) { + + return child; + + } + + } + + } + + return undefined; + + }, + + getObjectByName: function ( name, recursive ) { + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + var child = this.children[ i ]; + + if ( child.name === name ) { + + return child; + + } + + if ( recursive === true ) { + + child = child.getObjectByName( name, recursive ); + + if ( child !== undefined ) { + + return child; + + } + + } + + } + + return undefined; + + }, + + getChildByName: function ( name, recursive ) { + + console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' ); + return this.getObjectByName( name, recursive ); + + }, + + updateMatrix: function () { + + this.matrix.compose( this.position, this.quaternion, this.scale ); + + this.matrixWorldNeedsUpdate = true; + + }, + + updateMatrixWorld: function ( force ) { + + if ( this.matrixAutoUpdate === true ) this.updateMatrix(); + + if ( this.matrixWorldNeedsUpdate === true || force === true ) { + + if ( this.parent === undefined ) { + + this.matrixWorld.copy( this.matrix ); + + } else { + + this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); + + } + + this.matrixWorldNeedsUpdate = false; + + force = true; + + } + + // update children + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + this.children[ i ].updateMatrixWorld( force ); + + } + + }, + + clone: function ( object, recursive ) { + + if ( object === undefined ) object = new THREE.Object3D(); + if ( recursive === undefined ) recursive = true; + + object.name = this.name; + + object.up.copy( this.up ); + + object.position.copy( this.position ); + object.quaternion.copy( this.quaternion ); + object.scale.copy( this.scale ); + + object.renderDepth = this.renderDepth; + + object.rotationAutoUpdate = this.rotationAutoUpdate; + + object.matrix.copy( this.matrix ); + object.matrixWorld.copy( this.matrixWorld ); + + object.matrixAutoUpdate = this.matrixAutoUpdate; + object.matrixWorldNeedsUpdate = this.matrixWorldNeedsUpdate; + + object.visible = this.visible; + + object.castShadow = this.castShadow; + object.receiveShadow = this.receiveShadow; + + object.frustumCulled = this.frustumCulled; + + object.userData = JSON.parse( JSON.stringify( this.userData ) ); + + if ( recursive === true ) { + + for ( var i = 0; i < this.children.length; i ++ ) { + + var child = this.children[ i ]; + object.add( child.clone() ); + + } + + } + + return object; + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.Object3D.prototype ); + +THREE.Object3DIdCount = 0; + +// File:src/core/Projector.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author julianwa / https://github.com/julianwa + */ + +THREE.Projector = function () { + + var _object, _objectCount, _objectPool = [], _objectPoolLength = 0, + _vertex, _vertexCount, _vertexPool = [], _vertexPoolLength = 0, + _face, _faceCount, _facePool = [], _facePoolLength = 0, + _line, _lineCount, _linePool = [], _linePoolLength = 0, + _sprite, _spriteCount, _spritePool = [], _spritePoolLength = 0, + + _renderData = { objects: [], lights: [], elements: [] }, + + _vA = new THREE.Vector3(), + _vB = new THREE.Vector3(), + _vC = new THREE.Vector3(), + + _vector3 = new THREE.Vector3(), + _vector4 = new THREE.Vector4(), + + _clipBox = new THREE.Box3( new THREE.Vector3( - 1, - 1, - 1 ), new THREE.Vector3( 1, 1, 1 ) ), + _boundingBox = new THREE.Box3(), + _points3 = new Array( 3 ), + _points4 = new Array( 4 ), + + _viewMatrix = new THREE.Matrix4(), + _viewProjectionMatrix = new THREE.Matrix4(), + + _modelMatrix, + _modelViewProjectionMatrix = new THREE.Matrix4(), + + _normalMatrix = new THREE.Matrix3(), + + _frustum = new THREE.Frustum(), + + _clippedVertex1PositionScreen = new THREE.Vector4(), + _clippedVertex2PositionScreen = new THREE.Vector4(); + + this.projectVector = function ( vector, camera ) { + + camera.matrixWorldInverse.getInverse( camera.matrixWorld ); + + _viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); + + return vector.applyProjection( _viewProjectionMatrix ); + + }; + + this.unprojectVector = function () { + + var projectionMatrixInverse = new THREE.Matrix4(); + + return function ( vector, camera ) { + + projectionMatrixInverse.getInverse( camera.projectionMatrix ); + _viewProjectionMatrix.multiplyMatrices( camera.matrixWorld, projectionMatrixInverse ); + + return vector.applyProjection( _viewProjectionMatrix ); + + }; + + }(); + + this.pickingRay = function ( vector, camera ) { + + // set two vectors with opposing z values + vector.z = - 1.0; + var end = new THREE.Vector3( vector.x, vector.y, 1.0 ); + + this.unprojectVector( vector, camera ); + this.unprojectVector( end, camera ); + + // find direction from vector to end + end.sub( vector ).normalize(); + + return new THREE.Raycaster( vector, end ); + + }; + + var RenderList = function () { + + var normals = []; + var uvs = []; + + var object = null; + var material = null; + + var normalMatrix = new THREE.Matrix3(); + + var setObject = function ( value ) { + + object = value; + material = object.material; + + normalMatrix.getNormalMatrix( object.matrixWorld ); + + normals.length = 0; + uvs.length = 0; + + }; + + var projectVertex = function ( vertex ) { + + var position = vertex.position; + var positionWorld = vertex.positionWorld; + var positionScreen = vertex.positionScreen; + + positionWorld.copy( position ).applyMatrix4( _modelMatrix ); + positionScreen.copy( positionWorld ).applyMatrix4( _viewProjectionMatrix ); + + var invW = 1 / positionScreen.w; + + positionScreen.x *= invW; + positionScreen.y *= invW; + positionScreen.z *= invW; + + vertex.visible = positionScreen.x >= - 1 && positionScreen.x <= 1 && + positionScreen.y >= - 1 && positionScreen.y <= 1 && + positionScreen.z >= - 1 && positionScreen.z <= 1; + + }; + + var pushVertex = function ( x, y, z ) { + + _vertex = getNextVertexInPool(); + _vertex.position.set( x, y, z ); + + projectVertex( _vertex ); + + }; + + var pushNormal = function ( x, y, z ) { + + normals.push( x, y, z ); + + }; + + var pushUv = function ( x, y ) { + + uvs.push( x, y ); + + }; + + var checkTriangleVisibility = function ( v1, v2, v3 ) { + + if ( v1.visible === true || v2.visible === true || v3.visible === true ) return true; + + _points3[ 0 ] = v1.positionScreen; + _points3[ 1 ] = v2.positionScreen; + _points3[ 2 ] = v3.positionScreen; + + return _clipBox.isIntersectionBox( _boundingBox.setFromPoints( _points3 ) ); + + }; + + var checkBackfaceCulling = function ( v1, v2, v3 ) { + + return ( ( v3.positionScreen.x - v1.positionScreen.x ) * + ( v2.positionScreen.y - v1.positionScreen.y ) - + ( v3.positionScreen.y - v1.positionScreen.y ) * + ( v2.positionScreen.x - v1.positionScreen.x ) ) < 0; + + }; + + var pushLine = function ( a, b ) { + + var v1 = _vertexPool[ a ]; + var v2 = _vertexPool[ b ]; + + _line = getNextLineInPool(); + + _line.id = object.id; + _line.v1.copy( v1 ); + _line.v2.copy( v2 ); + _line.z = ( v1.positionScreen.z + v2.positionScreen.z ) / 2; + + _line.material = object.material; + + _renderData.elements.push( _line ); + + }; + + var pushTriangle = function ( a, b, c ) { + + var v1 = _vertexPool[ a ]; + var v2 = _vertexPool[ b ]; + var v3 = _vertexPool[ c ]; + + if ( checkTriangleVisibility( v1, v2, v3 ) === false ) return; + + if ( material.side === THREE.DoubleSide || checkBackfaceCulling( v1, v2, v3 ) === true ) { + + _face = getNextFaceInPool(); + + _face.id = object.id; + _face.v1.copy( v1 ); + _face.v2.copy( v2 ); + _face.v3.copy( v3 ); + _face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3; + + for ( var i = 0; i < 3; i ++ ) { + + var offset = arguments[ i ] * 3; + var normal = _face.vertexNormalsModel[ i ]; + + normal.set( normals[ offset ], normals[ offset + 1 ], normals[ offset + 2 ] ); + normal.applyMatrix3( normalMatrix ).normalize(); + + var offset2 = arguments[ i ] * 2; + + var uv = _face.uvs[ i ]; + uv.set( uvs[ offset2 ], uvs[ offset2 + 1 ] ); + + } + + _face.vertexNormalsLength = 3; + + _face.material = object.material; + + _renderData.elements.push( _face ); + + } + + }; + + return { + setObject: setObject, + projectVertex: projectVertex, + checkTriangleVisibility: checkTriangleVisibility, + checkBackfaceCulling: checkBackfaceCulling, + pushVertex: pushVertex, + pushNormal: pushNormal, + pushUv: pushUv, + pushLine: pushLine, + pushTriangle: pushTriangle + } + + }; + + var renderList = new RenderList(); + + this.projectScene = function ( scene, camera, sortObjects, sortElements ) { + + _faceCount = 0; + _lineCount = 0; + _spriteCount = 0; + + _renderData.elements.length = 0; + + if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + if ( camera.parent === undefined ) camera.updateMatrixWorld(); + + _viewMatrix.copy( camera.matrixWorldInverse.getInverse( camera.matrixWorld ) ); + _viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix ); + + _frustum.setFromMatrix( _viewProjectionMatrix ); + + // + + _objectCount = 0; + + _renderData.objects.length = 0; + _renderData.lights.length = 0; + + scene.traverseVisible( function ( object ) { + + if ( object instanceof THREE.Light ) { + + _renderData.lights.push( object ); + + } else if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Sprite ) { + + if ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) { + + _object = getNextObjectInPool(); + _object.id = object.id; + _object.object = object; + + if ( object.renderDepth !== null ) { + + _object.z = object.renderDepth; + + } else { + + _vector3.setFromMatrixPosition( object.matrixWorld ); + _vector3.applyProjection( _viewProjectionMatrix ); + _object.z = _vector3.z; + + } + + _renderData.objects.push( _object ); + + } + + } + + } ); + + if ( sortObjects === true ) { + + _renderData.objects.sort( painterSort ); + + } + + // + + for ( var o = 0, ol = _renderData.objects.length; o < ol; o ++ ) { + + var object = _renderData.objects[ o ].object; + var geometry = object.geometry; + + renderList.setObject( object ); + + _modelMatrix = object.matrixWorld; + + _vertexCount = 0; + + if ( object instanceof THREE.Mesh ) { + + if ( geometry instanceof THREE.BufferGeometry ) { + + var attributes = geometry.attributes; + var offsets = geometry.offsets; + + if ( attributes.position === undefined ) continue; + + var positions = attributes.position.array; + + for ( var i = 0, l = positions.length; i < l; i += 3 ) { + + renderList.pushVertex( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); + + } + + if ( attributes.normal !== undefined ) { + + var normals = attributes.normal.array; + + for ( var i = 0, l = normals.length; i < l; i += 3 ) { + + renderList.pushNormal( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ); + + } + + } + + if ( attributes.uv !== undefined ) { + + var uvs = attributes.uv.array; + + for ( var i = 0, l = uvs.length; i < l; i += 2 ) { + + renderList.pushUv( uvs[ i ], uvs[ i + 1 ] ); + + } + + } + + if ( attributes.index !== undefined ) { + + var indices = attributes.index.array; + + if ( offsets.length > 0 ) { + + for ( var o = 0; o < offsets.length; o ++ ) { + + var offset = offsets[ o ]; + var index = offset.index; + + for ( var i = offset.start, l = offset.start + offset.count; i < l; i += 3 ) { + + renderList.pushTriangle( indices[ i ] + index, indices[ i + 1 ] + index, indices[ i + 2 ] + index ); + + } + + } + + } else { + + for ( var i = 0, l = indices.length; i < l; i += 3 ) { + + renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] ); + + } + + } + + } else { + + for ( var i = 0, l = positions.length / 3; i < l; i += 3 ) { + + renderList.pushTriangle( i, i + 1, i + 2 ); + + } + + } + + } else if ( geometry instanceof THREE.Geometry ) { + + var vertices = geometry.vertices; + var faces = geometry.faces; + var faceVertexUvs = geometry.faceVertexUvs[ 0 ]; + + _normalMatrix.getNormalMatrix( _modelMatrix ); + + var isFaceMaterial = object.material instanceof THREE.MeshFaceMaterial; + var objectMaterials = isFaceMaterial === true ? object.material : null; + + for ( var v = 0, vl = vertices.length; v < vl; v ++ ) { + + var vertex = vertices[ v ]; + renderList.pushVertex( vertex.x, vertex.y, vertex.z ); + + } + + for ( var f = 0, fl = faces.length; f < fl; f ++ ) { + + var face = faces[ f ]; + + var material = isFaceMaterial === true + ? objectMaterials.materials[ face.materialIndex ] + : object.material; + + if ( material === undefined ) continue; + + var side = material.side; + + var v1 = _vertexPool[ face.a ]; + var v2 = _vertexPool[ face.b ]; + var v3 = _vertexPool[ face.c ]; + + if ( material.morphTargets === true ) { + + var morphTargets = geometry.morphTargets; + var morphInfluences = object.morphTargetInfluences; + + var v1p = v1.position; + var v2p = v2.position; + var v3p = v3.position; + + _vA.set( 0, 0, 0 ); + _vB.set( 0, 0, 0 ); + _vC.set( 0, 0, 0 ); + + for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { + + var influence = morphInfluences[ t ]; + + if ( influence === 0 ) continue; + + var targets = morphTargets[ t ].vertices; + + _vA.x += ( targets[ face.a ].x - v1p.x ) * influence; + _vA.y += ( targets[ face.a ].y - v1p.y ) * influence; + _vA.z += ( targets[ face.a ].z - v1p.z ) * influence; + + _vB.x += ( targets[ face.b ].x - v2p.x ) * influence; + _vB.y += ( targets[ face.b ].y - v2p.y ) * influence; + _vB.z += ( targets[ face.b ].z - v2p.z ) * influence; + + _vC.x += ( targets[ face.c ].x - v3p.x ) * influence; + _vC.y += ( targets[ face.c ].y - v3p.y ) * influence; + _vC.z += ( targets[ face.c ].z - v3p.z ) * influence; + + } + + v1.position.add( _vA ); + v2.position.add( _vB ); + v3.position.add( _vC ); + + renderList.projectVertex( v1 ); + renderList.projectVertex( v2 ); + renderList.projectVertex( v3 ); + + } + + if ( renderList.checkTriangleVisibility( v1, v2, v3 ) === false ) continue; + + var visible = renderList.checkBackfaceCulling( v1, v2, v3 ); + + if ( side !== THREE.DoubleSide ) { + if ( side === THREE.FrontSide && visible === false ) continue; + if ( side === THREE.BackSide && visible === true ) continue; + } + + _face = getNextFaceInPool(); + + _face.id = object.id; + _face.v1.copy( v1 ); + _face.v2.copy( v2 ); + _face.v3.copy( v3 ); + + _face.normalModel.copy( face.normal ); + + if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) { + + _face.normalModel.negate(); + + } + + _face.normalModel.applyMatrix3( _normalMatrix ).normalize(); + + var faceVertexNormals = face.vertexNormals; + + for ( var n = 0, nl = Math.min( faceVertexNormals.length, 3 ); n < nl; n ++ ) { + + var normalModel = _face.vertexNormalsModel[ n ]; + normalModel.copy( faceVertexNormals[ n ] ); + + if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) { + + normalModel.negate(); + + } + + normalModel.applyMatrix3( _normalMatrix ).normalize(); + + } + + _face.vertexNormalsLength = faceVertexNormals.length; + + var vertexUvs = faceVertexUvs[ f ]; + + if ( vertexUvs !== undefined ) { + + for ( var u = 0; u < 3; u ++ ) { + + _face.uvs[ u ].copy( vertexUvs[ u ] ); + + } + + } + + _face.color = face.color; + _face.material = material; + + _face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3; + + _renderData.elements.push( _face ); + + } + + } + + } else if ( object instanceof THREE.Line ) { + + if ( geometry instanceof THREE.BufferGeometry ) { + + var attributes = geometry.attributes; + + if ( attributes.position !== undefined ) { + + var positions = attributes.position.array; + + for ( var i = 0, l = positions.length; i < l; i += 3 ) { + + renderList.pushVertex( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); + + } + + if ( attributes.index !== undefined ) { + + var indices = attributes.index.array; + + for ( var i = 0, l = indices.length; i < l; i += 2 ) { + + renderList.pushLine( indices[ i ], indices[ i + 1 ] ); + + } + + } else { + + var step = object.type === THREE.LinePieces ? 2 : 1; + + for ( var i = 0, l = ( positions.length / 3 ) - 1; i < l; i += step ) { + + renderList.pushLine( i, i + 1 ); + + } + + } + + } + + } else if ( geometry instanceof THREE.Geometry ) { + + _modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix ); + + var vertices = object.geometry.vertices; + + if ( vertices.length === 0 ) continue; + + v1 = getNextVertexInPool(); + v1.positionScreen.copy( vertices[ 0 ] ).applyMatrix4( _modelViewProjectionMatrix ); + + // Handle LineStrip and LinePieces + var step = object.type === THREE.LinePieces ? 2 : 1; + + for ( var v = 1, vl = vertices.length; v < vl; v ++ ) { + + v1 = getNextVertexInPool(); + v1.positionScreen.copy( vertices[ v ] ).applyMatrix4( _modelViewProjectionMatrix ); + + if ( ( v + 1 ) % step > 0 ) continue; + + v2 = _vertexPool[ _vertexCount - 2 ]; + + _clippedVertex1PositionScreen.copy( v1.positionScreen ); + _clippedVertex2PositionScreen.copy( v2.positionScreen ); + + if ( clipLine( _clippedVertex1PositionScreen, _clippedVertex2PositionScreen ) === true ) { + + // Perform the perspective divide + _clippedVertex1PositionScreen.multiplyScalar( 1 / _clippedVertex1PositionScreen.w ); + _clippedVertex2PositionScreen.multiplyScalar( 1 / _clippedVertex2PositionScreen.w ); + + _line = getNextLineInPool(); + + _line.id = object.id; + _line.v1.positionScreen.copy( _clippedVertex1PositionScreen ); + _line.v2.positionScreen.copy( _clippedVertex2PositionScreen ); + + _line.z = Math.max( _clippedVertex1PositionScreen.z, _clippedVertex2PositionScreen.z ); + + _line.material = object.material; + + if ( object.material.vertexColors === THREE.VertexColors ) { + + _line.vertexColors[ 0 ].copy( object.geometry.colors[ v ] ); + _line.vertexColors[ 1 ].copy( object.geometry.colors[ v - 1 ] ); + + } + + _renderData.elements.push( _line ); + + } + + } + + } + + } else if ( object instanceof THREE.Sprite ) { + + _vector4.set( _modelMatrix.elements[ 12 ], _modelMatrix.elements[ 13 ], _modelMatrix.elements[ 14 ], 1 ); + _vector4.applyMatrix4( _viewProjectionMatrix ); + + var invW = 1 / _vector4.w; + + _vector4.z *= invW; + + if ( _vector4.z >= - 1 && _vector4.z <= 1 ) { + + _sprite = getNextSpriteInPool(); + _sprite.id = object.id; + _sprite.x = _vector4.x * invW; + _sprite.y = _vector4.y * invW; + _sprite.z = _vector4.z; + _sprite.object = object; + + _sprite.rotation = object.rotation; + + _sprite.scale.x = object.scale.x * Math.abs( _sprite.x - ( _vector4.x + camera.projectionMatrix.elements[ 0 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 12 ] ) ); + _sprite.scale.y = object.scale.y * Math.abs( _sprite.y - ( _vector4.y + camera.projectionMatrix.elements[ 5 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 13 ] ) ); + + _sprite.material = object.material; + + _renderData.elements.push( _sprite ); + + } + + } + + } + + if ( sortElements === true ) _renderData.elements.sort( painterSort ); + + return _renderData; + + }; + + // Pools + + function getNextObjectInPool() { + + if ( _objectCount === _objectPoolLength ) { + + var object = new THREE.RenderableObject(); + _objectPool.push( object ); + _objectPoolLength ++; + _objectCount ++; + return object; + + } + + return _objectPool[ _objectCount ++ ]; + + } + + function getNextVertexInPool() { + + if ( _vertexCount === _vertexPoolLength ) { + + var vertex = new THREE.RenderableVertex(); + _vertexPool.push( vertex ); + _vertexPoolLength ++; + _vertexCount ++; + return vertex; + + } + + return _vertexPool[ _vertexCount ++ ]; + + } + + function getNextFaceInPool() { + + if ( _faceCount === _facePoolLength ) { + + var face = new THREE.RenderableFace(); + _facePool.push( face ); + _facePoolLength ++; + _faceCount ++; + return face; + + } + + return _facePool[ _faceCount ++ ]; + + + } + + function getNextLineInPool() { + + if ( _lineCount === _linePoolLength ) { + + var line = new THREE.RenderableLine(); + _linePool.push( line ); + _linePoolLength ++; + _lineCount ++ + return line; + + } + + return _linePool[ _lineCount ++ ]; + + } + + function getNextSpriteInPool() { + + if ( _spriteCount === _spritePoolLength ) { + + var sprite = new THREE.RenderableSprite(); + _spritePool.push( sprite ); + _spritePoolLength ++; + _spriteCount ++ + return sprite; + + } + + return _spritePool[ _spriteCount ++ ]; + + } + + // + + function painterSort( a, b ) { + + if ( a.z !== b.z ) { + + return b.z - a.z; + + } else if ( a.id !== b.id ) { + + return a.id - b.id; + + } else { + + return 0; + + } + + } + + function clipLine( s1, s2 ) { + + var alpha1 = 0, alpha2 = 1, + + // Calculate the boundary coordinate of each vertex for the near and far clip planes, + // Z = -1 and Z = +1, respectively. + bc1near = s1.z + s1.w, + bc2near = s2.z + s2.w, + bc1far = - s1.z + s1.w, + bc2far = - s2.z + s2.w; + + if ( bc1near >= 0 && bc2near >= 0 && bc1far >= 0 && bc2far >= 0 ) { + + // Both vertices lie entirely within all clip planes. + return true; + + } else if ( ( bc1near < 0 && bc2near < 0 ) || ( bc1far < 0 && bc2far < 0 ) ) { + + // Both vertices lie entirely outside one of the clip planes. + return false; + + } else { + + // The line segment spans at least one clip plane. + + if ( bc1near < 0 ) { + + // v1 lies outside the near plane, v2 inside + alpha1 = Math.max( alpha1, bc1near / ( bc1near - bc2near ) ); + + } else if ( bc2near < 0 ) { + + // v2 lies outside the near plane, v1 inside + alpha2 = Math.min( alpha2, bc1near / ( bc1near - bc2near ) ); + + } + + if ( bc1far < 0 ) { + + // v1 lies outside the far plane, v2 inside + alpha1 = Math.max( alpha1, bc1far / ( bc1far - bc2far ) ); + + } else if ( bc2far < 0 ) { + + // v2 lies outside the far plane, v2 inside + alpha2 = Math.min( alpha2, bc1far / ( bc1far - bc2far ) ); + + } + + if ( alpha2 < alpha1 ) { + + // The line segment spans two boundaries, but is outside both of them. + // (This can't happen when we're only clipping against just near/far but good + // to leave the check here for future usage if other clip planes are added.) + return false; + + } else { + + // Update the s1 and s2 vertices to match the clipped line segment. + s1.lerp( s2, alpha1 ); + s2.lerp( s1, 1 - alpha2 ); + + return true; + + } + + } + + } + +}; + +// File:src/core/Face3.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Face3 = function ( a, b, c, normal, color, materialIndex ) { + + this.a = a; + this.b = b; + this.c = c; + + this.normal = normal instanceof THREE.Vector3 ? normal : new THREE.Vector3(); + this.vertexNormals = normal instanceof Array ? normal : []; + + this.color = color instanceof THREE.Color ? color : new THREE.Color(); + this.vertexColors = color instanceof Array ? color : []; + + this.vertexTangents = []; + + this.materialIndex = materialIndex !== undefined ? materialIndex : 0; + +}; + +THREE.Face3.prototype = { + + constructor: THREE.Face3, + + clone: function () { + + var face = new THREE.Face3( this.a, this.b, this.c ); + + face.normal.copy( this.normal ); + face.color.copy( this.color ); + + face.materialIndex = this.materialIndex; + + for ( var i = 0, il = this.vertexNormals.length; i < il; i ++ ) { + + face.vertexNormals[ i ] = this.vertexNormals[ i ].clone(); + + } + + for ( var i = 0, il = this.vertexColors.length; i < il; i ++ ) { + + face.vertexColors[ i ] = this.vertexColors[ i ].clone(); + + } + + for ( var i = 0, il = this.vertexTangents.length; i < il; i ++ ) { + + face.vertexTangents[ i ] = this.vertexTangents[ i ].clone(); + + } + + return face; + + } + +}; + +// File:src/core/Face4.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Face4 = function ( a, b, c, d, normal, color, materialIndex ) { + + console.warn( 'THREE.Face4 has been removed. A THREE.Face3 will be created instead.' ) + return new THREE.Face3( a, b, c, normal, color, materialIndex ); + +}; + +// File:src/core/BufferAttribute.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.BufferAttribute = function ( array, itemSize ) { + + this.array = array; + this.itemSize = itemSize; + +}; + +THREE.BufferAttribute.prototype = { + + constructor: THREE.BufferAttribute, + + get length () { + + return this.array.length; + + }, + + set: function ( value ) { + + this.array.set( value ); + + return this; + + }, + + setX: function ( index, x ) { + + this.array[ index * this.itemSize ] = x; + + return this; + + }, + + setY: function ( index, y ) { + + this.array[ index * this.itemSize + 1 ] = y; + + return this; + + }, + + setZ: function ( index, z ) { + + this.array[ index * this.itemSize + 2 ] = z; + + return this; + + }, + + setXY: function ( index, x, y ) { + + index *= this.itemSize; + + this.array[ index ] = x; + this.array[ index + 1 ] = y; + + return this; + + }, + + setXYZ: function ( index, x, y, z ) { + + index *= this.itemSize; + + this.array[ index ] = x; + this.array[ index + 1 ] = y; + this.array[ index + 2 ] = z; + + return this; + + }, + + setXYZW: function ( index, x, y, z, w ) { + + index *= this.itemSize; + + this.array[ index ] = x; + this.array[ index + 1 ] = y; + this.array[ index + 2 ] = z; + this.array[ index + 3 ] = w; + + return this; + + } + +}; + +// + +THREE.Int8Attribute = function ( data, itemSize ) { + + console.warn( 'THREE.Int8Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); + +}; + +THREE.Uint8Attribute = function ( data, itemSize ) { + + console.warn( 'THREE.Uint8Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); + +}; + +THREE.Uint8ClampedAttribute = function ( data, itemSize ) { + + console.warn( 'THREE.Uint8ClampedAttribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); + + +}; + +THREE.Int16Attribute = function ( data, itemSize ) { + + console.warn( 'THREE.Int16Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); + +}; + +THREE.Uint16Attribute = function ( data, itemSize ) { + + console.warn( 'THREE.Uint16Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); + +}; + +THREE.Int32Attribute = function ( data, itemSize ) { + + console.warn( 'THREE.Int32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); + +}; + +THREE.Uint32Attribute = function ( data, itemSize ) { + + console.warn( 'THREE.Uint32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); + +}; + +THREE.Float32Attribute = function ( data, itemSize ) { + + console.warn( 'THREE.Float32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); + +}; + +THREE.Float64Attribute = function ( data, itemSize ) { + + console.warn( 'THREE.Float64Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); + +}; + +// File:src/core/BufferGeometry.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.BufferGeometry = function () { + + this.id = THREE.GeometryIdCount ++; + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + + this.attributes = {}; + this.drawcalls = []; + this.offsets = this.drawcalls; // backwards compatibility + + this.boundingBox = null; + this.boundingSphere = null; + +}; + +THREE.BufferGeometry.prototype = { + + constructor: THREE.BufferGeometry, + + addAttribute: function ( name, attribute ) { + + if ( attribute instanceof THREE.BufferAttribute === false ) { + + console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' ); + + this.attributes[ name ] = { array: arguments[ 1 ], itemSize: arguments[ 2 ] }; + + return; + + } + + this.attributes[ name ] = attribute; + + }, + + getAttribute: function ( name ) { + + return this.attributes[ name ]; + + }, + + addDrawCall: function ( start, count, indexOffset ) { + + this.drawcalls.push( { + + start: start, + count: count, + index: indexOffset !== undefined ? indexOffset : 0 + + } ); + + }, + + applyMatrix: function ( matrix ) { + + var position = this.attributes.position; + + if ( position !== undefined ) { + + matrix.applyToVector3Array( position.array ); + position.needsUpdate = true; + + } + + var normal = this.attributes.normal; + + if ( normal !== undefined ) { + + var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); + + normalMatrix.applyToVector3Array( normal.array ); + normal.needsUpdate = true; + + } + + }, + + fromGeometry: function ( geometry, settings ) { + + settings = settings || { 'vertexColors': THREE.NoColors }; + + var vertices = geometry.vertices; + var faces = geometry.faces; + var faceVertexUvs = geometry.faceVertexUvs; + var vertexColors = settings.vertexColors; + var hasFaceVertexUv = faceVertexUvs[ 0 ].length > 0; + var hasFaceVertexNormals = faces[ 0 ].vertexNormals.length == 3; + + var positions = new Float32Array( faces.length * 3 * 3 ); + this.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) ); + + var normals = new Float32Array( faces.length * 3 * 3 ); + this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); + + if ( vertexColors !== THREE.NoColors ) { + + var colors = new Float32Array( faces.length * 3 * 3 ); + this.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) ); + + } + + if ( hasFaceVertexUv === true ) { + + var uvs = new Float32Array( faces.length * 3 * 2 ); + this.addAttribute( 'uvs', new THREE.BufferAttribute( uvs, 2 ) ); + + } + + for ( var i = 0, i2 = 0, i3 = 0; i < faces.length; i ++, i2 += 6, i3 += 9 ) { + + var face = faces[ i ]; + + var a = vertices[ face.a ]; + var b = vertices[ face.b ]; + var c = vertices[ face.c ]; + + positions[ i3 ] = a.x; + positions[ i3 + 1 ] = a.y; + positions[ i3 + 2 ] = a.z; + + positions[ i3 + 3 ] = b.x; + positions[ i3 + 4 ] = b.y; + positions[ i3 + 5 ] = b.z; + + positions[ i3 + 6 ] = c.x; + positions[ i3 + 7 ] = c.y; + positions[ i3 + 8 ] = c.z; + + if ( hasFaceVertexNormals === true ) { + + var na = face.vertexNormals[ 0 ]; + var nb = face.vertexNormals[ 1 ]; + var nc = face.vertexNormals[ 2 ]; + + normals[ i3 ] = na.x; + normals[ i3 + 1 ] = na.y; + normals[ i3 + 2 ] = na.z; + + normals[ i3 + 3 ] = nb.x; + normals[ i3 + 4 ] = nb.y; + normals[ i3 + 5 ] = nb.z; + + normals[ i3 + 6 ] = nc.x; + normals[ i3 + 7 ] = nc.y; + normals[ i3 + 8 ] = nc.z; + + } else { + + var n = face.normal; + + normals[ i3 ] = n.x; + normals[ i3 + 1 ] = n.y; + normals[ i3 + 2 ] = n.z; + + normals[ i3 + 3 ] = n.x; + normals[ i3 + 4 ] = n.y; + normals[ i3 + 5 ] = n.z; + + normals[ i3 + 6 ] = n.x; + normals[ i3 + 7 ] = n.y; + normals[ i3 + 8 ] = n.z; + + } + + if ( vertexColors === THREE.FaceColors ) { + + var fc = face.color; + + colors[ i3 ] = fc.r; + colors[ i3 + 1 ] = fc.g; + colors[ i3 + 2 ] = fc.b; + + colors[ i3 + 3 ] = fc.r; + colors[ i3 + 4 ] = fc.g; + colors[ i3 + 5 ] = fc.b; + + colors[ i3 + 6 ] = fc.r; + colors[ i3 + 7 ] = fc.g; + colors[ i3 + 8 ] = fc.b; + + } else if ( vertexColors === THREE.VertexColors ) { + + var vca = face.vertexColors[ 0 ]; + var vcb = face.vertexColors[ 1 ]; + var vcc = face.vertexColors[ 2 ]; + + colors[ i3 ] = vca.r; + colors[ i3 + 1 ] = vca.g; + colors[ i3 + 2 ] = vca.b; + + colors[ i3 + 3 ] = vcb.r; + colors[ i3 + 4 ] = vcb.g; + colors[ i3 + 5 ] = vcb.b; + + colors[ i3 + 6 ] = vcc.r; + colors[ i3 + 7 ] = vcc.g; + colors[ i3 + 8 ] = vcc.b; + + } + + if ( hasFaceVertexUv === true ) { + + var uva = faceVertexUvs[ 0 ][ i ][ 0 ]; + var uvb = faceVertexUvs[ 0 ][ i ][ 1 ]; + var uvc = faceVertexUvs[ 0 ][ i ][ 2 ]; + + uvs[ i2 ] = uva.x; + uvs[ i2 + 1 ] = uva.y; + + uvs[ i2 + 2 ] = uvb.x; + uvs[ i2 + 3 ] = uvb.y; + + uvs[ i2 + 4 ] = uvc.x; + uvs[ i2 + 5 ] = uvc.y; + + } + + } + + this.computeBoundingSphere() + + return this; + + }, + + computeBoundingBox: function () { + + if ( this.boundingBox === null ) { + + this.boundingBox = new THREE.Box3(); + + } + + var positions = this.attributes[ 'position' ].array; + + if ( positions ) { + + var bb = this.boundingBox; + + if ( positions.length >= 3 ) { + bb.min.x = bb.max.x = positions[ 0 ]; + bb.min.y = bb.max.y = positions[ 1 ]; + bb.min.z = bb.max.z = positions[ 2 ]; + } + + for ( var i = 3, il = positions.length; i < il; i += 3 ) { + + var x = positions[ i ]; + var y = positions[ i + 1 ]; + var z = positions[ i + 2 ]; + + // bounding box + + if ( x < bb.min.x ) { + + bb.min.x = x; + + } else if ( x > bb.max.x ) { + + bb.max.x = x; + + } + + if ( y < bb.min.y ) { + + bb.min.y = y; + + } else if ( y > bb.max.y ) { + + bb.max.y = y; + + } + + if ( z < bb.min.z ) { + + bb.min.z = z; + + } else if ( z > bb.max.z ) { + + bb.max.z = z; + + } + + } + + } + + if ( positions === undefined || positions.length === 0 ) { + + this.boundingBox.min.set( 0, 0, 0 ); + this.boundingBox.max.set( 0, 0, 0 ); + + } + + if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { + + console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.' ); + + } + + }, + + computeBoundingSphere: function () { + + var box = new THREE.Box3(); + var vector = new THREE.Vector3(); + + return function () { + + if ( this.boundingSphere === null ) { + + this.boundingSphere = new THREE.Sphere(); + + } + + var positions = this.attributes[ 'position' ].array; + + if ( positions ) { + + box.makeEmpty(); + + var center = this.boundingSphere.center; + + for ( var i = 0, il = positions.length; i < il; i += 3 ) { + + vector.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); + box.expandByPoint( vector ); + + } + + box.center( center ); + + // hoping to find a boundingSphere with a radius smaller than the + // boundingSphere of the boundingBox: sqrt(3) smaller in the best case + + var maxRadiusSq = 0; + + for ( var i = 0, il = positions.length; i < il; i += 3 ) { + + vector.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); + maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) ); + + } + + this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); + + if ( isNaN( this.boundingSphere.radius ) ) { + + console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.' ); + + } + + } + + } + + }(), + + computeFaceNormals: function () { + + // backwards compatibility + + }, + + computeVertexNormals: function () { + + if ( this.attributes[ 'position' ] ) { + + var i, il; + var j, jl; + + var nVertexElements = this.attributes[ 'position' ].array.length; + + if ( this.attributes[ 'normal' ] === undefined ) { + + this.attributes[ 'normal' ] = { + + itemSize: 3, + array: new Float32Array( nVertexElements ) + + }; + + } else { + + // reset existing normals to zero + + for ( i = 0, il = this.attributes[ 'normal' ].array.length; i < il; i ++ ) { + + this.attributes[ 'normal' ].array[ i ] = 0; + + } + + } + + var positions = this.attributes[ 'position' ].array; + var normals = this.attributes[ 'normal' ].array; + + var vA, vB, vC, x, y, z, + + pA = new THREE.Vector3(), + pB = new THREE.Vector3(), + pC = new THREE.Vector3(), + + cb = new THREE.Vector3(), + ab = new THREE.Vector3(); + + // indexed elements + + if ( this.attributes[ 'index' ] ) { + + var indices = this.attributes[ 'index' ].array; + + var offsets = ( this.offsets.length > 0 ? this.offsets : [ { start: 0, count: indices.length, index: 0 } ] ); + + for ( j = 0, jl = offsets.length; j < jl; ++ j ) { + + var start = offsets[ j ].start; + var count = offsets[ j ].count; + var index = offsets[ j ].index; + + for ( i = start, il = start + count; i < il; i += 3 ) { + + vA = index + indices[ i ]; + vB = index + indices[ i + 1 ]; + vC = index + indices[ i + 2 ]; + + x = positions[ vA * 3 ]; + y = positions[ vA * 3 + 1 ]; + z = positions[ vA * 3 + 2 ]; + pA.set( x, y, z ); + + x = positions[ vB * 3 ]; + y = positions[ vB * 3 + 1 ]; + z = positions[ vB * 3 + 2 ]; + pB.set( x, y, z ); + + x = positions[ vC * 3 ]; + y = positions[ vC * 3 + 1 ]; + z = positions[ vC * 3 + 2 ]; + pC.set( x, y, z ); + + cb.subVectors( pC, pB ); + ab.subVectors( pA, pB ); + cb.cross( ab ); + + normals[ vA * 3 ] += cb.x; + normals[ vA * 3 + 1 ] += cb.y; + normals[ vA * 3 + 2 ] += cb.z; + + normals[ vB * 3 ] += cb.x; + normals[ vB * 3 + 1 ] += cb.y; + normals[ vB * 3 + 2 ] += cb.z; + + normals[ vC * 3 ] += cb.x; + normals[ vC * 3 + 1 ] += cb.y; + normals[ vC * 3 + 2 ] += cb.z; + + } + + } + + // non-indexed elements (unconnected triangle soup) + + } else { + + for ( i = 0, il = positions.length; i < il; i += 9 ) { + + x = positions[ i ]; + y = positions[ i + 1 ]; + z = positions[ i + 2 ]; + pA.set( x, y, z ); + + x = positions[ i + 3 ]; + y = positions[ i + 4 ]; + z = positions[ i + 5 ]; + pB.set( x, y, z ); + + x = positions[ i + 6 ]; + y = positions[ i + 7 ]; + z = positions[ i + 8 ]; + pC.set( x, y, z ); + + cb.subVectors( pC, pB ); + ab.subVectors( pA, pB ); + cb.cross( ab ); + + normals[ i ] = cb.x; + normals[ i + 1 ] = cb.y; + normals[ i + 2 ] = cb.z; + + normals[ i + 3 ] = cb.x; + normals[ i + 4 ] = cb.y; + normals[ i + 5 ] = cb.z; + + normals[ i + 6 ] = cb.x; + normals[ i + 7 ] = cb.y; + normals[ i + 8 ] = cb.z; + + } + + } + + this.normalizeNormals(); + + this.normalsNeedUpdate = true; + + } + + }, + + computeTangents: function () { + + // based on http://www.terathon.com/code/tangent.html + // (per vertex tangents) + + if ( this.attributes[ 'index' ] === undefined || + this.attributes[ 'position' ] === undefined || + this.attributes[ 'normal' ] === undefined || + this.attributes[ 'uv' ] === undefined ) { + + console.warn( 'Missing required attributes (index, position, normal or uv) in BufferGeometry.computeTangents()' ); + return; + + } + + var indices = this.attributes[ 'index' ].array; + var positions = this.attributes[ 'position' ].array; + var normals = this.attributes[ 'normal' ].array; + var uvs = this.attributes[ 'uv' ].array; + + var nVertices = positions.length / 3; + + if ( this.attributes[ 'tangent' ] === undefined ) { + + var nTangentElements = 4 * nVertices; + + this.attributes[ 'tangent' ] = { + + itemSize: 4, + array: new Float32Array( nTangentElements ) + + }; + + } + + var tangents = this.attributes[ 'tangent' ].array; + + var tan1 = [], tan2 = []; + + for ( var k = 0; k < nVertices; k ++ ) { + + tan1[ k ] = new THREE.Vector3(); + tan2[ k ] = new THREE.Vector3(); + + } + + var xA, yA, zA, + xB, yB, zB, + xC, yC, zC, + + uA, vA, + uB, vB, + uC, vC, + + x1, x2, y1, y2, z1, z2, + s1, s2, t1, t2, r; + + var sdir = new THREE.Vector3(), tdir = new THREE.Vector3(); + + function handleTriangle( a, b, c ) { + + xA = positions[ a * 3 ]; + yA = positions[ a * 3 + 1 ]; + zA = positions[ a * 3 + 2 ]; + + xB = positions[ b * 3 ]; + yB = positions[ b * 3 + 1 ]; + zB = positions[ b * 3 + 2 ]; + + xC = positions[ c * 3 ]; + yC = positions[ c * 3 + 1 ]; + zC = positions[ c * 3 + 2 ]; + + uA = uvs[ a * 2 ]; + vA = uvs[ a * 2 + 1 ]; + + uB = uvs[ b * 2 ]; + vB = uvs[ b * 2 + 1 ]; + + uC = uvs[ c * 2 ]; + vC = uvs[ c * 2 + 1 ]; + + x1 = xB - xA; + x2 = xC - xA; + + y1 = yB - yA; + y2 = yC - yA; + + z1 = zB - zA; + z2 = zC - zA; + + s1 = uB - uA; + s2 = uC - uA; + + t1 = vB - vA; + t2 = vC - vA; + + r = 1.0 / ( s1 * t2 - s2 * t1 ); + + sdir.set( + ( t2 * x1 - t1 * x2 ) * r, + ( t2 * y1 - t1 * y2 ) * r, + ( t2 * z1 - t1 * z2 ) * r + ); + + tdir.set( + ( s1 * x2 - s2 * x1 ) * r, + ( s1 * y2 - s2 * y1 ) * r, + ( s1 * z2 - s2 * z1 ) * r + ); + + tan1[ a ].add( sdir ); + tan1[ b ].add( sdir ); + tan1[ c ].add( sdir ); + + tan2[ a ].add( tdir ); + tan2[ b ].add( tdir ); + tan2[ c ].add( tdir ); + + } + + var i, il; + var j, jl; + var iA, iB, iC; + + var offsets = this.offsets; + + for ( j = 0, jl = offsets.length; j < jl; ++ j ) { + + var start = offsets[ j ].start; + var count = offsets[ j ].count; + var index = offsets[ j ].index; + + for ( i = start, il = start + count; i < il; i += 3 ) { + + iA = index + indices[ i ]; + iB = index + indices[ i + 1 ]; + iC = index + indices[ i + 2 ]; + + handleTriangle( iA, iB, iC ); + + } + + } + + var tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3(); + var n = new THREE.Vector3(), n2 = new THREE.Vector3(); + var w, t, test; + + function handleVertex( v ) { + + n.x = normals[ v * 3 ]; + n.y = normals[ v * 3 + 1 ]; + n.z = normals[ v * 3 + 2 ]; + + n2.copy( n ); + + t = tan1[ v ]; + + // Gram-Schmidt orthogonalize + + tmp.copy( t ); + tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize(); + + // Calculate handedness + + tmp2.crossVectors( n2, t ); + test = tmp2.dot( tan2[ v ] ); + w = ( test < 0.0 ) ? - 1.0 : 1.0; + + tangents[ v * 4 ] = tmp.x; + tangents[ v * 4 + 1 ] = tmp.y; + tangents[ v * 4 + 2 ] = tmp.z; + tangents[ v * 4 + 3 ] = w; + + } + + for ( j = 0, jl = offsets.length; j < jl; ++ j ) { + + var start = offsets[ j ].start; + var count = offsets[ j ].count; + var index = offsets[ j ].index; + + for ( i = start, il = start + count; i < il; i += 3 ) { + + iA = index + indices[ i ]; + iB = index + indices[ i + 1 ]; + iC = index + indices[ i + 2 ]; + + handleVertex( iA ); + handleVertex( iB ); + handleVertex( iC ); + + } + + } + + }, + + /* + computeOffsets + Compute the draw offset for large models by chunking the index buffer into chunks of 65k addressable vertices. + This method will effectively rewrite the index buffer and remap all attributes to match the new indices. + WARNING: This method will also expand the vertex count to prevent sprawled triangles across draw offsets. + indexBufferSize - Defaults to 65535, but allows for larger or smaller chunks. + */ + computeOffsets: function ( indexBufferSize ) { + + var size = indexBufferSize; + if ( indexBufferSize === undefined ) + size = 65535; //WebGL limits type of index buffer values to 16-bit. + + var s = Date.now(); + + var indices = this.attributes[ 'index' ].array; + var vertices = this.attributes[ 'position' ].array; + + var verticesCount = ( vertices.length / 3 ); + var facesCount = ( indices.length / 3 ); + + /* + console.log("Computing buffers in offsets of "+size+" -> indices:"+indices.length+" vertices:"+vertices.length); + console.log("Faces to process: "+(indices.length/3)); + console.log("Reordering "+verticesCount+" vertices."); + */ + + var sortedIndices = new Uint16Array( indices.length ); //16-bit buffers + var indexPtr = 0; + var vertexPtr = 0; + + var offsets = [ { start:0, count:0, index:0 } ]; + var offset = offsets[ 0 ]; + + var duplicatedVertices = 0; + var newVerticeMaps = 0; + var faceVertices = new Int32Array( 6 ); + var vertexMap = new Int32Array( vertices.length ); + var revVertexMap = new Int32Array( vertices.length ); + for ( var j = 0; j < vertices.length; j ++ ) { vertexMap[ j ] = - 1; revVertexMap[ j ] = - 1; } + + /* + Traverse every face and reorder vertices in the proper offsets of 65k. + We can have more than 65k entries in the index buffer per offset, but only reference 65k values. + */ + for ( var findex = 0; findex < facesCount; findex ++ ) { + newVerticeMaps = 0; + + for ( var vo = 0; vo < 3; vo ++ ) { + var vid = indices[ findex * 3 + vo ]; + if ( vertexMap[ vid ] == - 1 ) { + //Unmapped vertice + faceVertices[ vo * 2 ] = vid; + faceVertices[ vo * 2 + 1 ] = - 1; + newVerticeMaps ++; + } else if ( vertexMap[ vid ] < offset.index ) { + //Reused vertices from previous block (duplicate) + faceVertices[ vo * 2 ] = vid; + faceVertices[ vo * 2 + 1 ] = - 1; + duplicatedVertices ++; + } else { + //Reused vertice in the current block + faceVertices[ vo * 2 ] = vid; + faceVertices[ vo * 2 + 1 ] = vertexMap[ vid ]; + } + } + + var faceMax = vertexPtr + newVerticeMaps; + if ( faceMax > ( offset.index + size ) ) { + var new_offset = { start:indexPtr, count:0, index:vertexPtr }; + offsets.push( new_offset ); + offset = new_offset; + + //Re-evaluate reused vertices in light of new offset. + for ( var v = 0; v < 6; v += 2 ) { + var new_vid = faceVertices[ v + 1 ]; + if ( new_vid > - 1 && new_vid < offset.index ) + faceVertices[ v + 1 ] = - 1; + } + } + + //Reindex the face. + for ( var v = 0; v < 6; v += 2 ) { + var vid = faceVertices[ v ]; + var new_vid = faceVertices[ v + 1 ]; + + if ( new_vid === - 1 ) + new_vid = vertexPtr ++; + + vertexMap[ vid ] = new_vid; + revVertexMap[ new_vid ] = vid; + sortedIndices[ indexPtr ++ ] = new_vid - offset.index; //XXX overflows at 16bit + offset.count ++; + } + } + + /* Move all attribute values to map to the new computed indices , also expand the vertice stack to match our new vertexPtr. */ + this.reorderBuffers( sortedIndices, revVertexMap, vertexPtr ); + this.offsets = offsets; + + /* + var orderTime = Date.now(); + console.log("Reorder time: "+(orderTime-s)+"ms"); + console.log("Duplicated "+duplicatedVertices+" vertices."); + console.log("Compute Buffers time: "+(Date.now()-s)+"ms"); + console.log("Draw offsets: "+offsets.length); + */ + + return offsets; + }, + + merge: function () { + + console.log( 'BufferGeometry.merge(): TODO' ); + + }, + + normalizeNormals: function () { + + var normals = this.attributes[ 'normal' ].array; + + var x, y, z, n; + + for ( var i = 0, il = normals.length; i < il; i += 3 ) { + + x = normals[ i ]; + y = normals[ i + 1 ]; + z = normals[ i + 2 ]; + + n = 1.0 / Math.sqrt( x * x + y * y + z * z ); + + normals[ i ] *= n; + normals[ i + 1 ] *= n; + normals[ i + 2 ] *= n; + + } + + }, + + /* + reoderBuffers: + Reorder attributes based on a new indexBuffer and indexMap. + indexBuffer - Uint16Array of the new ordered indices. + indexMap - Int32Array where the position is the new vertex ID and the value the old vertex ID for each vertex. + vertexCount - Amount of total vertices considered in this reordering (in case you want to grow the vertice stack). + */ + reorderBuffers: function ( indexBuffer, indexMap, vertexCount ) { + + /* Create a copy of all attributes for reordering. */ + var sortedAttributes = {}; + var types = [ Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array ]; + for ( var attr in this.attributes ) { + if ( attr == 'index' ) + continue; + var sourceArray = this.attributes[ attr ].array; + for ( var i = 0, il = types.length; i < il; i ++ ) { + var type = types[ i ]; + if ( sourceArray instanceof type ) { + sortedAttributes[ attr ] = new type( this.attributes[ attr ].itemSize * vertexCount ); + break; + } + } + } + + /* Move attribute positions based on the new index map */ + for ( var new_vid = 0; new_vid < vertexCount; new_vid ++ ) { + var vid = indexMap[ new_vid ]; + for ( var attr in this.attributes ) { + if ( attr == 'index' ) + continue; + var attrArray = this.attributes[ attr ].array; + var attrSize = this.attributes[ attr ].itemSize; + var sortedAttr = sortedAttributes[ attr ]; + for ( var k = 0; k < attrSize; k ++ ) + sortedAttr[ new_vid * attrSize + k ] = attrArray[ vid * attrSize + k ]; + } + } + + /* Carry the new sorted buffers locally */ + this.attributes[ 'index' ].array = indexBuffer; + for ( var attr in this.attributes ) { + if ( attr == 'index' ) + continue; + this.attributes[ attr ].array = sortedAttributes[ attr ]; + this.attributes[ attr ].numItems = this.attributes[ attr ].itemSize * vertexCount; + } + }, + + clone: function () { + + var geometry = new THREE.BufferGeometry(); + + var types = [ Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array ]; + + for ( var attr in this.attributes ) { + + var sourceAttr = this.attributes[ attr ]; + var sourceArray = sourceAttr.array; + + var attribute = { + + itemSize: sourceAttr.itemSize, + array: null + + }; + + for ( var i = 0, il = types.length; i < il; i ++ ) { + + var type = types[ i ]; + + if ( sourceArray instanceof type ) { + + attribute.array = new type( sourceArray ); + break; + + } + + } + + geometry.attributes[ attr ] = attribute; + + } + + for ( var i = 0, il = this.offsets.length; i < il; i ++ ) { + + var offset = this.offsets[ i ]; + + geometry.offsets.push( { + + start: offset.start, + index: offset.index, + count: offset.count + + } ); + + } + + return geometry; + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.BufferGeometry.prototype ); + +// File:src/core/Geometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author kile / http://kile.stravaganza.org/ + * @author alteredq / http://alteredqualia.com/ + * @author mikael emtinger / http://gomo.se/ + * @author zz85 / http://www.lab4games.net/zz85/blog + * @author bhouston / http://exocortex.com + */ + +THREE.Geometry = function () { + + this.id = THREE.GeometryIdCount ++; + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + + this.vertices = []; + this.colors = []; // one-to-one vertex colors, used in Points and Line + + this.faces = []; + + this.faceVertexUvs = [ [] ]; + + this.morphTargets = []; + this.morphColors = []; + this.morphNormals = []; + + this.skinWeights = []; + this.skinIndices = []; + + this.lineDistances = []; + + this.boundingBox = null; + this.boundingSphere = null; + + this.hasTangents = false; + + this.dynamic = true; // the intermediate typed arrays will be deleted when set to false + + // update flags + + this.verticesNeedUpdate = false; + this.elementsNeedUpdate = false; + this.uvsNeedUpdate = false; + this.normalsNeedUpdate = false; + this.tangentsNeedUpdate = false; + this.colorsNeedUpdate = false; + this.lineDistancesNeedUpdate = false; + + this.buffersNeedUpdate = false; + this.groupsNeedUpdate = false; + +}; + +THREE.Geometry.prototype = { + + constructor: THREE.Geometry, + + applyMatrix: function ( matrix ) { + + var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); + + for ( var i = 0, il = this.vertices.length; i < il; i ++ ) { + + var vertex = this.vertices[ i ]; + vertex.applyMatrix4( matrix ); + + } + + for ( var i = 0, il = this.faces.length; i < il; i ++ ) { + + var face = this.faces[ i ]; + face.normal.applyMatrix3( normalMatrix ).normalize(); + + for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { + + face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize(); + + } + + } + + if ( this.boundingBox instanceof THREE.Box3 ) { + + this.computeBoundingBox(); + + } + + if ( this.boundingSphere instanceof THREE.Sphere ) { + + this.computeBoundingSphere(); + + } + + }, + + center: function () { + + this.computeBoundingBox(); + + var offset = new THREE.Vector3(); + + offset.addVectors( this.boundingBox.min, this.boundingBox.max ); + offset.multiplyScalar( - 0.5 ); + + this.applyMatrix( new THREE.Matrix4().makeTranslation( offset.x, offset.y, offset.z ) ); + this.computeBoundingBox(); + + return offset; + + }, + + computeFaceNormals: function () { + + var cb = new THREE.Vector3(), ab = new THREE.Vector3(); + + for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) { + + var face = this.faces[ f ]; + + var vA = this.vertices[ face.a ]; + var vB = this.vertices[ face.b ]; + var vC = this.vertices[ face.c ]; + + cb.subVectors( vC, vB ); + ab.subVectors( vA, vB ); + cb.cross( ab ); + + cb.normalize(); + + face.normal.copy( cb ); + + } + + }, + + computeVertexNormals: function ( areaWeighted ) { + + var v, vl, f, fl, face, vertices; + + vertices = new Array( this.vertices.length ); + + for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + + vertices[ v ] = new THREE.Vector3(); + + } + + if ( areaWeighted ) { + + // vertex normals weighted by triangle areas + // http://www.iquilezles.org/www/articles/normals/normals.htm + + var vA, vB, vC, vD; + var cb = new THREE.Vector3(), ab = new THREE.Vector3(), + db = new THREE.Vector3(), dc = new THREE.Vector3(), bc = new THREE.Vector3(); + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + vA = this.vertices[ face.a ]; + vB = this.vertices[ face.b ]; + vC = this.vertices[ face.c ]; + + cb.subVectors( vC, vB ); + ab.subVectors( vA, vB ); + cb.cross( ab ); + + vertices[ face.a ].add( cb ); + vertices[ face.b ].add( cb ); + vertices[ face.c ].add( cb ); + + } + + } else { + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + vertices[ face.a ].add( face.normal ); + vertices[ face.b ].add( face.normal ); + vertices[ face.c ].add( face.normal ); + + } + + } + + for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + + vertices[ v ].normalize(); + + } + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + face.vertexNormals[ 0 ] = vertices[ face.a ].clone(); + face.vertexNormals[ 1 ] = vertices[ face.b ].clone(); + face.vertexNormals[ 2 ] = vertices[ face.c ].clone(); + + } + + }, + + computeMorphNormals: function () { + + var i, il, f, fl, face; + + // save original normals + // - create temp variables on first access + // otherwise just copy (for faster repeated calls) + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + if ( ! face.__originalFaceNormal ) { + + face.__originalFaceNormal = face.normal.clone(); + + } else { + + face.__originalFaceNormal.copy( face.normal ); + + } + + if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = []; + + for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) { + + if ( ! face.__originalVertexNormals[ i ] ) { + + face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone(); + + } else { + + face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] ); + + } + + } + + } + + // use temp geometry to compute face and vertex normals for each morph + + var tmpGeo = new THREE.Geometry(); + tmpGeo.faces = this.faces; + + for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) { + + // create on first access + + if ( ! this.morphNormals[ i ] ) { + + this.morphNormals[ i ] = {}; + this.morphNormals[ i ].faceNormals = []; + this.morphNormals[ i ].vertexNormals = []; + + var dstNormalsFace = this.morphNormals[ i ].faceNormals; + var dstNormalsVertex = this.morphNormals[ i ].vertexNormals; + + var faceNormal, vertexNormals; + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + faceNormal = new THREE.Vector3(); + vertexNormals = { a: new THREE.Vector3(), b: new THREE.Vector3(), c: new THREE.Vector3() }; + + dstNormalsFace.push( faceNormal ); + dstNormalsVertex.push( vertexNormals ); + + } + + } + + var morphNormals = this.morphNormals[ i ]; + + // set vertices to morph target + + tmpGeo.vertices = this.morphTargets[ i ].vertices; + + // compute morph normals + + tmpGeo.computeFaceNormals(); + tmpGeo.computeVertexNormals(); + + // store morph normals + + var faceNormal, vertexNormals; + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + faceNormal = morphNormals.faceNormals[ f ]; + vertexNormals = morphNormals.vertexNormals[ f ]; + + faceNormal.copy( face.normal ); + + vertexNormals.a.copy( face.vertexNormals[ 0 ] ); + vertexNormals.b.copy( face.vertexNormals[ 1 ] ); + vertexNormals.c.copy( face.vertexNormals[ 2 ] ); + + } + + } + + // restore original normals + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + face.normal = face.__originalFaceNormal; + face.vertexNormals = face.__originalVertexNormals; + + } + + }, + + computeTangents: function () { + + // based on http://www.terathon.com/code/tangent.html + // tangents go to vertices + + var f, fl, v, vl, i, il, vertexIndex, + face, uv, vA, vB, vC, uvA, uvB, uvC, + x1, x2, y1, y2, z1, z2, + s1, s2, t1, t2, r, t, test, + tan1 = [], tan2 = [], + sdir = new THREE.Vector3(), tdir = new THREE.Vector3(), + tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3(), + n = new THREE.Vector3(), w; + + for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + + tan1[ v ] = new THREE.Vector3(); + tan2[ v ] = new THREE.Vector3(); + + } + + function handleTriangle( context, a, b, c, ua, ub, uc ) { + + vA = context.vertices[ a ]; + vB = context.vertices[ b ]; + vC = context.vertices[ c ]; + + uvA = uv[ ua ]; + uvB = uv[ ub ]; + uvC = uv[ uc ]; + + x1 = vB.x - vA.x; + x2 = vC.x - vA.x; + y1 = vB.y - vA.y; + y2 = vC.y - vA.y; + z1 = vB.z - vA.z; + z2 = vC.z - vA.z; + + s1 = uvB.x - uvA.x; + s2 = uvC.x - uvA.x; + t1 = uvB.y - uvA.y; + t2 = uvC.y - uvA.y; + + r = 1.0 / ( s1 * t2 - s2 * t1 ); + sdir.set( ( t2 * x1 - t1 * x2 ) * r, + ( t2 * y1 - t1 * y2 ) * r, + ( t2 * z1 - t1 * z2 ) * r ); + tdir.set( ( s1 * x2 - s2 * x1 ) * r, + ( s1 * y2 - s2 * y1 ) * r, + ( s1 * z2 - s2 * z1 ) * r ); + + tan1[ a ].add( sdir ); + tan1[ b ].add( sdir ); + tan1[ c ].add( sdir ); + + tan2[ a ].add( tdir ); + tan2[ b ].add( tdir ); + tan2[ c ].add( tdir ); + + } + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + uv = this.faceVertexUvs[ 0 ][ f ]; // use UV layer 0 for tangents + + handleTriangle( this, face.a, face.b, face.c, 0, 1, 2 ); + + } + + var faceIndex = [ 'a', 'b', 'c', 'd' ]; + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + for ( i = 0; i < Math.min( face.vertexNormals.length, 3 ); i ++ ) { + + n.copy( face.vertexNormals[ i ] ); + + vertexIndex = face[ faceIndex[ i ] ]; + + t = tan1[ vertexIndex ]; + + // Gram-Schmidt orthogonalize + + tmp.copy( t ); + tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize(); + + // Calculate handedness + + tmp2.crossVectors( face.vertexNormals[ i ], t ); + test = tmp2.dot( tan2[ vertexIndex ] ); + w = ( test < 0.0 ) ? - 1.0 : 1.0; + + face.vertexTangents[ i ] = new THREE.Vector4( tmp.x, tmp.y, tmp.z, w ); + + } + + } + + this.hasTangents = true; + + }, + + computeLineDistances: function () { + + var d = 0; + var vertices = this.vertices; + + for ( var i = 0, il = vertices.length; i < il; i ++ ) { + + if ( i > 0 ) { + + d += vertices[ i ].distanceTo( vertices[ i - 1 ] ); + + } + + this.lineDistances[ i ] = d; + + } + + }, + + computeBoundingBox: function () { + + if ( this.boundingBox === null ) { + + this.boundingBox = new THREE.Box3(); + + } + + this.boundingBox.setFromPoints( this.vertices ); + + }, + + computeBoundingSphere: function () { + + if ( this.boundingSphere === null ) { + + this.boundingSphere = new THREE.Sphere(); + + } + + this.boundingSphere.setFromPoints( this.vertices ); + + }, + + merge: function ( geometry, matrix, materialIndexOffset ) { + + if ( geometry instanceof THREE.Geometry === false ) { + + console.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry ); + return; + + } + + var normalMatrix, + vertexOffset = this.vertices.length, + uvPosition = this.faceVertexUvs[ 0 ].length, + vertices1 = this.vertices, + vertices2 = geometry.vertices, + faces1 = this.faces, + faces2 = geometry.faces, + uvs1 = this.faceVertexUvs[ 0 ], + uvs2 = geometry.faceVertexUvs[ 0 ]; + + if ( materialIndexOffset === undefined ) materialIndexOffset = 0; + + if ( matrix !== undefined ) { + + normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); + + } + + // vertices + + for ( var i = 0, il = vertices2.length; i < il; i ++ ) { + + var vertex = vertices2[ i ]; + + var vertexCopy = vertex.clone(); + + if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix ); + + vertices1.push( vertexCopy ); + + } + + // faces + + for ( i = 0, il = faces2.length; i < il; i ++ ) { + + var face = faces2[ i ], faceCopy, normal, color, + faceVertexNormals = face.vertexNormals, + faceVertexColors = face.vertexColors; + + faceCopy = new THREE.Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); + faceCopy.normal.copy( face.normal ); + + if ( normalMatrix !== undefined ) { + + faceCopy.normal.applyMatrix3( normalMatrix ).normalize(); + + } + + for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { + + normal = faceVertexNormals[ j ].clone(); + + if ( normalMatrix !== undefined ) { + + normal.applyMatrix3( normalMatrix ).normalize(); + + } + + faceCopy.vertexNormals.push( normal ); + + } + + faceCopy.color.copy( face.color ); + + for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { + + color = faceVertexColors[ j ]; + faceCopy.vertexColors.push( color.clone() ); + + } + + faceCopy.materialIndex = face.materialIndex + materialIndexOffset; + + faces1.push( faceCopy ); + + } + + // uvs + + for ( i = 0, il = uvs2.length; i < il; i ++ ) { + + var uv = uvs2[ i ], uvCopy = []; + + if ( uv === undefined ) { + + continue; + + } + + for ( var j = 0, jl = uv.length; j < jl; j ++ ) { + + uvCopy.push( new THREE.Vector2( uv[ j ].x, uv[ j ].y ) ); + + } + + uvs1.push( uvCopy ); + + } + + }, + + /* + * Checks for duplicate vertices with hashmap. + * Duplicated vertices are removed + * and faces' vertices are updated. + */ + + mergeVertices: function () { + + var verticesMap = {}; // Hashmap for looking up vertice by position coordinates (and making sure they are unique) + var unique = [], changes = []; + + var v, key; + var precisionPoints = 4; // number of decimal points, eg. 4 for epsilon of 0.0001 + var precision = Math.pow( 10, precisionPoints ); + var i,il, face; + var indices, k, j, jl, u; + + for ( i = 0, il = this.vertices.length; i < il; i ++ ) { + + v = this.vertices[ i ]; + key = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision ); + + if ( verticesMap[ key ] === undefined ) { + + verticesMap[ key ] = i; + unique.push( this.vertices[ i ] ); + changes[ i ] = unique.length - 1; + + } else { + + //console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]); + changes[ i ] = changes[ verticesMap[ key ] ]; + + } + + }; + + + // if faces are completely degenerate after merging vertices, we + // have to remove them from the geometry. + var faceIndicesToRemove = []; + + for ( i = 0, il = this.faces.length; i < il; i ++ ) { + + face = this.faces[ i ]; + + face.a = changes[ face.a ]; + face.b = changes[ face.b ]; + face.c = changes[ face.c ]; + + indices = [ face.a, face.b, face.c ]; + + var dupIndex = - 1; + + // if any duplicate vertices are found in a Face3 + // we have to remove the face as nothing can be saved + for ( var n = 0; n < 3; n ++ ) { + if ( indices[ n ] == indices[ ( n + 1 ) % 3 ] ) { + + dupIndex = n; + faceIndicesToRemove.push( i ); + break; + + } + } + + } + + for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) { + var idx = faceIndicesToRemove[ i ]; + + this.faces.splice( idx, 1 ); + + for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) { + + this.faceVertexUvs[ j ].splice( idx, 1 ); + + } + + } + + // Use unique set of vertices + + var diff = this.vertices.length - unique.length; + this.vertices = unique; + return diff; + + }, + + // Geometry splitting + + makeGroups: ( function () { + + var geometryGroupCounter = 0; + + return function ( usesFaceMaterial, maxVerticesInGroup ) { + + var f, fl, face, materialIndex, + groupHash, hash_map = {},geometryGroup; + + var numMorphTargets = this.morphTargets.length; + var numMorphNormals = this.morphNormals.length; + + this.geometryGroups = {}; + this.geometryGroupsList = []; + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + materialIndex = usesFaceMaterial ? face.materialIndex : 0; + + if ( ! ( materialIndex in hash_map ) ) { + + hash_map[ materialIndex ] = { 'hash': materialIndex, 'counter': 0 }; + + } + + groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter; + + if ( ! ( groupHash in this.geometryGroups ) ) { + + geometryGroup = { 'id': geometryGroupCounter++, 'faces3': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals }; + this.geometryGroups[ groupHash ] = geometryGroup; + this.geometryGroupsList.push(geometryGroup); + } + + if ( this.geometryGroups[ groupHash ].vertices + 3 > maxVerticesInGroup ) { + + hash_map[ materialIndex ].counter += 1; + groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter; + + if ( ! ( groupHash in this.geometryGroups ) ) { + + geometryGroup = { 'id': geometryGroupCounter++, 'faces3': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals }; + this.geometryGroups[ groupHash ] = geometryGroup; + this.geometryGroupsList.push(geometryGroup); + + } + + } + + this.geometryGroups[ groupHash ].faces3.push( f ); + this.geometryGroups[ groupHash ].vertices += 3; + + } + + }; + + } )(), + + clone: function () { + + var geometry = new THREE.Geometry(); + + var vertices = this.vertices; + + for ( var i = 0, il = vertices.length; i < il; i ++ ) { + + geometry.vertices.push( vertices[ i ].clone() ); + + } + + var faces = this.faces; + + for ( var i = 0, il = faces.length; i < il; i ++ ) { + + geometry.faces.push( faces[ i ].clone() ); + + } + + var uvs = this.faceVertexUvs[ 0 ]; + + for ( var i = 0, il = uvs.length; i < il; i ++ ) { + + var uv = uvs[ i ], uvCopy = []; + + for ( var j = 0, jl = uv.length; j < jl; j ++ ) { + + uvCopy.push( new THREE.Vector2( uv[ j ].x, uv[ j ].y ) ); + + } + + geometry.faceVertexUvs[ 0 ].push( uvCopy ); + + } + + return geometry; + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.Geometry.prototype ); + +THREE.GeometryIdCount = 0; + +// File:src/cameras/Camera.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.Camera = function () { + + THREE.Object3D.call( this ); + + this.matrixWorldInverse = new THREE.Matrix4(); + this.projectionMatrix = new THREE.Matrix4(); + +}; + +THREE.Camera.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.Camera.prototype.lookAt = function () { + + // This routine does not support cameras with rotated and/or translated parent(s) + + var m1 = new THREE.Matrix4(); + + return function ( vector ) { + + m1.lookAt( this.position, vector, this.up ); + + this.quaternion.setFromRotationMatrix( m1 ); + + }; + +}(); + +THREE.Camera.prototype.clone = function ( camera ) { + + if ( camera === undefined ) camera = new THREE.Camera(); + + THREE.Object3D.prototype.clone.call( this, camera ); + + camera.matrixWorldInverse.copy( this.matrixWorldInverse ); + camera.projectionMatrix.copy( this.projectionMatrix ); + + return camera; +}; + +// File:src/cameras/CubeCamera.js + +/** + * Camera for rendering cube maps + * - renders scene into axis-aligned cube + * + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.CubeCamera = function ( near, far, cubeResolution ) { + + THREE.Object3D.call( this ); + + var fov = 90, aspect = 1; + + var cameraPX = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraPX.up.set( 0, - 1, 0 ); + cameraPX.lookAt( new THREE.Vector3( 1, 0, 0 ) ); + this.add( cameraPX ); + + var cameraNX = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraNX.up.set( 0, - 1, 0 ); + cameraNX.lookAt( new THREE.Vector3( - 1, 0, 0 ) ); + this.add( cameraNX ); + + var cameraPY = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraPY.up.set( 0, 0, 1 ); + cameraPY.lookAt( new THREE.Vector3( 0, 1, 0 ) ); + this.add( cameraPY ); + + var cameraNY = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraNY.up.set( 0, 0, - 1 ); + cameraNY.lookAt( new THREE.Vector3( 0, - 1, 0 ) ); + this.add( cameraNY ); + + var cameraPZ = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraPZ.up.set( 0, - 1, 0 ); + cameraPZ.lookAt( new THREE.Vector3( 0, 0, 1 ) ); + this.add( cameraPZ ); + + var cameraNZ = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraNZ.up.set( 0, - 1, 0 ); + cameraNZ.lookAt( new THREE.Vector3( 0, 0, - 1 ) ); + this.add( cameraNZ ); + + this.renderTarget = new THREE.WebGLRenderTargetCube( cubeResolution, cubeResolution, { format: THREE.RGBFormat, magFilter: THREE.LinearFilter, minFilter: THREE.LinearFilter } ); + + this.updateCubeMap = function ( renderer, scene ) { + + var renderTarget = this.renderTarget; + var generateMipmaps = renderTarget.generateMipmaps; + + renderTarget.generateMipmaps = false; + + renderTarget.activeCubeFace = 0; + renderer.render( scene, cameraPX, renderTarget ); + + renderTarget.activeCubeFace = 1; + renderer.render( scene, cameraNX, renderTarget ); + + renderTarget.activeCubeFace = 2; + renderer.render( scene, cameraPY, renderTarget ); + + renderTarget.activeCubeFace = 3; + renderer.render( scene, cameraNY, renderTarget ); + + renderTarget.activeCubeFace = 4; + renderer.render( scene, cameraPZ, renderTarget ); + + renderTarget.generateMipmaps = generateMipmaps; + + renderTarget.activeCubeFace = 5; + renderer.render( scene, cameraNZ, renderTarget ); + + }; + +}; + +THREE.CubeCamera.prototype = Object.create( THREE.Object3D.prototype ); + +// File:src/cameras/OrthographicCamera.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.OrthographicCamera = function ( left, right, top, bottom, near, far ) { + + THREE.Camera.call( this ); + + this.left = left; + this.right = right; + this.top = top; + this.bottom = bottom; + + this.near = ( near !== undefined ) ? near : 0.1; + this.far = ( far !== undefined ) ? far : 2000; + + this.updateProjectionMatrix(); + +}; + +THREE.OrthographicCamera.prototype = Object.create( THREE.Camera.prototype ); + +THREE.OrthographicCamera.prototype.updateProjectionMatrix = function () { + + this.projectionMatrix.makeOrthographic( this.left, this.right, this.top, this.bottom, this.near, this.far ); + +}; + +THREE.OrthographicCamera.prototype.clone = function () { + + var camera = new THREE.OrthographicCamera(); + + THREE.Camera.prototype.clone.call( this, camera ); + + camera.left = this.left; + camera.right = this.right; + camera.top = this.top; + camera.bottom = this.bottom; + + camera.near = this.near; + camera.far = this.far; + + return camera; +}; + +// File:src/cameras/PerspectiveCamera.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author greggman / http://games.greggman.com/ + * @author zz85 / http://www.lab4games.net/zz85/blog + */ + +THREE.PerspectiveCamera = function ( fov, aspect, near, far ) { + + THREE.Camera.call( this ); + + this.fov = fov !== undefined ? fov : 50; + this.aspect = aspect !== undefined ? aspect : 1; + this.near = near !== undefined ? near : 0.1; + this.far = far !== undefined ? far : 2000; + + this.updateProjectionMatrix(); + +}; + +THREE.PerspectiveCamera.prototype = Object.create( THREE.Camera.prototype ); + + +/** + * Uses Focal Length (in mm) to estimate and set FOV + * 35mm (fullframe) camera is used if frame size is not specified; + * Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html + */ + +THREE.PerspectiveCamera.prototype.setLens = function ( focalLength, frameHeight ) { + + if ( frameHeight === undefined ) frameHeight = 24; + + this.fov = 2 * THREE.Math.radToDeg( Math.atan( frameHeight / ( focalLength * 2 ) ) ); + this.updateProjectionMatrix(); + +} + + +/** + * Sets an offset in a larger frustum. This is useful for multi-window or + * multi-monitor/multi-machine setups. + * + * For example, if you have 3x2 monitors and each monitor is 1920x1080 and + * the monitors are in grid like this + * + * +---+---+---+ + * | A | B | C | + * +---+---+---+ + * | D | E | F | + * +---+---+---+ + * + * then for each monitor you would call it like this + * + * var w = 1920; + * var h = 1080; + * var fullWidth = w * 3; + * var fullHeight = h * 2; + * + * --A-- + * camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); + * --B-- + * camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); + * --C-- + * camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); + * --D-- + * camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); + * --E-- + * camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); + * --F-- + * camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); + * + * Note there is no reason monitors have to be the same size or in a grid. + */ + +THREE.PerspectiveCamera.prototype.setViewOffset = function ( fullWidth, fullHeight, x, y, width, height ) { + + this.fullWidth = fullWidth; + this.fullHeight = fullHeight; + this.x = x; + this.y = y; + this.width = width; + this.height = height; + + this.updateProjectionMatrix(); + +}; + + +THREE.PerspectiveCamera.prototype.updateProjectionMatrix = function () { + + if ( this.fullWidth ) { + + var aspect = this.fullWidth / this.fullHeight; + var top = Math.tan( THREE.Math.degToRad( this.fov * 0.5 ) ) * this.near; + var bottom = - top; + var left = aspect * bottom; + var right = aspect * top; + var width = Math.abs( right - left ); + var height = Math.abs( top - bottom ); + + this.projectionMatrix.makeFrustum( + left + this.x * width / this.fullWidth, + left + ( this.x + this.width ) * width / this.fullWidth, + top - ( this.y + this.height ) * height / this.fullHeight, + top - this.y * height / this.fullHeight, + this.near, + this.far + ); + + } else { + + this.projectionMatrix.makePerspective( this.fov, this.aspect, this.near, this.far ); + + } + +}; + +THREE.PerspectiveCamera.prototype.clone = function () { + + var camera = new THREE.PerspectiveCamera(); + + THREE.Camera.prototype.clone.call( this, camera ); + + camera.fov = this.fov; + camera.aspect = this.aspect; + camera.near = this.near; + camera.far = this.far; + + return camera; +}; + +// File:src/lights/Light.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Light = function ( color ) { + + THREE.Object3D.call( this ); + + this.color = new THREE.Color( color ); + +}; + +THREE.Light.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.Light.prototype.clone = function ( light ) { + + if ( light === undefined ) light = new THREE.Light(); + + THREE.Object3D.prototype.clone.call( this, light ); + + light.color.copy( this.color ); + + return light; + +}; + +// File:src/lights/AmbientLight.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.AmbientLight = function ( color ) { + + THREE.Light.call( this, color ); + +}; + +THREE.AmbientLight.prototype = Object.create( THREE.Light.prototype ); + +THREE.AmbientLight.prototype.clone = function () { + + var light = new THREE.AmbientLight(); + + THREE.Light.prototype.clone.call( this, light ); + + return light; + +}; + +// File:src/lights/AreaLight.js + +/** + * @author MPanknin / http://www.redplant.de/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.AreaLight = function ( color, intensity ) { + + THREE.Light.call( this, color ); + + this.normal = new THREE.Vector3( 0, - 1, 0 ); + this.right = new THREE.Vector3( 1, 0, 0 ); + + this.intensity = ( intensity !== undefined ) ? intensity : 1; + + this.width = 1.0; + this.height = 1.0; + + this.constantAttenuation = 1.5; + this.linearAttenuation = 0.5; + this.quadraticAttenuation = 0.1; + +}; + +THREE.AreaLight.prototype = Object.create( THREE.Light.prototype ); + + +// File:src/lights/DirectionalLight.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.DirectionalLight = function ( color, intensity ) { + + THREE.Light.call( this, color ); + + this.position.set( 0, 1, 0 ); + this.target = new THREE.Object3D(); + + this.intensity = ( intensity !== undefined ) ? intensity : 1; + + this.castShadow = false; + this.onlyShadow = false; + + // + + this.shadowCameraNear = 50; + this.shadowCameraFar = 5000; + + this.shadowCameraLeft = - 500; + this.shadowCameraRight = 500; + this.shadowCameraTop = 500; + this.shadowCameraBottom = - 500; + + this.shadowCameraVisible = false; + + this.shadowBias = 0; + this.shadowDarkness = 0.5; + + this.shadowMapWidth = 512; + this.shadowMapHeight = 512; + + // + + this.shadowCascade = false; + + this.shadowCascadeOffset = new THREE.Vector3( 0, 0, - 1000 ); + this.shadowCascadeCount = 2; + + this.shadowCascadeBias = [ 0, 0, 0 ]; + this.shadowCascadeWidth = [ 512, 512, 512 ]; + this.shadowCascadeHeight = [ 512, 512, 512 ]; + + this.shadowCascadeNearZ = [ - 1.000, 0.990, 0.998 ]; + this.shadowCascadeFarZ = [ 0.990, 0.998, 1.000 ]; + + this.shadowCascadeArray = []; + + // + + this.shadowMap = null; + this.shadowMapSize = null; + this.shadowCamera = null; + this.shadowMatrix = null; + +}; + +THREE.DirectionalLight.prototype = Object.create( THREE.Light.prototype ); + +THREE.DirectionalLight.prototype.clone = function () { + + var light = new THREE.DirectionalLight(); + + THREE.Light.prototype.clone.call( this, light ); + + light.target = this.target.clone(); + + light.intensity = this.intensity; + + light.castShadow = this.castShadow; + light.onlyShadow = this.onlyShadow; + + // + + light.shadowCameraNear = this.shadowCameraNear; + light.shadowCameraFar = this.shadowCameraFar; + + light.shadowCameraLeft = this.shadowCameraLeft; + light.shadowCameraRight = this.shadowCameraRight; + light.shadowCameraTop = this.shadowCameraTop; + light.shadowCameraBottom = this.shadowCameraBottom; + + light.shadowCameraVisible = this.shadowCameraVisible; + + light.shadowBias = this.shadowBias; + light.shadowDarkness = this.shadowDarkness; + + light.shadowMapWidth = this.shadowMapWidth; + light.shadowMapHeight = this.shadowMapHeight; + + // + + light.shadowCascade = this.shadowCascade; + + light.shadowCascadeOffset.copy( this.shadowCascadeOffset ); + light.shadowCascadeCount = this.shadowCascadeCount; + + light.shadowCascadeBias = this.shadowCascadeBias.slice( 0 ); + light.shadowCascadeWidth = this.shadowCascadeWidth.slice( 0 ); + light.shadowCascadeHeight = this.shadowCascadeHeight.slice( 0 ); + + light.shadowCascadeNearZ = this.shadowCascadeNearZ.slice( 0 ); + light.shadowCascadeFarZ = this.shadowCascadeFarZ.slice( 0 ); + + return light; + +}; + +// File:src/lights/HemisphereLight.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.HemisphereLight = function ( skyColor, groundColor, intensity ) { + + THREE.Light.call( this, skyColor ); + + this.position.set( 0, 100, 0 ); + + this.groundColor = new THREE.Color( groundColor ); + this.intensity = ( intensity !== undefined ) ? intensity : 1; + +}; + +THREE.HemisphereLight.prototype = Object.create( THREE.Light.prototype ); + +THREE.HemisphereLight.prototype.clone = function () { + + var light = new THREE.HemisphereLight(); + + THREE.Light.prototype.clone.call( this, light ); + + light.groundColor.copy( this.groundColor ); + light.intensity = this.intensity; + + return light; + +}; + +// File:src/lights/PointLight.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.PointLight = function ( color, intensity, distance ) { + + THREE.Light.call( this, color ); + + this.intensity = ( intensity !== undefined ) ? intensity : 1; + this.distance = ( distance !== undefined ) ? distance : 0; + +}; + +THREE.PointLight.prototype = Object.create( THREE.Light.prototype ); + +THREE.PointLight.prototype.clone = function () { + + var light = new THREE.PointLight(); + + THREE.Light.prototype.clone.call( this, light ); + + light.intensity = this.intensity; + light.distance = this.distance; + + return light; + +}; + +// File:src/lights/SpotLight.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.SpotLight = function ( color, intensity, distance, angle, exponent ) { + + THREE.Light.call( this, color ); + + this.position.set( 0, 1, 0 ); + this.target = new THREE.Object3D(); + + this.intensity = ( intensity !== undefined ) ? intensity : 1; + this.distance = ( distance !== undefined ) ? distance : 0; + this.angle = ( angle !== undefined ) ? angle : Math.PI / 3; + this.exponent = ( exponent !== undefined ) ? exponent : 10; + + this.castShadow = false; + this.onlyShadow = false; + + // + + this.shadowCameraNear = 50; + this.shadowCameraFar = 5000; + this.shadowCameraFov = 50; + + this.shadowCameraVisible = false; + + this.shadowBias = 0; + this.shadowDarkness = 0.5; + + this.shadowMapWidth = 512; + this.shadowMapHeight = 512; + + // + + this.shadowMap = null; + this.shadowMapSize = null; + this.shadowCamera = null; + this.shadowMatrix = null; + +}; + +THREE.SpotLight.prototype = Object.create( THREE.Light.prototype ); + +THREE.SpotLight.prototype.clone = function () { + + var light = new THREE.SpotLight(); + + THREE.Light.prototype.clone.call( this, light ); + + light.target = this.target.clone(); + + light.intensity = this.intensity; + light.distance = this.distance; + light.angle = this.angle; + light.exponent = this.exponent; + + light.castShadow = this.castShadow; + light.onlyShadow = this.onlyShadow; + + // + + light.shadowCameraNear = this.shadowCameraNear; + light.shadowCameraFar = this.shadowCameraFar; + light.shadowCameraFov = this.shadowCameraFov; + + light.shadowCameraVisible = this.shadowCameraVisible; + + light.shadowBias = this.shadowBias; + light.shadowDarkness = this.shadowDarkness; + + light.shadowMapWidth = this.shadowMapWidth; + light.shadowMapHeight = this.shadowMapHeight; + + return light; + +}; + +// File:src/loaders/Cache.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Cache = function () { + + this.files = {}; + +}; + +THREE.Cache.prototype = { + + constructor: THREE.Cache, + + add: function ( key, file ) { + + // console.log( 'THREE.Cache', 'Adding key:', key ); + + this.files[ key ] = file; + + }, + + get: function ( key ) { + + // console.log( 'THREE.Cache', 'Checking key:', key ); + + return this.files[ key ]; + + }, + + remove: function ( key ) { + + delete this.files[ key ]; + + }, + + clear: function () { + + this.files = {} + + } + +}; + +// File:src/loaders/Loader.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Loader = function ( showStatus ) { + + this.showStatus = showStatus; + this.statusDomElement = showStatus ? THREE.Loader.prototype.addStatusElement() : null; + + this.imageLoader = new THREE.ImageLoader(); + + this.onLoadStart = function () {}; + this.onLoadProgress = function () {}; + this.onLoadComplete = function () {}; + +}; + +THREE.Loader.prototype = { + + constructor: THREE.Loader, + + crossOrigin: undefined, + + addStatusElement: function () { + + var e = document.createElement( 'div' ); + + e.style.position = 'absolute'; + e.style.right = '0px'; + e.style.top = '0px'; + e.style.fontSize = '0.8em'; + e.style.textAlign = 'left'; + e.style.background = 'rgba(0,0,0,0.25)'; + e.style.color = '#fff'; + e.style.width = '120px'; + e.style.padding = '0.5em 0.5em 0.5em 0.5em'; + e.style.zIndex = 1000; + + e.innerHTML = 'Loading ...'; + + return e; + + }, + + updateProgress: function ( progress ) { + + var message = 'Loaded '; + + if ( progress.total ) { + + message += ( 100 * progress.loaded / progress.total ).toFixed( 0 ) + '%'; + + + } else { + + message += ( progress.loaded / 1024 ).toFixed( 2 ) + ' KB'; + + } + + this.statusDomElement.innerHTML = message; + + }, + + extractUrlBase: function ( url ) { + + var parts = url.split( '/' ); + + if ( parts.length === 1 ) return './'; + + parts.pop(); + + return parts.join( '/' ) + '/'; + + }, + + initMaterials: function ( materials, texturePath ) { + + var array = []; + + for ( var i = 0; i < materials.length; ++ i ) { + + array[ i ] = this.createMaterial( materials[ i ], texturePath ); + + } + + return array; + + }, + + needsTangents: function ( materials ) { + + for ( var i = 0, il = materials.length; i < il; i ++ ) { + + var m = materials[ i ]; + + if ( m instanceof THREE.ShaderMaterial ) return true; + + } + + return false; + + }, + + createMaterial: function ( m, texturePath ) { + + var scope = this; + + function nearest_pow2( n ) { + + var l = Math.log( n ) / Math.LN2; + return Math.pow( 2, Math.round( l ) ); + + } + + function create_texture( where, name, sourceFile, repeat, offset, wrap, anisotropy ) { + + var fullPath = texturePath + sourceFile; + + var texture; + + var loader = THREE.Loader.Handlers.get( fullPath ); + + if ( loader !== null ) { + + texture = loader.load( fullPath ); + + } else { + + texture = new THREE.Texture(); + + loader = scope.imageLoader; + loader.crossOrigin = scope.crossOrigin; + loader.load( fullPath, function ( image ) { + + if ( THREE.Math.isPowerOfTwo( image.width ) === false || + THREE.Math.isPowerOfTwo( image.height ) === false ) { + + var width = nearest_pow2( image.width ); + var height = nearest_pow2( image.height ); + + var canvas = document.createElement( 'canvas' ); + canvas.width = width; + canvas.height = height; + + var context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0, width, height ); + + texture.image = canvas; + + } else { + + texture.image = image; + + } + + texture.needsUpdate = true; + + } ); + + } + + texture.sourceFile = sourceFile; + + if ( repeat ) { + + texture.repeat.set( repeat[ 0 ], repeat[ 1 ] ); + + if ( repeat[ 0 ] !== 1 ) texture.wrapS = THREE.RepeatWrapping; + if ( repeat[ 1 ] !== 1 ) texture.wrapT = THREE.RepeatWrapping; + + } + + if ( offset ) { + + texture.offset.set( offset[ 0 ], offset[ 1 ] ); + + } + + if ( wrap ) { + + var wrapMap = { + 'repeat': THREE.RepeatWrapping, + 'mirror': THREE.MirroredRepeatWrapping + } + + if ( wrapMap[ wrap[ 0 ] ] !== undefined ) texture.wrapS = wrapMap[ wrap[ 0 ] ]; + if ( wrapMap[ wrap[ 1 ] ] !== undefined ) texture.wrapT = wrapMap[ wrap[ 1 ] ]; + + } + + if ( anisotropy ) { + + texture.anisotropy = anisotropy; + + } + + where[ name ] = texture; + + } + + function rgb2hex( rgb ) { + + return ( rgb[ 0 ] * 255 << 16 ) + ( rgb[ 1 ] * 255 << 8 ) + rgb[ 2 ] * 255; + + } + + // defaults + + var mtype = 'MeshLambertMaterial'; + var mpars = { color: 0xeeeeee, opacity: 1.0, map: null, lightMap: null, normalMap: null, bumpMap: null, wireframe: false }; + + // parameters from model file + + if ( m.shading ) { + + var shading = m.shading.toLowerCase(); + + if ( shading === 'phong' ) mtype = 'MeshPhongMaterial'; + else if ( shading === 'basic' ) mtype = 'MeshBasicMaterial'; + + } + + if ( m.blending !== undefined && THREE[ m.blending ] !== undefined ) { + + mpars.blending = THREE[ m.blending ]; + + } + + if ( m.transparent !== undefined || m.opacity < 1.0 ) { + + mpars.transparent = m.transparent; + + } + + if ( m.depthTest !== undefined ) { + + mpars.depthTest = m.depthTest; + + } + + if ( m.depthWrite !== undefined ) { + + mpars.depthWrite = m.depthWrite; + + } + + if ( m.visible !== undefined ) { + + mpars.visible = m.visible; + + } + + if ( m.flipSided !== undefined ) { + + mpars.side = THREE.BackSide; + + } + + if ( m.doubleSided !== undefined ) { + + mpars.side = THREE.DoubleSide; + + } + + if ( m.wireframe !== undefined ) { + + mpars.wireframe = m.wireframe; + + } + + if ( m.vertexColors !== undefined ) { + + if ( m.vertexColors === 'face' ) { + + mpars.vertexColors = THREE.FaceColors; + + } else if ( m.vertexColors ) { + + mpars.vertexColors = THREE.VertexColors; + + } + + } + + // colors + + if ( m.colorDiffuse ) { + + mpars.color = rgb2hex( m.colorDiffuse ); + + } else if ( m.DbgColor ) { + + mpars.color = m.DbgColor; + + } + + if ( m.colorSpecular ) { + + mpars.specular = rgb2hex( m.colorSpecular ); + + } + + if ( m.colorAmbient ) { + + mpars.ambient = rgb2hex( m.colorAmbient ); + + } + + if ( m.colorEmissive ) { + + mpars.emissive = rgb2hex( m.colorEmissive ); + + } + + // modifiers + + if ( m.transparency ) { + + mpars.opacity = m.transparency; + + } + + if ( m.specularCoef ) { + + mpars.shininess = m.specularCoef; + + } + + // textures + + if ( m.mapDiffuse && texturePath ) { + + create_texture( mpars, 'map', m.mapDiffuse, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy ); + + } + + if ( m.mapLight && texturePath ) { + + create_texture( mpars, 'lightMap', m.mapLight, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy ); + + } + + if ( m.mapBump && texturePath ) { + + create_texture( mpars, 'bumpMap', m.mapBump, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy ); + + } + + if ( m.mapNormal && texturePath ) { + + create_texture( mpars, 'normalMap', m.mapNormal, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy ); + + } + + if ( m.mapSpecular && texturePath ) { + + create_texture( mpars, 'specularMap', m.mapSpecular, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy ); + + } + + if ( m.mapAlpha && texturePath ) { + + create_texture( mpars, 'alphaMap', m.mapAlpha, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy ); + + } + + // + + if ( m.mapBumpScale ) { + + mpars.bumpScale = m.mapBumpScale; + + } + + // special case for normal mapped material + + if ( m.mapNormal ) { + + var shader = THREE.ShaderLib[ 'normalmap' ]; + var uniforms = THREE.UniformsUtils.clone( shader.uniforms ); + + uniforms[ 'tNormal' ].value = mpars.normalMap; + + if ( m.mapNormalFactor ) { + + uniforms[ 'uNormalScale' ].value.set( m.mapNormalFactor, m.mapNormalFactor ); + + } + + if ( mpars.map ) { + + uniforms[ 'tDiffuse' ].value = mpars.map; + uniforms[ 'enableDiffuse' ].value = true; + + } + + if ( mpars.specularMap ) { + + uniforms[ 'tSpecular' ].value = mpars.specularMap; + uniforms[ 'enableSpecular' ].value = true; + + } + + if ( mpars.lightMap ) { + + uniforms[ 'tAO' ].value = mpars.lightMap; + uniforms[ 'enableAO' ].value = true; + + } + + // for the moment don't handle displacement texture + + uniforms[ 'diffuse' ].value.setHex( mpars.color ); + uniforms[ 'specular' ].value.setHex( mpars.specular ); + uniforms[ 'ambient' ].value.setHex( mpars.ambient ); + + uniforms[ 'shininess' ].value = mpars.shininess; + + if ( mpars.opacity !== undefined ) { + + uniforms[ 'opacity' ].value = mpars.opacity; + + } + + var parameters = { fragmentShader: shader.fragmentShader, vertexShader: shader.vertexShader, uniforms: uniforms, lights: true, fog: true }; + var material = new THREE.ShaderMaterial( parameters ); + + if ( mpars.transparent ) { + + material.transparent = true; + + } + + } else { + + var material = new THREE[ mtype ]( mpars ); + + } + + if ( m.DbgName !== undefined ) material.name = m.DbgName; + + return material; + + } + +}; + +THREE.Loader.Handlers = { + + handlers: [], + + add: function ( regex, loader ) { + + this.handlers.push( regex, loader ); + + }, + + get: function ( file ) { + + for ( var i = 0, l = this.handlers.length; i < l; i += 2 ) { + + var regex = this.handlers[ i ]; + var loader = this.handlers[ i + 1 ]; + + if ( regex.test( file ) ) { + + return loader; + + } + + } + + return null; + + } + +}; + +// File:src/loaders/XHRLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.XHRLoader = function ( manager ) { + + this.cache = new THREE.Cache(); + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.XHRLoader.prototype = { + + constructor: THREE.XHRLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var cached = scope.cache.get( url ); + + if ( cached !== undefined ) { + + if ( onLoad ) onLoad( cached ); + return; + + } + + var request = new XMLHttpRequest(); + request.open( 'GET', url, true ); + + request.addEventListener( 'load', function ( event ) { + + scope.cache.add( url, this.response ); + + if ( onLoad ) onLoad( this.response ); + + scope.manager.itemEnd( url ); + + }, false ); + + if ( onProgress !== undefined ) { + + request.addEventListener( 'progress', function ( event ) { + + onProgress( event ); + + }, false ); + + } + + if ( onError !== undefined ) { + + request.addEventListener( 'error', function ( event ) { + + onError( event ); + + }, false ); + + } + + if ( this.crossOrigin !== undefined ) request.crossOrigin = this.crossOrigin; + if ( this.responseType !== undefined ) request.responseType = this.responseType; + + request.send( null ); + + scope.manager.itemStart( url ); + + }, + + setResponseType: function ( value ) { + + this.responseType = value; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + } + +}; + +// File:src/loaders/ImageLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.ImageLoader = function ( manager ) { + + this.cache = new THREE.Cache(); + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.ImageLoader.prototype = { + + constructor: THREE.ImageLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var cached = scope.cache.get( url ); + + if ( cached !== undefined ) { + + onLoad( cached ); + return; + + } + + var image = document.createElement( 'img' ); + + if ( onLoad !== undefined ) { + + image.addEventListener( 'load', function ( event ) { + + scope.cache.add( url, this ); + + onLoad( this ); + scope.manager.itemEnd( url ); + + }, false ); + + } + + if ( onProgress !== undefined ) { + + image.addEventListener( 'progress', function ( event ) { + + onProgress( event ); + + }, false ); + + } + + if ( onError !== undefined ) { + + image.addEventListener( 'error', function ( event ) { + + onError( event ); + + }, false ); + + } + + if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; + + image.src = url; + + scope.manager.itemStart( url ); + + return image; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + } + +} + +// File:src/loaders/JSONLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.JSONLoader = function ( showStatus ) { + + THREE.Loader.call( this, showStatus ); + + this.withCredentials = false; + +}; + +THREE.JSONLoader.prototype = Object.create( THREE.Loader.prototype ); + +THREE.JSONLoader.prototype.load = function ( url, callback, texturePath ) { + + var scope = this; + + // todo: unify load API to for easier SceneLoader use + + texturePath = texturePath && ( typeof texturePath === 'string' ) ? texturePath : this.extractUrlBase( url ); + + this.onLoadStart(); + this.loadAjaxJSON( this, url, callback, texturePath ); + +}; + +THREE.JSONLoader.prototype.loadAjaxJSON = function ( context, url, callback, texturePath, callbackProgress ) { + + var xhr = new XMLHttpRequest(); + + var length = 0; + + xhr.onreadystatechange = function () { + + if ( xhr.readyState === xhr.DONE ) { + + if ( xhr.status === 200 || xhr.status === 0 ) { + + if ( xhr.responseText ) { + + var json = JSON.parse( xhr.responseText ); + + if ( json.metadata !== undefined && json.metadata.type === 'scene' ) { + + console.error( 'THREE.JSONLoader: "' + url + '" seems to be a Scene. Use THREE.SceneLoader instead.' ); + return; + + } + + var result = context.parse( json, texturePath ); + callback( result.geometry, result.materials ); + + } else { + + console.error( 'THREE.JSONLoader: "' + url + '" seems to be unreachable or the file is empty.' ); + + } + + // in context of more complex asset initialization + // do not block on single failed file + // maybe should go even one more level up + + context.onLoadComplete(); + + } else { + + console.error( 'THREE.JSONLoader: Couldn\'t load "' + url + '" (' + xhr.status + ')' ); + + } + + } else if ( xhr.readyState === xhr.LOADING ) { + + if ( callbackProgress ) { + + if ( length === 0 ) { + + length = xhr.getResponseHeader( 'Content-Length' ); + + } + + callbackProgress( { total: length, loaded: xhr.responseText.length } ); + + } + + } else if ( xhr.readyState === xhr.HEADERS_RECEIVED ) { + + if ( callbackProgress !== undefined ) { + + length = xhr.getResponseHeader( 'Content-Length' ); + + } + + } + + }; + + xhr.open( 'GET', url, true ); + xhr.withCredentials = this.withCredentials; + xhr.send( null ); + +}; + +THREE.JSONLoader.prototype.parse = function ( json, texturePath ) { + + var scope = this, + geometry = new THREE.Geometry(), + scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0; + + parseModel( scale ); + + parseSkin(); + parseMorphing( scale ); + + geometry.computeFaceNormals(); + geometry.computeBoundingSphere(); + + function parseModel( scale ) { + + function isBitSet( value, position ) { + + return value & ( 1 << position ); + + } + + var i, j, fi, + + offset, zLength, + + colorIndex, normalIndex, uvIndex, materialIndex, + + type, + isQuad, + hasMaterial, + hasFaceVertexUv, + hasFaceNormal, hasFaceVertexNormal, + hasFaceColor, hasFaceVertexColor, + + vertex, face, faceA, faceB, color, hex, normal, + + uvLayer, uv, u, v, + + faces = json.faces, + vertices = json.vertices, + normals = json.normals, + colors = json.colors, + + nUvLayers = 0; + + if ( json.uvs !== undefined ) { + + // disregard empty arrays + + for ( i = 0; i < json.uvs.length; i ++ ) { + + if ( json.uvs[ i ].length ) nUvLayers ++; + + } + + for ( i = 0; i < nUvLayers; i ++ ) { + + geometry.faceVertexUvs[ i ] = []; + + } + + } + + offset = 0; + zLength = vertices.length; + + while ( offset < zLength ) { + + vertex = new THREE.Vector3(); + + vertex.x = vertices[ offset ++ ] * scale; + vertex.y = vertices[ offset ++ ] * scale; + vertex.z = vertices[ offset ++ ] * scale; + + geometry.vertices.push( vertex ); + + } + + offset = 0; + zLength = faces.length; + + while ( offset < zLength ) { + + type = faces[ offset ++ ]; + + + isQuad = isBitSet( type, 0 ); + hasMaterial = isBitSet( type, 1 ); + hasFaceVertexUv = isBitSet( type, 3 ); + hasFaceNormal = isBitSet( type, 4 ); + hasFaceVertexNormal = isBitSet( type, 5 ); + hasFaceColor = isBitSet( type, 6 ); + hasFaceVertexColor = isBitSet( type, 7 ); + + // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); + + if ( isQuad ) { + + faceA = new THREE.Face3(); + faceA.a = faces[ offset ]; + faceA.b = faces[ offset + 1 ]; + faceA.c = faces[ offset + 3 ]; + + faceB = new THREE.Face3(); + faceB.a = faces[ offset + 1 ]; + faceB.b = faces[ offset + 2 ]; + faceB.c = faces[ offset + 3 ]; + + offset += 4; + + if ( hasMaterial ) { + + materialIndex = faces[ offset ++ ]; + faceA.materialIndex = materialIndex; + faceB.materialIndex = materialIndex; + + } + + // to get face <=> uv index correspondence + + fi = geometry.faces.length; + + if ( hasFaceVertexUv ) { + + for ( i = 0; i < nUvLayers; i ++ ) { + + uvLayer = json.uvs[ i ]; + + geometry.faceVertexUvs[ i ][ fi ] = []; + geometry.faceVertexUvs[ i ][ fi + 1 ] = [] + + for ( j = 0; j < 4; j ++ ) { + + uvIndex = faces[ offset ++ ]; + + u = uvLayer[ uvIndex * 2 ]; + v = uvLayer[ uvIndex * 2 + 1 ]; + + uv = new THREE.Vector2( u, v ); + + if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv ); + if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv ); + + } + + } + + } + + if ( hasFaceNormal ) { + + normalIndex = faces[ offset ++ ] * 3; + + faceA.normal.set( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); + + faceB.normal.copy( faceA.normal ); + + } + + if ( hasFaceVertexNormal ) { + + for ( i = 0; i < 4; i ++ ) { + + normalIndex = faces[ offset ++ ] * 3; + + normal = new THREE.Vector3( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); + + + if ( i !== 2 ) faceA.vertexNormals.push( normal ); + if ( i !== 0 ) faceB.vertexNormals.push( normal ); + + } + + } + + + if ( hasFaceColor ) { + + colorIndex = faces[ offset ++ ]; + hex = colors[ colorIndex ]; + + faceA.color.setHex( hex ); + faceB.color.setHex( hex ); + + } + + + if ( hasFaceVertexColor ) { + + for ( i = 0; i < 4; i ++ ) { + + colorIndex = faces[ offset ++ ]; + hex = colors[ colorIndex ]; + + if ( i !== 2 ) faceA.vertexColors.push( new THREE.Color( hex ) ); + if ( i !== 0 ) faceB.vertexColors.push( new THREE.Color( hex ) ); + + } + + } + + geometry.faces.push( faceA ); + geometry.faces.push( faceB ); + + } else { + + face = new THREE.Face3(); + face.a = faces[ offset ++ ]; + face.b = faces[ offset ++ ]; + face.c = faces[ offset ++ ]; + + if ( hasMaterial ) { + + materialIndex = faces[ offset ++ ]; + face.materialIndex = materialIndex; + + } + + // to get face <=> uv index correspondence + + fi = geometry.faces.length; + + if ( hasFaceVertexUv ) { + + for ( i = 0; i < nUvLayers; i ++ ) { + + uvLayer = json.uvs[ i ]; + + geometry.faceVertexUvs[ i ][ fi ] = []; + + for ( j = 0; j < 3; j ++ ) { + + uvIndex = faces[ offset ++ ]; + + u = uvLayer[ uvIndex * 2 ]; + v = uvLayer[ uvIndex * 2 + 1 ]; + + uv = new THREE.Vector2( u, v ); + + geometry.faceVertexUvs[ i ][ fi ].push( uv ); + + } + + } + + } + + if ( hasFaceNormal ) { + + normalIndex = faces[ offset ++ ] * 3; + + face.normal.set( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); + + } + + if ( hasFaceVertexNormal ) { + + for ( i = 0; i < 3; i ++ ) { + + normalIndex = faces[ offset ++ ] * 3; + + normal = new THREE.Vector3( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); + + face.vertexNormals.push( normal ); + + } + + } + + + if ( hasFaceColor ) { + + colorIndex = faces[ offset ++ ]; + face.color.setHex( colors[ colorIndex ] ); + + } + + + if ( hasFaceVertexColor ) { + + for ( i = 0; i < 3; i ++ ) { + + colorIndex = faces[ offset ++ ]; + face.vertexColors.push( new THREE.Color( colors[ colorIndex ] ) ); + + } + + } + + geometry.faces.push( face ); + + } + + } + + }; + + function parseSkin() { + var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2; + + if ( json.skinWeights ) { + + for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) { + + var x = json.skinWeights[ i ]; + var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0; + var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0; + var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0; + + geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) ); + + } + + } + + if ( json.skinIndices ) { + + for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) { + + var a = json.skinIndices[ i ]; + var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0; + var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0; + var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0; + + geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) ); + + } + + } + + geometry.bones = json.bones; + + if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) { + + console.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' + + geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' ); + + } + + + // could change this to json.animations[0] or remove completely + + geometry.animation = json.animation; + geometry.animations = json.animations; + + }; + + function parseMorphing( scale ) { + + if ( json.morphTargets !== undefined ) { + + var i, l, v, vl, dstVertices, srcVertices; + + for ( i = 0, l = json.morphTargets.length; i < l; i ++ ) { + + geometry.morphTargets[ i ] = {}; + geometry.morphTargets[ i ].name = json.morphTargets[ i ].name; + geometry.morphTargets[ i ].vertices = []; + + dstVertices = geometry.morphTargets[ i ].vertices; + srcVertices = json.morphTargets [ i ].vertices; + + for ( v = 0, vl = srcVertices.length; v < vl; v += 3 ) { + + var vertex = new THREE.Vector3(); + vertex.x = srcVertices[ v ] * scale; + vertex.y = srcVertices[ v + 1 ] * scale; + vertex.z = srcVertices[ v + 2 ] * scale; + + dstVertices.push( vertex ); + + } + + } + + } + + if ( json.morphColors !== undefined ) { + + var i, l, c, cl, dstColors, srcColors, color; + + for ( i = 0, l = json.morphColors.length; i < l; i ++ ) { + + geometry.morphColors[ i ] = {}; + geometry.morphColors[ i ].name = json.morphColors[ i ].name; + geometry.morphColors[ i ].colors = []; + + dstColors = geometry.morphColors[ i ].colors; + srcColors = json.morphColors [ i ].colors; + + for ( c = 0, cl = srcColors.length; c < cl; c += 3 ) { + + color = new THREE.Color( 0xffaa00 ); + color.setRGB( srcColors[ c ], srcColors[ c + 1 ], srcColors[ c + 2 ] ); + dstColors.push( color ); + + } + + } + + } + + }; + + if ( json.materials === undefined || json.materials.length === 0 ) { + + return { geometry: geometry }; + + } else { + + var materials = this.initMaterials( json.materials, texturePath ); + + if ( this.needsTangents( materials ) ) { + + geometry.computeTangents(); + + } + + return { geometry: geometry, materials: materials }; + + } + +}; + +// File:src/loaders/LoadingManager.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.LoadingManager = function ( onLoad, onProgress, onError ) { + + var scope = this; + + var loaded = 0, total = 0; + + this.onLoad = onLoad; + this.onProgress = onProgress; + this.onError = onError; + + this.itemStart = function ( url ) { + + total ++; + + }; + + this.itemEnd = function ( url ) { + + loaded ++; + + if ( scope.onProgress !== undefined ) { + + scope.onProgress( url, loaded, total ); + + } + + if ( loaded === total && scope.onLoad !== undefined ) { + + scope.onLoad(); + + } + + }; + +}; + +THREE.DefaultLoadingManager = new THREE.LoadingManager(); + +// File:src/loaders/BufferGeometryLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.BufferGeometryLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.BufferGeometryLoader.prototype = { + + constructor: THREE.BufferGeometryLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.XHRLoader(); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( JSON.parse( text ) ) ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + parse: function ( json ) { + + var geometry = new THREE.BufferGeometry(); + + var attributes = json.attributes; + + for ( var key in attributes ) { + + var attribute = attributes[ key ]; + + geometry.attributes[ key ] = { + itemSize: attribute.itemSize, + array: new self[ attribute.type ]( attribute.array ) + } + + } + + var offsets = json.offsets; + + if ( offsets !== undefined ) { + + geometry.offsets = JSON.parse( JSON.stringify( offsets ) ); + + } + + var boundingSphere = json.boundingSphere; + + if ( boundingSphere !== undefined ) { + + geometry.boundingSphere = new THREE.Sphere( + new THREE.Vector3().fromArray( boundingSphere.center !== undefined ? boundingSphere.center : [ 0, 0, 0 ] ), + boundingSphere.radius + ); + + } + + return geometry; + + } + +}; + +// File:src/loaders/MaterialLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.MaterialLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.MaterialLoader.prototype = { + + constructor: THREE.MaterialLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.XHRLoader(); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( JSON.parse( text ) ) ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + parse: function ( json ) { + + var material = new THREE[ json.type ]; + + if ( json.color !== undefined ) material.color.setHex( json.color ); + if ( json.ambient !== undefined ) material.ambient.setHex( json.ambient ); + if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive ); + if ( json.specular !== undefined ) material.specular.setHex( json.specular ); + if ( json.shininess !== undefined ) material.shininess = json.shininess; + if ( json.uniforms !== undefined ) material.uniforms = json.uniforms; + if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader; + if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader; + if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors; + if ( json.blending !== undefined ) material.blending = json.blending; + if ( json.side !== undefined ) material.side = json.side; + if ( json.opacity !== undefined ) material.opacity = json.opacity; + if ( json.transparent !== undefined ) material.transparent = json.transparent; + if ( json.wireframe !== undefined ) material.wireframe = json.wireframe; + + if ( json.materials !== undefined ) { + + for ( var i = 0, l = json.materials.length; i < l; i ++ ) { + + material.materials.push( this.parse( json.materials[ i ] ) ); + + } + + } + + return material; + + } + +}; + +// File:src/loaders/ObjectLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.ObjectLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.ObjectLoader.prototype = { + + constructor: THREE.ObjectLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( JSON.parse( text ) ) ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + parse: function ( json ) { + + var geometries = this.parseGeometries( json.geometries ); + var materials = this.parseMaterials( json.materials ); + var object = this.parseObject( json.object, geometries, materials ); + + return object; + + }, + + parseGeometries: function ( json ) { + + var geometries = {}; + + if ( json !== undefined ) { + + var geometryLoader = new THREE.JSONLoader(); + var bufferGeometryLoader = new THREE.BufferGeometryLoader(); + + for ( var i = 0, l = json.length; i < l; i ++ ) { + + var geometry; + var data = json[ i ]; + + switch ( data.type ) { + + case 'PlaneGeometry': + + geometry = new THREE.PlaneGeometry( + data.width, + data.height, + data.widthSegments, + data.heightSegments + ); + + break; + + case 'BoxGeometry': + case 'CubeGeometry': // backwards compatible + + geometry = new THREE.BoxGeometry( + data.width, + data.height, + data.depth, + data.widthSegments, + data.heightSegments, + data.depthSegments + ); + + break; + + case 'CircleGeometry': + + geometry = new THREE.CircleGeometry( + data.radius, + data.segments + ); + + break; + + case 'CylinderGeometry': + + geometry = new THREE.CylinderGeometry( + data.radiusTop, + data.radiusBottom, + data.height, + data.radialSegments, + data.heightSegments, + data.openEnded + ); + + break; + + case 'SphereGeometry': + + geometry = new THREE.SphereGeometry( + data.radius, + data.widthSegments, + data.heightSegments, + data.phiStart, + data.phiLength, + data.thetaStart, + data.thetaLength + ); + + break; + + case 'IcosahedronGeometry': + + geometry = new THREE.IcosahedronGeometry( + data.radius, + data.detail + ); + + break; + + case 'TorusGeometry': + + geometry = new THREE.TorusGeometry( + data.radius, + data.tube, + data.radialSegments, + data.tubularSegments, + data.arc + ); + + break; + + case 'TorusKnotGeometry': + + geometry = new THREE.TorusKnotGeometry( + data.radius, + data.tube, + data.radialSegments, + data.tubularSegments, + data.p, + data.q, + data.heightScale + ); + + break; + + case 'BufferGeometry': + + geometry = bufferGeometryLoader.parse( data.data ); + + break; + + case 'Geometry': + + geometry = geometryLoader.parse( data.data ).geometry; + + break; + + } + + geometry.uuid = data.uuid; + + if ( data.name !== undefined ) geometry.name = data.name; + + geometries[ data.uuid ] = geometry; + + } + + } + + return geometries; + + }, + + parseMaterials: function ( json ) { + + var materials = {}; + + if ( json !== undefined ) { + + var loader = new THREE.MaterialLoader(); + + for ( var i = 0, l = json.length; i < l; i ++ ) { + + var data = json[ i ]; + var material = loader.parse( data ); + + material.uuid = data.uuid; + + if ( data.name !== undefined ) material.name = data.name; + + materials[ data.uuid ] = material; + + } + + } + + return materials; + + }, + + parseObject: function () { + + var matrix = new THREE.Matrix4(); + + return function ( data, geometries, materials ) { + + var object; + + switch ( data.type ) { + + case 'Scene': + + object = new THREE.Scene(); + + break; + + case 'PerspectiveCamera': + + object = new THREE.PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); + + break; + + case 'OrthographicCamera': + + object = new THREE.OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); + + break; + + case 'AmbientLight': + + object = new THREE.AmbientLight( data.color ); + + break; + + case 'DirectionalLight': + + object = new THREE.DirectionalLight( data.color, data.intensity ); + + break; + + case 'PointLight': + + object = new THREE.PointLight( data.color, data.intensity, data.distance ); + + break; + + case 'SpotLight': + + object = new THREE.SpotLight( data.color, data.intensity, data.distance, data.angle, data.exponent ); + + break; + + case 'HemisphereLight': + + object = new THREE.HemisphereLight( data.color, data.groundColor, data.intensity ); + + break; + + case 'Mesh': + + var geometry = geometries[ data.geometry ]; + var material = materials[ data.material ]; + + if ( geometry === undefined ) { + + console.error( 'THREE.ObjectLoader: Undefined geometry ' + data.geometry ); + + } + + if ( material === undefined ) { + + console.error( 'THREE.ObjectLoader: Undefined material ' + data.material ); + + } + + object = new THREE.Mesh( geometry, material ); + + break; + + case 'Sprite': + + var material = materials[ data.material ]; + + if ( material === undefined ) { + + console.error( 'THREE.ObjectLoader: Undefined material ' + data.material ); + + } + + object = new THREE.Sprite( material ); + + break; + + default: + + object = new THREE.Object3D(); + + } + + object.uuid = data.uuid; + + if ( data.name !== undefined ) object.name = data.name; + if ( data.matrix !== undefined ) { + + matrix.fromArray( data.matrix ); + matrix.decompose( object.position, object.quaternion, object.scale ); + + } else { + + if ( data.position !== undefined ) object.position.fromArray( data.position ); + if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); + if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); + + } + + if ( data.visible !== undefined ) object.visible = data.visible; + if ( data.userData !== undefined ) object.userData = data.userData; + + if ( data.children !== undefined ) { + + for ( var child in data.children ) { + + object.add( this.parseObject( data.children[ child ], geometries, materials ) ); + + } + + } + + return object; + + } + + }() + +}; + +// File:src/loaders/TextureLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.TextureLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.TextureLoader.prototype = { + + constructor: THREE.TextureLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.ImageLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( image ) { + + var texture = new THREE.Texture( image ); + texture.needsUpdate = true; + + if ( onLoad !== undefined ) { + + onLoad( texture ); + + } + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + } + +}; + +// File:src/materials/Material.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Material = function () { + + this.id = THREE.MaterialIdCount ++; + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + + this.side = THREE.FrontSide; + + this.opacity = 1; + this.transparent = false; + + this.blending = THREE.NormalBlending; + + this.blendSrc = THREE.SrcAlphaFactor; + this.blendDst = THREE.OneMinusSrcAlphaFactor; + this.blendEquation = THREE.AddEquation; + + this.depthTest = true; + this.depthWrite = true; + + this.polygonOffset = false; + this.polygonOffsetFactor = 0; + this.polygonOffsetUnits = 0; + + this.alphaTest = 0; + + this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer + + this.visible = true; + + this.needsUpdate = true; + +}; + +THREE.Material.prototype = { + + constructor: THREE.Material, + + setValues: function ( values ) { + + if ( values === undefined ) return; + + for ( var key in values ) { + + var newValue = values[ key ]; + + if ( newValue === undefined ) { + + console.warn( "THREE.Material: '" + key + "' parameter is undefined." ); + continue; + + } + + if ( key in this ) { + + var currentValue = this[ key ]; + + if ( currentValue instanceof THREE.Color ) { + + currentValue.set( newValue ); + + } else if ( currentValue instanceof THREE.Vector3 && newValue instanceof THREE.Vector3 ) { + + currentValue.copy( newValue ); + + } else if ( key == 'overdraw' ) { + + // ensure overdraw is backwards-compatable with legacy boolean type + this[ key ] = Number( newValue ); + + } else { + + this[ key ] = newValue; + + } + + } + + } + + }, + + clone: function ( material ) { + + if ( material === undefined ) material = new THREE.Material(); + + material.name = this.name; + + material.side = this.side; + + material.opacity = this.opacity; + material.transparent = this.transparent; + + material.blending = this.blending; + + material.blendSrc = this.blendSrc; + material.blendDst = this.blendDst; + material.blendEquation = this.blendEquation; + + material.depthTest = this.depthTest; + material.depthWrite = this.depthWrite; + + material.polygonOffset = this.polygonOffset; + material.polygonOffsetFactor = this.polygonOffsetFactor; + material.polygonOffsetUnits = this.polygonOffsetUnits; + + material.alphaTest = this.alphaTest; + + material.overdraw = this.overdraw; + + material.visible = this.visible; + + return material; + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.Material.prototype ); + +THREE.MaterialIdCount = 0; + +// File:src/materials/LineBasicMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * linewidth: , + * linecap: "round", + * linejoin: "round", + * + * vertexColors: + * + * fog: + * } + */ + +THREE.LineBasicMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.color = new THREE.Color( 0xffffff ); + + this.linewidth = 1; + this.linecap = 'round'; + this.linejoin = 'round'; + + this.vertexColors = THREE.NoColors; + + this.fog = true; + + this.setValues( parameters ); + +}; + +THREE.LineBasicMaterial.prototype = Object.create( THREE.Material.prototype ); + +THREE.LineBasicMaterial.prototype.clone = function () { + + var material = new THREE.LineBasicMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.color.copy( this.color ); + + material.linewidth = this.linewidth; + material.linecap = this.linecap; + material.linejoin = this.linejoin; + + material.vertexColors = this.vertexColors; + + material.fog = this.fog; + + return material; + +}; + +// File:src/materials/LineDashedMaterial.js + +/** + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * linewidth: , + * + * scale: , + * dashSize: , + * gapSize: , + * + * vertexColors: + * + * fog: + * } + */ + +THREE.LineDashedMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.color = new THREE.Color( 0xffffff ); + + this.linewidth = 1; + + this.scale = 1; + this.dashSize = 3; + this.gapSize = 1; + + this.vertexColors = false; + + this.fog = true; + + this.setValues( parameters ); + +}; + +THREE.LineDashedMaterial.prototype = Object.create( THREE.Material.prototype ); + +THREE.LineDashedMaterial.prototype.clone = function () { + + var material = new THREE.LineDashedMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.color.copy( this.color ); + + material.linewidth = this.linewidth; + + material.scale = this.scale; + material.dashSize = this.dashSize; + material.gapSize = this.gapSize; + + material.vertexColors = this.vertexColors; + + material.fog = this.fog; + + return material; + +}; + +// File:src/materials/MeshBasicMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * + * specularMap: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: , + * refractionRatio: , + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: , + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: , + * morphTargets: , + * + * fog: + * } + */ + +THREE.MeshBasicMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.color = new THREE.Color( 0xffffff ); // emissive + + this.map = null; + + this.lightMap = null; + + this.specularMap = null; + + this.alphaMap = null; + + this.envMap = null; + this.combine = THREE.MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; + + this.fog = true; + + this.shading = THREE.SmoothShading; + + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; + + this.vertexColors = THREE.NoColors; + + this.skinning = false; + this.morphTargets = false; + + this.setValues( parameters ); + +}; + +THREE.MeshBasicMaterial.prototype = Object.create( THREE.Material.prototype ); + +THREE.MeshBasicMaterial.prototype.clone = function () { + + var material = new THREE.MeshBasicMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.color.copy( this.color ); + + material.map = this.map; + + material.lightMap = this.lightMap; + + material.specularMap = this.specularMap; + + material.alphaMap = this.alphaMap; + + material.envMap = this.envMap; + material.combine = this.combine; + material.reflectivity = this.reflectivity; + material.refractionRatio = this.refractionRatio; + + material.fog = this.fog; + + material.shading = this.shading; + + material.wireframe = this.wireframe; + material.wireframeLinewidth = this.wireframeLinewidth; + material.wireframeLinecap = this.wireframeLinecap; + material.wireframeLinejoin = this.wireframeLinejoin; + + material.vertexColors = this.vertexColors; + + material.skinning = this.skinning; + material.morphTargets = this.morphTargets; + + return material; + +}; + +// File:src/materials/MeshLambertMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * ambient: , + * emissive: , + * opacity: , + * + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * + * specularMap: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: , + * refractionRatio: , + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: , + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: , + * morphTargets: , + * morphNormals: , + * + * fog: + * } + */ + +THREE.MeshLambertMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.color = new THREE.Color( 0xffffff ); // diffuse + this.ambient = new THREE.Color( 0xffffff ); + this.emissive = new THREE.Color( 0x000000 ); + + this.wrapAround = false; + this.wrapRGB = new THREE.Vector3( 1, 1, 1 ); + + this.map = null; + + this.lightMap = null; + + this.specularMap = null; + + this.alphaMap = null; + + this.envMap = null; + this.combine = THREE.MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; + + this.fog = true; + + this.shading = THREE.SmoothShading; + + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; + + this.vertexColors = THREE.NoColors; + + this.skinning = false; + this.morphTargets = false; + this.morphNormals = false; + + this.setValues( parameters ); + +}; + +THREE.MeshLambertMaterial.prototype = Object.create( THREE.Material.prototype ); + +THREE.MeshLambertMaterial.prototype.clone = function () { + + var material = new THREE.MeshLambertMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.color.copy( this.color ); + material.ambient.copy( this.ambient ); + material.emissive.copy( this.emissive ); + + material.wrapAround = this.wrapAround; + material.wrapRGB.copy( this.wrapRGB ); + + material.map = this.map; + + material.lightMap = this.lightMap; + + material.specularMap = this.specularMap; + + material.alphaMap = this.alphaMap; + + material.envMap = this.envMap; + material.combine = this.combine; + material.reflectivity = this.reflectivity; + material.refractionRatio = this.refractionRatio; + + material.fog = this.fog; + + material.shading = this.shading; + + material.wireframe = this.wireframe; + material.wireframeLinewidth = this.wireframeLinewidth; + material.wireframeLinecap = this.wireframeLinecap; + material.wireframeLinejoin = this.wireframeLinejoin; + + material.vertexColors = this.vertexColors; + + material.skinning = this.skinning; + material.morphTargets = this.morphTargets; + material.morphNormals = this.morphNormals; + + return material; + +}; + +// File:src/materials/MeshPhongMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * ambient: , + * emissive: , + * specular: , + * shininess: , + * opacity: , + * + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * + * bumpMap: new THREE.Texture( ), + * bumpScale: , + * + * normalMap: new THREE.Texture( ), + * normalScale: , + * + * specularMap: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: , + * refractionRatio: , + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: , + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: , + * morphTargets: , + * morphNormals: , + * + * fog: + * } + */ + +THREE.MeshPhongMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.color = new THREE.Color( 0xffffff ); // diffuse + this.ambient = new THREE.Color( 0xffffff ); + this.emissive = new THREE.Color( 0x000000 ); + this.specular = new THREE.Color( 0x111111 ); + this.shininess = 30; + + this.metal = false; + + this.wrapAround = false; + this.wrapRGB = new THREE.Vector3( 1, 1, 1 ); + + this.map = null; + + this.lightMap = null; + + this.bumpMap = null; + this.bumpScale = 1; + + this.normalMap = null; + this.normalScale = new THREE.Vector2( 1, 1 ); + + this.specularMap = null; + + this.alphaMap = null; + + this.envMap = null; + this.combine = THREE.MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; + + this.fog = true; + + this.shading = THREE.SmoothShading; + + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; + + this.vertexColors = THREE.NoColors; + + this.skinning = false; + this.morphTargets = false; + this.morphNormals = false; + + this.setValues( parameters ); + +}; + +THREE.MeshPhongMaterial.prototype = Object.create( THREE.Material.prototype ); + +THREE.MeshPhongMaterial.prototype.clone = function () { + + var material = new THREE.MeshPhongMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.color.copy( this.color ); + material.ambient.copy( this.ambient ); + material.emissive.copy( this.emissive ); + material.specular.copy( this.specular ); + material.shininess = this.shininess; + + material.metal = this.metal; + + material.wrapAround = this.wrapAround; + material.wrapRGB.copy( this.wrapRGB ); + + material.map = this.map; + + material.lightMap = this.lightMap; + + material.bumpMap = this.bumpMap; + material.bumpScale = this.bumpScale; + + material.normalMap = this.normalMap; + material.normalScale.copy( this.normalScale ); + + material.specularMap = this.specularMap; + + material.alphaMap = this.alphaMap; + + material.envMap = this.envMap; + material.combine = this.combine; + material.reflectivity = this.reflectivity; + material.refractionRatio = this.refractionRatio; + + material.fog = this.fog; + + material.shading = this.shading; + + material.wireframe = this.wireframe; + material.wireframeLinewidth = this.wireframeLinewidth; + material.wireframeLinecap = this.wireframeLinecap; + material.wireframeLinejoin = this.wireframeLinejoin; + + material.vertexColors = this.vertexColors; + + material.skinning = this.skinning; + material.morphTargets = this.morphTargets; + material.morphNormals = this.morphNormals; + + return material; + +}; + +// File:src/materials/MeshDepthMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * opacity: , + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: + * } + */ + +THREE.MeshDepthMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.morphTargets = false; + this.wireframe = false; + this.wireframeLinewidth = 1; + + this.setValues( parameters ); + +}; + +THREE.MeshDepthMaterial.prototype = Object.create( THREE.Material.prototype ); + +THREE.MeshDepthMaterial.prototype.clone = function () { + + var material = new THREE.MeshDepthMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.wireframe = this.wireframe; + material.wireframeLinewidth = this.wireframeLinewidth; + + return material; + +}; + +// File:src/materials/MeshNormalMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * + * parameters = { + * opacity: , + * + * shading: THREE.FlatShading, + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: + * } + */ + +THREE.MeshNormalMaterial = function ( parameters ) { + + THREE.Material.call( this, parameters ); + + this.shading = THREE.FlatShading; + + this.wireframe = false; + this.wireframeLinewidth = 1; + + this.morphTargets = false; + + this.setValues( parameters ); + +}; + +THREE.MeshNormalMaterial.prototype = Object.create( THREE.Material.prototype ); + +THREE.MeshNormalMaterial.prototype.clone = function () { + + var material = new THREE.MeshNormalMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.shading = this.shading; + + material.wireframe = this.wireframe; + material.wireframeLinewidth = this.wireframeLinewidth; + + return material; + +}; + +// File:src/materials/MeshFaceMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.MeshFaceMaterial = function ( materials ) { + + this.materials = materials instanceof Array ? materials : []; + +}; + +THREE.MeshFaceMaterial.prototype.clone = function () { + + var material = new THREE.MeshFaceMaterial(); + + for ( var i = 0; i < this.materials.length; i ++ ) { + + material.materials.push( this.materials[ i ].clone() ); + + } + + return material; + +}; + +// File:src/materials/PointCloudMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + * + * size: , + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * vertexColors: , + * + * fog: + * } + */ + +THREE.PointCloudMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.color = new THREE.Color( 0xffffff ); + + this.map = null; + + this.size = 1; + this.sizeAttenuation = true; + + this.vertexColors = THREE.NoColors; + + this.fog = true; + + this.setValues( parameters ); + +}; + +THREE.PointCloudMaterial.prototype = Object.create( THREE.Material.prototype ); + +THREE.PointCloudMaterial.prototype.clone = function () { + + var material = new THREE.PointCloudMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.color.copy( this.color ); + + material.map = this.map; + + material.size = this.size; + material.sizeAttenuation = this.sizeAttenuation; + + material.vertexColors = this.vertexColors; + + material.fog = this.fog; + + return material; + +}; + +// backwards compatibility + +THREE.ParticleBasicMaterial = function ( parameters ) { + + console.warn( 'THREE.ParticleBasicMaterial has been renamed to THREE.PointCloudMaterial.' ); + return new THREE.PointCloudMaterial( parameters ); + +}; + +THREE.ParticleSystemMaterial = function ( parameters ) { + + console.warn( 'THREE.ParticleSystemMaterial has been renamed to THREE.PointCloudMaterial.' ); + return new THREE.PointCloudMaterial( parameters ); + +}; + +// File:src/materials/ShaderMaterial.js + +/** + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * defines: { "label" : "value" }, + * uniforms: { "parameter1": { type: "f", value: 1.0 }, "parameter2": { type: "i" value2: 2 } }, + * + * fragmentShader: , + * vertexShader: , + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: , + * + * lights: , + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: , + * morphTargets: , + * morphNormals: , + * + * fog: + * } + */ + +THREE.ShaderMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.defines = {}; + this.uniforms = {}; + this.attributes = null; + + this.vertexShader = 'void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}'; + this.fragmentShader = 'void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}'; + + this.shading = THREE.SmoothShading; + + this.linewidth = 1; + + this.wireframe = false; + this.wireframeLinewidth = 1; + + this.fog = false; // set to use scene fog + + this.lights = false; // set to use scene lights + + this.vertexColors = THREE.NoColors; // set to use "color" attribute stream + + this.skinning = false; // set to use skinning attribute streams + + this.morphTargets = false; // set to use morph targets + this.morphNormals = false; // set to use morph normals + + // When rendered geometry doesn't include these attributes but the material does, + // use these default values in WebGL. This avoids errors when buffer data is missing. + this.defaultAttributeValues = { + 'color': [ 1, 1, 1 ], + 'uv': [ 0, 0 ], + 'uv2': [ 0, 0 ] + }; + + this.index0AttributeName = undefined; + + this.setValues( parameters ); + +}; + +THREE.ShaderMaterial.prototype = Object.create( THREE.Material.prototype ); + +THREE.ShaderMaterial.prototype.clone = function () { + + var material = new THREE.ShaderMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.fragmentShader = this.fragmentShader; + material.vertexShader = this.vertexShader; + + material.uniforms = THREE.UniformsUtils.clone( this.uniforms ); + + material.attributes = this.attributes; + material.defines = this.defines; + + material.shading = this.shading; + + material.wireframe = this.wireframe; + material.wireframeLinewidth = this.wireframeLinewidth; + + material.fog = this.fog; + + material.lights = this.lights; + + material.vertexColors = this.vertexColors; + + material.skinning = this.skinning; + + material.morphTargets = this.morphTargets; + material.morphNormals = this.morphNormals; + + return material; + +}; + +// File:src/materials/RawShaderMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.RawShaderMaterial = function ( parameters ) { + + THREE.ShaderMaterial.call( this, parameters ); + +}; + +THREE.RawShaderMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype ); + +THREE.RawShaderMaterial.prototype.clone = function () { + + var material = new THREE.RawShaderMaterial(); + + THREE.ShaderMaterial.prototype.clone.call( this, material ); + + return material; + +}; + +// File:src/materials/SpriteMaterial.js + +/** + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * uvOffset: new THREE.Vector2(), + * uvScale: new THREE.Vector2(), + * + * fog: + * } + */ + +THREE.SpriteMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + // defaults + + this.color = new THREE.Color( 0xffffff ); + this.map = null; + + this.rotation = 0; + + this.fog = false; + + // set parameters + + this.setValues( parameters ); + +}; + +THREE.SpriteMaterial.prototype = Object.create( THREE.Material.prototype ); + +THREE.SpriteMaterial.prototype.clone = function () { + + var material = new THREE.SpriteMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.color.copy( this.color ); + material.map = this.map; + + material.rotation = this.rotation; + + material.fog = this.fog; + + return material; + +}; + +// File:src/materials/SpriteCanvasMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * + * parameters = { + * color: , + * program: , + * opacity: , + * blending: THREE.NormalBlending + * } + */ + +THREE.SpriteCanvasMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.color = new THREE.Color( 0xffffff ); + this.program = function ( context, color ) {}; + + this.setValues( parameters ); + +}; + +THREE.SpriteCanvasMaterial.prototype = Object.create( THREE.Material.prototype ); + +THREE.SpriteCanvasMaterial.prototype.clone = function () { + + var material = new THREE.SpriteCanvasMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.color.copy( this.color ); + material.program = this.program; + + return material; + +}; + +// backwards compatibility + +THREE.ParticleCanvasMaterial = THREE.SpriteCanvasMaterial; + +// File:src/textures/Texture.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author szimek / https://github.com/szimek/ + */ + +THREE.Texture = function ( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + + this.id = THREE.TextureIdCount ++; + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + + this.image = image !== undefined ? image : THREE.Texture.DEFAULT_IMAGE; + this.mipmaps = []; + + this.mapping = mapping !== undefined ? mapping : THREE.Texture.DEFAULT_MAPPING; + + this.wrapS = wrapS !== undefined ? wrapS : THREE.ClampToEdgeWrapping; + this.wrapT = wrapT !== undefined ? wrapT : THREE.ClampToEdgeWrapping; + + this.magFilter = magFilter !== undefined ? magFilter : THREE.LinearFilter; + this.minFilter = minFilter !== undefined ? minFilter : THREE.LinearMipMapLinearFilter; + + this.anisotropy = anisotropy !== undefined ? anisotropy : 1; + + this.format = format !== undefined ? format : THREE.RGBAFormat; + this.type = type !== undefined ? type : THREE.UnsignedByteType; + + this.offset = new THREE.Vector2( 0, 0 ); + this.repeat = new THREE.Vector2( 1, 1 ); + + this.generateMipmaps = true; + this.premultiplyAlpha = false; + this.flipY = true; + this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) + + this._needsUpdate = false; + this.onUpdate = null; + +}; + +THREE.Texture.DEFAULT_IMAGE = undefined; +THREE.Texture.DEFAULT_MAPPING = new THREE.UVMapping(); + +THREE.Texture.prototype = { + + constructor: THREE.Texture, + + get needsUpdate () { + + return this._needsUpdate; + + }, + + set needsUpdate ( value ) { + + if ( value === true ) this.update(); + + this._needsUpdate = value; + + }, + + clone: function ( texture ) { + + if ( texture === undefined ) texture = new THREE.Texture(); + + texture.image = this.image; + texture.mipmaps = this.mipmaps.slice( 0 ); + + texture.mapping = this.mapping; + + texture.wrapS = this.wrapS; + texture.wrapT = this.wrapT; + + texture.magFilter = this.magFilter; + texture.minFilter = this.minFilter; + + texture.anisotropy = this.anisotropy; + + texture.format = this.format; + texture.type = this.type; + + texture.offset.copy( this.offset ); + texture.repeat.copy( this.repeat ); + + texture.generateMipmaps = this.generateMipmaps; + texture.premultiplyAlpha = this.premultiplyAlpha; + texture.flipY = this.flipY; + texture.unpackAlignment = this.unpackAlignment; + + return texture; + + }, + + update: function () { + + this.dispatchEvent( { type: 'update' } ); + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.Texture.prototype ); + +THREE.TextureIdCount = 0; + +// File:src/textures/CubeTexture.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.CubeTexture = function ( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + + THREE.Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + + this.images = images; + +}; + +THREE.CubeTexture.prototype = Object.create( THREE.Texture.prototype ); + +THREE.CubeTexture.clone = function ( texture ) { + + if ( texture === undefined ) texture = new THREE.CubeTexture(); + + THREE.Texture.prototype.clone.call( this, texture ); + + texture.images = this.images; + + return texture; + +}; + +// File:src/textures/CompressedTexture.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.CompressedTexture = function ( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) { + + THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + + this.image = { width: width, height: height }; + this.mipmaps = mipmaps; + + this.generateMipmaps = false; // WebGL currently can't generate mipmaps for compressed textures, they must be embedded in DDS file + +}; + +THREE.CompressedTexture.prototype = Object.create( THREE.Texture.prototype ); + +THREE.CompressedTexture.prototype.clone = function () { + + var texture = new THREE.CompressedTexture(); + + THREE.Texture.prototype.clone.call( this, texture ); + + return texture; + +}; + +// File:src/textures/DataTexture.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.DataTexture = function ( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) { + + THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + + this.image = { data: data, width: width, height: height }; + +}; + +THREE.DataTexture.prototype = Object.create( THREE.Texture.prototype ); + +THREE.DataTexture.prototype.clone = function () { + + var texture = new THREE.DataTexture(); + + THREE.Texture.prototype.clone.call( this, texture ); + + return texture; + +}; + +// File:src/objects/PointCloud.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.PointCloud = function ( geometry, material ) { + + THREE.Object3D.call( this ); + + this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); + this.material = material !== undefined ? material : new THREE.PointCloudMaterial( { color: Math.random() * 0xffffff } ); + + this.sortParticles = false; + +}; + +THREE.PointCloud.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.PointCloud.prototype.raycast = ( function () { + + var inverseMatrix = new THREE.Matrix4(); + var ray = new THREE.Ray(); + + return function ( raycaster, intersects ) { + + var object = this; + var geometry = object.geometry; + var threshold = raycaster.params.PointCloud.threshold; + + inverseMatrix.getInverse( this.matrixWorld ); + ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); + + if ( geometry.boundingBox !== null ) { + + if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) { + + return; + + } + + } + + var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); + var position = new THREE.Vector3(); + + var testPoint = function ( point, index ) { + + var rayPointDistance = ray.distanceToPoint( point ); + + if ( rayPointDistance < localThreshold ) { + + var intersectPoint = ray.closestPointToPoint( point ); + intersectPoint.applyMatrix4( object.matrixWorld ); + + var distance = raycaster.ray.origin.distanceTo( intersectPoint ); + + intersects.push( { + + distance: distance, + distanceToRay: rayPointDistance, + point: intersectPoint.clone(), + index: index, + face: null, + object: object + + } ); + + } + + }; + + if ( geometry instanceof THREE.BufferGeometry ) { + + var attributes = geometry.attributes; + var positions = attributes.position.array; + + if ( attributes.index !== undefined ) { + + var indices = attributes.index.array; + var offsets = geometry.offsets; + + if ( offsets.length === 0 ) { + + var offset = { + start: 0, + count: indices.length, + index: 0 + }; + + offsets = [ offset ]; + + } + + for ( var oi = 0, ol = offsets.length; oi < ol; ++oi ) { + + var start = offsets[ oi ].start; + var count = offsets[ oi ].count; + var index = offsets[ oi ].index; + + for ( var i = start, il = start + count; i < il; i ++ ) { + + var a = index + indices[ i ]; + + position.set( + positions[ a * 3 ], + positions[ a * 3 + 1 ], + positions[ a * 3 + 2 ] + ); + + testPoint( position, a ); + + } + + } + + } else { + + var pointCount = positions.length / 3; + + for ( var i = 0; i < pointCount; i ++ ) { + + position.set( + positions[ 3 * i ], + positions[ 3 * i + 1 ], + positions[ 3 * i + 2 ] + ); + + testPoint( position, i ); + + } + + } + + } else { + + var vertices = this.geometry.vertices; + + for ( var i = 0; i < vertices.length; i ++ ) { + + testPoint( vertices[ i ], i ); + + } + + } + + }; + +}() ); + +THREE.PointCloud.prototype.clone = function ( object ) { + + if ( object === undefined ) object = new THREE.PointCloud( this.geometry, this.material ); + + object.sortParticles = this.sortParticles; + + THREE.Object3D.prototype.clone.call( this, object ); + + return object; + +}; + +// Backwards compatibility + +THREE.ParticleSystem = function ( geometry, material ) { + + console.warn( 'THREE.ParticleSystem has been renamed to THREE.PointCloud.' ); + return new THREE.PointCloud( geometry, material ); + +}; + +// File:src/objects/Line.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Line = function ( geometry, material, type ) { + + THREE.Object3D.call( this ); + + this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); + this.material = material !== undefined ? material : new THREE.LineBasicMaterial( { color: Math.random() * 0xffffff } ); + + this.type = ( type !== undefined ) ? type : THREE.LineStrip; + +}; + +THREE.LineStrip = 0; +THREE.LinePieces = 1; + +THREE.Line.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.Line.prototype.raycast = ( function () { + + var inverseMatrix = new THREE.Matrix4(); + var ray = new THREE.Ray(); + var sphere = new THREE.Sphere(); + + return function ( raycaster, intersects ) { + + var precision = raycaster.linePrecision; + var precisionSq = precision * precision; + + var geometry = this.geometry; + + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + + // Checking boundingSphere distance to ray + + sphere.copy( geometry.boundingSphere ); + sphere.applyMatrix4( this.matrixWorld ); + + if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) { + + return; + + } + + inverseMatrix.getInverse( this.matrixWorld ); + ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); + + /* if ( geometry instanceof THREE.BufferGeometry ) { + + } else */ if ( geometry instanceof THREE.Geometry ) { + + var vertices = geometry.vertices; + var nbVertices = vertices.length; + var interSegment = new THREE.Vector3(); + var interRay = new THREE.Vector3(); + var step = this.type === THREE.LineStrip ? 1 : 2; + + for ( var i = 0; i < nbVertices - 1; i = i + step ) { + + var distSq = ray.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment ); + + if ( distSq > precisionSq ) continue; + + var distance = ray.origin.distanceTo( interRay ); + + if ( distance < raycaster.near || distance > raycaster.far ) continue; + + intersects.push( { + + distance: distance, + // What do we want? intersection point on the ray or on the segment?? + // point: raycaster.ray.at( distance ), + point: interSegment.clone().applyMatrix4( this.matrixWorld ), + face: null, + faceIndex: null, + object: this + + } ); + + } + + } + + }; + +}() ); + +THREE.Line.prototype.clone = function ( object ) { + + if ( object === undefined ) object = new THREE.Line( this.geometry, this.material, this.type ); + + THREE.Object3D.prototype.clone.call( this, object ); + + return object; + +}; + +// File:src/objects/Mesh.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author mikael emtinger / http://gomo.se/ + * @author jonobr1 / http://jonobr1.com/ + */ + +THREE.Mesh = function ( geometry, material ) { + + THREE.Object3D.call( this ); + + this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); + this.material = material !== undefined ? material : new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff } ); + + this.updateMorphTargets(); + +}; + +THREE.Mesh.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.Mesh.prototype.updateMorphTargets = function () { + + if ( this.geometry.morphTargets !== undefined && this.geometry.morphTargets.length > 0 ) { + + this.morphTargetBase = - 1; + this.morphTargetForcedOrder = []; + this.morphTargetInfluences = []; + this.morphTargetDictionary = {}; + + for ( var m = 0, ml = this.geometry.morphTargets.length; m < ml; m ++ ) { + + this.morphTargetInfluences.push( 0 ); + this.morphTargetDictionary[ this.geometry.morphTargets[ m ].name ] = m; + + } + + } + +}; + +THREE.Mesh.prototype.getMorphTargetIndexByName = function ( name ) { + + if ( this.morphTargetDictionary[ name ] !== undefined ) { + + return this.morphTargetDictionary[ name ]; + + } + + console.log( 'THREE.Mesh.getMorphTargetIndexByName: morph target ' + name + ' does not exist. Returning 0.' ); + + return 0; + +}; + + +THREE.Mesh.prototype.raycast = ( function () { + + var inverseMatrix = new THREE.Matrix4(); + var ray = new THREE.Ray(); + var sphere = new THREE.Sphere(); + + var vA = new THREE.Vector3(); + var vB = new THREE.Vector3(); + var vC = new THREE.Vector3(); + + return function ( raycaster, intersects ) { + + var geometry = this.geometry; + + // Checking boundingSphere distance to ray + + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + + sphere.copy( geometry.boundingSphere ); + sphere.applyMatrix4( this.matrixWorld ); + + if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) { + + return; + + } + + // Check boundingBox before continuing + + inverseMatrix.getInverse( this.matrixWorld ); + ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); + + if ( geometry.boundingBox !== null ) { + + if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) { + + return; + + } + + } + + if ( geometry instanceof THREE.BufferGeometry ) { + + var material = this.material; + + if ( material === undefined ) return; + + var attributes = geometry.attributes; + + var a, b, c; + var precision = raycaster.precision; + + if ( attributes.index !== undefined ) { + + var indices = attributes.index.array; + var positions = attributes.position.array; + var offsets = geometry.offsets; + + if ( offsets.length === 0 ) { + + offsets = [ { start: 0, count: indices.length, index: 0 } ]; + + } + + for ( var oi = 0, ol = offsets.length; oi < ol; ++oi ) { + + var start = offsets[ oi ].start; + var count = offsets[ oi ].count; + var index = offsets[ oi ].index; + + for ( var i = start, il = start + count; i < il; i += 3 ) { + + a = index + indices[ i ]; + b = index + indices[ i + 1 ]; + c = index + indices[ i + 2 ]; + + vA.set( + positions[ a * 3 ], + positions[ a * 3 + 1 ], + positions[ a * 3 + 2 ] + ); + vB.set( + positions[ b * 3 ], + positions[ b * 3 + 1 ], + positions[ b * 3 + 2 ] + ); + vC.set( + positions[ c * 3 ], + positions[ c * 3 + 1 ], + positions[ c * 3 + 2 ] + ); + + + if ( material.side === THREE.BackSide ) { + + var intersectionPoint = ray.intersectTriangle( vC, vB, vA, true ); + + } else { + + var intersectionPoint = ray.intersectTriangle( vA, vB, vC, material.side !== THREE.DoubleSide ); + + } + + if ( intersectionPoint === null ) continue; + + intersectionPoint.applyMatrix4( this.matrixWorld ); + + var distance = raycaster.ray.origin.distanceTo( intersectionPoint ); + + if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue; + + intersects.push( { + + distance: distance, + point: intersectionPoint, + indices: [ a, b, c ], + face: null, + faceIndex: null, + object: this + + } ); + + } + + } + + } else { + + var positions = attributes.position.array; + + for ( var i = 0, j = 0, il = positions.length; i < il; i += 3, j += 9 ) { + + a = i; + b = i + 1; + c = i + 2; + + vA.set( + positions[ j ], + positions[ j + 1 ], + positions[ j + 2 ] + ); + vB.set( + positions[ j + 3 ], + positions[ j + 4 ], + positions[ j + 5 ] + ); + vC.set( + positions[ j + 6 ], + positions[ j + 7 ], + positions[ j + 8 ] + ); + + + if ( material.side === THREE.BackSide ) { + + var intersectionPoint = ray.intersectTriangle( vC, vB, vA, true ); + + } else { + + var intersectionPoint = ray.intersectTriangle( vA, vB, vC, material.side !== THREE.DoubleSide ); + + } + + if ( intersectionPoint === null ) continue; + + intersectionPoint.applyMatrix4( this.matrixWorld ); + + var distance = raycaster.ray.origin.distanceTo( intersectionPoint ); + + if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue; + + intersects.push( { + + distance: distance, + point: intersectionPoint, + indices: [ a, b, c ], + face: null, + faceIndex: null, + object: this + + } ); + + } + + } + + } else if ( geometry instanceof THREE.Geometry ) { + + var isFaceMaterial = this.material instanceof THREE.MeshFaceMaterial; + var objectMaterials = isFaceMaterial === true ? this.material.materials : null; + + var a, b, c, d; + var precision = raycaster.precision; + + var vertices = geometry.vertices; + + for ( var f = 0, fl = geometry.faces.length; f < fl; f ++ ) { + + var face = geometry.faces[ f ]; + + var material = isFaceMaterial === true ? objectMaterials[ face.materialIndex ] : this.material; + + if ( material === undefined ) continue; + + a = vertices[ face.a ]; + b = vertices[ face.b ]; + c = vertices[ face.c ]; + + if ( material.morphTargets === true ) { + + var morphTargets = geometry.morphTargets; + var morphInfluences = this.morphTargetInfluences; + + vA.set( 0, 0, 0 ); + vB.set( 0, 0, 0 ); + vC.set( 0, 0, 0 ); + + for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { + + var influence = morphInfluences[ t ]; + + if ( influence === 0 ) continue; + + var targets = morphTargets[ t ].vertices; + + vA.x += ( targets[ face.a ].x - a.x ) * influence; + vA.y += ( targets[ face.a ].y - a.y ) * influence; + vA.z += ( targets[ face.a ].z - a.z ) * influence; + + vB.x += ( targets[ face.b ].x - b.x ) * influence; + vB.y += ( targets[ face.b ].y - b.y ) * influence; + vB.z += ( targets[ face.b ].z - b.z ) * influence; + + vC.x += ( targets[ face.c ].x - c.x ) * influence; + vC.y += ( targets[ face.c ].y - c.y ) * influence; + vC.z += ( targets[ face.c ].z - c.z ) * influence; + + } + + vA.add( a ); + vB.add( b ); + vC.add( c ); + + a = vA; + b = vB; + c = vC; + + } + + if ( material.side === THREE.BackSide ) { + + var intersectionPoint = ray.intersectTriangle( c, b, a, true ); + + } else { + + var intersectionPoint = ray.intersectTriangle( a, b, c, material.side !== THREE.DoubleSide ); + + } + + if ( intersectionPoint === null ) continue; + + intersectionPoint.applyMatrix4( this.matrixWorld ); + + var distance = raycaster.ray.origin.distanceTo( intersectionPoint ); + + if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue; + + intersects.push( { + + distance: distance, + point: intersectionPoint, + face: face, + faceIndex: f, + object: this + + } ); + + } + + } + + }; + +}() ); + +THREE.Mesh.prototype.clone = function ( object, recursive ) { + + if ( object === undefined ) object = new THREE.Mesh( this.geometry, this.material ); + + THREE.Object3D.prototype.clone.call( this, object, recursive ); + + return object; + +}; + +// File:src/objects/Bone.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author ikerr / http://verold.com + */ + +THREE.Bone = function ( belongsToSkin ) { + + THREE.Object3D.call( this ); + + this.skin = belongsToSkin; + + this.accumulatedRotWeight = 0; + this.accumulatedPosWeight = 0; + this.accumulatedSclWeight = 0; + +}; + +THREE.Bone.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.Bone.prototype.updateMatrixWorld = function ( force ) { + + THREE.Object3D.prototype.updateMatrixWorld.call( this, force ); + + // Reset weights to be re-accumulated in the next frame + + this.accumulatedRotWeight = 0; + this.accumulatedPosWeight = 0; + this.accumulatedSclWeight = 0; + +}; + + +// File:src/objects/Skeleton.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author michael guerrero / http://realitymeltdown.com + * @author ikerr / http://verold.com + */ + +THREE.Skeleton = function ( bones, boneInverses, useVertexTexture ) { + + this.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true; + + this.identityMatrix = new THREE.Matrix4(); + + // copy the bone array + + bones = bones || []; + + this.bones = bones.slice( 0 ); + + // create a bone texture or an array of floats + + if ( this.useVertexTexture ) { + + // layout (1 matrix = 4 pixels) + // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) + // with 8x8 pixel texture max 16 bones (8 * 8 / 4) + // 16x16 pixel texture max 64 bones (16 * 16 / 4) + // 32x32 pixel texture max 256 bones (32 * 32 / 4) + // 64x64 pixel texture max 1024 bones (64 * 64 / 4) + + var size; + + if ( this.bones.length > 256 ) + size = 64; + else if ( this.bones.length > 64 ) + size = 32; + else if ( this.bones.length > 16 ) + size = 16; + else + size = 8; + + this.boneTextureWidth = size; + this.boneTextureHeight = size; + + this.boneMatrices = new Float32Array( this.boneTextureWidth * this.boneTextureHeight * 4 ); // 4 floats per RGBA pixel + this.boneTexture = new THREE.DataTexture( this.boneMatrices, this.boneTextureWidth, this.boneTextureHeight, THREE.RGBAFormat, THREE.FloatType ); + this.boneTexture.minFilter = THREE.NearestFilter; + this.boneTexture.magFilter = THREE.NearestFilter; + this.boneTexture.generateMipmaps = false; + this.boneTexture.flipY = false; + + } else { + + this.boneMatrices = new Float32Array( 16 * this.bones.length ); + + } + + // use the supplied bone inverses or calculate the inverses + + if ( boneInverses === undefined ) { + + this.calculateInverses(); + + } else { + + if ( this.bones.length === boneInverses.length ) { + + this.boneInverses = boneInverses.slice( 0 ); + + } else { + + console.warn( 'THREE.Skeleton bonInverses is the wrong length.' ); + + this.boneInverses = []; + + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + + this.boneInverses.push( new THREE.Matrix4() ); + + } + + } + + } + +}; + +THREE.Skeleton.prototype.calculateInverses = function () { + + this.boneInverses = []; + + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + + var inverse = new THREE.Matrix4(); + + if ( this.bones[ b ] ) { + + inverse.getInverse( this.bones[ b ].matrixWorld ); + + } + + this.boneInverses.push( inverse ); + + } + +}; + +THREE.Skeleton.prototype.pose = function () { + + var bone; + + // recover the bind-time world matrices + + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + + bone = this.bones[ b ]; + + if ( bone ) { + + bone.matrixWorld.getInverse( this.boneInverses[ b ] ); + + } + + } + + // compute the local matrices, positions, rotations and scales + + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + + bone = this.bones[ b ]; + + if ( bone ) { + + if ( bone.parent ) { + + bone.matrix.getInverse( bone.parent.matrixWorld ); + bone.matrix.multiply( bone.matrixWorld ); + + } + else { + + bone.matrix.copy( bone.matrixWorld ); + + } + + bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); + + } + + } + +}; + +THREE.Skeleton.prototype.update = function () { + + var offsetMatrix = new THREE.Matrix4(); + + // flatten bone matrices to array + + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + + // compute the offset between the current and the original transform + + var matrix = this.bones[ b ] ? this.bones[ b ].matrixWorld : this.identityMatrix; + + offsetMatrix.multiplyMatrices( matrix, this.boneInverses[ b ] ); + offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 ); + + } + + if ( this.useVertexTexture ) { + + this.boneTexture.needsUpdate = true; + + } + +}; + + +// File:src/objects/SkinnedMesh.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author ikerr / http://verold.com + */ + +THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) { + + THREE.Mesh.call( this, geometry, material ); + + this.bindMode = "attached"; + this.bindMatrix = new THREE.Matrix4(); + this.bindMatrixInverse = new THREE.Matrix4(); + + // init bones + + // TODO: remove bone creation as there is no reason (other than + // convenience) for THREE.SkinnedMesh to do this. + + var bones = []; + + if ( this.geometry && this.geometry.bones !== undefined ) { + + var bone, gbone, p, q, s; + + for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++b ) { + + gbone = this.geometry.bones[ b ]; + + p = gbone.pos; + q = gbone.rotq; + s = gbone.scl; + + bone = new THREE.Bone( this ); + bones.push( bone ); + + bone.name = gbone.name; + bone.position.set( p[ 0 ], p[ 1 ], p[ 2 ] ); + bone.quaternion.set( q[ 0 ], q[ 1 ], q[ 2 ], q[ 3 ] ); + + if ( s !== undefined ) { + + bone.scale.set( s[ 0 ], s[ 1 ], s[ 2 ] ); + + } else { + + bone.scale.set( 1, 1, 1 ); + + } + + } + + for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++b ) { + + gbone = this.geometry.bones[ b ]; + + if ( gbone.parent !== - 1 ) { + + bones[ gbone.parent ].add( bones[ b ] ); + + } else { + + this.add( bones[ b ] ); + + } + + } + + } + + this.normalizeSkinWeights(); + + this.updateMatrixWorld( true ); + this.bind( new THREE.Skeleton( bones, undefined, useVertexTexture ) ); + +}; + + +THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype ); + +THREE.SkinnedMesh.prototype.bind = function( skeleton, bindMatrix ) { + + this.skeleton = skeleton; + + if ( bindMatrix === undefined ) { + + this.updateMatrixWorld( true ); + + bindMatrix = this.matrixWorld; + + } + + this.bindMatrix.copy( bindMatrix ); + this.bindMatrixInverse.getInverse( bindMatrix ); + +}; + +THREE.SkinnedMesh.prototype.pose = function () { + + this.skeleton.pose(); + +}; + +THREE.SkinnedMesh.prototype.normalizeSkinWeights = function () { + + if ( this.geometry instanceof THREE.Geometry ) { + + for ( var i = 0; i < this.geometry.skinIndices.length; i ++ ) { + + var sw = this.geometry.skinWeights[ i ]; + + var scale = 1.0 / sw.lengthManhattan(); + + if ( scale !== Infinity ) { + + sw.multiplyScalar( scale ); + + } else { + + sw.set( 1 ); // this will be normalized by the shader anyway + + } + + } + + } else { + + // skinning weights assumed to be normalized for THREE.BufferGeometry + + } + +}; + +THREE.SkinnedMesh.prototype.updateMatrixWorld = function( force ) { + + THREE.Mesh.prototype.updateMatrixWorld.call( this, true ); + + if ( this.bindMode === "attached" ) { + + this.bindMatrixInverse.getInverse( this.matrixWorld ); + + } else if ( this.bindMode === "detached" ) { + + this.bindMatrixInverse.getInverse( this.bindMatrix ); + + } else { + + console.warn( 'THREE.SkinnedMesh unreckognized bindMode: ' + this.bindMode ); + + } + +}; + +THREE.SkinnedMesh.prototype.clone = function( object ) { + + if ( object === undefined ) { + + object = new THREE.SkinnedMesh( this.geometry, this.material, this.useVertexTexture ); + + } + + THREE.Mesh.prototype.clone.call( this, object ); + + return object; + +}; + + +// File:src/objects/MorphAnimMesh.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.MorphAnimMesh = function ( geometry, material ) { + + THREE.Mesh.call( this, geometry, material ); + + // API + + this.duration = 1000; // milliseconds + this.mirroredLoop = false; + this.time = 0; + + // internals + + this.lastKeyframe = 0; + this.currentKeyframe = 0; + + this.direction = 1; + this.directionBackwards = false; + + this.setFrameRange( 0, this.geometry.morphTargets.length - 1 ); + +}; + +THREE.MorphAnimMesh.prototype = Object.create( THREE.Mesh.prototype ); + +THREE.MorphAnimMesh.prototype.setFrameRange = function ( start, end ) { + + this.startKeyframe = start; + this.endKeyframe = end; + + this.length = this.endKeyframe - this.startKeyframe + 1; + +}; + +THREE.MorphAnimMesh.prototype.setDirectionForward = function () { + + this.direction = 1; + this.directionBackwards = false; + +}; + +THREE.MorphAnimMesh.prototype.setDirectionBackward = function () { + + this.direction = - 1; + this.directionBackwards = true; + +}; + +THREE.MorphAnimMesh.prototype.parseAnimations = function () { + + var geometry = this.geometry; + + if ( ! geometry.animations ) geometry.animations = {}; + + var firstAnimation, animations = geometry.animations; + + var pattern = /([a-z]+)_?(\d+)/; + + for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) { + + var morph = geometry.morphTargets[ i ]; + var parts = morph.name.match( pattern ); + + if ( parts && parts.length > 1 ) { + + var label = parts[ 1 ]; + var num = parts[ 2 ]; + + if ( ! animations[ label ] ) animations[ label ] = { start: Infinity, end: - Infinity }; + + var animation = animations[ label ]; + + if ( i < animation.start ) animation.start = i; + if ( i > animation.end ) animation.end = i; + + if ( ! firstAnimation ) firstAnimation = label; + + } + + } + + geometry.firstAnimation = firstAnimation; + +}; + +THREE.MorphAnimMesh.prototype.setAnimationLabel = function ( label, start, end ) { + + if ( ! this.geometry.animations ) this.geometry.animations = {}; + + this.geometry.animations[ label ] = { start: start, end: end }; + +}; + +THREE.MorphAnimMesh.prototype.playAnimation = function ( label, fps ) { + + var animation = this.geometry.animations[ label ]; + + if ( animation ) { + + this.setFrameRange( animation.start, animation.end ); + this.duration = 1000 * ( ( animation.end - animation.start ) / fps ); + this.time = 0; + + } else { + + console.warn( 'animation[' + label + '] undefined' ); + + } + +}; + +THREE.MorphAnimMesh.prototype.updateAnimation = function ( delta ) { + + var frameTime = this.duration / this.length; + + this.time += this.direction * delta; + + if ( this.mirroredLoop ) { + + if ( this.time > this.duration || this.time < 0 ) { + + this.direction *= - 1; + + if ( this.time > this.duration ) { + + this.time = this.duration; + this.directionBackwards = true; + + } + + if ( this.time < 0 ) { + + this.time = 0; + this.directionBackwards = false; + + } + + } + + } else { + + this.time = this.time % this.duration; + + if ( this.time < 0 ) this.time += this.duration; + + } + + var keyframe = this.startKeyframe + THREE.Math.clamp( Math.floor( this.time / frameTime ), 0, this.length - 1 ); + + if ( keyframe !== this.currentKeyframe ) { + + this.morphTargetInfluences[ this.lastKeyframe ] = 0; + this.morphTargetInfluences[ this.currentKeyframe ] = 1; + + this.morphTargetInfluences[ keyframe ] = 0; + + this.lastKeyframe = this.currentKeyframe; + this.currentKeyframe = keyframe; + + } + + var mix = ( this.time % frameTime ) / frameTime; + + if ( this.directionBackwards ) { + + mix = 1 - mix; + + } + + this.morphTargetInfluences[ this.currentKeyframe ] = mix; + this.morphTargetInfluences[ this.lastKeyframe ] = 1 - mix; + +}; + +THREE.MorphAnimMesh.prototype.interpolateTargets = function ( a, b, t ) { + + var influences = this.morphTargetInfluences; + + for ( var i = 0, l = influences.length; i < l; i ++ ) { + + influences[ i ] = 0; + + } + + if ( a > -1 ) influences[ a ] = 1 - t; + if ( b > -1 ) influences[ b ] = t; + +}; + +THREE.MorphAnimMesh.prototype.clone = function ( object ) { + + if ( object === undefined ) object = new THREE.MorphAnimMesh( this.geometry, this.material ); + + object.duration = this.duration; + object.mirroredLoop = this.mirroredLoop; + object.time = this.time; + + object.lastKeyframe = this.lastKeyframe; + object.currentKeyframe = this.currentKeyframe; + + object.direction = this.direction; + object.directionBackwards = this.directionBackwards; + + THREE.Mesh.prototype.clone.call( this, object ); + + return object; + +}; + +// File:src/objects/LOD.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.LOD = function () { + + THREE.Object3D.call( this ); + + this.objects = []; + +}; + + +THREE.LOD.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.LOD.prototype.addLevel = function ( object, distance ) { + + if ( distance === undefined ) distance = 0; + + distance = Math.abs( distance ); + + for ( var l = 0; l < this.objects.length; l ++ ) { + + if ( distance < this.objects[ l ].distance ) { + + break; + + } + + } + + this.objects.splice( l, 0, { distance: distance, object: object } ); + this.add( object ); + +}; + +THREE.LOD.prototype.getObjectForDistance = function ( distance ) { + + for ( var i = 1, l = this.objects.length; i < l; i ++ ) { + + if ( distance < this.objects[ i ].distance ) { + + break; + + } + + } + + return this.objects[ i - 1 ].object; + +}; + +THREE.LOD.prototype.raycast = ( function () { + + var matrixPosition = new THREE.Vector3(); + + return function ( raycaster, intersects ) { + + matrixPosition.setFromMatrixPosition( this.matrixWorld ); + + var distance = raycaster.ray.origin.distanceTo( matrixPosition ); + + this.getObjectForDistance( distance ).raycast( raycaster, intersects ); + + }; + +}() ); + +THREE.LOD.prototype.update = function () { + + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + + return function ( camera ) { + + if ( this.objects.length > 1 ) { + + v1.setFromMatrixPosition( camera.matrixWorld ); + v2.setFromMatrixPosition( this.matrixWorld ); + + var distance = v1.distanceTo( v2 ); + + this.objects[ 0 ].object.visible = true; + + for ( var i = 1, l = this.objects.length; i < l; i ++ ) { + + if ( distance >= this.objects[ i ].distance ) { + + this.objects[ i - 1 ].object.visible = false; + this.objects[ i ].object.visible = true; + + } else { + + break; + + } + + } + + for ( ; i < l; i ++ ) { + + this.objects[ i ].object.visible = false; + + } + + } + + }; + +}(); + +THREE.LOD.prototype.clone = function ( object ) { + + if ( object === undefined ) object = new THREE.LOD(); + + THREE.Object3D.prototype.clone.call( this, object ); + + for ( var i = 0, l = this.objects.length; i < l; i ++ ) { + var x = this.objects[ i ].object.clone(); + x.visible = i === 0; + object.addLevel( x, this.objects[ i ].distance ); + } + + return object; + +}; + +// File:src/objects/Sprite.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Sprite = ( function () { + + var vertices = new Float32Array( [ - 0.5, - 0.5, 0, 0.5, - 0.5, 0, 0.5, 0.5, 0 ] ); + + var geometry = new THREE.BufferGeometry(); + geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + + return function ( material ) { + + THREE.Object3D.call( this ); + + this.geometry = geometry; + this.material = ( material !== undefined ) ? material : new THREE.SpriteMaterial(); + + }; + +} )(); + +THREE.Sprite.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.Sprite.prototype.raycast = ( function () { + + var matrixPosition = new THREE.Vector3(); + + return function ( raycaster, intersects ) { + + matrixPosition.setFromMatrixPosition( this.matrixWorld ); + + var distance = raycaster.ray.distanceToPoint( matrixPosition ); + + if ( distance > this.scale.x ) { + + return; + + } + + intersects.push( { + + distance: distance, + point: this.position, + face: null, + object: this + + } ); + + }; + +}() ); + +THREE.Sprite.prototype.updateMatrix = function () { + + this.matrix.compose( this.position, this.quaternion, this.scale ); + + this.matrixWorldNeedsUpdate = true; + +}; + +THREE.Sprite.prototype.clone = function ( object ) { + + if ( object === undefined ) object = new THREE.Sprite( this.material ); + + THREE.Object3D.prototype.clone.call( this, object ); + + return object; + +}; + +// Backwards compatibility + +THREE.Particle = THREE.Sprite; + +// File:src/scenes/Scene.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Scene = function () { + + THREE.Object3D.call( this ); + + this.fog = null; + this.overrideMaterial = null; + + this.autoUpdate = true; // checked by the renderer + this.matrixAutoUpdate = false; + + this.__lights = []; + + this.__objectsAdded = []; + this.__objectsRemoved = []; + +}; + +THREE.Scene.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.Scene.prototype.__addObject = function ( object ) { + + if ( object instanceof THREE.Light ) { + + if ( this.__lights.indexOf( object ) === - 1 ) { + + this.__lights.push( object ); + + } + + if ( object.target && object.target.parent === undefined ) { + + this.add( object.target ); + + } + + } else if ( ! ( object instanceof THREE.Camera || object instanceof THREE.Bone ) ) { + + this.__objectsAdded.push( object ); + + // check if previously removed + + var i = this.__objectsRemoved.indexOf( object ); + + if ( i !== - 1 ) { + + this.__objectsRemoved.splice( i, 1 ); + + } + + } + + this.dispatchEvent( { type: 'objectAdded', object: object } ); + object.dispatchEvent( { type: 'addedToScene', scene: this } ); + + for ( var c = 0; c < object.children.length; c ++ ) { + + this.__addObject( object.children[ c ] ); + + } + +}; + +THREE.Scene.prototype.__removeObject = function ( object ) { + + if ( object instanceof THREE.Light ) { + + var i = this.__lights.indexOf( object ); + + if ( i !== - 1 ) { + + this.__lights.splice( i, 1 ); + + } + + if ( object.shadowCascadeArray ) { + + for ( var x = 0; x < object.shadowCascadeArray.length; x ++ ) { + + this.__removeObject( object.shadowCascadeArray[ x ] ); + + } + + } + + } else if ( ! ( object instanceof THREE.Camera ) ) { + + this.__objectsRemoved.push( object ); + + // check if previously added + + var i = this.__objectsAdded.indexOf( object ); + + if ( i !== - 1 ) { + + this.__objectsAdded.splice( i, 1 ); + + } + + } + + this.dispatchEvent( { type: 'objectRemoved', object: object } ); + object.dispatchEvent( { type: 'removedFromScene', scene: this } ); + + for ( var c = 0; c < object.children.length; c ++ ) { + + this.__removeObject( object.children[ c ] ); + + } + +}; + +THREE.Scene.prototype.clone = function ( object ) { + + if ( object === undefined ) object = new THREE.Scene(); + + THREE.Object3D.prototype.clone.call( this, object ); + + if ( this.fog !== null ) object.fog = this.fog.clone(); + if ( this.overrideMaterial !== null ) object.overrideMaterial = this.overrideMaterial.clone(); + + object.autoUpdate = this.autoUpdate; + object.matrixAutoUpdate = this.matrixAutoUpdate; + + return object; + +}; + +// File:src/scenes/Fog.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Fog = function ( color, near, far ) { + + this.name = ''; + + this.color = new THREE.Color( color ); + + this.near = ( near !== undefined ) ? near : 1; + this.far = ( far !== undefined ) ? far : 1000; + +}; + +THREE.Fog.prototype.clone = function () { + + return new THREE.Fog( this.color.getHex(), this.near, this.far ); + +}; + +// File:src/scenes/FogExp2.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.FogExp2 = function ( color, density ) { + + this.name = ''; + + this.color = new THREE.Color( color ); + this.density = ( density !== undefined ) ? density : 0.00025; + +}; + +THREE.FogExp2.prototype.clone = function () { + + return new THREE.FogExp2( this.color.getHex(), this.density ); + +}; + +// File:src/renderers/CanvasRenderer.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.CanvasRenderer = function ( parameters ) { + + console.log( 'THREE.CanvasRenderer', THREE.REVISION ); + + var smoothstep = THREE.Math.smoothstep; + + parameters = parameters || {}; + + var _this = this, + _renderData, _elements, _lights, + _projector = new THREE.Projector(), + + _canvas = parameters.canvas !== undefined + ? parameters.canvas + : document.createElement( 'canvas' ), + + _canvasWidth = _canvas.width, + _canvasHeight = _canvas.height, + _canvasWidthHalf = Math.floor( _canvasWidth / 2 ), + _canvasHeightHalf = Math.floor( _canvasHeight / 2 ), + + _viewportX = 0, + _viewportY = 0, + _viewportWidth = _canvasWidth, + _viewportHeight = _canvasHeight, + + _context = _canvas.getContext( '2d', { + alpha: parameters.alpha === true + } ), + + _clearColor = new THREE.Color( 0x000000 ), + _clearAlpha = 0, + + _contextGlobalAlpha = 1, + _contextGlobalCompositeOperation = 0, + _contextStrokeStyle = null, + _contextFillStyle = null, + _contextLineWidth = null, + _contextLineCap = null, + _contextLineJoin = null, + _contextLineDash = [], + + _camera, + + _v1, _v2, _v3, _v4, + _v5 = new THREE.RenderableVertex(), + _v6 = new THREE.RenderableVertex(), + + _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, + _v4x, _v4y, _v5x, _v5y, _v6x, _v6y, + + _color = new THREE.Color(), + _color1 = new THREE.Color(), + _color2 = new THREE.Color(), + _color3 = new THREE.Color(), + _color4 = new THREE.Color(), + + _diffuseColor = new THREE.Color(), + _emissiveColor = new THREE.Color(), + + _lightColor = new THREE.Color(), + + _patterns = {}, + + _image, _uvs, + _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, + + _clipBox = new THREE.Box2(), + _clearBox = new THREE.Box2(), + _elemBox = new THREE.Box2(), + + _ambientLight = new THREE.Color(), + _directionalLights = new THREE.Color(), + _pointLights = new THREE.Color(), + + _vector3 = new THREE.Vector3(), // Needed for PointLight + _centroid = new THREE.Vector3(), + _normal = new THREE.Vector3(), + _normalViewMatrix = new THREE.Matrix3(); + + // dash+gap fallbacks for Firefox and everything else + + if ( _context.setLineDash === undefined ) { + + _context.setLineDash = function () {} + + } + + this.domElement = _canvas; + + this.devicePixelRatio = parameters.devicePixelRatio !== undefined + ? parameters.devicePixelRatio + : self.devicePixelRatio !== undefined + ? self.devicePixelRatio + : 1; + + this.autoClear = true; + this.sortObjects = true; + this.sortElements = true; + + this.info = { + + render: { + + vertices: 0, + faces: 0 + + } + + } + + // WebGLRenderer compatibility + + this.supportsVertexTextures = function () {}; + this.setFaceCulling = function () {}; + + this.setSize = function ( width, height, updateStyle ) { + + _canvasWidth = width * this.devicePixelRatio; + _canvasHeight = height * this.devicePixelRatio; + + _canvas.width = _canvasWidth; + _canvas.height = _canvasHeight; + + _canvasWidthHalf = Math.floor( _canvasWidth / 2 ); + _canvasHeightHalf = Math.floor( _canvasHeight / 2 ); + + if ( updateStyle !== false ) { + + _canvas.style.width = width + 'px'; + _canvas.style.height = height + 'px'; + + } + + _clipBox.min.set( -_canvasWidthHalf, -_canvasHeightHalf ), + _clipBox.max.set( _canvasWidthHalf, _canvasHeightHalf ); + + _clearBox.min.set( - _canvasWidthHalf, - _canvasHeightHalf ); + _clearBox.max.set( _canvasWidthHalf, _canvasHeightHalf ); + + _contextGlobalAlpha = 1; + _contextGlobalCompositeOperation = 0; + _contextStrokeStyle = null; + _contextFillStyle = null; + _contextLineWidth = null; + _contextLineCap = null; + _contextLineJoin = null; + + this.setViewport( 0, 0, width, height ); + + }; + + this.setViewport = function ( x, y, width, height ) { + + _viewportX = x * this.devicePixelRatio; + _viewportY = y * this.devicePixelRatio; + + _viewportWidth = width * this.devicePixelRatio; + _viewportHeight = height * this.devicePixelRatio; + + }; + + this.setScissor = function () {}; + this.enableScissorTest = function () {}; + + this.setClearColor = function ( color, alpha ) { + + _clearColor.set( color ); + _clearAlpha = alpha !== undefined ? alpha : 1; + + _clearBox.min.set( - _canvasWidthHalf, - _canvasHeightHalf ); + _clearBox.max.set( _canvasWidthHalf, _canvasHeightHalf ); + + }; + + this.setClearColorHex = function ( hex, alpha ) { + + console.warn( 'THREE.CanvasRenderer: .setClearColorHex() is being removed. Use .setClearColor() instead.' ); + this.setClearColor( hex, alpha ); + + }; + + this.getClearColor = function () { + + return _clearColor; + + }; + + this.getClearAlpha = function () { + + return _clearAlpha; + + }; + + this.getMaxAnisotropy = function () { + + return 0; + + }; + + this.clear = function () { + + if ( _clearBox.empty() === false ) { + + _clearBox.intersect( _clipBox ); + _clearBox.expandByScalar( 2 ); + + _clearBox.min.x = _clearBox.min.x + _canvasWidthHalf; + _clearBox.min.y = - _clearBox.min.y + _canvasHeightHalf; + _clearBox.max.x = _clearBox.max.x + _canvasWidthHalf; + _clearBox.max.y = - _clearBox.max.y + _canvasHeightHalf; + + if ( _clearAlpha < 1 ) { + + _context.clearRect( + _clearBox.min.x | 0, + _clearBox.min.y | 0, + ( _clearBox.max.x - _clearBox.min.x ) | 0, + ( _clearBox.max.y - _clearBox.min.y ) | 0 + ); + + } + + if ( _clearAlpha > 0 ) { + + setBlending( THREE.NormalBlending ); + setOpacity( 1 ); + + setFillStyle( 'rgba(' + Math.floor( _clearColor.r * 255 ) + ',' + Math.floor( _clearColor.g * 255 ) + ',' + Math.floor( _clearColor.b * 255 ) + ',' + _clearAlpha + ')' ); + + _context.fillRect( + _clearBox.min.x | 0, + _clearBox.min.y | 0, + ( _clearBox.max.x - _clearBox.min.x ) | 0, + ( _clearBox.max.y - _clearBox.min.y ) | 0 + ); + + } + + _clearBox.makeEmpty(); + + } + + }; + + // compatibility + + this.clearColor = function () {}; + this.clearDepth = function () {}; + this.clearStencil = function () {}; + + this.render = function ( scene, camera ) { + + if ( camera instanceof THREE.Camera === false ) { + + console.error( 'THREE.CanvasRenderer.render: camera is not an instance of THREE.Camera.' ); + return; + + } + + if ( this.autoClear === true ) this.clear(); + + _this.info.render.vertices = 0; + _this.info.render.faces = 0; + + _context.setTransform( _viewportWidth / _canvasWidth, 0, 0, - _viewportHeight / _canvasHeight, _viewportX, _canvasHeight - _viewportY ); + _context.translate( _canvasWidthHalf, _canvasHeightHalf ); + + _renderData = _projector.projectScene( scene, camera, this.sortObjects, this.sortElements ); + _elements = _renderData.elements; + _lights = _renderData.lights; + _camera = camera; + + _normalViewMatrix.getNormalMatrix( camera.matrixWorldInverse ); + + /* DEBUG + setFillStyle( 'rgba( 0, 255, 255, 0.5 )' ); + _context.fillRect( _clipBox.min.x, _clipBox.min.y, _clipBox.max.x - _clipBox.min.x, _clipBox.max.y - _clipBox.min.y ); + */ + + calculateLights(); + + for ( var e = 0, el = _elements.length; e < el; e ++ ) { + + var element = _elements[ e ]; + + var material = element.material; + + if ( material === undefined || material.opacity === 0 ) continue; + + _elemBox.makeEmpty(); + + if ( element instanceof THREE.RenderableSprite ) { + + _v1 = element; + _v1.x *= _canvasWidthHalf; _v1.y *= _canvasHeightHalf; + + renderSprite( _v1, element, material ); + + } else if ( element instanceof THREE.RenderableLine ) { + + _v1 = element.v1; _v2 = element.v2; + + _v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf; + _v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf; + + _elemBox.setFromPoints( [ + _v1.positionScreen, + _v2.positionScreen + ] ); + + if ( _clipBox.isIntersectionBox( _elemBox ) === true ) { + + renderLine( _v1, _v2, element, material ); + + } + + } else if ( element instanceof THREE.RenderableFace ) { + + _v1 = element.v1; _v2 = element.v2; _v3 = element.v3; + + if ( _v1.positionScreen.z < - 1 || _v1.positionScreen.z > 1 ) continue; + if ( _v2.positionScreen.z < - 1 || _v2.positionScreen.z > 1 ) continue; + if ( _v3.positionScreen.z < - 1 || _v3.positionScreen.z > 1 ) continue; + + _v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf; + _v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf; + _v3.positionScreen.x *= _canvasWidthHalf; _v3.positionScreen.y *= _canvasHeightHalf; + + if ( material.overdraw > 0 ) { + + expand( _v1.positionScreen, _v2.positionScreen, material.overdraw ); + expand( _v2.positionScreen, _v3.positionScreen, material.overdraw ); + expand( _v3.positionScreen, _v1.positionScreen, material.overdraw ); + + } + + _elemBox.setFromPoints( [ + _v1.positionScreen, + _v2.positionScreen, + _v3.positionScreen + ] ); + + if ( _clipBox.isIntersectionBox( _elemBox ) === true ) { + + renderFace3( _v1, _v2, _v3, 0, 1, 2, element, material ); + + } + + } + + /* DEBUG + setLineWidth( 1 ); + setStrokeStyle( 'rgba( 0, 255, 0, 0.5 )' ); + _context.strokeRect( _elemBox.min.x, _elemBox.min.y, _elemBox.max.x - _elemBox.min.x, _elemBox.max.y - _elemBox.min.y ); + */ + + _clearBox.union( _elemBox ); + + } + + /* DEBUG + setLineWidth( 1 ); + setStrokeStyle( 'rgba( 255, 0, 0, 0.5 )' ); + _context.strokeRect( _clearBox.min.x, _clearBox.min.y, _clearBox.max.x - _clearBox.min.x, _clearBox.max.y - _clearBox.min.y ); + */ + + _context.setTransform( 1, 0, 0, 1, 0, 0 ); + + }; + + // + + function calculateLights() { + + _ambientLight.setRGB( 0, 0, 0 ); + _directionalLights.setRGB( 0, 0, 0 ); + _pointLights.setRGB( 0, 0, 0 ); + + for ( var l = 0, ll = _lights.length; l < ll; l ++ ) { + + var light = _lights[ l ]; + var lightColor = light.color; + + if ( light instanceof THREE.AmbientLight ) { + + _ambientLight.add( lightColor ); + + } else if ( light instanceof THREE.DirectionalLight ) { + + // for sprites + + _directionalLights.add( lightColor ); + + } else if ( light instanceof THREE.PointLight ) { + + // for sprites + + _pointLights.add( lightColor ); + + } + + } + + } + + function calculateLight( position, normal, color ) { + + for ( var l = 0, ll = _lights.length; l < ll; l ++ ) { + + var light = _lights[ l ]; + + _lightColor.copy( light.color ); + + if ( light instanceof THREE.DirectionalLight ) { + + var lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ).normalize(); + + var amount = normal.dot( lightPosition ); + + if ( amount <= 0 ) continue; + + amount *= light.intensity; + + color.add( _lightColor.multiplyScalar( amount ) ); + + } else if ( light instanceof THREE.PointLight ) { + + var lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ); + + var amount = normal.dot( _vector3.subVectors( lightPosition, position ).normalize() ); + + if ( amount <= 0 ) continue; + + amount *= light.distance == 0 ? 1 : 1 - Math.min( position.distanceTo( lightPosition ) / light.distance, 1 ); + + if ( amount == 0 ) continue; + + amount *= light.intensity; + + color.add( _lightColor.multiplyScalar( amount ) ); + + } + + } + + } + + function renderSprite( v1, element, material ) { + + setOpacity( material.opacity ); + setBlending( material.blending ); + + var scaleX = element.scale.x * _canvasWidthHalf; + var scaleY = element.scale.y * _canvasHeightHalf; + + var dist = 0.5 * Math.sqrt( scaleX * scaleX + scaleY * scaleY ); // allow for rotated sprite + _elemBox.min.set( v1.x - dist, v1.y - dist ); + _elemBox.max.set( v1.x + dist, v1.y + dist ); + + if ( material instanceof THREE.SpriteMaterial ) { + + var texture = material.map; + + if ( texture !== null && texture.image !== undefined ) { + + if ( texture.hasEventListener( 'update', onTextureUpdate ) === false ) { + + if ( texture.image.width > 0 ) { + + textureToPattern( texture ); + + } + + texture.addEventListener( 'update', onTextureUpdate ); + + } + + var pattern = _patterns[ texture.id ]; + + if ( pattern !== undefined ) { + + setFillStyle( pattern ); + + } else { + + setFillStyle( 'rgba( 0, 0, 0, 1 )' ); + + } + + // + + var bitmap = texture.image; + + var ox = bitmap.width * texture.offset.x; + var oy = bitmap.height * texture.offset.y; + + var sx = bitmap.width * texture.repeat.x; + var sy = bitmap.height * texture.repeat.y; + + var cx = scaleX / sx; + var cy = scaleY / sy; + + _context.save(); + _context.translate( v1.x, v1.y ); + if ( material.rotation !== 0 ) _context.rotate( material.rotation ); + _context.translate( - scaleX / 2, - scaleY / 2 ); + _context.scale( cx, cy ); + _context.translate( - ox, - oy ); + _context.fillRect( ox, oy, sx, sy ); + _context.restore(); + + } else { + + // no texture + + setFillStyle( material.color.getStyle() ); + + _context.save(); + _context.translate( v1.x, v1.y ); + if ( material.rotation !== 0 ) _context.rotate( material.rotation ); + _context.scale( scaleX, - scaleY ); + _context.fillRect( - 0.5, - 0.5, 1, 1 ); + _context.restore(); + + } + + } else if ( material instanceof THREE.SpriteCanvasMaterial ) { + + setStrokeStyle( material.color.getStyle() ); + setFillStyle( material.color.getStyle() ); + + _context.save(); + _context.translate( v1.x, v1.y ); + if ( material.rotation !== 0 ) _context.rotate( material.rotation ); + _context.scale( scaleX, scaleY ); + + material.program( _context ); + + _context.restore(); + + } + + /* DEBUG + setStrokeStyle( 'rgb(255,255,0)' ); + _context.beginPath(); + _context.moveTo( v1.x - 10, v1.y ); + _context.lineTo( v1.x + 10, v1.y ); + _context.moveTo( v1.x, v1.y - 10 ); + _context.lineTo( v1.x, v1.y + 10 ); + _context.stroke(); + */ + + } + + function renderLine( v1, v2, element, material ) { + + setOpacity( material.opacity ); + setBlending( material.blending ); + + _context.beginPath(); + _context.moveTo( v1.positionScreen.x, v1.positionScreen.y ); + _context.lineTo( v2.positionScreen.x, v2.positionScreen.y ); + + if ( material instanceof THREE.LineBasicMaterial ) { + + setLineWidth( material.linewidth ); + setLineCap( material.linecap ); + setLineJoin( material.linejoin ); + + if ( material.vertexColors !== THREE.VertexColors ) { + + setStrokeStyle( material.color.getStyle() ); + + } else { + + var colorStyle1 = element.vertexColors[ 0 ].getStyle(); + var colorStyle2 = element.vertexColors[ 1 ].getStyle(); + + if ( colorStyle1 === colorStyle2 ) { + + setStrokeStyle( colorStyle1 ); + + } else { + + try { + + var grad = _context.createLinearGradient( + v1.positionScreen.x, + v1.positionScreen.y, + v2.positionScreen.x, + v2.positionScreen.y + ); + grad.addColorStop( 0, colorStyle1 ); + grad.addColorStop( 1, colorStyle2 ); + + } catch ( exception ) { + + grad = colorStyle1; + + } + + setStrokeStyle( grad ); + + } + + } + + _context.stroke(); + _elemBox.expandByScalar( material.linewidth * 2 ); + + } else if ( material instanceof THREE.LineDashedMaterial ) { + + setLineWidth( material.linewidth ); + setLineCap( material.linecap ); + setLineJoin( material.linejoin ); + setStrokeStyle( material.color.getStyle() ); + setLineDash( [ material.dashSize, material.gapSize ] ); + + _context.stroke(); + + _elemBox.expandByScalar( material.linewidth * 2 ); + + setLineDash( [] ); + + } + + } + + function renderFace3( v1, v2, v3, uv1, uv2, uv3, element, material ) { + + _this.info.render.vertices += 3; + _this.info.render.faces ++; + + setOpacity( material.opacity ); + setBlending( material.blending ); + + _v1x = v1.positionScreen.x; _v1y = v1.positionScreen.y; + _v2x = v2.positionScreen.x; _v2y = v2.positionScreen.y; + _v3x = v3.positionScreen.x; _v3y = v3.positionScreen.y; + + drawTriangle( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y ); + + if ( ( material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) && material.map === null ) { + + _diffuseColor.copy( material.color ); + _emissiveColor.copy( material.emissive ); + + if ( material.vertexColors === THREE.FaceColors ) { + + _diffuseColor.multiply( element.color ); + + } + + _color.copy( _ambientLight ); + + _centroid.copy( v1.positionWorld ).add( v2.positionWorld ).add( v3.positionWorld ).divideScalar( 3 ); + + calculateLight( _centroid, element.normalModel, _color ); + + _color.multiply( _diffuseColor ).add( _emissiveColor ); + + material.wireframe === true + ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) + : fillPath( _color ); + + } else if ( material instanceof THREE.MeshBasicMaterial || + material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.MeshPhongMaterial ) { + + if ( material.map !== null ) { + + if ( material.map.mapping instanceof THREE.UVMapping ) { + + _uvs = element.uvs; + patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uvs[ uv1 ].x, _uvs[ uv1 ].y, _uvs[ uv2 ].x, _uvs[ uv2 ].y, _uvs[ uv3 ].x, _uvs[ uv3 ].y, material.map ); + + } + + } else if ( material.envMap !== null ) { + + if ( material.envMap.mapping instanceof THREE.SphericalReflectionMapping ) { + + _normal.copy( element.vertexNormalsModel[ uv1 ] ).applyMatrix3( _normalViewMatrix ); + _uv1x = 0.5 * _normal.x + 0.5; + _uv1y = 0.5 * _normal.y + 0.5; + + _normal.copy( element.vertexNormalsModel[ uv2 ] ).applyMatrix3( _normalViewMatrix ); + _uv2x = 0.5 * _normal.x + 0.5; + _uv2y = 0.5 * _normal.y + 0.5; + + _normal.copy( element.vertexNormalsModel[ uv3 ] ).applyMatrix3( _normalViewMatrix ); + _uv3x = 0.5 * _normal.x + 0.5; + _uv3y = 0.5 * _normal.y + 0.5; + + patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, material.envMap ); + + } else if ( material.envMap.mapping instanceof THREE.SphericalRefractionMapping ) { + + _normal.copy( element.vertexNormalsModel[ uv1 ] ).applyMatrix3( _normalViewMatrix ); + _uv1x = - 0.5 * _normal.x + 0.5; + _uv1y = - 0.5 * _normal.y + 0.5; + + _normal.copy( element.vertexNormalsModel[ uv2 ] ).applyMatrix3( _normalViewMatrix ); + _uv2x = - 0.5 * _normal.x + 0.5; + _uv2y = - 0.5 * _normal.y + 0.5; + + _normal.copy( element.vertexNormalsModel[ uv3 ] ).applyMatrix3( _normalViewMatrix ); + _uv3x = - 0.5 * _normal.x + 0.5; + _uv3y = - 0.5 * _normal.y + 0.5; + + patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, material.envMap ); + + } + + + } else { + + _color.copy( material.color ); + + if ( material.vertexColors === THREE.FaceColors ) { + + _color.multiply( element.color ); + + } + + material.wireframe === true + ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) + : fillPath( _color ); + + } + + } else if ( material instanceof THREE.MeshDepthMaterial ) { + + _color.r = _color.g = _color.b = 1 - smoothstep( v1.positionScreen.z * v1.positionScreen.w, _camera.near, _camera.far ); + + material.wireframe === true + ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) + : fillPath( _color ); + + } else if ( material instanceof THREE.MeshNormalMaterial ) { + + _normal.copy( element.normalModel ).applyMatrix3( _normalViewMatrix ); + + _color.setRGB( _normal.x, _normal.y, _normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 ); + + material.wireframe === true + ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) + : fillPath( _color ); + + } else { + + _color.setRGB( 1, 1, 1 ); + + material.wireframe === true + ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) + : fillPath( _color ); + + } + + } + + // + + function drawTriangle( x0, y0, x1, y1, x2, y2 ) { + + _context.beginPath(); + _context.moveTo( x0, y0 ); + _context.lineTo( x1, y1 ); + _context.lineTo( x2, y2 ); + _context.closePath(); + + } + + function strokePath( color, linewidth, linecap, linejoin ) { + + setLineWidth( linewidth ); + setLineCap( linecap ); + setLineJoin( linejoin ); + setStrokeStyle( color.getStyle() ); + + _context.stroke(); + + _elemBox.expandByScalar( linewidth * 2 ); + + } + + function fillPath( color ) { + + setFillStyle( color.getStyle() ); + _context.fill(); + + } + + function onTextureUpdate ( event ) { + + textureToPattern( event.target ); + + } + + function textureToPattern( texture ) { + + if ( texture instanceof THREE.CompressedTexture ) return; + + var repeatX = texture.wrapS === THREE.RepeatWrapping; + var repeatY = texture.wrapT === THREE.RepeatWrapping; + + var image = texture.image; + + var canvas = document.createElement( 'canvas' ); + canvas.width = image.width; + canvas.height = image.height; + + var context = canvas.getContext( '2d' ); + context.setTransform( 1, 0, 0, - 1, 0, image.height ); + context.drawImage( image, 0, 0 ); + + _patterns[ texture.id ] = _context.createPattern( + canvas, repeatX === true && repeatY === true + ? 'repeat' + : repeatX === true && repeatY === false + ? 'repeat-x' + : repeatX === false && repeatY === true + ? 'repeat-y' + : 'no-repeat' + ); + + } + + function patternPath( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, texture ) { + + if ( texture instanceof THREE.DataTexture ) return; + + if ( texture.hasEventListener( 'update', onTextureUpdate ) === false ) { + + if ( texture.image !== undefined && texture.image.width > 0 ) { + + textureToPattern( texture ); + + } + + texture.addEventListener( 'update', onTextureUpdate ); + + } + + var pattern = _patterns[ texture.id ]; + + if ( pattern !== undefined ) { + + setFillStyle( pattern ); + + } else { + + setFillStyle( 'rgba(0,0,0,1)' ); + _context.fill(); + + return; + + } + + // http://extremelysatisfactorytotalitarianism.com/blog/?p=2120 + + var a, b, c, d, e, f, det, idet, + offsetX = texture.offset.x / texture.repeat.x, + offsetY = texture.offset.y / texture.repeat.y, + width = texture.image.width * texture.repeat.x, + height = texture.image.height * texture.repeat.y; + + u0 = ( u0 + offsetX ) * width; + v0 = ( v0 + offsetY ) * height; + + u1 = ( u1 + offsetX ) * width; + v1 = ( v1 + offsetY ) * height; + + u2 = ( u2 + offsetX ) * width; + v2 = ( v2 + offsetY ) * height; + + x1 -= x0; y1 -= y0; + x2 -= x0; y2 -= y0; + + u1 -= u0; v1 -= v0; + u2 -= u0; v2 -= v0; + + det = u1 * v2 - u2 * v1; + + if ( det === 0 ) return; + + idet = 1 / det; + + a = ( v2 * x1 - v1 * x2 ) * idet; + b = ( v2 * y1 - v1 * y2 ) * idet; + c = ( u1 * x2 - u2 * x1 ) * idet; + d = ( u1 * y2 - u2 * y1 ) * idet; + + e = x0 - a * u0 - c * v0; + f = y0 - b * u0 - d * v0; + + _context.save(); + _context.transform( a, b, c, d, e, f ); + _context.fill(); + _context.restore(); + + } + + function clipImage( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, image ) { + + // http://extremelysatisfactorytotalitarianism.com/blog/?p=2120 + + var a, b, c, d, e, f, det, idet, + width = image.width - 1, + height = image.height - 1; + + u0 *= width; v0 *= height; + u1 *= width; v1 *= height; + u2 *= width; v2 *= height; + + x1 -= x0; y1 -= y0; + x2 -= x0; y2 -= y0; + + u1 -= u0; v1 -= v0; + u2 -= u0; v2 -= v0; + + det = u1 * v2 - u2 * v1; + + idet = 1 / det; + + a = ( v2 * x1 - v1 * x2 ) * idet; + b = ( v2 * y1 - v1 * y2 ) * idet; + c = ( u1 * x2 - u2 * x1 ) * idet; + d = ( u1 * y2 - u2 * y1 ) * idet; + + e = x0 - a * u0 - c * v0; + f = y0 - b * u0 - d * v0; + + _context.save(); + _context.transform( a, b, c, d, e, f ); + _context.clip(); + _context.drawImage( image, 0, 0 ); + _context.restore(); + + } + + // Hide anti-alias gaps + + function expand( v1, v2, pixels ) { + + var x = v2.x - v1.x, y = v2.y - v1.y, + det = x * x + y * y, idet; + + if ( det === 0 ) return; + + idet = pixels / Math.sqrt( det ); + + x *= idet; y *= idet; + + v2.x += x; v2.y += y; + v1.x -= x; v1.y -= y; + + } + + // Context cached methods. + + function setOpacity( value ) { + + if ( _contextGlobalAlpha !== value ) { + + _context.globalAlpha = value; + _contextGlobalAlpha = value; + + } + + } + + function setBlending( value ) { + + if ( _contextGlobalCompositeOperation !== value ) { + + if ( value === THREE.NormalBlending ) { + + _context.globalCompositeOperation = 'source-over'; + + } else if ( value === THREE.AdditiveBlending ) { + + _context.globalCompositeOperation = 'lighter'; + + } else if ( value === THREE.SubtractiveBlending ) { + + _context.globalCompositeOperation = 'darker'; + + } + + _contextGlobalCompositeOperation = value; + + } + + } + + function setLineWidth( value ) { + + if ( _contextLineWidth !== value ) { + + _context.lineWidth = value; + _contextLineWidth = value; + + } + + } + + function setLineCap( value ) { + + // "butt", "round", "square" + + if ( _contextLineCap !== value ) { + + _context.lineCap = value; + _contextLineCap = value; + + } + + } + + function setLineJoin( value ) { + + // "round", "bevel", "miter" + + if ( _contextLineJoin !== value ) { + + _context.lineJoin = value; + _contextLineJoin = value; + + } + + } + + function setStrokeStyle( value ) { + + if ( _contextStrokeStyle !== value ) { + + _context.strokeStyle = value; + _contextStrokeStyle = value; + + } + + } + + function setFillStyle( value ) { + + if ( _contextFillStyle !== value ) { + + _context.fillStyle = value; + _contextFillStyle = value; + + } + + } + + function setLineDash( value ) { + + if ( _contextLineDash.length !== value.length ) { + + _context.setLineDash( value ); + _contextLineDash = value; + + } + + } + +}; + +// File:src/renderers/shaders/ShaderChunk.js + +THREE.ShaderChunk = {}; + +// File:src/renderers/shaders/ShaderChunk/alphatest_fragment.glsl + +THREE.ShaderChunk[ 'alphatest_fragment'] = "#ifdef ALPHATEST\n\n if ( gl_FragColor.a < ALPHATEST ) discard;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/lights_lambert_vertex.glsl + +THREE.ShaderChunk[ 'lights_lambert_vertex'] = "vLightFront = vec3( 0.0 );\n\n#ifdef DOUBLE_SIDED\n\n vLightBack = vec3( 0.0 );\n\n#endif\n\ntransformedNormal = normalize( transformedNormal );\n\n#if MAX_DIR_LIGHTS > 0\n\nfor( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\n vec3 dirVector = normalize( lDirection.xyz );\n\n float dotProduct = dot( transformedNormal, dirVector );\n vec3 directionalLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n #ifdef DOUBLE_SIDED\n\n vec3 directionalLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n #ifdef WRAP_AROUND\n\n vec3 directionalLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n #endif\n\n #endif\n\n #ifdef WRAP_AROUND\n\n vec3 directionalLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n directionalLightWeighting = mix( directionalLightWeighting, directionalLightWeightingHalf, wrapRGB );\n\n #ifdef DOUBLE_SIDED\n\n directionalLightWeightingBack = mix( directionalLightWeightingBack, directionalLightWeightingHalfBack, wrapRGB );\n\n #endif\n\n #endif\n\n vLightFront += directionalLightColor[ i ] * directionalLightWeighting;\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += directionalLightColor[ i ] * directionalLightWeightingBack;\n\n #endif\n\n}\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n for( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\n vec3 lVector = lPosition.xyz - mvPosition.xyz;\n\n float lDistance = 1.0;\n if ( pointLightDistance[ i ] > 0.0 )\n lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\n\n lVector = normalize( lVector );\n float dotProduct = dot( transformedNormal, lVector );\n\n vec3 pointLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n #ifdef DOUBLE_SIDED\n\n vec3 pointLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n #ifdef WRAP_AROUND\n\n vec3 pointLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n #endif\n\n #endif\n\n #ifdef WRAP_AROUND\n\n vec3 pointLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n pointLightWeighting = mix( pointLightWeighting, pointLightWeightingHalf, wrapRGB );\n\n #ifdef DOUBLE_SIDED\n\n pointLightWeightingBack = mix( pointLightWeightingBack, pointLightWeightingHalfBack, wrapRGB );\n\n #endif\n\n #endif\n\n vLightFront += pointLightColor[ i ] * pointLightWeighting * lDistance;\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += pointLightColor[ i ] * pointLightWeightingBack * lDistance;\n\n #endif\n\n }\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n for( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\n vec3 lVector = lPosition.xyz - mvPosition.xyz;\n\n float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - worldPosition.xyz ) );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );\n\n float lDistance = 1.0;\n if ( spotLightDistance[ i ] > 0.0 )\n lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );\n\n lVector = normalize( lVector );\n\n float dotProduct = dot( transformedNormal, lVector );\n vec3 spotLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n #ifdef DOUBLE_SIDED\n\n vec3 spotLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n #ifdef WRAP_AROUND\n\n vec3 spotLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n #endif\n\n #endif\n\n #ifdef WRAP_AROUND\n\n vec3 spotLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n spotLightWeighting = mix( spotLightWeighting, spotLightWeightingHalf, wrapRGB );\n\n #ifdef DOUBLE_SIDED\n\n spotLightWeightingBack = mix( spotLightWeightingBack, spotLightWeightingHalfBack, wrapRGB );\n\n #endif\n\n #endif\n\n vLightFront += spotLightColor[ i ] * spotLightWeighting * lDistance * spotEffect;\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += spotLightColor[ i ] * spotLightWeightingBack * lDistance * spotEffect;\n\n #endif\n\n }\n\n }\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );\n vec3 lVector = normalize( lDirection.xyz );\n\n float dotProduct = dot( transformedNormal, lVector );\n\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n float hemiDiffuseWeightBack = -0.5 * dotProduct + 0.5;\n\n vLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );\n\n #endif\n\n }\n\n#endif\n\nvLightFront = vLightFront * diffuse + ambient * ambientLightColor + emissive;\n\n#ifdef DOUBLE_SIDED\n\n vLightBack = vLightBack * diffuse + ambient * ambientLightColor + emissive;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/map_particle_pars_fragment.glsl + +THREE.ShaderChunk[ 'map_particle_pars_fragment'] = "#ifdef USE_MAP\n\n uniform sampler2D map;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/default_vertex.glsl + +THREE.ShaderChunk[ 'default_vertex'] = "vec4 mvPosition;\n\n#ifdef USE_SKINNING\n\n mvPosition = modelViewMatrix * skinned;\n\n#endif\n\n#if !defined( USE_SKINNING ) && defined( USE_MORPHTARGETS )\n\n mvPosition = modelViewMatrix * vec4( morphed, 1.0 );\n\n#endif\n\n#if !defined( USE_SKINNING ) && ! defined( USE_MORPHTARGETS )\n\n mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\n#endif\n\ngl_Position = projectionMatrix * mvPosition;"; + +// File:src/renderers/shaders/ShaderChunk/map_pars_fragment.glsl + +THREE.ShaderChunk[ 'map_pars_fragment'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n varying vec2 vUv;\n\n#endif\n\n#ifdef USE_MAP\n\n uniform sampler2D map;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/skinnormal_vertex.glsl + +THREE.ShaderChunk[ 'skinnormal_vertex'] = "#ifdef USE_SKINNING\n\n mat4 skinMatrix = mat4( 0.0 );\n skinMatrix += skinWeight.x * boneMatX;\n skinMatrix += skinWeight.y * boneMatY;\n skinMatrix += skinWeight.z * boneMatZ;\n skinMatrix += skinWeight.w * boneMatW;\n skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\n #ifdef USE_MORPHNORMALS\n\n vec4 skinnedNormal = skinMatrix * vec4( morphedNormal, 0.0 );\n\n #else\n\n vec4 skinnedNormal = skinMatrix * vec4( normal, 0.0 );\n\n #endif\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_vertex.glsl + +THREE.ShaderChunk[ 'logdepthbuf_pars_vertex'] = "#ifdef USE_LOGDEPTHBUF\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n varying float vFragDepth;\n\n #endif\n\n uniform float logDepthBufFC;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/lightmap_pars_vertex.glsl + +THREE.ShaderChunk[ 'lightmap_pars_vertex'] = "#ifdef USE_LIGHTMAP\n\n varying vec2 vUv2;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/lights_phong_fragment.glsl + +THREE.ShaderChunk[ 'lights_phong_fragment'] = "vec3 normal = normalize( vNormal );\nvec3 viewPosition = normalize( vViewPosition );\n\n#ifdef DOUBLE_SIDED\n\n normal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n\n#endif\n\n#ifdef USE_NORMALMAP\n\n normal = perturbNormal2Arb( -vViewPosition, normal );\n\n#elif defined( USE_BUMPMAP )\n\n normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n vec3 pointDiffuse = vec3( 0.0 );\n vec3 pointSpecular = vec3( 0.0 );\n\n for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\n vec3 lVector = lPosition.xyz + vViewPosition.xyz;\n\n float lDistance = 1.0;\n if ( pointLightDistance[ i ] > 0.0 )\n lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\n\n lVector = normalize( lVector );\n\n // diffuse\n\n float dotProduct = dot( normal, lVector );\n\n #ifdef WRAP_AROUND\n\n float pointDiffuseWeightFull = max( dotProduct, 0.0 );\n float pointDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n vec3 pointDiffuseWeight = mix( vec3( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );\n\n #else\n\n float pointDiffuseWeight = max( dotProduct, 0.0 );\n\n #endif\n\n pointDiffuse += diffuse * pointLightColor[ i ] * pointDiffuseWeight * lDistance;\n\n // specular\n\n vec3 pointHalfVector = normalize( lVector + viewPosition );\n float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );\n float pointSpecularWeight = specularStrength * max( pow( pointDotNormalHalf, shininess ), 0.0 );\n\n float specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, pointHalfVector ), 0.0 ), 5.0 );\n pointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance * specularNormalization;\n\n }\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n vec3 spotDiffuse = vec3( 0.0 );\n vec3 spotSpecular = vec3( 0.0 );\n\n for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\n vec3 lVector = lPosition.xyz + vViewPosition.xyz;\n\n float lDistance = 1.0;\n if ( spotLightDistance[ i ] > 0.0 )\n lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );\n\n lVector = normalize( lVector );\n\n float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );\n\n // diffuse\n\n float dotProduct = dot( normal, lVector );\n\n #ifdef WRAP_AROUND\n\n float spotDiffuseWeightFull = max( dotProduct, 0.0 );\n float spotDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n vec3 spotDiffuseWeight = mix( vec3( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );\n\n #else\n\n float spotDiffuseWeight = max( dotProduct, 0.0 );\n\n #endif\n\n spotDiffuse += diffuse * spotLightColor[ i ] * spotDiffuseWeight * lDistance * spotEffect;\n\n // specular\n\n vec3 spotHalfVector = normalize( lVector + viewPosition );\n float spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );\n float spotSpecularWeight = specularStrength * max( pow( spotDotNormalHalf, shininess ), 0.0 );\n\n float specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, spotHalfVector ), 0.0 ), 5.0 );\n spotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * specularNormalization * spotEffect;\n\n }\n\n }\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n vec3 dirDiffuse = vec3( 0.0 );\n vec3 dirSpecular = vec3( 0.0 );\n\n for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\n vec3 dirVector = normalize( lDirection.xyz );\n\n // diffuse\n\n float dotProduct = dot( normal, dirVector );\n\n #ifdef WRAP_AROUND\n\n float dirDiffuseWeightFull = max( dotProduct, 0.0 );\n float dirDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n vec3 dirDiffuseWeight = mix( vec3( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), wrapRGB );\n\n #else\n\n float dirDiffuseWeight = max( dotProduct, 0.0 );\n\n #endif\n\n dirDiffuse += diffuse * directionalLightColor[ i ] * dirDiffuseWeight;\n\n // specular\n\n vec3 dirHalfVector = normalize( dirVector + viewPosition );\n float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );\n float dirSpecularWeight = specularStrength * max( pow( dirDotNormalHalf, shininess ), 0.0 );\n\n /*\n // fresnel term from skin shader\n const float F0 = 0.128;\n\n float base = 1.0 - dot( viewPosition, dirHalfVector );\n float exponential = pow( base, 5.0 );\n\n float fresnel = exponential + F0 * ( 1.0 - exponential );\n */\n\n /*\n // fresnel term from fresnel shader\n const float mFresnelBias = 0.08;\n const float mFresnelScale = 0.3;\n const float mFresnelPower = 5.0;\n\n float fresnel = mFresnelBias + mFresnelScale * pow( 1.0 + dot( normalize( -viewPosition ), normal ), mFresnelPower );\n */\n\n float specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n // dirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization * fresnel;\n\n vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( dirVector, dirHalfVector ), 0.0 ), 5.0 );\n dirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;\n\n\n }\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n vec3 hemiDiffuse = vec3( 0.0 );\n vec3 hemiSpecular = vec3( 0.0 );\n\n for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );\n vec3 lVector = normalize( lDirection.xyz );\n\n // diffuse\n\n float dotProduct = dot( normal, lVector );\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n vec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n hemiDiffuse += diffuse * hemiColor;\n\n // specular (sky light)\n\n vec3 hemiHalfVectorSky = normalize( lVector + viewPosition );\n float hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;\n float hemiSpecularWeightSky = specularStrength * max( pow( max( hemiDotNormalHalfSky, 0.0 ), shininess ), 0.0 );\n\n // specular (ground light)\n\n vec3 lVectorGround = -lVector;\n\n vec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );\n float hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;\n float hemiSpecularWeightGround = specularStrength * max( pow( max( hemiDotNormalHalfGround, 0.0 ), shininess ), 0.0 );\n\n float dotProductGround = dot( normal, lVectorGround );\n\n float specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n vec3 schlickSky = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, hemiHalfVectorSky ), 0.0 ), 5.0 );\n vec3 schlickGround = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 0.0 ), 5.0 );\n hemiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );\n\n }\n\n#endif\n\nvec3 totalDiffuse = vec3( 0.0 );\nvec3 totalSpecular = vec3( 0.0 );\n\n#if MAX_DIR_LIGHTS > 0\n\n totalDiffuse += dirDiffuse;\n totalSpecular += dirSpecular;\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n totalDiffuse += hemiDiffuse;\n totalSpecular += hemiSpecular;\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n totalDiffuse += pointDiffuse;\n totalSpecular += pointSpecular;\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n totalDiffuse += spotDiffuse;\n totalSpecular += spotSpecular;\n\n#endif\n\n#ifdef METAL\n\n gl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient + totalSpecular );\n\n#else\n\n gl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient ) + totalSpecular;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/fog_pars_fragment.glsl + +THREE.ShaderChunk[ 'fog_pars_fragment'] = "#ifdef USE_FOG\n\n uniform vec3 fogColor;\n\n #ifdef FOG_EXP2\n\n uniform float fogDensity;\n\n #else\n\n uniform float fogNear;\n uniform float fogFar;\n #endif\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/morphnormal_vertex.glsl + +THREE.ShaderChunk[ 'morphnormal_vertex'] = "#ifdef USE_MORPHNORMALS\n\n vec3 morphedNormal = vec3( 0.0 );\n\n morphedNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n morphedNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n morphedNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n morphedNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n\n morphedNormal += normal;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'envmap_pars_fragment'] = "#ifdef USE_ENVMAP\n\n uniform float reflectivity;\n uniform samplerCube envMap;\n uniform float flipEnvMap;\n uniform int combine;\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\n\n uniform bool useRefract;\n uniform float refractionRatio;\n\n #else\n\n varying vec3 vReflect;\n\n #endif\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_fragment.glsl + +THREE.ShaderChunk[ 'logdepthbuf_fragment'] = "#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n\n gl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'normalmap_pars_fragment'] = "#ifdef USE_NORMALMAP\n\n uniform sampler2D normalMap;\n uniform vec2 normalScale;\n\n // Per-Pixel Tangent Space Normal Mapping\n // http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html\n\n vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\n vec3 q0 = dFdx( eye_pos.xyz );\n vec3 q1 = dFdy( eye_pos.xyz );\n vec2 st0 = dFdx( vUv.st );\n vec2 st1 = dFdy( vUv.st );\n\n vec3 S = normalize( q0 * st1.t - q1 * st0.t );\n vec3 T = normalize( -q0 * st1.s + q1 * st0.s );\n vec3 N = normalize( surf_norm );\n\n vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n mapN.xy = normalScale * mapN.xy;\n mat3 tsn = mat3( S, T, N );\n return normalize( tsn * mapN );\n\n }\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_vertex.glsl + +THREE.ShaderChunk[ 'lights_phong_pars_vertex'] = "#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/lightmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'lightmap_pars_fragment'] = "#ifdef USE_LIGHTMAP\n\n varying vec2 vUv2;\n uniform sampler2D lightMap;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl + +THREE.ShaderChunk[ 'shadowmap_vertex'] = "#ifdef USE_SHADOWMAP\n\n for( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n\n }\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/lights_phong_vertex.glsl + +THREE.ShaderChunk[ 'lights_phong_vertex'] = "#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n vWorldPosition = worldPosition.xyz;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/map_fragment.glsl + +THREE.ShaderChunk[ 'map_fragment'] = "#ifdef USE_MAP\n\n vec4 texelColor = texture2D( map, vUv );\n\n #ifdef GAMMA_INPUT\n\n texelColor.xyz *= texelColor.xyz;\n\n #endif\n\n gl_FragColor = gl_FragColor * texelColor;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/lightmap_vertex.glsl + +THREE.ShaderChunk[ 'lightmap_vertex'] = "#ifdef USE_LIGHTMAP\n\n vUv2 = uv2;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl + +THREE.ShaderChunk[ 'map_particle_fragment'] = "#ifdef USE_MAP\n\n gl_FragColor = gl_FragColor * texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) );\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/color_pars_fragment.glsl + +THREE.ShaderChunk[ 'color_pars_fragment'] = "#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/color_vertex.glsl + +THREE.ShaderChunk[ 'color_vertex'] = "#ifdef USE_COLOR\n\n #ifdef GAMMA_INPUT\n\n vColor = color * color;\n\n #else\n\n vColor = color;\n\n #endif\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/skinning_vertex.glsl + +THREE.ShaderChunk[ 'skinning_vertex'] = "#ifdef USE_SKINNING\n\n #ifdef USE_MORPHTARGETS\n\n vec4 skinVertex = bindMatrix * vec4( morphed, 1.0 );\n\n #else\n\n vec4 skinVertex = bindMatrix * vec4( position, 1.0 );\n\n #endif\n\n vec4 skinned = vec4( 0.0 );\n skinned += boneMatX * skinVertex * skinWeight.x;\n skinned += boneMatY * skinVertex * skinWeight.y;\n skinned += boneMatZ * skinVertex * skinWeight.z;\n skinned += boneMatW * skinVertex * skinWeight.w;\n skinned = bindMatrixInverse * skinned;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl + +THREE.ShaderChunk[ 'envmap_pars_vertex'] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP )\n\n varying vec3 vReflect;\n\n uniform float refractionRatio;\n uniform bool useRefract;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/linear_to_gamma_fragment.glsl + +THREE.ShaderChunk[ 'linear_to_gamma_fragment'] = "#ifdef GAMMA_OUTPUT\n\n gl_FragColor.xyz = sqrt( gl_FragColor.xyz );\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl + +THREE.ShaderChunk[ 'color_pars_vertex'] = "#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/lights_lambert_pars_vertex.glsl + +THREE.ShaderChunk[ 'lights_lambert_pars_vertex'] = "uniform vec3 ambient;\nuniform vec3 diffuse;\nuniform vec3 emissive;\n\nuniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#ifdef WRAP_AROUND\n\n uniform vec3 wrapRGB;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/map_pars_vertex.glsl + +THREE.ShaderChunk[ 'map_pars_vertex'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n varying vec2 vUv;\n uniform vec4 offsetRepeat;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/envmap_fragment.glsl + +THREE.ShaderChunk[ 'envmap_fragment'] = "#ifdef USE_ENVMAP\n\n vec3 reflectVec;\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\n\n vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\n // http://en.wikibooks.org/wiki/GLSL_Programming/Applying_Matrix_Transformations\n // Transforming Normal Vectors with the Inverse Transformation\n\n vec3 worldNormal = normalize( vec3( vec4( normal, 0.0 ) * viewMatrix ) );\n\n if ( useRefract ) {\n\n reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\n } else { \n\n reflectVec = reflect( cameraToVertex, worldNormal );\n\n }\n\n #else\n\n reflectVec = vReflect;\n\n #endif\n\n #ifdef DOUBLE_SIDED\n\n float flipNormal = ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n vec4 cubeColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n #else\n\n vec4 cubeColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n #endif\n\n #ifdef GAMMA_INPUT\n\n cubeColor.xyz *= cubeColor.xyz;\n\n #endif\n\n if ( combine == 1 ) {\n\n gl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularStrength * reflectivity );\n\n } else if ( combine == 2 ) {\n\n gl_FragColor.xyz += cubeColor.xyz * specularStrength * reflectivity;\n\n } else {\n\n gl_FragColor.xyz = mix( gl_FragColor.xyz, gl_FragColor.xyz * cubeColor.xyz, specularStrength * reflectivity );\n\n }\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/specularmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'specularmap_pars_fragment'] = "#ifdef USE_SPECULARMAP\n\n uniform sampler2D specularMap;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_vertex.glsl + +THREE.ShaderChunk[ 'logdepthbuf_vertex'] = "#ifdef USE_LOGDEPTHBUF\n\n gl_Position.z = log2(max(1e-6, gl_Position.w + 1.0)) * logDepthBufFC;\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n vFragDepth = 1.0 + gl_Position.w;\n\n#else\n\n gl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;\n\n #endif\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/morphtarget_pars_vertex.glsl + +THREE.ShaderChunk[ 'morphtarget_pars_vertex'] = "#ifdef USE_MORPHTARGETS\n\n #ifndef USE_MORPHNORMALS\n\n uniform float morphTargetInfluences[ 8 ];\n\n #else\n\n uniform float morphTargetInfluences[ 4 ];\n\n #endif\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/specularmap_fragment.glsl + +THREE.ShaderChunk[ 'specularmap_fragment'] = "float specularStrength;\n\n#ifdef USE_SPECULARMAP\n\n vec4 texelSpecular = texture2D( specularMap, vUv );\n specularStrength = texelSpecular.r;\n\n#else\n\n specularStrength = 1.0;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/fog_fragment.glsl + +THREE.ShaderChunk[ 'fog_fragment'] = "#ifdef USE_FOG\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n float depth = gl_FragDepthEXT / gl_FragCoord.w;\n\n #else\n\n float depth = gl_FragCoord.z / gl_FragCoord.w;\n\n #endif\n\n #ifdef FOG_EXP2\n\n const float LOG2 = 1.442695;\n float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );\n fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );\n\n #else\n\n float fogFactor = smoothstep( fogNear, fogFar, depth );\n\n #endif\n \n gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/bumpmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'bumpmap_pars_fragment'] = "#ifdef USE_BUMPMAP\n\n uniform sampler2D bumpMap;\n uniform float bumpScale;\n\n // Derivative maps - bump mapping unparametrized surfaces by Morten Mikkelsen\n // http://mmikkelsen3d.blogspot.sk/2011/07/derivative-maps.html\n\n // Evaluate the derivative of the height w.r.t. screen-space using forward differencing (listing 2)\n\n vec2 dHdxy_fwd() {\n\n vec2 dSTdx = dFdx( vUv );\n vec2 dSTdy = dFdy( vUv );\n\n float Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\n return vec2( dBx, dBy );\n\n }\n\n vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\n vec3 vSigmaX = dFdx( surf_pos );\n vec3 vSigmaY = dFdy( surf_pos );\n vec3 vN = surf_norm; // normalized\n\n vec3 R1 = cross( vSigmaY, vN );\n vec3 R2 = cross( vN, vSigmaX );\n\n float fDet = dot( vSigmaX, R1 );\n\n vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n return normalize( abs( fDet ) * surf_norm - vGrad );\n\n }\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl + +THREE.ShaderChunk[ 'defaultnormal_vertex'] = "vec3 objectNormal;\n\n#ifdef USE_SKINNING\n\n objectNormal = skinnedNormal.xyz;\n\n#endif\n\n#if !defined( USE_SKINNING ) && defined( USE_MORPHNORMALS )\n\n objectNormal = morphedNormal;\n\n#endif\n\n#if !defined( USE_SKINNING ) && ! defined( USE_MORPHNORMALS )\n\n objectNormal = normal;\n\n#endif\n\n#ifdef FLIP_SIDED\n\n objectNormal = -objectNormal;\n\n#endif\n\nvec3 transformedNormal = normalMatrix * objectNormal;"; + +// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl + +THREE.ShaderChunk[ 'lights_phong_pars_fragment'] = "uniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n\n uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n\n#ifdef WRAP_AROUND\n\n uniform vec3 wrapRGB;\n\n#endif\n\nvarying vec3 vViewPosition;\nvarying vec3 vNormal;"; + +// File:src/renderers/shaders/ShaderChunk/skinbase_vertex.glsl + +THREE.ShaderChunk[ 'skinbase_vertex'] = "#ifdef USE_SKINNING\n\n mat4 boneMatX = getBoneMatrix( skinIndex.x );\n mat4 boneMatY = getBoneMatrix( skinIndex.y );\n mat4 boneMatZ = getBoneMatrix( skinIndex.z );\n mat4 boneMatW = getBoneMatrix( skinIndex.w );\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/map_vertex.glsl + +THREE.ShaderChunk[ 'map_vertex'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n vUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/lightmap_fragment.glsl + +THREE.ShaderChunk[ 'lightmap_fragment'] = "#ifdef USE_LIGHTMAP\n\n gl_FragColor = gl_FragColor * texture2D( lightMap, vUv2 );\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl + +THREE.ShaderChunk[ 'shadowmap_pars_vertex'] = "#ifdef USE_SHADOWMAP\n\n varying vec4 vShadowCoord[ MAX_SHADOWS ];\n uniform mat4 shadowMatrix[ MAX_SHADOWS ];\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/color_fragment.glsl + +THREE.ShaderChunk[ 'color_fragment'] = "#ifdef USE_COLOR\n\n gl_FragColor = gl_FragColor * vec4( vColor, 1.0 );\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/morphtarget_vertex.glsl + +THREE.ShaderChunk[ 'morphtarget_vertex'] = "#ifdef USE_MORPHTARGETS\n\n vec3 morphed = vec3( 0.0 );\n morphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n morphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n morphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n morphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\n #ifndef USE_MORPHNORMALS\n\n morphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n morphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n morphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n morphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\n #endif\n\n morphed += position;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/envmap_vertex.glsl + +THREE.ShaderChunk[ 'envmap_vertex'] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP )\n\n vec3 worldNormal = mat3( modelMatrix[ 0 ].xyz, modelMatrix[ 1 ].xyz, modelMatrix[ 2 ].xyz ) * objectNormal;\n worldNormal = normalize( worldNormal );\n\n vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\n if ( useRefract ) {\n\n vReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\n } else {\n\n vReflect = reflect( cameraToVertex, worldNormal );\n\n }\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/shadowmap_fragment.glsl + +THREE.ShaderChunk[ 'shadowmap_fragment'] = "#ifdef USE_SHADOWMAP\n\n #ifdef SHADOWMAP_DEBUG\n\n vec3 frustumColors[3];\n frustumColors[0] = vec3( 1.0, 0.5, 0.0 );\n frustumColors[1] = vec3( 0.0, 1.0, 0.8 );\n frustumColors[2] = vec3( 0.0, 0.5, 1.0 );\n\n #endif\n\n #ifdef SHADOWMAP_CASCADE\n\n int inFrustumCount = 0;\n\n #endif\n\n float fDepth;\n vec3 shadowColor = vec3( 1.0 );\n\n for( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;\n\n // if ( something && something ) breaks ATI OpenGL shader compiler\n // if ( all( something, something ) ) using this instead\n\n bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n bool inFrustum = all( inFrustumVec );\n\n // don't shadow pixels outside of light frustum\n // use just first frustum (for cascades)\n // don't shadow pixels behind far plane of light frustum\n\n #ifdef SHADOWMAP_CASCADE\n\n inFrustumCount += int( inFrustum );\n bvec3 frustumTestVec = bvec3( inFrustum, inFrustumCount == 1, shadowCoord.z <= 1.0 );\n\n #else\n\n bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\n #endif\n\n bool frustumTest = all( frustumTestVec );\n\n if ( frustumTest ) {\n\n shadowCoord.z += shadowBias[ i ];\n\n #if defined( SHADOWMAP_TYPE_PCF )\n\n // Percentage-close filtering\n // (9 pixel kernel)\n // http://fabiensanglard.net/shadowmappingPCF/\n\n float shadow = 0.0;\n\n /*\n // nested loops breaks shader compiler / validator on some ATI cards when using OpenGL\n // must enroll loop manually\n\n for ( float y = -1.25; y <= 1.25; y += 1.25 )\n for ( float x = -1.25; x <= 1.25; x += 1.25 ) {\n\n vec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );\n\n // doesn't seem to produce any noticeable visual difference compared to simple texture2D lookup\n //vec4 rgbaDepth = texture2DProj( shadowMap[ i ], vec4( vShadowCoord[ i ].w * ( vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy ), 0.05, vShadowCoord[ i ].w ) );\n\n float fDepth = unpackDepth( rgbaDepth );\n\n if ( fDepth < shadowCoord.z )\n shadow += 1.0;\n\n }\n\n shadow /= 9.0;\n\n */\n\n const float shadowDelta = 1.0 / 9.0;\n\n float xPixelOffset = 1.0 / shadowMapSize[ i ].x;\n float yPixelOffset = 1.0 / shadowMapSize[ i ].y;\n\n float dx0 = -1.25 * xPixelOffset;\n float dy0 = -1.25 * yPixelOffset;\n float dx1 = 1.25 * xPixelOffset;\n float dy1 = 1.25 * yPixelOffset;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n\n #elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n // Percentage-close filtering\n // (9 pixel kernel)\n // http://fabiensanglard.net/shadowmappingPCF/\n\n float shadow = 0.0;\n\n float xPixelOffset = 1.0 / shadowMapSize[ i ].x;\n float yPixelOffset = 1.0 / shadowMapSize[ i ].y;\n\n float dx0 = -1.0 * xPixelOffset;\n float dy0 = -1.0 * yPixelOffset;\n float dx1 = 1.0 * xPixelOffset;\n float dy1 = 1.0 * yPixelOffset;\n\n mat3 shadowKernel;\n mat3 depthKernel;\n\n depthKernel[0][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n depthKernel[0][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n depthKernel[0][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n depthKernel[1][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n depthKernel[1][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n depthKernel[1][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n depthKernel[2][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n depthKernel[2][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n depthKernel[2][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n\n vec3 shadowZ = vec3( shadowCoord.z );\n shadowKernel[0] = vec3(lessThan(depthKernel[0], shadowZ ));\n shadowKernel[0] *= vec3(0.25);\n\n shadowKernel[1] = vec3(lessThan(depthKernel[1], shadowZ ));\n shadowKernel[1] *= vec3(0.25);\n\n shadowKernel[2] = vec3(lessThan(depthKernel[2], shadowZ ));\n shadowKernel[2] *= vec3(0.25);\n\n vec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[i].xy );\n\n shadowKernel[0] = mix( shadowKernel[1], shadowKernel[0], fractionalCoord.x );\n shadowKernel[1] = mix( shadowKernel[2], shadowKernel[1], fractionalCoord.x );\n\n vec4 shadowValues;\n shadowValues.x = mix( shadowKernel[0][1], shadowKernel[0][0], fractionalCoord.y );\n shadowValues.y = mix( shadowKernel[0][2], shadowKernel[0][1], fractionalCoord.y );\n shadowValues.z = mix( shadowKernel[1][1], shadowKernel[1][0], fractionalCoord.y );\n shadowValues.w = mix( shadowKernel[1][2], shadowKernel[1][1], fractionalCoord.y );\n\n shadow = dot( shadowValues, vec4( 1.0 ) );\n\n shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n\n #else\n\n vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );\n float fDepth = unpackDepth( rgbaDepth );\n\n if ( fDepth < shadowCoord.z )\n\n // spot with multiple shadows is darker\n\n shadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] );\n\n // spot with multiple shadows has the same color as single shadow spot\n\n // shadowColor = min( shadowColor, vec3( shadowDarkness[ i ] ) );\n\n #endif\n\n }\n\n\n #ifdef SHADOWMAP_DEBUG\n\n #ifdef SHADOWMAP_CASCADE\n\n if ( inFrustum && inFrustumCount == 1 ) gl_FragColor.xyz *= frustumColors[ i ];\n\n #else\n\n if ( inFrustum ) gl_FragColor.xyz *= frustumColors[ i ];\n\n #endif\n\n #endif\n\n }\n\n #ifdef GAMMA_OUTPUT\n\n shadowColor *= shadowColor;\n\n #endif\n\n gl_FragColor.xyz = gl_FragColor.xyz * shadowColor;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl + +THREE.ShaderChunk[ 'worldpos_vertex'] = "#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n\n #ifdef USE_SKINNING\n\n vec4 worldPosition = modelMatrix * skinned;\n\n #endif\n\n #if defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )\n\n vec4 worldPosition = modelMatrix * vec4( morphed, 1.0 );\n\n #endif\n\n #if ! defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )\n\n vec4 worldPosition = modelMatrix * vec4( position, 1.0 );\n\n #endif\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'shadowmap_pars_fragment'] = "#ifdef USE_SHADOWMAP\n\n uniform sampler2D shadowMap[ MAX_SHADOWS ];\n uniform vec2 shadowMapSize[ MAX_SHADOWS ];\n\n uniform float shadowDarkness[ MAX_SHADOWS ];\n uniform float shadowBias[ MAX_SHADOWS ];\n\n varying vec4 vShadowCoord[ MAX_SHADOWS ];\n\n float unpackDepth( const in vec4 rgba_depth ) {\n\n const vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n float depth = dot( rgba_depth, bit_shift );\n return depth;\n\n }\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/skinning_pars_vertex.glsl + +THREE.ShaderChunk[ 'skinning_pars_vertex'] = "#ifdef USE_SKINNING\n\n uniform mat4 bindMatrix;\n uniform mat4 bindMatrixInverse;\n\n #ifdef BONE_TEXTURE\n\n uniform sampler2D boneTexture;\n uniform int boneTextureWidth;\n uniform int boneTextureHeight;\n\n mat4 getBoneMatrix( const in float i ) {\n\n float j = i * 4.0;\n float x = mod( j, float( boneTextureWidth ) );\n float y = floor( j / float( boneTextureWidth ) );\n\n float dx = 1.0 / float( boneTextureWidth );\n float dy = 1.0 / float( boneTextureHeight );\n\n y = dy * ( y + 0.5 );\n\n vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\n mat4 bone = mat4( v1, v2, v3, v4 );\n\n return bone;\n\n }\n\n #else\n\n uniform mat4 boneGlobalMatrices[ MAX_BONES ];\n\n mat4 getBoneMatrix( const in float i ) {\n\n mat4 bone = boneGlobalMatrices[ int(i) ];\n return bone;\n\n }\n\n #endif\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_fragment.glsl + +THREE.ShaderChunk[ 'logdepthbuf_pars_fragment'] = "#ifdef USE_LOGDEPTHBUF\n\n uniform float logDepthBufFC;\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n #extension GL_EXT_frag_depth : enable\n varying float vFragDepth;\n\n #endif\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/alphamap_fragment.glsl + +THREE.ShaderChunk[ 'alphamap_fragment'] = "#ifdef USE_ALPHAMAP\n\n gl_FragColor.a *= texture2D( alphaMap, vUv ).g;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/alphamap_pars_fragment.glsl + +THREE.ShaderChunk[ 'alphamap_pars_fragment'] = "#ifdef USE_ALPHAMAP\n\n uniform sampler2D alphaMap;\n\n#endif\n"; + +// File:src/renderers/shaders/UniformsUtils.js + +/** + * Uniform Utilities + */ + +THREE.UniformsUtils = { + + merge: function ( uniforms ) { + + var u, p, tmp, merged = {}; + + for ( u = 0; u < uniforms.length; u ++ ) { + + tmp = this.clone( uniforms[ u ] ); + + for ( p in tmp ) { + + merged[ p ] = tmp[ p ]; + + } + + } + + return merged; + + }, + + clone: function ( uniforms_src ) { + + var u, p, parameter, parameter_src, uniforms_dst = {}; + + for ( u in uniforms_src ) { + + uniforms_dst[ u ] = {}; + + for ( p in uniforms_src[ u ] ) { + + parameter_src = uniforms_src[ u ][ p ]; + + if ( parameter_src instanceof THREE.Color || + parameter_src instanceof THREE.Vector2 || + parameter_src instanceof THREE.Vector3 || + parameter_src instanceof THREE.Vector4 || + parameter_src instanceof THREE.Matrix4 || + parameter_src instanceof THREE.Texture ) { + + uniforms_dst[ u ][ p ] = parameter_src.clone(); + + } else if ( parameter_src instanceof Array ) { + + uniforms_dst[ u ][ p ] = parameter_src.slice(); + + } else { + + uniforms_dst[ u ][ p ] = parameter_src; + + } + + } + + } + + return uniforms_dst; + + } + +}; + +// File:src/renderers/shaders/UniformsLib.js + +/** + * Uniforms library for shared webgl shaders + */ + +THREE.UniformsLib = { + + common: { + + "diffuse" : { type: "c", value: new THREE.Color( 0xeeeeee ) }, + "opacity" : { type: "f", value: 1.0 }, + + "map" : { type: "t", value: null }, + "offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) }, + + "lightMap" : { type: "t", value: null }, + "specularMap" : { type: "t", value: null }, + "alphaMap" : { type: "t", value: null }, + + "envMap" : { type: "t", value: null }, + "flipEnvMap" : { type: "f", value: - 1 }, + "useRefract" : { type: "i", value: 0 }, + "reflectivity" : { type: "f", value: 1.0 }, + "refractionRatio" : { type: "f", value: 0.98 }, + "combine" : { type: "i", value: 0 }, + + "morphTargetInfluences" : { type: "f", value: 0 } + + }, + + bump: { + + "bumpMap" : { type: "t", value: null }, + "bumpScale" : { type: "f", value: 1 } + + }, + + normalmap: { + + "normalMap" : { type: "t", value: null }, + "normalScale" : { type: "v2", value: new THREE.Vector2( 1, 1 ) } + }, + + fog : { + + "fogDensity" : { type: "f", value: 0.00025 }, + "fogNear" : { type: "f", value: 1 }, + "fogFar" : { type: "f", value: 2000 }, + "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) } + + }, + + lights: { + + "ambientLightColor" : { type: "fv", value: [] }, + + "directionalLightDirection" : { type: "fv", value: [] }, + "directionalLightColor" : { type: "fv", value: [] }, + + "hemisphereLightDirection" : { type: "fv", value: [] }, + "hemisphereLightSkyColor" : { type: "fv", value: [] }, + "hemisphereLightGroundColor" : { type: "fv", value: [] }, + + "pointLightColor" : { type: "fv", value: [] }, + "pointLightPosition" : { type: "fv", value: [] }, + "pointLightDistance" : { type: "fv1", value: [] }, + + "spotLightColor" : { type: "fv", value: [] }, + "spotLightPosition" : { type: "fv", value: [] }, + "spotLightDirection" : { type: "fv", value: [] }, + "spotLightDistance" : { type: "fv1", value: [] }, + "spotLightAngleCos" : { type: "fv1", value: [] }, + "spotLightExponent" : { type: "fv1", value: [] } + + }, + + particle: { + + "psColor" : { type: "c", value: new THREE.Color( 0xeeeeee ) }, + "opacity" : { type: "f", value: 1.0 }, + "size" : { type: "f", value: 1.0 }, + "scale" : { type: "f", value: 1.0 }, + "map" : { type: "t", value: null }, + + "fogDensity" : { type: "f", value: 0.00025 }, + "fogNear" : { type: "f", value: 1 }, + "fogFar" : { type: "f", value: 2000 }, + "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) } + + }, + + shadowmap: { + + "shadowMap": { type: "tv", value: [] }, + "shadowMapSize": { type: "v2v", value: [] }, + + "shadowBias" : { type: "fv1", value: [] }, + "shadowDarkness": { type: "fv1", value: [] }, + + "shadowMatrix" : { type: "m4v", value: [] } + + } + +}; + +// File:src/renderers/shaders/ShaderLib.js + +/** + * Webgl Shader Library for three.js + * + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + */ + + +THREE.ShaderLib = { + + 'basic': { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "shadowmap" ] + + ] ), + + vertexShader: [ + + THREE.ShaderChunk[ "map_pars_vertex" ], + THREE.ShaderChunk[ "lightmap_pars_vertex" ], + THREE.ShaderChunk[ "envmap_pars_vertex" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "map_vertex" ], + THREE.ShaderChunk[ "lightmap_vertex" ], + THREE.ShaderChunk[ "color_vertex" ], + THREE.ShaderChunk[ "skinbase_vertex" ], + + " #ifdef USE_ENVMAP", + + THREE.ShaderChunk[ "morphnormal_vertex" ], + THREE.ShaderChunk[ "skinnormal_vertex" ], + THREE.ShaderChunk[ "defaultnormal_vertex" ], + + " #endif", + + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "default_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "envmap_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], + + "}" + + ].join("\n"), + + fragmentShader: [ + + "uniform vec3 diffuse;", + "uniform float opacity;", + + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "map_pars_fragment" ], + THREE.ShaderChunk[ "alphamap_pars_fragment" ], + THREE.ShaderChunk[ "lightmap_pars_fragment" ], + THREE.ShaderChunk[ "envmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "specularmap_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " gl_FragColor = vec4( diffuse, opacity );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "map_fragment" ], + THREE.ShaderChunk[ "alphamap_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "specularmap_fragment" ], + THREE.ShaderChunk[ "lightmap_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "envmap_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], + + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + + THREE.ShaderChunk[ "fog_fragment" ], + + "}" + + ].join("\n") + + }, + + 'lambert': { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "lights" ], + THREE.UniformsLib[ "shadowmap" ], + + { + "ambient" : { type: "c", value: new THREE.Color( 0xffffff ) }, + "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) }, + "wrapRGB" : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) } + } + + ] ), + + vertexShader: [ + + "#define LAMBERT", + + "varying vec3 vLightFront;", + + "#ifdef DOUBLE_SIDED", + + " varying vec3 vLightBack;", + + "#endif", + + THREE.ShaderChunk[ "map_pars_vertex" ], + THREE.ShaderChunk[ "lightmap_pars_vertex" ], + THREE.ShaderChunk[ "envmap_pars_vertex" ], + THREE.ShaderChunk[ "lights_lambert_pars_vertex" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "map_vertex" ], + THREE.ShaderChunk[ "lightmap_vertex" ], + THREE.ShaderChunk[ "color_vertex" ], + + THREE.ShaderChunk[ "morphnormal_vertex" ], + THREE.ShaderChunk[ "skinbase_vertex" ], + THREE.ShaderChunk[ "skinnormal_vertex" ], + THREE.ShaderChunk[ "defaultnormal_vertex" ], + + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "default_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "envmap_vertex" ], + THREE.ShaderChunk[ "lights_lambert_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], + + "}" + + ].join("\n"), + + fragmentShader: [ + + "uniform float opacity;", + + "varying vec3 vLightFront;", + + "#ifdef DOUBLE_SIDED", + + " varying vec3 vLightBack;", + + "#endif", + + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "map_pars_fragment" ], + THREE.ShaderChunk[ "alphamap_pars_fragment" ], + THREE.ShaderChunk[ "lightmap_pars_fragment" ], + THREE.ShaderChunk[ "envmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "specularmap_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " gl_FragColor = vec4( vec3( 1.0 ), opacity );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "map_fragment" ], + THREE.ShaderChunk[ "alphamap_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "specularmap_fragment" ], + + " #ifdef DOUBLE_SIDED", + + //"float isFront = float( gl_FrontFacing );", + //"gl_FragColor.xyz *= isFront * vLightFront + ( 1.0 - isFront ) * vLightBack;", + + " if ( gl_FrontFacing )", + " gl_FragColor.xyz *= vLightFront;", + " else", + " gl_FragColor.xyz *= vLightBack;", + + " #else", + + " gl_FragColor.xyz *= vLightFront;", + + " #endif", + + THREE.ShaderChunk[ "lightmap_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "envmap_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], + + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + + THREE.ShaderChunk[ "fog_fragment" ], + + "}" + + ].join("\n") + + }, + + 'phong': { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "bump" ], + THREE.UniformsLib[ "normalmap" ], + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "lights" ], + THREE.UniformsLib[ "shadowmap" ], + + { + "ambient" : { type: "c", value: new THREE.Color( 0xffffff ) }, + "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) }, + "specular" : { type: "c", value: new THREE.Color( 0x111111 ) }, + "shininess": { type: "f", value: 30 }, + "wrapRGB" : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) } + } + + ] ), + + vertexShader: [ + + "#define PHONG", + + "varying vec3 vViewPosition;", + "varying vec3 vNormal;", + + THREE.ShaderChunk[ "map_pars_vertex" ], + THREE.ShaderChunk[ "lightmap_pars_vertex" ], + THREE.ShaderChunk[ "envmap_pars_vertex" ], + THREE.ShaderChunk[ "lights_phong_pars_vertex" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "map_vertex" ], + THREE.ShaderChunk[ "lightmap_vertex" ], + THREE.ShaderChunk[ "color_vertex" ], + + THREE.ShaderChunk[ "morphnormal_vertex" ], + THREE.ShaderChunk[ "skinbase_vertex" ], + THREE.ShaderChunk[ "skinnormal_vertex" ], + THREE.ShaderChunk[ "defaultnormal_vertex" ], + + " vNormal = normalize( transformedNormal );", + + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "default_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + " vViewPosition = -mvPosition.xyz;", + + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "envmap_vertex" ], + THREE.ShaderChunk[ "lights_phong_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], + + "}" + + ].join("\n"), + + fragmentShader: [ + + "uniform vec3 diffuse;", + "uniform float opacity;", + + "uniform vec3 ambient;", + "uniform vec3 emissive;", + "uniform vec3 specular;", + "uniform float shininess;", + + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "map_pars_fragment" ], + THREE.ShaderChunk[ "alphamap_pars_fragment" ], + THREE.ShaderChunk[ "lightmap_pars_fragment" ], + THREE.ShaderChunk[ "envmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "lights_phong_pars_fragment" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "bumpmap_pars_fragment" ], + THREE.ShaderChunk[ "normalmap_pars_fragment" ], + THREE.ShaderChunk[ "specularmap_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " gl_FragColor = vec4( vec3( 1.0 ), opacity );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "map_fragment" ], + THREE.ShaderChunk[ "alphamap_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "specularmap_fragment" ], + + THREE.ShaderChunk[ "lights_phong_fragment" ], + + THREE.ShaderChunk[ "lightmap_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "envmap_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], + + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + + THREE.ShaderChunk[ "fog_fragment" ], + + "}" + + ].join("\n") + + }, + + 'particle_basic': { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "particle" ], + THREE.UniformsLib[ "shadowmap" ] + + ] ), + + vertexShader: [ + + "uniform float size;", + "uniform float scale;", + + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "color_vertex" ], + + " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + + " #ifdef USE_SIZEATTENUATION", + " gl_PointSize = size * ( scale / length( mvPosition.xyz ) );", + " #else", + " gl_PointSize = size;", + " #endif", + + " gl_Position = projectionMatrix * mvPosition;", + + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], + + "}" + + ].join("\n"), + + fragmentShader: [ + + "uniform vec3 psColor;", + "uniform float opacity;", + + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "map_particle_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " gl_FragColor = vec4( psColor, opacity );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "map_particle_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], + THREE.ShaderChunk[ "fog_fragment" ], + + "}" + + ].join("\n") + + }, + + 'dashed': { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "fog" ], + + { + "scale" : { type: "f", value: 1 }, + "dashSize" : { type: "f", value: 1 }, + "totalSize": { type: "f", value: 2 } + } + + ] ), + + vertexShader: [ + + "uniform float scale;", + "attribute float lineDistance;", + + "varying float vLineDistance;", + + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "color_vertex" ], + + " vLineDistance = scale * lineDistance;", + + " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + " gl_Position = projectionMatrix * mvPosition;", + + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + "}" + + ].join("\n"), + + fragmentShader: [ + + "uniform vec3 diffuse;", + "uniform float opacity;", + + "uniform float dashSize;", + "uniform float totalSize;", + + "varying float vLineDistance;", + + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " if ( mod( vLineDistance, totalSize ) > dashSize ) {", + + " discard;", + + " }", + + " gl_FragColor = vec4( diffuse, opacity );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "fog_fragment" ], + + "}" + + ].join("\n") + + }, + + 'depth': { + + uniforms: { + + "mNear": { type: "f", value: 1.0 }, + "mFar" : { type: "f", value: 2000.0 }, + "opacity" : { type: "f", value: 1.0 } + + }, + + vertexShader: [ + + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "default_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + "}" + + ].join("\n"), + + fragmentShader: [ + + "uniform float mNear;", + "uniform float mFar;", + "uniform float opacity;", + + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + + " #ifdef USE_LOGDEPTHBUF_EXT", + + " float depth = gl_FragDepthEXT / gl_FragCoord.w;", + + " #else", + + " float depth = gl_FragCoord.z / gl_FragCoord.w;", + + " #endif", + + " float color = 1.0 - smoothstep( mNear, mFar, depth );", + " gl_FragColor = vec4( vec3( color ), opacity );", + + "}" + + ].join("\n") + + }, + + 'normal': { + + uniforms: { + + "opacity" : { type: "f", value: 1.0 } + + }, + + vertexShader: [ + + "varying vec3 vNormal;", + + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + " vNormal = normalize( normalMatrix * normal );", + + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "default_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + "}" + + ].join("\n"), + + fragmentShader: [ + + "uniform float opacity;", + "varying vec3 vNormal;", + + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " gl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + + "}" + + ].join("\n") + + }, + + /* ------------------------------------------------------------------------- + // Normal map shader + // - Blinn-Phong + // - normal + diffuse + specular + AO + displacement + reflection + shadow maps + // - point and directional lights (use with "lights: true" material option) + ------------------------------------------------------------------------- */ + + 'normalmap' : { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "lights" ], + THREE.UniformsLib[ "shadowmap" ], + + { + + "enableAO" : { type: "i", value: 0 }, + "enableDiffuse" : { type: "i", value: 0 }, + "enableSpecular" : { type: "i", value: 0 }, + "enableReflection" : { type: "i", value: 0 }, + "enableDisplacement": { type: "i", value: 0 }, + + "tDisplacement": { type: "t", value: null }, // must go first as this is vertex texture + "tDiffuse" : { type: "t", value: null }, + "tCube" : { type: "t", value: null }, + "tNormal" : { type: "t", value: null }, + "tSpecular" : { type: "t", value: null }, + "tAO" : { type: "t", value: null }, + + "uNormalScale": { type: "v2", value: new THREE.Vector2( 1, 1 ) }, + + "uDisplacementBias": { type: "f", value: 0.0 }, + "uDisplacementScale": { type: "f", value: 1.0 }, + + "diffuse": { type: "c", value: new THREE.Color( 0xffffff ) }, + "specular": { type: "c", value: new THREE.Color( 0x111111 ) }, + "ambient": { type: "c", value: new THREE.Color( 0xffffff ) }, + "shininess": { type: "f", value: 30 }, + "opacity": { type: "f", value: 1 }, + + "useRefract": { type: "i", value: 0 }, + "refractionRatio": { type: "f", value: 0.98 }, + "reflectivity": { type: "f", value: 0.5 }, + + "uOffset" : { type: "v2", value: new THREE.Vector2( 0, 0 ) }, + "uRepeat" : { type: "v2", value: new THREE.Vector2( 1, 1 ) }, + + "wrapRGB" : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) } + + } + + ] ), + + fragmentShader: [ + + "uniform vec3 ambient;", + "uniform vec3 diffuse;", + "uniform vec3 specular;", + "uniform float shininess;", + "uniform float opacity;", + + "uniform bool enableDiffuse;", + "uniform bool enableSpecular;", + "uniform bool enableAO;", + "uniform bool enableReflection;", + + "uniform sampler2D tDiffuse;", + "uniform sampler2D tNormal;", + "uniform sampler2D tSpecular;", + "uniform sampler2D tAO;", + + "uniform samplerCube tCube;", + + "uniform vec2 uNormalScale;", + + "uniform bool useRefract;", + "uniform float refractionRatio;", + "uniform float reflectivity;", + + "varying vec3 vTangent;", + "varying vec3 vBinormal;", + "varying vec3 vNormal;", + "varying vec2 vUv;", + + "uniform vec3 ambientLightColor;", + + "#if MAX_DIR_LIGHTS > 0", + + " uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];", + " uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];", + + "#endif", + + "#if MAX_HEMI_LIGHTS > 0", + + " uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];", + " uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];", + " uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];", + + "#endif", + + "#if MAX_POINT_LIGHTS > 0", + + " uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];", + " uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];", + " uniform float pointLightDistance[ MAX_POINT_LIGHTS ];", + + "#endif", + + "#if MAX_SPOT_LIGHTS > 0", + + " uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];", + " uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];", + " uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];", + " uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];", + " uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];", + " uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];", + + "#endif", + + "#ifdef WRAP_AROUND", + + " uniform vec3 wrapRGB;", + + "#endif", + + "varying vec3 vWorldPosition;", + "varying vec3 vViewPosition;", + + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + + " gl_FragColor = vec4( vec3( 1.0 ), opacity );", + + " vec3 specularTex = vec3( 1.0 );", + + " vec3 normalTex = texture2D( tNormal, vUv ).xyz * 2.0 - 1.0;", + " normalTex.xy *= uNormalScale;", + " normalTex = normalize( normalTex );", + + " if( enableDiffuse ) {", + + " #ifdef GAMMA_INPUT", + + " vec4 texelColor = texture2D( tDiffuse, vUv );", + " texelColor.xyz *= texelColor.xyz;", + + " gl_FragColor = gl_FragColor * texelColor;", + + " #else", + + " gl_FragColor = gl_FragColor * texture2D( tDiffuse, vUv );", + + " #endif", + + " }", + + " if( enableAO ) {", + + " #ifdef GAMMA_INPUT", + + " vec4 aoColor = texture2D( tAO, vUv );", + " aoColor.xyz *= aoColor.xyz;", + + " gl_FragColor.xyz = gl_FragColor.xyz * aoColor.xyz;", + + " #else", + + " gl_FragColor.xyz = gl_FragColor.xyz * texture2D( tAO, vUv ).xyz;", + + " #endif", + + " }", + + THREE.ShaderChunk[ "alphatest_fragment" ], + + " if( enableSpecular )", + " specularTex = texture2D( tSpecular, vUv ).xyz;", + + " mat3 tsb = mat3( normalize( vTangent ), normalize( vBinormal ), normalize( vNormal ) );", + " vec3 finalNormal = tsb * normalTex;", + + " #ifdef FLIP_SIDED", + + " finalNormal = -finalNormal;", + + " #endif", + + " vec3 normal = normalize( finalNormal );", + " vec3 viewPosition = normalize( vViewPosition );", + + // point lights + + " #if MAX_POINT_LIGHTS > 0", + + " vec3 pointDiffuse = vec3( 0.0 );", + " vec3 pointSpecular = vec3( 0.0 );", + + " for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {", + + " vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );", + " vec3 pointVector = lPosition.xyz + vViewPosition.xyz;", + + " float pointDistance = 1.0;", + " if ( pointLightDistance[ i ] > 0.0 )", + " pointDistance = 1.0 - min( ( length( pointVector ) / pointLightDistance[ i ] ), 1.0 );", + + " pointVector = normalize( pointVector );", + + // diffuse + + " #ifdef WRAP_AROUND", + + " float pointDiffuseWeightFull = max( dot( normal, pointVector ), 0.0 );", + " float pointDiffuseWeightHalf = max( 0.5 * dot( normal, pointVector ) + 0.5, 0.0 );", + + " vec3 pointDiffuseWeight = mix( vec3( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );", + + " #else", + + " float pointDiffuseWeight = max( dot( normal, pointVector ), 0.0 );", + + " #endif", + + " pointDiffuse += pointDistance * pointLightColor[ i ] * diffuse * pointDiffuseWeight;", + + // specular + + " vec3 pointHalfVector = normalize( pointVector + viewPosition );", + " float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );", + " float pointSpecularWeight = specularTex.r * max( pow( pointDotNormalHalf, shininess ), 0.0 );", + + " float specularNormalization = ( shininess + 2.0 ) / 8.0;", + + " vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( pointVector, pointHalfVector ), 0.0 ), 5.0 );", + " pointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * pointDistance * specularNormalization;", + + " }", + + " #endif", + + // spot lights + + " #if MAX_SPOT_LIGHTS > 0", + + " vec3 spotDiffuse = vec3( 0.0 );", + " vec3 spotSpecular = vec3( 0.0 );", + + " for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {", + + " vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );", + " vec3 spotVector = lPosition.xyz + vViewPosition.xyz;", + + " float spotDistance = 1.0;", + " if ( spotLightDistance[ i ] > 0.0 )", + " spotDistance = 1.0 - min( ( length( spotVector ) / spotLightDistance[ i ] ), 1.0 );", + + " spotVector = normalize( spotVector );", + + " float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );", + + " if ( spotEffect > spotLightAngleCos[ i ] ) {", + + " spotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );", + + // diffuse + + " #ifdef WRAP_AROUND", + + " float spotDiffuseWeightFull = max( dot( normal, spotVector ), 0.0 );", + " float spotDiffuseWeightHalf = max( 0.5 * dot( normal, spotVector ) + 0.5, 0.0 );", + + " vec3 spotDiffuseWeight = mix( vec3( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );", + + " #else", + + " float spotDiffuseWeight = max( dot( normal, spotVector ), 0.0 );", + + " #endif", + + " spotDiffuse += spotDistance * spotLightColor[ i ] * diffuse * spotDiffuseWeight * spotEffect;", + + // specular + + " vec3 spotHalfVector = normalize( spotVector + viewPosition );", + " float spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );", + " float spotSpecularWeight = specularTex.r * max( pow( spotDotNormalHalf, shininess ), 0.0 );", + + " float specularNormalization = ( shininess + 2.0 ) / 8.0;", + + " vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( spotVector, spotHalfVector ), 0.0 ), 5.0 );", + " spotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * spotDistance * specularNormalization * spotEffect;", + + " }", + + " }", + + " #endif", + + // directional lights + + " #if MAX_DIR_LIGHTS > 0", + + " vec3 dirDiffuse = vec3( 0.0 );", + " vec3 dirSpecular = vec3( 0.0 );", + + " for( int i = 0; i < MAX_DIR_LIGHTS; i++ ) {", + + " vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );", + " vec3 dirVector = normalize( lDirection.xyz );", + + // diffuse + + " #ifdef WRAP_AROUND", + + " float directionalLightWeightingFull = max( dot( normal, dirVector ), 0.0 );", + " float directionalLightWeightingHalf = max( 0.5 * dot( normal, dirVector ) + 0.5, 0.0 );", + + " vec3 dirDiffuseWeight = mix( vec3( directionalLightWeightingFull ), vec3( directionalLightWeightingHalf ), wrapRGB );", + + " #else", + + " float dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );", + + " #endif", + + " dirDiffuse += directionalLightColor[ i ] * diffuse * dirDiffuseWeight;", + + // specular + + " vec3 dirHalfVector = normalize( dirVector + viewPosition );", + " float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );", + " float dirSpecularWeight = specularTex.r * max( pow( dirDotNormalHalf, shininess ), 0.0 );", + + " float specularNormalization = ( shininess + 2.0 ) / 8.0;", + + " vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( dirVector, dirHalfVector ), 0.0 ), 5.0 );", + " dirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;", + + " }", + + " #endif", + + // hemisphere lights + + " #if MAX_HEMI_LIGHTS > 0", + + " vec3 hemiDiffuse = vec3( 0.0 );", + " vec3 hemiSpecular = vec3( 0.0 );" , + + " for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {", + + " vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );", + " vec3 lVector = normalize( lDirection.xyz );", + + // diffuse + + " float dotProduct = dot( normal, lVector );", + " float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;", + + " vec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );", + + " hemiDiffuse += diffuse * hemiColor;", + + // specular (sky light) + + + " vec3 hemiHalfVectorSky = normalize( lVector + viewPosition );", + " float hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;", + " float hemiSpecularWeightSky = specularTex.r * max( pow( max( hemiDotNormalHalfSky, 0.0 ), shininess ), 0.0 );", + + // specular (ground light) + + " vec3 lVectorGround = -lVector;", + + " vec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );", + " float hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;", + " float hemiSpecularWeightGround = specularTex.r * max( pow( max( hemiDotNormalHalfGround, 0.0 ), shininess ), 0.0 );", + + " float dotProductGround = dot( normal, lVectorGround );", + + " float specularNormalization = ( shininess + 2.0 ) / 8.0;", + + " vec3 schlickSky = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, hemiHalfVectorSky ), 0.0 ), 5.0 );", + " vec3 schlickGround = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 0.0 ), 5.0 );", + " hemiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );", + + " }", + + " #endif", + + // all lights contribution summation + + " vec3 totalDiffuse = vec3( 0.0 );", + " vec3 totalSpecular = vec3( 0.0 );", + + " #if MAX_DIR_LIGHTS > 0", + + " totalDiffuse += dirDiffuse;", + " totalSpecular += dirSpecular;", + + " #endif", + + " #if MAX_HEMI_LIGHTS > 0", + + " totalDiffuse += hemiDiffuse;", + " totalSpecular += hemiSpecular;", + + " #endif", + + " #if MAX_POINT_LIGHTS > 0", + + " totalDiffuse += pointDiffuse;", + " totalSpecular += pointSpecular;", + + " #endif", + + " #if MAX_SPOT_LIGHTS > 0", + + " totalDiffuse += spotDiffuse;", + " totalSpecular += spotSpecular;", + + " #endif", + + " #ifdef METAL", + + " gl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * ambient + totalSpecular );", + + " #else", + + " gl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * ambient ) + totalSpecular;", + + " #endif", + + " if ( enableReflection ) {", + + " vec3 vReflect;", + " vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );", + + " if ( useRefract ) {", + + " vReflect = refract( cameraToVertex, normal, refractionRatio );", + + " } else {", + + " vReflect = reflect( cameraToVertex, normal );", + + " }", + + " vec4 cubeColor = textureCube( tCube, vec3( -vReflect.x, vReflect.yz ) );", + + " #ifdef GAMMA_INPUT", + + " cubeColor.xyz *= cubeColor.xyz;", + + " #endif", + + " gl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularTex.r * reflectivity );", + + " }", + + THREE.ShaderChunk[ "shadowmap_fragment" ], + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + THREE.ShaderChunk[ "fog_fragment" ], + + "}" + + ].join("\n"), + + vertexShader: [ + + "attribute vec4 tangent;", + + "uniform vec2 uOffset;", + "uniform vec2 uRepeat;", + + "uniform bool enableDisplacement;", + + "#ifdef VERTEX_TEXTURES", + + " uniform sampler2D tDisplacement;", + " uniform float uDisplacementScale;", + " uniform float uDisplacementBias;", + + "#endif", + + "varying vec3 vTangent;", + "varying vec3 vBinormal;", + "varying vec3 vNormal;", + "varying vec2 vUv;", + + "varying vec3 vWorldPosition;", + "varying vec3 vViewPosition;", + + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "skinbase_vertex" ], + THREE.ShaderChunk[ "skinnormal_vertex" ], + + // normal, tangent and binormal vectors + + " #ifdef USE_SKINNING", + + " vNormal = normalize( normalMatrix * skinnedNormal.xyz );", + + " vec4 skinnedTangent = skinMatrix * vec4( tangent.xyz, 0.0 );", + " vTangent = normalize( normalMatrix * skinnedTangent.xyz );", + + " #else", + + " vNormal = normalize( normalMatrix * normal );", + " vTangent = normalize( normalMatrix * tangent.xyz );", + + " #endif", + + " vBinormal = normalize( cross( vNormal, vTangent ) * tangent.w );", + + " vUv = uv * uRepeat + uOffset;", + + // displacement mapping + + " vec3 displacedPosition;", + + " #ifdef VERTEX_TEXTURES", + + " if ( enableDisplacement ) {", + + " vec3 dv = texture2D( tDisplacement, uv ).xyz;", + " float df = uDisplacementScale * dv.x + uDisplacementBias;", + " displacedPosition = position + normalize( normal ) * df;", + + " } else {", + + " #ifdef USE_SKINNING", + + " vec4 skinVertex = bindMatrix * vec4( position, 1.0 );", + + " vec4 skinned = vec4( 0.0 );", + " skinned += boneMatX * skinVertex * skinWeight.x;", + " skinned += boneMatY * skinVertex * skinWeight.y;", + " skinned += boneMatZ * skinVertex * skinWeight.z;", + " skinned += boneMatW * skinVertex * skinWeight.w;", + " skinned = bindMatrixInverse * skinned;", + + " displacedPosition = skinned.xyz;", + + " #else", + + " displacedPosition = position;", + + " #endif", + + " }", + + " #else", + + " #ifdef USE_SKINNING", + + " vec4 skinVertex = bindMatrix * vec4( position, 1.0 );", + + " vec4 skinned = vec4( 0.0 );", + " skinned += boneMatX * skinVertex * skinWeight.x;", + " skinned += boneMatY * skinVertex * skinWeight.y;", + " skinned += boneMatZ * skinVertex * skinWeight.z;", + " skinned += boneMatW * skinVertex * skinWeight.w;", + " skinned = bindMatrixInverse * skinned;", + + " displacedPosition = skinned.xyz;", + + " #else", + + " displacedPosition = position;", + + " #endif", + + " #endif", + + // + + " vec4 mvPosition = modelViewMatrix * vec4( displacedPosition, 1.0 );", + " vec4 worldPosition = modelMatrix * vec4( displacedPosition, 1.0 );", + + " gl_Position = projectionMatrix * mvPosition;", + + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + // + + " vWorldPosition = worldPosition.xyz;", + " vViewPosition = -mvPosition.xyz;", + + // shadows + + " #ifdef USE_SHADOWMAP", + + " for( int i = 0; i < MAX_SHADOWS; i ++ ) {", + + " vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;", + + " }", + + " #endif", + + "}" + + ].join("\n") + + }, + + /* ------------------------------------------------------------------------- + // Cube map shader + ------------------------------------------------------------------------- */ + + 'cube': { + + uniforms: { "tCube": { type: "t", value: null }, + "tFlip": { type: "f", value: - 1 } }, + + vertexShader: [ + + "varying vec3 vWorldPosition;", + + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + " vec4 worldPosition = modelMatrix * vec4( position, 1.0 );", + " vWorldPosition = worldPosition.xyz;", + + " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + "}" + + ].join("\n"), + + fragmentShader: [ + + "uniform samplerCube tCube;", + "uniform float tFlip;", + + "varying vec3 vWorldPosition;", + + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + + "}" + + ].join("\n") + + }, + + /* Depth encoding into RGBA texture + * + * based on SpiderGL shadow map example + * http://spidergl.org/example.php?id=6 + * + * originally from + * http://www.gamedev.net/topic/442138-packing-a-float-into-a-a8r8g8b8-texture-shader/page__whichpage__1%25EF%25BF%25BD + * + * see also + * http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ + */ + + 'depthRGBA': { + + uniforms: {}, + + vertexShader: [ + + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "skinbase_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "default_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + "}" + + ].join("\n"), + + fragmentShader: [ + + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "vec4 pack_depth( const in float depth ) {", + + " const vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );", + " const vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );", + " vec4 res = mod( depth * bit_shift * vec4( 255 ), vec4( 256 ) ) / vec4( 255 );", // " vec4 res = fract( depth * bit_shift );", + " res -= res.xxyz * bit_mask;", + " return res;", + + "}", + + "void main() {", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + + " #ifdef USE_LOGDEPTHBUF_EXT", + + " gl_FragData[ 0 ] = pack_depth( gl_FragDepthEXT );", + + " #else", + + " gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );", + + " #endif", + + //"gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z / gl_FragCoord.w );", + //"float z = ( ( gl_FragCoord.z / gl_FragCoord.w ) - 3.0 ) / ( 4000.0 - 3.0 );", + //"gl_FragData[ 0 ] = pack_depth( z );", + //"gl_FragData[ 0 ] = vec4( z, z, z, 1.0 );", + + "}" + + ].join("\n") + + } + +}; + +// File:src/renderers/WebGLRenderer.js + +/** + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author szimek / https://github.com/szimek/ + */ + +THREE.WebGLRenderer = function ( parameters ) { + + console.log( 'THREE.WebGLRenderer', THREE.REVISION ); + + parameters = parameters || {}; + + var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' ), + _context = parameters.context !== undefined ? parameters.context : null, + + _precision = parameters.precision !== undefined ? parameters.precision : 'highp', + + _alpha = parameters.alpha !== undefined ? parameters.alpha : false, + _depth = parameters.depth !== undefined ? parameters.depth : true, + _stencil = parameters.stencil !== undefined ? parameters.stencil : true, + _antialias = parameters.antialias !== undefined ? parameters.antialias : false, + _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, + _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, + _logarithmicDepthBuffer = parameters.logarithmicDepthBuffer !== undefined ? parameters.logarithmicDepthBuffer : false, + + _clearColor = new THREE.Color( 0x000000 ), + _clearAlpha = 0; + + var opaqueObjects = []; + var transparentObjects = []; + + // public properties + + this.domElement = _canvas; + this.context = null; + this.devicePixelRatio = parameters.devicePixelRatio !== undefined + ? parameters.devicePixelRatio + : self.devicePixelRatio !== undefined + ? self.devicePixelRatio + : 1; + + // clearing + + this.autoClear = true; + this.autoClearColor = true; + this.autoClearDepth = true; + this.autoClearStencil = true; + + // scene graph + + this.sortObjects = true; + + // physically based shading + + this.gammaInput = false; + this.gammaOutput = false; + + // shadow map + + this.shadowMapEnabled = false; + this.shadowMapAutoUpdate = true; + this.shadowMapType = THREE.PCFShadowMap; + this.shadowMapCullFace = THREE.CullFaceFront; + this.shadowMapDebug = false; + this.shadowMapCascade = false; + + // morphs + + this.maxMorphTargets = 8; + this.maxMorphNormals = 4; + + // flags + + this.autoScaleCubemaps = true; + + // custom render plugins + + this.renderPluginsPre = []; + this.renderPluginsPost = []; + + // info + + this.info = { + + memory: { + + programs: 0, + geometries: 0, + textures: 0 + + }, + + render: { + + calls: 0, + vertices: 0, + faces: 0, + points: 0 + + } + + }; + + // internal properties + + var _this = this, + + _programs = [], + + // internal state cache + + _currentProgram = null, + _currentFramebuffer = null, + _currentMaterialId = - 1, + _currentGeometryGroupHash = null, + _currentCamera = null, + + _usedTextureUnits = 0, + + // GL state cache + + _oldDoubleSided = - 1, + _oldFlipSided = - 1, + + _oldBlending = - 1, + + _oldBlendEquation = - 1, + _oldBlendSrc = - 1, + _oldBlendDst = - 1, + + _oldDepthTest = - 1, + _oldDepthWrite = - 1, + + _oldPolygonOffset = null, + _oldPolygonOffsetFactor = null, + _oldPolygonOffsetUnits = null, + + _oldLineWidth = null, + + _viewportX = 0, + _viewportY = 0, + _viewportWidth = _canvas.width, + _viewportHeight = _canvas.height, + _currentWidth = 0, + _currentHeight = 0, + + _newAttributes = new Uint8Array( 16 ), + _enabledAttributes = new Uint8Array( 16 ), + + // frustum + + _frustum = new THREE.Frustum(), + + // camera matrices cache + + _projScreenMatrix = new THREE.Matrix4(), + _projScreenMatrixPS = new THREE.Matrix4(), + + _vector3 = new THREE.Vector3(), + + // light arrays cache + + _direction = new THREE.Vector3(), + + _lightsNeedUpdate = true, + + _lights = { + + ambient: [ 0, 0, 0 ], + directional: { length: 0, colors:[], positions: [] }, + point: { length: 0, colors: [], positions: [], distances: [] }, + spot: { length: 0, colors: [], positions: [], distances: [], directions: [], anglesCos: [], exponents: [] }, + hemi: { length: 0, skyColors: [], groundColors: [], positions: [] } + + }; + + // initialize + + var _gl; + + var _glExtensionTextureFloat; + var _glExtensionTextureFloatLinear; + var _glExtensionStandardDerivatives; + var _glExtensionTextureFilterAnisotropic; + var _glExtensionCompressedTextureS3TC; + var _glExtensionElementIndexUint; + var _glExtensionFragDepth; + + + initGL(); + + setDefaultGLState(); + + this.context = _gl; + + // GPU capabilities + + var _maxTextures = _gl.getParameter( _gl.MAX_TEXTURE_IMAGE_UNITS ); + var _maxVertexTextures = _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); + var _maxTextureSize = _gl.getParameter( _gl.MAX_TEXTURE_SIZE ); + var _maxCubemapSize = _gl.getParameter( _gl.MAX_CUBE_MAP_TEXTURE_SIZE ); + + var _maxAnisotropy = _glExtensionTextureFilterAnisotropic ? _gl.getParameter( _glExtensionTextureFilterAnisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT ) : 0; + + var _supportsVertexTextures = ( _maxVertexTextures > 0 ); + var _supportsBoneTextures = _supportsVertexTextures && _glExtensionTextureFloat; + + var _compressedTextureFormats = _glExtensionCompressedTextureS3TC ? _gl.getParameter( _gl.COMPRESSED_TEXTURE_FORMATS ) : []; + + // + + var _vertexShaderPrecisionHighpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.HIGH_FLOAT ); + var _vertexShaderPrecisionMediumpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.MEDIUM_FLOAT ); + var _vertexShaderPrecisionLowpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.LOW_FLOAT ); + + var _fragmentShaderPrecisionHighpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.HIGH_FLOAT ); + var _fragmentShaderPrecisionMediumpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.MEDIUM_FLOAT ); + var _fragmentShaderPrecisionLowpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.LOW_FLOAT ); + + // clamp precision to maximum available + + var highpAvailable = _vertexShaderPrecisionHighpFloat.precision > 0 && _fragmentShaderPrecisionHighpFloat.precision > 0; + var mediumpAvailable = _vertexShaderPrecisionMediumpFloat.precision > 0 && _fragmentShaderPrecisionMediumpFloat.precision > 0; + + if ( _precision === 'highp' && ! highpAvailable ) { + + if ( mediumpAvailable ) { + + _precision = 'mediump'; + console.warn( 'THREE.WebGLRenderer: highp not supported, using mediump.' ); + + } else { + + _precision = 'lowp'; + console.warn( 'THREE.WebGLRenderer: highp and mediump not supported, using lowp.' ); + + } + + } + + if ( _precision === 'mediump' && ! mediumpAvailable ) { + + _precision = 'lowp'; + console.warn( 'THREE.WebGLRenderer: mediump not supported, using lowp.' ); + + } + + // API + + this.getContext = function () { + + return _gl; + + }; + + this.supportsVertexTextures = function () { + + return _supportsVertexTextures; + + }; + + this.supportsFloatTextures = function () { + + return _glExtensionTextureFloat; + + }; + + this.supportsStandardDerivatives = function () { + + return _glExtensionStandardDerivatives; + + }; + + this.supportsCompressedTextureS3TC = function () { + + return _glExtensionCompressedTextureS3TC; + + }; + + this.getMaxAnisotropy = function () { + + return _maxAnisotropy; + + }; + + this.getPrecision = function () { + + return _precision; + + }; + + this.setSize = function ( width, height, updateStyle ) { + + _canvas.width = width * this.devicePixelRatio; + _canvas.height = height * this.devicePixelRatio; + + if ( updateStyle !== false ) { + + _canvas.style.width = width + 'px'; + _canvas.style.height = height + 'px'; + + } + + this.setViewport( 0, 0, width, height ); + + }; + + this.setViewport = function ( x, y, width, height ) { + + _viewportX = x * this.devicePixelRatio; + _viewportY = y * this.devicePixelRatio; + + _viewportWidth = width * this.devicePixelRatio; + _viewportHeight = height * this.devicePixelRatio; + + _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); + + }; + + this.setScissor = function ( x, y, width, height ) { + + _gl.scissor( + x * this.devicePixelRatio, + y * this.devicePixelRatio, + width * this.devicePixelRatio, + height * this.devicePixelRatio + ); + + }; + + this.enableScissorTest = function ( enable ) { + + enable ? _gl.enable( _gl.SCISSOR_TEST ) : _gl.disable( _gl.SCISSOR_TEST ); + + }; + + // Clearing + + this.setClearColor = function ( color, alpha ) { + + _clearColor.set( color ); + _clearAlpha = alpha !== undefined ? alpha : 1; + + _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); + + }; + + this.setClearColorHex = function ( hex, alpha ) { + + console.warn( 'THREE.WebGLRenderer: .setClearColorHex() is being removed. Use .setClearColor() instead.' ); + this.setClearColor( hex, alpha ); + + }; + + this.getClearColor = function () { + + return _clearColor; + + }; + + this.getClearAlpha = function () { + + return _clearAlpha; + + }; + + this.clear = function ( color, depth, stencil ) { + + var bits = 0; + + if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT; + if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT; + if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT; + + _gl.clear( bits ); + + }; + + this.clearColor = function () { + + _gl.clear( _gl.COLOR_BUFFER_BIT ); + + }; + + this.clearDepth = function () { + + _gl.clear( _gl.DEPTH_BUFFER_BIT ); + + }; + + this.clearStencil = function () { + + _gl.clear( _gl.STENCIL_BUFFER_BIT ); + + }; + + this.clearTarget = function ( renderTarget, color, depth, stencil ) { + + this.setRenderTarget( renderTarget ); + this.clear( color, depth, stencil ); + + }; + + // Plugins + + this.addPostPlugin = function ( plugin ) { + + plugin.init( this ); + this.renderPluginsPost.push( plugin ); + + }; + + this.addPrePlugin = function ( plugin ) { + + plugin.init( this ); + this.renderPluginsPre.push( plugin ); + + }; + + // Rendering + + this.updateShadowMap = function ( scene, camera ) { + + _currentProgram = null; + _oldBlending = - 1; + _oldDepthTest = - 1; + _oldDepthWrite = - 1; + _currentGeometryGroupHash = - 1; + _currentMaterialId = - 1; + _lightsNeedUpdate = true; + _oldDoubleSided = - 1; + _oldFlipSided = - 1; + + initObjects( scene ); + + this.shadowMapPlugin.update( scene, camera ); + + }; + + // Internal functions + + // Buffer allocation + + function createParticleBuffers ( geometry ) { + + geometry.__webglVertexBuffer = _gl.createBuffer(); + geometry.__webglColorBuffer = _gl.createBuffer(); + + _this.info.memory.geometries ++; + + }; + + function createLineBuffers ( geometry ) { + + geometry.__webglVertexBuffer = _gl.createBuffer(); + geometry.__webglColorBuffer = _gl.createBuffer(); + geometry.__webglLineDistanceBuffer = _gl.createBuffer(); + + _this.info.memory.geometries ++; + + }; + + function createMeshBuffers ( geometryGroup ) { + + geometryGroup.__webglVertexBuffer = _gl.createBuffer(); + geometryGroup.__webglNormalBuffer = _gl.createBuffer(); + geometryGroup.__webglTangentBuffer = _gl.createBuffer(); + geometryGroup.__webglColorBuffer = _gl.createBuffer(); + geometryGroup.__webglUVBuffer = _gl.createBuffer(); + geometryGroup.__webglUV2Buffer = _gl.createBuffer(); + + geometryGroup.__webglSkinIndicesBuffer = _gl.createBuffer(); + geometryGroup.__webglSkinWeightsBuffer = _gl.createBuffer(); + + geometryGroup.__webglFaceBuffer = _gl.createBuffer(); + geometryGroup.__webglLineBuffer = _gl.createBuffer(); + + var m, ml; + + if ( geometryGroup.numMorphTargets ) { + + geometryGroup.__webglMorphTargetsBuffers = []; + + for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) { + + geometryGroup.__webglMorphTargetsBuffers.push( _gl.createBuffer() ); + + } + + } + + if ( geometryGroup.numMorphNormals ) { + + geometryGroup.__webglMorphNormalsBuffers = []; + + for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) { + + geometryGroup.__webglMorphNormalsBuffers.push( _gl.createBuffer() ); + + } + + } + + _this.info.memory.geometries ++; + + }; + + // Events + + var onGeometryDispose = function ( event ) { + + var geometry = event.target; + + geometry.removeEventListener( 'dispose', onGeometryDispose ); + + deallocateGeometry( geometry ); + + }; + + var onTextureDispose = function ( event ) { + + var texture = event.target; + + texture.removeEventListener( 'dispose', onTextureDispose ); + + deallocateTexture( texture ); + + _this.info.memory.textures --; + + + }; + + var onRenderTargetDispose = function ( event ) { + + var renderTarget = event.target; + + renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); + + deallocateRenderTarget( renderTarget ); + + _this.info.memory.textures --; + + }; + + var onMaterialDispose = function ( event ) { + + var material = event.target; + + material.removeEventListener( 'dispose', onMaterialDispose ); + + deallocateMaterial( material ); + + }; + + // Buffer deallocation + + var deleteBuffers = function ( geometry ) { + + if ( geometry.__webglVertexBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglVertexBuffer ); + if ( geometry.__webglNormalBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglNormalBuffer ); + if ( geometry.__webglTangentBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglTangentBuffer ); + if ( geometry.__webglColorBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglColorBuffer ); + if ( geometry.__webglUVBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglUVBuffer ); + if ( geometry.__webglUV2Buffer !== undefined ) _gl.deleteBuffer( geometry.__webglUV2Buffer ); + + if ( geometry.__webglSkinIndicesBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglSkinIndicesBuffer ); + if ( geometry.__webglSkinWeightsBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglSkinWeightsBuffer ); + + if ( geometry.__webglFaceBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglFaceBuffer ); + if ( geometry.__webglLineBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglLineBuffer ); + + if ( geometry.__webglLineDistanceBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglLineDistanceBuffer ); + // custom attributes + + if ( geometry.__webglCustomAttributesList !== undefined ) { + + for ( var id in geometry.__webglCustomAttributesList ) { + + _gl.deleteBuffer( geometry.__webglCustomAttributesList[ id ].buffer ); + + } + + } + + _this.info.memory.geometries --; + + }; + + var deallocateGeometry = function ( geometry ) { + + geometry.__webglInit = undefined; + + if ( geometry instanceof THREE.BufferGeometry ) { + + var attributes = geometry.attributes; + + for ( var key in attributes ) { + + if ( attributes[ key ].buffer !== undefined ) { + + _gl.deleteBuffer( attributes[ key ].buffer ); + + } + + } + + _this.info.memory.geometries --; + + } else { + + if ( geometry.geometryGroups !== undefined ) { + + for ( var i = 0,l = geometry.geometryGroupsList.length; i 0 ) { + + geometryGroup.__uvArray = new Float32Array( nvertices * 2 ); + + } + + if ( geometry.faceVertexUvs.length > 1 ) { + + geometryGroup.__uv2Array = new Float32Array( nvertices * 2 ); + + } + + } + + if ( object.geometry.skinWeights.length && object.geometry.skinIndices.length ) { + + geometryGroup.__skinIndexArray = new Float32Array( nvertices * 4 ); + geometryGroup.__skinWeightArray = new Float32Array( nvertices * 4 ); + + } + + var UintArray = _glExtensionElementIndexUint !== null && ntris > 21845 ? Uint32Array : Uint16Array; // 65535 / 3 + + geometryGroup.__typeArray = UintArray; + geometryGroup.__faceArray = new UintArray( ntris * 3 ); + geometryGroup.__lineArray = new UintArray( nlines * 2 ); + + var m, ml; + + if ( geometryGroup.numMorphTargets ) { + + geometryGroup.__morphTargetsArrays = []; + + for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) { + + geometryGroup.__morphTargetsArrays.push( new Float32Array( nvertices * 3 ) ); + + } + + } + + if ( geometryGroup.numMorphNormals ) { + + geometryGroup.__morphNormalsArrays = []; + + for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) { + + geometryGroup.__morphNormalsArrays.push( new Float32Array( nvertices * 3 ) ); + + } + + } + + geometryGroup.__webglFaceCount = ntris * 3; + geometryGroup.__webglLineCount = nlines * 2; + + + // custom attributes + + if ( material.attributes ) { + + if ( geometryGroup.__webglCustomAttributesList === undefined ) { + + geometryGroup.__webglCustomAttributesList = []; + + } + + for ( var a in material.attributes ) { + + // Do a shallow copy of the attribute object so different geometryGroup chunks use different + // attribute buffers which are correctly indexed in the setMeshBuffers function + + var originalAttribute = material.attributes[ a ]; + + var attribute = {}; + + for ( var property in originalAttribute ) { + + attribute[ property ] = originalAttribute[ property ]; + + } + + if ( ! attribute.__webglInitialized || attribute.createUniqueBuffers ) { + + attribute.__webglInitialized = true; + + var size = 1; // "f" and "i" + + if ( attribute.type === 'v2' ) size = 2; + else if ( attribute.type === 'v3' ) size = 3; + else if ( attribute.type === 'v4' ) size = 4; + else if ( attribute.type === 'c' ) size = 3; + + attribute.size = size; + + attribute.array = new Float32Array( nvertices * size ); + + attribute.buffer = _gl.createBuffer(); + attribute.buffer.belongsToAttribute = a; + + originalAttribute.needsUpdate = true; + attribute.__original = originalAttribute; + + } + + geometryGroup.__webglCustomAttributesList.push( attribute ); + + } + + } + + geometryGroup.__inittedArrays = true; + + }; + + function getBufferMaterial( object, geometryGroup ) { + + return object.material instanceof THREE.MeshFaceMaterial + ? object.material.materials[ geometryGroup.materialIndex ] + : object.material; + + }; + + function materialNeedsSmoothNormals ( material ) { + + return material && material.shading !== undefined && material.shading === THREE.SmoothShading; + + }; + + function bufferGuessNormalType ( material ) { + + // only MeshBasicMaterial and MeshDepthMaterial don't need normals + + if ( ( material instanceof THREE.MeshBasicMaterial && ! material.envMap ) || material instanceof THREE.MeshDepthMaterial ) { + + return false; + + } + + if ( materialNeedsSmoothNormals( material ) ) { + + return THREE.SmoothShading; + + } else { + + return THREE.FlatShading; + + } + + }; + + function bufferGuessVertexColorType( material ) { + + if ( material.vertexColors ) { + + return material.vertexColors; + + } + + return false; + + }; + + function bufferGuessUVType( material ) { + + // material must use some texture to require uvs + + if ( material.map || + material.lightMap || + material.bumpMap || + material.normalMap || + material.specularMap || + material.alphaMap || + material instanceof THREE.ShaderMaterial ) { + + return true; + + } + + return false; + + }; + + // + + function initDirectBuffers( geometry ) { + + for ( var name in geometry.attributes ) { + + var bufferType = ( name === 'index' ) ? _gl.ELEMENT_ARRAY_BUFFER : _gl.ARRAY_BUFFER; + + var attribute = geometry.attributes[ name ]; + attribute.buffer = _gl.createBuffer(); + + _gl.bindBuffer( bufferType, attribute.buffer ); + _gl.bufferData( bufferType, attribute.array, _gl.STATIC_DRAW ); + + } + + } + + // Buffer setting + + function setParticleBuffers ( geometry, hint, object ) { + + var v, c, vertex, offset, index, color, + + vertices = geometry.vertices, + vl = vertices.length, + + colors = geometry.colors, + cl = colors.length, + + vertexArray = geometry.__vertexArray, + colorArray = geometry.__colorArray, + + sortArray = geometry.__sortArray, + + dirtyVertices = geometry.verticesNeedUpdate, + dirtyElements = geometry.elementsNeedUpdate, + dirtyColors = geometry.colorsNeedUpdate, + + customAttributes = geometry.__webglCustomAttributesList, + i, il, + a, ca, cal, value, + customAttribute; + + if ( object.sortParticles ) { + + _projScreenMatrixPS.copy( _projScreenMatrix ); + _projScreenMatrixPS.multiply( object.matrixWorld ); + + for ( v = 0; v < vl; v ++ ) { + + vertex = vertices[ v ]; + + _vector3.copy( vertex ); + _vector3.applyProjection( _projScreenMatrixPS ); + + sortArray[ v ] = [ _vector3.z, v ]; + + } + + sortArray.sort( numericalSort ); + + for ( v = 0; v < vl; v ++ ) { + + vertex = vertices[ sortArray[ v ][ 1 ] ]; + + offset = v * 3; + + vertexArray[ offset ] = vertex.x; + vertexArray[ offset + 1 ] = vertex.y; + vertexArray[ offset + 2 ] = vertex.z; + + } + + for ( c = 0; c < cl; c ++ ) { + + offset = c * 3; + + color = colors[ sortArray[ c ][ 1 ] ]; + + colorArray[ offset ] = color.r; + colorArray[ offset + 1 ] = color.g; + colorArray[ offset + 2 ] = color.b; + + } + + if ( customAttributes ) { + + for ( i = 0, il = customAttributes.length; i < il; i ++ ) { + + customAttribute = customAttributes[ i ]; + + if ( ! ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) ) continue; + + offset = 0; + + cal = customAttribute.value.length; + + if ( customAttribute.size === 1 ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + index = sortArray[ ca ][ 1 ]; + + customAttribute.array[ ca ] = customAttribute.value[ index ]; + + } + + } else if ( customAttribute.size === 2 ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + index = sortArray[ ca ][ 1 ]; + + value = customAttribute.value[ index ]; + + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + + offset += 2; + + } + + } else if ( customAttribute.size === 3 ) { + + if ( customAttribute.type === 'c' ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + index = sortArray[ ca ][ 1 ]; + + value = customAttribute.value[ index ]; + + customAttribute.array[ offset ] = value.r; + customAttribute.array[ offset + 1 ] = value.g; + customAttribute.array[ offset + 2 ] = value.b; + + offset += 3; + + } + + } else { + + for ( ca = 0; ca < cal; ca ++ ) { + + index = sortArray[ ca ][ 1 ]; + + value = customAttribute.value[ index ]; + + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + customAttribute.array[ offset + 2 ] = value.z; + + offset += 3; + + } + + } + + } else if ( customAttribute.size === 4 ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + index = sortArray[ ca ][ 1 ]; + + value = customAttribute.value[ index ]; + + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + customAttribute.array[ offset + 2 ] = value.z; + customAttribute.array[ offset + 3 ] = value.w; + + offset += 4; + + } + + } + + } + + } + + } else { + + if ( dirtyVertices ) { + + for ( v = 0; v < vl; v ++ ) { + + vertex = vertices[ v ]; + + offset = v * 3; + + vertexArray[ offset ] = vertex.x; + vertexArray[ offset + 1 ] = vertex.y; + vertexArray[ offset + 2 ] = vertex.z; + + } + + } + + if ( dirtyColors ) { + + for ( c = 0; c < cl; c ++ ) { + + color = colors[ c ]; + + offset = c * 3; + + colorArray[ offset ] = color.r; + colorArray[ offset + 1 ] = color.g; + colorArray[ offset + 2 ] = color.b; + + } + + } + + if ( customAttributes ) { + + for ( i = 0, il = customAttributes.length; i < il; i ++ ) { + + customAttribute = customAttributes[ i ]; + + if ( customAttribute.needsUpdate && + ( customAttribute.boundTo === undefined || + customAttribute.boundTo === 'vertices' ) ) { + + cal = customAttribute.value.length; + + offset = 0; + + if ( customAttribute.size === 1 ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + customAttribute.array[ ca ] = customAttribute.value[ ca ]; + + } + + } else if ( customAttribute.size === 2 ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + value = customAttribute.value[ ca ]; + + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + + offset += 2; + + } + + } else if ( customAttribute.size === 3 ) { + + if ( customAttribute.type === 'c' ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + value = customAttribute.value[ ca ]; + + customAttribute.array[ offset ] = value.r; + customAttribute.array[ offset + 1 ] = value.g; + customAttribute.array[ offset + 2 ] = value.b; + + offset += 3; + + } + + } else { + + for ( ca = 0; ca < cal; ca ++ ) { + + value = customAttribute.value[ ca ]; + + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + customAttribute.array[ offset + 2 ] = value.z; + + offset += 3; + + } + + } + + } else if ( customAttribute.size === 4 ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + value = customAttribute.value[ ca ]; + + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + customAttribute.array[ offset + 2 ] = value.z; + customAttribute.array[ offset + 3 ] = value.w; + + offset += 4; + + } + + } + + } + + } + + } + + } + + if ( dirtyVertices || object.sortParticles ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); + + } + + if ( dirtyColors || object.sortParticles ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); + + } + + if ( customAttributes ) { + + for ( i = 0, il = customAttributes.length; i < il; i ++ ) { + + customAttribute = customAttributes[ i ]; + + if ( customAttribute.needsUpdate || object.sortParticles ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); + + } + + } + + } + + } + + function setLineBuffers ( geometry, hint ) { + + var v, c, d, vertex, offset, color, + + vertices = geometry.vertices, + colors = geometry.colors, + lineDistances = geometry.lineDistances, + + vl = vertices.length, + cl = colors.length, + dl = lineDistances.length, + + vertexArray = geometry.__vertexArray, + colorArray = geometry.__colorArray, + lineDistanceArray = geometry.__lineDistanceArray, + + dirtyVertices = geometry.verticesNeedUpdate, + dirtyColors = geometry.colorsNeedUpdate, + dirtyLineDistances = geometry.lineDistancesNeedUpdate, + + customAttributes = geometry.__webglCustomAttributesList, + + i, il, + a, ca, cal, value, + customAttribute; + + if ( dirtyVertices ) { + + for ( v = 0; v < vl; v ++ ) { + + vertex = vertices[ v ]; + + offset = v * 3; + + vertexArray[ offset ] = vertex.x; + vertexArray[ offset + 1 ] = vertex.y; + vertexArray[ offset + 2 ] = vertex.z; + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); + + } + + if ( dirtyColors ) { + + for ( c = 0; c < cl; c ++ ) { + + color = colors[ c ]; + + offset = c * 3; + + colorArray[ offset ] = color.r; + colorArray[ offset + 1 ] = color.g; + colorArray[ offset + 2 ] = color.b; + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); + + } + + if ( dirtyLineDistances ) { + + for ( d = 0; d < dl; d ++ ) { + + lineDistanceArray[ d ] = lineDistances[ d ]; + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglLineDistanceBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, lineDistanceArray, hint ); + + } + + if ( customAttributes ) { + + for ( i = 0, il = customAttributes.length; i < il; i ++ ) { + + customAttribute = customAttributes[ i ]; + + if ( customAttribute.needsUpdate && + ( customAttribute.boundTo === undefined || + customAttribute.boundTo === 'vertices' ) ) { + + offset = 0; + + cal = customAttribute.value.length; + + if ( customAttribute.size === 1 ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + customAttribute.array[ ca ] = customAttribute.value[ ca ]; + + } + + } else if ( customAttribute.size === 2 ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + value = customAttribute.value[ ca ]; + + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + + offset += 2; + + } + + } else if ( customAttribute.size === 3 ) { + + if ( customAttribute.type === 'c' ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + value = customAttribute.value[ ca ]; + + customAttribute.array[ offset ] = value.r; + customAttribute.array[ offset + 1 ] = value.g; + customAttribute.array[ offset + 2 ] = value.b; + + offset += 3; + + } + + } else { + + for ( ca = 0; ca < cal; ca ++ ) { + + value = customAttribute.value[ ca ]; + + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + customAttribute.array[ offset + 2 ] = value.z; + + offset += 3; + + } + + } + + } else if ( customAttribute.size === 4 ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + value = customAttribute.value[ ca ]; + + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + customAttribute.array[ offset + 2 ] = value.z; + customAttribute.array[ offset + 3 ] = value.w; + + offset += 4; + + } + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); + + } + + } + + } + + } + + function setMeshBuffers( geometryGroup, object, hint, dispose, material ) { + + if ( ! geometryGroup.__inittedArrays ) { + + return; + + } + + var normalType = bufferGuessNormalType( material ), + vertexColorType = bufferGuessVertexColorType( material ), + uvType = bufferGuessUVType( material ), + + needsSmoothNormals = ( normalType === THREE.SmoothShading ); + + var f, fl, fi, face, + vertexNormals, faceNormal, normal, + vertexColors, faceColor, + vertexTangents, + uv, uv2, v1, v2, v3, v4, t1, t2, t3, t4, n1, n2, n3, n4, + c1, c2, c3, + sw1, sw2, sw3, sw4, + si1, si2, si3, si4, + sa1, sa2, sa3, sa4, + sb1, sb2, sb3, sb4, + m, ml, i, il, + vn, uvi, uv2i, + vk, vkl, vka, + nka, chf, faceVertexNormals, + a, + + vertexIndex = 0, + + offset = 0, + offset_uv = 0, + offset_uv2 = 0, + offset_face = 0, + offset_normal = 0, + offset_tangent = 0, + offset_line = 0, + offset_color = 0, + offset_skin = 0, + offset_morphTarget = 0, + offset_custom = 0, + offset_customSrc = 0, + + value, + + vertexArray = geometryGroup.__vertexArray, + uvArray = geometryGroup.__uvArray, + uv2Array = geometryGroup.__uv2Array, + normalArray = geometryGroup.__normalArray, + tangentArray = geometryGroup.__tangentArray, + colorArray = geometryGroup.__colorArray, + + skinIndexArray = geometryGroup.__skinIndexArray, + skinWeightArray = geometryGroup.__skinWeightArray, + + morphTargetsArrays = geometryGroup.__morphTargetsArrays, + morphNormalsArrays = geometryGroup.__morphNormalsArrays, + + customAttributes = geometryGroup.__webglCustomAttributesList, + customAttribute, + + faceArray = geometryGroup.__faceArray, + lineArray = geometryGroup.__lineArray, + + geometry = object.geometry, // this is shared for all chunks + + dirtyVertices = geometry.verticesNeedUpdate, + dirtyElements = geometry.elementsNeedUpdate, + dirtyUvs = geometry.uvsNeedUpdate, + dirtyNormals = geometry.normalsNeedUpdate, + dirtyTangents = geometry.tangentsNeedUpdate, + dirtyColors = geometry.colorsNeedUpdate, + dirtyMorphTargets = geometry.morphTargetsNeedUpdate, + + vertices = geometry.vertices, + chunk_faces3 = geometryGroup.faces3, + obj_faces = geometry.faces, + + obj_uvs = geometry.faceVertexUvs[ 0 ], + obj_uvs2 = geometry.faceVertexUvs[ 1 ], + + obj_colors = geometry.colors, + + obj_skinIndices = geometry.skinIndices, + obj_skinWeights = geometry.skinWeights, + + morphTargets = geometry.morphTargets, + morphNormals = geometry.morphNormals; + + if ( dirtyVertices ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + v1 = vertices[ face.a ]; + v2 = vertices[ face.b ]; + v3 = vertices[ face.c ]; + + vertexArray[ offset ] = v1.x; + vertexArray[ offset + 1 ] = v1.y; + vertexArray[ offset + 2 ] = v1.z; + + vertexArray[ offset + 3 ] = v2.x; + vertexArray[ offset + 4 ] = v2.y; + vertexArray[ offset + 5 ] = v2.z; + + vertexArray[ offset + 6 ] = v3.x; + vertexArray[ offset + 7 ] = v3.y; + vertexArray[ offset + 8 ] = v3.z; + + offset += 9; + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); + + } + + if ( dirtyMorphTargets ) { + + for ( vk = 0, vkl = morphTargets.length; vk < vkl; vk ++ ) { + + offset_morphTarget = 0; + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + chf = chunk_faces3[ f ]; + face = obj_faces[ chf ]; + + // morph positions + + v1 = morphTargets[ vk ].vertices[ face.a ]; + v2 = morphTargets[ vk ].vertices[ face.b ]; + v3 = morphTargets[ vk ].vertices[ face.c ]; + + vka = morphTargetsArrays[ vk ]; + + vka[ offset_morphTarget ] = v1.x; + vka[ offset_morphTarget + 1 ] = v1.y; + vka[ offset_morphTarget + 2 ] = v1.z; + + vka[ offset_morphTarget + 3 ] = v2.x; + vka[ offset_morphTarget + 4 ] = v2.y; + vka[ offset_morphTarget + 5 ] = v2.z; + + vka[ offset_morphTarget + 6 ] = v3.x; + vka[ offset_morphTarget + 7 ] = v3.y; + vka[ offset_morphTarget + 8 ] = v3.z; + + // morph normals + + if ( material.morphNormals ) { + + if ( needsSmoothNormals ) { + + faceVertexNormals = morphNormals[ vk ].vertexNormals[ chf ]; + + n1 = faceVertexNormals.a; + n2 = faceVertexNormals.b; + n3 = faceVertexNormals.c; + + } else { + + n1 = morphNormals[ vk ].faceNormals[ chf ]; + n2 = n1; + n3 = n1; + + } + + nka = morphNormalsArrays[ vk ]; + + nka[ offset_morphTarget ] = n1.x; + nka[ offset_morphTarget + 1 ] = n1.y; + nka[ offset_morphTarget + 2 ] = n1.z; + + nka[ offset_morphTarget + 3 ] = n2.x; + nka[ offset_morphTarget + 4 ] = n2.y; + nka[ offset_morphTarget + 5 ] = n2.z; + + nka[ offset_morphTarget + 6 ] = n3.x; + nka[ offset_morphTarget + 7 ] = n3.y; + nka[ offset_morphTarget + 8 ] = n3.z; + + } + + // + + offset_morphTarget += 9; + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ vk ] ); + _gl.bufferData( _gl.ARRAY_BUFFER, morphTargetsArrays[ vk ], hint ); + + if ( material.morphNormals ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ vk ] ); + _gl.bufferData( _gl.ARRAY_BUFFER, morphNormalsArrays[ vk ], hint ); + + } + + } + + } + + if ( obj_skinWeights.length ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + // weights + + sw1 = obj_skinWeights[ face.a ]; + sw2 = obj_skinWeights[ face.b ]; + sw3 = obj_skinWeights[ face.c ]; + + skinWeightArray[ offset_skin ] = sw1.x; + skinWeightArray[ offset_skin + 1 ] = sw1.y; + skinWeightArray[ offset_skin + 2 ] = sw1.z; + skinWeightArray[ offset_skin + 3 ] = sw1.w; + + skinWeightArray[ offset_skin + 4 ] = sw2.x; + skinWeightArray[ offset_skin + 5 ] = sw2.y; + skinWeightArray[ offset_skin + 6 ] = sw2.z; + skinWeightArray[ offset_skin + 7 ] = sw2.w; + + skinWeightArray[ offset_skin + 8 ] = sw3.x; + skinWeightArray[ offset_skin + 9 ] = sw3.y; + skinWeightArray[ offset_skin + 10 ] = sw3.z; + skinWeightArray[ offset_skin + 11 ] = sw3.w; + + // indices + + si1 = obj_skinIndices[ face.a ]; + si2 = obj_skinIndices[ face.b ]; + si3 = obj_skinIndices[ face.c ]; + + skinIndexArray[ offset_skin ] = si1.x; + skinIndexArray[ offset_skin + 1 ] = si1.y; + skinIndexArray[ offset_skin + 2 ] = si1.z; + skinIndexArray[ offset_skin + 3 ] = si1.w; + + skinIndexArray[ offset_skin + 4 ] = si2.x; + skinIndexArray[ offset_skin + 5 ] = si2.y; + skinIndexArray[ offset_skin + 6 ] = si2.z; + skinIndexArray[ offset_skin + 7 ] = si2.w; + + skinIndexArray[ offset_skin + 8 ] = si3.x; + skinIndexArray[ offset_skin + 9 ] = si3.y; + skinIndexArray[ offset_skin + 10 ] = si3.z; + skinIndexArray[ offset_skin + 11 ] = si3.w; + + offset_skin += 12; + + } + + if ( offset_skin > 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, skinIndexArray, hint ); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, skinWeightArray, hint ); + + } + + } + + if ( dirtyColors && vertexColorType ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + vertexColors = face.vertexColors; + faceColor = face.color; + + if ( vertexColors.length === 3 && vertexColorType === THREE.VertexColors ) { + + c1 = vertexColors[ 0 ]; + c2 = vertexColors[ 1 ]; + c3 = vertexColors[ 2 ]; + + } else { + + c1 = faceColor; + c2 = faceColor; + c3 = faceColor; + + } + + colorArray[ offset_color ] = c1.r; + colorArray[ offset_color + 1 ] = c1.g; + colorArray[ offset_color + 2 ] = c1.b; + + colorArray[ offset_color + 3 ] = c2.r; + colorArray[ offset_color + 4 ] = c2.g; + colorArray[ offset_color + 5 ] = c2.b; + + colorArray[ offset_color + 6 ] = c3.r; + colorArray[ offset_color + 7 ] = c3.g; + colorArray[ offset_color + 8 ] = c3.b; + + offset_color += 9; + + } + + if ( offset_color > 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); + + } + + } + + if ( dirtyTangents && geometry.hasTangents ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + vertexTangents = face.vertexTangents; + + t1 = vertexTangents[ 0 ]; + t2 = vertexTangents[ 1 ]; + t3 = vertexTangents[ 2 ]; + + tangentArray[ offset_tangent ] = t1.x; + tangentArray[ offset_tangent + 1 ] = t1.y; + tangentArray[ offset_tangent + 2 ] = t1.z; + tangentArray[ offset_tangent + 3 ] = t1.w; + + tangentArray[ offset_tangent + 4 ] = t2.x; + tangentArray[ offset_tangent + 5 ] = t2.y; + tangentArray[ offset_tangent + 6 ] = t2.z; + tangentArray[ offset_tangent + 7 ] = t2.w; + + tangentArray[ offset_tangent + 8 ] = t3.x; + tangentArray[ offset_tangent + 9 ] = t3.y; + tangentArray[ offset_tangent + 10 ] = t3.z; + tangentArray[ offset_tangent + 11 ] = t3.w; + + offset_tangent += 12; + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, tangentArray, hint ); + + } + + if ( dirtyNormals && normalType ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + vertexNormals = face.vertexNormals; + faceNormal = face.normal; + + if ( vertexNormals.length === 3 && needsSmoothNormals ) { + + for ( i = 0; i < 3; i ++ ) { + + vn = vertexNormals[ i ]; + + normalArray[ offset_normal ] = vn.x; + normalArray[ offset_normal + 1 ] = vn.y; + normalArray[ offset_normal + 2 ] = vn.z; + + offset_normal += 3; + + } + + } else { + + for ( i = 0; i < 3; i ++ ) { + + normalArray[ offset_normal ] = faceNormal.x; + normalArray[ offset_normal + 1 ] = faceNormal.y; + normalArray[ offset_normal + 2 ] = faceNormal.z; + + offset_normal += 3; + + } + + } + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, normalArray, hint ); + + } + + if ( dirtyUvs && obj_uvs && uvType ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + fi = chunk_faces3[ f ]; + + uv = obj_uvs[ fi ]; + + if ( uv === undefined ) continue; + + for ( i = 0; i < 3; i ++ ) { + + uvi = uv[ i ]; + + uvArray[ offset_uv ] = uvi.x; + uvArray[ offset_uv + 1 ] = uvi.y; + + offset_uv += 2; + + } + + } + + if ( offset_uv > 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, uvArray, hint ); + + } + + } + + if ( dirtyUvs && obj_uvs2 && uvType ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + fi = chunk_faces3[ f ]; + + uv2 = obj_uvs2[ fi ]; + + if ( uv2 === undefined ) continue; + + for ( i = 0; i < 3; i ++ ) { + + uv2i = uv2[ i ]; + + uv2Array[ offset_uv2 ] = uv2i.x; + uv2Array[ offset_uv2 + 1 ] = uv2i.y; + + offset_uv2 += 2; + + } + + } + + if ( offset_uv2 > 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, uv2Array, hint ); + + } + + } + + if ( dirtyElements ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + faceArray[ offset_face ] = vertexIndex; + faceArray[ offset_face + 1 ] = vertexIndex + 1; + faceArray[ offset_face + 2 ] = vertexIndex + 2; + + offset_face += 3; + + lineArray[ offset_line ] = vertexIndex; + lineArray[ offset_line + 1 ] = vertexIndex + 1; + + lineArray[ offset_line + 2 ] = vertexIndex; + lineArray[ offset_line + 3 ] = vertexIndex + 2; + + lineArray[ offset_line + 4 ] = vertexIndex + 1; + lineArray[ offset_line + 5 ] = vertexIndex + 2; + + offset_line += 6; + + vertexIndex += 3; + + } + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer ); + _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, faceArray, hint ); + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer ); + _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, lineArray, hint ); + + } + + if ( customAttributes ) { + + for ( i = 0, il = customAttributes.length; i < il; i ++ ) { + + customAttribute = customAttributes[ i ]; + + if ( ! customAttribute.__original.needsUpdate ) continue; + + offset_custom = 0; + offset_customSrc = 0; + + if ( customAttribute.size === 1 ) { + + if ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + customAttribute.array[ offset_custom ] = customAttribute.value[ face.a ]; + customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ face.b ]; + customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ face.c ]; + + offset_custom += 3; + + } + + } else if ( customAttribute.boundTo === 'faces' ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces3[ f ] ]; + + customAttribute.array[ offset_custom ] = value; + customAttribute.array[ offset_custom + 1 ] = value; + customAttribute.array[ offset_custom + 2 ] = value; + + offset_custom += 3; + + } + + } + + } else if ( customAttribute.size === 2 ) { + + if ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + v1 = customAttribute.value[ face.a ]; + v2 = customAttribute.value[ face.b ]; + v3 = customAttribute.value[ face.c ]; + + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + + customAttribute.array[ offset_custom + 2 ] = v2.x; + customAttribute.array[ offset_custom + 3 ] = v2.y; + + customAttribute.array[ offset_custom + 4 ] = v3.x; + customAttribute.array[ offset_custom + 5 ] = v3.y; + + offset_custom += 6; + + } + + } else if ( customAttribute.boundTo === 'faces' ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces3[ f ] ]; + + v1 = value; + v2 = value; + v3 = value; + + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + + customAttribute.array[ offset_custom + 2 ] = v2.x; + customAttribute.array[ offset_custom + 3 ] = v2.y; + + customAttribute.array[ offset_custom + 4 ] = v3.x; + customAttribute.array[ offset_custom + 5 ] = v3.y; + + offset_custom += 6; + + } + + } + + } else if ( customAttribute.size === 3 ) { + + var pp; + + if ( customAttribute.type === 'c' ) { + + pp = [ 'r', 'g', 'b' ]; + + } else { + + pp = [ 'x', 'y', 'z' ]; + + } + + if ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + v1 = customAttribute.value[ face.a ]; + v2 = customAttribute.value[ face.b ]; + v3 = customAttribute.value[ face.c ]; + + customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; + + offset_custom += 9; + + } + + } else if ( customAttribute.boundTo === 'faces' ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces3[ f ] ]; + + v1 = value; + v2 = value; + v3 = value; + + customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; + + offset_custom += 9; + + } + + } else if ( customAttribute.boundTo === 'faceVertices' ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces3[ f ] ]; + + v1 = value[ 0 ]; + v2 = value[ 1 ]; + v3 = value[ 2 ]; + + customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; + + offset_custom += 9; + + } + + } + + } else if ( customAttribute.size === 4 ) { + + if ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + v1 = customAttribute.value[ face.a ]; + v2 = customAttribute.value[ face.b ]; + v3 = customAttribute.value[ face.c ]; + + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + customAttribute.array[ offset_custom + 2 ] = v1.z; + customAttribute.array[ offset_custom + 3 ] = v1.w; + + customAttribute.array[ offset_custom + 4 ] = v2.x; + customAttribute.array[ offset_custom + 5 ] = v2.y; + customAttribute.array[ offset_custom + 6 ] = v2.z; + customAttribute.array[ offset_custom + 7 ] = v2.w; + + customAttribute.array[ offset_custom + 8 ] = v3.x; + customAttribute.array[ offset_custom + 9 ] = v3.y; + customAttribute.array[ offset_custom + 10 ] = v3.z; + customAttribute.array[ offset_custom + 11 ] = v3.w; + + offset_custom += 12; + + } + + } else if ( customAttribute.boundTo === 'faces' ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces3[ f ] ]; + + v1 = value; + v2 = value; + v3 = value; + + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + customAttribute.array[ offset_custom + 2 ] = v1.z; + customAttribute.array[ offset_custom + 3 ] = v1.w; + + customAttribute.array[ offset_custom + 4 ] = v2.x; + customAttribute.array[ offset_custom + 5 ] = v2.y; + customAttribute.array[ offset_custom + 6 ] = v2.z; + customAttribute.array[ offset_custom + 7 ] = v2.w; + + customAttribute.array[ offset_custom + 8 ] = v3.x; + customAttribute.array[ offset_custom + 9 ] = v3.y; + customAttribute.array[ offset_custom + 10 ] = v3.z; + customAttribute.array[ offset_custom + 11 ] = v3.w; + + offset_custom += 12; + + } + + } else if ( customAttribute.boundTo === 'faceVertices' ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces3[ f ] ]; + + v1 = value[ 0 ]; + v2 = value[ 1 ]; + v3 = value[ 2 ]; + + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + customAttribute.array[ offset_custom + 2 ] = v1.z; + customAttribute.array[ offset_custom + 3 ] = v1.w; + + customAttribute.array[ offset_custom + 4 ] = v2.x; + customAttribute.array[ offset_custom + 5 ] = v2.y; + customAttribute.array[ offset_custom + 6 ] = v2.z; + customAttribute.array[ offset_custom + 7 ] = v2.w; + + customAttribute.array[ offset_custom + 8 ] = v3.x; + customAttribute.array[ offset_custom + 9 ] = v3.y; + customAttribute.array[ offset_custom + 10 ] = v3.z; + customAttribute.array[ offset_custom + 11 ] = v3.w; + + offset_custom += 12; + + } + + } + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); + + } + + } + + if ( dispose ) { + + delete geometryGroup.__inittedArrays; + delete geometryGroup.__colorArray; + delete geometryGroup.__normalArray; + delete geometryGroup.__tangentArray; + delete geometryGroup.__uvArray; + delete geometryGroup.__uv2Array; + delete geometryGroup.__faceArray; + delete geometryGroup.__vertexArray; + delete geometryGroup.__lineArray; + delete geometryGroup.__skinIndexArray; + delete geometryGroup.__skinWeightArray; + + } + + }; + + function setDirectBuffers( geometry, hint ) { + + var attributes = geometry.attributes; + + var attributeName, attributeItem; + + for ( attributeName in attributes ) { + + attributeItem = attributes[ attributeName ]; + + if ( attributeItem.needsUpdate ) { + + if ( attributeName === 'index' ) { + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, attributeItem.buffer ); + _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, attributeItem.array, hint ); + + } else { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, attributeItem.array, hint ); + + } + + attributeItem.needsUpdate = false; + + } + + } + + } + + // Buffer rendering + + this.renderBufferImmediate = function ( object, program, material ) { + + initAttributes(); + + if ( object.hasPositions && ! object.__webglVertexBuffer ) object.__webglVertexBuffer = _gl.createBuffer(); + if ( object.hasNormals && ! object.__webglNormalBuffer ) object.__webglNormalBuffer = _gl.createBuffer(); + if ( object.hasUvs && ! object.__webglUvBuffer ) object.__webglUvBuffer = _gl.createBuffer(); + if ( object.hasColors && ! object.__webglColorBuffer ) object.__webglColorBuffer = _gl.createBuffer(); + + if ( object.hasPositions ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglVertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW ); + enableAttribute( program.attributes.position ); + _gl.vertexAttribPointer( program.attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + + } + + if ( object.hasNormals ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglNormalBuffer ); + + if ( material.shading === THREE.FlatShading ) { + + var nx, ny, nz, + nax, nbx, ncx, nay, nby, ncy, naz, nbz, ncz, + normalArray, + i, il = object.count * 3; + + for ( i = 0; i < il; i += 9 ) { + + normalArray = object.normalArray; + + nax = normalArray[ i ]; + nay = normalArray[ i + 1 ]; + naz = normalArray[ i + 2 ]; + + nbx = normalArray[ i + 3 ]; + nby = normalArray[ i + 4 ]; + nbz = normalArray[ i + 5 ]; + + ncx = normalArray[ i + 6 ]; + ncy = normalArray[ i + 7 ]; + ncz = normalArray[ i + 8 ]; + + nx = ( nax + nbx + ncx ) / 3; + ny = ( nay + nby + ncy ) / 3; + nz = ( naz + nbz + ncz ) / 3; + + normalArray[ i ] = nx; + normalArray[ i + 1 ] = ny; + normalArray[ i + 2 ] = nz; + + normalArray[ i + 3 ] = nx; + normalArray[ i + 4 ] = ny; + normalArray[ i + 5 ] = nz; + + normalArray[ i + 6 ] = nx; + normalArray[ i + 7 ] = ny; + normalArray[ i + 8 ] = nz; + + } + + } + + _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW ); + enableAttribute( program.attributes.normal ); + _gl.vertexAttribPointer( program.attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); + + } + + if ( object.hasUvs && material.map ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglUvBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW ); + enableAttribute( program.attributes.uv ); + _gl.vertexAttribPointer( program.attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); + + } + + if ( object.hasColors && material.vertexColors !== THREE.NoColors ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglColorBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW ); + enableAttribute( program.attributes.color ); + _gl.vertexAttribPointer( program.attributes.color, 3, _gl.FLOAT, false, 0, 0 ); + + } + + disableUnusedAttributes(); + + _gl.drawArrays( _gl.TRIANGLES, 0, object.count ); + + object.count = 0; + + }; + + function setupVertexAttributes( material, programAttributes, geometryAttributes, startIndex ) { + + for ( var attributeName in programAttributes ) { + + var attributePointer = programAttributes[ attributeName ]; + var attributeItem = geometryAttributes[ attributeName ]; + + if ( attributePointer >= 0 ) { + + if ( attributeItem ) { + + var attributeSize = attributeItem.itemSize; + + _gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer ); + enableAttribute( attributePointer ); + _gl.vertexAttribPointer( attributePointer, attributeSize, _gl.FLOAT, false, 0, startIndex * attributeSize * 4 ); // 4 bytes per Float32 + + } else if ( material.defaultAttributeValues ) { + + if ( material.defaultAttributeValues[ attributeName ].length === 2 ) { + + _gl.vertexAttrib2fv( attributePointer, material.defaultAttributeValues[ attributeName ] ); + + } else if ( material.defaultAttributeValues[ attributeName ].length === 3 ) { + + _gl.vertexAttrib3fv( attributePointer, material.defaultAttributeValues[ attributeName ] ); + + } + + } + + } + + } + + disableUnusedAttributes(); + + } + + this.renderBufferDirect = function ( camera, lights, fog, material, geometry, object ) { + + if ( material.visible === false ) return; + + var linewidth, a, attribute; + var attributeItem, attributeName, attributePointer, attributeSize; + + var program = setProgram( camera, lights, fog, material, object ); + + var programAttributes = program.attributes; + var geometryAttributes = geometry.attributes; + + var updateBuffers = false, + wireframeBit = material.wireframe ? 1 : 0, + geometryHash = ( geometry.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit; + + if ( geometryHash !== _currentGeometryGroupHash ) { + + _currentGeometryGroupHash = geometryHash; + updateBuffers = true; + + } + + if ( updateBuffers ) { + + initAttributes(); + + } + + // render mesh + + if ( object instanceof THREE.Mesh ) { + + var index = geometryAttributes[ 'index' ]; + + if ( index ) { + + // indexed triangles + + var type, size; + + if ( index.array instanceof Uint32Array ) { + + type = _gl.UNSIGNED_INT; + size = 4; + + } else { + + type = _gl.UNSIGNED_SHORT; + size = 2; + + } + + var offsets = geometry.offsets; + + if ( offsets.length === 0 ) { + + if ( updateBuffers ) { + + setupVertexAttributes( material, programAttributes, geometryAttributes, 0 ); + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); + + } + + _gl.drawElements( _gl.TRIANGLES, index.array.length, type, 0 ); + + _this.info.render.calls ++; + _this.info.render.vertices += index.array.length; // not really true, here vertices can be shared + _this.info.render.faces += index.array.length / 3; + + } else { + + // if there is more than 1 chunk + // must set attribute pointers to use new offsets for each chunk + // even if geometry and materials didn't change + + updateBuffers = true; + + for ( var i = 0, il = offsets.length; i < il; i ++ ) { + + var startIndex = offsets[ i ].index; + + if ( updateBuffers ) { + + setupVertexAttributes( material, programAttributes, geometryAttributes, startIndex ); + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); + + } + + // render indexed triangles + + _gl.drawElements( _gl.TRIANGLES, offsets[ i ].count, type, offsets[ i ].start * size ); + + _this.info.render.calls ++; + _this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared + _this.info.render.faces += offsets[ i ].count / 3; + + } + + } + + } else { + + // non-indexed triangles + + if ( updateBuffers ) { + + setupVertexAttributes( material, programAttributes, geometryAttributes, 0 ); + + } + + var position = geometry.attributes[ 'position' ]; + + // render non-indexed triangles + + _gl.drawArrays( _gl.TRIANGLES, 0, position.array.length / 3 ); + + _this.info.render.calls ++; + _this.info.render.vertices += position.array.length / 3; + _this.info.render.faces += position.array.length / 9; + + } + + } else if ( object instanceof THREE.PointCloud ) { + + // render particles + + if ( updateBuffers ) { + + setupVertexAttributes( material, programAttributes, geometryAttributes, 0 ); + + } + + var position = geometryAttributes[ 'position' ]; + + // render particles + + _gl.drawArrays( _gl.POINTS, 0, position.array.length / 3 ); + + _this.info.render.calls ++; + _this.info.render.points += position.array.length / 3; + + } else if ( object instanceof THREE.Line ) { + + var mode = ( object.type === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES; + + setLineWidth( material.linewidth ); + + var index = geometryAttributes[ 'index' ]; + + if ( index ) { + + // indexed lines + + var type, size; + + if ( index.array instanceof Uint32Array ) { + + type = _gl.UNSIGNED_INT; + size = 4; + + } else { + + type = _gl.UNSIGNED_SHORT; + size = 2; + + } + + var offsets = geometry.offsets; + + if ( offsets.length === 0 ) { + + if ( updateBuffers ) { + + setupVertexAttributes( material, programAttributes, geometryAttributes, 0 ); + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); + + } + + _gl.drawElements( mode, index.array.length, type, 0 ); // 2 bytes per Uint16Array + + _this.info.render.calls ++; + _this.info.render.vertices += index.array.length; // not really true, here vertices can be shared + + } else { + + // if there is more than 1 chunk + // must set attribute pointers to use new offsets for each chunk + // even if geometry and materials didn't change + + if ( offsets.length > 1 ) updateBuffers = true; + + for ( var i = 0, il = offsets.length; i < il; i ++ ) { + + var startIndex = offsets[ i ].index; + + if ( updateBuffers ) { + + setupVertexAttributes( material, programAttributes, geometryAttributes, startIndex ); + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); + + } + + // render indexed lines + + _gl.drawElements( mode, offsets[ i ].count, type, offsets[ i ].start * size ); // 2 bytes per Uint16Array + + _this.info.render.calls ++; + _this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared + + } + + } + + } else { + + // non-indexed lines + + if ( updateBuffers ) { + + setupVertexAttributes( material, programAttributes, geometryAttributes, 0 ); + + } + + var position = geometryAttributes[ 'position' ]; + + _gl.drawArrays( mode, 0, position.array.length / 3 ); + + _this.info.render.calls ++; + _this.info.render.points += position.array.length / 3; + + } + + } + + }; + + this.renderBuffer = function ( camera, lights, fog, material, geometryGroup, object ) { + + if ( material.visible === false ) return; + + var linewidth, a, attribute, i, il; + + var program = setProgram( camera, lights, fog, material, object ); + + var attributes = program.attributes; + + var updateBuffers = false, + wireframeBit = material.wireframe ? 1 : 0, + geometryGroupHash = ( geometryGroup.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit; + + if ( geometryGroupHash !== _currentGeometryGroupHash ) { + + _currentGeometryGroupHash = geometryGroupHash; + updateBuffers = true; + + } + + if ( updateBuffers ) { + + initAttributes(); + + } + + // vertices + + if ( ! material.morphTargets && attributes.position >= 0 ) { + + if ( updateBuffers ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); + enableAttribute( attributes.position ); + _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + + } + + } else { + + if ( object.morphTargetBase ) { + + setupMorphTargets( material, geometryGroup, object ); + + } + + } + + + if ( updateBuffers ) { + + // custom attributes + + // Use the per-geometryGroup custom attribute arrays which are setup in initMeshBuffers + + if ( geometryGroup.__webglCustomAttributesList ) { + + for ( i = 0, il = geometryGroup.__webglCustomAttributesList.length; i < il; i ++ ) { + + attribute = geometryGroup.__webglCustomAttributesList[ i ]; + + if ( attributes[ attribute.buffer.belongsToAttribute ] >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, attribute.buffer ); + enableAttribute( attributes[ attribute.buffer.belongsToAttribute ] ); + _gl.vertexAttribPointer( attributes[ attribute.buffer.belongsToAttribute ], attribute.size, _gl.FLOAT, false, 0, 0 ); + + } + + } + + } + + + // colors + + if ( attributes.color >= 0 ) { + + if ( object.geometry.colors.length > 0 || object.geometry.faces.length > 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer ); + enableAttribute( attributes.color ); + _gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 ); + + } else if ( material.defaultAttributeValues ) { + + + _gl.vertexAttrib3fv( attributes.color, material.defaultAttributeValues.color ); + + } + + } + + // normals + + if ( attributes.normal >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer ); + enableAttribute( attributes.normal ); + _gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); + + } + + // tangents + + if ( attributes.tangent >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer ); + enableAttribute( attributes.tangent ); + _gl.vertexAttribPointer( attributes.tangent, 4, _gl.FLOAT, false, 0, 0 ); + + } + + // uvs + + if ( attributes.uv >= 0 ) { + + if ( object.geometry.faceVertexUvs[ 0 ] ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer ); + enableAttribute( attributes.uv ); + _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); + + } else if ( material.defaultAttributeValues ) { + + + _gl.vertexAttrib2fv( attributes.uv, material.defaultAttributeValues.uv ); + + } + + } + + if ( attributes.uv2 >= 0 ) { + + if ( object.geometry.faceVertexUvs[ 1 ] ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer ); + enableAttribute( attributes.uv2 ); + _gl.vertexAttribPointer( attributes.uv2, 2, _gl.FLOAT, false, 0, 0 ); + + } else if ( material.defaultAttributeValues ) { + + + _gl.vertexAttrib2fv( attributes.uv2, material.defaultAttributeValues.uv2 ); + + } + + } + + if ( material.skinning && + attributes.skinIndex >= 0 && attributes.skinWeight >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer ); + enableAttribute( attributes.skinIndex ); + _gl.vertexAttribPointer( attributes.skinIndex, 4, _gl.FLOAT, false, 0, 0 ); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer ); + enableAttribute( attributes.skinWeight ); + _gl.vertexAttribPointer( attributes.skinWeight, 4, _gl.FLOAT, false, 0, 0 ); + + } + + // line distances + + if ( attributes.lineDistance >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglLineDistanceBuffer ); + enableAttribute( attributes.lineDistance ); + _gl.vertexAttribPointer( attributes.lineDistance, 1, _gl.FLOAT, false, 0, 0 ); + + } + + } + + disableUnusedAttributes(); + + // render mesh + + if ( object instanceof THREE.Mesh ) { + + var type = geometryGroup.__typeArray === Uint32Array ? _gl.UNSIGNED_INT : _gl.UNSIGNED_SHORT; + + // wireframe + + if ( material.wireframe ) { + + setLineWidth( material.wireframeLinewidth ); + if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer ); + _gl.drawElements( _gl.LINES, geometryGroup.__webglLineCount, type, 0 ); + + // triangles + + } else { + + if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer ); + _gl.drawElements( _gl.TRIANGLES, geometryGroup.__webglFaceCount, type, 0 ); + + } + + _this.info.render.calls ++; + _this.info.render.vertices += geometryGroup.__webglFaceCount; + _this.info.render.faces += geometryGroup.__webglFaceCount / 3; + + // render lines + + } else if ( object instanceof THREE.Line ) { + + var mode = ( object.type === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES; + + setLineWidth( material.linewidth ); + + _gl.drawArrays( mode, 0, geometryGroup.__webglLineCount ); + + _this.info.render.calls ++; + + // render particles + + } else if ( object instanceof THREE.PointCloud ) { + + _gl.drawArrays( _gl.POINTS, 0, geometryGroup.__webglParticleCount ); + + _this.info.render.calls ++; + _this.info.render.points += geometryGroup.__webglParticleCount; + + } + + }; + + function initAttributes() { + + for ( var i = 0, l = _newAttributes.length; i < l; i ++ ) { + + _newAttributes[ i ] = 0; + + } + + } + + function enableAttribute( attribute ) { + + _newAttributes[ attribute ] = 1; + + if ( _enabledAttributes[ attribute ] === 0 ) { + + _gl.enableVertexAttribArray( attribute ); + _enabledAttributes[ attribute ] = 1; + + } + + } + + function disableUnusedAttributes() { + + for ( var i = 0, l = _enabledAttributes.length; i < l; i ++ ) { + + if ( _enabledAttributes[ i ] !== _newAttributes[ i ] ) { + + _gl.disableVertexAttribArray( i ); + _enabledAttributes[ i ] = 0; + + } + + } + + } + + function setupMorphTargets ( material, geometryGroup, object ) { + + // set base + + var attributes = material.program.attributes; + + if ( object.morphTargetBase !== - 1 && attributes.position >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ object.morphTargetBase ] ); + enableAttribute( attributes.position ); + _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + + } else if ( attributes.position >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); + enableAttribute( attributes.position ); + _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + + } + + if ( object.morphTargetForcedOrder.length ) { + + // set forced order + + var m = 0; + var order = object.morphTargetForcedOrder; + var influences = object.morphTargetInfluences; + + while ( m < material.numSupportedMorphTargets && m < order.length ) { + + if ( attributes[ 'morphTarget' + m ] >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ order[ m ] ] ); + enableAttribute( attributes[ 'morphTarget' + m ] ); + _gl.vertexAttribPointer( attributes[ 'morphTarget' + m ], 3, _gl.FLOAT, false, 0, 0 ); + + } + + if ( attributes[ 'morphNormal' + m ] >= 0 && material.morphNormals ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ order[ m ] ] ); + enableAttribute( attributes[ 'morphNormal' + m ] ); + _gl.vertexAttribPointer( attributes[ 'morphNormal' + m ], 3, _gl.FLOAT, false, 0, 0 ); + + } + + object.__webglMorphTargetInfluences[ m ] = influences[ order[ m ] ]; + + m ++; + } + + } else { + + // find the most influencing + + var influence, activeInfluenceIndices = []; + var influences = object.morphTargetInfluences; + var i, il = influences.length; + + for ( i = 0; i < il; i ++ ) { + + influence = influences[ i ]; + + if ( influence > 0 ) { + + activeInfluenceIndices.push( [ influence, i ] ); + + } + + } + + if ( activeInfluenceIndices.length > material.numSupportedMorphTargets ) { + + activeInfluenceIndices.sort( numericalSort ); + activeInfluenceIndices.length = material.numSupportedMorphTargets; + + } else if ( activeInfluenceIndices.length > material.numSupportedMorphNormals ) { + + activeInfluenceIndices.sort( numericalSort ); + + } else if ( activeInfluenceIndices.length === 0 ) { + + activeInfluenceIndices.push( [ 0, 0 ] ); + + }; + + var influenceIndex, m = 0; + + while ( m < material.numSupportedMorphTargets ) { + + if ( activeInfluenceIndices[ m ] ) { + + influenceIndex = activeInfluenceIndices[ m ][ 1 ]; + + if ( attributes[ 'morphTarget' + m ] >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ influenceIndex ] ); + enableAttribute( attributes[ 'morphTarget' + m ] ); + _gl.vertexAttribPointer( attributes[ 'morphTarget' + m ], 3, _gl.FLOAT, false, 0, 0 ); + + } + + if ( attributes[ 'morphNormal' + m ] >= 0 && material.morphNormals ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ influenceIndex ] ); + enableAttribute( attributes[ 'morphNormal' + m ] ); + _gl.vertexAttribPointer( attributes[ 'morphNormal' + m ], 3, _gl.FLOAT, false, 0, 0 ); + + + } + + object.__webglMorphTargetInfluences[ m ] = influences[ influenceIndex ]; + + } else { + + /* + _gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 ); + + if ( material.morphNormals ) { + + _gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 ); + + } + */ + + object.__webglMorphTargetInfluences[ m ] = 0; + + } + + m ++; + + } + + } + + // load updated influences uniform + + if ( material.program.uniforms.morphTargetInfluences !== null ) { + + _gl.uniform1fv( material.program.uniforms.morphTargetInfluences, object.__webglMorphTargetInfluences ); + + } + + }; + + // Sorting + + function painterSortStable ( a, b ) { + + if ( a.z !== b.z ) { + + return b.z - a.z; + + } else { + + return a.id - b.id; + + } + + }; + + function reversePainterSortStable ( a, b ) { + + if ( a.z !== b.z ) { + + return a.z - b.z; + + } else { + + return a.id - b.id; + + } + + }; + + function numericalSort ( a, b ) { + + return b[ 0 ] - a[ 0 ]; + + }; + + + // Rendering + + this.render = function ( scene, camera, renderTarget, forceClear ) { + + if ( camera instanceof THREE.Camera === false ) { + + console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); + return; + + } + + var i, il, + + webglObject, object, + renderList, + + lights = scene.__lights, + fog = scene.fog; + + // reset caching for this frame + + _currentMaterialId = - 1; + _currentCamera = null; + _lightsNeedUpdate = true; + + // update scene graph + + if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + + // update camera matrices and frustum + + if ( camera.parent === undefined ) camera.updateMatrixWorld(); + + // update Skeleton objects + function updateSkeletons( object ) { + + if ( object instanceof THREE.SkinnedMesh ) { + + object.skeleton.update(); + + } + + for ( var i = 0, l = object.children.length; i < l; i ++ ) { + + updateSkeletons( object.children[ i ] ); + + } + + } + + updateSkeletons( scene ); + + camera.matrixWorldInverse.getInverse( camera.matrixWorld ); + + _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); + _frustum.setFromMatrix( _projScreenMatrix ); + + initObjects( scene ); + + opaqueObjects.length = 0; + transparentObjects.length = 0; + + projectObject( scene, scene, camera ); + + if ( _this.sortObjects === true ) { + + opaqueObjects.sort( painterSortStable ); + transparentObjects.sort( reversePainterSortStable ); + + } + + // custom render plugins (pre pass) + + renderPlugins( this.renderPluginsPre, scene, camera ); + + // + + _this.info.render.calls = 0; + _this.info.render.vertices = 0; + _this.info.render.faces = 0; + _this.info.render.points = 0; + + this.setRenderTarget( renderTarget ); + + if ( this.autoClear || forceClear ) { + + this.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil ); + + } + + // set matrices for regular objects (frustum culled) + + + + + // set matrices for immediate objects + + renderList = scene.__webglObjectsImmediate; + + for ( i = 0, il = renderList.length; i < il; i ++ ) { + + webglObject = renderList[ i ]; + object = webglObject.object; + + if ( object.visible ) { + + setupMatrices( object, camera ); + + unrollImmediateBufferMaterial( webglObject ); + + } + + } + + if ( scene.overrideMaterial ) { + + var material = scene.overrideMaterial; + + this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); + this.setDepthTest( material.depthTest ); + this.setDepthWrite( material.depthWrite ); + setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); + + renderObjects( opaqueObjects, camera, lights, fog, true, material ); + renderObjects( transparentObjects, camera, lights, fog, true, material ); + renderObjectsImmediate( scene.__webglObjectsImmediate, '', camera, lights, fog, false, material ); + + } else { + + var material = null; + + // opaque pass (front-to-back order) + + this.setBlending( THREE.NoBlending ); + + renderObjects( opaqueObjects, camera, lights, fog, false, material ); + renderObjectsImmediate( scene.__webglObjectsImmediate, 'opaque', camera, lights, fog, false, material ); + + // transparent pass (back-to-front order) + + renderObjects( transparentObjects, camera, lights, fog, true, material ); + renderObjectsImmediate( scene.__webglObjectsImmediate, 'transparent', camera, lights, fog, true, material ); + + } + + // custom render plugins (post pass) + + renderPlugins( this.renderPluginsPost, scene, camera ); + + + // Generate mipmap if we're using any kind of mipmap filtering + + if ( renderTarget && renderTarget.generateMipmaps && renderTarget.minFilter !== THREE.NearestFilter && renderTarget.minFilter !== THREE.LinearFilter ) { + + updateRenderTargetMipmap( renderTarget ); + + } + + // Ensure depth buffer writing is enabled so it can be cleared on next render + + this.setDepthTest( true ); + this.setDepthWrite( true ); + + // _gl.finish(); + + }; + + function projectObject(scene, object,camera){ + + if ( object.visible === false ) return; + + var webglObjects = scene.__webglObjects[ object.id ]; + + if ( webglObjects && ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) ) { + + updateObject( scene, object ); + + for ( var i = 0, l = webglObjects.length; i < l; i ++ ) { + + var webglObject = webglObjects[i]; + + unrollBufferMaterial( webglObject ); + + webglObject.render = true; + + if ( _this.sortObjects === true ) { + + if ( object.renderDepth !== null ) { + + webglObject.z = object.renderDepth; + + } else { + + _vector3.setFromMatrixPosition( object.matrixWorld ); + _vector3.applyProjection( _projScreenMatrix ); + + webglObject.z = _vector3.z; + + } + + } + + } + + } + + for ( var i = 0, l = object.children.length; i < l; i ++ ) { + + projectObject( scene, object.children[ i ], camera ); + + } + + } + + function renderPlugins( plugins, scene, camera ) { + + if ( plugins.length === 0 ) return; + + for ( var i = 0, il = plugins.length; i < il; i ++ ) { + + // reset state for plugin (to start from clean slate) + + _currentProgram = null; + _currentCamera = null; + + _oldBlending = - 1; + _oldDepthTest = - 1; + _oldDepthWrite = - 1; + _oldDoubleSided = - 1; + _oldFlipSided = - 1; + _currentGeometryGroupHash = - 1; + _currentMaterialId = - 1; + + _lightsNeedUpdate = true; + + plugins[ i ].render( scene, camera, _currentWidth, _currentHeight ); + + // reset state after plugin (anything could have changed) + + _currentProgram = null; + _currentCamera = null; + + _oldBlending = - 1; + _oldDepthTest = - 1; + _oldDepthWrite = - 1; + _oldDoubleSided = - 1; + _oldFlipSided = - 1; + _currentGeometryGroupHash = - 1; + _currentMaterialId = - 1; + + _lightsNeedUpdate = true; + + } + + }; + + function renderObjects( renderList, camera, lights, fog, useBlending, overrideMaterial ) { + + var webglObject, object, buffer, material; + + for ( var i = renderList.length - 1; i !== - 1; i -- ) { + + webglObject = renderList[ i ]; + + object = webglObject.object; + buffer = webglObject.buffer; + + setupMatrices( object, camera ); + + if ( overrideMaterial ) { + + material = overrideMaterial; + + } else { + + material = webglObject.material; + + if ( ! material ) continue; + + if ( useBlending ) _this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); + + _this.setDepthTest( material.depthTest ); + _this.setDepthWrite( material.depthWrite ); + setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); + + } + + _this.setMaterialFaces( material ); + + if ( buffer instanceof THREE.BufferGeometry ) { + + _this.renderBufferDirect( camera, lights, fog, material, buffer, object ); + + } else { + + _this.renderBuffer( camera, lights, fog, material, buffer, object ); + + } + + } + + }; + + function renderObjectsImmediate ( renderList, materialType, camera, lights, fog, useBlending, overrideMaterial ) { + + var webglObject, object, material, program; + + for ( var i = 0, il = renderList.length; i < il; i ++ ) { + + webglObject = renderList[ i ]; + object = webglObject.object; + + if ( object.visible ) { + + if ( overrideMaterial ) { + + material = overrideMaterial; + + } else { + + material = webglObject[ materialType ]; + + if ( ! material ) continue; + + if ( useBlending ) _this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); + + _this.setDepthTest( material.depthTest ); + _this.setDepthWrite( material.depthWrite ); + setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); + + } + + _this.renderImmediateObject( camera, lights, fog, material, object ); + + } + + } + + }; + + this.renderImmediateObject = function ( camera, lights, fog, material, object ) { + + var program = setProgram( camera, lights, fog, material, object ); + + _currentGeometryGroupHash = - 1; + + _this.setMaterialFaces( material ); + + if ( object.immediateRenderCallback ) { + + object.immediateRenderCallback( program, _gl, _frustum ); + + } else { + + object.render( function ( object ) { _this.renderBufferImmediate( object, program, material ); } ); + + } + + }; + + function unrollImmediateBufferMaterial ( globject ) { + + var object = globject.object, + material = object.material; + + if ( material.transparent ) { + + globject.transparent = material; + globject.opaque = null; + + } else { + + globject.opaque = material; + globject.transparent = null; + + } + + }; + + function unrollBufferMaterial ( globject ) { + + var object = globject.object; + var buffer = globject.buffer; + + var geometry = object.geometry; + var material = object.material; + + if ( material instanceof THREE.MeshFaceMaterial ) { + + var materialIndex = geometry instanceof THREE.BufferGeometry ? 0 : buffer.materialIndex; + + material = material.materials[ materialIndex ]; + + if ( material.transparent ) { + + globject.material = material; + transparentObjects.push( globject ); + + } else { + + globject.material = material; + opaqueObjects.push( globject ); + + } + + } else { + + if ( material ) { + + if ( material.transparent ) { + + globject.material = material; + transparentObjects.push( globject ); + + } else { + + globject.material = material; + opaqueObjects.push( globject ); + + } + + } + + } + + }; + + // Objects refresh + + var initObjects = function ( scene ) { + + if ( ! scene.__webglObjects ) { + + scene.__webglObjects = {}; + scene.__webglObjectsImmediate = []; + + } + + while ( scene.__objectsAdded.length ) { + + addObject( scene.__objectsAdded[ 0 ], scene ); + scene.__objectsAdded.splice( 0, 1 ); + + } + + while ( scene.__objectsRemoved.length ) { + + removeObject( scene.__objectsRemoved[ 0 ], scene ); + scene.__objectsRemoved.splice( 0, 1 ); + + } + + }; + + // Objects adding + + function addObject( object, scene ) { + + var g, geometry, geometryGroup; + + if ( object.__webglInit === undefined ) { + + object.__webglInit = true; + + object._modelViewMatrix = new THREE.Matrix4(); + object._normalMatrix = new THREE.Matrix3(); + + } + + geometry = object.geometry; + + if ( geometry === undefined ) { + + // ImmediateRenderObject + + } else if ( geometry.__webglInit === undefined ) { + + geometry.__webglInit = true; + geometry.addEventListener( 'dispose', onGeometryDispose ); + + if ( geometry instanceof THREE.BufferGeometry ) { + + initDirectBuffers( geometry ); + + } else if ( object instanceof THREE.Mesh ) { + + if ( object.__webglActive !== undefined ) { + + removeObject( object, scene ); + + } + + initGeometryGroups(scene, object, geometry); + + } else if ( object instanceof THREE.Line ) { + + if ( ! geometry.__webglVertexBuffer ) { + + createLineBuffers( geometry ); + initLineBuffers( geometry, object ); + + geometry.verticesNeedUpdate = true; + geometry.colorsNeedUpdate = true; + geometry.lineDistancesNeedUpdate = true; + + } + + } else if ( object instanceof THREE.PointCloud ) { + + if ( ! geometry.__webglVertexBuffer ) { + + createParticleBuffers( geometry ); + initParticleBuffers( geometry, object ); + + geometry.verticesNeedUpdate = true; + geometry.colorsNeedUpdate = true; + + } + + } + + } + + if ( object.__webglActive === undefined) { + + if ( object instanceof THREE.Mesh ) { + + geometry = object.geometry; + + if ( geometry instanceof THREE.BufferGeometry ) { + + addBuffer( scene.__webglObjects, geometry, object ); + + } else if ( geometry instanceof THREE.Geometry ) { + + for ( var i = 0,l = geometry.geometryGroupsList.length; i= 0; o -- ) { + + if ( objlist[ o ].object === object ) { + + objlist.splice( o, 1 ); + + } + + } + + }; + + // Materials + + this.initMaterial = function ( material, lights, fog, object ) { + + material.addEventListener( 'dispose', onMaterialDispose ); + + var u, a, identifiers, i, parameters, maxLightCount, maxBones, maxShadows, shaderID; + + if ( material instanceof THREE.MeshDepthMaterial ) { + + shaderID = 'depth'; + + } else if ( material instanceof THREE.MeshNormalMaterial ) { + + shaderID = 'normal'; + + } else if ( material instanceof THREE.MeshBasicMaterial ) { + + shaderID = 'basic'; + + } else if ( material instanceof THREE.MeshLambertMaterial ) { + + shaderID = 'lambert'; + + } else if ( material instanceof THREE.MeshPhongMaterial ) { + + shaderID = 'phong'; + + } else if ( material instanceof THREE.LineBasicMaterial ) { + + shaderID = 'basic'; + + } else if ( material instanceof THREE.LineDashedMaterial ) { + + shaderID = 'dashed'; + + } else if ( material instanceof THREE.PointCloudMaterial ) { + + shaderID = 'particle_basic'; + + } + + if ( shaderID ) { + + var shader = THREE.ShaderLib[ shaderID ]; + + material.__webglShader = { + uniforms: THREE.UniformsUtils.clone( shader.uniforms ), + vertexShader: shader.vertexShader, + fragmentShader: shader.fragmentShader + } + + } else { + + material.__webglShader = { + uniforms: material.uniforms, + vertexShader: material.vertexShader, + fragmentShader: material.fragmentShader + } + + } + + // heuristics to create shader parameters according to lights in the scene + // (not to blow over maxLights budget) + + maxLightCount = allocateLights( lights ); + + maxShadows = allocateShadows( lights ); + + maxBones = allocateBones( object ); + + parameters = { + + precision: _precision, + supportsVertexTextures: _supportsVertexTextures, + + map: !! material.map, + envMap: !! material.envMap, + lightMap: !! material.lightMap, + bumpMap: !! material.bumpMap, + normalMap: !! material.normalMap, + specularMap: !! material.specularMap, + alphaMap: !! material.alphaMap, + + vertexColors: material.vertexColors, + + fog: fog, + useFog: material.fog, + fogExp: fog instanceof THREE.FogExp2, + + sizeAttenuation: material.sizeAttenuation, + logarithmicDepthBuffer: _logarithmicDepthBuffer, + + skinning: material.skinning, + maxBones: maxBones, + useVertexTexture: _supportsBoneTextures && object && object.skeleton && object.skeleton.useVertexTexture, + + morphTargets: material.morphTargets, + morphNormals: material.morphNormals, + maxMorphTargets: this.maxMorphTargets, + maxMorphNormals: this.maxMorphNormals, + + maxDirLights: maxLightCount.directional, + maxPointLights: maxLightCount.point, + maxSpotLights: maxLightCount.spot, + maxHemiLights: maxLightCount.hemi, + + maxShadows: maxShadows, + shadowMapEnabled: this.shadowMapEnabled && object.receiveShadow && maxShadows > 0, + shadowMapType: this.shadowMapType, + shadowMapDebug: this.shadowMapDebug, + shadowMapCascade: this.shadowMapCascade, + + alphaTest: material.alphaTest, + metal: material.metal, + wrapAround: material.wrapAround, + doubleSided: material.side === THREE.DoubleSide, + flipSided: material.side === THREE.BackSide + + }; + + // Generate code + + var chunks = []; + + if ( shaderID ) { + + chunks.push( shaderID ); + + } else { + + chunks.push( material.fragmentShader ); + chunks.push( material.vertexShader ); + + } + + for ( var d in material.defines ) { + + chunks.push( d ); + chunks.push( material.defines[ d ] ); + + } + + for ( var p in parameters ) { + + chunks.push( p ); + chunks.push( parameters[ p ] ); + + } + + var code = chunks.join(); + + var program; + + // Check if code has been already compiled + + for ( var p = 0, pl = _programs.length; p < pl; p ++ ) { + + var programInfo = _programs[ p ]; + + if ( programInfo.code === code ) { + + program = programInfo; + program.usedTimes ++; + + break; + + } + + } + + if ( program === undefined ) { + + program = new THREE.WebGLProgram( this, code, material, parameters ); + _programs.push( program ); + + _this.info.memory.programs = _programs.length; + + } + + material.program = program; + + var attributes = material.program.attributes; + + if ( material.morphTargets ) { + + material.numSupportedMorphTargets = 0; + + var id, base = 'morphTarget'; + + for ( i = 0; i < this.maxMorphTargets; i ++ ) { + + id = base + i; + + if ( attributes[ id ] >= 0 ) { + + material.numSupportedMorphTargets ++; + + } + + } + + } + + if ( material.morphNormals ) { + + material.numSupportedMorphNormals = 0; + + var id, base = 'morphNormal'; + + for ( i = 0; i < this.maxMorphNormals; i ++ ) { + + id = base + i; + + if ( attributes[ id ] >= 0 ) { + + material.numSupportedMorphNormals ++; + + } + + } + + } + + material.uniformsList = []; + + for ( u in material.__webglShader.uniforms ) { + + var location = material.program.uniforms[ u ]; + + if ( location ) { + material.uniformsList.push( [ material.__webglShader.uniforms[ u ], location ] ); + } + + } + + }; + + function setProgram( camera, lights, fog, material, object ) { + + _usedTextureUnits = 0; + + if ( material.needsUpdate ) { + + if ( material.program ) deallocateMaterial( material ); + + _this.initMaterial( material, lights, fog, object ); + material.needsUpdate = false; + + } + + if ( material.morphTargets ) { + + if ( ! object.__webglMorphTargetInfluences ) { + + object.__webglMorphTargetInfluences = new Float32Array( _this.maxMorphTargets ); + + } + + } + + var refreshProgram = false; + var refreshMaterial = false; + var refreshLights = false; + + var program = material.program, + p_uniforms = program.uniforms, + m_uniforms = material.__webglShader.uniforms; + + if ( program.id !== _currentProgram ) { + + _gl.useProgram( program.program ); + _currentProgram = program.id; + + refreshProgram = true; + refreshMaterial = true; + refreshLights = true; + + } + + if ( material.id !== _currentMaterialId ) { + + if ( _currentMaterialId === -1 ) refreshLights = true; + _currentMaterialId = material.id; + + refreshMaterial = true; + + } + + if ( refreshProgram || camera !== _currentCamera ) { + + _gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); + + if ( _logarithmicDepthBuffer ) { + + _gl.uniform1f( p_uniforms.logDepthBufFC, 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); + + } + + + if ( camera !== _currentCamera ) _currentCamera = camera; + + // load material specific uniforms + // (shader material also gets them for the sake of genericity) + + if ( material instanceof THREE.ShaderMaterial || + material instanceof THREE.MeshPhongMaterial || + material.envMap ) { + + if ( p_uniforms.cameraPosition !== null ) { + + _vector3.setFromMatrixPosition( camera.matrixWorld ); + _gl.uniform3f( p_uniforms.cameraPosition, _vector3.x, _vector3.y, _vector3.z ); + + } + + } + + if ( material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.ShaderMaterial || + material.skinning ) { + + if ( p_uniforms.viewMatrix !== null ) { + + _gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, camera.matrixWorldInverse.elements ); + + } + + } + + } + + // skinning uniforms must be set even if material didn't change + // auto-setting of texture unit for bone texture must go before other textures + // not sure why, but otherwise weird things happen + + if ( material.skinning ) { + + if ( object.bindMatrix && p_uniforms.bindMatrix !== null ) { + + _gl.uniformMatrix4fv( p_uniforms.bindMatrix, false, object.bindMatrix.elements ); + + } + + if ( object.bindMatrixInverse && p_uniforms.bindMatrixInverse !== null ) { + + _gl.uniformMatrix4fv( p_uniforms.bindMatrixInverse, false, object.bindMatrixInverse.elements ); + + } + + if ( _supportsBoneTextures && object.skeleton && object.skeleton.useVertexTexture ) { + + if ( p_uniforms.boneTexture !== null ) { + + var textureUnit = getTextureUnit(); + + _gl.uniform1i( p_uniforms.boneTexture, textureUnit ); + _this.setTexture( object.skeleton.boneTexture, textureUnit ); + + } + + if ( p_uniforms.boneTextureWidth !== null ) { + + _gl.uniform1i( p_uniforms.boneTextureWidth, object.skeleton.boneTextureWidth ); + + } + + if ( p_uniforms.boneTextureHeight !== null ) { + + _gl.uniform1i( p_uniforms.boneTextureHeight, object.skeleton.boneTextureHeight ); + + } + + } else if ( object.skeleton && object.skeleton.boneMatrices ) { + + if ( p_uniforms.boneGlobalMatrices !== null ) { + + _gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.skeleton.boneMatrices ); + + } + + } + + } + + if ( refreshMaterial ) { + + // refresh uniforms common to several materials + + if ( fog && material.fog ) { + + refreshUniformsFog( m_uniforms, fog ); + + } + + if ( material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.MeshLambertMaterial || + material.lights ) { + + if ( _lightsNeedUpdate ) { + + refreshLights = true; + setupLights( lights ); + _lightsNeedUpdate = false; + } + + if ( refreshLights ) { + refreshUniformsLights( m_uniforms, _lights ); + markUniformsLightsNeedsUpdate( m_uniforms, true ); + } else { + markUniformsLightsNeedsUpdate( m_uniforms, false ); + } + + } + + if ( material instanceof THREE.MeshBasicMaterial || + material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.MeshPhongMaterial ) { + + refreshUniformsCommon( m_uniforms, material ); + + } + + // refresh single material specific uniforms + + if ( material instanceof THREE.LineBasicMaterial ) { + + refreshUniformsLine( m_uniforms, material ); + + } else if ( material instanceof THREE.LineDashedMaterial ) { + + refreshUniformsLine( m_uniforms, material ); + refreshUniformsDash( m_uniforms, material ); + + } else if ( material instanceof THREE.PointCloudMaterial ) { + + refreshUniformsParticle( m_uniforms, material ); + + } else if ( material instanceof THREE.MeshPhongMaterial ) { + + refreshUniformsPhong( m_uniforms, material ); + + } else if ( material instanceof THREE.MeshLambertMaterial ) { + + refreshUniformsLambert( m_uniforms, material ); + + } else if ( material instanceof THREE.MeshDepthMaterial ) { + + m_uniforms.mNear.value = camera.near; + m_uniforms.mFar.value = camera.far; + m_uniforms.opacity.value = material.opacity; + + } else if ( material instanceof THREE.MeshNormalMaterial ) { + + m_uniforms.opacity.value = material.opacity; + + } + + if ( object.receiveShadow && ! material._shadowPass ) { + + refreshUniformsShadow( m_uniforms, lights ); + + } + + // load common uniforms + + loadUniformsGeneric( material.uniformsList ); + + } + + loadUniformsMatrices( p_uniforms, object ); + + if ( p_uniforms.modelMatrix !== null ) { + + _gl.uniformMatrix4fv( p_uniforms.modelMatrix, false, object.matrixWorld.elements ); + + } + + return program; + + }; + + // Uniforms (refresh uniforms objects) + + function refreshUniformsCommon ( uniforms, material ) { + + uniforms.opacity.value = material.opacity; + + if ( _this.gammaInput ) { + + uniforms.diffuse.value.copyGammaToLinear( material.color ); + + } else { + + uniforms.diffuse.value = material.color; + + } + + uniforms.map.value = material.map; + uniforms.lightMap.value = material.lightMap; + uniforms.specularMap.value = material.specularMap; + uniforms.alphaMap.value = material.alphaMap; + + if ( material.bumpMap ) { + + uniforms.bumpMap.value = material.bumpMap; + uniforms.bumpScale.value = material.bumpScale; + + } + + if ( material.normalMap ) { + + uniforms.normalMap.value = material.normalMap; + uniforms.normalScale.value.copy( material.normalScale ); + + } + + // uv repeat and offset setting priorities + // 1. color map + // 2. specular map + // 3. normal map + // 4. bump map + // 5. alpha map + + var uvScaleMap; + + if ( material.map ) { + + uvScaleMap = material.map; + + } else if ( material.specularMap ) { + + uvScaleMap = material.specularMap; + + } else if ( material.normalMap ) { + + uvScaleMap = material.normalMap; + + } else if ( material.bumpMap ) { + + uvScaleMap = material.bumpMap; + + } else if ( material.alphaMap ) { + + uvScaleMap = material.alphaMap; + + } + + if ( uvScaleMap !== undefined ) { + + var offset = uvScaleMap.offset; + var repeat = uvScaleMap.repeat; + + uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y ); + + } + + uniforms.envMap.value = material.envMap; + uniforms.flipEnvMap.value = ( material.envMap instanceof THREE.WebGLRenderTargetCube ) ? 1 : - 1; + + if ( _this.gammaInput ) { + + //uniforms.reflectivity.value = material.reflectivity * material.reflectivity; + uniforms.reflectivity.value = material.reflectivity; + + } else { + + uniforms.reflectivity.value = material.reflectivity; + + } + + uniforms.refractionRatio.value = material.refractionRatio; + uniforms.combine.value = material.combine; + uniforms.useRefract.value = material.envMap && material.envMap.mapping instanceof THREE.CubeRefractionMapping; + + }; + + function refreshUniformsLine ( uniforms, material ) { + + uniforms.diffuse.value = material.color; + uniforms.opacity.value = material.opacity; + + }; + + function refreshUniformsDash ( uniforms, material ) { + + uniforms.dashSize.value = material.dashSize; + uniforms.totalSize.value = material.dashSize + material.gapSize; + uniforms.scale.value = material.scale; + + }; + + function refreshUniformsParticle ( uniforms, material ) { + + uniforms.psColor.value = material.color; + uniforms.opacity.value = material.opacity; + uniforms.size.value = material.size; + uniforms.scale.value = _canvas.height / 2.0; // TODO: Cache this. + + uniforms.map.value = material.map; + + }; + + function refreshUniformsFog ( uniforms, fog ) { + + uniforms.fogColor.value = fog.color; + + if ( fog instanceof THREE.Fog ) { + + uniforms.fogNear.value = fog.near; + uniforms.fogFar.value = fog.far; + + } else if ( fog instanceof THREE.FogExp2 ) { + + uniforms.fogDensity.value = fog.density; + + } + + }; + + function refreshUniformsPhong ( uniforms, material ) { + + uniforms.shininess.value = material.shininess; + + if ( _this.gammaInput ) { + + uniforms.ambient.value.copyGammaToLinear( material.ambient ); + uniforms.emissive.value.copyGammaToLinear( material.emissive ); + uniforms.specular.value.copyGammaToLinear( material.specular ); + + } else { + + uniforms.ambient.value = material.ambient; + uniforms.emissive.value = material.emissive; + uniforms.specular.value = material.specular; + + } + + if ( material.wrapAround ) { + + uniforms.wrapRGB.value.copy( material.wrapRGB ); + + } + + }; + + function refreshUniformsLambert ( uniforms, material ) { + + if ( _this.gammaInput ) { + + uniforms.ambient.value.copyGammaToLinear( material.ambient ); + uniforms.emissive.value.copyGammaToLinear( material.emissive ); + + } else { + + uniforms.ambient.value = material.ambient; + uniforms.emissive.value = material.emissive; + + } + + if ( material.wrapAround ) { + + uniforms.wrapRGB.value.copy( material.wrapRGB ); + + } + + }; + + function refreshUniformsLights ( uniforms, lights ) { + + uniforms.ambientLightColor.value = lights.ambient; + + uniforms.directionalLightColor.value = lights.directional.colors; + uniforms.directionalLightDirection.value = lights.directional.positions; + + uniforms.pointLightColor.value = lights.point.colors; + uniforms.pointLightPosition.value = lights.point.positions; + uniforms.pointLightDistance.value = lights.point.distances; + + uniforms.spotLightColor.value = lights.spot.colors; + uniforms.spotLightPosition.value = lights.spot.positions; + uniforms.spotLightDistance.value = lights.spot.distances; + uniforms.spotLightDirection.value = lights.spot.directions; + uniforms.spotLightAngleCos.value = lights.spot.anglesCos; + uniforms.spotLightExponent.value = lights.spot.exponents; + + uniforms.hemisphereLightSkyColor.value = lights.hemi.skyColors; + uniforms.hemisphereLightGroundColor.value = lights.hemi.groundColors; + uniforms.hemisphereLightDirection.value = lights.hemi.positions; + + }; + + // If uniforms are marked as clean, they don't need to be loaded to the GPU. + + function markUniformsLightsNeedsUpdate ( uniforms, boolean ) { + + uniforms.ambientLightColor.needsUpdate = boolean; + + uniforms.directionalLightColor.needsUpdate = boolean; + uniforms.directionalLightDirection.needsUpdate = boolean; + + uniforms.pointLightColor.needsUpdate = boolean; + uniforms.pointLightPosition.needsUpdate = boolean; + uniforms.pointLightDistance.needsUpdate = boolean; + + uniforms.spotLightColor.needsUpdate = boolean; + uniforms.spotLightPosition.needsUpdate = boolean; + uniforms.spotLightDistance.needsUpdate = boolean; + uniforms.spotLightDirection.needsUpdate = boolean; + uniforms.spotLightAngleCos.needsUpdate = boolean; + uniforms.spotLightExponent.needsUpdate = boolean; + + uniforms.hemisphereLightSkyColor.needsUpdate = boolean; + uniforms.hemisphereLightGroundColor.needsUpdate = boolean; + uniforms.hemisphereLightDirection.needsUpdate = boolean; + + }; + + function refreshUniformsShadow ( uniforms, lights ) { + + if ( uniforms.shadowMatrix ) { + + var j = 0; + + for ( var i = 0, il = lights.length; i < il; i ++ ) { + + var light = lights[ i ]; + + if ( ! light.castShadow ) continue; + + if ( light instanceof THREE.SpotLight || ( light instanceof THREE.DirectionalLight && ! light.shadowCascade ) ) { + + uniforms.shadowMap.value[ j ] = light.shadowMap; + uniforms.shadowMapSize.value[ j ] = light.shadowMapSize; + + uniforms.shadowMatrix.value[ j ] = light.shadowMatrix; + + uniforms.shadowDarkness.value[ j ] = light.shadowDarkness; + uniforms.shadowBias.value[ j ] = light.shadowBias; + + j ++; + + } + + } + + } + + }; + + // Uniforms (load to GPU) + + function loadUniformsMatrices ( uniforms, object ) { + + _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object._modelViewMatrix.elements ); + + if ( uniforms.normalMatrix ) { + + _gl.uniformMatrix3fv( uniforms.normalMatrix, false, object._normalMatrix.elements ); + + } + + }; + + function getTextureUnit() { + + var textureUnit = _usedTextureUnits; + + if ( textureUnit >= _maxTextures ) { + + console.warn( 'WebGLRenderer: trying to use ' + textureUnit + ' texture units while this GPU supports only ' + _maxTextures ); + + } + + _usedTextureUnits += 1; + + return textureUnit; + + }; + + function loadUniformsGeneric ( uniforms ) { + + var texture, textureUnit, offset; + + for ( var j = 0, jl = uniforms.length; j < jl; j ++ ) { + + var uniform = uniforms[ j ][ 0 ]; + + // needsUpdate property is not added to all uniforms. + if ( uniform.needsUpdate === false ) continue; + + var type = uniform.type; + var value = uniform.value; + var location = uniforms[ j ][ 1 ]; + + switch ( type ) { + + case '1i': + _gl.uniform1i( location, value ); + break; + + case '1f': + _gl.uniform1f( location, value ); + break; + + case '2f': + _gl.uniform2f( location, value[ 0 ], value[ 1 ] ); + break; + + case '3f': + _gl.uniform3f( location, value[ 0 ], value[ 1 ], value[ 2 ] ); + break; + + case '4f': + _gl.uniform4f( location, value[ 0 ], value[ 1 ], value[ 2 ], value[ 3 ] ); + break; + + case '1iv': + _gl.uniform1iv( location, value ); + break; + + case '3iv': + _gl.uniform3iv( location, value ); + break; + + case '1fv': + _gl.uniform1fv( location, value ); + break; + + case '2fv': + _gl.uniform2fv( location, value ); + break; + + case '3fv': + _gl.uniform3fv( location, value ); + break; + + case '4fv': + _gl.uniform4fv( location, value ); + break; + + case 'Matrix3fv': + _gl.uniformMatrix3fv( location, false, value ); + break; + + case 'Matrix4fv': + _gl.uniformMatrix4fv( location, false, value ); + break; + + // + + case 'i': + + // single integer + _gl.uniform1i( location, value ); + + break; + + case 'f': + + // single float + _gl.uniform1f( location, value ); + + break; + + case 'v2': + + // single THREE.Vector2 + _gl.uniform2f( location, value.x, value.y ); + + break; + + case 'v3': + + // single THREE.Vector3 + _gl.uniform3f( location, value.x, value.y, value.z ); + + break; + + case 'v4': + + // single THREE.Vector4 + _gl.uniform4f( location, value.x, value.y, value.z, value.w ); + + break; + + case 'c': + + // single THREE.Color + _gl.uniform3f( location, value.r, value.g, value.b ); + + break; + + case 'iv1': + + // flat array of integers (JS or typed array) + _gl.uniform1iv( location, value ); + + break; + + case 'iv': + + // flat array of integers with 3 x N size (JS or typed array) + _gl.uniform3iv( location, value ); + + break; + + case 'fv1': + + // flat array of floats (JS or typed array) + _gl.uniform1fv( location, value ); + + break; + + case 'fv': + + // flat array of floats with 3 x N size (JS or typed array) + _gl.uniform3fv( location, value ); + + break; + + case 'v2v': + + // array of THREE.Vector2 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 2 * value.length ); + + } + + for ( var i = 0, il = value.length; i < il; i ++ ) { + + offset = i * 2; + + uniform._array[ offset ] = value[ i ].x; + uniform._array[ offset + 1 ] = value[ i ].y; + + } + + _gl.uniform2fv( location, uniform._array ); + + break; + + case 'v3v': + + // array of THREE.Vector3 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 3 * value.length ); + + } + + for ( var i = 0, il = value.length; i < il; i ++ ) { + + offset = i * 3; + + uniform._array[ offset ] = value[ i ].x; + uniform._array[ offset + 1 ] = value[ i ].y; + uniform._array[ offset + 2 ] = value[ i ].z; + + } + + _gl.uniform3fv( location, uniform._array ); + + break; + + case 'v4v': + + // array of THREE.Vector4 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 4 * value.length ); + + } + + for ( var i = 0, il = value.length; i < il; i ++ ) { + + offset = i * 4; + + uniform._array[ offset ] = value[ i ].x; + uniform._array[ offset + 1 ] = value[ i ].y; + uniform._array[ offset + 2 ] = value[ i ].z; + uniform._array[ offset + 3 ] = value[ i ].w; + + } + + _gl.uniform4fv( location, uniform._array ); + + break; + + case 'm3': + + // single THREE.Matrix3 + _gl.uniformMatrix3fv( location, false, value.elements ); + + break; + + case 'm3v': + + // array of THREE.Matrix3 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 9 * value.length ); + + } + + for ( var i = 0, il = value.length; i < il; i ++ ) { + + value[ i ].flattenToArrayOffset( uniform._array, i * 9 ); + + } + + _gl.uniformMatrix3fv( location, false, uniform._array ); + + break; + + case 'm4': + + // single THREE.Matrix4 + _gl.uniformMatrix4fv( location, false, value.elements ); + + break; + + case 'm4v': + + // array of THREE.Matrix4 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 16 * value.length ); + + } + + for ( var i = 0, il = value.length; i < il; i ++ ) { + + value[ i ].flattenToArrayOffset( uniform._array, i * 16 ); + + } + + _gl.uniformMatrix4fv( location, false, uniform._array ); + + break; + + case 't': + + // single THREE.Texture (2d or cube) + + texture = value; + textureUnit = getTextureUnit(); + + _gl.uniform1i( location, textureUnit ); + + if ( ! texture ) continue; + + if ( texture instanceof THREE.CubeTexture || + ( texture.image instanceof Array && texture.image.length === 6 ) ) { // CompressedTexture can have Array in image :/ + + setCubeTexture( texture, textureUnit ); + + } else if ( texture instanceof THREE.WebGLRenderTargetCube ) { + + setCubeTextureDynamic( texture, textureUnit ); + + } else { + + _this.setTexture( texture, textureUnit ); + + } + + break; + + case 'tv': + + // array of THREE.Texture (2d) + + if ( uniform._array === undefined ) { + + uniform._array = []; + + } + + for ( var i = 0, il = uniform.value.length; i < il; i ++ ) { + + uniform._array[ i ] = getTextureUnit(); + + } + + _gl.uniform1iv( location, uniform._array ); + + for ( var i = 0, il = uniform.value.length; i < il; i ++ ) { + + texture = uniform.value[ i ]; + textureUnit = uniform._array[ i ]; + + if ( ! texture ) continue; + + _this.setTexture( texture, textureUnit ); + + } + + break; + + default: + + console.warn( 'THREE.WebGLRenderer: Unknown uniform type: ' + type ); + + } + + } + + }; + + function setupMatrices ( object, camera ) { + + object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + object._normalMatrix.getNormalMatrix( object._modelViewMatrix ); + + }; + + // + + function setColorGamma( array, offset, color, intensitySq ) { + + array[ offset ] = color.r * color.r * intensitySq; + array[ offset + 1 ] = color.g * color.g * intensitySq; + array[ offset + 2 ] = color.b * color.b * intensitySq; + + }; + + function setColorLinear( array, offset, color, intensity ) { + + array[ offset ] = color.r * intensity; + array[ offset + 1 ] = color.g * intensity; + array[ offset + 2 ] = color.b * intensity; + + }; + + function setupLights ( lights ) { + + var l, ll, light, n, + r = 0, g = 0, b = 0, + color, skyColor, groundColor, + intensity, intensitySq, + position, + distance, + + zlights = _lights, + + dirColors = zlights.directional.colors, + dirPositions = zlights.directional.positions, + + pointColors = zlights.point.colors, + pointPositions = zlights.point.positions, + pointDistances = zlights.point.distances, + + spotColors = zlights.spot.colors, + spotPositions = zlights.spot.positions, + spotDistances = zlights.spot.distances, + spotDirections = zlights.spot.directions, + spotAnglesCos = zlights.spot.anglesCos, + spotExponents = zlights.spot.exponents, + + hemiSkyColors = zlights.hemi.skyColors, + hemiGroundColors = zlights.hemi.groundColors, + hemiPositions = zlights.hemi.positions, + + dirLength = 0, + pointLength = 0, + spotLength = 0, + hemiLength = 0, + + dirCount = 0, + pointCount = 0, + spotCount = 0, + hemiCount = 0, + + dirOffset = 0, + pointOffset = 0, + spotOffset = 0, + hemiOffset = 0; + + for ( l = 0, ll = lights.length; l < ll; l ++ ) { + + light = lights[ l ]; + + if ( light.onlyShadow ) continue; + + color = light.color; + intensity = light.intensity; + distance = light.distance; + + if ( light instanceof THREE.AmbientLight ) { + + if ( ! light.visible ) continue; + + if ( _this.gammaInput ) { + + r += color.r * color.r; + g += color.g * color.g; + b += color.b * color.b; + + } else { + + r += color.r; + g += color.g; + b += color.b; + + } + + } else if ( light instanceof THREE.DirectionalLight ) { + + dirCount += 1; + + if ( ! light.visible ) continue; + + _direction.setFromMatrixPosition( light.matrixWorld ); + _vector3.setFromMatrixPosition( light.target.matrixWorld ); + _direction.sub( _vector3 ); + _direction.normalize(); + + dirOffset = dirLength * 3; + + dirPositions[ dirOffset ] = _direction.x; + dirPositions[ dirOffset + 1 ] = _direction.y; + dirPositions[ dirOffset + 2 ] = _direction.z; + + if ( _this.gammaInput ) { + + setColorGamma( dirColors, dirOffset, color, intensity * intensity ); + + } else { + + setColorLinear( dirColors, dirOffset, color, intensity ); + + } + + dirLength += 1; + + } else if ( light instanceof THREE.PointLight ) { + + pointCount += 1; + + if ( ! light.visible ) continue; + + pointOffset = pointLength * 3; + + if ( _this.gammaInput ) { + + setColorGamma( pointColors, pointOffset, color, intensity * intensity ); + + } else { + + setColorLinear( pointColors, pointOffset, color, intensity ); + + } + + _vector3.setFromMatrixPosition( light.matrixWorld ); + + pointPositions[ pointOffset ] = _vector3.x; + pointPositions[ pointOffset + 1 ] = _vector3.y; + pointPositions[ pointOffset + 2 ] = _vector3.z; + + pointDistances[ pointLength ] = distance; + + pointLength += 1; + + } else if ( light instanceof THREE.SpotLight ) { + + spotCount += 1; + + if ( ! light.visible ) continue; + + spotOffset = spotLength * 3; + + if ( _this.gammaInput ) { + + setColorGamma( spotColors, spotOffset, color, intensity * intensity ); + + } else { + + setColorLinear( spotColors, spotOffset, color, intensity ); + + } + + _vector3.setFromMatrixPosition( light.matrixWorld ); + + spotPositions[ spotOffset ] = _vector3.x; + spotPositions[ spotOffset + 1 ] = _vector3.y; + spotPositions[ spotOffset + 2 ] = _vector3.z; + + spotDistances[ spotLength ] = distance; + + _direction.copy( _vector3 ); + _vector3.setFromMatrixPosition( light.target.matrixWorld ); + _direction.sub( _vector3 ); + _direction.normalize(); + + spotDirections[ spotOffset ] = _direction.x; + spotDirections[ spotOffset + 1 ] = _direction.y; + spotDirections[ spotOffset + 2 ] = _direction.z; + + spotAnglesCos[ spotLength ] = Math.cos( light.angle ); + spotExponents[ spotLength ] = light.exponent; + + spotLength += 1; + + } else if ( light instanceof THREE.HemisphereLight ) { + + hemiCount += 1; + + if ( ! light.visible ) continue; + + _direction.setFromMatrixPosition( light.matrixWorld ); + _direction.normalize(); + + hemiOffset = hemiLength * 3; + + hemiPositions[ hemiOffset ] = _direction.x; + hemiPositions[ hemiOffset + 1 ] = _direction.y; + hemiPositions[ hemiOffset + 2 ] = _direction.z; + + skyColor = light.color; + groundColor = light.groundColor; + + if ( _this.gammaInput ) { + + intensitySq = intensity * intensity; + + setColorGamma( hemiSkyColors, hemiOffset, skyColor, intensitySq ); + setColorGamma( hemiGroundColors, hemiOffset, groundColor, intensitySq ); + + } else { + + setColorLinear( hemiSkyColors, hemiOffset, skyColor, intensity ); + setColorLinear( hemiGroundColors, hemiOffset, groundColor, intensity ); + + } + + hemiLength += 1; + + } + + } + + // null eventual remains from removed lights + // (this is to avoid if in shader) + + for ( l = dirLength * 3, ll = Math.max( dirColors.length, dirCount * 3 ); l < ll; l ++ ) dirColors[ l ] = 0.0; + for ( l = pointLength * 3, ll = Math.max( pointColors.length, pointCount * 3 ); l < ll; l ++ ) pointColors[ l ] = 0.0; + for ( l = spotLength * 3, ll = Math.max( spotColors.length, spotCount * 3 ); l < ll; l ++ ) spotColors[ l ] = 0.0; + for ( l = hemiLength * 3, ll = Math.max( hemiSkyColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiSkyColors[ l ] = 0.0; + for ( l = hemiLength * 3, ll = Math.max( hemiGroundColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiGroundColors[ l ] = 0.0; + + zlights.directional.length = dirLength; + zlights.point.length = pointLength; + zlights.spot.length = spotLength; + zlights.hemi.length = hemiLength; + + zlights.ambient[ 0 ] = r; + zlights.ambient[ 1 ] = g; + zlights.ambient[ 2 ] = b; + + }; + + // GL state setting + + this.setFaceCulling = function ( cullFace, frontFaceDirection ) { + + if ( cullFace === THREE.CullFaceNone ) { + + _gl.disable( _gl.CULL_FACE ); + + } else { + + if ( frontFaceDirection === THREE.FrontFaceDirectionCW ) { + + _gl.frontFace( _gl.CW ); + + } else { + + _gl.frontFace( _gl.CCW ); + + } + + if ( cullFace === THREE.CullFaceBack ) { + + _gl.cullFace( _gl.BACK ); + + } else if ( cullFace === THREE.CullFaceFront ) { + + _gl.cullFace( _gl.FRONT ); + + } else { + + _gl.cullFace( _gl.FRONT_AND_BACK ); + + } + + _gl.enable( _gl.CULL_FACE ); + + } + + }; + + this.setMaterialFaces = function ( material ) { + + var doubleSided = material.side === THREE.DoubleSide; + var flipSided = material.side === THREE.BackSide; + + if ( _oldDoubleSided !== doubleSided ) { + + if ( doubleSided ) { + + _gl.disable( _gl.CULL_FACE ); + + } else { + + _gl.enable( _gl.CULL_FACE ); + + } + + _oldDoubleSided = doubleSided; + + } + + if ( _oldFlipSided !== flipSided ) { + + if ( flipSided ) { + + _gl.frontFace( _gl.CW ); + + } else { + + _gl.frontFace( _gl.CCW ); + + } + + _oldFlipSided = flipSided; + + } + + }; + + this.setDepthTest = function ( depthTest ) { + + if ( _oldDepthTest !== depthTest ) { + + if ( depthTest ) { + + _gl.enable( _gl.DEPTH_TEST ); + + } else { + + _gl.disable( _gl.DEPTH_TEST ); + + } + + _oldDepthTest = depthTest; + + } + + }; + + this.setDepthWrite = function ( depthWrite ) { + + if ( _oldDepthWrite !== depthWrite ) { + + _gl.depthMask( depthWrite ); + _oldDepthWrite = depthWrite; + + } + + }; + + function setLineWidth ( width ) { + + if ( width !== _oldLineWidth ) { + + _gl.lineWidth( width ); + + _oldLineWidth = width; + + } + + }; + + function setPolygonOffset ( polygonoffset, factor, units ) { + + if ( _oldPolygonOffset !== polygonoffset ) { + + if ( polygonoffset ) { + + _gl.enable( _gl.POLYGON_OFFSET_FILL ); + + } else { + + _gl.disable( _gl.POLYGON_OFFSET_FILL ); + + } + + _oldPolygonOffset = polygonoffset; + + } + + if ( polygonoffset && ( _oldPolygonOffsetFactor !== factor || _oldPolygonOffsetUnits !== units ) ) { + + _gl.polygonOffset( factor, units ); + + _oldPolygonOffsetFactor = factor; + _oldPolygonOffsetUnits = units; + + } + + }; + + this.setBlending = function ( blending, blendEquation, blendSrc, blendDst ) { + + if ( blending !== _oldBlending ) { + + if ( blending === THREE.NoBlending ) { + + _gl.disable( _gl.BLEND ); + + } else if ( blending === THREE.AdditiveBlending ) { + + _gl.enable( _gl.BLEND ); + _gl.blendEquation( _gl.FUNC_ADD ); + _gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE ); + + } else if ( blending === THREE.SubtractiveBlending ) { + + // TODO: Find blendFuncSeparate() combination + _gl.enable( _gl.BLEND ); + _gl.blendEquation( _gl.FUNC_ADD ); + _gl.blendFunc( _gl.ZERO, _gl.ONE_MINUS_SRC_COLOR ); + + } else if ( blending === THREE.MultiplyBlending ) { + + // TODO: Find blendFuncSeparate() combination + _gl.enable( _gl.BLEND ); + _gl.blendEquation( _gl.FUNC_ADD ); + _gl.blendFunc( _gl.ZERO, _gl.SRC_COLOR ); + + } else if ( blending === THREE.CustomBlending ) { + + _gl.enable( _gl.BLEND ); + + } else { + + _gl.enable( _gl.BLEND ); + _gl.blendEquationSeparate( _gl.FUNC_ADD, _gl.FUNC_ADD ); + _gl.blendFuncSeparate( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA, _gl.ONE, _gl.ONE_MINUS_SRC_ALPHA ); + + } + + _oldBlending = blending; + + } + + if ( blending === THREE.CustomBlending ) { + + if ( blendEquation !== _oldBlendEquation ) { + + _gl.blendEquation( paramThreeToGL( blendEquation ) ); + + _oldBlendEquation = blendEquation; + + } + + if ( blendSrc !== _oldBlendSrc || blendDst !== _oldBlendDst ) { + + _gl.blendFunc( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ) ); + + _oldBlendSrc = blendSrc; + _oldBlendDst = blendDst; + + } + + } else { + + _oldBlendEquation = null; + _oldBlendSrc = null; + _oldBlendDst = null; + + } + + }; + + // Textures + + function setTextureParameters ( textureType, texture, isImagePowerOfTwo ) { + + if ( isImagePowerOfTwo ) { + + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, paramThreeToGL( texture.wrapS ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, paramThreeToGL( texture.wrapT ) ); + + _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, paramThreeToGL( texture.magFilter ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, paramThreeToGL( texture.minFilter ) ); + + } else { + + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); + + _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) ); + + } + + if ( _glExtensionTextureFilterAnisotropic && texture.type !== THREE.FloatType ) { + + if ( texture.anisotropy > 1 || texture.__oldAnisotropy ) { + + _gl.texParameterf( textureType, _glExtensionTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, _maxAnisotropy ) ); + texture.__oldAnisotropy = texture.anisotropy; + + } + + } + + }; + + this.setTexture = function ( texture, slot ) { + + if ( texture.needsUpdate ) { + + if ( ! texture.__webglInit ) { + + texture.__webglInit = true; + + texture.addEventListener( 'dispose', onTextureDispose ); + + texture.__webglTexture = _gl.createTexture(); + + _this.info.memory.textures ++; + + } + + _gl.activeTexture( _gl.TEXTURE0 + slot ); + _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); + + _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); + _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ); + _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment ); + + var image = texture.image, + isImagePowerOfTwo = THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ), + glFormat = paramThreeToGL( texture.format ), + glType = paramThreeToGL( texture.type ); + + setTextureParameters( _gl.TEXTURE_2D, texture, isImagePowerOfTwo ); + + var mipmap, mipmaps = texture.mipmaps; + + if ( texture instanceof THREE.DataTexture ) { + + // use manually created mipmaps if available + // if there are no manual mipmaps + // set 0 level mipmap and then use GL to generate other mipmap levels + + if ( mipmaps.length > 0 && isImagePowerOfTwo ) { + + for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + + mipmap = mipmaps[ i ]; + _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + + } + + texture.generateMipmaps = false; + + } else { + + _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data ); + + } + + } else if ( texture instanceof THREE.CompressedTexture ) { + + for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + + mipmap = mipmaps[ i ]; + if ( texture.format !== THREE.RGBAFormat ) { + _gl.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + } else { + _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + } + + } + + } else { // regular Texture (image, video, canvas) + + // use manually created mipmaps if available + // if there are no manual mipmaps + // set 0 level mipmap and then use GL to generate other mipmap levels + + if ( mipmaps.length > 0 && isImagePowerOfTwo ) { + + for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + + mipmap = mipmaps[ i ]; + _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap ); + + } + + texture.generateMipmaps = false; + + } else { + + _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, texture.image ); + + } + + } + + if ( texture.generateMipmaps && isImagePowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); + + texture.needsUpdate = false; + + if ( texture.onUpdate ) texture.onUpdate(); + + } else { + + _gl.activeTexture( _gl.TEXTURE0 + slot ); + _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); + + } + + }; + + function clampToMaxSize ( image, maxSize ) { + + if ( image.width <= maxSize && image.height <= maxSize ) { + + return image; + + } + + // Warning: Scaling through the canvas will only work with images that use + // premultiplied alpha. + + var maxDimension = Math.max( image.width, image.height ); + var newWidth = Math.floor( image.width * maxSize / maxDimension ); + var newHeight = Math.floor( image.height * maxSize / maxDimension ); + + var canvas = document.createElement( 'canvas' ); + canvas.width = newWidth; + canvas.height = newHeight; + + var ctx = canvas.getContext( '2d' ); + ctx.drawImage( image, 0, 0, image.width, image.height, 0, 0, newWidth, newHeight ); + + return canvas; + + } + + function setCubeTexture ( texture, slot ) { + + if ( texture.image.length === 6 ) { + + if ( texture.needsUpdate ) { + + if ( ! texture.image.__webglTextureCube ) { + + texture.addEventListener( 'dispose', onTextureDispose ); + + texture.image.__webglTextureCube = _gl.createTexture(); + + _this.info.memory.textures ++; + + } + + _gl.activeTexture( _gl.TEXTURE0 + slot ); + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube ); + + _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); + + var isCompressed = texture instanceof THREE.CompressedTexture; + + var cubeImage = []; + + for ( var i = 0; i < 6; i ++ ) { + + if ( _this.autoScaleCubemaps && ! isCompressed ) { + + cubeImage[ i ] = clampToMaxSize( texture.image[ i ], _maxCubemapSize ); + + } else { + + cubeImage[ i ] = texture.image[ i ]; + + } + + } + + var image = cubeImage[ 0 ], + isImagePowerOfTwo = THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ), + glFormat = paramThreeToGL( texture.format ), + glType = paramThreeToGL( texture.type ); + + setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isImagePowerOfTwo ); + + for ( var i = 0; i < 6; i ++ ) { + + if ( ! isCompressed ) { + + _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] ); + + } else { + + var mipmap, mipmaps = cubeImage[ i ].mipmaps; + + for ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) { + + mipmap = mipmaps[ j ]; + if ( texture.format !== THREE.RGBAFormat ) { + + _gl.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + + } else { + _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + } + + } + } + } + + if ( texture.generateMipmaps && isImagePowerOfTwo ) { + + _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + + } + + texture.needsUpdate = false; + + if ( texture.onUpdate ) texture.onUpdate(); + + } else { + + _gl.activeTexture( _gl.TEXTURE0 + slot ); + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube ); + + } + + } + + }; + + function setCubeTextureDynamic ( texture, slot ) { + + _gl.activeTexture( _gl.TEXTURE0 + slot ); + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.__webglTexture ); + + }; + + // Render targets + + function setupFrameBuffer ( framebuffer, renderTarget, textureTarget ) { + + _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureTarget, renderTarget.__webglTexture, 0 ); + + }; + + function setupRenderBuffer ( renderbuffer, renderTarget ) { + + _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); + + if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + + /* For some reason this is not working. Defaulting to RGBA4. + } else if ( ! renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.STENCIL_INDEX8, renderTarget.width, renderTarget.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + */ + } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + + } else { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height ); + + } + + }; + + this.setRenderTarget = function ( renderTarget ) { + + var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube ); + + if ( renderTarget && ! renderTarget.__webglFramebuffer ) { + + if ( renderTarget.depthBuffer === undefined ) renderTarget.depthBuffer = true; + if ( renderTarget.stencilBuffer === undefined ) renderTarget.stencilBuffer = true; + + renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); + + renderTarget.__webglTexture = _gl.createTexture(); + + _this.info.memory.textures ++; + + // Setup texture, create render and frame buffers + + var isTargetPowerOfTwo = THREE.Math.isPowerOfTwo( renderTarget.width ) && THREE.Math.isPowerOfTwo( renderTarget.height ), + glFormat = paramThreeToGL( renderTarget.format ), + glType = paramThreeToGL( renderTarget.type ); + + if ( isCube ) { + + renderTarget.__webglFramebuffer = []; + renderTarget.__webglRenderbuffer = []; + + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture ); + setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget, isTargetPowerOfTwo ); + + for ( var i = 0; i < 6; i ++ ) { + + renderTarget.__webglFramebuffer[ i ] = _gl.createFramebuffer(); + renderTarget.__webglRenderbuffer[ i ] = _gl.createRenderbuffer(); + + _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); + + setupFrameBuffer( renderTarget.__webglFramebuffer[ i ], renderTarget, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i ); + setupRenderBuffer( renderTarget.__webglRenderbuffer[ i ], renderTarget ); + + } + + if ( isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + + } else { + + renderTarget.__webglFramebuffer = _gl.createFramebuffer(); + + if ( renderTarget.shareDepthFrom ) { + + renderTarget.__webglRenderbuffer = renderTarget.shareDepthFrom.__webglRenderbuffer; + + } else { + + renderTarget.__webglRenderbuffer = _gl.createRenderbuffer(); + + } + + _gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture ); + setTextureParameters( _gl.TEXTURE_2D, renderTarget, isTargetPowerOfTwo ); + + _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); + + setupFrameBuffer( renderTarget.__webglFramebuffer, renderTarget, _gl.TEXTURE_2D ); + + if ( renderTarget.shareDepthFrom ) { + + if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { + + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderTarget.__webglRenderbuffer ); + + } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderTarget.__webglRenderbuffer ); + + } + + } else { + + setupRenderBuffer( renderTarget.__webglRenderbuffer, renderTarget ); + + } + + if ( isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); + + } + + // Release everything + + if ( isCube ) { + + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); + + } else { + + _gl.bindTexture( _gl.TEXTURE_2D, null ); + + } + + _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); + _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); + + } + + var framebuffer, width, height, vx, vy; + + if ( renderTarget ) { + + if ( isCube ) { + + framebuffer = renderTarget.__webglFramebuffer[ renderTarget.activeCubeFace ]; + + } else { + + framebuffer = renderTarget.__webglFramebuffer; + + } + + width = renderTarget.width; + height = renderTarget.height; + + vx = 0; + vy = 0; + + } else { + + framebuffer = null; + + width = _viewportWidth; + height = _viewportHeight; + + vx = _viewportX; + vy = _viewportY; + + } + + if ( framebuffer !== _currentFramebuffer ) { + + _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + _gl.viewport( vx, vy, width, height ); + + _currentFramebuffer = framebuffer; + + } + + _currentWidth = width; + _currentHeight = height; + + }; + + function updateRenderTargetMipmap ( renderTarget ) { + + if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) { + + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture ); + _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); + + } else { + + _gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture ); + _gl.generateMipmap( _gl.TEXTURE_2D ); + _gl.bindTexture( _gl.TEXTURE_2D, null ); + + } + + }; + + // Fallback filters for non-power-of-2 textures + + function filterFallback ( f ) { + + if ( f === THREE.NearestFilter || f === THREE.NearestMipMapNearestFilter || f === THREE.NearestMipMapLinearFilter ) { + + return _gl.NEAREST; + + } + + return _gl.LINEAR; + + }; + + // Map three.js constants to WebGL constants + + function paramThreeToGL ( p ) { + + if ( p === THREE.RepeatWrapping ) return _gl.REPEAT; + if ( p === THREE.ClampToEdgeWrapping ) return _gl.CLAMP_TO_EDGE; + if ( p === THREE.MirroredRepeatWrapping ) return _gl.MIRRORED_REPEAT; + + if ( p === THREE.NearestFilter ) return _gl.NEAREST; + if ( p === THREE.NearestMipMapNearestFilter ) return _gl.NEAREST_MIPMAP_NEAREST; + if ( p === THREE.NearestMipMapLinearFilter ) return _gl.NEAREST_MIPMAP_LINEAR; + + if ( p === THREE.LinearFilter ) return _gl.LINEAR; + if ( p === THREE.LinearMipMapNearestFilter ) return _gl.LINEAR_MIPMAP_NEAREST; + if ( p === THREE.LinearMipMapLinearFilter ) return _gl.LINEAR_MIPMAP_LINEAR; + + if ( p === THREE.UnsignedByteType ) return _gl.UNSIGNED_BYTE; + if ( p === THREE.UnsignedShort4444Type ) return _gl.UNSIGNED_SHORT_4_4_4_4; + if ( p === THREE.UnsignedShort5551Type ) return _gl.UNSIGNED_SHORT_5_5_5_1; + if ( p === THREE.UnsignedShort565Type ) return _gl.UNSIGNED_SHORT_5_6_5; + + if ( p === THREE.ByteType ) return _gl.BYTE; + if ( p === THREE.ShortType ) return _gl.SHORT; + if ( p === THREE.UnsignedShortType ) return _gl.UNSIGNED_SHORT; + if ( p === THREE.IntType ) return _gl.INT; + if ( p === THREE.UnsignedIntType ) return _gl.UNSIGNED_INT; + if ( p === THREE.FloatType ) return _gl.FLOAT; + + if ( p === THREE.AlphaFormat ) return _gl.ALPHA; + if ( p === THREE.RGBFormat ) return _gl.RGB; + if ( p === THREE.RGBAFormat ) return _gl.RGBA; + if ( p === THREE.LuminanceFormat ) return _gl.LUMINANCE; + if ( p === THREE.LuminanceAlphaFormat ) return _gl.LUMINANCE_ALPHA; + + if ( p === THREE.AddEquation ) return _gl.FUNC_ADD; + if ( p === THREE.SubtractEquation ) return _gl.FUNC_SUBTRACT; + if ( p === THREE.ReverseSubtractEquation ) return _gl.FUNC_REVERSE_SUBTRACT; + + if ( p === THREE.ZeroFactor ) return _gl.ZERO; + if ( p === THREE.OneFactor ) return _gl.ONE; + if ( p === THREE.SrcColorFactor ) return _gl.SRC_COLOR; + if ( p === THREE.OneMinusSrcColorFactor ) return _gl.ONE_MINUS_SRC_COLOR; + if ( p === THREE.SrcAlphaFactor ) return _gl.SRC_ALPHA; + if ( p === THREE.OneMinusSrcAlphaFactor ) return _gl.ONE_MINUS_SRC_ALPHA; + if ( p === THREE.DstAlphaFactor ) return _gl.DST_ALPHA; + if ( p === THREE.OneMinusDstAlphaFactor ) return _gl.ONE_MINUS_DST_ALPHA; + + if ( p === THREE.DstColorFactor ) return _gl.DST_COLOR; + if ( p === THREE.OneMinusDstColorFactor ) return _gl.ONE_MINUS_DST_COLOR; + if ( p === THREE.SrcAlphaSaturateFactor ) return _gl.SRC_ALPHA_SATURATE; + + if ( _glExtensionCompressedTextureS3TC !== undefined ) { + + if ( p === THREE.RGB_S3TC_DXT1_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGB_S3TC_DXT1_EXT; + if ( p === THREE.RGBA_S3TC_DXT1_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT1_EXT; + if ( p === THREE.RGBA_S3TC_DXT3_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT3_EXT; + if ( p === THREE.RGBA_S3TC_DXT5_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT5_EXT; + + } + + return 0; + + }; + + // Allocations + + function allocateBones ( object ) { + + if ( _supportsBoneTextures && object && object.skeleton && object.skeleton.useVertexTexture ) { + + return 1024; + + } else { + + // default for when object is not specified + // ( for example when prebuilding shader + // to be used with multiple objects ) + // + // - leave some extra space for other uniforms + // - limit here is ANGLE's 254 max uniform vectors + // (up to 54 should be safe) + + var nVertexUniforms = _gl.getParameter( _gl.MAX_VERTEX_UNIFORM_VECTORS ); + var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); + + var maxBones = nVertexMatrices; + + if ( object !== undefined && object instanceof THREE.SkinnedMesh ) { + + maxBones = Math.min( object.skeleton.bones.length, maxBones ); + + if ( maxBones < object.skeleton.bones.length ) { + + console.warn( 'WebGLRenderer: too many bones - ' + object.skeleton.bones.length + ', this GPU supports just ' + maxBones + ' (try OpenGL instead of ANGLE)' ); + + } + + } + + return maxBones; + + } + + }; + + function allocateLights( lights ) { + + var dirLights = 0; + var pointLights = 0; + var spotLights = 0; + var hemiLights = 0; + + for ( var l = 0, ll = lights.length; l < ll; l ++ ) { + + var light = lights[ l ]; + + if ( light.onlyShadow || light.visible === false ) continue; + + if ( light instanceof THREE.DirectionalLight ) dirLights ++; + if ( light instanceof THREE.PointLight ) pointLights ++; + if ( light instanceof THREE.SpotLight ) spotLights ++; + if ( light instanceof THREE.HemisphereLight ) hemiLights ++; + + } + + return { 'directional': dirLights, 'point': pointLights, 'spot': spotLights, 'hemi': hemiLights }; + + }; + + function allocateShadows( lights ) { + + var maxShadows = 0; + + for ( var l = 0, ll = lights.length; l < ll; l ++ ) { + + var light = lights[ l ]; + + if ( ! light.castShadow ) continue; + + if ( light instanceof THREE.SpotLight ) maxShadows ++; + if ( light instanceof THREE.DirectionalLight && ! light.shadowCascade ) maxShadows ++; + + } + + return maxShadows; + + }; + + // Initialization + + function initGL() { + + try { + + var attributes = { + alpha: _alpha, + depth: _depth, + stencil: _stencil, + antialias: _antialias, + premultipliedAlpha: _premultipliedAlpha, + preserveDrawingBuffer: _preserveDrawingBuffer + }; + + _gl = _context || _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes ); + + if ( _gl === null ) { + + throw 'Error creating WebGL context.'; + + } + + } catch ( error ) { + + console.error( error ); + + } + + _glExtensionTextureFloat = _gl.getExtension( 'OES_texture_float' ); + _glExtensionTextureFloatLinear = _gl.getExtension( 'OES_texture_float_linear' ); + _glExtensionStandardDerivatives = _gl.getExtension( 'OES_standard_derivatives' ); + + _glExtensionTextureFilterAnisotropic = _gl.getExtension( 'EXT_texture_filter_anisotropic' ) || _gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || _gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); + + _glExtensionCompressedTextureS3TC = _gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || _gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || _gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); + + _glExtensionElementIndexUint = _gl.getExtension( 'OES_element_index_uint' ); + + + if ( _glExtensionTextureFloat === null ) { + + console.log( 'THREE.WebGLRenderer: Float textures not supported.' ); + + } + + if ( _glExtensionStandardDerivatives === null ) { + + console.log( 'THREE.WebGLRenderer: Standard derivatives not supported.' ); + + } + + if ( _glExtensionTextureFilterAnisotropic === null ) { + + console.log( 'THREE.WebGLRenderer: Anisotropic texture filtering not supported.' ); + + } + + if ( _glExtensionCompressedTextureS3TC === null ) { + + console.log( 'THREE.WebGLRenderer: S3TC compressed textures not supported.' ); + + } + + if ( _glExtensionElementIndexUint === null ) { + + console.log( 'THREE.WebGLRenderer: elementindex as unsigned integer not supported.' ); + + } + + if ( _gl.getShaderPrecisionFormat === undefined ) { + + _gl.getShaderPrecisionFormat = function () { + + return { + 'rangeMin': 1, + 'rangeMax': 1, + 'precision': 1 + }; + + } + } + + if ( _logarithmicDepthBuffer ) { + + _glExtensionFragDepth = _gl.getExtension( 'EXT_frag_depth' ); + + } + + }; + + function setDefaultGLState () { + + _gl.clearColor( 0, 0, 0, 1 ); + _gl.clearDepth( 1 ); + _gl.clearStencil( 0 ); + + _gl.enable( _gl.DEPTH_TEST ); + _gl.depthFunc( _gl.LEQUAL ); + + _gl.frontFace( _gl.CCW ); + _gl.cullFace( _gl.BACK ); + _gl.enable( _gl.CULL_FACE ); + + _gl.enable( _gl.BLEND ); + _gl.blendEquation( _gl.FUNC_ADD ); + _gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA ); + + _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); + + _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); + + }; + + // default plugins (order is important) + + this.shadowMapPlugin = new THREE.ShadowMapPlugin(); + this.addPrePlugin( this.shadowMapPlugin ); + + this.addPostPlugin( new THREE.SpritePlugin() ); + this.addPostPlugin( new THREE.LensFlarePlugin() ); + +}; + +// File:src/renderers/WebGLRenderTarget.js + +/** + * @author szimek / https://github.com/szimek/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.WebGLRenderTarget = function ( width, height, options ) { + + this.width = width; + this.height = height; + + options = options || {}; + + this.wrapS = options.wrapS !== undefined ? options.wrapS : THREE.ClampToEdgeWrapping; + this.wrapT = options.wrapT !== undefined ? options.wrapT : THREE.ClampToEdgeWrapping; + + this.magFilter = options.magFilter !== undefined ? options.magFilter : THREE.LinearFilter; + this.minFilter = options.minFilter !== undefined ? options.minFilter : THREE.LinearMipMapLinearFilter; + + this.anisotropy = options.anisotropy !== undefined ? options.anisotropy : 1; + + this.offset = new THREE.Vector2( 0, 0 ); + this.repeat = new THREE.Vector2( 1, 1 ); + + this.format = options.format !== undefined ? options.format : THREE.RGBAFormat; + this.type = options.type !== undefined ? options.type : THREE.UnsignedByteType; + + this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; + this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true; + + this.generateMipmaps = true; + + this.shareDepthFrom = null; + +}; + +THREE.WebGLRenderTarget.prototype = { + + constructor: THREE.WebGLRenderTarget, + + setSize: function ( width, height ) { + + this.width = width; + this.height = height; + + }, + + clone: function () { + + var tmp = new THREE.WebGLRenderTarget( this.width, this.height ); + + tmp.wrapS = this.wrapS; + tmp.wrapT = this.wrapT; + + tmp.magFilter = this.magFilter; + tmp.minFilter = this.minFilter; + + tmp.anisotropy = this.anisotropy; + + tmp.offset.copy( this.offset ); + tmp.repeat.copy( this.repeat ); + + tmp.format = this.format; + tmp.type = this.type; + + tmp.depthBuffer = this.depthBuffer; + tmp.stencilBuffer = this.stencilBuffer; + + tmp.generateMipmaps = this.generateMipmaps; + + tmp.shareDepthFrom = this.shareDepthFrom; + + return tmp; + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.WebGLRenderTarget.prototype ); + +// File:src/renderers/WebGLRenderTargetCube.js + +/** + * @author alteredq / http://alteredqualia.com + */ + +THREE.WebGLRenderTargetCube = function ( width, height, options ) { + + THREE.WebGLRenderTarget.call( this, width, height, options ); + + this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5 + +}; + +THREE.WebGLRenderTargetCube.prototype = Object.create( THREE.WebGLRenderTarget.prototype ); + +// File:src/renderers/webgl/WebGLProgram.js + +THREE.WebGLProgram = ( function () { + + var programIdCount = 0; + + var generateDefines = function ( defines ) { + + var value, chunk, chunks = []; + + for ( var d in defines ) { + + value = defines[ d ]; + if ( value === false ) continue; + + chunk = "#define " + d + " " + value; + chunks.push( chunk ); + + } + + return chunks.join( "\n" ); + + }; + + var cacheUniformLocations = function ( gl, program, identifiers ) { + + var uniforms = {}; + + for ( var i = 0, l = identifiers.length; i < l; i ++ ) { + + var id = identifiers[ i ]; + uniforms[ id ] = gl.getUniformLocation( program, id ); + + } + + return uniforms; + + }; + + var cacheAttributeLocations = function ( gl, program, identifiers ) { + + var attributes = {}; + + for ( var i = 0, l = identifiers.length; i < l; i ++ ) { + + var id = identifiers[ i ]; + attributes[ id ] = gl.getAttribLocation( program, id ); + + } + + return attributes; + + }; + + return function ( renderer, code, material, parameters ) { + + var _this = renderer; + var _gl = _this.context; + + var defines = material.defines; + var uniforms = material.__webglShader.uniforms; + var attributes = material.attributes; + + var vertexShader = material.__webglShader.vertexShader; + var fragmentShader = material.__webglShader.fragmentShader; + + var index0AttributeName = material.index0AttributeName; + + if ( index0AttributeName === undefined && parameters.morphTargets === true ) { + + // programs with morphTargets displace position out of attribute 0 + + index0AttributeName = 'position'; + + } + + var shadowMapTypeDefine = "SHADOWMAP_TYPE_BASIC"; + + if ( parameters.shadowMapType === THREE.PCFShadowMap ) { + + shadowMapTypeDefine = "SHADOWMAP_TYPE_PCF"; + + } else if ( parameters.shadowMapType === THREE.PCFSoftShadowMap ) { + + shadowMapTypeDefine = "SHADOWMAP_TYPE_PCF_SOFT"; + + } + + // console.log( "building new program " ); + + // + + var customDefines = generateDefines( defines ); + + // + + var program = _gl.createProgram(); + + var prefix_vertex, prefix_fragment; + + if ( material instanceof THREE.RawShaderMaterial ) { + + prefix_vertex = ''; + prefix_fragment = ''; + + } else { + + prefix_vertex = [ + + "precision " + parameters.precision + " float;", + "precision " + parameters.precision + " int;", + + customDefines, + + parameters.supportsVertexTextures ? "#define VERTEX_TEXTURES" : "", + + _this.gammaInput ? "#define GAMMA_INPUT" : "", + _this.gammaOutput ? "#define GAMMA_OUTPUT" : "", + + "#define MAX_DIR_LIGHTS " + parameters.maxDirLights, + "#define MAX_POINT_LIGHTS " + parameters.maxPointLights, + "#define MAX_SPOT_LIGHTS " + parameters.maxSpotLights, + "#define MAX_HEMI_LIGHTS " + parameters.maxHemiLights, + + "#define MAX_SHADOWS " + parameters.maxShadows, + + "#define MAX_BONES " + parameters.maxBones, + + parameters.map ? "#define USE_MAP" : "", + parameters.envMap ? "#define USE_ENVMAP" : "", + parameters.lightMap ? "#define USE_LIGHTMAP" : "", + parameters.bumpMap ? "#define USE_BUMPMAP" : "", + parameters.normalMap ? "#define USE_NORMALMAP" : "", + parameters.specularMap ? "#define USE_SPECULARMAP" : "", + parameters.alphaMap ? "#define USE_ALPHAMAP" : "", + parameters.vertexColors ? "#define USE_COLOR" : "", + + parameters.skinning ? "#define USE_SKINNING" : "", + parameters.useVertexTexture ? "#define BONE_TEXTURE" : "", + + parameters.morphTargets ? "#define USE_MORPHTARGETS" : "", + parameters.morphNormals ? "#define USE_MORPHNORMALS" : "", + parameters.wrapAround ? "#define WRAP_AROUND" : "", + parameters.doubleSided ? "#define DOUBLE_SIDED" : "", + parameters.flipSided ? "#define FLIP_SIDED" : "", + + parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", + parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", + parameters.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "", + parameters.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "", + + parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "", + + parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", + //_this._glExtensionFragDepth ? "#define USE_LOGDEPTHBUF_EXT" : "", + + + "uniform mat4 modelMatrix;", + "uniform mat4 modelViewMatrix;", + "uniform mat4 projectionMatrix;", + "uniform mat4 viewMatrix;", + "uniform mat3 normalMatrix;", + "uniform vec3 cameraPosition;", + + "attribute vec3 position;", + "attribute vec3 normal;", + "attribute vec2 uv;", + "attribute vec2 uv2;", + + "#ifdef USE_COLOR", + + " attribute vec3 color;", + + "#endif", + + "#ifdef USE_MORPHTARGETS", + + " attribute vec3 morphTarget0;", + " attribute vec3 morphTarget1;", + " attribute vec3 morphTarget2;", + " attribute vec3 morphTarget3;", + + " #ifdef USE_MORPHNORMALS", + + " attribute vec3 morphNormal0;", + " attribute vec3 morphNormal1;", + " attribute vec3 morphNormal2;", + " attribute vec3 morphNormal3;", + + " #else", + + " attribute vec3 morphTarget4;", + " attribute vec3 morphTarget5;", + " attribute vec3 morphTarget6;", + " attribute vec3 morphTarget7;", + + " #endif", + + "#endif", + + "#ifdef USE_SKINNING", + + " attribute vec4 skinIndex;", + " attribute vec4 skinWeight;", + + "#endif", + + "" + + ].join( '\n' ); + + prefix_fragment = [ + + "precision " + parameters.precision + " float;", + "precision " + parameters.precision + " int;", + + ( parameters.bumpMap || parameters.normalMap ) ? "#extension GL_OES_standard_derivatives : enable" : "", + + customDefines, + + "#define MAX_DIR_LIGHTS " + parameters.maxDirLights, + "#define MAX_POINT_LIGHTS " + parameters.maxPointLights, + "#define MAX_SPOT_LIGHTS " + parameters.maxSpotLights, + "#define MAX_HEMI_LIGHTS " + parameters.maxHemiLights, + + "#define MAX_SHADOWS " + parameters.maxShadows, + + parameters.alphaTest ? "#define ALPHATEST " + parameters.alphaTest: "", + + _this.gammaInput ? "#define GAMMA_INPUT" : "", + _this.gammaOutput ? "#define GAMMA_OUTPUT" : "", + + ( parameters.useFog && parameters.fog ) ? "#define USE_FOG" : "", + ( parameters.useFog && parameters.fogExp ) ? "#define FOG_EXP2" : "", + + parameters.map ? "#define USE_MAP" : "", + parameters.envMap ? "#define USE_ENVMAP" : "", + parameters.lightMap ? "#define USE_LIGHTMAP" : "", + parameters.bumpMap ? "#define USE_BUMPMAP" : "", + parameters.normalMap ? "#define USE_NORMALMAP" : "", + parameters.specularMap ? "#define USE_SPECULARMAP" : "", + parameters.alphaMap ? "#define USE_ALPHAMAP" : "", + parameters.vertexColors ? "#define USE_COLOR" : "", + + parameters.metal ? "#define METAL" : "", + parameters.wrapAround ? "#define WRAP_AROUND" : "", + parameters.doubleSided ? "#define DOUBLE_SIDED" : "", + parameters.flipSided ? "#define FLIP_SIDED" : "", + + parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", + parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", + parameters.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "", + parameters.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "", + + parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", + //_this._glExtensionFragDepth ? "#define USE_LOGDEPTHBUF_EXT" : "", + + "uniform mat4 viewMatrix;", + "uniform vec3 cameraPosition;", + "" + + ].join( '\n' ); + + } + + var glVertexShader = new THREE.WebGLShader( _gl, _gl.VERTEX_SHADER, prefix_vertex + vertexShader ); + var glFragmentShader = new THREE.WebGLShader( _gl, _gl.FRAGMENT_SHADER, prefix_fragment + fragmentShader ); + + _gl.attachShader( program, glVertexShader ); + _gl.attachShader( program, glFragmentShader ); + + if ( index0AttributeName !== undefined ) { + + // Force a particular attribute to index 0. + // because potentially expensive emulation is done by browser if attribute 0 is disabled. + // And, color, for example is often automatically bound to index 0 so disabling it + + _gl.bindAttribLocation( program, 0, index0AttributeName ); + + } + + _gl.linkProgram( program ); + + if ( _gl.getProgramParameter( program, _gl.LINK_STATUS ) === false ) { + + console.error( 'THREE.WebGLProgram: Could not initialise shader.' ); + console.error( 'gl.VALIDATE_STATUS', _gl.getProgramParameter( program, _gl.VALIDATE_STATUS ) ); + console.error( 'gl.getError()', _gl.getError() ); + + } + + if ( _gl.getProgramInfoLog( program ) !== '' ) { + + console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', _gl.getProgramInfoLog( program ) ); + + } + + // clean up + + _gl.deleteShader( glVertexShader ); + _gl.deleteShader( glFragmentShader ); + + // cache uniform locations + + var identifiers = [ + + 'viewMatrix', 'modelViewMatrix', 'projectionMatrix', 'normalMatrix', 'modelMatrix', 'cameraPosition', 'morphTargetInfluences', 'bindMatrix', 'bindMatrixInverse' + + ]; + + if ( parameters.useVertexTexture ) { + + identifiers.push( 'boneTexture' ); + identifiers.push( 'boneTextureWidth' ); + identifiers.push( 'boneTextureHeight' ); + + } else { + + identifiers.push( 'boneGlobalMatrices' ); + + } + + if ( parameters.logarithmicDepthBuffer ) { + + identifiers.push('logDepthBufFC'); + + } + + + for ( var u in uniforms ) { + + identifiers.push( u ); + + } + + this.uniforms = cacheUniformLocations( _gl, program, identifiers ); + + // cache attributes locations + + identifiers = [ + + "position", "normal", "uv", "uv2", "tangent", "color", + "skinIndex", "skinWeight", "lineDistance" + + ]; + + for ( var i = 0; i < parameters.maxMorphTargets; i ++ ) { + + identifiers.push( "morphTarget" + i ); + + } + + for ( var i = 0; i < parameters.maxMorphNormals; i ++ ) { + + identifiers.push( "morphNormal" + i ); + + } + + for ( var a in attributes ) { + + identifiers.push( a ); + + } + + this.attributes = cacheAttributeLocations( _gl, program, identifiers ); + + // + + this.id = programIdCount ++; + this.code = code; + this.usedTimes = 1; + this.program = program; + this.vertexShader = glVertexShader; + this.fragmentShader = glFragmentShader; + + return this; + + }; + +} )(); + +// File:src/renderers/webgl/WebGLShader.js + +THREE.WebGLShader = ( function () { + + var addLineNumbers = function ( string ) { + + var lines = string.split( '\n' ); + + for ( var i = 0; i < lines.length; i ++ ) { + + lines[ i ] = ( i + 1 ) + ': ' + lines[ i ]; + + } + + return lines.join( '\n' ); + + }; + + return function ( gl, type, string ) { + + var shader = gl.createShader( type ); + + gl.shaderSource( shader, string ); + gl.compileShader( shader ); + + if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) { + + console.error( 'THREE.WebGLShader: Shader couldn\'t compile.' ); + + } + + if ( gl.getShaderInfoLog( shader ) !== '' ) { + + console.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', gl.getShaderInfoLog( shader ) ); + console.warn( addLineNumbers( string ) ); + + } + + // --enable-privileged-webgl-extension + // console.log( type, gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); + + return shader; + + }; + +} )(); + +// File:src/renderers/renderables/RenderableVertex.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.RenderableVertex = function () { + + this.position = new THREE.Vector3(); + this.positionWorld = new THREE.Vector3(); + this.positionScreen = new THREE.Vector4(); + + this.visible = true; + +}; + +THREE.RenderableVertex.prototype.copy = function ( vertex ) { + + this.positionWorld.copy( vertex.positionWorld ); + this.positionScreen.copy( vertex.positionScreen ); + +}; + +// File:src/renderers/renderables/RenderableFace.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.RenderableFace = function () { + + this.id = 0; + + this.v1 = new THREE.RenderableVertex(); + this.v2 = new THREE.RenderableVertex(); + this.v3 = new THREE.RenderableVertex(); + + this.normalModel = new THREE.Vector3(); + + this.vertexNormalsModel = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ]; + this.vertexNormalsLength = 0; + + this.color = new THREE.Color(); + this.material = null; + this.uvs = [ new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() ]; + + this.z = 0; + +}; + +// File:src/renderers/renderables/RenderableObject.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.RenderableObject = function () { + + this.id = 0; + + this.object = null; + this.z = 0; + +}; + +// File:src/renderers/renderables/RenderableSprite.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.RenderableSprite = function () { + + this.id = 0; + + this.object = null; + + this.x = 0; + this.y = 0; + this.z = 0; + + this.rotation = 0; + this.scale = new THREE.Vector2(); + + this.material = null; + +}; + +// File:src/renderers/renderables/RenderableLine.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.RenderableLine = function () { + + this.id = 0; + + this.v1 = new THREE.RenderableVertex(); + this.v2 = new THREE.RenderableVertex(); + + this.vertexColors = [ new THREE.Color(), new THREE.Color() ]; + this.material = null; + + this.z = 0; + +}; + +// File:src/extras/GeometryUtils.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.GeometryUtils = { + + merge: function ( geometry1, geometry2, materialIndexOffset ) { + + console.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' ); + + var matrix; + + if ( geometry2 instanceof THREE.Mesh ) { + + geometry2.matrixAutoUpdate && geometry2.updateMatrix(); + + matrix = geometry2.matrix; + geometry2 = geometry2.geometry; + + } + + geometry1.merge( geometry2, matrix, materialIndexOffset ); + + }, + + center: function ( geometry ) { + + console.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' ); + return geometry.center(); + + } + +}; + +// File:src/extras/ImageUtils.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author Daosheng Mu / https://github.com/DaoshengMu/ + */ + +THREE.ImageUtils = { + + crossOrigin: undefined, + + loadTexture: function ( url, mapping, onLoad, onError ) { + + var loader = new THREE.ImageLoader(); + loader.crossOrigin = this.crossOrigin; + + var texture = new THREE.Texture( undefined, mapping ); + + loader.load( url, function ( image ) { + + texture.image = image; + texture.needsUpdate = true; + + if ( onLoad ) onLoad( texture ); + + }, undefined, function ( event ) { + + if ( onError ) onError( event ); + + } ); + + texture.sourceFile = url; + + return texture; + + }, + + loadTextureCube: function ( array, mapping, onLoad, onError ) { + + var images = []; + + var loader = new THREE.ImageLoader(); + loader.crossOrigin = this.crossOrigin; + + var texture = new THREE.CubeTexture( images, mapping ); + + // no flipping needed for cube textures + + texture.flipY = false; + + var loaded = 0; + + var loadTexture = function ( i ) { + + loader.load( array[ i ], function ( image ) { + + texture.images[ i ] = image; + + loaded += 1; + + if ( loaded === 6 ) { + + texture.needsUpdate = true; + + if ( onLoad ) onLoad( texture ); + + } + + } ); + + } + + for ( var i = 0, il = array.length; i < il; ++ i ) { + + loadTexture( i ); + + } + + return texture; + + }, + + loadCompressedTexture: function () { + + console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' ) + + }, + + loadCompressedTextureCube: function () { + + console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' ) + + }, + + getNormalMap: function ( image, depth ) { + + // Adapted from http://www.paulbrunt.co.uk/lab/heightnormal/ + + var cross = function ( a, b ) { + + return [ a[ 1 ] * b[ 2 ] - a[ 2 ] * b[ 1 ], a[ 2 ] * b[ 0 ] - a[ 0 ] * b[ 2 ], a[ 0 ] * b[ 1 ] - a[ 1 ] * b[ 0 ] ]; + + } + + var subtract = function ( a, b ) { + + return [ a[ 0 ] - b[ 0 ], a[ 1 ] - b[ 1 ], a[ 2 ] - b[ 2 ] ]; + + } + + var normalize = function ( a ) { + + var l = Math.sqrt( a[ 0 ] * a[ 0 ] + a[ 1 ] * a[ 1 ] + a[ 2 ] * a[ 2 ] ); + return [ a[ 0 ] / l, a[ 1 ] / l, a[ 2 ] / l ]; + + } + + depth = depth | 1; + + var width = image.width; + var height = image.height; + + var canvas = document.createElement( 'canvas' ); + canvas.width = width; + canvas.height = height; + + var context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0 ); + + var data = context.getImageData( 0, 0, width, height ).data; + var imageData = context.createImageData( width, height ); + var output = imageData.data; + + for ( var x = 0; x < width; x ++ ) { + + for ( var y = 0; y < height; y ++ ) { + + var ly = y - 1 < 0 ? 0 : y - 1; + var uy = y + 1 > height - 1 ? height - 1 : y + 1; + var lx = x - 1 < 0 ? 0 : x - 1; + var ux = x + 1 > width - 1 ? width - 1 : x + 1; + + var points = []; + var origin = [ 0, 0, data[ ( y * width + x ) * 4 ] / 255 * depth ]; + points.push( [ - 1, 0, data[ ( y * width + lx ) * 4 ] / 255 * depth ] ); + points.push( [ - 1, - 1, data[ ( ly * width + lx ) * 4 ] / 255 * depth ] ); + points.push( [ 0, - 1, data[ ( ly * width + x ) * 4 ] / 255 * depth ] ); + points.push( [ 1, - 1, data[ ( ly * width + ux ) * 4 ] / 255 * depth ] ); + points.push( [ 1, 0, data[ ( y * width + ux ) * 4 ] / 255 * depth ] ); + points.push( [ 1, 1, data[ ( uy * width + ux ) * 4 ] / 255 * depth ] ); + points.push( [ 0, 1, data[ ( uy * width + x ) * 4 ] / 255 * depth ] ); + points.push( [ - 1, 1, data[ ( uy * width + lx ) * 4 ] / 255 * depth ] ); + + var normals = []; + var num_points = points.length; + + for ( var i = 0; i < num_points; i ++ ) { + + var v1 = points[ i ]; + var v2 = points[ ( i + 1 ) % num_points ]; + v1 = subtract( v1, origin ); + v2 = subtract( v2, origin ); + normals.push( normalize( cross( v1, v2 ) ) ); + + } + + var normal = [ 0, 0, 0 ]; + + for ( var i = 0; i < normals.length; i ++ ) { + + normal[ 0 ] += normals[ i ][ 0 ]; + normal[ 1 ] += normals[ i ][ 1 ]; + normal[ 2 ] += normals[ i ][ 2 ]; + + } + + normal[ 0 ] /= normals.length; + normal[ 1 ] /= normals.length; + normal[ 2 ] /= normals.length; + + var idx = ( y * width + x ) * 4; + + output[ idx ] = ( ( normal[ 0 ] + 1.0 ) / 2.0 * 255 ) | 0; + output[ idx + 1 ] = ( ( normal[ 1 ] + 1.0 ) / 2.0 * 255 ) | 0; + output[ idx + 2 ] = ( normal[ 2 ] * 255 ) | 0; + output[ idx + 3 ] = 255; + + } + + } + + context.putImageData( imageData, 0, 0 ); + + return canvas; + + }, + + generateDataTexture: function ( width, height, color ) { + + var size = width * height; + var data = new Uint8Array( 3 * size ); + + var r = Math.floor( color.r * 255 ); + var g = Math.floor( color.g * 255 ); + var b = Math.floor( color.b * 255 ); + + for ( var i = 0; i < size; i ++ ) { + + data[ i * 3 ] = r; + data[ i * 3 + 1 ] = g; + data[ i * 3 + 2 ] = b; + + } + + var texture = new THREE.DataTexture( data, width, height, THREE.RGBFormat ); + texture.needsUpdate = true; + + return texture; + + } + +}; + +// File:src/extras/SceneUtils.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.SceneUtils = { + + createMultiMaterialObject: function ( geometry, materials ) { + + var group = new THREE.Object3D(); + + for ( var i = 0, l = materials.length; i < l; i ++ ) { + + group.add( new THREE.Mesh( geometry, materials[ i ] ) ); + + } + + return group; + + }, + + detach: function ( child, parent, scene ) { + + child.applyMatrix( parent.matrixWorld ); + parent.remove( child ); + scene.add( child ); + + }, + + attach: function ( child, scene, parent ) { + + var matrixWorldInverse = new THREE.Matrix4(); + matrixWorldInverse.getInverse( parent.matrixWorld ); + child.applyMatrix( matrixWorldInverse ); + + scene.remove( child ); + parent.add( child ); + + } + +}; + +// File:src/extras/FontUtils.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * @author alteredq / http://alteredqualia.com/ + * + * For Text operations in three.js (See TextGeometry) + * + * It uses techniques used in: + * + * typeface.js and canvastext + * For converting fonts and rendering with javascript + * http://typeface.neocracy.org + * + * Triangulation ported from AS3 + * Simple Polygon Triangulation + * http://actionsnippet.com/?p=1462 + * + * A Method to triangulate shapes with holes + * http://www.sakri.net/blog/2009/06/12/an-approach-to-triangulating-polygons-with-holes/ + * + */ + +THREE.FontUtils = { + + faces: {}, + + // Just for now. face[weight][style] + + face: 'helvetiker', + weight: 'normal', + style: 'normal', + size: 150, + divisions: 10, + + getFace: function () { + + try { + + return this.faces[ this.face ][ this.weight ][ this.style ]; + + } catch (e) { + + throw "The font " + this.face + " with " + this.weight + " weight and " + this.style + " style is missing." + + }; + + }, + + loadFace: function ( data ) { + + var family = data.familyName.toLowerCase(); + + var ThreeFont = this; + + ThreeFont.faces[ family ] = ThreeFont.faces[ family ] || {}; + + ThreeFont.faces[ family ][ data.cssFontWeight ] = ThreeFont.faces[ family ][ data.cssFontWeight ] || {}; + ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data; + + var face = ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data; + + return data; + + }, + + drawText: function ( text ) { + + var characterPts = [], allPts = []; + + // RenderText + + var i, p, + face = this.getFace(), + scale = this.size / face.resolution, + offset = 0, + chars = String( text ).split( '' ), + length = chars.length; + + var fontPaths = []; + + for ( i = 0; i < length; i ++ ) { + + var path = new THREE.Path(); + + var ret = this.extractGlyphPoints( chars[ i ], face, scale, offset, path ); + offset += ret.offset; + + fontPaths.push( ret.path ); + + } + + // get the width + + var width = offset / 2; + // + // for ( p = 0; p < allPts.length; p++ ) { + // + // allPts[ p ].x -= width; + // + // } + + //var extract = this.extractPoints( allPts, characterPts ); + //extract.contour = allPts; + + //extract.paths = fontPaths; + //extract.offset = width; + + return { paths: fontPaths, offset: width }; + + }, + + + + + extractGlyphPoints: function ( c, face, scale, offset, path ) { + + var pts = []; + + var i, i2, divisions, + outline, action, length, + scaleX, scaleY, + x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2, + laste, + glyph = face.glyphs[ c ] || face.glyphs[ '?' ]; + + if ( ! glyph ) return; + + if ( glyph.o ) { + + outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) ); + length = outline.length; + + scaleX = scale; + scaleY = scale; + + for ( i = 0; i < length; ) { + + action = outline[ i ++ ]; + + //console.log( action ); + + switch ( action ) { + + case 'm': + + // Move To + + x = outline[ i ++ ] * scaleX + offset; + y = outline[ i ++ ] * scaleY; + + path.moveTo( x, y ); + break; + + case 'l': + + // Line To + + x = outline[ i ++ ] * scaleX + offset; + y = outline[ i ++ ] * scaleY; + path.lineTo( x,y ); + break; + + case 'q': + + // QuadraticCurveTo + + cpx = outline[ i ++ ] * scaleX + offset; + cpy = outline[ i ++ ] * scaleY; + cpx1 = outline[ i ++ ] * scaleX + offset; + cpy1 = outline[ i ++ ] * scaleY; + + path.quadraticCurveTo( cpx1, cpy1, cpx, cpy ); + + laste = pts[ pts.length - 1 ]; + + if ( laste ) { + + cpx0 = laste.x; + cpy0 = laste.y; + + for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) { + + var t = i2 / divisions; + var tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx ); + var ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy ); + } + + } + + break; + + case 'b': + + // Cubic Bezier Curve + + cpx = outline[ i ++ ] * scaleX + offset; + cpy = outline[ i ++ ] * scaleY; + cpx1 = outline[ i ++ ] * scaleX + offset; + cpy1 = outline[ i ++ ] * scaleY; + cpx2 = outline[ i ++ ] * scaleX + offset; + cpy2 = outline[ i ++ ] * scaleY; + + path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy ); + + laste = pts[ pts.length - 1 ]; + + if ( laste ) { + + cpx0 = laste.x; + cpy0 = laste.y; + + for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) { + + var t = i2 / divisions; + var tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx ); + var ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy ); + + } + + } + + break; + + } + + } + } + + + + return { offset: glyph.ha * scale, path:path }; + } + +}; + + +THREE.FontUtils.generateShapes = function ( text, parameters ) { + + // Parameters + + parameters = parameters || {}; + + var size = parameters.size !== undefined ? parameters.size : 100; + var curveSegments = parameters.curveSegments !== undefined ? parameters.curveSegments : 4; + + var font = parameters.font !== undefined ? parameters.font : 'helvetiker'; + var weight = parameters.weight !== undefined ? parameters.weight : 'normal'; + var style = parameters.style !== undefined ? parameters.style : 'normal'; + + THREE.FontUtils.size = size; + THREE.FontUtils.divisions = curveSegments; + + THREE.FontUtils.face = font; + THREE.FontUtils.weight = weight; + THREE.FontUtils.style = style; + + // Get a Font data json object + + var data = THREE.FontUtils.drawText( text ); + + var paths = data.paths; + var shapes = []; + + for ( var p = 0, pl = paths.length; p < pl; p ++ ) { + + Array.prototype.push.apply( shapes, paths[ p ].toShapes() ); + + } + + return shapes; + +}; + + +/** + * This code is a quick port of code written in C++ which was submitted to + * flipcode.com by John W. Ratcliff // July 22, 2000 + * See original code and more information here: + * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml + * + * ported to actionscript by Zevan Rosser + * www.actionsnippet.com + * + * ported to javascript by Joshua Koo + * http://www.lab4games.net/zz85/blog + * + */ + + +( function ( namespace ) { + + var EPSILON = 0.0000000001; + + // takes in an contour array and returns + + var process = function ( contour, indices ) { + + var n = contour.length; + + if ( n < 3 ) return null; + + var result = [], + verts = [], + vertIndices = []; + + /* we want a counter-clockwise polygon in verts */ + + var u, v, w; + + if ( area( contour ) > 0.0 ) { + + for ( v = 0; v < n; v ++ ) verts[ v ] = v; + + } else { + + for ( v = 0; v < n; v ++ ) verts[ v ] = ( n - 1 ) - v; + + } + + var nv = n; + + /* remove nv - 2 vertices, creating 1 triangle every time */ + + var count = 2 * nv; /* error detection */ + + for ( v = nv - 1; nv > 2; ) { + + /* if we loop, it is probably a non-simple polygon */ + + if ( ( count -- ) <= 0 ) { + + //** Triangulate: ERROR - probable bad polygon! + + //throw ( "Warning, unable to triangulate polygon!" ); + //return null; + // Sometimes warning is fine, especially polygons are triangulated in reverse. + console.log( 'Warning, unable to triangulate polygon!' ); + + if ( indices ) return vertIndices; + return result; + + } + + /* three consecutive vertices in current polygon, */ + + u = v; if ( nv <= u ) u = 0; /* previous */ + v = u + 1; if ( nv <= v ) v = 0; /* new v */ + w = v + 1; if ( nv <= w ) w = 0; /* next */ + + if ( snip( contour, u, v, w, nv, verts ) ) { + + var a, b, c, s, t; + + /* true names of the vertices */ + + a = verts[ u ]; + b = verts[ v ]; + c = verts[ w ]; + + /* output Triangle */ + + result.push( [ contour[ a ], + contour[ b ], + contour[ c ] ] ); + + + vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] ); + + /* remove v from the remaining polygon */ + + for ( s = v, t = v + 1; t < nv; s++, t++ ) { + + verts[ s ] = verts[ t ]; + + } + + nv --; + + /* reset error detection counter */ + + count = 2 * nv; + + } + + } + + if ( indices ) return vertIndices; + return result; + + }; + + // calculate area of the contour polygon + + var area = function ( contour ) { + + var n = contour.length; + var a = 0.0; + + for ( var p = n - 1, q = 0; q < n; p = q ++ ) { + + a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; + + } + + return a * 0.5; + + }; + + var snip = function ( contour, u, v, w, n, verts ) { + + var p; + var ax, ay, bx, by; + var cx, cy, px, py; + + ax = contour[ verts[ u ] ].x; + ay = contour[ verts[ u ] ].y; + + bx = contour[ verts[ v ] ].x; + by = contour[ verts[ v ] ].y; + + cx = contour[ verts[ w ] ].x; + cy = contour[ verts[ w ] ].y; + + if ( EPSILON > ( ( ( bx - ax ) * ( cy - ay ) ) - ( ( by - ay ) * ( cx - ax ) ) ) ) return false; + + var aX, aY, bX, bY, cX, cY; + var apx, apy, bpx, bpy, cpx, cpy; + var cCROSSap, bCROSScp, aCROSSbp; + + aX = cx - bx; aY = cy - by; + bX = ax - cx; bY = ay - cy; + cX = bx - ax; cY = by - ay; + + for ( p = 0; p < n; p ++ ) { + + px = contour[ verts[ p ] ].x + py = contour[ verts[ p ] ].y + + if ( ( ( px === ax ) && ( py === ay ) ) || + ( ( px === bx ) && ( py === by ) ) || + ( ( px === cx ) && ( py === cy ) ) ) continue; + + apx = px - ax; apy = py - ay; + bpx = px - bx; bpy = py - by; + cpx = px - cx; cpy = py - cy; + + // see if p is inside triangle abc + + aCROSSbp = aX * bpy - aY * bpx; + cCROSSap = cX * apy - cY * apx; + bCROSScp = bX * cpy - bY * cpx; + + if ( ( aCROSSbp >= - EPSILON ) && ( bCROSScp >= - EPSILON ) && ( cCROSSap >= - EPSILON ) ) return false; + + } + + return true; + + }; + + + namespace.Triangulate = process; + namespace.Triangulate.area = area; + + return namespace; + +} )( THREE.FontUtils ); + +// To use the typeface.js face files, hook up the API +self._typeface_js = { faces: THREE.FontUtils.faces, loadFace: THREE.FontUtils.loadFace }; +THREE.typeface_js = self._typeface_js; + +// File:src/extras/core/Curve.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * Extensible curve object + * + * Some common of Curve methods + * .getPoint(t), getTangent(t) + * .getPointAt(u), getTagentAt(u) + * .getPoints(), .getSpacedPoints() + * .getLength() + * .updateArcLengths() + * + * This following classes subclasses THREE.Curve: + * + * -- 2d classes -- + * THREE.LineCurve + * THREE.QuadraticBezierCurve + * THREE.CubicBezierCurve + * THREE.SplineCurve + * THREE.ArcCurve + * THREE.EllipseCurve + * + * -- 3d classes -- + * THREE.LineCurve3 + * THREE.QuadraticBezierCurve3 + * THREE.CubicBezierCurve3 + * THREE.SplineCurve3 + * THREE.ClosedSplineCurve3 + * + * A series of curves can be represented as a THREE.CurvePath + * + **/ + +/************************************************************** + * Abstract Curve base class + **************************************************************/ + +THREE.Curve = function () { + +}; + +// Virtual base class method to overwrite and implement in subclasses +// - t [0 .. 1] + +THREE.Curve.prototype.getPoint = function ( t ) { + + console.log( "Warning, getPoint() not implemented!" ); + return null; + +}; + +// Get point at relative position in curve according to arc length +// - u [0 .. 1] + +THREE.Curve.prototype.getPointAt = function ( u ) { + + var t = this.getUtoTmapping( u ); + return this.getPoint( t ); + +}; + +// Get sequence of points using getPoint( t ) + +THREE.Curve.prototype.getPoints = function ( divisions ) { + + if ( ! divisions ) divisions = 5; + + var d, pts = []; + + for ( d = 0; d <= divisions; d ++ ) { + + pts.push( this.getPoint( d / divisions ) ); + + } + + return pts; + +}; + +// Get sequence of points using getPointAt( u ) + +THREE.Curve.prototype.getSpacedPoints = function ( divisions ) { + + if ( ! divisions ) divisions = 5; + + var d, pts = []; + + for ( d = 0; d <= divisions; d ++ ) { + + pts.push( this.getPointAt( d / divisions ) ); + + } + + return pts; + +}; + +// Get total curve arc length + +THREE.Curve.prototype.getLength = function () { + + var lengths = this.getLengths(); + return lengths[ lengths.length - 1 ]; + +}; + +// Get list of cumulative segment lengths + +THREE.Curve.prototype.getLengths = function ( divisions ) { + + if ( ! divisions ) divisions = (this.__arcLengthDivisions) ? (this.__arcLengthDivisions): 200; + + if ( this.cacheArcLengths + && ( this.cacheArcLengths.length == divisions + 1 ) + && ! this.needsUpdate) { + + //console.log( "cached", this.cacheArcLengths ); + return this.cacheArcLengths; + + } + + this.needsUpdate = false; + + var cache = []; + var current, last = this.getPoint( 0 ); + var p, sum = 0; + + cache.push( 0 ); + + for ( p = 1; p <= divisions; p ++ ) { + + current = this.getPoint ( p / divisions ); + sum += current.distanceTo( last ); + cache.push( sum ); + last = current; + + } + + this.cacheArcLengths = cache; + + return cache; // { sums: cache, sum:sum }; Sum is in the last element. + +}; + + +THREE.Curve.prototype.updateArcLengths = function() { + this.needsUpdate = true; + this.getLengths(); +}; + +// Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equi distance + +THREE.Curve.prototype.getUtoTmapping = function ( u, distance ) { + + var arcLengths = this.getLengths(); + + var i = 0, il = arcLengths.length; + + var targetArcLength; // The targeted u distance value to get + + if ( distance ) { + + targetArcLength = distance; + + } else { + + targetArcLength = u * arcLengths[ il - 1 ]; + + } + + //var time = Date.now(); + + // binary search for the index with largest value smaller than target u distance + + var low = 0, high = il - 1, comparison; + + while ( low <= high ) { + + i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats + + comparison = arcLengths[ i ] - targetArcLength; + + if ( comparison < 0 ) { + + low = i + 1; + continue; + + } else if ( comparison > 0 ) { + + high = i - 1; + continue; + + } else { + + high = i; + break; + + // DONE + + } + + } + + i = high; + + //console.log('b' , i, low, high, Date.now()- time); + + if ( arcLengths[ i ] == targetArcLength ) { + + var t = i / ( il - 1 ); + return t; + + } + + // we could get finer grain at lengths, or use simple interpolatation between two points + + var lengthBefore = arcLengths[ i ]; + var lengthAfter = arcLengths[ i + 1 ]; + + var segmentLength = lengthAfter - lengthBefore; + + // determine where we are between the 'before' and 'after' points + + var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; + + // add that fractional amount to t + + var t = ( i + segmentFraction ) / ( il -1 ); + + return t; + +}; + +// Returns a unit vector tangent at t +// In case any sub curve does not implement its tangent derivation, +// 2 points a small delta apart will be used to find its gradient +// which seems to give a reasonable approximation + +THREE.Curve.prototype.getTangent = function( t ) { + + var delta = 0.0001; + var t1 = t - delta; + var t2 = t + delta; + + // Capping in case of danger + + if ( t1 < 0 ) t1 = 0; + if ( t2 > 1 ) t2 = 1; + + var pt1 = this.getPoint( t1 ); + var pt2 = this.getPoint( t2 ); + + var vec = pt2.clone().sub(pt1); + return vec.normalize(); + +}; + + +THREE.Curve.prototype.getTangentAt = function ( u ) { + + var t = this.getUtoTmapping( u ); + return this.getTangent( t ); + +}; + + + + + +/************************************************************** + * Utils + **************************************************************/ + +THREE.Curve.Utils = { + + tangentQuadraticBezier: function ( t, p0, p1, p2 ) { + + return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 ); + + }, + + // Puay Bing, thanks for helping with this derivative! + + tangentCubicBezier: function (t, p0, p1, p2, p3 ) { + + return - 3 * p0 * (1 - t) * (1 - t) + + 3 * p1 * (1 - t) * (1-t) - 6 *t *p1 * (1-t) + + 6 * t * p2 * (1-t) - 3 * t * t * p2 + + 3 * t * t * p3; + }, + + + tangentSpline: function ( t, p0, p1, p2, p3 ) { + + // To check if my formulas are correct + + var h00 = 6 * t * t - 6 * t; // derived from 2t^3 − 3t^2 + 1 + var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t + var h01 = - 6 * t * t + 6 * t; // − 2t3 + 3t2 + var h11 = 3 * t * t - 2 * t; // t3 − t2 + + return h00 + h10 + h01 + h11; + + }, + + // Catmull-Rom + + interpolate: function( p0, p1, p2, p3, t ) { + + var v0 = ( p2 - p0 ) * 0.5; + var v1 = ( p3 - p1 ) * 0.5; + var t2 = t * t; + var t3 = t * t2; + return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; + + } + +}; + + +// TODO: Transformation for Curves? + +/************************************************************** + * 3D Curves + **************************************************************/ + +// A Factory method for creating new curve subclasses + +THREE.Curve.create = function ( constructor, getPointFunc ) { + + constructor.prototype = Object.create( THREE.Curve.prototype ); + constructor.prototype.getPoint = getPointFunc; + + return constructor; + +}; + +// File:src/extras/core/CurvePath.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * + **/ + +/************************************************************** + * Curved Path - a curve path is simply a array of connected + * curves, but retains the api of a curve + **************************************************************/ + +THREE.CurvePath = function () { + + this.curves = []; + this.bends = []; + + this.autoClose = false; // Automatically closes the path +}; + +THREE.CurvePath.prototype = Object.create( THREE.Curve.prototype ); + +THREE.CurvePath.prototype.add = function ( curve ) { + + this.curves.push( curve ); + +}; + +THREE.CurvePath.prototype.checkConnection = function() { + // TODO + // If the ending of curve is not connected to the starting + // or the next curve, then, this is not a real path +}; + +THREE.CurvePath.prototype.closePath = function() { + // TODO Test + // and verify for vector3 (needs to implement equals) + // Add a line curve if start and end of lines are not connected + var startPoint = this.curves[0].getPoint(0); + var endPoint = this.curves[this.curves.length-1].getPoint(1); + + if (! startPoint.equals(endPoint)) { + this.curves.push( new THREE.LineCurve(endPoint, startPoint) ); + } + +}; + +// To get accurate point with reference to +// entire path distance at time t, +// following has to be done: + +// 1. Length of each sub path have to be known +// 2. Locate and identify type of curve +// 3. Get t for the curve +// 4. Return curve.getPointAt(t') + +THREE.CurvePath.prototype.getPoint = function( t ) { + + var d = t * this.getLength(); + var curveLengths = this.getCurveLengths(); + var i = 0, diff, curve; + + // To think about boundaries points. + + while ( i < curveLengths.length ) { + + if ( curveLengths[ i ] >= d ) { + + diff = curveLengths[ i ] - d; + curve = this.curves[ i ]; + + var u = 1 - diff / curve.getLength(); + + return curve.getPointAt( u ); + + break; + } + + i ++; + + } + + return null; + + // loop where sum != 0, sum > d , sum+1 maxX ) maxX = p.x; + else if ( p.x < minX ) minX = p.x; + + if ( p.y > maxY ) maxY = p.y; + else if ( p.y < minY ) minY = p.y; + + if ( v3 ) { + + if ( p.z > maxZ ) maxZ = p.z; + else if ( p.z < minZ ) minZ = p.z; + + } + + sum.add( p ); + + } + + var ret = { + + minX: minX, + minY: minY, + maxX: maxX, + maxY: maxY + + }; + + if ( v3 ) { + + ret.maxZ = maxZ; + ret.minZ = minZ; + + } + + return ret; + +}; + +/************************************************************** + * Create Geometries Helpers + **************************************************************/ + +/// Generate geometry from path points (for Line or Points objects) + +THREE.CurvePath.prototype.createPointsGeometry = function( divisions ) { + + var pts = this.getPoints( divisions, true ); + return this.createGeometry( pts ); + +}; + +// Generate geometry from equidistance sampling along the path + +THREE.CurvePath.prototype.createSpacedPointsGeometry = function( divisions ) { + + var pts = this.getSpacedPoints( divisions, true ); + return this.createGeometry( pts ); + +}; + +THREE.CurvePath.prototype.createGeometry = function( points ) { + + var geometry = new THREE.Geometry(); + + for ( var i = 0; i < points.length; i ++ ) { + + geometry.vertices.push( new THREE.Vector3( points[ i ].x, points[ i ].y, points[ i ].z || 0) ); + + } + + return geometry; + +}; + + +/************************************************************** + * Bend / Wrap Helper Methods + **************************************************************/ + +// Wrap path / Bend modifiers? + +THREE.CurvePath.prototype.addWrapPath = function ( bendpath ) { + + this.bends.push( bendpath ); + +}; + +THREE.CurvePath.prototype.getTransformedPoints = function( segments, bends ) { + + var oldPts = this.getPoints( segments ); // getPoints getSpacedPoints + var i, il; + + if ( ! bends ) { + + bends = this.bends; + + } + + for ( i = 0, il = bends.length; i < il; i ++ ) { + + oldPts = this.getWrapPoints( oldPts, bends[ i ] ); + + } + + return oldPts; + +}; + +THREE.CurvePath.prototype.getTransformedSpacedPoints = function( segments, bends ) { + + var oldPts = this.getSpacedPoints( segments ); + + var i, il; + + if ( ! bends ) { + + bends = this.bends; + + } + + for ( i = 0, il = bends.length; i < il; i ++ ) { + + oldPts = this.getWrapPoints( oldPts, bends[ i ] ); + + } + + return oldPts; + +}; + +// This returns getPoints() bend/wrapped around the contour of a path. +// Read http://www.planetclegg.com/projects/WarpingTextToSplines.html + +THREE.CurvePath.prototype.getWrapPoints = function ( oldPts, path ) { + + var bounds = this.getBoundingBox(); + + var i, il, p, oldX, oldY, xNorm; + + for ( i = 0, il = oldPts.length; i < il; i ++ ) { + + p = oldPts[ i ]; + + oldX = p.x; + oldY = p.y; + + xNorm = oldX / bounds.maxX; + + // If using actual distance, for length > path, requires line extrusions + //xNorm = path.getUtoTmapping(xNorm, oldX); // 3 styles. 1) wrap stretched. 2) wrap stretch by arc length 3) warp by actual distance + + xNorm = path.getUtoTmapping( xNorm, oldX ); + + // check for out of bounds? + + var pathPt = path.getPoint( xNorm ); + var normal = path.getTangent( xNorm ); + normal.set( - normal.y, normal.x ).multiplyScalar( oldY ); + + p.x = pathPt.x + normal.x; + p.y = pathPt.y + normal.y; + + } + + return oldPts; + +}; + + +// File:src/extras/core/Gyroscope.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Gyroscope = function () { + + THREE.Object3D.call( this ); + +}; + +THREE.Gyroscope.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.Gyroscope.prototype.updateMatrixWorld = function ( force ) { + + this.matrixAutoUpdate && this.updateMatrix(); + + // update matrixWorld + + if ( this.matrixWorldNeedsUpdate || force ) { + + if ( this.parent ) { + + this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); + + this.matrixWorld.decompose( this.translationWorld, this.quaternionWorld, this.scaleWorld ); + this.matrix.decompose( this.translationObject, this.quaternionObject, this.scaleObject ); + + this.matrixWorld.compose( this.translationWorld, this.quaternionObject, this.scaleWorld ); + + + } else { + + this.matrixWorld.copy( this.matrix ); + + } + + + this.matrixWorldNeedsUpdate = false; + + force = true; + + } + + // update children + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + this.children[ i ].updateMatrixWorld( force ); + + } + +}; + +THREE.Gyroscope.prototype.translationWorld = new THREE.Vector3(); +THREE.Gyroscope.prototype.translationObject = new THREE.Vector3(); +THREE.Gyroscope.prototype.quaternionWorld = new THREE.Quaternion(); +THREE.Gyroscope.prototype.quaternionObject = new THREE.Quaternion(); +THREE.Gyroscope.prototype.scaleWorld = new THREE.Vector3(); +THREE.Gyroscope.prototype.scaleObject = new THREE.Vector3(); + + +// File:src/extras/core/Path.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * Creates free form 2d path using series of points, lines or curves. + * + **/ + +THREE.Path = function ( points ) { + + THREE.CurvePath.call(this); + + this.actions = []; + + if ( points ) { + + this.fromPoints( points ); + + } + +}; + +THREE.Path.prototype = Object.create( THREE.CurvePath.prototype ); + +THREE.PathActions = { + + MOVE_TO: 'moveTo', + LINE_TO: 'lineTo', + QUADRATIC_CURVE_TO: 'quadraticCurveTo', // Bezier quadratic curve + BEZIER_CURVE_TO: 'bezierCurveTo', // Bezier cubic curve + CSPLINE_THRU: 'splineThru', // Catmull-rom spline + ARC: 'arc', // Circle + ELLIPSE: 'ellipse' +}; + +// TODO Clean up PATH API + +// Create path using straight lines to connect all points +// - vectors: array of Vector2 + +THREE.Path.prototype.fromPoints = function ( vectors ) { + + this.moveTo( vectors[ 0 ].x, vectors[ 0 ].y ); + + for ( var v = 1, vlen = vectors.length; v < vlen; v ++ ) { + + this.lineTo( vectors[ v ].x, vectors[ v ].y ); + + }; + +}; + +// startPath() endPath()? + +THREE.Path.prototype.moveTo = function ( x, y ) { + + var args = Array.prototype.slice.call( arguments ); + this.actions.push( { action: THREE.PathActions.MOVE_TO, args: args } ); + +}; + +THREE.Path.prototype.lineTo = function ( x, y ) { + + var args = Array.prototype.slice.call( arguments ); + + var lastargs = this.actions[ this.actions.length - 1 ].args; + + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; + + var curve = new THREE.LineCurve( new THREE.Vector2( x0, y0 ), new THREE.Vector2( x, y ) ); + this.curves.push( curve ); + + this.actions.push( { action: THREE.PathActions.LINE_TO, args: args } ); + +}; + +THREE.Path.prototype.quadraticCurveTo = function( aCPx, aCPy, aX, aY ) { + + var args = Array.prototype.slice.call( arguments ); + + var lastargs = this.actions[ this.actions.length - 1 ].args; + + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; + + var curve = new THREE.QuadraticBezierCurve( new THREE.Vector2( x0, y0 ), + new THREE.Vector2( aCPx, aCPy ), + new THREE.Vector2( aX, aY ) ); + this.curves.push( curve ); + + this.actions.push( { action: THREE.PathActions.QUADRATIC_CURVE_TO, args: args } ); + +}; + +THREE.Path.prototype.bezierCurveTo = function( aCP1x, aCP1y, + aCP2x, aCP2y, + aX, aY ) { + + var args = Array.prototype.slice.call( arguments ); + + var lastargs = this.actions[ this.actions.length - 1 ].args; + + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; + + var curve = new THREE.CubicBezierCurve( new THREE.Vector2( x0, y0 ), + new THREE.Vector2( aCP1x, aCP1y ), + new THREE.Vector2( aCP2x, aCP2y ), + new THREE.Vector2( aX, aY ) ); + this.curves.push( curve ); + + this.actions.push( { action: THREE.PathActions.BEZIER_CURVE_TO, args: args } ); + +}; + +THREE.Path.prototype.splineThru = function( pts /*Array of Vector*/ ) { + + var args = Array.prototype.slice.call( arguments ); + var lastargs = this.actions[ this.actions.length - 1 ].args; + + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; +//--- + var npts = [ new THREE.Vector2( x0, y0 ) ]; + Array.prototype.push.apply( npts, pts ); + + var curve = new THREE.SplineCurve( npts ); + this.curves.push( curve ); + + this.actions.push( { action: THREE.PathActions.CSPLINE_THRU, args: args } ); + +}; + +// FUTURE: Change the API or follow canvas API? + +THREE.Path.prototype.arc = function ( aX, aY, aRadius, + aStartAngle, aEndAngle, aClockwise ) { + + var lastargs = this.actions[ this.actions.length - 1].args; + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; + + this.absarc(aX + x0, aY + y0, aRadius, + aStartAngle, aEndAngle, aClockwise ); + + }; + + THREE.Path.prototype.absarc = function ( aX, aY, aRadius, + aStartAngle, aEndAngle, aClockwise ) { + this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise); + }; + +THREE.Path.prototype.ellipse = function ( aX, aY, xRadius, yRadius, + aStartAngle, aEndAngle, aClockwise ) { + + var lastargs = this.actions[ this.actions.length - 1].args; + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; + + this.absellipse(aX + x0, aY + y0, xRadius, yRadius, + aStartAngle, aEndAngle, aClockwise ); + + }; + + +THREE.Path.prototype.absellipse = function ( aX, aY, xRadius, yRadius, + aStartAngle, aEndAngle, aClockwise ) { + + var args = Array.prototype.slice.call( arguments ); + var curve = new THREE.EllipseCurve( aX, aY, xRadius, yRadius, + aStartAngle, aEndAngle, aClockwise ); + this.curves.push( curve ); + + var lastPoint = curve.getPoint(1); + args.push(lastPoint.x); + args.push(lastPoint.y); + + this.actions.push( { action: THREE.PathActions.ELLIPSE, args: args } ); + + }; + +THREE.Path.prototype.getSpacedPoints = function ( divisions, closedPath ) { + + if ( ! divisions ) divisions = 40; + + var points = []; + + for ( var i = 0; i < divisions; i ++ ) { + + points.push( this.getPoint( i / divisions ) ); + + //if( !this.getPoint( i / divisions ) ) throw "DIE"; + + } + + // if ( closedPath ) { + // + // points.push( points[ 0 ] ); + // + // } + + return points; + +}; + +/* Return an array of vectors based on contour of the path */ + +THREE.Path.prototype.getPoints = function( divisions, closedPath ) { + + if (this.useSpacedPoints) { + console.log('tata'); + return this.getSpacedPoints( divisions, closedPath ); + } + + divisions = divisions || 12; + + var points = []; + + var i, il, item, action, args; + var cpx, cpy, cpx2, cpy2, cpx1, cpy1, cpx0, cpy0, + laste, j, + t, tx, ty; + + for ( i = 0, il = this.actions.length; i < il; i ++ ) { + + item = this.actions[ i ]; + + action = item.action; + args = item.args; + + switch( action ) { + + case THREE.PathActions.MOVE_TO: + + points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) ); + + break; + + case THREE.PathActions.LINE_TO: + + points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) ); + + break; + + case THREE.PathActions.QUADRATIC_CURVE_TO: + + cpx = args[ 2 ]; + cpy = args[ 3 ]; + + cpx1 = args[ 0 ]; + cpy1 = args[ 1 ]; + + if ( points.length > 0 ) { + + laste = points[ points.length - 1 ]; + + cpx0 = laste.x; + cpy0 = laste.y; + + } else { + + laste = this.actions[ i - 1 ].args; + + cpx0 = laste[ laste.length - 2 ]; + cpy0 = laste[ laste.length - 1 ]; + + } + + for ( j = 1; j <= divisions; j ++ ) { + + t = j / divisions; + + tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx ); + ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy ); + + points.push( new THREE.Vector2( tx, ty ) ); + + } + + break; + + case THREE.PathActions.BEZIER_CURVE_TO: + + cpx = args[ 4 ]; + cpy = args[ 5 ]; + + cpx1 = args[ 0 ]; + cpy1 = args[ 1 ]; + + cpx2 = args[ 2 ]; + cpy2 = args[ 3 ]; + + if ( points.length > 0 ) { + + laste = points[ points.length - 1 ]; + + cpx0 = laste.x; + cpy0 = laste.y; + + } else { + + laste = this.actions[ i - 1 ].args; + + cpx0 = laste[ laste.length - 2 ]; + cpy0 = laste[ laste.length - 1 ]; + + } + + + for ( j = 1; j <= divisions; j ++ ) { + + t = j / divisions; + + tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx ); + ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy ); + + points.push( new THREE.Vector2( tx, ty ) ); + + } + + break; + + case THREE.PathActions.CSPLINE_THRU: + + laste = this.actions[ i - 1 ].args; + + var last = new THREE.Vector2( laste[ laste.length - 2 ], laste[ laste.length - 1 ] ); + var spts = [ last ]; + + var n = divisions * args[ 0 ].length; + + spts = spts.concat( args[ 0 ] ); + + var spline = new THREE.SplineCurve( spts ); + + for ( j = 1; j <= n; j ++ ) { + + points.push( spline.getPointAt( j / n ) ) ; + + } + + break; + + case THREE.PathActions.ARC: + + var aX = args[ 0 ], aY = args[ 1 ], + aRadius = args[ 2 ], + aStartAngle = args[ 3 ], aEndAngle = args[ 4 ], + aClockwise = !! args[ 5 ]; + + var deltaAngle = aEndAngle - aStartAngle; + var angle; + var tdivisions = divisions * 2; + + for ( j = 1; j <= tdivisions; j ++ ) { + + t = j / tdivisions; + + if ( ! aClockwise ) { + + t = 1 - t; + + } + + angle = aStartAngle + t * deltaAngle; + + tx = aX + aRadius * Math.cos( angle ); + ty = aY + aRadius * Math.sin( angle ); + + //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty); + + points.push( new THREE.Vector2( tx, ty ) ); + + } + + //console.log(points); + + break; + + case THREE.PathActions.ELLIPSE: + + var aX = args[ 0 ], aY = args[ 1 ], + xRadius = args[ 2 ], + yRadius = args[ 3 ], + aStartAngle = args[ 4 ], aEndAngle = args[ 5 ], + aClockwise = !! args[ 6 ]; + + + var deltaAngle = aEndAngle - aStartAngle; + var angle; + var tdivisions = divisions * 2; + + for ( j = 1; j <= tdivisions; j ++ ) { + + t = j / tdivisions; + + if ( ! aClockwise ) { + + t = 1 - t; + + } + + angle = aStartAngle + t * deltaAngle; + + tx = aX + xRadius * Math.cos( angle ); + ty = aY + yRadius * Math.sin( angle ); + + //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty); + + points.push( new THREE.Vector2( tx, ty ) ); + + } + + //console.log(points); + + break; + + } // end switch + + } + + + + // Normalize to remove the closing point by default. + var lastPoint = points[ points.length - 1]; + var EPSILON = 0.0000000001; + if ( Math.abs(lastPoint.x - points[ 0 ].x) < EPSILON && + Math.abs(lastPoint.y - points[ 0 ].y) < EPSILON) + points.splice( points.length - 1, 1); + if ( closedPath ) { + + points.push( points[ 0 ] ); + + } + + return points; + +}; + +// +// Breaks path into shapes +// +// Assumptions (if parameter isCCW==true the opposite holds): +// - solid shapes are defined clockwise (CW) +// - holes are defined counterclockwise (CCW) +// +// If parameter noHoles==true: +// - all subPaths are regarded as solid shapes +// - definition order CW/CCW has no relevance +// + +THREE.Path.prototype.toShapes = function( isCCW, noHoles ) { + + function extractSubpaths( inActions ) { + + var i, il, item, action, args; + + var subPaths = [], lastPath = new THREE.Path(); + + for ( i = 0, il = inActions.length; i < il; i ++ ) { + + item = inActions[ i ]; + + args = item.args; + action = item.action; + + if ( action == THREE.PathActions.MOVE_TO ) { + + if ( lastPath.actions.length != 0 ) { + + subPaths.push( lastPath ); + lastPath = new THREE.Path(); + + } + + } + + lastPath[ action ].apply( lastPath, args ); + + } + + if ( lastPath.actions.length != 0 ) { + + subPaths.push( lastPath ); + + } + + // console.log(subPaths); + + return subPaths; + } + + function toShapesNoHoles( inSubpaths ) { + + var shapes = []; + + for ( var i = 0, il = inSubpaths.length; i < il; i ++ ) { + + var tmpPath = inSubpaths[ i ]; + + var tmpShape = new THREE.Shape(); + tmpShape.actions = tmpPath.actions; + tmpShape.curves = tmpPath.curves; + + shapes.push( tmpShape ); + } + + //console.log("shape", shapes); + + return shapes; + }; + + function isPointInsidePolygon( inPt, inPolygon ) { + var EPSILON = 0.0000000001; + + var polyLen = inPolygon.length; + + // inPt on polygon contour => immediate success or + // toggling of inside/outside at every single! intersection point of an edge + // with the horizontal line through inPt, left of inPt + // not counting lowerY endpoints of edges and whole edges on that line + var inside = false; + for( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) { + var edgeLowPt = inPolygon[ p ]; + var edgeHighPt = inPolygon[ q ]; + + var edgeDx = edgeHighPt.x - edgeLowPt.x; + var edgeDy = edgeHighPt.y - edgeLowPt.y; + + if ( Math.abs(edgeDy) > EPSILON ) { // not parallel + if ( edgeDy < 0 ) { + edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx; + edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy; + } + if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue; + + if ( inPt.y == edgeLowPt.y ) { + if ( inPt.x == edgeLowPt.x ) return true; // inPt is on contour ? + // continue; // no intersection or edgeLowPt => doesn't count !!! + } else { + var perpEdge = edgeDy * (inPt.x - edgeLowPt.x) - edgeDx * (inPt.y - edgeLowPt.y); + if ( perpEdge == 0 ) return true; // inPt is on contour ? + if ( perpEdge < 0 ) continue; + inside = ! inside; // true intersection left of inPt + } + } else { // parallel or colinear + if ( inPt.y != edgeLowPt.y ) continue; // parallel + // egde lies on the same horizontal line as inPt + if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) || + ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour ! + // continue; + } + } + + return inside; + } + + + var subPaths = extractSubpaths( this.actions ); + if ( subPaths.length == 0 ) return []; + + if ( noHoles === true ) return toShapesNoHoles( subPaths ); + + + var solid, tmpPath, tmpShape, shapes = []; + + if ( subPaths.length == 1) { + + tmpPath = subPaths[0]; + tmpShape = new THREE.Shape(); + tmpShape.actions = tmpPath.actions; + tmpShape.curves = tmpPath.curves; + shapes.push( tmpShape ); + return shapes; + + } + + var holesFirst = ! THREE.Shape.Utils.isClockWise( subPaths[ 0 ].getPoints() ); + holesFirst = isCCW ? ! holesFirst : holesFirst; + + // console.log("Holes first", holesFirst); + + var betterShapeHoles = []; + var newShapes = []; + var newShapeHoles = []; + var mainIdx = 0; + var tmpPoints; + + newShapes[mainIdx] = undefined; + newShapeHoles[mainIdx] = []; + + var i, il; + + for ( i = 0, il = subPaths.length; i < il; i ++ ) { + + tmpPath = subPaths[ i ]; + tmpPoints = tmpPath.getPoints(); + solid = THREE.Shape.Utils.isClockWise( tmpPoints ); + solid = isCCW ? ! solid : solid; + + if ( solid ) { + + if ( (! holesFirst ) && ( newShapes[mainIdx] ) ) mainIdx ++; + + newShapes[mainIdx] = { s: new THREE.Shape(), p: tmpPoints }; + newShapes[mainIdx].s.actions = tmpPath.actions; + newShapes[mainIdx].s.curves = tmpPath.curves; + + if ( holesFirst ) mainIdx ++; + newShapeHoles[mainIdx] = []; + + //console.log('cw', i); + + } else { + + newShapeHoles[mainIdx].push( { h: tmpPath, p: tmpPoints[0] } ); + + //console.log('ccw', i); + + } + + } + + // only Holes? -> probably all Shapes with wrong orientation + if ( ! newShapes[0] ) return toShapesNoHoles( subPaths ); + + + if ( newShapes.length > 1 ) { + var ambigious = false; + var toChange = []; + + for (var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { + betterShapeHoles[sIdx] = []; + } + for (var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { + var sh = newShapes[sIdx]; + var sho = newShapeHoles[sIdx]; + for (var hIdx = 0; hIdx < sho.length; hIdx ++ ) { + var ho = sho[hIdx]; + var hole_unassigned = true; + for (var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { + if ( isPointInsidePolygon( ho.p, newShapes[s2Idx].p ) ) { + if ( sIdx != s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } ); + if ( hole_unassigned ) { + hole_unassigned = false; + betterShapeHoles[s2Idx].push( ho ); + } else { + ambigious = true; + } + } + } + if ( hole_unassigned ) { betterShapeHoles[sIdx].push( ho ); } + } + } + // console.log("ambigious: ", ambigious); + if ( toChange.length > 0 ) { + // console.log("to change: ", toChange); + if (! ambigious) newShapeHoles = betterShapeHoles; + } + } + + var tmpHoles, j, jl; + for ( i = 0, il = newShapes.length; i < il; i ++ ) { + tmpShape = newShapes[i].s; + shapes.push( tmpShape ); + tmpHoles = newShapeHoles[i]; + for ( j = 0, jl = tmpHoles.length; j < jl; j ++ ) { + tmpShape.holes.push( tmpHoles[j].h ); + } + } + + //console.log("shape", shapes); + + return shapes; + +}; + +// File:src/extras/core/Shape.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * Defines a 2d shape plane using paths. + **/ + +// STEP 1 Create a path. +// STEP 2 Turn path into shape. +// STEP 3 ExtrudeGeometry takes in Shape/Shapes +// STEP 3a - Extract points from each shape, turn to vertices +// STEP 3b - Triangulate each shape, add faces. + +THREE.Shape = function () { + + THREE.Path.apply( this, arguments ); + this.holes = []; + +}; + +THREE.Shape.prototype = Object.create( THREE.Path.prototype ); + +// Convenience method to return ExtrudeGeometry + +THREE.Shape.prototype.extrude = function ( options ) { + + var extruded = new THREE.ExtrudeGeometry( this, options ); + return extruded; + +}; + +// Convenience method to return ShapeGeometry + +THREE.Shape.prototype.makeGeometry = function ( options ) { + + var geometry = new THREE.ShapeGeometry( this, options ); + return geometry; + +}; + +// Get points of holes + +THREE.Shape.prototype.getPointsHoles = function ( divisions ) { + + var i, il = this.holes.length, holesPts = []; + + for ( i = 0; i < il; i ++ ) { + + holesPts[ i ] = this.holes[ i ].getTransformedPoints( divisions, this.bends ); + + } + + return holesPts; + +}; + +// Get points of holes (spaced by regular distance) + +THREE.Shape.prototype.getSpacedPointsHoles = function ( divisions ) { + + var i, il = this.holes.length, holesPts = []; + + for ( i = 0; i < il; i ++ ) { + + holesPts[ i ] = this.holes[ i ].getTransformedSpacedPoints( divisions, this.bends ); + + } + + return holesPts; + +}; + + +// Get points of shape and holes (keypoints based on segments parameter) + +THREE.Shape.prototype.extractAllPoints = function ( divisions ) { + + return { + + shape: this.getTransformedPoints( divisions ), + holes: this.getPointsHoles( divisions ) + + }; + +}; + +THREE.Shape.prototype.extractPoints = function ( divisions ) { + + if (this.useSpacedPoints) { + return this.extractAllSpacedPoints(divisions); + } + + return this.extractAllPoints(divisions); + +}; + +// +// THREE.Shape.prototype.extractAllPointsWithBend = function ( divisions, bend ) { +// +// return { +// +// shape: this.transform( bend, divisions ), +// holes: this.getPointsHoles( divisions, bend ) +// +// }; +// +// }; + +// Get points of shape and holes (spaced by regular distance) + +THREE.Shape.prototype.extractAllSpacedPoints = function ( divisions ) { + + return { + + shape: this.getTransformedSpacedPoints( divisions ), + holes: this.getSpacedPointsHoles( divisions ) + + }; + +}; + +/************************************************************** + * Utils + **************************************************************/ + +THREE.Shape.Utils = { + + triangulateShape: function ( contour, holes ) { + + function point_in_segment_2D_colin( inSegPt1, inSegPt2, inOtherPt ) { + // inOtherPt needs to be colinear to the inSegment + if ( inSegPt1.x != inSegPt2.x ) { + if ( inSegPt1.x < inSegPt2.x ) { + return ( ( inSegPt1.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt2.x ) ); + } else { + return ( ( inSegPt2.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt1.x ) ); + } + } else { + if ( inSegPt1.y < inSegPt2.y ) { + return ( ( inSegPt1.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt2.y ) ); + } else { + return ( ( inSegPt2.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt1.y ) ); + } + } + } + + function intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2Pt2, inExcludeAdjacentSegs ) { + var EPSILON = 0.0000000001; + + var seg1dx = inSeg1Pt2.x - inSeg1Pt1.x, seg1dy = inSeg1Pt2.y - inSeg1Pt1.y; + var seg2dx = inSeg2Pt2.x - inSeg2Pt1.x, seg2dy = inSeg2Pt2.y - inSeg2Pt1.y; + + var seg1seg2dx = inSeg1Pt1.x - inSeg2Pt1.x; + var seg1seg2dy = inSeg1Pt1.y - inSeg2Pt1.y; + + var limit = seg1dy * seg2dx - seg1dx * seg2dy; + var perpSeg1 = seg1dy * seg1seg2dx - seg1dx * seg1seg2dy; + + if ( Math.abs(limit) > EPSILON ) { // not parallel + + var perpSeg2; + if ( limit > 0 ) { + if ( ( perpSeg1 < 0 ) || ( perpSeg1 > limit ) ) return []; + perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; + if ( ( perpSeg2 < 0 ) || ( perpSeg2 > limit ) ) return []; + } else { + if ( ( perpSeg1 > 0 ) || ( perpSeg1 < limit ) ) return []; + perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; + if ( ( perpSeg2 > 0 ) || ( perpSeg2 < limit ) ) return []; + } + + // i.e. to reduce rounding errors + // intersection at endpoint of segment#1? + if ( perpSeg2 == 0 ) { + if ( ( inExcludeAdjacentSegs ) && + ( ( perpSeg1 == 0 ) || ( perpSeg1 == limit ) ) ) return []; + return [ inSeg1Pt1 ]; + } + if ( perpSeg2 == limit ) { + if ( ( inExcludeAdjacentSegs ) && + ( ( perpSeg1 == 0 ) || ( perpSeg1 == limit ) ) ) return []; + return [ inSeg1Pt2 ]; + } + // intersection at endpoint of segment#2? + if ( perpSeg1 == 0 ) return [ inSeg2Pt1 ]; + if ( perpSeg1 == limit ) return [ inSeg2Pt2 ]; + + // return real intersection point + var factorSeg1 = perpSeg2 / limit; + return [ { x: inSeg1Pt1.x + factorSeg1 * seg1dx, + y: inSeg1Pt1.y + factorSeg1 * seg1dy } ]; + + } else { // parallel or colinear + if ( ( perpSeg1 != 0 ) || + ( seg2dy * seg1seg2dx != seg2dx * seg1seg2dy ) ) return []; + + // they are collinear or degenerate + var seg1Pt = ( (seg1dx == 0) && (seg1dy == 0) ); // segment1 ist just a point? + var seg2Pt = ( (seg2dx == 0) && (seg2dy == 0) ); // segment2 ist just a point? + // both segments are points + if ( seg1Pt && seg2Pt ) { + if ( (inSeg1Pt1.x != inSeg2Pt1.x) || + (inSeg1Pt1.y != inSeg2Pt1.y) ) return []; // they are distinct points + return [ inSeg1Pt1 ]; // they are the same point + } + // segment#1 is a single point + if ( seg1Pt ) { + if (! point_in_segment_2D_colin( inSeg2Pt1, inSeg2Pt2, inSeg1Pt1 ) ) return []; // but not in segment#2 + return [ inSeg1Pt1 ]; + } + // segment#2 is a single point + if ( seg2Pt ) { + if (! point_in_segment_2D_colin( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1 ) ) return []; // but not in segment#1 + return [ inSeg2Pt1 ]; + } + + // they are collinear segments, which might overlap + var seg1min, seg1max, seg1minVal, seg1maxVal; + var seg2min, seg2max, seg2minVal, seg2maxVal; + if (seg1dx != 0) { // the segments are NOT on a vertical line + if ( inSeg1Pt1.x < inSeg1Pt2.x ) { + seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.x; + seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.x; + } else { + seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.x; + seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.x; + } + if ( inSeg2Pt1.x < inSeg2Pt2.x ) { + seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.x; + seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.x; + } else { + seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.x; + seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.x; + } + } else { // the segments are on a vertical line + if ( inSeg1Pt1.y < inSeg1Pt2.y ) { + seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.y; + seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.y; + } else { + seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.y; + seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.y; + } + if ( inSeg2Pt1.y < inSeg2Pt2.y ) { + seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.y; + seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.y; + } else { + seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.y; + seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.y; + } + } + if ( seg1minVal <= seg2minVal ) { + if ( seg1maxVal < seg2minVal ) return []; + if ( seg1maxVal == seg2minVal ) { + if ( inExcludeAdjacentSegs ) return []; + return [ seg2min ]; + } + if ( seg1maxVal <= seg2maxVal ) return [ seg2min, seg1max ]; + return [ seg2min, seg2max ]; + } else { + if ( seg1minVal > seg2maxVal ) return []; + if ( seg1minVal == seg2maxVal ) { + if ( inExcludeAdjacentSegs ) return []; + return [ seg1min ]; + } + if ( seg1maxVal <= seg2maxVal ) return [ seg1min, seg1max ]; + return [ seg1min, seg2max ]; + } + } + } + + function isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt ) { + // The order of legs is important + + var EPSILON = 0.0000000001; + + // translation of all points, so that Vertex is at (0,0) + var legFromPtX = inLegFromPt.x - inVertex.x, legFromPtY = inLegFromPt.y - inVertex.y; + var legToPtX = inLegToPt.x - inVertex.x, legToPtY = inLegToPt.y - inVertex.y; + var otherPtX = inOtherPt.x - inVertex.x, otherPtY = inOtherPt.y - inVertex.y; + + // main angle >0: < 180 deg.; 0: 180 deg.; <0: > 180 deg. + var from2toAngle = legFromPtX * legToPtY - legFromPtY * legToPtX; + var from2otherAngle = legFromPtX * otherPtY - legFromPtY * otherPtX; + + if ( Math.abs(from2toAngle) > EPSILON ) { // angle != 180 deg. + + var other2toAngle = otherPtX * legToPtY - otherPtY * legToPtX; + // console.log( "from2to: " + from2toAngle + ", from2other: " + from2otherAngle + ", other2to: " + other2toAngle ); + + if ( from2toAngle > 0 ) { // main angle < 180 deg. + return ( ( from2otherAngle >= 0 ) && ( other2toAngle >= 0 ) ); + } else { // main angle > 180 deg. + return ( ( from2otherAngle >= 0 ) || ( other2toAngle >= 0 ) ); + } + } else { // angle == 180 deg. + // console.log( "from2to: 180 deg., from2other: " + from2otherAngle ); + return ( from2otherAngle > 0 ); + } + } + + + function removeHoles( contour, holes ) { + + var shape = contour.concat(); // work on this shape + var hole; + + function isCutLineInsideAngles( inShapeIdx, inHoleIdx ) { + // Check if hole point lies within angle around shape point + var lastShapeIdx = shape.length - 1; + + var prevShapeIdx = inShapeIdx - 1; + if ( prevShapeIdx < 0 ) prevShapeIdx = lastShapeIdx; + + var nextShapeIdx = inShapeIdx + 1; + if ( nextShapeIdx > lastShapeIdx ) nextShapeIdx = 0; + + var insideAngle = isPointInsideAngle( shape[inShapeIdx], shape[ prevShapeIdx ], shape[ nextShapeIdx ], hole[inHoleIdx] ); + if (! insideAngle ) { + // console.log( "Vertex (Shape): " + inShapeIdx + ", Point: " + hole[inHoleIdx].x + "/" + hole[inHoleIdx].y ); + return false; + } + + // Check if shape point lies within angle around hole point + var lastHoleIdx = hole.length - 1; + + var prevHoleIdx = inHoleIdx - 1; + if ( prevHoleIdx < 0 ) prevHoleIdx = lastHoleIdx; + + var nextHoleIdx = inHoleIdx + 1; + if ( nextHoleIdx > lastHoleIdx ) nextHoleIdx = 0; + + insideAngle = isPointInsideAngle( hole[inHoleIdx], hole[ prevHoleIdx ], hole[ nextHoleIdx ], shape[inShapeIdx] ); + if (! insideAngle ) { + // console.log( "Vertex (Hole): " + inHoleIdx + ", Point: " + shape[inShapeIdx].x + "/" + shape[inShapeIdx].y ); + return false; + } + + return true; + } + + function intersectsShapeEdge( inShapePt, inHolePt ) { + // checks for intersections with shape edges + var sIdx, nextIdx, intersection; + for ( sIdx = 0; sIdx < shape.length; sIdx ++ ) { + nextIdx = sIdx+1; nextIdx %= shape.length; + intersection = intersect_segments_2D( inShapePt, inHolePt, shape[sIdx], shape[nextIdx], true ); + if ( intersection.length > 0 ) return true; + } + + return false; + } + + var indepHoles = []; + + function intersectsHoleEdge( inShapePt, inHolePt ) { + // checks for intersections with hole edges + var ihIdx, chkHole, + hIdx, nextIdx, intersection; + for ( ihIdx = 0; ihIdx < indepHoles.length; ihIdx ++ ) { + chkHole = holes[indepHoles[ihIdx]]; + for ( hIdx = 0; hIdx < chkHole.length; hIdx ++ ) { + nextIdx = hIdx+1; nextIdx %= chkHole.length; + intersection = intersect_segments_2D( inShapePt, inHolePt, chkHole[hIdx], chkHole[nextIdx], true ); + if ( intersection.length > 0 ) return true; + } + } + return false; + } + + var holeIndex, shapeIndex, + shapePt, holePt, + holeIdx, cutKey, failedCuts = [], + tmpShape1, tmpShape2, + tmpHole1, tmpHole2; + + for ( var h = 0, hl = holes.length; h < hl; h ++ ) { + + indepHoles.push( h ); + + } + + var minShapeIndex = 0; + var counter = indepHoles.length * 2; + while ( indepHoles.length > 0 ) { + counter --; + if ( counter < 0 ) { + console.log( "Infinite Loop! Holes left:" + indepHoles.length + ", Probably Hole outside Shape!" ); + break; + } + + // search for shape-vertex and hole-vertex, + // which can be connected without intersections + for ( shapeIndex = minShapeIndex; shapeIndex < shape.length; shapeIndex ++ ) { + + shapePt = shape[ shapeIndex ]; + holeIndex = - 1; + + // search for hole which can be reached without intersections + for ( var h = 0; h < indepHoles.length; h ++ ) { + holeIdx = indepHoles[h]; + + // prevent multiple checks + cutKey = shapePt.x + ":" + shapePt.y + ":" + holeIdx; + if ( failedCuts[cutKey] !== undefined ) continue; + + hole = holes[holeIdx]; + for ( var h2 = 0; h2 < hole.length; h2 ++ ) { + holePt = hole[ h2 ]; + if (! isCutLineInsideAngles( shapeIndex, h2 ) ) continue; + if ( intersectsShapeEdge( shapePt, holePt ) ) continue; + if ( intersectsHoleEdge( shapePt, holePt ) ) continue; + + holeIndex = h2; + indepHoles.splice(h,1); + + tmpShape1 = shape.slice( 0, shapeIndex+1 ); + tmpShape2 = shape.slice( shapeIndex ); + tmpHole1 = hole.slice( holeIndex ); + tmpHole2 = hole.slice( 0, holeIndex+1 ); + + shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 ); + + minShapeIndex = shapeIndex; + + // Debug only, to show the selected cuts + // glob_CutLines.push( [ shapePt, holePt ] ); + + break; + } + if ( holeIndex >= 0 ) break; // hole-vertex found + + failedCuts[cutKey] = true; // remember failure + } + if ( holeIndex >= 0 ) break; // hole-vertex found + } + } + + return shape; /* shape with no holes */ + } + + + var i, il, f, face, + key, index, + allPointsMap = {}; + + // To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first. + + var allpoints = contour.concat(); + + for ( var h = 0, hl = holes.length; h < hl; h ++ ) { + + Array.prototype.push.apply( allpoints, holes[h] ); + + } + + //console.log( "allpoints",allpoints, allpoints.length ); + + // prepare all points map + + for ( i = 0, il = allpoints.length; i < il; i ++ ) { + + key = allpoints[ i ].x + ":" + allpoints[ i ].y; + + if ( allPointsMap[ key ] !== undefined ) { + + console.log( "Duplicate point", key ); + + } + + allPointsMap[ key ] = i; + + } + + // remove holes by cutting paths to holes and adding them to the shape + var shapeWithoutHoles = removeHoles( contour, holes ); + + var triangles = THREE.FontUtils.Triangulate( shapeWithoutHoles, false ); // True returns indices for points of spooled shape + //console.log( "triangles",triangles, triangles.length ); + + // check all face vertices against all points map + + for ( i = 0, il = triangles.length; i < il; i ++ ) { + + face = triangles[ i ]; + + for ( f = 0; f < 3; f ++ ) { + + key = face[ f ].x + ":" + face[ f ].y; + + index = allPointsMap[ key ]; + + if ( index !== undefined ) { + + face[ f ] = index; + + } + + } + + } + + return triangles.concat(); + + }, + + isClockWise: function ( pts ) { + + return THREE.FontUtils.Triangulate.area( pts ) < 0; + + }, + + // Bezier Curves formulas obtained from + // http://en.wikipedia.org/wiki/B%C3%A9zier_curve + + // Quad Bezier Functions + + b2p0: function ( t, p ) { + + var k = 1 - t; + return k * k * p; + + }, + + b2p1: function ( t, p ) { + + return 2 * ( 1 - t ) * t * p; + + }, + + b2p2: function ( t, p ) { + + return t * t * p; + + }, + + b2: function ( t, p0, p1, p2 ) { + + return this.b2p0( t, p0 ) + this.b2p1( t, p1 ) + this.b2p2( t, p2 ); + + }, + + // Cubic Bezier Functions + + b3p0: function ( t, p ) { + + var k = 1 - t; + return k * k * k * p; + + }, + + b3p1: function ( t, p ) { + + var k = 1 - t; + return 3 * k * k * t * p; + + }, + + b3p2: function ( t, p ) { + + var k = 1 - t; + return 3 * k * t * t * p; + + }, + + b3p3: function ( t, p ) { + + return t * t * t * p; + + }, + + b3: function ( t, p0, p1, p2, p3 ) { + + return this.b3p0( t, p0 ) + this.b3p1( t, p1 ) + this.b3p2( t, p2 ) + this.b3p3( t, p3 ); + + } + +}; + + +// File:src/extras/curves/LineCurve.js + +/************************************************************** + * Line + **************************************************************/ + +THREE.LineCurve = function ( v1, v2 ) { + + this.v1 = v1; + this.v2 = v2; + +}; + +THREE.LineCurve.prototype = Object.create( THREE.Curve.prototype ); + +THREE.LineCurve.prototype.getPoint = function ( t ) { + + var point = this.v2.clone().sub(this.v1); + point.multiplyScalar( t ).add( this.v1 ); + + return point; + +}; + +// Line curve is linear, so we can overwrite default getPointAt + +THREE.LineCurve.prototype.getPointAt = function ( u ) { + + return this.getPoint( u ); + +}; + +THREE.LineCurve.prototype.getTangent = function( t ) { + + var tangent = this.v2.clone().sub(this.v1); + + return tangent.normalize(); + +}; + +// File:src/extras/curves/QuadraticBezierCurve.js + +/************************************************************** + * Quadratic Bezier curve + **************************************************************/ + + +THREE.QuadraticBezierCurve = function ( v0, v1, v2 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + +}; + +THREE.QuadraticBezierCurve.prototype = Object.create( THREE.Curve.prototype ); + + +THREE.QuadraticBezierCurve.prototype.getPoint = function ( t ) { + + var tx, ty; + + tx = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x ); + ty = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y ); + + return new THREE.Vector2( tx, ty ); + +}; + + +THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) { + + var tx, ty; + + tx = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.x, this.v1.x, this.v2.x ); + ty = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.y, this.v1.y, this.v2.y ); + + // returns unit vector + + var tangent = new THREE.Vector2( tx, ty ); + tangent.normalize(); + + return tangent; + +}; + +// File:src/extras/curves/CubicBezierCurve.js + +/************************************************************** + * Cubic Bezier curve + **************************************************************/ + +THREE.CubicBezierCurve = function ( v0, v1, v2, v3 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; + +}; + +THREE.CubicBezierCurve.prototype = Object.create( THREE.Curve.prototype ); + +THREE.CubicBezierCurve.prototype.getPoint = function ( t ) { + + var tx, ty; + + tx = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ); + ty = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ); + + return new THREE.Vector2( tx, ty ); + +}; + +THREE.CubicBezierCurve.prototype.getTangent = function( t ) { + + var tx, ty; + + tx = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ); + ty = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ); + + var tangent = new THREE.Vector2( tx, ty ); + tangent.normalize(); + + return tangent; + +}; + +// File:src/extras/curves/SplineCurve.js + +/************************************************************** + * Spline curve + **************************************************************/ + +THREE.SplineCurve = function ( points /* array of Vector2 */ ) { + + this.points = (points == undefined) ? [] : points; + +}; + +THREE.SplineCurve.prototype = Object.create( THREE.Curve.prototype ); + +THREE.SplineCurve.prototype.getPoint = function ( t ) { + + var v = new THREE.Vector2(); + var c = []; + var points = this.points, point, intPoint, weight; + point = ( points.length - 1 ) * t; + + intPoint = Math.floor( point ); + weight = point - intPoint; + + c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1; + c[ 1 ] = intPoint; + c[ 2 ] = intPoint > points.length - 2 ? points.length -1 : intPoint + 1; + c[ 3 ] = intPoint > points.length - 3 ? points.length -1 : intPoint + 2; + + v.x = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].x, points[ c[ 1 ] ].x, points[ c[ 2 ] ].x, points[ c[ 3 ] ].x, weight ); + v.y = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].y, points[ c[ 1 ] ].y, points[ c[ 2 ] ].y, points[ c[ 3 ] ].y, weight ); + + return v; + +}; + +// File:src/extras/curves/EllipseCurve.js + +/************************************************************** + * Ellipse curve + **************************************************************/ + +THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise ) { + + this.aX = aX; + this.aY = aY; + + this.xRadius = xRadius; + this.yRadius = yRadius; + + this.aStartAngle = aStartAngle; + this.aEndAngle = aEndAngle; + + this.aClockwise = aClockwise; + +}; + +THREE.EllipseCurve.prototype = Object.create( THREE.Curve.prototype ); + +THREE.EllipseCurve.prototype.getPoint = function ( t ) { + + var angle; + var deltaAngle = this.aEndAngle - this.aStartAngle; + + if ( deltaAngle < 0 ) deltaAngle += Math.PI * 2; + if ( deltaAngle > Math.PI * 2 ) deltaAngle -= Math.PI * 2; + + if ( this.aClockwise === true ) { + + angle = this.aEndAngle + ( 1 - t ) * ( Math.PI * 2 - deltaAngle ); + + } else { + + angle = this.aStartAngle + t * deltaAngle; + + } + + var tx = this.aX + this.xRadius * Math.cos( angle ); + var ty = this.aY + this.yRadius * Math.sin( angle ); + + return new THREE.Vector2( tx, ty ); + +}; + +// File:src/extras/curves/ArcCurve.js + +/************************************************************** + * Arc curve + **************************************************************/ + +THREE.ArcCurve = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + + THREE.EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); +}; + +THREE.ArcCurve.prototype = Object.create( THREE.EllipseCurve.prototype ); + +// File:src/extras/curves/LineCurve3.js + +/************************************************************** + * Line3D + **************************************************************/ + +THREE.LineCurve3 = THREE.Curve.create( + + function ( v1, v2 ) { + + this.v1 = v1; + this.v2 = v2; + + }, + + function ( t ) { + + var r = new THREE.Vector3(); + + + r.subVectors( this.v2, this.v1 ); // diff + r.multiplyScalar( t ); + r.add( this.v1 ); + + return r; + + } + +); + +// File:src/extras/curves/QuadraticBezierCurve3.js + +/************************************************************** + * Quadratic Bezier 3D curve + **************************************************************/ + +THREE.QuadraticBezierCurve3 = THREE.Curve.create( + + function ( v0, v1, v2 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + + }, + + function ( t ) { + + var tx, ty, tz; + + tx = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x ); + ty = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y ); + tz = THREE.Shape.Utils.b2( t, this.v0.z, this.v1.z, this.v2.z ); + + return new THREE.Vector3( tx, ty, tz ); + + } + +); + +// File:src/extras/curves/CubicBezierCurve3.js + +/************************************************************** + * Cubic Bezier 3D curve + **************************************************************/ + +THREE.CubicBezierCurve3 = THREE.Curve.create( + + function ( v0, v1, v2, v3 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; + + }, + + function ( t ) { + + var tx, ty, tz; + + tx = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ); + ty = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ); + tz = THREE.Shape.Utils.b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z ); + + return new THREE.Vector3( tx, ty, tz ); + + } + +); + +// File:src/extras/curves/SplineCurve3.js + +/************************************************************** + * Spline 3D curve + **************************************************************/ + + +THREE.SplineCurve3 = THREE.Curve.create( + + function ( points /* array of Vector3 */) { + + this.points = (points == undefined) ? [] : points; + + }, + + function ( t ) { + + var v = new THREE.Vector3(); + var c = []; + var points = this.points, point, intPoint, weight; + point = ( points.length - 1 ) * t; + + intPoint = Math.floor( point ); + weight = point - intPoint; + + c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1; + c[ 1 ] = intPoint; + c[ 2 ] = intPoint > points.length - 2 ? points.length - 1 : intPoint + 1; + c[ 3 ] = intPoint > points.length - 3 ? points.length - 1 : intPoint + 2; + + var pt0 = points[ c[0] ], + pt1 = points[ c[1] ], + pt2 = points[ c[2] ], + pt3 = points[ c[3] ]; + + v.x = THREE.Curve.Utils.interpolate(pt0.x, pt1.x, pt2.x, pt3.x, weight); + v.y = THREE.Curve.Utils.interpolate(pt0.y, pt1.y, pt2.y, pt3.y, weight); + v.z = THREE.Curve.Utils.interpolate(pt0.z, pt1.z, pt2.z, pt3.z, weight); + + return v; + + } + +); + + +// THREE.SplineCurve3.prototype.getTangent = function(t) { +// var v = new THREE.Vector3(); +// var c = []; +// var points = this.points, point, intPoint, weight; +// point = ( points.length - 1 ) * t; + +// intPoint = Math.floor( point ); +// weight = point - intPoint; + +// c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1; +// c[ 1 ] = intPoint; +// c[ 2 ] = intPoint > points.length - 2 ? points.length - 1 : intPoint + 1; +// c[ 3 ] = intPoint > points.length - 3 ? points.length - 1 : intPoint + 2; + +// var pt0 = points[ c[0] ], +// pt1 = points[ c[1] ], +// pt2 = points[ c[2] ], +// pt3 = points[ c[3] ]; + +// // t = weight; +// v.x = THREE.Curve.Utils.tangentSpline( t, pt0.x, pt1.x, pt2.x, pt3.x ); +// v.y = THREE.Curve.Utils.tangentSpline( t, pt0.y, pt1.y, pt2.y, pt3.y ); +// v.z = THREE.Curve.Utils.tangentSpline( t, pt0.z, pt1.z, pt2.z, pt3.z ); + +// return v; + +// } + +// File:src/extras/curves/ClosedSplineCurve3.js + +/************************************************************** + * Closed Spline 3D curve + **************************************************************/ + + +THREE.ClosedSplineCurve3 = THREE.Curve.create( + + function ( points /* array of Vector3 */) { + + this.points = (points == undefined) ? [] : points; + + }, + + function ( t ) { + + var v = new THREE.Vector3(); + var c = []; + var points = this.points, point, intPoint, weight; + point = ( points.length - 0 ) * t; + // This needs to be from 0-length +1 + + intPoint = Math.floor( point ); + weight = point - intPoint; + + intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length; + c[ 0 ] = ( intPoint - 1 ) % points.length; + c[ 1 ] = ( intPoint ) % points.length; + c[ 2 ] = ( intPoint + 1 ) % points.length; + c[ 3 ] = ( intPoint + 2 ) % points.length; + + v.x = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].x, points[ c[ 1 ] ].x, points[ c[ 2 ] ].x, points[ c[ 3 ] ].x, weight ); + v.y = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].y, points[ c[ 1 ] ].y, points[ c[ 2 ] ].y, points[ c[ 3 ] ].y, weight ); + v.z = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].z, points[ c[ 1 ] ].z, points[ c[ 2 ] ].z, points[ c[ 3 ] ].z, weight ); + + return v; + + } + +); + +// File:src/extras/animation/AnimationHandler.js + +/** + * @author mikael emtinger / http://gomo.se/ + */ + +THREE.AnimationHandler = { + + LINEAR: 0, + CATMULLROM: 1, + CATMULLROM_FORWARD: 2, + + // + + add: function () { console.warn( 'THREE.AnimationHandler.add() has been deprecated.' ); }, + get: function () { console.warn( 'THREE.AnimationHandler.get() has been deprecated.' ); }, + remove: function () { console.warn( 'THREE.AnimationHandler.remove() has been deprecated.' ); }, + + // + + animations: [], + + init: function ( data ) { + + if ( data.initialized === true ) return; + + // loop through all keys + + for ( var h = 0; h < data.hierarchy.length; h ++ ) { + + for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + + // remove minus times + + if ( data.hierarchy[ h ].keys[ k ].time < 0 ) { + + data.hierarchy[ h ].keys[ k ].time = 0; + + } + + // create quaternions + + if ( data.hierarchy[ h ].keys[ k ].rot !== undefined && + ! ( data.hierarchy[ h ].keys[ k ].rot instanceof THREE.Quaternion ) ) { + + var quat = data.hierarchy[ h ].keys[ k ].rot; + data.hierarchy[ h ].keys[ k ].rot = new THREE.Quaternion().fromArray( quat ); + + } + + } + + // prepare morph target keys + + if ( data.hierarchy[ h ].keys.length && data.hierarchy[ h ].keys[ 0 ].morphTargets !== undefined ) { + + // get all used + + var usedMorphTargets = {}; + + for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + + for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) { + + var morphTargetName = data.hierarchy[ h ].keys[ k ].morphTargets[ m ]; + usedMorphTargets[ morphTargetName ] = - 1; + + } + + } + + data.hierarchy[ h ].usedMorphTargets = usedMorphTargets; + + + // set all used on all frames + + for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + + var influences = {}; + + for ( var morphTargetName in usedMorphTargets ) { + + for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) { + + if ( data.hierarchy[ h ].keys[ k ].morphTargets[ m ] === morphTargetName ) { + + influences[ morphTargetName ] = data.hierarchy[ h ].keys[ k ].morphTargetsInfluences[ m ]; + break; + + } + + } + + if ( m === data.hierarchy[ h ].keys[ k ].morphTargets.length ) { + + influences[ morphTargetName ] = 0; + + } + + } + + data.hierarchy[ h ].keys[ k ].morphTargetsInfluences = influences; + + } + + } + + + // remove all keys that are on the same time + + for ( var k = 1; k < data.hierarchy[ h ].keys.length; k ++ ) { + + if ( data.hierarchy[ h ].keys[ k ].time === data.hierarchy[ h ].keys[ k - 1 ].time ) { + + data.hierarchy[ h ].keys.splice( k, 1 ); + k --; + + } + + } + + + // set index + + for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + + data.hierarchy[ h ].keys[ k ].index = k; + + } + + } + + data.initialized = true; + + return data; + + }, + + parse: function ( root ) { + + var parseRecurseHierarchy = function ( root, hierarchy ) { + + hierarchy.push( root ); + + for ( var c = 0; c < root.children.length; c ++ ) + parseRecurseHierarchy( root.children[ c ], hierarchy ); + + }; + + // setup hierarchy + + var hierarchy = []; + + if ( root instanceof THREE.SkinnedMesh ) { + + for ( var b = 0; b < root.skeleton.bones.length; b ++ ) { + + hierarchy.push( root.skeleton.bones[ b ] ); + + } + + } else { + + parseRecurseHierarchy( root, hierarchy ); + + } + + return hierarchy; + + }, + + play: function ( animation ) { + + if ( this.animations.indexOf( animation ) === - 1 ) { + + this.animations.push( animation ); + + } + + }, + + stop: function ( animation ) { + + var index = this.animations.indexOf( animation ); + + if ( index !== - 1 ) { + + this.animations.splice( index, 1 ); + + } + + }, + + update: function ( deltaTimeMS ) { + + for ( var i = 0; i < this.animations.length; i ++ ) { + + this.animations[ i ].update( deltaTimeMS ); + + } + + } + +}; + +// File:src/extras/animation/Animation.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Animation = function ( root, data ) { + + this.root = root; + this.data = THREE.AnimationHandler.init( data ); + this.hierarchy = THREE.AnimationHandler.parse( root ); + + this.currentTime = 0; + this.timeScale = 1; + + this.isPlaying = false; + this.loop = true; + this.weight = 0; + + this.interpolationType = THREE.AnimationHandler.LINEAR; + +}; + + +THREE.Animation.prototype.keyTypes = [ "pos", "rot", "scl" ]; + + +THREE.Animation.prototype.play = function ( startTime, weight ) { + + this.currentTime = startTime !== undefined ? startTime : 0; + this.weight = weight !== undefined ? weight: 1; + + this.isPlaying = true; + + this.reset(); + + THREE.AnimationHandler.play( this ); + +}; + + +THREE.Animation.prototype.stop = function() { + + this.isPlaying = false; + + THREE.AnimationHandler.stop( this ); + +}; + +THREE.Animation.prototype.reset = function () { + + for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { + + var object = this.hierarchy[ h ]; + + object.matrixAutoUpdate = true; + + if ( object.animationCache === undefined ) { + + object.animationCache = {}; + + } + + if ( object.animationCache[this.data.name] === undefined ) { + + object.animationCache[this.data.name] = {}; + object.animationCache[this.data.name].prevKey = { pos: 0, rot: 0, scl: 0 }; + object.animationCache[this.data.name].nextKey = { pos: 0, rot: 0, scl: 0 }; + object.animationCache[this.data.name].originalMatrix = object.matrix; + + } + + var animationCache = object.animationCache[this.data.name]; + + // Get keys to match our current time + + for ( var t = 0; t < 3; t ++ ) { + + var type = this.keyTypes[ t ]; + + var prevKey = this.data.hierarchy[ h ].keys[ 0 ]; + var nextKey = this.getNextKeyWith( type, h, 1 ); + + while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) { + + prevKey = nextKey; + nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 ); + + } + + animationCache.prevKey[ type ] = prevKey; + animationCache.nextKey[ type ] = nextKey; + + } + + } + +}; + + +THREE.Animation.prototype.update = (function(){ + + var points = []; + var target = new THREE.Vector3(); + var newVector = new THREE.Vector3(); + var newQuat = new THREE.Quaternion(); + + // Catmull-Rom spline + + var interpolateCatmullRom = function ( points, scale ) { + + var c = [], v3 = [], + point, intPoint, weight, w2, w3, + pa, pb, pc, pd; + + point = ( points.length - 1 ) * scale; + intPoint = Math.floor( point ); + weight = point - intPoint; + + c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1; + c[ 1 ] = intPoint; + c[ 2 ] = intPoint > points.length - 2 ? intPoint : intPoint + 1; + c[ 3 ] = intPoint > points.length - 3 ? intPoint : intPoint + 2; + + pa = points[ c[ 0 ] ]; + pb = points[ c[ 1 ] ]; + pc = points[ c[ 2 ] ]; + pd = points[ c[ 3 ] ]; + + w2 = weight * weight; + w3 = weight * w2; + + v3[ 0 ] = interpolate( pa[ 0 ], pb[ 0 ], pc[ 0 ], pd[ 0 ], weight, w2, w3 ); + v3[ 1 ] = interpolate( pa[ 1 ], pb[ 1 ], pc[ 1 ], pd[ 1 ], weight, w2, w3 ); + v3[ 2 ] = interpolate( pa[ 2 ], pb[ 2 ], pc[ 2 ], pd[ 2 ], weight, w2, w3 ); + + return v3; + + }; + + var interpolate = function ( p0, p1, p2, p3, t, t2, t3 ) { + + var v0 = ( p2 - p0 ) * 0.5, + v1 = ( p3 - p1 ) * 0.5; + + return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1; + + }; + + return function ( delta ) { + + if ( this.isPlaying === false ) return; + + this.currentTime += delta * this.timeScale; + + if ( this.weight === 0 ) + return; + + // + + var duration = this.data.length; + + if ( this.loop === true && this.currentTime > duration ) { + + this.currentTime %= duration; + this.reset(); + + } else if ( this.loop === false && this.currentTime > duration ) { + + this.stop(); + return; + + } + + for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { + + var object = this.hierarchy[ h ]; + var animationCache = object.animationCache[this.data.name]; + + // loop through pos/rot/scl + + for ( var t = 0; t < 3; t ++ ) { + + // get keys + + var type = this.keyTypes[ t ]; + var prevKey = animationCache.prevKey[ type ]; + var nextKey = animationCache.nextKey[ type ]; + + if ( nextKey.time <= this.currentTime ) { + + prevKey = this.data.hierarchy[ h ].keys[ 0 ]; + nextKey = this.getNextKeyWith( type, h, 1 ); + + while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) { + + prevKey = nextKey; + nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 ); + + } + + animationCache.prevKey[ type ] = prevKey; + animationCache.nextKey[ type ] = nextKey; + + } + + object.matrixAutoUpdate = true; + object.matrixWorldNeedsUpdate = true; + + var scale = ( this.currentTime - prevKey.time ) / ( nextKey.time - prevKey.time ); + + var prevXYZ = prevKey[ type ]; + var nextXYZ = nextKey[ type ]; + + if ( scale < 0 ) scale = 0; + if ( scale > 1 ) scale = 1; + + // interpolate + + if ( type === "pos" ) { + + if ( this.interpolationType === THREE.AnimationHandler.LINEAR ) { + + newVector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale; + newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale; + newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale; + + // blend + if ( object instanceof THREE.Bone ) { + + var proportionalWeight = this.weight / ( this.weight + object.accumulatedPosWeight ); + object.position.lerp( newVector, proportionalWeight ); + object.accumulatedPosWeight += this.weight; + + } else { + + object.position.copy( newVector ); + + } + + } else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || + this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + + points[ 0 ] = this.getPrevKeyWith( "pos", h, prevKey.index - 1 )[ "pos" ]; + points[ 1 ] = prevXYZ; + points[ 2 ] = nextXYZ; + points[ 3 ] = this.getNextKeyWith( "pos", h, nextKey.index + 1 )[ "pos" ]; + + scale = scale * 0.33 + 0.33; + + var currentPoint = interpolateCatmullRom( points, scale ); + var proportionalWeight = 1; + + if ( object instanceof THREE.Bone ) { + + proportionalWeight = this.weight / ( this.weight + object.accumulatedPosWeight ); + object.accumulatedPosWeight += this.weight; + + } + + // blend + + var vector = object.position; + + vector.x = vector.x + ( currentPoint[ 0 ] - vector.x ) * proportionalWeight; + vector.y = vector.y + ( currentPoint[ 1 ] - vector.y ) * proportionalWeight; + vector.z = vector.z + ( currentPoint[ 2 ] - vector.z ) * proportionalWeight; + + if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + + var forwardPoint = interpolateCatmullRom( points, scale * 1.01 ); + + target.set( forwardPoint[ 0 ], forwardPoint[ 1 ], forwardPoint[ 2 ] ); + target.sub( vector ); + target.y = 0; + target.normalize(); + + var angle = Math.atan2( target.x, target.z ); + object.rotation.set( 0, angle, 0 ); + + } + + } + + } else if ( type === "rot" ) { + + THREE.Quaternion.slerp( prevXYZ, nextXYZ, newQuat, scale ); + + // Avoid paying the cost of an additional slerp if we don't have to + if ( ! ( object instanceof THREE.Bone ) ) { + + object.quaternion.copy(newQuat); + + } else if ( object.accumulatedRotWeight === 0 ) { + + object.quaternion.copy(newQuat); + object.accumulatedRotWeight = this.weight; + + } else { + + var proportionalWeight = this.weight / ( this.weight + object.accumulatedRotWeight ); + THREE.Quaternion.slerp( object.quaternion, newQuat, object.quaternion, proportionalWeight ); + object.accumulatedRotWeight += this.weight; + + } + + } else if ( type === "scl" ) { + + newVector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale; + newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale; + newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale; + + if ( object instanceof THREE.Bone ) { + + var proportionalWeight = this.weight / ( this.weight + object.accumulatedSclWeight); + object.scale.lerp( newVector, proportionalWeight ); + object.accumulatedSclWeight += this.weight; + + } else { + + object.scale.copy( newVector ); + + } + + } + + } + + } + + return true; + + }; + +})(); + + + + + +// Get next key with + +THREE.Animation.prototype.getNextKeyWith = function ( type, h, key ) { + + var keys = this.data.hierarchy[ h ].keys; + + if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || + this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + + key = key < keys.length - 1 ? key : keys.length - 1; + + } else { + + key = key % keys.length; + + } + + for ( ; key < keys.length; key ++ ) { + + if ( keys[ key ][ type ] !== undefined ) { + + return keys[ key ]; + + } + + } + + return this.data.hierarchy[ h ].keys[ 0 ]; + +}; + +// Get previous key with + +THREE.Animation.prototype.getPrevKeyWith = function ( type, h, key ) { + + var keys = this.data.hierarchy[ h ].keys; + + if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || + this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + + key = key > 0 ? key : 0; + + } else { + + key = key >= 0 ? key : key + keys.length; + + } + + + for ( ; key >= 0; key -- ) { + + if ( keys[ key ][ type ] !== undefined ) { + + return keys[ key ]; + + } + + } + + return this.data.hierarchy[ h ].keys[ keys.length - 1 ]; + +}; + +// File:src/extras/animation/KeyFrameAnimation.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author khang duong + * @author erik kitson + */ + +THREE.KeyFrameAnimation = function ( data ) { + + this.root = data.node; + this.data = THREE.AnimationHandler.init( data ); + this.hierarchy = THREE.AnimationHandler.parse( this.root ); + this.currentTime = 0; + this.timeScale = 0.001; + this.isPlaying = false; + this.isPaused = true; + this.loop = true; + + // initialize to first keyframes + + for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { + + var keys = this.data.hierarchy[h].keys, + sids = this.data.hierarchy[h].sids, + obj = this.hierarchy[h]; + + if ( keys.length && sids ) { + + for ( var s = 0; s < sids.length; s ++ ) { + + var sid = sids[ s ], + next = this.getNextKeyWith( sid, h, 0 ); + + if ( next ) { + + next.apply( sid ); + + } + + } + + obj.matrixAutoUpdate = false; + this.data.hierarchy[h].node.updateMatrix(); + obj.matrixWorldNeedsUpdate = true; + + } + + } + +}; + + +THREE.KeyFrameAnimation.prototype.play = function ( startTime ) { + + this.currentTime = startTime !== undefined ? startTime : 0; + + if ( this.isPlaying === false ) { + + this.isPlaying = true; + + // reset key cache + + var h, hl = this.hierarchy.length, + object, + node; + + for ( h = 0; h < hl; h ++ ) { + + object = this.hierarchy[ h ]; + node = this.data.hierarchy[ h ]; + + if ( node.animationCache === undefined ) { + + node.animationCache = {}; + node.animationCache.prevKey = null; + node.animationCache.nextKey = null; + node.animationCache.originalMatrix = object.matrix; + + } + + var keys = this.data.hierarchy[h].keys; + + if (keys.length) { + + node.animationCache.prevKey = keys[ 0 ]; + node.animationCache.nextKey = keys[ 1 ]; + + this.startTime = Math.min( keys[0].time, this.startTime ); + this.endTime = Math.max( keys[keys.length - 1].time, this.endTime ); + + } + + } + + this.update( 0 ); + + } + + this.isPaused = false; + + THREE.AnimationHandler.play( this ); + +}; + + +THREE.KeyFrameAnimation.prototype.stop = function() { + + this.isPlaying = false; + this.isPaused = false; + + THREE.AnimationHandler.stop( this ); + + // reset JIT matrix and remove cache + + for ( var h = 0; h < this.data.hierarchy.length; h ++ ) { + + var obj = this.hierarchy[ h ]; + var node = this.data.hierarchy[ h ]; + + if ( node.animationCache !== undefined ) { + + var original = node.animationCache.originalMatrix; + + original.copy( obj.matrix ); + obj.matrix = original; + + delete node.animationCache; + + } + + } + +}; + + +// Update + +THREE.KeyFrameAnimation.prototype.update = function ( delta ) { + + if ( this.isPlaying === false ) return; + + this.currentTime += delta * this.timeScale; + + // + + var duration = this.data.length; + + if ( this.loop === true && this.currentTime > duration ) { + + this.currentTime %= duration; + + } + + this.currentTime = Math.min( this.currentTime, duration ); + + for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { + + var object = this.hierarchy[ h ]; + var node = this.data.hierarchy[ h ]; + + var keys = node.keys, + animationCache = node.animationCache; + + + if ( keys.length ) { + + var prevKey = animationCache.prevKey; + var nextKey = animationCache.nextKey; + + if ( nextKey.time <= this.currentTime ) { + + while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) { + + prevKey = nextKey; + nextKey = keys[ prevKey.index + 1 ]; + + } + + animationCache.prevKey = prevKey; + animationCache.nextKey = nextKey; + + } + + if ( nextKey.time >= this.currentTime ) { + + prevKey.interpolate( nextKey, this.currentTime ); + + } else { + + prevKey.interpolate( nextKey, nextKey.time ); + + } + + this.data.hierarchy[ h ].node.updateMatrix(); + object.matrixWorldNeedsUpdate = true; + + } + + } + +}; + +// Get next key with + +THREE.KeyFrameAnimation.prototype.getNextKeyWith = function( sid, h, key ) { + + var keys = this.data.hierarchy[ h ].keys; + key = key % keys.length; + + for ( ; key < keys.length; key ++ ) { + + if ( keys[ key ].hasTarget( sid ) ) { + + return keys[ key ]; + + } + + } + + return keys[ 0 ]; + +}; + +// Get previous key with + +THREE.KeyFrameAnimation.prototype.getPrevKeyWith = function( sid, h, key ) { + + var keys = this.data.hierarchy[ h ].keys; + key = key >= 0 ? key : key + keys.length; + + for ( ; key >= 0; key -- ) { + + if ( keys[ key ].hasTarget( sid ) ) { + + return keys[ key ]; + + } + + } + + return keys[ keys.length - 1 ]; + +}; + +// File:src/extras/animation/MorphAnimation.js + +/** + * @author mrdoob / http://mrdoob.com + */ + +THREE.MorphAnimation = function ( mesh ) { + + this.mesh = mesh; + this.frames = mesh.morphTargetInfluences.length; + this.currentTime = 0; + this.duration = 1000; + this.loop = true; + + this.isPlaying = false; + +}; + +THREE.MorphAnimation.prototype = { + + play: function () { + + this.isPlaying = true; + + }, + + pause: function () { + + this.isPlaying = false; + + }, + + update: ( function () { + + var lastFrame = 0; + var currentFrame = 0; + + return function ( delta ) { + + if ( this.isPlaying === false ) return; + + this.currentTime += delta; + + if ( this.loop === true && this.currentTime > this.duration ) { + + this.currentTime %= this.duration; + + } + + this.currentTime = Math.min( this.currentTime, this.duration ); + + var interpolation = this.duration / this.frames; + var frame = Math.floor( this.currentTime / interpolation ); + + if ( frame != currentFrame ) { + + this.mesh.morphTargetInfluences[ lastFrame ] = 0; + this.mesh.morphTargetInfluences[ currentFrame ] = 1; + this.mesh.morphTargetInfluences[ frame ] = 0; + + lastFrame = currentFrame; + currentFrame = frame; + + } + + this.mesh.morphTargetInfluences[ frame ] = ( this.currentTime % interpolation ) / interpolation; + this.mesh.morphTargetInfluences[ lastFrame ] = 1 - this.mesh.morphTargetInfluences[ frame ]; + + } + + } )() + +}; + +// File:src/extras/geometries/BoxGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as + */ + +THREE.BoxGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) { + + THREE.Geometry.call( this ); + + this.parameters = { + width: width, + height: height, + depth: depth, + widthSegments: widthSegments, + heightSegments: heightSegments, + depthSegments: depthSegments + }; + + this.widthSegments = widthSegments || 1; + this.heightSegments = heightSegments || 1; + this.depthSegments = depthSegments || 1; + + var scope = this; + + var width_half = width / 2; + var height_half = height / 2; + var depth_half = depth / 2; + + buildPlane( 'z', 'y', - 1, - 1, depth, height, width_half, 0 ); // px + buildPlane( 'z', 'y', 1, - 1, depth, height, - width_half, 1 ); // nx + buildPlane( 'x', 'z', 1, 1, width, depth, height_half, 2 ); // py + buildPlane( 'x', 'z', 1, - 1, width, depth, - height_half, 3 ); // ny + buildPlane( 'x', 'y', 1, - 1, width, height, depth_half, 4 ); // pz + buildPlane( 'x', 'y', - 1, - 1, width, height, - depth_half, 5 ); // nz + + function buildPlane( u, v, udir, vdir, width, height, depth, materialIndex ) { + + var w, ix, iy, + gridX = scope.widthSegments, + gridY = scope.heightSegments, + width_half = width / 2, + height_half = height / 2, + offset = scope.vertices.length; + + if ( ( u === 'x' && v === 'y' ) || ( u === 'y' && v === 'x' ) ) { + + w = 'z'; + + } else if ( ( u === 'x' && v === 'z' ) || ( u === 'z' && v === 'x' ) ) { + + w = 'y'; + gridY = scope.depthSegments; + + } else if ( ( u === 'z' && v === 'y' ) || ( u === 'y' && v === 'z' ) ) { + + w = 'x'; + gridX = scope.depthSegments; + + } + + var gridX1 = gridX + 1, + gridY1 = gridY + 1, + segment_width = width / gridX, + segment_height = height / gridY, + normal = new THREE.Vector3(); + + normal[ w ] = depth > 0 ? 1 : - 1; + + for ( iy = 0; iy < gridY1; iy ++ ) { + + for ( ix = 0; ix < gridX1; ix ++ ) { + + var vector = new THREE.Vector3(); + vector[ u ] = ( ix * segment_width - width_half ) * udir; + vector[ v ] = ( iy * segment_height - height_half ) * vdir; + vector[ w ] = depth; + + scope.vertices.push( vector ); + + } + + } + + for ( iy = 0; iy < gridY; iy ++ ) { + + for ( ix = 0; ix < gridX; ix ++ ) { + + var a = ix + gridX1 * iy; + var b = ix + gridX1 * ( iy + 1 ); + var c = ( ix + 1 ) + gridX1 * ( iy + 1 ); + var d = ( ix + 1 ) + gridX1 * iy; + + var uva = new THREE.Vector2( ix / gridX, 1 - iy / gridY ); + var uvb = new THREE.Vector2( ix / gridX, 1 - ( iy + 1 ) / gridY ); + var uvc = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - ( iy + 1 ) / gridY ); + var uvd = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - iy / gridY ); + + var face = new THREE.Face3( a + offset, b + offset, d + offset ); + face.normal.copy( normal ); + face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() ); + face.materialIndex = materialIndex; + + scope.faces.push( face ); + scope.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + + face = new THREE.Face3( b + offset, c + offset, d + offset ); + face.normal.copy( normal ); + face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() ); + face.materialIndex = materialIndex; + + scope.faces.push( face ); + scope.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + + } + + } + + } + + this.mergeVertices(); + +}; + +THREE.BoxGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +// File:src/extras/geometries/CircleGeometry.js + +/** + * @author hughes + */ + +THREE.CircleGeometry = function ( radius, segments, thetaStart, thetaLength ) { + + THREE.Geometry.call( this ); + + this.parameters = { + radius: radius, + segments: segments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + + radius = radius || 50; + segments = segments !== undefined ? Math.max( 3, segments ) : 8; + + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; + + var i, uvs = [], + center = new THREE.Vector3(), centerUV = new THREE.Vector2( 0.5, 0.5 ); + + this.vertices.push(center); + uvs.push( centerUV ); + + for ( i = 0; i <= segments; i ++ ) { + + var vertex = new THREE.Vector3(); + var segment = thetaStart + i / segments * thetaLength; + + vertex.x = radius * Math.cos( segment ); + vertex.y = radius * Math.sin( segment ); + + this.vertices.push( vertex ); + uvs.push( new THREE.Vector2( ( vertex.x / radius + 1 ) / 2, ( vertex.y / radius + 1 ) / 2 ) ); + + } + + var n = new THREE.Vector3( 0, 0, 1 ); + + for ( i = 1; i <= segments; i ++ ) { + + this.faces.push( new THREE.Face3( i, i + 1, 0, [ n.clone(), n.clone(), n.clone() ] ) ); + this.faceVertexUvs[ 0 ].push( [ uvs[ i ].clone(), uvs[ i + 1 ].clone(), centerUV.clone() ] ); + + } + + this.computeFaceNormals(); + + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + +}; + +THREE.CircleGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +// File:src/extras/geometries/CubeGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + + +THREE.CubeGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) { + + console.warn( 'THEE.CubeGeometry has been renamed to THREE.BoxGeometry.' ); + return new THREE.BoxGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ); + + }; + +// File:src/extras/geometries/CylinderGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded ) { + + THREE.Geometry.call( this ); + + this.parameters = { + radiusTop: radiusTop, + radiusBottom: radiusBottom, + height: height, + radialSegments: radialSegments, + heightSegments: heightSegments, + openEnded: openEnded + }; + + radiusTop = radiusTop !== undefined ? radiusTop : 20; + radiusBottom = radiusBottom !== undefined ? radiusBottom : 20; + height = height !== undefined ? height : 100; + + radialSegments = radialSegments || 8; + heightSegments = heightSegments || 1; + + openEnded = openEnded !== undefined ? openEnded : false; + + var heightHalf = height / 2; + + var x, y, vertices = [], uvs = []; + + for ( y = 0; y <= heightSegments; y ++ ) { + + var verticesRow = []; + var uvsRow = []; + + var v = y / heightSegments; + var radius = v * ( radiusBottom - radiusTop ) + radiusTop; + + for ( x = 0; x <= radialSegments; x ++ ) { + + var u = x / radialSegments; + + var vertex = new THREE.Vector3(); + vertex.x = radius * Math.sin( u * Math.PI * 2 ); + vertex.y = - v * height + heightHalf; + vertex.z = radius * Math.cos( u * Math.PI * 2 ); + + this.vertices.push( vertex ); + + verticesRow.push( this.vertices.length - 1 ); + uvsRow.push( new THREE.Vector2( u, 1 - v ) ); + + } + + vertices.push( verticesRow ); + uvs.push( uvsRow ); + + } + + var tanTheta = ( radiusBottom - radiusTop ) / height; + var na, nb; + + for ( x = 0; x < radialSegments; x ++ ) { + + if ( radiusTop !== 0 ) { + + na = this.vertices[ vertices[ 0 ][ x ] ].clone(); + nb = this.vertices[ vertices[ 0 ][ x + 1 ] ].clone(); + + } else { + + na = this.vertices[ vertices[ 1 ][ x ] ].clone(); + nb = this.vertices[ vertices[ 1 ][ x + 1 ] ].clone(); + + } + + na.setY( Math.sqrt( na.x * na.x + na.z * na.z ) * tanTheta ).normalize(); + nb.setY( Math.sqrt( nb.x * nb.x + nb.z * nb.z ) * tanTheta ).normalize(); + + for ( y = 0; y < heightSegments; y ++ ) { + + var v1 = vertices[ y ][ x ]; + var v2 = vertices[ y + 1 ][ x ]; + var v3 = vertices[ y + 1 ][ x + 1 ]; + var v4 = vertices[ y ][ x + 1 ]; + + var n1 = na.clone(); + var n2 = na.clone(); + var n3 = nb.clone(); + var n4 = nb.clone(); + + var uv1 = uvs[ y ][ x ].clone(); + var uv2 = uvs[ y + 1 ][ x ].clone(); + var uv3 = uvs[ y + 1 ][ x + 1 ].clone(); + var uv4 = uvs[ y ][ x + 1 ].clone(); + + this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] ); + + this.faces.push( new THREE.Face3( v2, v3, v4, [ n2.clone(), n3, n4.clone() ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] ); + + } + + } + + // top cap + + if ( openEnded === false && radiusTop > 0 ) { + + this.vertices.push( new THREE.Vector3( 0, heightHalf, 0 ) ); + + for ( x = 0; x < radialSegments; x ++ ) { + + var v1 = vertices[ 0 ][ x ]; + var v2 = vertices[ 0 ][ x + 1 ]; + var v3 = this.vertices.length - 1; + + var n1 = new THREE.Vector3( 0, 1, 0 ); + var n2 = new THREE.Vector3( 0, 1, 0 ); + var n3 = new THREE.Vector3( 0, 1, 0 ); + + var uv1 = uvs[ 0 ][ x ].clone(); + var uv2 = uvs[ 0 ][ x + 1 ].clone(); + var uv3 = new THREE.Vector2( uv2.x, 0 ); + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); + + } + + } + + // bottom cap + + if ( openEnded === false && radiusBottom > 0 ) { + + this.vertices.push( new THREE.Vector3( 0, - heightHalf, 0 ) ); + + for ( x = 0; x < radialSegments; x ++ ) { + + var v1 = vertices[ y ][ x + 1 ]; + var v2 = vertices[ y ][ x ]; + var v3 = this.vertices.length - 1; + + var n1 = new THREE.Vector3( 0, - 1, 0 ); + var n2 = new THREE.Vector3( 0, - 1, 0 ); + var n3 = new THREE.Vector3( 0, - 1, 0 ); + + var uv1 = uvs[ y ][ x + 1 ].clone(); + var uv2 = uvs[ y ][ x ].clone(); + var uv3 = new THREE.Vector2( uv2.x, 1 ); + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); + + } + + } + + this.computeFaceNormals(); + +} + +THREE.CylinderGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +// File:src/extras/geometries/ExtrudeGeometry.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * + * Creates extruded geometry from a path shape. + * + * parameters = { + * + * curveSegments: , // number of points on the curves + * steps: , // number of points for z-side extrusions / used for subdividing segements of extrude spline too + * amount: , // Depth to extrude the shape + * + * bevelEnabled: , // turn on bevel + * bevelThickness: , // how deep into the original shape bevel goes + * bevelSize: , // how far from shape outline is bevel + * bevelSegments: , // number of bevel layers + * + * extrudePath: // 3d spline path to extrude shape along. (creates Frames if .frames aren't defined) + * frames: // containing arrays of tangents, normals, binormals + * + * material: // material index for front and back faces + * extrudeMaterial: // material index for extrusion and beveled faces + * uvGenerator: // object that provides UV generator functions + * + * } + **/ + +THREE.ExtrudeGeometry = function ( shapes, options ) { + + if ( typeof( shapes ) === "undefined" ) { + shapes = []; + return; + } + + THREE.Geometry.call( this ); + + shapes = shapes instanceof Array ? shapes : [ shapes ]; + + this.addShapeList( shapes, options ); + + this.computeFaceNormals(); + + // can't really use automatic vertex normals + // as then front and back sides get smoothed too + // should do separate smoothing just for sides + + //this.computeVertexNormals(); + + //console.log( "took", ( Date.now() - startTime ) ); + +}; + +THREE.ExtrudeGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +THREE.ExtrudeGeometry.prototype.addShapeList = function ( shapes, options ) { + var sl = shapes.length; + + for ( var s = 0; s < sl; s ++ ) { + var shape = shapes[ s ]; + this.addShape( shape, options ); + } +}; + +THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { + + var amount = options.amount !== undefined ? options.amount : 100; + + var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10 + var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8 + var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; + + var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false + + var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; + + var steps = options.steps !== undefined ? options.steps : 1; + + var extrudePath = options.extrudePath; + var extrudePts, extrudeByPath = false; + + var material = options.material; + var extrudeMaterial = options.extrudeMaterial; + + // Use default WorldUVGenerator if no UV generators are specified. + var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : THREE.ExtrudeGeometry.WorldUVGenerator; + + var splineTube, binormal, normal, position2; + if ( extrudePath ) { + + extrudePts = extrudePath.getSpacedPoints( steps ); + + extrudeByPath = true; + bevelEnabled = false; // bevels not supported for path extrusion + + // SETUP TNB variables + + // Reuse TNB from TubeGeomtry for now. + // TODO1 - have a .isClosed in spline? + + splineTube = options.frames !== undefined ? options.frames : new THREE.TubeGeometry.FrenetFrames(extrudePath, steps, false); + + // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); + + binormal = new THREE.Vector3(); + normal = new THREE.Vector3(); + position2 = new THREE.Vector3(); + + } + + // Safeguards if bevels are not enabled + + if ( ! bevelEnabled ) { + + bevelSegments = 0; + bevelThickness = 0; + bevelSize = 0; + + } + + // Variables initalization + + var ahole, h, hl; // looping of holes + var scope = this; + var bevelPoints = []; + + var shapesOffset = this.vertices.length; + + var shapePoints = shape.extractPoints( curveSegments ); + + var vertices = shapePoints.shape; + var holes = shapePoints.holes; + + var reverse = ! THREE.Shape.Utils.isClockWise( vertices ) ; + + if ( reverse ) { + + vertices = vertices.reverse(); + + // Maybe we should also check if holes are in the opposite direction, just to be safe ... + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + + if ( THREE.Shape.Utils.isClockWise( ahole ) ) { + + holes[ h ] = ahole.reverse(); + + } + + } + + reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)! + + } + + + var faces = THREE.Shape.Utils.triangulateShape ( vertices, holes ); + + /* Vertices */ + + var contour = vertices; // vertices has all points but contour has only points of circumference + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + + vertices = vertices.concat( ahole ); + + } + + + function scalePt2 ( pt, vec, size ) { + + if ( ! vec ) console.log( "die" ); + + return vec.clone().multiplyScalar( size ).add( pt ); + + } + + var b, bs, t, z, + vert, vlen = vertices.length, + face, flen = faces.length, + cont, clen = contour.length; + + + // Find directions for point movement + + var RAD_TO_DEGREES = 180 / Math.PI; + + + function getBevelVec( inPt, inPrev, inNext ) { + + var EPSILON = 0.0000000001; + var sign = THREE.Math.sign; + + // computes for inPt the corresponding point inPt' on a new contour + // shiftet by 1 unit (length of normalized vector) to the left + // if we walk along contour clockwise, this new contour is outside the old one + // + // inPt' is the intersection of the two lines parallel to the two + // adjacent edges of inPt at a distance of 1 unit on the left side. + + var v_trans_x, v_trans_y, shrink_by = 1; // resulting translation vector for inPt + + // good reading for geometry algorithms (here: line-line intersection) + // http://geomalgorithms.com/a05-_intersect-1.html + + var v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y; + var v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y; + + var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); + + // check for colinear edges + var colinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); + + if ( Math.abs( colinear0 ) > EPSILON ) { // not colinear + + // length of vectors for normalizing + + var v_prev_len = Math.sqrt( v_prev_lensq ); + var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); + + // shift adjacent points by unit vectors to the left + + var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); + var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); + + var ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); + var ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); + + // scaling factor for v_prev to intersection point + + var sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - + ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / + ( v_prev_x * v_next_y - v_prev_y * v_next_x ); + + // vector from inPt to intersection point + + v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); + v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); + + // Don't normalize!, otherwise sharp corners become ugly + // but prevent crazy spikes + var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ) + if ( v_trans_lensq <= 2 ) { + return new THREE.Vector2( v_trans_x, v_trans_y ); + } else { + shrink_by = Math.sqrt( v_trans_lensq / 2 ); + } + + } else { // handle special case of colinear edges + + var direction_eq = false; // assumes: opposite + if ( v_prev_x > EPSILON ) { + if ( v_next_x > EPSILON ) { direction_eq = true; } + } else { + if ( v_prev_x < - EPSILON ) { + if ( v_next_x < - EPSILON ) { direction_eq = true; } + } else { + if ( sign(v_prev_y) == sign(v_next_y) ) { direction_eq = true; } + } + } + + if ( direction_eq ) { + // console.log("Warning: lines are a straight sequence"); + v_trans_x = - v_prev_y; + v_trans_y = v_prev_x; + shrink_by = Math.sqrt( v_prev_lensq ); + } else { + // console.log("Warning: lines are a straight spike"); + v_trans_x = v_prev_x; + v_trans_y = v_prev_y; + shrink_by = Math.sqrt( v_prev_lensq / 2 ); + } + + } + + return new THREE.Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); + + } + + + var contourMovements = []; + + for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + + if ( j === il ) j = 0; + if ( k === il ) k = 0; + + // (j)---(i)---(k) + // console.log('i,j,k', i, j , k) + + var pt_i = contour[ i ]; + var pt_j = contour[ j ]; + var pt_k = contour[ k ]; + + contourMovements[ i ]= getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); + + } + + var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat(); + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + + oneHoleMovements = []; + + for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + + if ( j === il ) j = 0; + if ( k === il ) k = 0; + + // (j)---(i)---(k) + oneHoleMovements[ i ]= getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); + + } + + holesMovements.push( oneHoleMovements ); + verticesMovements = verticesMovements.concat( oneHoleMovements ); + + } + + + // Loop bevelSegments, 1 for the front, 1 for the back + + for ( b = 0; b < bevelSegments; b ++ ) { + //for ( b = bevelSegments; b > 0; b -- ) { + + t = b / bevelSegments; + z = bevelThickness * ( 1 - t ); + + //z = bevelThickness * t; + bs = bevelSize * ( Math.sin ( t * Math.PI/2 ) ) ; // curved + //bs = bevelSize * t ; // linear + + // contract shape + + for ( i = 0, il = contour.length; i < il; i ++ ) { + + vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); + + v( vert.x, vert.y, - z ); + + } + + // expand holes + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + oneHoleMovements = holesMovements[ h ]; + + for ( i = 0, il = ahole.length; i < il; i ++ ) { + + vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); + + v( vert.x, vert.y, - z ); + + } + + } + + } + + bs = bevelSize; + + // Back facing vertices + + for ( i = 0; i < vlen; i ++ ) { + + vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + + if ( ! extrudeByPath ) { + + v( vert.x, vert.y, 0 ); + + } else { + + // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); + + normal.copy( splineTube.normals[0] ).multiplyScalar(vert.x); + binormal.copy( splineTube.binormals[0] ).multiplyScalar(vert.y); + + position2.copy( extrudePts[0] ).add(normal).add(binormal); + + v( position2.x, position2.y, position2.z ); + + } + + } + + // Add stepped vertices... + // Including front facing vertices + + var s; + + for ( s = 1; s <= steps; s ++ ) { + + for ( i = 0; i < vlen; i ++ ) { + + vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + + if ( ! extrudeByPath ) { + + v( vert.x, vert.y, amount / steps * s ); + + } else { + + // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); + + normal.copy( splineTube.normals[s] ).multiplyScalar( vert.x ); + binormal.copy( splineTube.binormals[s] ).multiplyScalar( vert.y ); + + position2.copy( extrudePts[s] ).add( normal ).add( binormal ); + + v( position2.x, position2.y, position2.z ); + + } + + } + + } + + + // Add bevel segments planes + + //for ( b = 1; b <= bevelSegments; b ++ ) { + for ( b = bevelSegments - 1; b >= 0; b -- ) { + + t = b / bevelSegments; + z = bevelThickness * ( 1 - t ); + //bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) ); + bs = bevelSize * Math.sin ( t * Math.PI/2 ) ; + + // contract shape + + for ( i = 0, il = contour.length; i < il; i ++ ) { + + vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); + v( vert.x, vert.y, amount + z ); + + } + + // expand holes + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + oneHoleMovements = holesMovements[ h ]; + + for ( i = 0, il = ahole.length; i < il; i ++ ) { + + vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); + + if ( ! extrudeByPath ) { + + v( vert.x, vert.y, amount + z ); + + } else { + + v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); + + } + + } + + } + + } + + /* Faces */ + + // Top and bottom faces + + buildLidFaces(); + + // Sides faces + + buildSideFaces(); + + + ///// Internal functions + + function buildLidFaces() { + + if ( bevelEnabled ) { + + var layer = 0 ; // steps + 1 + var offset = vlen * layer; + + // Bottom faces + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + f3( face[ 2 ]+ offset, face[ 1 ]+ offset, face[ 0 ] + offset, true ); + + } + + layer = steps + bevelSegments * 2; + offset = vlen * layer; + + // Top faces + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset, false ); + + } + + } else { + + // Bottom faces + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + f3( face[ 2 ], face[ 1 ], face[ 0 ], true ); + + } + + // Top faces + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps, false ); + + } + } + + } + + // Create faces for the z-sides of the shape + + function buildSideFaces() { + + var layeroffset = 0; + sidewalls( contour, layeroffset ); + layeroffset += contour.length; + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + sidewalls( ahole, layeroffset ); + + //, true + layeroffset += ahole.length; + + } + + } + + function sidewalls( contour, layeroffset ) { + + var j, k; + i = contour.length; + + while ( --i >= 0 ) { + + j = i; + k = i - 1; + if ( k < 0 ) k = contour.length - 1; + + //console.log('b', i,j, i-1, k,vertices.length); + + var s = 0, sl = steps + bevelSegments * 2; + + for ( s = 0; s < sl; s ++ ) { + + var slen1 = vlen * s; + var slen2 = vlen * ( s + 1 ); + + var a = layeroffset + j + slen1, + b = layeroffset + k + slen1, + c = layeroffset + k + slen2, + d = layeroffset + j + slen2; + + f4( a, b, c, d, contour, s, sl, j, k ); + + } + } + + } + + + function v( x, y, z ) { + + scope.vertices.push( new THREE.Vector3( x, y, z ) ); + + } + + function f3( a, b, c, isBottom ) { + + a += shapesOffset; + b += shapesOffset; + c += shapesOffset; + + // normal, color, material + scope.faces.push( new THREE.Face3( a, b, c, null, null, material ) ); + + var uvs = isBottom ? uvgen.generateBottomUV( scope, shape, options, a, b, c ) : uvgen.generateTopUV( scope, shape, options, a, b, c ); + + scope.faceVertexUvs[ 0 ].push( uvs ); + + } + + function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) { + + a += shapesOffset; + b += shapesOffset; + c += shapesOffset; + d += shapesOffset; + + scope.faces.push( new THREE.Face3( a, b, d, null, null, extrudeMaterial ) ); + scope.faces.push( new THREE.Face3( b, c, d, null, null, extrudeMaterial ) ); + + var uvs = uvgen.generateSideWallUV( scope, shape, wallContour, options, a, b, c, d, + stepIndex, stepsLength, contourIndex1, contourIndex2 ); + + scope.faceVertexUvs[ 0 ].push( [ uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ] ); + scope.faceVertexUvs[ 0 ].push( [ uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ] ); + + } + +}; + +THREE.ExtrudeGeometry.WorldUVGenerator = { + + generateTopUV: function( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC ) { + var ax = geometry.vertices[ indexA ].x, + ay = geometry.vertices[ indexA ].y, + + bx = geometry.vertices[ indexB ].x, + by = geometry.vertices[ indexB ].y, + + cx = geometry.vertices[ indexC ].x, + cy = geometry.vertices[ indexC ].y; + + return [ + new THREE.Vector2( ax, ay ), + new THREE.Vector2( bx, by ), + new THREE.Vector2( cx, cy ) + ]; + + }, + + generateBottomUV: function( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC ) { + + return this.generateTopUV( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC ); + + }, + + generateSideWallUV: function( geometry, extrudedShape, wallContour, extrudeOptions, + indexA, indexB, indexC, indexD, stepIndex, stepsLength, + contourIndex1, contourIndex2 ) { + + var ax = geometry.vertices[ indexA ].x, + ay = geometry.vertices[ indexA ].y, + az = geometry.vertices[ indexA ].z, + + bx = geometry.vertices[ indexB ].x, + by = geometry.vertices[ indexB ].y, + bz = geometry.vertices[ indexB ].z, + + cx = geometry.vertices[ indexC ].x, + cy = geometry.vertices[ indexC ].y, + cz = geometry.vertices[ indexC ].z, + + dx = geometry.vertices[ indexD ].x, + dy = geometry.vertices[ indexD ].y, + dz = geometry.vertices[ indexD ].z; + + if ( Math.abs( ay - by ) < 0.01 ) { + return [ + new THREE.Vector2( ax, 1 - az ), + new THREE.Vector2( bx, 1 - bz ), + new THREE.Vector2( cx, 1 - cz ), + new THREE.Vector2( dx, 1 - dz ) + ]; + } else { + return [ + new THREE.Vector2( ay, 1 - az ), + new THREE.Vector2( by, 1 - bz ), + new THREE.Vector2( cy, 1 - cz ), + new THREE.Vector2( dy, 1 - dz ) + ]; + } + } +}; + +THREE.ExtrudeGeometry.__v1 = new THREE.Vector2(); +THREE.ExtrudeGeometry.__v2 = new THREE.Vector2(); +THREE.ExtrudeGeometry.__v3 = new THREE.Vector2(); +THREE.ExtrudeGeometry.__v4 = new THREE.Vector2(); +THREE.ExtrudeGeometry.__v5 = new THREE.Vector2(); +THREE.ExtrudeGeometry.__v6 = new THREE.Vector2(); + +// File:src/extras/geometries/ShapeGeometry.js + +/** + * @author jonobr1 / http://jonobr1.com + * + * Creates a one-sided polygonal geometry from a path shape. Similar to + * ExtrudeGeometry. + * + * parameters = { + * + * curveSegments: , // number of points on the curves. NOT USED AT THE MOMENT. + * + * material: // material index for front and back faces + * uvGenerator: // object that provides UV generator functions + * + * } + **/ + +THREE.ShapeGeometry = function ( shapes, options ) { + + THREE.Geometry.call( this ); + + if ( shapes instanceof Array === false ) shapes = [ shapes ]; + + this.addShapeList( shapes, options ); + + this.computeFaceNormals(); + +}; + +THREE.ShapeGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +/** + * Add an array of shapes to THREE.ShapeGeometry. + */ +THREE.ShapeGeometry.prototype.addShapeList = function ( shapes, options ) { + + for ( var i = 0, l = shapes.length; i < l; i ++ ) { + + this.addShape( shapes[ i ], options ); + + } + + return this; + +}; + +/** + * Adds a shape to THREE.ShapeGeometry, based on THREE.ExtrudeGeometry. + */ +THREE.ShapeGeometry.prototype.addShape = function ( shape, options ) { + + if ( options === undefined ) options = {}; + var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; + + var material = options.material; + var uvgen = options.UVGenerator === undefined ? THREE.ExtrudeGeometry.WorldUVGenerator : options.UVGenerator; + + // + + var i, l, hole, s; + + var shapesOffset = this.vertices.length; + var shapePoints = shape.extractPoints( curveSegments ); + + var vertices = shapePoints.shape; + var holes = shapePoints.holes; + + var reverse = ! THREE.Shape.Utils.isClockWise( vertices ); + + if ( reverse ) { + + vertices = vertices.reverse(); + + // Maybe we should also check if holes are in the opposite direction, just to be safe... + + for ( i = 0, l = holes.length; i < l; i ++ ) { + + hole = holes[ i ]; + + if ( THREE.Shape.Utils.isClockWise( hole ) ) { + + holes[ i ] = hole.reverse(); + + } + + } + + reverse = false; + + } + + var faces = THREE.Shape.Utils.triangulateShape( vertices, holes ); + + // Vertices + + var contour = vertices; + + for ( i = 0, l = holes.length; i < l; i ++ ) { + + hole = holes[ i ]; + vertices = vertices.concat( hole ); + + } + + // + + var vert, vlen = vertices.length; + var face, flen = faces.length; + var cont, clen = contour.length; + + for ( i = 0; i < vlen; i ++ ) { + + vert = vertices[ i ]; + + this.vertices.push( new THREE.Vector3( vert.x, vert.y, 0 ) ); + + } + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + + var a = face[ 0 ] + shapesOffset; + var b = face[ 1 ] + shapesOffset; + var c = face[ 2 ] + shapesOffset; + + this.faces.push( new THREE.Face3( a, b, c, null, null, material ) ); + this.faceVertexUvs[ 0 ].push( uvgen.generateBottomUV( this, shape, options, a, b, c ) ); + + } + +}; + +// File:src/extras/geometries/LatheGeometry.js + +/** + * @author astrodud / http://astrodud.isgreat.org/ + * @author zz85 / https://github.com/zz85 + * @author bhouston / http://exocortex.com + */ + +// points - to create a closed torus, one must use a set of points +// like so: [ a, b, c, d, a ], see first is the same as last. +// segments - the number of circumference segments to create +// phiStart - the starting radian +// phiLength - the radian (0 to 2*PI) range of the lathed section +// 2*pi is a closed lathe, less than 2PI is a portion. +THREE.LatheGeometry = function ( points, segments, phiStart, phiLength ) { + + THREE.Geometry.call( this ); + + segments = segments || 12; + phiStart = phiStart || 0; + phiLength = phiLength || 2 * Math.PI; + + var inversePointLength = 1.0 / ( points.length - 1 ); + var inverseSegments = 1.0 / segments; + + for ( var i = 0, il = segments; i <= il; i ++ ) { + + var phi = phiStart + i * inverseSegments * phiLength; + + var c = Math.cos( phi ), + s = Math.sin( phi ); + + for ( var j = 0, jl = points.length; j < jl; j ++ ) { + + var pt = points[ j ]; + + var vertex = new THREE.Vector3(); + + vertex.x = c * pt.x - s * pt.y; + vertex.y = s * pt.x + c * pt.y; + vertex.z = pt.z; + + this.vertices.push( vertex ); + + } + + } + + var np = points.length; + + for ( var i = 0, il = segments; i < il; i ++ ) { + + for ( var j = 0, jl = points.length - 1; j < jl; j ++ ) { + + var base = j + np * i; + var a = base; + var b = base + np; + var c = base + 1 + np; + var d = base + 1; + + var u0 = i * inverseSegments; + var v0 = j * inversePointLength; + var u1 = u0 + inverseSegments; + var v1 = v0 + inversePointLength; + + this.faces.push( new THREE.Face3( a, b, d ) ); + + this.faceVertexUvs[ 0 ].push( [ + + new THREE.Vector2( u0, v0 ), + new THREE.Vector2( u1, v0 ), + new THREE.Vector2( u0, v1 ) + + ] ); + + this.faces.push( new THREE.Face3( b, c, d ) ); + + this.faceVertexUvs[ 0 ].push( [ + + new THREE.Vector2( u1, v0 ), + new THREE.Vector2( u1, v1 ), + new THREE.Vector2( u0, v1 ) + + ] ); + + + } + + } + + this.mergeVertices(); + this.computeFaceNormals(); + this.computeVertexNormals(); + +}; + +THREE.LatheGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +// File:src/extras/geometries/PlaneGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as + */ + +THREE.PlaneGeometry = function ( width, height, widthSegments, heightSegments ) { + + THREE.Geometry.call( this ); + + this.parameters = { + width: width, + height: height, + widthSegments: widthSegments, + heightSegments: heightSegments + }; + + var ix, iz; + var width_half = width / 2; + var height_half = height / 2; + + var gridX = widthSegments || 1; + var gridZ = heightSegments || 1; + + var gridX1 = gridX + 1; + var gridZ1 = gridZ + 1; + + var segment_width = width / gridX; + var segment_height = height / gridZ; + + var normal = new THREE.Vector3( 0, 0, 1 ); + + for ( iz = 0; iz < gridZ1; iz ++ ) { + + var y = iz * segment_height - height_half; + + for ( ix = 0; ix < gridX1; ix ++ ) { + + var x = ix * segment_width - width_half; + + this.vertices.push( new THREE.Vector3( x, - y, 0 ) ); + + } + + } + + for ( iz = 0; iz < gridZ; iz ++ ) { + + for ( ix = 0; ix < gridX; ix ++ ) { + + var a = ix + gridX1 * iz; + var b = ix + gridX1 * ( iz + 1 ); + var c = ( ix + 1 ) + gridX1 * ( iz + 1 ); + var d = ( ix + 1 ) + gridX1 * iz; + + var uva = new THREE.Vector2( ix / gridX, 1 - iz / gridZ ); + var uvb = new THREE.Vector2( ix / gridX, 1 - ( iz + 1 ) / gridZ ); + var uvc = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - ( iz + 1 ) / gridZ ); + var uvd = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - iz / gridZ ); + + var face = new THREE.Face3( a, b, d ); + face.normal.copy( normal ); + face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() ); + + this.faces.push( face ); + this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + + face = new THREE.Face3( b, c, d ); + face.normal.copy( normal ); + face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() ); + + this.faces.push( face ); + this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + + } + + } + +}; + +THREE.PlaneGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +// File:src/extras/geometries/RingGeometry.js + +/** + * @author Kaleb Murphy + */ + +THREE.RingGeometry = function ( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { + + THREE.Geometry.call( this ); + + innerRadius = innerRadius || 0; + outerRadius = outerRadius || 50; + + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; + + thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8; + phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 8; + + var i, o, uvs = [], radius = innerRadius, radiusStep = ( ( outerRadius - innerRadius ) / phiSegments ); + + for ( i = 0; i < phiSegments + 1; i ++ ) { // concentric circles inside ring + + for ( o = 0; o < thetaSegments + 1; o ++ ) { // number of segments per circle + + var vertex = new THREE.Vector3(); + var segment = thetaStart + o / thetaSegments * thetaLength; + vertex.x = radius * Math.cos( segment ); + vertex.y = radius * Math.sin( segment ); + + this.vertices.push( vertex ); + uvs.push( new THREE.Vector2( ( vertex.x / outerRadius + 1 ) / 2, ( vertex.y / outerRadius + 1 ) / 2 ) ); + } + + radius += radiusStep; + + } + + var n = new THREE.Vector3( 0, 0, 1 ); + + for ( i = 0; i < phiSegments; i ++ ) { // concentric circles inside ring + + var thetaSegment = i * (thetaSegments + 1); + + for ( o = 0; o < thetaSegments ; o ++ ) { // number of segments per circle + + var segment = o + thetaSegment; + + var v1 = segment; + var v2 = segment + thetaSegments + 1; + var v3 = segment + thetaSegments + 2; + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) ); + this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ]); + + v1 = segment; + v2 = segment + thetaSegments + 2; + v3 = segment + 1; + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) ); + this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ]); + + } + } + + this.computeFaceNormals(); + + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + +}; + +THREE.RingGeometry.prototype = Object.create( THREE.Geometry.prototype ); + + +// File:src/extras/geometries/SphereGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { + + THREE.Geometry.call( this ); + + this.parameters = { + radius: radius, + widthSegments: widthSegments, + heightSegments: heightSegments, + phiStart: phiStart, + phiLength: phiLength, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + + radius = radius || 50; + + widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 ); + heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 ); + + phiStart = phiStart !== undefined ? phiStart : 0; + phiLength = phiLength !== undefined ? phiLength : Math.PI * 2; + + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : Math.PI; + + var x, y, vertices = [], uvs = []; + + for ( y = 0; y <= heightSegments; y ++ ) { + + var verticesRow = []; + var uvsRow = []; + + for ( x = 0; x <= widthSegments; x ++ ) { + + var u = x / widthSegments; + var v = y / heightSegments; + + var vertex = new THREE.Vector3(); + vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); + vertex.y = radius * Math.cos( thetaStart + v * thetaLength ); + vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); + + this.vertices.push( vertex ); + + verticesRow.push( this.vertices.length - 1 ); + uvsRow.push( new THREE.Vector2( u, 1 - v ) ); + + } + + vertices.push( verticesRow ); + uvs.push( uvsRow ); + + } + + for ( y = 0; y < heightSegments; y ++ ) { + + for ( x = 0; x < widthSegments; x ++ ) { + + var v1 = vertices[ y ][ x + 1 ]; + var v2 = vertices[ y ][ x ]; + var v3 = vertices[ y + 1 ][ x ]; + var v4 = vertices[ y + 1 ][ x + 1 ]; + + var n1 = this.vertices[ v1 ].clone().normalize(); + var n2 = this.vertices[ v2 ].clone().normalize(); + var n3 = this.vertices[ v3 ].clone().normalize(); + var n4 = this.vertices[ v4 ].clone().normalize(); + + var uv1 = uvs[ y ][ x + 1 ].clone(); + var uv2 = uvs[ y ][ x ].clone(); + var uv3 = uvs[ y + 1 ][ x ].clone(); + var uv4 = uvs[ y + 1 ][ x + 1 ].clone(); + + if ( Math.abs( this.vertices[ v1 ].y ) === radius ) { + + uv1.x = ( uv1.x + uv2.x ) / 2; + this.faces.push( new THREE.Face3( v1, v3, v4, [ n1, n3, n4 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv3, uv4 ] ); + + } else if ( Math.abs( this.vertices[ v3 ].y ) === radius ) { + + uv3.x = ( uv3.x + uv4.x ) / 2; + this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); + + } else { + + this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] ); + + this.faces.push( new THREE.Face3( v2, v3, v4, [ n2.clone(), n3, n4.clone() ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] ); + + } + + } + + } + + this.computeFaceNormals(); + + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + +}; + +THREE.SphereGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +// File:src/extras/geometries/TextGeometry.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * @author alteredq / http://alteredqualia.com/ + * + * For creating 3D text geometry in three.js + * + * Text = 3D Text + * + * parameters = { + * size: , // size of the text + * height: , // thickness to extrude text + * curveSegments: , // number of points on the curves + * + * font: , // font name + * weight: , // font weight (normal, bold) + * style: , // font style (normal, italics) + * + * bevelEnabled: , // turn on bevel + * bevelThickness: , // how deep into text bevel goes + * bevelSize: , // how far from text outline is bevel + * } + * + */ + +/* Usage Examples + + // TextGeometry wrapper + + var text3d = new TextGeometry( text, options ); + + // Complete manner + + var textShapes = THREE.FontUtils.generateShapes( text, options ); + var text3d = new ExtrudeGeometry( textShapes, options ); + +*/ + + +THREE.TextGeometry = function ( text, parameters ) { + + parameters = parameters || {}; + + var textShapes = THREE.FontUtils.generateShapes( text, parameters ); + + // translate parameters to ExtrudeGeometry API + + parameters.amount = parameters.height !== undefined ? parameters.height : 50; + + // defaults + + if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10; + if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8; + if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false; + + THREE.ExtrudeGeometry.call( this, textShapes, parameters ); + +}; + +THREE.TextGeometry.prototype = Object.create( THREE.ExtrudeGeometry.prototype ); + +// File:src/extras/geometries/TorusGeometry.js + +/** + * @author oosmoxiecode + * @author mrdoob / http://mrdoob.com/ + * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3DLite/src/away3dlite/primitives/Torus.as?r=2888 + */ + +THREE.TorusGeometry = function ( radius, tube, radialSegments, tubularSegments, arc ) { + + THREE.Geometry.call( this ); + + this.parameters = { + radius: radius, + tube: tube, + radialSegments: radialSegments, + tubularSegments: tubularSegments, + arc: arc + }; + + radius = radius || 100; + tube = tube || 40; + radialSegments = radialSegments || 8; + tubularSegments = tubularSegments || 6; + arc = arc || Math.PI * 2; + + var center = new THREE.Vector3(), uvs = [], normals = []; + + for ( var j = 0; j <= radialSegments; j ++ ) { + + for ( var i = 0; i <= tubularSegments; i ++ ) { + + var u = i / tubularSegments * arc; + var v = j / radialSegments * Math.PI * 2; + + center.x = radius * Math.cos( u ); + center.y = radius * Math.sin( u ); + + var vertex = new THREE.Vector3(); + vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u ); + vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u ); + vertex.z = tube * Math.sin( v ); + + this.vertices.push( vertex ); + + uvs.push( new THREE.Vector2( i / tubularSegments, j / radialSegments ) ); + normals.push( vertex.clone().sub( center ).normalize() ); + + } + + } + + for ( var j = 1; j <= radialSegments; j ++ ) { + + for ( var i = 1; i <= tubularSegments; i ++ ) { + + var a = ( tubularSegments + 1 ) * j + i - 1; + var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1; + var c = ( tubularSegments + 1 ) * ( j - 1 ) + i; + var d = ( tubularSegments + 1 ) * j + i; + + var face = new THREE.Face3( a, b, d, [ normals[ a ].clone(), normals[ b ].clone(), normals[ d ].clone() ] ); + this.faces.push( face ); + this.faceVertexUvs[ 0 ].push( [ uvs[ a ].clone(), uvs[ b ].clone(), uvs[ d ].clone() ] ); + + face = new THREE.Face3( b, c, d, [ normals[ b ].clone(), normals[ c ].clone(), normals[ d ].clone() ] ); + this.faces.push( face ); + this.faceVertexUvs[ 0 ].push( [ uvs[ b ].clone(), uvs[ c ].clone(), uvs[ d ].clone() ] ); + + } + + } + + this.computeFaceNormals(); + +}; + +THREE.TorusGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +// File:src/extras/geometries/TorusKnotGeometry.js + +/** + * @author oosmoxiecode + * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3D/src/away3d/primitives/TorusKnot.as?spec=svn2473&r=2473 + */ + +THREE.TorusKnotGeometry = function ( radius, tube, radialSegments, tubularSegments, p, q, heightScale ) { + + THREE.Geometry.call( this ); + + this.parameters = { + radius: radius, + tube: tube, + radialSegments: radialSegments, + tubularSegments: tubularSegments, + p: p, + q: q, + heightScale: heightScale + }; + + radius = radius || 100; + tube = tube || 40; + radialSegments = radialSegments || 64; + tubularSegments = tubularSegments || 8; + p = p || 2; + q = q || 3; + heightScale = heightScale || 1; + + var grid = new Array( radialSegments ); + var tang = new THREE.Vector3(); + var n = new THREE.Vector3(); + var bitan = new THREE.Vector3(); + + for ( var i = 0; i < radialSegments; ++ i ) { + + grid[ i ] = new Array( tubularSegments ); + var u = i / radialSegments * 2 * p * Math.PI; + var p1 = getPos( u, q, p, radius, heightScale ); + var p2 = getPos( u + 0.01, q, p, radius, heightScale ); + tang.subVectors( p2, p1 ); + n.addVectors( p2, p1 ); + + bitan.crossVectors( tang, n ); + n.crossVectors( bitan, tang ); + bitan.normalize(); + n.normalize(); + + for ( var j = 0; j < tubularSegments; ++ j ) { + + var v = j / tubularSegments * 2 * Math.PI; + var cx = - tube * Math.cos( v ); // TODO: Hack: Negating it so it faces outside. + var cy = tube * Math.sin( v ); + + var pos = new THREE.Vector3(); + pos.x = p1.x + cx * n.x + cy * bitan.x; + pos.y = p1.y + cx * n.y + cy * bitan.y; + pos.z = p1.z + cx * n.z + cy * bitan.z; + + grid[ i ][ j ] = this.vertices.push( pos ) - 1; + + } + + } + + for ( var i = 0; i < radialSegments; ++ i ) { + + for ( var j = 0; j < tubularSegments; ++ j ) { + + var ip = ( i + 1 ) % radialSegments; + var jp = ( j + 1 ) % tubularSegments; + + var a = grid[ i ][ j ]; + var b = grid[ ip ][ j ]; + var c = grid[ ip ][ jp ]; + var d = grid[ i ][ jp ]; + + var uva = new THREE.Vector2( i / radialSegments, j / tubularSegments ); + var uvb = new THREE.Vector2( ( i + 1 ) / radialSegments, j / tubularSegments ); + var uvc = new THREE.Vector2( ( i + 1 ) / radialSegments, ( j + 1 ) / tubularSegments ); + var uvd = new THREE.Vector2( i / radialSegments, ( j + 1 ) / tubularSegments ); + + this.faces.push( new THREE.Face3( a, b, d ) ); + this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + + this.faces.push( new THREE.Face3( b, c, d ) ); + this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + + } + } + + this.computeFaceNormals(); + this.computeVertexNormals(); + + function getPos( u, in_q, in_p, radius, heightScale ) { + + var cu = Math.cos( u ); + var su = Math.sin( u ); + var quOverP = in_q / in_p * u; + var cs = Math.cos( quOverP ); + + var tx = radius * ( 2 + cs ) * 0.5 * cu; + var ty = radius * ( 2 + cs ) * su * 0.5; + var tz = heightScale * radius * Math.sin( quOverP ) * 0.5; + + return new THREE.Vector3( tx, ty, tz ); + + } + +}; + +THREE.TorusKnotGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +// File:src/extras/geometries/TubeGeometry.js + +/** + * @author WestLangley / https://github.com/WestLangley + * @author zz85 / https://github.com/zz85 + * @author miningold / https://github.com/miningold + * + * Modified from the TorusKnotGeometry by @oosmoxiecode + * + * Creates a tube which extrudes along a 3d spline + * + * Uses parallel transport frames as described in + * http://www.cs.indiana.edu/pub/techreports/TR425.pdf + */ + +THREE.TubeGeometry = function ( path, segments, radius, radialSegments, closed ) { + + THREE.Geometry.call( this ); + + this.parameters = { + path: path, + segments: segments, + radius: radius, + radialSegments: radialSegments, + closed: closed + }; + + segments = segments || 64; + radius = radius || 1; + radialSegments = radialSegments || 8; + closed = closed || false; + + var grid = []; + + var scope = this, + + tangent, + normal, + binormal, + + numpoints = segments + 1, + + x, y, z, + tx, ty, tz, + u, v, + + cx, cy, + pos, pos2 = new THREE.Vector3(), + i, j, + ip, jp, + a, b, c, d, + uva, uvb, uvc, uvd; + + var frames = new THREE.TubeGeometry.FrenetFrames( path, segments, closed ), + tangents = frames.tangents, + normals = frames.normals, + binormals = frames.binormals; + + // proxy internals + this.tangents = tangents; + this.normals = normals; + this.binormals = binormals; + + function vert( x, y, z ) { + + return scope.vertices.push( new THREE.Vector3( x, y, z ) ) - 1; + + } + + // consruct the grid + + for ( i = 0; i < numpoints; i ++ ) { + + grid[ i ] = []; + + u = i / ( numpoints - 1 ); + + pos = path.getPointAt( u ); + + tangent = tangents[ i ]; + normal = normals[ i ]; + binormal = binormals[ i ]; + + for ( j = 0; j < radialSegments; j ++ ) { + + v = j / radialSegments * 2 * Math.PI; + + cx = - radius * Math.cos( v ); // TODO: Hack: Negating it so it faces outside. + cy = radius * Math.sin( v ); + + pos2.copy( pos ); + pos2.x += cx * normal.x + cy * binormal.x; + pos2.y += cx * normal.y + cy * binormal.y; + pos2.z += cx * normal.z + cy * binormal.z; + + grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z ); + + } + } + + + // construct the mesh + + for ( i = 0; i < segments; i ++ ) { + + for ( j = 0; j < radialSegments; j ++ ) { + + ip = ( closed ) ? (i + 1) % segments : i + 1; + jp = (j + 1) % radialSegments; + + a = grid[ i ][ j ]; // *** NOT NECESSARILY PLANAR ! *** + b = grid[ ip ][ j ]; + c = grid[ ip ][ jp ]; + d = grid[ i ][ jp ]; + + uva = new THREE.Vector2( i / segments, j / radialSegments ); + uvb = new THREE.Vector2( ( i + 1 ) / segments, j / radialSegments ); + uvc = new THREE.Vector2( ( i + 1 ) / segments, ( j + 1 ) / radialSegments ); + uvd = new THREE.Vector2( i / segments, ( j + 1 ) / radialSegments ); + + this.faces.push( new THREE.Face3( a, b, d ) ); + this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + + this.faces.push( new THREE.Face3( b, c, d ) ); + this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + + } + } + + this.computeFaceNormals(); + this.computeVertexNormals(); + +}; + +THREE.TubeGeometry.prototype = Object.create( THREE.Geometry.prototype ); + + +// For computing of Frenet frames, exposing the tangents, normals and binormals the spline +THREE.TubeGeometry.FrenetFrames = function ( path, segments, closed ) { + + var tangent = new THREE.Vector3(), + normal = new THREE.Vector3(), + binormal = new THREE.Vector3(), + + tangents = [], + normals = [], + binormals = [], + + vec = new THREE.Vector3(), + mat = new THREE.Matrix4(), + + numpoints = segments + 1, + theta, + epsilon = 0.0001, + smallest, + + tx, ty, tz, + i, u, v; + + + // expose internals + this.tangents = tangents; + this.normals = normals; + this.binormals = binormals; + + // compute the tangent vectors for each segment on the path + + for ( i = 0; i < numpoints; i ++ ) { + + u = i / ( numpoints - 1 ); + + tangents[ i ] = path.getTangentAt( u ); + tangents[ i ].normalize(); + + } + + initialNormal3(); + + /* + function initialNormal1(lastBinormal) { + // fixed start binormal. Has dangers of 0 vectors + normals[ 0 ] = new THREE.Vector3(); + binormals[ 0 ] = new THREE.Vector3(); + if (lastBinormal===undefined) lastBinormal = new THREE.Vector3( 0, 0, 1 ); + normals[ 0 ].crossVectors( lastBinormal, tangents[ 0 ] ).normalize(); + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize(); + } + + function initialNormal2() { + + // This uses the Frenet-Serret formula for deriving binormal + var t2 = path.getTangentAt( epsilon ); + + normals[ 0 ] = new THREE.Vector3().subVectors( t2, tangents[ 0 ] ).normalize(); + binormals[ 0 ] = new THREE.Vector3().crossVectors( tangents[ 0 ], normals[ 0 ] ); + + normals[ 0 ].crossVectors( binormals[ 0 ], tangents[ 0 ] ).normalize(); // last binormal x tangent + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize(); + + } + */ + + function initialNormal3() { + // select an initial normal vector perpenicular to the first tangent vector, + // and in the direction of the smallest tangent xyz component + + normals[ 0 ] = new THREE.Vector3(); + binormals[ 0 ] = new THREE.Vector3(); + smallest = Number.MAX_VALUE; + tx = Math.abs( tangents[ 0 ].x ); + ty = Math.abs( tangents[ 0 ].y ); + tz = Math.abs( tangents[ 0 ].z ); + + if ( tx <= smallest ) { + smallest = tx; + normal.set( 1, 0, 0 ); + } + + if ( ty <= smallest ) { + smallest = ty; + normal.set( 0, 1, 0 ); + } + + if ( tz <= smallest ) { + normal.set( 0, 0, 1 ); + } + + vec.crossVectors( tangents[ 0 ], normal ).normalize(); + + normals[ 0 ].crossVectors( tangents[ 0 ], vec ); + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); + } + + + // compute the slowly-varying normal and binormal vectors for each segment on the path + + for ( i = 1; i < numpoints; i ++ ) { + + normals[ i ] = normals[ i-1 ].clone(); + + binormals[ i ] = binormals[ i-1 ].clone(); + + vec.crossVectors( tangents[ i-1 ], tangents[ i ] ); + + if ( vec.length() > epsilon ) { + + vec.normalize(); + + theta = Math.acos( THREE.Math.clamp( tangents[ i-1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors + + normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); + + } + + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + + } + + + // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same + + if ( closed ) { + + theta = Math.acos( THREE.Math.clamp( normals[ 0 ].dot( normals[ numpoints-1 ] ), - 1, 1 ) ); + theta /= ( numpoints - 1 ); + + if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ numpoints-1 ] ) ) > 0 ) { + + theta = - theta; + + } + + for ( i = 1; i < numpoints; i ++ ) { + + // twist a little... + normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + + } + + } +}; + +// File:src/extras/geometries/PolyhedronGeometry.js + +/** + * @author clockworkgeek / https://github.com/clockworkgeek + * @author timothypratley / https://github.com/timothypratley + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.PolyhedronGeometry = function ( vertices, indices, radius, detail ) { + + THREE.Geometry.call( this ); + + radius = radius || 1; + detail = detail || 0; + + var that = this; + + for ( var i = 0, l = vertices.length; i < l; i += 3 ) { + + prepare( new THREE.Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) ); + + } + + var midpoints = [], p = this.vertices; + + var faces = []; + + for ( var i = 0, j = 0, l = indices.length; i < l; i += 3, j ++ ) { + + var v1 = p[ indices[ i ] ]; + var v2 = p[ indices[ i + 1 ] ]; + var v3 = p[ indices[ i + 2 ] ]; + + faces[ j ] = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] ); + + } + + var centroid = new THREE.Vector3(); + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + subdivide( faces[ i ], detail ); + + } + + + // Handle case when face straddles the seam + + for ( var i = 0, l = this.faceVertexUvs[ 0 ].length; i < l; i ++ ) { + + var uvs = this.faceVertexUvs[ 0 ][ i ]; + + var x0 = uvs[ 0 ].x; + var x1 = uvs[ 1 ].x; + var x2 = uvs[ 2 ].x; + + var max = Math.max( x0, Math.max( x1, x2 ) ); + var min = Math.min( x0, Math.min( x1, x2 ) ); + + if ( max > 0.9 && min < 0.1 ) { // 0.9 is somewhat arbitrary + + if ( x0 < 0.2 ) uvs[ 0 ].x += 1; + if ( x1 < 0.2 ) uvs[ 1 ].x += 1; + if ( x2 < 0.2 ) uvs[ 2 ].x += 1; + + } + + } + + + // Apply radius + + for ( var i = 0, l = this.vertices.length; i < l; i ++ ) { + + this.vertices[ i ].multiplyScalar( radius ); + + } + + + // Merge vertices + + this.mergeVertices(); + + this.computeFaceNormals(); + + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + + + // Project vector onto sphere's surface + + function prepare( vector ) { + + var vertex = vector.normalize().clone(); + vertex.index = that.vertices.push( vertex ) - 1; + + // Texture coords are equivalent to map coords, calculate angle and convert to fraction of a circle. + + var u = azimuth( vector ) / 2 / Math.PI + 0.5; + var v = inclination( vector ) / Math.PI + 0.5; + vertex.uv = new THREE.Vector2( u, 1 - v ); + + return vertex; + + } + + + // Approximate a curved face with recursively sub-divided triangles. + + function make( v1, v2, v3 ) { + + var face = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] ); + that.faces.push( face ); + + centroid.copy( v1 ).add( v2 ).add( v3 ).divideScalar( 3 ); + + var azi = azimuth( centroid ); + + that.faceVertexUvs[ 0 ].push( [ + correctUV( v1.uv, v1, azi ), + correctUV( v2.uv, v2, azi ), + correctUV( v3.uv, v3, azi ) + ] ); + + } + + + // Analytically subdivide a face to the required detail level. + + function subdivide( face, detail ) { + + var cols = Math.pow(2, detail); + var cells = Math.pow(4, detail); + var a = prepare( that.vertices[ face.a ] ); + var b = prepare( that.vertices[ face.b ] ); + var c = prepare( that.vertices[ face.c ] ); + var v = []; + + // Construct all of the vertices for this subdivision. + + for ( var i = 0 ; i <= cols; i ++ ) { + + v[ i ] = []; + + var aj = prepare( a.clone().lerp( c, i / cols ) ); + var bj = prepare( b.clone().lerp( c, i / cols ) ); + var rows = cols - i; + + for ( var j = 0; j <= rows; j ++) { + + if ( j == 0 && i == cols ) { + + v[ i ][ j ] = aj; + + } else { + + v[ i ][ j ] = prepare( aj.clone().lerp( bj, j / rows ) ); + + } + + } + + } + + // Construct all of the faces. + + for ( var i = 0; i < cols ; i ++ ) { + + for ( var j = 0; j < 2 * (cols - i) - 1; j ++ ) { + + var k = Math.floor( j / 2 ); + + if ( j % 2 == 0 ) { + + make( + v[ i ][ k + 1], + v[ i + 1 ][ k ], + v[ i ][ k ] + ); + + } else { + + make( + v[ i ][ k + 1 ], + v[ i + 1][ k + 1], + v[ i + 1 ][ k ] + ); + + } + + } + + } + + } + + + // Angle around the Y axis, counter-clockwise when looking from above. + + function azimuth( vector ) { + + return Math.atan2( vector.z, - vector.x ); + + } + + + // Angle above the XZ plane. + + function inclination( vector ) { + + return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); + + } + + + // Texture fixing helper. Spheres have some odd behaviours. + + function correctUV( uv, vector, azimuth ) { + + if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) uv = new THREE.Vector2( uv.x - 1, uv.y ); + if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) uv = new THREE.Vector2( azimuth / 2 / Math.PI + 0.5, uv.y ); + return uv.clone(); + + } + + +}; + +THREE.PolyhedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +// File:src/extras/geometries/IcosahedronGeometry.js + +/** + * @author timothypratley / https://github.com/timothypratley + */ + +THREE.IcosahedronGeometry = function ( radius, detail ) { + + this.parameters = { + radius: radius, + detail: detail + }; + + var t = ( 1 + Math.sqrt( 5 ) ) / 2; + + var vertices = [ + - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0, + 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, + t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1 + ]; + + var indices = [ + 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, + 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, + 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, + 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1 + ]; + + THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); + +}; + +THREE.IcosahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +// File:src/extras/geometries/OctahedronGeometry.js + +/** + * @author timothypratley / https://github.com/timothypratley + */ + +THREE.OctahedronGeometry = function ( radius, detail ) { + + this.parameters = { + radius: radius, + detail: detail + }; + + var vertices = [ + 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0,- 1, 0, 0, 0, 1, 0, 0,- 1 + ]; + + var indices = [ + 0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2 + ]; + + THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); +}; + +THREE.OctahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +// File:src/extras/geometries/TetrahedronGeometry.js + +/** + * @author timothypratley / https://github.com/timothypratley + */ + +THREE.TetrahedronGeometry = function ( radius, detail ) { + + var vertices = [ + 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1 + ]; + + var indices = [ + 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1 + ]; + + THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); + +}; + +THREE.TetrahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +// File:src/extras/geometries/ParametricGeometry.js + +/** + * @author zz85 / https://github.com/zz85 + * Parametric Surfaces Geometry + * based on the brilliant article by @prideout http://prideout.net/blog/?p=44 + * + * new THREE.ParametricGeometry( parametricFunction, uSegments, ySegements ); + * + */ + +THREE.ParametricGeometry = function ( func, slices, stacks ) { + + THREE.Geometry.call( this ); + + var verts = this.vertices; + var faces = this.faces; + var uvs = this.faceVertexUvs[ 0 ]; + + var i, il, j, p; + var u, v; + + var stackCount = stacks + 1; + var sliceCount = slices + 1; + + for ( i = 0; i <= stacks; i ++ ) { + + v = i / stacks; + + for ( j = 0; j <= slices; j ++ ) { + + u = j / slices; + + p = func( u, v ); + verts.push( p ); + + } + } + + var a, b, c, d; + var uva, uvb, uvc, uvd; + + for ( i = 0; i < stacks; i ++ ) { + + for ( j = 0; j < slices; j ++ ) { + + a = i * sliceCount + j; + b = i * sliceCount + j + 1; + c = (i + 1) * sliceCount + j + 1; + d = (i + 1) * sliceCount + j; + + uva = new THREE.Vector2( j / slices, i / stacks ); + uvb = new THREE.Vector2( ( j + 1 ) / slices, i / stacks ); + uvc = new THREE.Vector2( ( j + 1 ) / slices, ( i + 1 ) / stacks ); + uvd = new THREE.Vector2( j / slices, ( i + 1 ) / stacks ); + + faces.push( new THREE.Face3( a, b, d ) ); + uvs.push( [ uva, uvb, uvd ] ); + + faces.push( new THREE.Face3( b, c, d ) ); + uvs.push( [ uvb.clone(), uvc, uvd.clone() ] ); + + } + + } + + // console.log(this); + + // magic bullet + // var diff = this.mergeVertices(); + // console.log('removed ', diff, ' vertices by merging'); + + this.computeFaceNormals(); + this.computeVertexNormals(); + +}; + +THREE.ParametricGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +// File:src/extras/helpers/AxisHelper.js + +/** + * @author sroucheray / http://sroucheray.org/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.AxisHelper = function ( size ) { + + size = size || 1; + + var vertices = new Float32Array( [ + 0, 0, 0, size, 0, 0, + 0, 0, 0, 0, size, 0, + 0, 0, 0, 0, 0, size + ] ); + + var colors = new Float32Array( [ + 1, 0, 0, 1, 0.6, 0, + 0, 1, 0, 0.6, 1, 0, + 0, 0, 1, 0, 0.6, 1 + ] ); + + var geometry = new THREE.BufferGeometry(); + geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) ); + + var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } ); + + THREE.Line.call( this, geometry, material, THREE.LinePieces ); + +}; + +THREE.AxisHelper.prototype = Object.create( THREE.Line.prototype ); + +// File:src/extras/helpers/ArrowHelper.js + +/** + * @author WestLangley / http://github.com/WestLangley + * @author zz85 / http://github.com/zz85 + * @author bhouston / http://exocortex.com + * + * Creates an arrow for visualizing directions + * + * Parameters: + * dir - Vector3 + * origin - Vector3 + * length - Number + * color - color in hex value + * headLength - Number + * headWidth - Number + */ + +THREE.ArrowHelper = ( function () { + + var lineGeometry = new THREE.Geometry(); + lineGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 1, 0 ) ); + + var coneGeometry = new THREE.CylinderGeometry( 0, 0.5, 1, 5, 1 ); + coneGeometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, - 0.5, 0 ) ); + + return function ( dir, origin, length, color, headLength, headWidth ) { + + // dir is assumed to be normalized + + THREE.Object3D.call( this ); + + if ( color === undefined ) color = 0xffff00; + if ( length === undefined ) length = 1; + if ( headLength === undefined ) headLength = 0.2 * length; + if ( headWidth === undefined ) headWidth = 0.2 * headLength; + + this.position.copy( origin ); + + this.line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color: color } ) ); + this.line.matrixAutoUpdate = false; + this.add( this.line ); + + this.cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color: color } ) ); + this.cone.matrixAutoUpdate = false; + this.add( this.cone ); + + this.setDirection( dir ); + this.setLength( length, headLength, headWidth ); + + } + +}() ); + +THREE.ArrowHelper.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.ArrowHelper.prototype.setDirection = ( function () { + + var axis = new THREE.Vector3(); + var radians; + + return function ( dir ) { + + // dir is assumed to be normalized + + if ( dir.y > 0.99999 ) { + + this.quaternion.set( 0, 0, 0, 1 ); + + } else if ( dir.y < - 0.99999 ) { + + this.quaternion.set( 1, 0, 0, 0 ); + + } else { + + axis.set( dir.z, 0, - dir.x ).normalize(); + + radians = Math.acos( dir.y ); + + this.quaternion.setFromAxisAngle( axis, radians ); + + } + + }; + +}() ); + +THREE.ArrowHelper.prototype.setLength = function ( length, headLength, headWidth ) { + + if ( headLength === undefined ) headLength = 0.2 * length; + if ( headWidth === undefined ) headWidth = 0.2 * headLength; + + this.line.scale.set( 1, length, 1 ); + this.line.updateMatrix(); + + this.cone.scale.set( headWidth, headLength, headWidth ); + this.cone.position.y = length; + this.cone.updateMatrix(); + +}; + +THREE.ArrowHelper.prototype.setColor = function ( color ) { + + this.line.material.color.set( color ); + this.cone.material.color.set( color ); + +}; + +// File:src/extras/helpers/BoxHelper.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.BoxHelper = function ( object ) { + + var geometry = new THREE.BufferGeometry(); + geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( 72 ), 3 ) ); + + THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: 0xffff00 } ), THREE.LinePieces ); + + if ( object !== undefined ) { + + this.update( object ); + + } + +}; + +THREE.BoxHelper.prototype = Object.create( THREE.Line.prototype ); + +THREE.BoxHelper.prototype.update = function ( object ) { + + var geometry = object.geometry; + + if ( geometry.boundingBox === null ) { + + geometry.computeBoundingBox(); + + } + + var min = geometry.boundingBox.min; + var max = geometry.boundingBox.max; + + /* + 5____4 + 1/___0/| + | 6__|_7 + 2/___3/ + + 0: max.x, max.y, max.z + 1: min.x, max.y, max.z + 2: min.x, min.y, max.z + 3: max.x, min.y, max.z + 4: max.x, max.y, min.z + 5: min.x, max.y, min.z + 6: min.x, min.y, min.z + 7: max.x, min.y, min.z + */ + + var vertices = this.geometry.attributes.position.array; + + vertices[ 0 ] = max.x; vertices[ 1 ] = max.y; vertices[ 2 ] = max.z; + vertices[ 3 ] = min.x; vertices[ 4 ] = max.y; vertices[ 5 ] = max.z; + + vertices[ 6 ] = min.x; vertices[ 7 ] = max.y; vertices[ 8 ] = max.z; + vertices[ 9 ] = min.x; vertices[ 10 ] = min.y; vertices[ 11 ] = max.z; + + vertices[ 12 ] = min.x; vertices[ 13 ] = min.y; vertices[ 14 ] = max.z; + vertices[ 15 ] = max.x; vertices[ 16 ] = min.y; vertices[ 17 ] = max.z; + + vertices[ 18 ] = max.x; vertices[ 19 ] = min.y; vertices[ 20 ] = max.z; + vertices[ 21 ] = max.x; vertices[ 22 ] = max.y; vertices[ 23 ] = max.z; + + // + + vertices[ 24 ] = max.x; vertices[ 25 ] = max.y; vertices[ 26 ] = min.z; + vertices[ 27 ] = min.x; vertices[ 28 ] = max.y; vertices[ 29 ] = min.z; + + vertices[ 30 ] = min.x; vertices[ 31 ] = max.y; vertices[ 32 ] = min.z; + vertices[ 33 ] = min.x; vertices[ 34 ] = min.y; vertices[ 35 ] = min.z; + + vertices[ 36 ] = min.x; vertices[ 37 ] = min.y; vertices[ 38 ] = min.z; + vertices[ 39 ] = max.x; vertices[ 40 ] = min.y; vertices[ 41 ] = min.z; + + vertices[ 42 ] = max.x; vertices[ 43 ] = min.y; vertices[ 44 ] = min.z; + vertices[ 45 ] = max.x; vertices[ 46 ] = max.y; vertices[ 47 ] = min.z; + + // + + vertices[ 48 ] = max.x; vertices[ 49 ] = max.y; vertices[ 50 ] = max.z; + vertices[ 51 ] = max.x; vertices[ 52 ] = max.y; vertices[ 53 ] = min.z; + + vertices[ 54 ] = min.x; vertices[ 55 ] = max.y; vertices[ 56 ] = max.z; + vertices[ 57 ] = min.x; vertices[ 58 ] = max.y; vertices[ 59 ] = min.z; + + vertices[ 60 ] = min.x; vertices[ 61 ] = min.y; vertices[ 62 ] = max.z; + vertices[ 63 ] = min.x; vertices[ 64 ] = min.y; vertices[ 65 ] = min.z; + + vertices[ 66 ] = max.x; vertices[ 67 ] = min.y; vertices[ 68 ] = max.z; + vertices[ 69 ] = max.x; vertices[ 70 ] = min.y; vertices[ 71 ] = min.z; + + this.geometry.attributes.position.needsUpdate = true; + + this.geometry.computeBoundingSphere(); + + this.matrixAutoUpdate = false; + this.matrixWorld = object.matrixWorld; + +}; + +// File:src/extras/helpers/BoundingBoxHelper.js + +/** + * @author WestLangley / http://github.com/WestLangley + */ + +// a helper to show the world-axis-aligned bounding box for an object + +THREE.BoundingBoxHelper = function ( object, hex ) { + + var color = ( hex !== undefined ) ? hex : 0x888888; + + this.object = object; + + this.box = new THREE.Box3(); + + THREE.Mesh.call( this, new THREE.BoxGeometry( 1, 1, 1 ), new THREE.MeshBasicMaterial( { color: color, wireframe: true } ) ); + +}; + +THREE.BoundingBoxHelper.prototype = Object.create( THREE.Mesh.prototype ); + +THREE.BoundingBoxHelper.prototype.update = function () { + + this.box.setFromObject( this.object ); + + this.box.size( this.scale ); + + this.box.center( this.position ); + +}; + +// File:src/extras/helpers/CameraHelper.js + +/** + * @author alteredq / http://alteredqualia.com/ + * + * - shows frustum, line of sight and up of the camera + * - suitable for fast updates + * - based on frustum visualization in lightgl.js shadowmap example + * http://evanw.github.com/lightgl.js/tests/shadowmap.html + */ + +THREE.CameraHelper = function ( camera ) { + + var geometry = new THREE.Geometry(); + var material = new THREE.LineBasicMaterial( { color: 0xffffff, vertexColors: THREE.FaceColors } ); + + var pointMap = {}; + + // colors + + var hexFrustum = 0xffaa00; + var hexCone = 0xff0000; + var hexUp = 0x00aaff; + var hexTarget = 0xffffff; + var hexCross = 0x333333; + + // near + + addLine( "n1", "n2", hexFrustum ); + addLine( "n2", "n4", hexFrustum ); + addLine( "n4", "n3", hexFrustum ); + addLine( "n3", "n1", hexFrustum ); + + // far + + addLine( "f1", "f2", hexFrustum ); + addLine( "f2", "f4", hexFrustum ); + addLine( "f4", "f3", hexFrustum ); + addLine( "f3", "f1", hexFrustum ); + + // sides + + addLine( "n1", "f1", hexFrustum ); + addLine( "n2", "f2", hexFrustum ); + addLine( "n3", "f3", hexFrustum ); + addLine( "n4", "f4", hexFrustum ); + + // cone + + addLine( "p", "n1", hexCone ); + addLine( "p", "n2", hexCone ); + addLine( "p", "n3", hexCone ); + addLine( "p", "n4", hexCone ); + + // up + + addLine( "u1", "u2", hexUp ); + addLine( "u2", "u3", hexUp ); + addLine( "u3", "u1", hexUp ); + + // target + + addLine( "c", "t", hexTarget ); + addLine( "p", "c", hexCross ); + + // cross + + addLine( "cn1", "cn2", hexCross ); + addLine( "cn3", "cn4", hexCross ); + + addLine( "cf1", "cf2", hexCross ); + addLine( "cf3", "cf4", hexCross ); + + function addLine( a, b, hex ) { + + addPoint( a, hex ); + addPoint( b, hex ); + + } + + function addPoint( id, hex ) { + + geometry.vertices.push( new THREE.Vector3() ); + geometry.colors.push( new THREE.Color( hex ) ); + + if ( pointMap[ id ] === undefined ) { + + pointMap[ id ] = []; + + } + + pointMap[ id ].push( geometry.vertices.length - 1 ); + + } + + THREE.Line.call( this, geometry, material, THREE.LinePieces ); + + this.camera = camera; + this.matrixWorld = camera.matrixWorld; + this.matrixAutoUpdate = false; + + this.pointMap = pointMap; + + this.update(); + +}; + +THREE.CameraHelper.prototype = Object.create( THREE.Line.prototype ); + +THREE.CameraHelper.prototype.update = function () { + + var vector = new THREE.Vector3(); + var camera = new THREE.Camera(); + var projector = new THREE.Projector(); + + return function () { + + var scope = this; + + var w = 1, h = 1; + + // we need just camera projection matrix + // world matrix must be identity + + camera.projectionMatrix.copy( this.camera.projectionMatrix ); + + // center / target + + setPoint( "c", 0, 0, - 1 ); + setPoint( "t", 0, 0, 1 ); + + // near + + setPoint( "n1", - w, - h, - 1 ); + setPoint( "n2", w, - h, - 1 ); + setPoint( "n3", - w, h, - 1 ); + setPoint( "n4", w, h, - 1 ); + + // far + + setPoint( "f1", - w, - h, 1 ); + setPoint( "f2", w, - h, 1 ); + setPoint( "f3", - w, h, 1 ); + setPoint( "f4", w, h, 1 ); + + // up + + setPoint( "u1", w * 0.7, h * 1.1, - 1 ); + setPoint( "u2", - w * 0.7, h * 1.1, - 1 ); + setPoint( "u3", 0, h * 2, - 1 ); + + // cross + + setPoint( "cf1", - w, 0, 1 ); + setPoint( "cf2", w, 0, 1 ); + setPoint( "cf3", 0, - h, 1 ); + setPoint( "cf4", 0, h, 1 ); + + setPoint( "cn1", - w, 0, - 1 ); + setPoint( "cn2", w, 0, - 1 ); + setPoint( "cn3", 0, - h, - 1 ); + setPoint( "cn4", 0, h, - 1 ); + + function setPoint( point, x, y, z ) { + + vector.set( x, y, z ); + projector.unprojectVector( vector, camera ); + + var points = scope.pointMap[ point ]; + + if ( points !== undefined ) { + + for ( var i = 0, il = points.length; i < il; i ++ ) { + + scope.geometry.vertices[ points[ i ] ].copy( vector ); + + } + + } + + } + + this.geometry.verticesNeedUpdate = true; + + }; + +}(); + +// File:src/extras/helpers/DirectionalLightHelper.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.DirectionalLightHelper = function ( light, size ) { + + THREE.Object3D.call( this ); + + this.light = light; + this.light.updateMatrixWorld(); + + this.matrixWorld = light.matrixWorld; + this.matrixAutoUpdate = false; + + size = size || 1; + + var geometry = new THREE.Geometry(); + geometry.vertices.push( + new THREE.Vector3( - size, size, 0 ), + new THREE.Vector3( size, size, 0 ), + new THREE.Vector3( size, - size, 0 ), + new THREE.Vector3( - size, - size, 0 ), + new THREE.Vector3( - size, size, 0 ) + ); + + var material = new THREE.LineBasicMaterial( { fog: false } ); + material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + this.lightPlane = new THREE.Line( geometry, material ); + this.add( this.lightPlane ); + + geometry = new THREE.Geometry(); + geometry.vertices.push( + new THREE.Vector3(), + new THREE.Vector3() + ); + + material = new THREE.LineBasicMaterial( { fog: false } ); + material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + this.targetLine = new THREE.Line( geometry, material ); + this.add( this.targetLine ); + + this.update(); + +}; + +THREE.DirectionalLightHelper.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.DirectionalLightHelper.prototype.dispose = function () { + + this.lightPlane.geometry.dispose(); + this.lightPlane.material.dispose(); + this.targetLine.geometry.dispose(); + this.targetLine.material.dispose(); +}; + +THREE.DirectionalLightHelper.prototype.update = function () { + + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + var v3 = new THREE.Vector3(); + + return function () { + + v1.setFromMatrixPosition( this.light.matrixWorld ); + v2.setFromMatrixPosition( this.light.target.matrixWorld ); + v3.subVectors( v2, v1 ); + + this.lightPlane.lookAt( v3 ); + this.lightPlane.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + this.targetLine.geometry.vertices[ 1 ].copy( v3 ); + this.targetLine.geometry.verticesNeedUpdate = true; + this.targetLine.material.color.copy( this.lightPlane.material.color ); + + } + +}(); + + +// File:src/extras/helpers/EdgesHelper.js + +/** + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.EdgesHelper = function ( object, hex ) { + + var color = ( hex !== undefined ) ? hex : 0xffffff; + + var edge = [ 0, 0 ], hash = {}; + var sortFunction = function ( a, b ) { return a - b }; + + var keys = [ 'a', 'b', 'c' ]; + var geometry = new THREE.BufferGeometry(); + + var geometry2 = object.geometry.clone(); + + geometry2.mergeVertices(); + geometry2.computeFaceNormals(); + + var vertices = geometry2.vertices; + var faces = geometry2.faces; + var numEdges = 0; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + for ( var j = 0; j < 3; j ++ ) { + + edge[ 0 ] = face[ keys[ j ] ]; + edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; + edge.sort( sortFunction ); + + var key = edge.toString(); + + if ( hash[ key ] === undefined ) { + + hash[ key ] = { vert1: edge[ 0 ], vert2: edge[ 1 ], face1: i, face2: undefined }; + numEdges ++; + + } else { + + hash[ key ].face2 = i; + + } + + } + + } + + geometry.addAttribute( 'position', new THREE.Float32Attribute( numEdges * 2 * 3, 3 ) ); + + var coords = geometry.attributes.position.array; + + var index = 0; + + for ( var key in hash ) { + + var h = hash[ key ]; + + if ( h.face2 === undefined || faces[ h.face1 ].normal.dot( faces[ h.face2 ].normal ) < 0.9999 ) { // hardwired const OK + + var vertex = vertices[ h.vert1 ]; + coords[ index ++ ] = vertex.x; + coords[ index ++ ] = vertex.y; + coords[ index ++ ] = vertex.z; + + vertex = vertices[ h.vert2 ]; + coords[ index ++ ] = vertex.x; + coords[ index ++ ] = vertex.y; + coords[ index ++ ] = vertex.z; + + } + + } + + THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color } ), THREE.LinePieces ); + + this.matrixAutoUpdate = false; + this.matrixWorld = object.matrixWorld; + +}; + +THREE.EdgesHelper.prototype = Object.create( THREE.Line.prototype ); + +// File:src/extras/helpers/FaceNormalsHelper.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.FaceNormalsHelper = function ( object, size, hex, linewidth ) { + + this.object = object; + + this.size = ( size !== undefined ) ? size : 1; + + var color = ( hex !== undefined ) ? hex : 0xffff00; + + var width = ( linewidth !== undefined ) ? linewidth : 1; + + var geometry = new THREE.Geometry(); + + var faces = this.object.geometry.faces; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + geometry.vertices.push( new THREE.Vector3(), new THREE.Vector3() ); + + } + + THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces ); + + this.matrixAutoUpdate = false; + + this.normalMatrix = new THREE.Matrix3(); + + this.update(); + +}; + +THREE.FaceNormalsHelper.prototype = Object.create( THREE.Line.prototype ); + +THREE.FaceNormalsHelper.prototype.update = function () { + + var vertices = this.geometry.vertices; + + var object = this.object; + var objectVertices = object.geometry.vertices; + var objectFaces = object.geometry.faces; + var objectWorldMatrix = object.matrixWorld; + + object.updateMatrixWorld( true ); + + this.normalMatrix.getNormalMatrix( objectWorldMatrix ); + + for ( var i = 0, i2 = 0, l = objectFaces.length; i < l; i ++, i2 += 2 ) { + + var face = objectFaces[ i ]; + + vertices[ i2 ].copy( objectVertices[ face.a ] ) + .add( objectVertices[ face.b ] ) + .add( objectVertices[ face.c ] ) + .divideScalar( 3 ) + .applyMatrix4( objectWorldMatrix ); + + vertices[ i2 + 1 ].copy( face.normal ) + .applyMatrix3( this.normalMatrix ) + .normalize() + .multiplyScalar( this.size ) + .add( vertices[ i2 ] ); + + } + + this.geometry.verticesNeedUpdate = true; + + return this; + +}; + + +// File:src/extras/helpers/GridHelper.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.GridHelper = function ( size, step ) { + + var geometry = new THREE.Geometry(); + var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } ); + + this.color1 = new THREE.Color( 0x444444 ); + this.color2 = new THREE.Color( 0x888888 ); + + for ( var i = - size; i <= size; i += step ) { + + geometry.vertices.push( + new THREE.Vector3( - size, 0, i ), new THREE.Vector3( size, 0, i ), + new THREE.Vector3( i, 0, - size ), new THREE.Vector3( i, 0, size ) + ); + + var color = i === 0 ? this.color1 : this.color2; + + geometry.colors.push( color, color, color, color ); + + } + + THREE.Line.call( this, geometry, material, THREE.LinePieces ); + +}; + +THREE.GridHelper.prototype = Object.create( THREE.Line.prototype ); + +THREE.GridHelper.prototype.setColors = function( colorCenterLine, colorGrid ) { + + this.color1.set( colorCenterLine ); + this.color2.set( colorGrid ); + + this.geometry.colorsNeedUpdate = true; + +} + +// File:src/extras/helpers/HemisphereLightHelper.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.HemisphereLightHelper = function ( light, sphereSize, arrowLength, domeSize ) { + + THREE.Object3D.call( this ); + + this.light = light; + this.light.updateMatrixWorld(); + + this.matrixWorld = light.matrixWorld; + this.matrixAutoUpdate = false; + + this.colors = [ new THREE.Color(), new THREE.Color() ]; + + var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 ); + geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) ); + + for ( var i = 0, il = 8; i < il; i ++ ) { + + geometry.faces[ i ].color = this.colors[ i < 4 ? 0 : 1 ]; + + } + + var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, wireframe: true } ); + + this.lightSphere = new THREE.Mesh( geometry, material ); + this.add( this.lightSphere ); + + this.update(); + +}; + +THREE.HemisphereLightHelper.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.HemisphereLightHelper.prototype.dispose = function () { + this.lightSphere.geometry.dispose(); + this.lightSphere.material.dispose(); +}; + +THREE.HemisphereLightHelper.prototype.update = function () { + + var vector = new THREE.Vector3(); + + return function () { + + this.colors[ 0 ].copy( this.light.color ).multiplyScalar( this.light.intensity ); + this.colors[ 1 ].copy( this.light.groundColor ).multiplyScalar( this.light.intensity ); + + this.lightSphere.lookAt( vector.setFromMatrixPosition( this.light.matrixWorld ).negate() ); + this.lightSphere.geometry.colorsNeedUpdate = true; + + } + +}(); + + +// File:src/extras/helpers/PointLightHelper.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.PointLightHelper = function ( light, sphereSize ) { + + this.light = light; + this.light.updateMatrixWorld(); + + var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 ); + var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } ); + material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + THREE.Mesh.call( this, geometry, material ); + + this.matrixWorld = this.light.matrixWorld; + this.matrixAutoUpdate = false; + + /* + var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 ); + var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); + + this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); + this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); + + var d = light.distance; + + if ( d === 0.0 ) { + + this.lightDistance.visible = false; + + } else { + + this.lightDistance.scale.set( d, d, d ); + + } + + this.add( this.lightDistance ); + */ + +}; + +THREE.PointLightHelper.prototype = Object.create( THREE.Mesh.prototype ); + +THREE.PointLightHelper.prototype.dispose = function () { + + this.geometry.dispose(); + this.material.dispose(); +}; + +THREE.PointLightHelper.prototype.update = function () { + + this.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + /* + var d = this.light.distance; + + if ( d === 0.0 ) { + + this.lightDistance.visible = false; + + } else { + + this.lightDistance.visible = true; + this.lightDistance.scale.set( d, d, d ); + + } + */ + +}; + + +// File:src/extras/helpers/SkeletonHelper.js + +/** + * @author Sean Griffin / http://twitter.com/sgrif + * @author Michael Guerrero / http://realitymeltdown.com + * @author mrdoob / http://mrdoob.com/ + * @author ikerr / http://verold.com + */ + +THREE.SkeletonHelper = function ( object ) { + + this.bones = this.getBoneList( object ); + + var geometry = new THREE.Geometry(); + + for ( var i = 0; i < this.bones.length; i ++ ) { + + var bone = this.bones[ i ]; + + if ( bone.parent instanceof THREE.Bone ) { + + geometry.vertices.push( new THREE.Vector3() ); + geometry.vertices.push( new THREE.Vector3() ); + geometry.colors.push( new THREE.Color( 0, 0, 1 ) ); + geometry.colors.push( new THREE.Color( 0, 1, 0 ) ); + + } + + } + + var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors, depthTest: false, depthWrite: false, transparent: true } ); + + THREE.Line.call( this, geometry, material, THREE.LinePieces ); + + this.root = object; + + this.matrixWorld = object.matrixWorld; + this.matrixAutoUpdate = false; + + this.update(); + +}; + + +THREE.SkeletonHelper.prototype = Object.create( THREE.Line.prototype ); + +THREE.SkeletonHelper.prototype.getBoneList = function( object ) { + + var boneList = []; + + if ( object instanceof THREE.Bone ) { + + boneList.push( object ); + + } + + for ( var i = 0; i < object.children.length; i ++ ) { + + boneList.push.apply( boneList, this.getBoneList( object.children[ i ] ) ); + + } + + return boneList; + +}; + +THREE.SkeletonHelper.prototype.update = function () { + + var geometry = this.geometry; + + var matrixWorldInv = new THREE.Matrix4().getInverse( this.root.matrixWorld ); + + var boneMatrix = new THREE.Matrix4(); + + var j = 0; + + for ( var i = 0; i < this.bones.length; i ++ ) { + + var bone = this.bones[ i ]; + + if ( bone.parent instanceof THREE.Bone ) { + + boneMatrix.multiplyMatrices( matrixWorldInv, bone.matrixWorld ); + geometry.vertices[ j ].setFromMatrixPosition( boneMatrix ); + + boneMatrix.multiplyMatrices( matrixWorldInv, bone.parent.matrixWorld ); + geometry.vertices[ j + 1 ].setFromMatrixPosition( boneMatrix ); + + j += 2; + + } + + } + + geometry.verticesNeedUpdate = true; + + geometry.computeBoundingSphere(); + +}; + +// File:src/extras/helpers/SpotLightHelper.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.SpotLightHelper = function ( light ) { + + THREE.Object3D.call( this ); + + this.light = light; + this.light.updateMatrixWorld(); + + this.matrixWorld = light.matrixWorld; + this.matrixAutoUpdate = false; + + var geometry = new THREE.CylinderGeometry( 0, 1, 1, 8, 1, true ); + + geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, - 0.5, 0 ) ); + geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) ); + + var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } ); + + this.cone = new THREE.Mesh( geometry, material ); + this.add( this.cone ); + + this.update(); + +}; + +THREE.SpotLightHelper.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.SpotLightHelper.prototype.dispose = function () { + this.cone.geometry.dispose(); + this.cone.material.dispose(); +}; + +THREE.SpotLightHelper.prototype.update = function () { + + var vector = new THREE.Vector3(); + var vector2 = new THREE.Vector3(); + + return function () { + + var coneLength = this.light.distance ? this.light.distance : 10000; + var coneWidth = coneLength * Math.tan( this.light.angle ); + + this.cone.scale.set( coneWidth, coneWidth, coneLength ); + + vector.setFromMatrixPosition( this.light.matrixWorld ); + vector2.setFromMatrixPosition( this.light.target.matrixWorld ); + + this.cone.lookAt( vector2.sub( vector ) ); + + this.cone.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + }; + +}(); + +// File:src/extras/helpers/VertexNormalsHelper.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.VertexNormalsHelper = function ( object, size, hex, linewidth ) { + + this.object = object; + + this.size = ( size !== undefined ) ? size : 1; + + var color = ( hex !== undefined ) ? hex : 0xff0000; + + var width = ( linewidth !== undefined ) ? linewidth : 1; + + var geometry = new THREE.Geometry(); + + var vertices = object.geometry.vertices; + + var faces = object.geometry.faces; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { + + geometry.vertices.push( new THREE.Vector3(), new THREE.Vector3() ); + + } + + } + + THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces ); + + this.matrixAutoUpdate = false; + + this.normalMatrix = new THREE.Matrix3(); + + this.update(); + +}; + +THREE.VertexNormalsHelper.prototype = Object.create( THREE.Line.prototype ); + +THREE.VertexNormalsHelper.prototype.update = ( function ( object ) { + + var v1 = new THREE.Vector3(); + + return function( object ) { + + var keys = [ 'a', 'b', 'c', 'd' ]; + + this.object.updateMatrixWorld( true ); + + this.normalMatrix.getNormalMatrix( this.object.matrixWorld ); + + var vertices = this.geometry.vertices; + + var verts = this.object.geometry.vertices; + + var faces = this.object.geometry.faces; + + var worldMatrix = this.object.matrixWorld; + + var idx = 0; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { + + var vertexId = face[ keys[ j ] ]; + var vertex = verts[ vertexId ]; + + var normal = face.vertexNormals[ j ]; + + vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix ); + + v1.copy( normal ).applyMatrix3( this.normalMatrix ).normalize().multiplyScalar( this.size ); + + v1.add( vertices[ idx ] ); + idx = idx + 1; + + vertices[ idx ].copy( v1 ); + idx = idx + 1; + + } + + } + + this.geometry.verticesNeedUpdate = true; + + return this; + + } + +}()); + +// File:src/extras/helpers/VertexTangentsHelper.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.VertexTangentsHelper = function ( object, size, hex, linewidth ) { + + this.object = object; + + this.size = ( size !== undefined ) ? size : 1; + + var color = ( hex !== undefined ) ? hex : 0x0000ff; + + var width = ( linewidth !== undefined ) ? linewidth : 1; + + var geometry = new THREE.Geometry(); + + var vertices = object.geometry.vertices; + + var faces = object.geometry.faces; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) { + + geometry.vertices.push( new THREE.Vector3() ); + geometry.vertices.push( new THREE.Vector3() ); + + } + + } + + THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces ); + + this.matrixAutoUpdate = false; + + this.update(); + +}; + +THREE.VertexTangentsHelper.prototype = Object.create( THREE.Line.prototype ); + +THREE.VertexTangentsHelper.prototype.update = ( function ( object ) { + + var v1 = new THREE.Vector3(); + + return function( object ) { + + var keys = [ 'a', 'b', 'c', 'd' ]; + + this.object.updateMatrixWorld( true ); + + var vertices = this.geometry.vertices; + + var verts = this.object.geometry.vertices; + + var faces = this.object.geometry.faces; + + var worldMatrix = this.object.matrixWorld; + + var idx = 0; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) { + + var vertexId = face[ keys[ j ] ]; + var vertex = verts[ vertexId ]; + + var tangent = face.vertexTangents[ j ]; + + vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix ); + + v1.copy( tangent ).transformDirection( worldMatrix ).multiplyScalar( this.size ); + + v1.add( vertices[ idx ] ); + idx = idx + 1; + + vertices[ idx ].copy( v1 ); + idx = idx + 1; + + } + + } + + this.geometry.verticesNeedUpdate = true; + + return this; + + } + +}()); + +// File:src/extras/helpers/WireframeHelper.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.WireframeHelper = function ( object, hex ) { + + var color = ( hex !== undefined ) ? hex : 0xffffff; + + var edge = [ 0, 0 ], hash = {}; + var sortFunction = function ( a, b ) { return a - b }; + + var keys = [ 'a', 'b', 'c' ]; + var geometry = new THREE.BufferGeometry(); + + if ( object.geometry instanceof THREE.Geometry ) { + + var vertices = object.geometry.vertices; + var faces = object.geometry.faces; + var numEdges = 0; + + // allocate maximal size + var edges = new Uint32Array( 6 * faces.length ); + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + for ( var j = 0; j < 3; j ++ ) { + + edge[ 0 ] = face[ keys[ j ] ]; + edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; + edge.sort( sortFunction ); + + var key = edge.toString(); + + if ( hash[ key ] === undefined ) { + + edges[ 2 * numEdges ] = edge[ 0 ]; + edges[ 2 * numEdges + 1 ] = edge[ 1 ]; + hash[ key ] = true; + numEdges ++; + + } + + } + + } + + var coords = new Float32Array( numEdges * 2 * 3 ); + + for ( var i = 0, l = numEdges; i < l; i ++ ) { + + for ( var j = 0; j < 2; j ++ ) { + + var vertex = vertices[ edges [ 2 * i + j] ]; + + var index = 6 * i + 3 * j; + coords[ index + 0 ] = vertex.x; + coords[ index + 1 ] = vertex.y; + coords[ index + 2 ] = vertex.z; + + } + + } + + geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); + + } else if ( object.geometry instanceof THREE.BufferGeometry ) { + + if ( object.geometry.attributes.index !== undefined ) { // Indexed BufferGeometry + + var vertices = object.geometry.attributes.position.array; + var indices = object.geometry.attributes.index.array; + var offsets = object.geometry.offsets; + var numEdges = 0; + + // allocate maximal size + var edges = new Uint32Array( 2 * indices.length ); + + for ( var o = 0, ol = offsets.length; o < ol; ++ o ) { + + var start = offsets[ o ].start; + var count = offsets[ o ].count; + var index = offsets[ o ].index; + + for ( var i = start, il = start + count; i < il; i += 3 ) { + + for ( var j = 0; j < 3; j ++ ) { + + edge[ 0 ] = index + indices[ i + j ]; + edge[ 1 ] = index + indices[ i + ( j + 1 ) % 3 ]; + edge.sort( sortFunction ); + + var key = edge.toString(); + + if ( hash[ key ] === undefined ) { + + edges[ 2 * numEdges ] = edge[ 0 ]; + edges[ 2 * numEdges + 1 ] = edge[ 1 ]; + hash[ key ] = true; + numEdges ++; + + } + + } + + } + + } + + var coords = new Float32Array( numEdges * 2 * 3 ); + + for ( var i = 0, l = numEdges; i < l; i ++ ) { + + for ( var j = 0; j < 2; j ++ ) { + + var index = 6 * i + 3 * j; + var index2 = 3 * edges[ 2 * i + j]; + coords[ index + 0 ] = vertices[ index2 ]; + coords[ index + 1 ] = vertices[ index2 + 1 ]; + coords[ index + 2 ] = vertices[ index2 + 2 ]; + + } + + } + + geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); + + } else { // non-indexed BufferGeometry + + var vertices = object.geometry.attributes.position.array; + var numEdges = vertices.length / 3; + var numTris = numEdges / 3; + + var coords = new Float32Array( numEdges * 2 * 3 ); + + for ( var i = 0, l = numTris; i < l; i ++ ) { + + for ( var j = 0; j < 3; j ++ ) { + + var index = 18 * i + 6 * j; + + var index1 = 9 * i + 3 * j; + coords[ index + 0 ] = vertices[ index1 ]; + coords[ index + 1 ] = vertices[ index1 + 1 ]; + coords[ index + 2 ] = vertices[ index1 + 2 ]; + + var index2 = 9 * i + 3 * ( ( j + 1 ) % 3 ); + coords[ index + 3 ] = vertices[ index2 ]; + coords[ index + 4 ] = vertices[ index2 + 1 ]; + coords[ index + 5 ] = vertices[ index2 + 2 ]; + + } + + } + + geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); + + } + + } + + THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color } ), THREE.LinePieces ); + + this.matrixAutoUpdate = false; + this.matrixWorld = object.matrixWorld; + +}; + +THREE.WireframeHelper.prototype = Object.create( THREE.Line.prototype ); + +// File:src/extras/objects/ImmediateRenderObject.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.ImmediateRenderObject = function () { + + THREE.Object3D.call( this ); + + this.render = function ( renderCallback ) {}; + +}; + +THREE.ImmediateRenderObject.prototype = Object.create( THREE.Object3D.prototype ); + +// File:src/extras/objects/LensFlare.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.LensFlare = function ( texture, size, distance, blending, color ) { + + THREE.Object3D.call( this ); + + this.lensFlares = []; + + this.positionScreen = new THREE.Vector3(); + this.customUpdateCallback = undefined; + + if( texture !== undefined ) { + + this.add( texture, size, distance, blending, color ); + + } + +}; + +THREE.LensFlare.prototype = Object.create( THREE.Object3D.prototype ); + + +/* + * Add: adds another flare + */ + +THREE.LensFlare.prototype.add = function ( texture, size, distance, blending, color, opacity ) { + + if( size === undefined ) size = - 1; + if( distance === undefined ) distance = 0; + if( opacity === undefined ) opacity = 1; + if( color === undefined ) color = new THREE.Color( 0xffffff ); + if( blending === undefined ) blending = THREE.NormalBlending; + + distance = Math.min( distance, Math.max( 0, distance ) ); + + this.lensFlares.push( { texture: texture, // THREE.Texture + size: size, // size in pixels (-1 = use texture.width) + distance: distance, // distance (0-1) from light source (0=at light source) + x: 0, y: 0, z: 0, // screen position (-1 => 1) z = 0 is ontop z = 1 is back + scale: 1, // scale + rotation: 1, // rotation + opacity: opacity, // opacity + color: color, // color + blending: blending } ); // blending + +}; + + +/* + * Update lens flares update positions on all flares based on the screen position + * Set myLensFlare.customUpdateCallback to alter the flares in your project specific way. + */ + +THREE.LensFlare.prototype.updateLensFlares = function () { + + var f, fl = this.lensFlares.length; + var flare; + var vecX = - this.positionScreen.x * 2; + var vecY = - this.positionScreen.y * 2; + + for( f = 0; f < fl; f ++ ) { + + flare = this.lensFlares[ f ]; + + flare.x = this.positionScreen.x + vecX * flare.distance; + flare.y = this.positionScreen.y + vecY * flare.distance; + + flare.wantedRotation = flare.x * Math.PI * 0.25; + flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25; + + } + +}; + + + + + + + + + + + + + +// File:src/extras/objects/MorphBlendMesh.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.MorphBlendMesh = function( geometry, material ) { + + THREE.Mesh.call( this, geometry, material ); + + this.animationsMap = {}; + this.animationsList = []; + + // prepare default animation + // (all frames played together in 1 second) + + var numFrames = this.geometry.morphTargets.length; + + var name = "__default"; + + var startFrame = 0; + var endFrame = numFrames - 1; + + var fps = numFrames / 1; + + this.createAnimation( name, startFrame, endFrame, fps ); + this.setAnimationWeight( name, 1 ); + +}; + +THREE.MorphBlendMesh.prototype = Object.create( THREE.Mesh.prototype ); + +THREE.MorphBlendMesh.prototype.createAnimation = function ( name, start, end, fps ) { + + var animation = { + + startFrame: start, + endFrame: end, + + length: end - start + 1, + + fps: fps, + duration: ( end - start ) / fps, + + lastFrame: 0, + currentFrame: 0, + + active: false, + + time: 0, + direction: 1, + weight: 1, + + directionBackwards: false, + mirroredLoop: false + + }; + + this.animationsMap[ name ] = animation; + this.animationsList.push( animation ); + +}; + +THREE.MorphBlendMesh.prototype.autoCreateAnimations = function ( fps ) { + + var pattern = /([a-z]+)_?(\d+)/; + + var firstAnimation, frameRanges = {}; + + var geometry = this.geometry; + + for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) { + + var morph = geometry.morphTargets[ i ]; + var chunks = morph.name.match( pattern ); + + if ( chunks && chunks.length > 1 ) { + + var name = chunks[ 1 ]; + var num = chunks[ 2 ]; + + if ( ! frameRanges[ name ] ) frameRanges[ name ] = { start: Infinity, end: - Infinity }; + + var range = frameRanges[ name ]; + + if ( i < range.start ) range.start = i; + if ( i > range.end ) range.end = i; + + if ( ! firstAnimation ) firstAnimation = name; + + } + + } + + for ( var name in frameRanges ) { + + var range = frameRanges[ name ]; + this.createAnimation( name, range.start, range.end, fps ); + + } + + this.firstAnimation = firstAnimation; + +}; + +THREE.MorphBlendMesh.prototype.setAnimationDirectionForward = function ( name ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.direction = 1; + animation.directionBackwards = false; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward = function ( name ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.direction = - 1; + animation.directionBackwards = true; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationFPS = function ( name, fps ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.fps = fps; + animation.duration = ( animation.end - animation.start ) / animation.fps; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationDuration = function ( name, duration ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.duration = duration; + animation.fps = ( animation.end - animation.start ) / animation.duration; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationWeight = function ( name, weight ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.weight = weight; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationTime = function ( name, time ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.time = time; + + } + +}; + +THREE.MorphBlendMesh.prototype.getAnimationTime = function ( name ) { + + var time = 0; + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + time = animation.time; + + } + + return time; + +}; + +THREE.MorphBlendMesh.prototype.getAnimationDuration = function ( name ) { + + var duration = - 1; + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + duration = animation.duration; + + } + + return duration; + +}; + +THREE.MorphBlendMesh.prototype.playAnimation = function ( name ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.time = 0; + animation.active = true; + + } else { + + console.warn( "animation[" + name + "] undefined" ); + + } + +}; + +THREE.MorphBlendMesh.prototype.stopAnimation = function ( name ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.active = false; + + } + +}; + +THREE.MorphBlendMesh.prototype.update = function ( delta ) { + + for ( var i = 0, il = this.animationsList.length; i < il; i ++ ) { + + var animation = this.animationsList[ i ]; + + if ( ! animation.active ) continue; + + var frameTime = animation.duration / animation.length; + + animation.time += animation.direction * delta; + + if ( animation.mirroredLoop ) { + + if ( animation.time > animation.duration || animation.time < 0 ) { + + animation.direction *= - 1; + + if ( animation.time > animation.duration ) { + + animation.time = animation.duration; + animation.directionBackwards = true; + + } + + if ( animation.time < 0 ) { + + animation.time = 0; + animation.directionBackwards = false; + + } + + } + + } else { + + animation.time = animation.time % animation.duration; + + if ( animation.time < 0 ) animation.time += animation.duration; + + } + + var keyframe = animation.startFrame + THREE.Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 ); + var weight = animation.weight; + + if ( keyframe !== animation.currentFrame ) { + + this.morphTargetInfluences[ animation.lastFrame ] = 0; + this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight; + + this.morphTargetInfluences[ keyframe ] = 0; + + animation.lastFrame = animation.currentFrame; + animation.currentFrame = keyframe; + + } + + var mix = ( animation.time % frameTime ) / frameTime; + + if ( animation.directionBackwards ) mix = 1 - mix; + + this.morphTargetInfluences[ animation.currentFrame ] = mix * weight; + this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight; + + } + +}; + +// File:src/extras/renderers/plugins/LensFlarePlugin.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.LensFlarePlugin = function () { + + var flares = []; + + var _gl, _renderer, _precision, _lensFlare = {}; + + this.init = function ( renderer ) { + + _gl = renderer.context; + _renderer = renderer; + + _precision = renderer.getPrecision(); + + _lensFlare.vertices = new Float32Array( 8 + 8 ); + _lensFlare.faces = new Uint16Array( 6 ); + + var i = 0; + _lensFlare.vertices[ i ++ ] = - 1; _lensFlare.vertices[ i ++ ] = - 1; // vertex + _lensFlare.vertices[ i ++ ] = 0; _lensFlare.vertices[ i ++ ] = 0; // uv... etc. + + _lensFlare.vertices[ i ++ ] = 1; _lensFlare.vertices[ i ++ ] = - 1; + _lensFlare.vertices[ i ++ ] = 1; _lensFlare.vertices[ i ++ ] = 0; + + _lensFlare.vertices[ i ++ ] = 1; _lensFlare.vertices[ i ++ ] = 1; + _lensFlare.vertices[ i ++ ] = 1; _lensFlare.vertices[ i ++ ] = 1; + + _lensFlare.vertices[ i ++ ] = - 1; _lensFlare.vertices[ i ++ ] = 1; + _lensFlare.vertices[ i ++ ] = 0; _lensFlare.vertices[ i ++ ] = 1; + + i = 0; + _lensFlare.faces[ i ++ ] = 0; _lensFlare.faces[ i ++ ] = 1; _lensFlare.faces[ i ++ ] = 2; + _lensFlare.faces[ i ++ ] = 0; _lensFlare.faces[ i ++ ] = 2; _lensFlare.faces[ i ++ ] = 3; + + // buffers + + _lensFlare.vertexBuffer = _gl.createBuffer(); + _lensFlare.elementBuffer = _gl.createBuffer(); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, _lensFlare.vertices, _gl.STATIC_DRAW ); + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer ); + _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.faces, _gl.STATIC_DRAW ); + + // textures + + _lensFlare.tempTexture = _gl.createTexture(); + _lensFlare.occlusionTexture = _gl.createTexture(); + + _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture ); + _gl.texImage2D( _gl.TEXTURE_2D, 0, _gl.RGB, 16, 16, 0, _gl.RGB, _gl.UNSIGNED_BYTE, null ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, _gl.NEAREST ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, _gl.NEAREST ); + + _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.occlusionTexture ); + _gl.texImage2D( _gl.TEXTURE_2D, 0, _gl.RGBA, 16, 16, 0, _gl.RGBA, _gl.UNSIGNED_BYTE, null ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, _gl.NEAREST ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, _gl.NEAREST ); + + if ( _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) <= 0 ) { + + _lensFlare.hasVertexTexture = false; + _lensFlare.program = createProgram( THREE.ShaderFlares[ "lensFlare" ], _precision ); + + } else { + + _lensFlare.hasVertexTexture = true; + _lensFlare.program = createProgram( THREE.ShaderFlares[ "lensFlareVertexTexture" ], _precision ); + + } + + _lensFlare.attributes = {}; + _lensFlare.uniforms = {}; + + _lensFlare.attributes.vertex = _gl.getAttribLocation ( _lensFlare.program, "position" ); + _lensFlare.attributes.uv = _gl.getAttribLocation ( _lensFlare.program, "uv" ); + + _lensFlare.uniforms.renderType = _gl.getUniformLocation( _lensFlare.program, "renderType" ); + _lensFlare.uniforms.map = _gl.getUniformLocation( _lensFlare.program, "map" ); + _lensFlare.uniforms.occlusionMap = _gl.getUniformLocation( _lensFlare.program, "occlusionMap" ); + _lensFlare.uniforms.opacity = _gl.getUniformLocation( _lensFlare.program, "opacity" ); + _lensFlare.uniforms.color = _gl.getUniformLocation( _lensFlare.program, "color" ); + _lensFlare.uniforms.scale = _gl.getUniformLocation( _lensFlare.program, "scale" ); + _lensFlare.uniforms.rotation = _gl.getUniformLocation( _lensFlare.program, "rotation" ); + _lensFlare.uniforms.screenPosition = _gl.getUniformLocation( _lensFlare.program, "screenPosition" ); + + }; + + + /* + * Render lens flares + * Method: renders 16x16 0xff00ff-colored points scattered over the light source area, + * reads these back and calculates occlusion. + * Then _lensFlare.update_lensFlares() is called to re-position and + * update transparency of flares. Then they are rendered. + * + */ + + this.render = function ( scene, camera, viewportWidth, viewportHeight ) { + + flares.length = 0; + + scene.traverseVisible( function ( child ) { + + if ( child instanceof THREE.LensFlare ) { + + flares.push( child ); + + } + + } ); + + if ( flares.length === 0 ) return; + + var tempPosition = new THREE.Vector3(); + + var invAspect = viewportHeight / viewportWidth, + halfViewportWidth = viewportWidth * 0.5, + halfViewportHeight = viewportHeight * 0.5; + + var size = 16 / viewportHeight, + scale = new THREE.Vector2( size * invAspect, size ); + + var screenPosition = new THREE.Vector3( 1, 1, 0 ), + screenPositionPixels = new THREE.Vector2( 1, 1 ); + + var uniforms = _lensFlare.uniforms, + attributes = _lensFlare.attributes; + + // set _lensFlare program and reset blending + + _gl.useProgram( _lensFlare.program ); + + _gl.enableVertexAttribArray( _lensFlare.attributes.vertex ); + _gl.enableVertexAttribArray( _lensFlare.attributes.uv ); + + // loop through all lens flares to update their occlusion and positions + // setup gl and common used attribs/unforms + + _gl.uniform1i( uniforms.occlusionMap, 0 ); + _gl.uniform1i( uniforms.map, 1 ); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer ); + _gl.vertexAttribPointer( attributes.vertex, 2, _gl.FLOAT, false, 2 * 8, 0 ); + _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 2 * 8, 8 ); + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer ); + + _gl.disable( _gl.CULL_FACE ); + _gl.depthMask( false ); + + for ( var i = 0, l = flares.length; i < l; i ++ ) { + + size = 16 / viewportHeight; + scale.set( size * invAspect, size ); + + // calc object screen position + + var flare = flares[ i ]; + + tempPosition.set( flare.matrixWorld.elements[12], flare.matrixWorld.elements[13], flare.matrixWorld.elements[14] ); + + tempPosition.applyMatrix4( camera.matrixWorldInverse ); + tempPosition.applyProjection( camera.projectionMatrix ); + + // setup arrays for gl programs + + screenPosition.copy( tempPosition ) + + screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth; + screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight; + + // screen cull + + if ( _lensFlare.hasVertexTexture || ( + screenPositionPixels.x > 0 && + screenPositionPixels.x < viewportWidth && + screenPositionPixels.y > 0 && + screenPositionPixels.y < viewportHeight ) ) { + + // save current RGB to temp texture + + _gl.activeTexture( _gl.TEXTURE1 ); + _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture ); + _gl.copyTexImage2D( _gl.TEXTURE_2D, 0, _gl.RGB, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 ); + + + // render pink quad + + _gl.uniform1i( uniforms.renderType, 0 ); + _gl.uniform2f( uniforms.scale, scale.x, scale.y ); + _gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); + + _gl.disable( _gl.BLEND ); + _gl.enable( _gl.DEPTH_TEST ); + + _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); + + + // copy result to occlusionMap + + _gl.activeTexture( _gl.TEXTURE0 ); + _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.occlusionTexture ); + _gl.copyTexImage2D( _gl.TEXTURE_2D, 0, _gl.RGBA, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 ); + + + // restore graphics + + _gl.uniform1i( uniforms.renderType, 1 ); + _gl.disable( _gl.DEPTH_TEST ); + + _gl.activeTexture( _gl.TEXTURE1 ); + _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture ); + _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); + + + // update object positions + + flare.positionScreen.copy( screenPosition ) + + if ( flare.customUpdateCallback ) { + + flare.customUpdateCallback( flare ); + + } else { + + flare.updateLensFlares(); + + } + + // render flares + + _gl.uniform1i( uniforms.renderType, 2 ); + _gl.enable( _gl.BLEND ); + + for ( var j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) { + + var sprite = flare.lensFlares[ j ]; + + if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) { + + screenPosition.x = sprite.x; + screenPosition.y = sprite.y; + screenPosition.z = sprite.z; + + size = sprite.size * sprite.scale / viewportHeight; + + scale.x = size * invAspect; + scale.y = size; + + _gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); + _gl.uniform2f( uniforms.scale, scale.x, scale.y ); + _gl.uniform1f( uniforms.rotation, sprite.rotation ); + + _gl.uniform1f( uniforms.opacity, sprite.opacity ); + _gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b ); + + _renderer.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst ); + _renderer.setTexture( sprite.texture, 1 ); + + _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); + + } + + } + + } + + } + + // restore gl + + _gl.enable( _gl.CULL_FACE ); + _gl.enable( _gl.DEPTH_TEST ); + _gl.depthMask( true ); + + }; + + function createProgram ( shader, precision ) { + + var program = _gl.createProgram(); + + var fragmentShader = _gl.createShader( _gl.FRAGMENT_SHADER ); + var vertexShader = _gl.createShader( _gl.VERTEX_SHADER ); + + var prefix = "precision " + precision + " float;\n"; + + _gl.shaderSource( fragmentShader, prefix + shader.fragmentShader ); + _gl.shaderSource( vertexShader, prefix + shader.vertexShader ); + + _gl.compileShader( fragmentShader ); + _gl.compileShader( vertexShader ); + + _gl.attachShader( program, fragmentShader ); + _gl.attachShader( program, vertexShader ); + + _gl.linkProgram( program ); + + return program; + + }; + +}; + +// File:src/extras/renderers/plugins/ShadowMapPlugin.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.ShadowMapPlugin = function () { + + var _gl, + _renderer, + _depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin, + + _frustum = new THREE.Frustum(), + _projScreenMatrix = new THREE.Matrix4(), + + _min = new THREE.Vector3(), + _max = new THREE.Vector3(), + + _matrixPosition = new THREE.Vector3(), + + _renderList = []; + + this.init = function ( renderer ) { + + _gl = renderer.context; + _renderer = renderer; + + var depthShader = THREE.ShaderLib[ "depthRGBA" ]; + var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms ); + + _depthMaterial = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms } ); + _depthMaterialMorph = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true } ); + _depthMaterialSkin = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, skinning: true } ); + _depthMaterialMorphSkin = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true, skinning: true } ); + + _depthMaterial._shadowPass = true; + _depthMaterialMorph._shadowPass = true; + _depthMaterialSkin._shadowPass = true; + _depthMaterialMorphSkin._shadowPass = true; + + }; + + this.render = function ( scene, camera ) { + + if ( ! ( _renderer.shadowMapEnabled && _renderer.shadowMapAutoUpdate ) ) return; + + this.update( scene, camera ); + + }; + + this.update = function ( scene, camera ) { + + var i, il, j, jl, n, + + shadowMap, shadowMatrix, shadowCamera, + program, buffer, material, + webglObject, object, light, + + lights = [], + k = 0, + + fog = null; + + // set GL state for depth map + + _gl.clearColor( 1, 1, 1, 1 ); + _gl.disable( _gl.BLEND ); + + _gl.enable( _gl.CULL_FACE ); + _gl.frontFace( _gl.CCW ); + + if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) { + + _gl.cullFace( _gl.FRONT ); + + } else { + + _gl.cullFace( _gl.BACK ); + + } + + _renderer.setDepthTest( true ); + + // preprocess lights + // - skip lights that are not casting shadows + // - create virtual lights for cascaded shadow maps + + for ( i = 0, il = scene.__lights.length; i < il; i ++ ) { + + light = scene.__lights[ i ]; + + if ( ! light.castShadow ) continue; + + if ( ( light instanceof THREE.DirectionalLight ) && light.shadowCascade ) { + + for ( n = 0; n < light.shadowCascadeCount; n ++ ) { + + var virtualLight; + + if ( ! light.shadowCascadeArray[ n ] ) { + + virtualLight = createVirtualLight( light, n ); + virtualLight.originalCamera = camera; + + var gyro = new THREE.Gyroscope(); + gyro.position.copy( light.shadowCascadeOffset ); + + gyro.add( virtualLight ); + gyro.add( virtualLight.target ); + + camera.add( gyro ); + + light.shadowCascadeArray[ n ] = virtualLight; + + console.log( "Created virtualLight", virtualLight ); + + } else { + + virtualLight = light.shadowCascadeArray[ n ]; + + } + + updateVirtualLight( light, n ); + + lights[ k ] = virtualLight; + k ++; + + } + + } else { + + lights[ k ] = light; + k ++; + + } + + } + + // render depth map + + for ( i = 0, il = lights.length; i < il; i ++ ) { + + light = lights[ i ]; + + if ( ! light.shadowMap ) { + + var shadowFilter = THREE.LinearFilter; + + if ( _renderer.shadowMapType === THREE.PCFSoftShadowMap ) { + + shadowFilter = THREE.NearestFilter; + + } + + var pars = { minFilter: shadowFilter, magFilter: shadowFilter, format: THREE.RGBAFormat }; + + light.shadowMap = new THREE.WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, pars ); + light.shadowMapSize = new THREE.Vector2( light.shadowMapWidth, light.shadowMapHeight ); + + light.shadowMatrix = new THREE.Matrix4(); + + } + + if ( ! light.shadowCamera ) { + + if ( light instanceof THREE.SpotLight ) { + + light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, light.shadowMapWidth / light.shadowMapHeight, light.shadowCameraNear, light.shadowCameraFar ); + + } else if ( light instanceof THREE.DirectionalLight ) { + + light.shadowCamera = new THREE.OrthographicCamera( light.shadowCameraLeft, light.shadowCameraRight, light.shadowCameraTop, light.shadowCameraBottom, light.shadowCameraNear, light.shadowCameraFar ); + + } else { + + console.error( "Unsupported light type for shadow" ); + continue; + + } + + scene.add( light.shadowCamera ); + + if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + + } + + if ( light.shadowCameraVisible && ! light.cameraHelper ) { + + light.cameraHelper = new THREE.CameraHelper( light.shadowCamera ); + light.shadowCamera.add( light.cameraHelper ); + + } + + if ( light.isVirtual && virtualLight.originalCamera == camera ) { + + updateShadowCamera( camera, light ); + + } + + shadowMap = light.shadowMap; + shadowMatrix = light.shadowMatrix; + shadowCamera = light.shadowCamera; + + shadowCamera.position.setFromMatrixPosition( light.matrixWorld ); + _matrixPosition.setFromMatrixPosition( light.target.matrixWorld ); + shadowCamera.lookAt( _matrixPosition ); + shadowCamera.updateMatrixWorld(); + + shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld ); + + if ( light.cameraHelper ) light.cameraHelper.visible = light.shadowCameraVisible; + if ( light.shadowCameraVisible ) light.cameraHelper.update(); + + // compute shadow matrix + + shadowMatrix.set( 0.5, 0.0, 0.0, 0.5, + 0.0, 0.5, 0.0, 0.5, + 0.0, 0.0, 0.5, 0.5, + 0.0, 0.0, 0.0, 1.0 ); + + shadowMatrix.multiply( shadowCamera.projectionMatrix ); + shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); + + // update camera matrices and frustum + + _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); + _frustum.setFromMatrix( _projScreenMatrix ); + + // render shadow map + + _renderer.setRenderTarget( shadowMap ); + _renderer.clear(); + + // set object matrices & frustum culling + + _renderList.length = 0; + projectObject(scene,scene,shadowCamera); + + + // render regular objects + + var objectMaterial, useMorphing, useSkinning; + + for ( j = 0, jl = _renderList.length; j < jl; j ++ ) { + + webglObject = _renderList[ j ]; + + object = webglObject.object; + buffer = webglObject.buffer; + + // culling is overriden globally for all objects + // while rendering depth map + + // need to deal with MeshFaceMaterial somehow + // in that case just use the first of material.materials for now + // (proper solution would require to break objects by materials + // similarly to regular rendering and then set corresponding + // depth materials per each chunk instead of just once per object) + + objectMaterial = getObjectMaterial( object ); + + useMorphing = object.geometry.morphTargets !== undefined && object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets; + useSkinning = object instanceof THREE.SkinnedMesh && objectMaterial.skinning; + + if ( object.customDepthMaterial ) { + + material = object.customDepthMaterial; + + } else if ( useSkinning ) { + + material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin; + + } else if ( useMorphing ) { + + material = _depthMaterialMorph; + + } else { + + material = _depthMaterial; + + } + + _renderer.setMaterialFaces( objectMaterial ); + + if ( buffer instanceof THREE.BufferGeometry ) { + + _renderer.renderBufferDirect( shadowCamera, scene.__lights, fog, material, buffer, object ); + + } else { + + _renderer.renderBuffer( shadowCamera, scene.__lights, fog, material, buffer, object ); + + } + + } + + // set matrices and render immediate objects + + var renderList = scene.__webglObjectsImmediate; + + for ( j = 0, jl = renderList.length; j < jl; j ++ ) { + + webglObject = renderList[ j ]; + object = webglObject.object; + + if ( object.visible && object.castShadow ) { + + object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); + + _renderer.renderImmediateObject( shadowCamera, scene.__lights, fog, _depthMaterial, object ); + + } + + } + + } + + // restore GL state + + var clearColor = _renderer.getClearColor(), + clearAlpha = _renderer.getClearAlpha(); + + _gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha ); + _gl.enable( _gl.BLEND ); + + if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) { + + _gl.cullFace( _gl.BACK ); + + } + + }; + + function projectObject(scene, object,shadowCamera){ + + if ( object.visible ) { + + var webglObjects = scene.__webglObjects[object.id]; + + if (webglObjects && object.castShadow && (object.frustumCulled === false || _frustum.intersectsObject( object ) === true) ) { + + + for (var i = 0, l = webglObjects.length; i < l; i++){ + + var webglObject = webglObjects[i]; + + object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); + _renderList.push(webglObject); + + } + } + + for(var i = 0, l = object.children.length; i < l; i++) { + + projectObject(scene, object.children[i],shadowCamera); + } + + } + } + + function createVirtualLight( light, cascade ) { + + var virtualLight = new THREE.DirectionalLight(); + + virtualLight.isVirtual = true; + + virtualLight.onlyShadow = true; + virtualLight.castShadow = true; + + virtualLight.shadowCameraNear = light.shadowCameraNear; + virtualLight.shadowCameraFar = light.shadowCameraFar; + + virtualLight.shadowCameraLeft = light.shadowCameraLeft; + virtualLight.shadowCameraRight = light.shadowCameraRight; + virtualLight.shadowCameraBottom = light.shadowCameraBottom; + virtualLight.shadowCameraTop = light.shadowCameraTop; + + virtualLight.shadowCameraVisible = light.shadowCameraVisible; + + virtualLight.shadowDarkness = light.shadowDarkness; + + virtualLight.shadowBias = light.shadowCascadeBias[ cascade ]; + virtualLight.shadowMapWidth = light.shadowCascadeWidth[ cascade ]; + virtualLight.shadowMapHeight = light.shadowCascadeHeight[ cascade ]; + + virtualLight.pointsWorld = []; + virtualLight.pointsFrustum = []; + + var pointsWorld = virtualLight.pointsWorld, + pointsFrustum = virtualLight.pointsFrustum; + + for ( var i = 0; i < 8; i ++ ) { + + pointsWorld[ i ] = new THREE.Vector3(); + pointsFrustum[ i ] = new THREE.Vector3(); + + } + + var nearZ = light.shadowCascadeNearZ[ cascade ]; + var farZ = light.shadowCascadeFarZ[ cascade ]; + + pointsFrustum[ 0 ].set( - 1, - 1, nearZ ); + pointsFrustum[ 1 ].set( 1, - 1, nearZ ); + pointsFrustum[ 2 ].set( - 1, 1, nearZ ); + pointsFrustum[ 3 ].set( 1, 1, nearZ ); + + pointsFrustum[ 4 ].set( - 1, - 1, farZ ); + pointsFrustum[ 5 ].set( 1, - 1, farZ ); + pointsFrustum[ 6 ].set( - 1, 1, farZ ); + pointsFrustum[ 7 ].set( 1, 1, farZ ); + + return virtualLight; + + } + + // Synchronize virtual light with the original light + + function updateVirtualLight( light, cascade ) { + + var virtualLight = light.shadowCascadeArray[ cascade ]; + + virtualLight.position.copy( light.position ); + virtualLight.target.position.copy( light.target.position ); + virtualLight.lookAt( virtualLight.target ); + + virtualLight.shadowCameraVisible = light.shadowCameraVisible; + virtualLight.shadowDarkness = light.shadowDarkness; + + virtualLight.shadowBias = light.shadowCascadeBias[ cascade ]; + + var nearZ = light.shadowCascadeNearZ[ cascade ]; + var farZ = light.shadowCascadeFarZ[ cascade ]; + + var pointsFrustum = virtualLight.pointsFrustum; + + pointsFrustum[ 0 ].z = nearZ; + pointsFrustum[ 1 ].z = nearZ; + pointsFrustum[ 2 ].z = nearZ; + pointsFrustum[ 3 ].z = nearZ; + + pointsFrustum[ 4 ].z = farZ; + pointsFrustum[ 5 ].z = farZ; + pointsFrustum[ 6 ].z = farZ; + pointsFrustum[ 7 ].z = farZ; + + } + + // Fit shadow camera's ortho frustum to camera frustum + + function updateShadowCamera( camera, light ) { + + var shadowCamera = light.shadowCamera, + pointsFrustum = light.pointsFrustum, + pointsWorld = light.pointsWorld; + + _min.set( Infinity, Infinity, Infinity ); + _max.set( - Infinity, - Infinity, - Infinity ); + + for ( var i = 0; i < 8; i ++ ) { + + var p = pointsWorld[ i ]; + + p.copy( pointsFrustum[ i ] ); + THREE.ShadowMapPlugin.__projector.unprojectVector( p, camera ); + + p.applyMatrix4( shadowCamera.matrixWorldInverse ); + + if ( p.x < _min.x ) _min.x = p.x; + if ( p.x > _max.x ) _max.x = p.x; + + if ( p.y < _min.y ) _min.y = p.y; + if ( p.y > _max.y ) _max.y = p.y; + + if ( p.z < _min.z ) _min.z = p.z; + if ( p.z > _max.z ) _max.z = p.z; + + } + + shadowCamera.left = _min.x; + shadowCamera.right = _max.x; + shadowCamera.top = _max.y; + shadowCamera.bottom = _min.y; + + // can't really fit near/far + //shadowCamera.near = _min.z; + //shadowCamera.far = _max.z; + + shadowCamera.updateProjectionMatrix(); + + } + + // For the moment just ignore objects that have multiple materials with different animation methods + // Only the first material will be taken into account for deciding which depth material to use for shadow maps + + function getObjectMaterial( object ) { + + return object.material instanceof THREE.MeshFaceMaterial + ? object.material.materials[ 0 ] + : object.material; + + }; + +}; + +THREE.ShadowMapPlugin.__projector = new THREE.Projector(); + +// File:src/extras/renderers/plugins/SpritePlugin.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.SpritePlugin = function () { + + var _gl, _renderer, _texture; + + var sprites = []; + + var vertices, faces, vertexBuffer, elementBuffer; + var program, attributes, uniforms; + + this.init = function ( renderer ) { + + _gl = renderer.context; + _renderer = renderer; + + vertices = new Float32Array( [ + - 0.5, - 0.5, 0, 0, + 0.5, - 0.5, 1, 0, + 0.5, 0.5, 1, 1, + - 0.5, 0.5, 0, 1 + ] ); + + faces = new Uint16Array( [ + 0, 1, 2, + 0, 2, 3 + ] ); + + vertexBuffer = _gl.createBuffer(); + elementBuffer = _gl.createBuffer(); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, vertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, vertices, _gl.STATIC_DRAW ); + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); + _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, faces, _gl.STATIC_DRAW ); + + program = createProgram(); + + attributes = { + position: _gl.getAttribLocation ( program, 'position' ), + uv: _gl.getAttribLocation ( program, 'uv' ) + }; + + uniforms = { + uvOffset: _gl.getUniformLocation( program, 'uvOffset' ), + uvScale: _gl.getUniformLocation( program, 'uvScale' ), + + rotation: _gl.getUniformLocation( program, 'rotation' ), + scale: _gl.getUniformLocation( program, 'scale' ), + + color: _gl.getUniformLocation( program, 'color' ), + map: _gl.getUniformLocation( program, 'map' ), + opacity: _gl.getUniformLocation( program, 'opacity' ), + + modelViewMatrix: _gl.getUniformLocation( program, 'modelViewMatrix' ), + projectionMatrix: _gl.getUniformLocation( program, 'projectionMatrix' ), + + fogType: _gl.getUniformLocation( program, 'fogType' ), + fogDensity: _gl.getUniformLocation( program, 'fogDensity' ), + fogNear: _gl.getUniformLocation( program, 'fogNear' ), + fogFar: _gl.getUniformLocation( program, 'fogFar' ), + fogColor: _gl.getUniformLocation( program, 'fogColor' ), + + alphaTest: _gl.getUniformLocation( program, 'alphaTest' ) + }; + + var canvas = document.createElement( 'canvas' ); + canvas.width = 8; + canvas.height = 8; + + var context = canvas.getContext( '2d' ); + context.fillStyle = 'white'; + context.fillRect( 0, 0, 8, 8 ); + + _texture = new THREE.Texture( canvas ); + _texture.needsUpdate = true; + + }; + + this.render = function ( scene, camera, viewportWidth, viewportHeight ) { + + sprites.length = 0; + + scene.traverseVisible( function ( child ) { + + if ( child instanceof THREE.Sprite ) { + + sprites.push( child ); + + } + + } ); + + if ( sprites.length === 0 ) return; + + // setup gl + + _gl.useProgram( program ); + + _gl.enableVertexAttribArray( attributes.position ); + _gl.enableVertexAttribArray( attributes.uv ); + + _gl.disable( _gl.CULL_FACE ); + _gl.enable( _gl.BLEND ); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, vertexBuffer ); + _gl.vertexAttribPointer( attributes.position, 2, _gl.FLOAT, false, 2 * 8, 0 ); + _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 2 * 8, 8 ); + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); + + _gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); + + _gl.activeTexture( _gl.TEXTURE0 ); + _gl.uniform1i( uniforms.map, 0 ); + + var oldFogType = 0; + var sceneFogType = 0; + var fog = scene.fog; + + if ( fog ) { + + _gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b ); + + if ( fog instanceof THREE.Fog ) { + + _gl.uniform1f( uniforms.fogNear, fog.near ); + _gl.uniform1f( uniforms.fogFar, fog.far ); + + _gl.uniform1i( uniforms.fogType, 1 ); + oldFogType = 1; + sceneFogType = 1; + + } else if ( fog instanceof THREE.FogExp2 ) { + + _gl.uniform1f( uniforms.fogDensity, fog.density ); + + _gl.uniform1i( uniforms.fogType, 2 ); + oldFogType = 2; + sceneFogType = 2; + + } + + } else { + + _gl.uniform1i( uniforms.fogType, 0 ); + oldFogType = 0; + sceneFogType = 0; + + } + + + // update positions and sort + + for ( var i = 0, l = sprites.length; i < l; i ++ ) { + + var sprite = sprites[ i ]; + var material = sprite.material; + + sprite._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld ); + sprite.z = - sprite._modelViewMatrix.elements[ 14 ]; + + } + + sprites.sort( painterSortStable ); + + // render all sprites + + var scale = []; + + for ( var i = 0, l = sprites.length; i < l; i ++ ) { + + var sprite = sprites[ i ]; + var material = sprite.material; + + _gl.uniform1f( uniforms.alphaTest, material.alphaTest ); + _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite._modelViewMatrix.elements ); + + scale[ 0 ] = sprite.scale.x; + scale[ 1 ] = sprite.scale.y; + + var fogType = 0; + + if ( scene.fog && material.fog ) { + + fogType = sceneFogType; + + } + + if ( oldFogType !== fogType ) { + + _gl.uniform1i( uniforms.fogType, fogType ); + oldFogType = fogType; + + } + + if ( material.map !== null ) { + + _gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y ); + _gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y ); + + } else { + + _gl.uniform2f( uniforms.uvOffset, 0, 0 ); + _gl.uniform2f( uniforms.uvScale, 1, 1 ); + + } + + _gl.uniform1f( uniforms.opacity, material.opacity ); + _gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b ); + + _gl.uniform1f( uniforms.rotation, material.rotation ); + _gl.uniform2fv( uniforms.scale, scale ); + + _renderer.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); + _renderer.setDepthTest( material.depthTest ); + _renderer.setDepthWrite( material.depthWrite ); + + if ( material.map && material.map.image && material.map.image.width ) { + + _renderer.setTexture( material.map, 0 ); + + } else { + + _renderer.setTexture( _texture, 0 ); + + } + + _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); + + } + + // restore gl + + _gl.enable( _gl.CULL_FACE ); + + }; + + function createProgram () { + + var program = _gl.createProgram(); + + var vertexShader = _gl.createShader( _gl.VERTEX_SHADER ); + var fragmentShader = _gl.createShader( _gl.FRAGMENT_SHADER ); + + _gl.shaderSource( vertexShader, [ + + 'precision ' + _renderer.getPrecision() + ' float;', + + 'uniform mat4 modelViewMatrix;', + 'uniform mat4 projectionMatrix;', + 'uniform float rotation;', + 'uniform vec2 scale;', + 'uniform vec2 uvOffset;', + 'uniform vec2 uvScale;', + + 'attribute vec2 position;', + 'attribute vec2 uv;', + + 'varying vec2 vUV;', + + 'void main() {', + + 'vUV = uvOffset + uv * uvScale;', + + 'vec2 alignedPosition = position * scale;', + + 'vec2 rotatedPosition;', + 'rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;', + 'rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;', + + 'vec4 finalPosition;', + + 'finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );', + 'finalPosition.xy += rotatedPosition;', + 'finalPosition = projectionMatrix * finalPosition;', + + 'gl_Position = finalPosition;', + + '}' + + ].join( '\n' ) ); + + _gl.shaderSource( fragmentShader, [ + + 'precision ' + _renderer.getPrecision() + ' float;', + + 'uniform vec3 color;', + 'uniform sampler2D map;', + 'uniform float opacity;', + + 'uniform int fogType;', + 'uniform vec3 fogColor;', + 'uniform float fogDensity;', + 'uniform float fogNear;', + 'uniform float fogFar;', + 'uniform float alphaTest;', + + 'varying vec2 vUV;', + + 'void main() {', + + 'vec4 texture = texture2D( map, vUV );', + + 'if ( texture.a < alphaTest ) discard;', + + 'gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );', + + 'if ( fogType > 0 ) {', + + 'float depth = gl_FragCoord.z / gl_FragCoord.w;', + 'float fogFactor = 0.0;', + + 'if ( fogType == 1 ) {', + + 'fogFactor = smoothstep( fogNear, fogFar, depth );', + + '} else {', + + 'const float LOG2 = 1.442695;', + 'float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );', + 'fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );', + + '}', + + 'gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );', + + '}', + + '}' + + ].join( '\n' ) ); + + _gl.compileShader( vertexShader ); + _gl.compileShader( fragmentShader ); + + _gl.attachShader( program, vertexShader ); + _gl.attachShader( program, fragmentShader ); + + _gl.linkProgram( program ); + + return program; + + }; + + function painterSortStable ( a, b ) { + + if ( a.z !== b.z ) { + + return b.z - a.z; + + } else { + + return b.id - a.id; + + } + + }; + +}; + +// File:src/extras/renderers/plugins/DepthPassPlugin.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.DepthPassPlugin = function () { + + this.enabled = false; + this.renderTarget = null; + + var _gl, + _renderer, + _depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin, + + _frustum = new THREE.Frustum(), + _projScreenMatrix = new THREE.Matrix4(), + _renderList = []; + + this.init = function ( renderer ) { + + _gl = renderer.context; + _renderer = renderer; + + var depthShader = THREE.ShaderLib[ "depthRGBA" ]; + var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms ); + + _depthMaterial = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms } ); + _depthMaterialMorph = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true } ); + _depthMaterialSkin = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, skinning: true } ); + _depthMaterialMorphSkin = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true, skinning: true } ); + + _depthMaterial._shadowPass = true; + _depthMaterialMorph._shadowPass = true; + _depthMaterialSkin._shadowPass = true; + _depthMaterialMorphSkin._shadowPass = true; + + }; + + this.render = function ( scene, camera ) { + + if ( ! this.enabled ) return; + + this.update( scene, camera ); + + }; + + this.update = function ( scene, camera ) { + + var i, il, j, jl, n, + + program, buffer, material, + webglObject, object, light, + renderList, + + fog = null; + + // set GL state for depth map + + _gl.clearColor( 1, 1, 1, 1 ); + _gl.disable( _gl.BLEND ); + + _renderer.setDepthTest( true ); + + // update scene + + if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + + // update camera matrices and frustum + + camera.matrixWorldInverse.getInverse( camera.matrixWorld ); + + _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); + _frustum.setFromMatrix( _projScreenMatrix ); + + // render depth map + + _renderer.setRenderTarget( this.renderTarget ); + _renderer.clear(); + + // set object matrices & frustum culling + + _renderList.length = 0; + projectObject(scene,scene,camera); + + // render regular objects + + var objectMaterial, useMorphing, useSkinning; + + for ( j = 0, jl = _renderList.length; j < jl; j ++ ) { + + webglObject = _renderList[ j ]; + + object = webglObject.object; + buffer = webglObject.buffer; + + // todo: create proper depth material for particles + + if ( object instanceof THREE.PointCloud && ! object.customDepthMaterial ) continue; + + objectMaterial = getObjectMaterial( object ); + + if ( objectMaterial ) _renderer.setMaterialFaces( object.material ); + + useMorphing = object.geometry.morphTargets !== undefined && object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets; + useSkinning = object instanceof THREE.SkinnedMesh && objectMaterial.skinning; + + if ( object.customDepthMaterial ) { + + material = object.customDepthMaterial; + + } else if ( useSkinning ) { + + material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin; + + } else if ( useMorphing ) { + + material = _depthMaterialMorph; + + } else { + + material = _depthMaterial; + + } + + if ( buffer instanceof THREE.BufferGeometry ) { + + _renderer.renderBufferDirect( camera, scene.__lights, fog, material, buffer, object ); + + } else { + + _renderer.renderBuffer( camera, scene.__lights, fog, material, buffer, object ); + + } + + + } + + // set matrices and render immediate objects + + renderList = scene.__webglObjectsImmediate; + + for ( j = 0, jl = renderList.length; j < jl; j ++ ) { + + webglObject = renderList[ j ]; + object = webglObject.object; + + if ( object.visible ) { + + object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + + _renderer.renderImmediateObject( camera, scene.__lights, fog, _depthMaterial, object ); + + } + + } + + // restore GL state + + var clearColor = _renderer.getClearColor(), + clearAlpha = _renderer.getClearAlpha(); + + _gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha ); + _gl.enable( _gl.BLEND ); + + }; + + function projectObject(scene, object,camera){ + + if ( object.visible ) { + + var webglObjects = scene.__webglObjects[object.id]; + + if (webglObjects && (object.frustumCulled === false || _frustum.intersectsObject( object ) === true) ) { + + + for (var i = 0, l = webglObjects.length; i < l; i++){ + + var webglObject = webglObjects[i]; + + object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + _renderList.push(webglObject); + + } + } + + for(var i = 0, l = object.children.length; i < l; i++) { + + projectObject(scene, object.children[i], camera); + } + + } + } + + // For the moment just ignore objects that have multiple materials with different animation methods + // Only the first material will be taken into account for deciding which depth material to use + + function getObjectMaterial( object ) { + + return object.material instanceof THREE.MeshFaceMaterial + ? object.material.materials[ 0 ] + : object.material; + + }; + +}; + + +// File:src/extras/shaders/ShaderFlares.js + +/** + * @author mikael emtinger / http://gomo.se/ + */ + +THREE.ShaderFlares = { + + 'lensFlareVertexTexture': { + + vertexShader: [ + + "uniform lowp int renderType;", + + "uniform vec3 screenPosition;", + "uniform vec2 scale;", + "uniform float rotation;", + + "uniform sampler2D occlusionMap;", + + "attribute vec2 position;", + "attribute vec2 uv;", + + "varying vec2 vUV;", + "varying float vVisibility;", + + "void main() {", + + "vUV = uv;", + + "vec2 pos = position;", + + "if( renderType == 2 ) {", + + "vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );", + + "vVisibility = visibility.r / 9.0;", + "vVisibility *= 1.0 - visibility.g / 9.0;", + "vVisibility *= visibility.b / 9.0;", + "vVisibility *= 1.0 - visibility.a / 9.0;", + + "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", + "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", + + "}", + + "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform lowp int renderType;", + + "uniform sampler2D map;", + "uniform float opacity;", + "uniform vec3 color;", + + "varying vec2 vUV;", + "varying float vVisibility;", + + "void main() {", + + // pink square + + "if( renderType == 0 ) {", + + "gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );", + + // restore + + "} else if( renderType == 1 ) {", + + "gl_FragColor = texture2D( map, vUV );", + + // flare + + "} else {", + + "vec4 texture = texture2D( map, vUV );", + "texture.a *= opacity * vVisibility;", + "gl_FragColor = texture;", + "gl_FragColor.rgb *= color;", + + "}", + + "}" + ].join( "\n" ) + + }, + + + 'lensFlare': { + + vertexShader: [ + + "uniform lowp int renderType;", + + "uniform vec3 screenPosition;", + "uniform vec2 scale;", + "uniform float rotation;", + + "attribute vec2 position;", + "attribute vec2 uv;", + + "varying vec2 vUV;", + + "void main() {", + + "vUV = uv;", + + "vec2 pos = position;", + + "if( renderType == 2 ) {", + + "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", + "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", + + "}", + + "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "precision mediump float;", + + "uniform lowp int renderType;", + + "uniform sampler2D map;", + "uniform sampler2D occlusionMap;", + "uniform float opacity;", + "uniform vec3 color;", + + "varying vec2 vUV;", + + "void main() {", + + // pink square + + "if( renderType == 0 ) {", + + "gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );", + + // restore + + "} else if( renderType == 1 ) {", + + "gl_FragColor = texture2D( map, vUV );", + + // flare + + "} else {", + + "float visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a;", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a;", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a;", + "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;", + "visibility = ( 1.0 - visibility / 4.0 );", + + "vec4 texture = texture2D( map, vUV );", + "texture.a *= opacity * visibility;", + "gl_FragColor = texture;", + "gl_FragColor.rgb *= color;", + + "}", + + "}" + + ].join( "\n" ) + + } + +}; + diff --git a/package.json b/package.json index b58c00a5f..94a40ffe7 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,33 @@ { - "name": "cannon", - "version": "0.6.2", - "description": "A lightweight 3D physics engine written in JavaScript.", - "homepage": "https://github.com/schteppe/cannon.js", - "author": "Stefan Hedman (http://steffe.se)", + "name": "@feng3d/cannon", + "version": "0.5.1", + "description": "3d物理引擎", + "main": "lib/index.js", + "module": "lib/index.es.js", + "bundle": "dist/index.js", + "types": "dist/index.d.ts", + "namespace": "CANNON", + "author": "feng", + "license": "MIT", + "origin": { + "name": "0.4.1", + "version": "0.6.2", + "description": "A lightweight 3D physics engine written in JavaScript.", + "homepage": "https://github.com/schteppe/cannon.js", + "author": "Stefan Hedman (http://steffe.se)" + }, + "scripts": { + "start": "npm run watch", + "clean": "rimraf \"{lib,dist}\"", + "build": "npm run clean && rollup -c", + "build:prod": "npm run clean && cross-env NODE_ENV=production rollup -c", + "watch": "rollup -cw", + "test": "mocha", + "lint": "eslint --ext .js --ext .ts src test rollup.config.js --ignore-path .gitignore --max-warnings 0", + "lintfix": "npm run lint -- --fix", + "docs": "typedoc", + "release": "npm run build:prod && npm publish" + }, "keywords": [ "cannon.js", "cannon", @@ -11,34 +35,45 @@ "engine", "3d" ], - "main": "./src/Cannon.js", - "engines": { - "node": "*" - }, "repository": { "type": "git", - "url": "https://github.com/schteppe/cannon.js.git" + "url": "https://gitlab.com/feng3d/cannon.git" }, - "bugs": { - "url": "https://github.com/schteppe/cannon.js/issues" + "publishConfig": { + "access": "public" }, - "licenses": [ - { - "type": "MIT" - } + "files": [ + "dist/", + "lib", + "src" ], "devDependencies": { - "jshint": "latest", - "uglify-js": "latest", - "nodeunit": "^0.9.0", - "grunt": "~0.4.0", - "grunt-contrib-jshint": "~0.1.1", - "grunt-contrib-nodeunit": "^0.4.1", - "grunt-contrib-concat": "~0.1.3", - "grunt-contrib-uglify": "^0.5.1", - "grunt-browserify": "^2.1.4", - "grunt-contrib-yuidoc": "^0.5.2", - "browserify": "*" + "@types/node": "^18.6.1", + "@types/mocha": "9.1.0", + "@typescript-eslint/eslint-plugin": "5.17.0", + "@typescript-eslint/parser": "5.17.0", + "tslib": "^2.4.0", + "cross-env": "7.0.3", + "eslint": "8.12.0", + "mocha": "9.2.2", + "rimraf": "3.0.2", + "rollup": "2.70.1", + "rollup-plugin-buble": "0.19.8", + "rollup-plugin-commonjs": "10.1.0", + "rollup-plugin-dts": "4.2.0", + "rollup-plugin-json": "4.0.0", + "rollup-plugin-node-resolve": "5.2.0", + "rollup-plugin-replace": "2.2.0", + "rollup-plugin-sourcemaps": "0.6.3", + "rollup-plugin-string": "3.0.0", + "rollup-plugin-terser": "7.0.2", + "rollup-plugin-typescript": "1.0.1", + "ts-node": "10.7.0", + "typedoc": "0.21.4", + "typedoc-plugin-sourcefile-url": "1.0.6", + "typescript": "4.3.5" }, - "dependencies": {} + "dependencies": { + "feng3d": "^0.5.0" + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 000000000..de4357963 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,2138 @@ +lockfileVersion: 5.3 + +specifiers: + '@types/mocha': 9.1.0 + '@types/node': ^18.6.1 + '@typescript-eslint/eslint-plugin': 5.17.0 + '@typescript-eslint/parser': 5.17.0 + cross-env: 7.0.3 + eslint: 8.12.0 + feng3d: ^0.5.0 + mocha: 9.2.2 + rimraf: 3.0.2 + rollup: 2.70.1 + rollup-plugin-buble: 0.19.8 + rollup-plugin-commonjs: 10.1.0 + rollup-plugin-dts: 4.2.0 + rollup-plugin-json: 4.0.0 + rollup-plugin-node-resolve: 5.2.0 + rollup-plugin-replace: 2.2.0 + rollup-plugin-sourcemaps: 0.6.3 + rollup-plugin-string: 3.0.0 + rollup-plugin-terser: 7.0.2 + rollup-plugin-typescript: 1.0.1 + ts-node: 10.7.0 + tslib: ^2.4.0 + typedoc: 0.21.4 + typedoc-plugin-sourcefile-url: 1.0.6 + typescript: 4.3.5 + +dependencies: + feng3d: 0.5.0 + +devDependencies: + '@types/mocha': 9.1.0 + '@types/node': 18.6.1 + '@typescript-eslint/eslint-plugin': 5.17.0_62eb2401ea518356689583614b0033ac + '@typescript-eslint/parser': 5.17.0_eslint@8.12.0+typescript@4.3.5 + cross-env: 7.0.3 + eslint: 8.12.0 + mocha: 9.2.2 + rimraf: 3.0.2 + rollup: 2.70.1 + rollup-plugin-buble: 0.19.8 + rollup-plugin-commonjs: 10.1.0_rollup@2.70.1 + rollup-plugin-dts: 4.2.0_rollup@2.70.1+typescript@4.3.5 + rollup-plugin-json: 4.0.0 + rollup-plugin-node-resolve: 5.2.0_rollup@2.70.1 + rollup-plugin-replace: 2.2.0 + rollup-plugin-sourcemaps: 0.6.3_@types+node@18.6.1+rollup@2.70.1 + rollup-plugin-string: 3.0.0 + rollup-plugin-terser: 7.0.2_rollup@2.70.1 + rollup-plugin-typescript: 1.0.1_tslib@2.4.0+typescript@4.3.5 + ts-node: 10.7.0_460543de4106407bf2e2de97a08caad7 + tslib: 2.4.0 + typedoc: 0.21.4_typescript@4.3.5 + typedoc-plugin-sourcefile-url: 1.0.6_typedoc@0.21.4 + typescript: 4.3.5 + +packages: + + /@babel/code-frame/7.18.6: + resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.18.6 + dev: true + + /@babel/helper-validator-identifier/7.18.6: + resolution: {integrity: sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/highlight/7.18.6: + resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.18.6 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + + /@cspotcode/source-map-consumer/0.8.0: + resolution: {integrity: sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==} + engines: {node: '>= 12'} + dev: true + + /@cspotcode/source-map-support/0.7.0: + resolution: {integrity: sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==} + engines: {node: '>=12'} + dependencies: + '@cspotcode/source-map-consumer': 0.8.0 + dev: true + + /@eslint/eslintrc/1.3.0: + resolution: {integrity: sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.3.2 + globals: 13.17.0 + ignore: 5.2.0 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@feng3d/assets/0.5.0: + resolution: {integrity: sha512-H5qm47A6mgaxsE7XK/PaYHBM3CCjr+7C2UXXVVck8CBzYvdF68MU5OqtpEboQ128/rEWdfQ8B3lTwV6Yy4FiUQ==} + dependencies: + '@feng3d/core': 0.5.0 + '@feng3d/event': 0.5.0 + '@feng3d/objectview': 0.5.0 + '@feng3d/polyfill': 0.5.0 + '@feng3d/serialization': 0.5.0 + '@feng3d/watcher': 0.5.0 + dev: false + + /@feng3d/bezier/0.5.0: + resolution: {integrity: sha512-aIf+qa8TT+Kh5pJkHzR1BSTzv7dCQs09jwV4eADWVUIa612i8ZLhCQY4bl4qztZahIUVfK4YERR548Ztcu2N2w==} + dev: false + + /@feng3d/core/0.5.0: + resolution: {integrity: sha512-l/oYjLBkf20IzdiEq7w/bAm7KP93A+Mqw6fPtfEFYa1YSB+OPBuBsJwUAdwV1Yx0JL3Ku68LQutYeByh0aavpQ==} + dependencies: + '@feng3d/event': 0.5.0 + '@feng3d/filesystem': 0.5.0 + '@feng3d/math': 0.5.0 + '@feng3d/objectview': 0.5.0 + '@feng3d/polyfill': 0.5.0 + '@feng3d/renderer': 0.5.0 + '@feng3d/serialization': 0.5.0 + '@feng3d/shortcut': 0.5.0 + '@feng3d/task': 0.5.0 + '@feng3d/watcher': 0.5.0 + dev: false + + /@feng3d/event/0.5.0: + resolution: {integrity: sha512-PUXQpd4+zcps5LWzBTgudTxEQxDGa4jiSMi1rYT3M8V7yLMmyMDP761lCeqyxKX+Qsh2jNnrgiyR6yFkZDlJWg==} + dev: false + + /@feng3d/filesystem/0.5.0: + resolution: {integrity: sha512-FoCEmgiYioORbt0hP2Wpx5VKO0FPs+9J7y11JjuC085kJGSgRx+CeqUx0BarYE76IjagVr1ZU/zhlxMH5BIIlQ==} + dependencies: + '@feng3d/event': 0.5.0 + '@feng3d/polyfill': 0.5.0 + '@feng3d/task': 0.5.0 + dev: false + + /@feng3d/math/0.5.0: + resolution: {integrity: sha512-OC3rpUk12Ophl7+nP3aAJ4xGWW5/loAEd51/Po0gEu08v/adLj2vzmTAaIHEge1hDnQjBhx2oRiyC6W4+20ZDQ==} + dependencies: + '@feng3d/bezier': 0.5.0 + '@feng3d/objectview': 0.5.0 + '@feng3d/polyfill': 0.5.0 + '@feng3d/serialization': 0.5.0 + dev: false + + /@feng3d/objectview/0.5.0: + resolution: {integrity: sha512-bZqSudINlflR5XslN3bMrJEkKdySWValsYuE1oXQnWrd98bY+x1LoCP7pQsNvN4u8HwrWeMHgAn38r5UJFQibA==} + dev: false + + /@feng3d/parsers/0.5.0: + resolution: {integrity: sha512-waLVEe8gABSBqfXaqI5rZrX62Skwfl2zUBPPri2wHZPvVff3QzlocXoEvBrckM7YYt3FFnpTqmgBsCl18Z2uaw==} + dependencies: + '@feng3d/core': 0.5.0 + '@feng3d/event': 0.5.0 + '@feng3d/filesystem': 0.5.0 + '@feng3d/math': 0.5.0 + '@feng3d/objectview': 0.5.0 + '@feng3d/polyfill': 0.5.0 + '@feng3d/renderer': 0.5.0 + '@feng3d/serialization': 0.5.0 + '@feng3d/watcher': 0.5.0 + dev: false + + /@feng3d/particlesystem/0.5.0: + resolution: {integrity: sha512-DpGpjoM4IllMg6P7IFVIpDQd5EGt31D+2BQomq2YmDm37SIDaEdLai7eNyDhhLRN/DFtl8s/Q3XvbcUCKxCvAQ==} + dependencies: + '@feng3d/core': 0.5.0 + '@feng3d/event': 0.5.0 + '@feng3d/filesystem': 0.5.0 + '@feng3d/math': 0.5.0 + '@feng3d/objectview': 0.5.0 + '@feng3d/polyfill': 0.5.0 + '@feng3d/renderer': 0.5.0 + '@feng3d/serialization': 0.5.0 + '@feng3d/watcher': 0.5.0 + dev: false + + /@feng3d/polyfill/0.5.0: + resolution: {integrity: sha512-kOJgU1t5ibVIUO2Xqa39Gq0I5yCXCMjuVYfXWZn4w0fv3zmdVSTDW7Zfodgszaz25/0XxmONHeZJkVnAaryP2Q==} + dev: false + + /@feng3d/renderer/0.5.0: + resolution: {integrity: sha512-heNIxB5vZq+ekcggeDyX6pu2lP85SQhkCKSX7vHbUsEaQgJfkLmrkYxTIRQlhoBpvvFFSJQmI3H6LjV+g7zAcA==} + dependencies: + '@feng3d/objectview': 0.5.0 + '@feng3d/polyfill': 0.5.0 + '@feng3d/serialization': 0.5.0 + '@feng3d/watcher': 0.5.0 + dev: false + + /@feng3d/serialization/0.5.0: + resolution: {integrity: sha512-DUrx+EbuaikehKyd644WUWRbQKe3weXWSmbqQbLmGCl4NxyEzF/XPemrTP6b9+OStucHKeXVNTrS0FIyEDxppA==} + dependencies: + '@feng3d/polyfill': 0.5.0 + dev: false + + /@feng3d/shortcut/0.5.0: + resolution: {integrity: sha512-C5QchoDHAirGjit9lfXQC7KgoiTxGk6Vt28BMTL5JHVdEIgky5cgHXobIP4p/N7XupX3K62Ik6eSfinVnNaZtg==} + dependencies: + '@feng3d/event': 0.5.0 + dev: false + + /@feng3d/task/0.5.0: + resolution: {integrity: sha512-zYJdCwIDyi8PruDUI5DvD/PTFvXjSqp/2PnodxSmlMqzrMd0Uk+EtO2ux0e9Yl8tN5D0r4i4gDvtDPLvlBfjdg==} + dev: false + + /@feng3d/terrain/0.5.0: + resolution: {integrity: sha512-XmKx9CgNdIcoIIyMMkIE9s2PT7IscDIEkK12jqRriwaH/y8f+YnS9Dj0ZJ7uQ1IJBNsd/gVYdorm7Zygi5Kaig==} + dependencies: + '@feng3d/core': 0.5.0 + '@feng3d/event': 0.5.0 + '@feng3d/math': 0.5.0 + '@feng3d/objectview': 0.5.0 + '@feng3d/polyfill': 0.5.0 + '@feng3d/renderer': 0.5.0 + '@feng3d/serialization': 0.5.0 + '@feng3d/watcher': 0.5.0 + dev: false + + /@feng3d/ui/0.5.0: + resolution: {integrity: sha512-kHKu2f/VODPoxIIeUACw7Rc9nKF2S5B0v/bloNiMx8Een7zW+dxNMJ6CIvs3O4eXK4vIULPZ586pM/rTcKnNKw==} + dependencies: + '@feng3d/core': 0.5.0 + '@feng3d/event': 0.5.0 + '@feng3d/filesystem': 0.5.0 + '@feng3d/math': 0.5.0 + '@feng3d/objectview': 0.5.0 + '@feng3d/polyfill': 0.5.0 + '@feng3d/renderer': 0.5.0 + '@feng3d/serialization': 0.5.0 + '@feng3d/watcher': 0.5.0 + dev: false + + /@feng3d/watcher/0.5.0: + resolution: {integrity: sha512-BOBWWP/gbczyVeFnIaS4rCOA3lRdzGCmFU+abKWeQHC//o5K75bt92d6PKk9cqpULT/xW2NZsYNLkbkNIXmfHA==} + dev: false + + /@humanwhocodes/config-array/0.9.5: + resolution: {integrity: sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/object-schema/1.2.1: + resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + dev: true + + /@jridgewell/gen-mapping/0.3.2: + resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.14 + '@jridgewell/trace-mapping': 0.3.14 + dev: true + + /@jridgewell/resolve-uri/3.1.0: + resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/set-array/1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/source-map/0.3.2: + resolution: {integrity: sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==} + dependencies: + '@jridgewell/gen-mapping': 0.3.2 + '@jridgewell/trace-mapping': 0.3.14 + dev: true + + /@jridgewell/sourcemap-codec/1.4.14: + resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} + dev: true + + /@jridgewell/trace-mapping/0.3.14: + resolution: {integrity: sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.0 + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + + /@nodelib/fs.scandir/2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat/2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk/1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.13.0 + dev: true + + /@rollup/pluginutils/3.1.0_rollup@2.70.1: + resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} + engines: {node: '>= 8.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0 + dependencies: + '@types/estree': 0.0.39 + estree-walker: 1.0.1 + picomatch: 2.3.1 + rollup: 2.70.1 + dev: true + + /@tsconfig/node10/1.0.9: + resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} + dev: true + + /@tsconfig/node12/1.0.11: + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + dev: true + + /@tsconfig/node14/1.0.3: + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + dev: true + + /@tsconfig/node16/1.0.3: + resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==} + dev: true + + /@types/estree/0.0.39: + resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==} + dev: true + + /@types/estree/1.0.0: + resolution: {integrity: sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==} + dev: true + + /@types/json-schema/7.0.11: + resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} + dev: true + + /@types/mocha/9.1.0: + resolution: {integrity: sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg==} + dev: true + + /@types/node/18.6.1: + resolution: {integrity: sha512-z+2vB6yDt1fNwKOeGbckpmirO+VBDuQqecXkgeIqDlaOtmKn6hPR/viQ8cxCfqLU4fTlvM3+YjM367TukWdxpg==} + dev: true + + /@types/resolve/0.0.8: + resolution: {integrity: sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==} + dependencies: + '@types/node': 18.6.1 + dev: true + + /@typescript-eslint/eslint-plugin/5.17.0_62eb2401ea518356689583614b0033ac: + resolution: {integrity: sha512-qVstvQilEd89HJk3qcbKt/zZrfBZ+9h2ynpAGlWjWiizA7m/MtLT9RoX6gjtpE500vfIg8jogAkDzdCxbsFASQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/parser': 5.17.0_eslint@8.12.0+typescript@4.3.5 + '@typescript-eslint/scope-manager': 5.17.0 + '@typescript-eslint/type-utils': 5.17.0_eslint@8.12.0+typescript@4.3.5 + '@typescript-eslint/utils': 5.17.0_eslint@8.12.0+typescript@4.3.5 + debug: 4.3.4 + eslint: 8.12.0 + functional-red-black-tree: 1.0.1 + ignore: 5.2.0 + regexpp: 3.2.0 + semver: 7.3.7 + tsutils: 3.21.0_typescript@4.3.5 + typescript: 4.3.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser/5.17.0_eslint@8.12.0+typescript@4.3.5: + resolution: {integrity: sha512-aRzW9Jg5Rlj2t2/crzhA2f23SIYFlF9mchGudyP0uiD6SenIxzKoLjwzHbafgHn39dNV/TV7xwQkLfFTZlJ4ig==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 5.17.0 + '@typescript-eslint/types': 5.17.0 + '@typescript-eslint/typescript-estree': 5.17.0_typescript@4.3.5 + debug: 4.3.4 + eslint: 8.12.0 + typescript: 4.3.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager/5.17.0: + resolution: {integrity: sha512-062iCYQF/doQ9T2WWfJohQKKN1zmmXVfAcS3xaiialiw8ZUGy05Em6QVNYJGO34/sU1a7a+90U3dUNfqUDHr3w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.17.0 + '@typescript-eslint/visitor-keys': 5.17.0 + dev: true + + /@typescript-eslint/type-utils/5.17.0_eslint@8.12.0+typescript@4.3.5: + resolution: {integrity: sha512-3hU0RynUIlEuqMJA7dragb0/75gZmwNwFf/QJokWzPehTZousP/MNifVSgjxNcDCkM5HI2K22TjQWUmmHUINSg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/utils': 5.17.0_eslint@8.12.0+typescript@4.3.5 + debug: 4.3.4 + eslint: 8.12.0 + tsutils: 3.21.0_typescript@4.3.5 + typescript: 4.3.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types/5.17.0: + resolution: {integrity: sha512-AgQ4rWzmCxOZLioFEjlzOI3Ch8giDWx8aUDxyNw9iOeCvD3GEYAB7dxWGQy4T/rPVe8iPmu73jPHuaSqcjKvxw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@typescript-eslint/typescript-estree/5.17.0_typescript@4.3.5: + resolution: {integrity: sha512-X1gtjEcmM7Je+qJRhq7ZAAaNXYhTgqMkR10euC4Si6PIjb+kwEQHSxGazXUQXFyqfEXdkGf6JijUu5R0uceQzg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.17.0 + '@typescript-eslint/visitor-keys': 5.17.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.3.7 + tsutils: 3.21.0_typescript@4.3.5 + typescript: 4.3.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils/5.17.0_eslint@8.12.0+typescript@4.3.5: + resolution: {integrity: sha512-DVvndq1QoxQH+hFv+MUQHrrWZ7gQ5KcJzyjhzcqB1Y2Xes1UQQkTRPUfRpqhS8mhTWsSb2+iyvDW1Lef5DD7vA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@types/json-schema': 7.0.11 + '@typescript-eslint/scope-manager': 5.17.0 + '@typescript-eslint/types': 5.17.0 + '@typescript-eslint/typescript-estree': 5.17.0_typescript@4.3.5 + eslint: 8.12.0 + eslint-scope: 5.1.1 + eslint-utils: 3.0.0_eslint@8.12.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys/5.17.0: + resolution: {integrity: sha512-6K/zlc4OfCagUu7Am/BD5k8PSWQOgh34Nrv9Rxe2tBzlJ7uOeJ/h7ugCGDCeEZHT6k2CJBhbk9IsbkPI0uvUkA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.17.0 + eslint-visitor-keys: 3.3.0 + dev: true + + /@ungap/promise-all-settled/1.1.2: + resolution: {integrity: sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==} + dev: true + + /acorn-dynamic-import/4.0.0_acorn@6.4.2: + resolution: {integrity: sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==} + peerDependencies: + acorn: ^6.0.0 + dependencies: + acorn: 6.4.2 + dev: true + + /acorn-jsx/5.3.2_acorn@6.4.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 6.4.2 + dev: true + + /acorn-jsx/5.3.2_acorn@8.8.0: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.8.0 + dev: true + + /acorn-walk/8.2.0: + resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} + engines: {node: '>=0.4.0'} + dev: true + + /acorn/6.4.2: + resolution: {integrity: sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /acorn/8.8.0: + resolution: {integrity: sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /ajv/6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ansi-colors/4.1.1: + resolution: {integrity: sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==} + engines: {node: '>=6'} + dev: true + + /ansi-regex/5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-styles/3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + + /ansi-styles/4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /anymatch/3.1.2: + resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: true + + /arg/4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + dev: true + + /argparse/2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /array-union/2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /atob/2.1.2: + resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==} + engines: {node: '>= 4.5.0'} + hasBin: true + dev: true + + /balanced-match/1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /binary-extensions/2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + dev: true + + /brace-expansion/1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /braces/3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /browser-stdout/1.3.1: + resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} + dev: true + + /buble/0.19.8: + resolution: {integrity: sha512-IoGZzrUTY5fKXVkgGHw3QeXFMUNBFv+9l8a4QJKG1JhG3nCMHTdEX1DCOg8568E2Q9qvAQIiSokv6Jsgx8p2cA==} + hasBin: true + dependencies: + acorn: 6.4.2 + acorn-dynamic-import: 4.0.0_acorn@6.4.2 + acorn-jsx: 5.3.2_acorn@6.4.2 + chalk: 2.4.2 + magic-string: 0.25.9 + minimist: 1.2.6 + os-homedir: 2.0.0 + regexpu-core: 4.8.0 + dev: true + + /buffer-from/1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: true + + /builtin-modules/3.3.0: + resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} + engines: {node: '>=6'} + dev: true + + /callsites/3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /camelcase/6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + dev: true + + /chalk/2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + + /chalk/4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /chokidar/3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.2 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /cliui/7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: true + + /color-convert/1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: true + + /color-convert/2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name/1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: true + + /color-name/1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + + /commander/2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + dev: true + + /concat-map/0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /create-require/1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + dev: true + + /cross-env/7.0.3: + resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} + engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} + hasBin: true + dependencies: + cross-spawn: 7.0.3 + dev: true + + /cross-spawn/7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /debug/4.3.3_supports-color@8.1.1: + resolution: {integrity: sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + supports-color: 8.1.1 + dev: true + + /debug/4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /decamelize/4.0.0: + resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} + engines: {node: '>=10'} + dev: true + + /decode-uri-component/0.2.0: + resolution: {integrity: sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==} + engines: {node: '>=0.10'} + dev: true + + /deep-is/0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /diff/4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + dev: true + + /diff/5.0.0: + resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==} + engines: {node: '>=0.3.1'} + dev: true + + /dir-glob/3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /doctrine/3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /emoji-regex/8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: true + + /escalade/3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: true + + /escape-string-regexp/1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: true + + /escape-string-regexp/4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /eslint-scope/5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + dev: true + + /eslint-scope/7.1.1: + resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-utils/3.0.0_eslint@8.12.0: + resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} + engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} + peerDependencies: + eslint: '>=5' + dependencies: + eslint: 8.12.0 + eslint-visitor-keys: 2.1.0 + dev: true + + /eslint-visitor-keys/2.1.0: + resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} + engines: {node: '>=10'} + dev: true + + /eslint-visitor-keys/3.3.0: + resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint/8.12.0: + resolution: {integrity: sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint/eslintrc': 1.3.0 + '@humanwhocodes/config-array': 0.9.5 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.1.1 + eslint-utils: 3.0.0_eslint@8.12.0 + eslint-visitor-keys: 3.3.0 + espree: 9.3.2 + esquery: 1.4.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + functional-red-black-tree: 1.0.1 + glob-parent: 6.0.2 + globals: 13.17.0 + ignore: 5.2.0 + import-fresh: 3.3.0 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.1 + regexpp: 3.2.0 + strip-ansi: 6.0.1 + strip-json-comments: 3.1.1 + text-table: 0.2.0 + v8-compile-cache: 2.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree/9.3.2: + resolution: {integrity: sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.8.0 + acorn-jsx: 5.3.2_acorn@8.8.0 + eslint-visitor-keys: 3.3.0 + dev: true + + /esquery/1.4.0: + resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse/4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse/4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + dev: true + + /estraverse/5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /estree-walker/0.6.1: + resolution: {integrity: sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==} + dev: true + + /estree-walker/1.0.1: + resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==} + dev: true + + /esutils/2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /fast-deep-equal/3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-glob/3.2.11: + resolution: {integrity: sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify/2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein/2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fastq/1.13.0: + resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==} + dependencies: + reusify: 1.0.4 + dev: true + + /feng3d/0.5.0: + resolution: {integrity: sha512-2SRuXbZdTYdqtqgfOpkF5nXKTO5DjGJIIAaxq7IwDwdPYbtdUWeqvzx8mbe019ALDns/jaVFZ34opkG6uToi1g==} + dependencies: + '@feng3d/assets': 0.5.0 + '@feng3d/bezier': 0.5.0 + '@feng3d/core': 0.5.0 + '@feng3d/event': 0.5.0 + '@feng3d/filesystem': 0.5.0 + '@feng3d/math': 0.5.0 + '@feng3d/objectview': 0.5.0 + '@feng3d/parsers': 0.5.0 + '@feng3d/particlesystem': 0.5.0 + '@feng3d/polyfill': 0.5.0 + '@feng3d/renderer': 0.5.0 + '@feng3d/serialization': 0.5.0 + '@feng3d/shortcut': 0.5.0 + '@feng3d/task': 0.5.0 + '@feng3d/terrain': 0.5.0 + '@feng3d/ui': 0.5.0 + '@feng3d/watcher': 0.5.0 + dev: false + + /file-entry-cache/6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.0.4 + dev: true + + /fill-range/7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /find-up/5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache/3.0.4: + resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flatted: 3.2.6 + rimraf: 3.0.2 + dev: true + + /flat/5.0.2: + resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} + hasBin: true + dev: true + + /flatted/3.2.6: + resolution: {integrity: sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==} + dev: true + + /fs.realpath/1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /fsevents/2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /function-bind/1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + dev: true + + /functional-red-black-tree/1.0.1: + resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==} + dev: true + + /get-caller-file/2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + dev: true + + /glob-parent/5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-parent/6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob/7.2.0: + resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /glob/7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /globals/13.17.0: + resolution: {integrity: sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globby/11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.2.11 + ignore: 5.2.0 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /growl/1.10.5: + resolution: {integrity: sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==} + engines: {node: '>=4.x'} + dev: true + + /handlebars/4.7.7: + resolution: {integrity: sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==} + engines: {node: '>=0.4.7'} + hasBin: true + dependencies: + minimist: 1.2.6 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.16.3 + dev: true + + /has-flag/3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: true + + /has-flag/4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /has/1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + dev: true + + /he/1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + dev: true + + /ignore/5.2.0: + resolution: {integrity: sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==} + engines: {node: '>= 4'} + dev: true + + /import-fresh/3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /imurmurhash/0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /inflight/1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits/2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /is-binary-path/2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + dev: true + + /is-core-module/2.9.0: + resolution: {integrity: sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==} + dependencies: + has: 1.0.3 + dev: true + + /is-extglob/2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-fullwidth-code-point/3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + dev: true + + /is-glob/4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-module/1.0.0: + resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} + dev: true + + /is-number/7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-plain-obj/2.1.0: + resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} + engines: {node: '>=8'} + dev: true + + /is-reference/1.2.1: + resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} + dependencies: + '@types/estree': 1.0.0 + dev: true + + /is-unicode-supported/0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + dev: true + + /isexe/2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /jest-worker/26.6.2: + resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} + engines: {node: '>= 10.13.0'} + dependencies: + '@types/node': 18.6.1 + merge-stream: 2.0.0 + supports-color: 7.2.0 + dev: true + + /js-tokens/4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: true + + /js-yaml/4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /jsesc/0.5.0: + resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} + hasBin: true + dev: true + + /json-schema-traverse/0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-stable-stringify-without-jsonify/1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /jsonc-parser/3.1.0: + resolution: {integrity: sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==} + dev: true + + /levn/0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /locate-path/6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.merge/4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /log-symbols/4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + dev: true + + /lru-cache/6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /lunr/2.3.9: + resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} + dev: true + + /magic-string/0.25.9: + resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} + dependencies: + sourcemap-codec: 1.4.8 + dev: true + + /magic-string/0.26.2: + resolution: {integrity: sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==} + engines: {node: '>=12'} + dependencies: + sourcemap-codec: 1.4.8 + dev: true + + /make-error/1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: true + + /marked/2.1.3: + resolution: {integrity: sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA==} + engines: {node: '>= 10'} + hasBin: true + dev: true + + /merge-stream/2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: true + + /merge2/1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch/4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /minimatch/3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimatch/4.2.1: + resolution: {integrity: sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimist/1.2.6: + resolution: {integrity: sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==} + dev: true + + /mocha/9.2.2: + resolution: {integrity: sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==} + engines: {node: '>= 12.0.0'} + hasBin: true + dependencies: + '@ungap/promise-all-settled': 1.1.2 + ansi-colors: 4.1.1 + browser-stdout: 1.3.1 + chokidar: 3.5.3 + debug: 4.3.3_supports-color@8.1.1 + diff: 5.0.0 + escape-string-regexp: 4.0.0 + find-up: 5.0.0 + glob: 7.2.0 + growl: 1.10.5 + he: 1.2.0 + js-yaml: 4.1.0 + log-symbols: 4.1.0 + minimatch: 4.2.1 + ms: 2.1.3 + nanoid: 3.3.1 + serialize-javascript: 6.0.0 + strip-json-comments: 3.1.1 + supports-color: 8.1.1 + which: 2.0.2 + workerpool: 6.2.0 + yargs: 16.2.0 + yargs-parser: 20.2.4 + yargs-unparser: 2.0.0 + dev: true + + /ms/2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /ms/2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + dev: true + + /nanoid/3.3.1: + resolution: {integrity: sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + + /natural-compare/1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /neo-async/2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + dev: true + + /normalize-path/3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true + + /once/1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /optionator/0.9.1: + resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} + engines: {node: '>= 0.8.0'} + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.3 + dev: true + + /os-homedir/2.0.0: + resolution: {integrity: sha512-saRNz0DSC5C/I++gFIaJTXoFJMRwiP5zHar5vV3xQ2TkgEw6hDCcU5F272JjUylpiVgBrZNQHnfjkLabTfb92Q==} + engines: {node: '>=0.10.0'} + deprecated: This is not needed anymore. Use `require('os').homedir()` instead. + dev: true + + /p-limit/3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate/5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /parent-module/1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /path-exists/4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute/1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-key/3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-parse/1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + + /path-type/4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /picomatch/2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /prelude-ls/1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /progress/2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + dev: true + + /punycode/2.1.1: + resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} + engines: {node: '>=6'} + dev: true + + /queue-microtask/1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /randombytes/2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + dependencies: + safe-buffer: 5.2.1 + dev: true + + /readdirp/3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: true + + /regenerate-unicode-properties/9.0.0: + resolution: {integrity: sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA==} + engines: {node: '>=4'} + dependencies: + regenerate: 1.4.2 + dev: true + + /regenerate/1.4.2: + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + dev: true + + /regexpp/3.2.0: + resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} + engines: {node: '>=8'} + dev: true + + /regexpu-core/4.8.0: + resolution: {integrity: sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg==} + engines: {node: '>=4'} + dependencies: + regenerate: 1.4.2 + regenerate-unicode-properties: 9.0.0 + regjsgen: 0.5.2 + regjsparser: 0.7.0 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.0.0 + dev: true + + /regjsgen/0.5.2: + resolution: {integrity: sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==} + dev: true + + /regjsparser/0.7.0: + resolution: {integrity: sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ==} + hasBin: true + dependencies: + jsesc: 0.5.0 + dev: true + + /require-directory/2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + dev: true + + /resolve-from/4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /resolve/1.22.1: + resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} + hasBin: true + dependencies: + is-core-module: 2.9.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /reusify/1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rimraf/3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /rollup-plugin-buble/0.19.8: + resolution: {integrity: sha512-8J4zPk2DQdk3rxeZvxgzhHh/rm5nJkjwgcsUYisCQg1QbT5yagW+hehYEW7ZNns/NVbDCTv4JQ7h4fC8qKGOKw==} + deprecated: This module has been deprecated and is no longer maintained. Please use @rollup/plugin-buble. + dependencies: + buble: 0.19.8 + rollup-pluginutils: 2.8.2 + dev: true + + /rollup-plugin-commonjs/10.1.0_rollup@2.70.1: + resolution: {integrity: sha512-jlXbjZSQg8EIeAAvepNwhJj++qJWNJw1Cl0YnOqKtP5Djx+fFGkp3WRh+W0ASCaFG5w1jhmzDxgu3SJuVxPF4Q==} + deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-commonjs. + peerDependencies: + rollup: '>=1.12.0' + dependencies: + estree-walker: 0.6.1 + is-reference: 1.2.1 + magic-string: 0.25.9 + resolve: 1.22.1 + rollup: 2.70.1 + rollup-pluginutils: 2.8.2 + dev: true + + /rollup-plugin-dts/4.2.0_rollup@2.70.1+typescript@4.3.5: + resolution: {integrity: sha512-lx6irWVhz/x4//tIqRhzk4FOqGQ0n37ZM2wpPCn4uafl/EmiV92om7ZdAsq7Bzho6C+Xh5GfsyuP9H+Udv72Lg==} + engines: {node: '>=v12.22.10'} + peerDependencies: + rollup: ^2.55 + typescript: ^4.1 + dependencies: + magic-string: 0.26.2 + rollup: 2.70.1 + typescript: 4.3.5 + optionalDependencies: + '@babel/code-frame': 7.18.6 + dev: true + + /rollup-plugin-json/4.0.0: + resolution: {integrity: sha512-hgb8N7Cgfw5SZAkb3jf0QXii6QX/FOkiIq2M7BAQIEydjHvTyxXHQiIzZaTFgx1GK0cRCHOCBHIyEkkLdWKxow==} + deprecated: This module has been deprecated and is no longer maintained. Please use @rollup/plugin-json. + dependencies: + rollup-pluginutils: 2.8.2 + dev: true + + /rollup-plugin-node-resolve/5.2.0_rollup@2.70.1: + resolution: {integrity: sha512-jUlyaDXts7TW2CqQ4GaO5VJ4PwwaV8VUGA7+km3n6k6xtOEacf61u0VXwN80phY/evMcaS+9eIeJ9MOyDxt5Zw==} + deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-node-resolve. + peerDependencies: + rollup: '>=1.11.0' + dependencies: + '@types/resolve': 0.0.8 + builtin-modules: 3.3.0 + is-module: 1.0.0 + resolve: 1.22.1 + rollup: 2.70.1 + rollup-pluginutils: 2.8.2 + dev: true + + /rollup-plugin-replace/2.2.0: + resolution: {integrity: sha512-/5bxtUPkDHyBJAKketb4NfaeZjL5yLZdeUihSfbF2PQMz+rSTEb8ARKoOl3UBT4m7/X+QOXJo3sLTcq+yMMYTA==} + deprecated: This module has moved and is now available at @rollup/plugin-replace. Please update your dependencies. This version is no longer maintained. + dependencies: + magic-string: 0.25.9 + rollup-pluginutils: 2.8.2 + dev: true + + /rollup-plugin-sourcemaps/0.6.3_@types+node@18.6.1+rollup@2.70.1: + resolution: {integrity: sha512-paFu+nT1xvuO1tPFYXGe+XnQvg4Hjqv/eIhG8i5EspfYYPBKL57X7iVbfv55aNVASg3dzWvES9dmWsL2KhfByw==} + engines: {node: '>=10.0.0'} + peerDependencies: + '@types/node': '>=10.0.0' + rollup: '>=0.31.2' + peerDependenciesMeta: + '@types/node': + optional: true + dependencies: + '@rollup/pluginutils': 3.1.0_rollup@2.70.1 + '@types/node': 18.6.1 + rollup: 2.70.1 + source-map-resolve: 0.6.0 + dev: true + + /rollup-plugin-string/3.0.0: + resolution: {integrity: sha512-vqyzgn9QefAgeKi+Y4A7jETeIAU1zQmS6VotH6bzm/zmUQEnYkpIGRaOBPY41oiWYV4JyBoGAaBjYMYuv+6wVw==} + dependencies: + rollup-pluginutils: 2.8.2 + dev: true + + /rollup-plugin-terser/7.0.2_rollup@2.70.1: + resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==} + peerDependencies: + rollup: ^2.0.0 + dependencies: + '@babel/code-frame': 7.18.6 + jest-worker: 26.6.2 + rollup: 2.70.1 + serialize-javascript: 4.0.0 + terser: 5.14.2 + dev: true + + /rollup-plugin-typescript/1.0.1_tslib@2.4.0+typescript@4.3.5: + resolution: {integrity: sha512-rwJDNn9jv/NsKZuyBb/h0jsclP4CJ58qbvZt2Q9zDIGILF2LtdtvCqMOL+Gq9IVq5MTrTlHZNrn8h7VjQgd8tw==} + deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-typescript. + peerDependencies: + tslib: '*' + typescript: '>=2.1.0' + dependencies: + resolve: 1.22.1 + rollup-pluginutils: 2.8.2 + tslib: 2.4.0 + typescript: 4.3.5 + dev: true + + /rollup-pluginutils/2.8.2: + resolution: {integrity: sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==} + dependencies: + estree-walker: 0.6.1 + dev: true + + /rollup/2.70.1: + resolution: {integrity: sha512-CRYsI5EuzLbXdxC6RnYhOuRdtz4bhejPMSWjsFLfVM/7w/85n2szZv6yExqUXsBdz5KT8eoubeyDUDjhLHEslA==} + engines: {node: '>=10.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /run-parallel/1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /safe-buffer/5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: true + + /semver/7.3.7: + resolution: {integrity: sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /serialize-javascript/4.0.0: + resolution: {integrity: sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==} + dependencies: + randombytes: 2.1.0 + dev: true + + /serialize-javascript/6.0.0: + resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==} + dependencies: + randombytes: 2.1.0 + dev: true + + /shebang-command/2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex/3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /shiki/0.9.15: + resolution: {integrity: sha512-/Y0z9IzhJ8nD9nbceORCqu6NgT9X6I8Fk8c3SICHI5NbZRLdZYFaB233gwct9sU0vvSypyaL/qaKvzyQGJBZSw==} + dependencies: + jsonc-parser: 3.1.0 + vscode-oniguruma: 1.6.2 + vscode-textmate: 5.2.0 + dev: true + + /slash/3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /source-map-resolve/0.6.0: + resolution: {integrity: sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==} + deprecated: See https://github.com/lydell/source-map-resolve#deprecated + dependencies: + atob: 2.1.2 + decode-uri-component: 0.2.0 + dev: true + + /source-map-support/0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + + /source-map/0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + dev: true + + /sourcemap-codec/1.4.8: + resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} + dev: true + + /string-width/4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + dev: true + + /strip-ansi/6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-json-comments/3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /supports-color/5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: true + + /supports-color/7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-color/8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-preserve-symlinks-flag/1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: true + + /terser/5.14.2: + resolution: {integrity: sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + '@jridgewell/source-map': 0.3.2 + acorn: 8.8.0 + commander: 2.20.3 + source-map-support: 0.5.21 + dev: true + + /text-table/0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /to-regex-range/5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /ts-node/10.7.0_460543de4106407bf2e2de97a08caad7: + resolution: {integrity: sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.7.0 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.3 + '@types/node': 18.6.1 + acorn: 8.8.0 + acorn-walk: 8.2.0 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 4.3.5 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: true + + /tslib/1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + dev: true + + /tslib/2.4.0: + resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} + dev: true + + /tsutils/3.21.0_typescript@4.3.5: + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + dependencies: + tslib: 1.14.1 + typescript: 4.3.5 + dev: true + + /type-check/0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-fest/0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /typedoc-default-themes/0.12.10: + resolution: {integrity: sha512-fIS001cAYHkyQPidWXmHuhs8usjP5XVJjWB8oZGqkTowZaz3v7g3KDZeeqE82FBrmkAnIBOY3jgy7lnPnqATbA==} + engines: {node: '>= 8'} + dev: true + + /typedoc-plugin-sourcefile-url/1.0.6_typedoc@0.21.4: + resolution: {integrity: sha512-xHq9DzkoQywS7FyPneMm2/Hr9GRoCpjSQXkVN0W6SCJKP7fguqg2tasgh+8l5/mW6YSYvqCqEbkSYLbuD4Y6gA==} + peerDependencies: + typedoc: '>=0.16.0' + dependencies: + typedoc: 0.21.4_typescript@4.3.5 + dev: true + + /typedoc/0.21.4_typescript@4.3.5: + resolution: {integrity: sha512-slZQhvD9U0d9KacktYAyuNMMOXJRFNHy+Gd8xY2Qrqq3eTTTv3frv3N4au/cFnab9t3T5WA0Orb6QUjMc+1bDA==} + engines: {node: '>= 12.20.0'} + hasBin: true + peerDependencies: + typescript: 4.0.x || 4.1.x || 4.2.x || 4.3.x + dependencies: + glob: 7.2.3 + handlebars: 4.7.7 + lunr: 2.3.9 + marked: 2.1.3 + minimatch: 3.1.2 + progress: 2.0.3 + shiki: 0.9.15 + typedoc-default-themes: 0.12.10 + typescript: 4.3.5 + dev: true + + /typescript/4.3.5: + resolution: {integrity: sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: true + + /uglify-js/3.16.3: + resolution: {integrity: sha512-uVbFqx9vvLhQg0iBaau9Z75AxWJ8tqM9AV890dIZCLApF4rTcyHwmAvLeEdYRs+BzYWu8Iw81F79ah0EfTXbaw==} + engines: {node: '>=0.8.0'} + hasBin: true + requiresBuild: true + dev: true + optional: true + + /unicode-canonical-property-names-ecmascript/2.0.0: + resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==} + engines: {node: '>=4'} + dev: true + + /unicode-match-property-ecmascript/2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.0 + unicode-property-aliases-ecmascript: 2.0.0 + dev: true + + /unicode-match-property-value-ecmascript/2.0.0: + resolution: {integrity: sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==} + engines: {node: '>=4'} + dev: true + + /unicode-property-aliases-ecmascript/2.0.0: + resolution: {integrity: sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==} + engines: {node: '>=4'} + dev: true + + /uri-js/4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.1.1 + dev: true + + /v8-compile-cache-lib/3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + dev: true + + /v8-compile-cache/2.3.0: + resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==} + dev: true + + /vscode-oniguruma/1.6.2: + resolution: {integrity: sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA==} + dev: true + + /vscode-textmate/5.2.0: + resolution: {integrity: sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==} + dev: true + + /which/2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /word-wrap/1.2.3: + resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} + engines: {node: '>=0.10.0'} + dev: true + + /wordwrap/1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + dev: true + + /workerpool/6.2.0: + resolution: {integrity: sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==} + dev: true + + /wrap-ansi/7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /wrappy/1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /y18n/5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + dev: true + + /yallist/4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yargs-parser/20.2.4: + resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==} + engines: {node: '>=10'} + dev: true + + /yargs-unparser/2.0.0: + resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} + engines: {node: '>=10'} + dependencies: + camelcase: 6.3.0 + decamelize: 4.0.0 + flat: 5.0.2 + is-plain-obj: 2.1.0 + dev: true + + /yargs/16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + dependencies: + cliui: 7.0.4 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.4 + dev: true + + /yn/3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + dev: true + + /yocto-queue/0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 000000000..8177ee787 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,200 @@ +import path from 'path'; +import transpile from 'rollup-plugin-buble'; +import json from 'rollup-plugin-json'; +import resolve from 'rollup-plugin-node-resolve'; +import { string } from 'rollup-plugin-string'; +import sourcemaps from 'rollup-plugin-sourcemaps'; +import typescript from 'rollup-plugin-typescript'; +import dts from 'rollup-plugin-dts'; +import commonjs from 'rollup-plugin-commonjs'; +import replace from 'rollup-plugin-replace'; +import { terser } from 'rollup-plugin-terser'; +import fs from 'fs'; +import pkg from './package.json'; + +/** + * Get a list of the non-private sorted packages with Lerna v3 + * @see https://github.com/lerna/lerna/issues/1848 + * @return {Promise} List of packages + */ +async function main() +{ + const plugins = [ + sourcemaps(), + json(), + resolve({ + browser: true, + preferBuiltins: false, + }), + commonjs({}), + typescript(), + string({ + include: [ + '**/*.glsl', + ], + }), + replace({ + __VERSION__: pkg.version, + }), + transpile(), + ]; + + const compiled = (new Date()).toUTCString().replace(/GMT/g, 'UTC'); + const sourcemap = true; + const results = []; + + const namespaces = {}; + namespaces[pkg.name] = pkg.namespace || 'feng3d'; + for (const key in pkg.dependencies) + { + namespaces[key] = 'feng3d'; + } + + let banner = [ + `/*!`, + ` * ${pkg.name} - v${pkg.version}`, + ` * Compiled ${compiled}`, + ` *`, + ` * ${pkg.name} is licensed under the MIT License.`, + ` * http://www.opensource.org/licenses/mit-license`, + ` */`, + ].join('\n'); + + // Check for bundle folder + const external = Object.keys(pkg.dependencies || []); + const basePath = path.relative(__dirname, ''); + const input = path.join(basePath, 'src/index.ts'); + + const { + main, + module, + bundle, + bundleInput, + bundleOutput, + bundleNoExports, + standalone, + types, + } = pkg; + const freeze = false; + + results.push({ + input, + output: [ + { + banner, + file: path.join(basePath, main), + format: 'cjs', + freeze, + sourcemap, + }, + { + banner, + file: path.join(basePath, module), + format: 'esm', + freeze, + sourcemap, + }, + ], + external, + plugins, + }); + + results.push({ + input, + external: standalone ? [] : external, + output: [{ + file: path.join(basePath, types), + name: namespaces[pkg.name], + format: 'es', + footer: `export as namespace ${namespaces[pkg.name]};` + }], + plugins: [ + json(), + typescript({ tsconfig: './tsconfig.json' }), + dts({ respectExternal: true }), + ], + }); + + // The package.json file has a bundle field + // we'll use this to generate the bundle file + // this will package all dependencies + if (bundle) + { + let input = path.join(basePath, bundleInput || 'src/index.ts'); + + // TODO: remove check once all packages have been converted to typescript + if (!fs.existsSync(input)) + { + input = path.join(basePath, bundleInput || 'src/index.js'); + } + + const file = path.join(basePath, bundle); + const external = standalone ? null : Object.keys(namespaces); + const globals = standalone ? null : namespaces; + const ns = namespaces[pkg.name]; + const name = pkg.name.replace(/[^a-z0-9]+/g, '_'); + let footer; + + if (!standalone) + { + if (bundleNoExports !== true) + { + footer = `Object.assign(this.${ns}, ${name});`; + } + + if (ns.includes('.')) + { + const base = ns.split('.')[0]; + + banner += `\nthis.${base} = this.${base} || {};`; + } + + banner += `\nthis.${ns} = this.${ns} || {};`; + } + + results.push({ + input, + external, + output: Object.assign({ + banner, + file, + format: 'iife', + freeze, + globals, + name, + footer, + sourcemap, + }, bundleOutput), + treeshake: false, + plugins, + }); + + if (process.env.NODE_ENV === 'production') + { + results.push({ + input, + external, + output: Object.assign({ + banner, + file: file.replace(/\.js$/, '.min.js'), + format: 'iife', + freeze, + globals, + name, + footer, + sourcemap, + }, bundleOutput), + treeshake: false, + plugins: [...plugins, terser({ + output: { + comments: (node, comment) => comment.line === 1, + }, + })], + }); + } + } + + return results; +} + +export default main(); diff --git a/src/Cannon.js b/src/Cannon.js deleted file mode 100644 index 70e44a344..000000000 --- a/src/Cannon.js +++ /dev/null @@ -1,53 +0,0 @@ -// Export classes -module.exports = { - version : require('../package.json').version, - - AABB : require('./collision/AABB'), - ArrayCollisionMatrix : require('./collision/ArrayCollisionMatrix'), - Body : require('./objects/Body'), - Box : require('./shapes/Box'), - Broadphase : require('./collision/Broadphase'), - Constraint : require('./constraints/Constraint'), - ContactEquation : require('./equations/ContactEquation'), - Narrowphase : require('./world/Narrowphase'), - ConeTwistConstraint : require('./constraints/ConeTwistConstraint'), - ContactMaterial : require('./material/ContactMaterial'), - ConvexPolyhedron : require('./shapes/ConvexPolyhedron'), - Cylinder : require('./shapes/Cylinder'), - DistanceConstraint : require('./constraints/DistanceConstraint'), - Equation : require('./equations/Equation'), - EventTarget : require('./utils/EventTarget'), - FrictionEquation : require('./equations/FrictionEquation'), - GSSolver : require('./solver/GSSolver'), - GridBroadphase : require('./collision/GridBroadphase'), - Heightfield : require('./shapes/Heightfield'), - HingeConstraint : require('./constraints/HingeConstraint'), - LockConstraint : require('./constraints/LockConstraint'), - Mat3 : require('./math/Mat3'), - Material : require('./material/Material'), - NaiveBroadphase : require('./collision/NaiveBroadphase'), - ObjectCollisionMatrix : require('./collision/ObjectCollisionMatrix'), - Pool : require('./utils/Pool'), - Particle : require('./shapes/Particle'), - Plane : require('./shapes/Plane'), - PointToPointConstraint : require('./constraints/PointToPointConstraint'), - Quaternion : require('./math/Quaternion'), - Ray : require('./collision/Ray'), - RaycastVehicle : require('./objects/RaycastVehicle'), - RaycastResult : require('./collision/RaycastResult'), - RigidVehicle : require('./objects/RigidVehicle'), - RotationalEquation : require('./equations/RotationalEquation'), - RotationalMotorEquation : require('./equations/RotationalMotorEquation'), - SAPBroadphase : require('./collision/SAPBroadphase'), - SPHSystem : require('./objects/SPHSystem'), - Shape : require('./shapes/Shape'), - Solver : require('./solver/Solver'), - Sphere : require('./shapes/Sphere'), - SplitSolver : require('./solver/SplitSolver'), - Spring : require('./objects/Spring'), - Transform : require('./math/Transform'), - Trimesh : require('./shapes/Trimesh'), - Vec3 : require('./math/Vec3'), - Vec3Pool : require('./utils/Vec3Pool'), - World : require('./world/World'), -}; diff --git a/src/collision/AABB.js b/src/collision/AABB.js deleted file mode 100644 index 5ecd23f95..000000000 --- a/src/collision/AABB.js +++ /dev/null @@ -1,322 +0,0 @@ -var Vec3 = require('../math/Vec3'); -var Utils = require('../utils/Utils'); - -module.exports = AABB; - -/** - * Axis aligned bounding box class. - * @class AABB - * @constructor - * @param {Object} [options] - * @param {Vec3} [options.upperBound] - * @param {Vec3} [options.lowerBound] - */ -function AABB(options){ - options = options || {}; - - /** - * The lower bound of the bounding box. - * @property lowerBound - * @type {Vec3} - */ - this.lowerBound = new Vec3(); - if(options.lowerBound){ - this.lowerBound.copy(options.lowerBound); - } - - /** - * The upper bound of the bounding box. - * @property upperBound - * @type {Vec3} - */ - this.upperBound = new Vec3(); - if(options.upperBound){ - this.upperBound.copy(options.upperBound); - } -} - -var tmp = new Vec3(); - -/** - * Set the AABB bounds from a set of points. - * @method setFromPoints - * @param {Array} points An array of Vec3's. - * @param {Vec3} position - * @param {Quaternion} quaternion - * @param {number} skinSize - * @return {AABB} The self object - */ -AABB.prototype.setFromPoints = function(points, position, quaternion, skinSize){ - var l = this.lowerBound, - u = this.upperBound, - q = quaternion; - - // Set to the first point - l.copy(points[0]); - if(q){ - q.vmult(l, l); - } - u.copy(l); - - for(var i = 1; i u.x){ u.x = p.x; } - if(p.x < l.x){ l.x = p.x; } - if(p.y > u.y){ u.y = p.y; } - if(p.y < l.y){ l.y = p.y; } - if(p.z > u.z){ u.z = p.z; } - if(p.z < l.z){ l.z = p.z; } - } - - // Add offset - if (position) { - position.vadd(l, l); - position.vadd(u, u); - } - - if(skinSize){ - l.x -= skinSize; - l.y -= skinSize; - l.z -= skinSize; - u.x += skinSize; - u.y += skinSize; - u.z += skinSize; - } - - return this; -}; - -/** - * Copy bounds from an AABB to this AABB - * @method copy - * @param {AABB} aabb Source to copy from - * @return {AABB} The this object, for chainability - */ -AABB.prototype.copy = function(aabb){ - this.lowerBound.copy(aabb.lowerBound); - this.upperBound.copy(aabb.upperBound); - return this; -}; - -/** - * Clone an AABB - * @method clone - */ -AABB.prototype.clone = function(){ - return new AABB().copy(this); -}; - -/** - * Extend this AABB so that it covers the given AABB too. - * @method extend - * @param {AABB} aabb - */ -AABB.prototype.extend = function(aabb){ - this.lowerBound.x = Math.min(this.lowerBound.x, aabb.lowerBound.x); - this.upperBound.x = Math.max(this.upperBound.x, aabb.upperBound.x); - this.lowerBound.y = Math.min(this.lowerBound.y, aabb.lowerBound.y); - this.upperBound.y = Math.max(this.upperBound.y, aabb.upperBound.y); - this.lowerBound.z = Math.min(this.lowerBound.z, aabb.lowerBound.z); - this.upperBound.z = Math.max(this.upperBound.z, aabb.upperBound.z); -}; - -/** - * Returns true if the given AABB overlaps this AABB. - * @method overlaps - * @param {AABB} aabb - * @return {Boolean} - */ -AABB.prototype.overlaps = function(aabb){ - var l1 = this.lowerBound, - u1 = this.upperBound, - l2 = aabb.lowerBound, - u2 = aabb.upperBound; - - // l2 u2 - // |---------| - // |--------| - // l1 u1 - - var overlapsX = ((l2.x <= u1.x && u1.x <= u2.x) || (l1.x <= u2.x && u2.x <= u1.x)); - var overlapsY = ((l2.y <= u1.y && u1.y <= u2.y) || (l1.y <= u2.y && u2.y <= u1.y)); - var overlapsZ = ((l2.z <= u1.z && u1.z <= u2.z) || (l1.z <= u2.z && u2.z <= u1.z)); - - return overlapsX && overlapsY && overlapsZ; -}; - -// Mostly for debugging -AABB.prototype.volume = function(){ - var l = this.lowerBound, - u = this.upperBound; - return (u.x - l.x) * (u.y - l.y) * (u.z - l.z); -}; - - -/** - * Returns true if the given AABB is fully contained in this AABB. - * @method contains - * @param {AABB} aabb - * @return {Boolean} - */ -AABB.prototype.contains = function(aabb){ - var l1 = this.lowerBound, - u1 = this.upperBound, - l2 = aabb.lowerBound, - u2 = aabb.upperBound; - - // l2 u2 - // |---------| - // |---------------| - // l1 u1 - - return ( - (l1.x <= l2.x && u1.x >= u2.x) && - (l1.y <= l2.y && u1.y >= u2.y) && - (l1.z <= l2.z && u1.z >= u2.z) - ); -}; - -/** - * @method getCorners - * @param {Vec3} a - * @param {Vec3} b - * @param {Vec3} c - * @param {Vec3} d - * @param {Vec3} e - * @param {Vec3} f - * @param {Vec3} g - * @param {Vec3} h - */ -AABB.prototype.getCorners = function(a, b, c, d, e, f, g, h){ - var l = this.lowerBound, - u = this.upperBound; - - a.copy(l); - b.set( u.x, l.y, l.z ); - c.set( u.x, u.y, l.z ); - d.set( l.x, u.y, u.z ); - e.set( u.x, l.y, l.z ); - f.set( l.x, u.y, l.z ); - g.set( l.x, l.y, u.z ); - h.copy(u); -}; - -var transformIntoFrame_corners = [ - new Vec3(), - new Vec3(), - new Vec3(), - new Vec3(), - new Vec3(), - new Vec3(), - new Vec3(), - new Vec3() -]; - -/** - * Get the representation of an AABB in another frame. - * @method toLocalFrame - * @param {Transform} frame - * @param {AABB} target - * @return {AABB} The "target" AABB object. - */ -AABB.prototype.toLocalFrame = function(frame, target){ - - var corners = transformIntoFrame_corners; - var a = corners[0]; - var b = corners[1]; - var c = corners[2]; - var d = corners[3]; - var e = corners[4]; - var f = corners[5]; - var g = corners[6]; - var h = corners[7]; - - // Get corners in current frame - this.getCorners(a, b, c, d, e, f, g, h); - - // Transform them to new local frame - for(var i=0; i !== 8; i++){ - var corner = corners[i]; - frame.pointToLocal(corner, corner); - } - - return target.setFromPoints(corners); -}; - -/** - * Get the representation of an AABB in the global frame. - * @method toWorldFrame - * @param {Transform} frame - * @param {AABB} target - * @return {AABB} The "target" AABB object. - */ -AABB.prototype.toWorldFrame = function(frame, target){ - - var corners = transformIntoFrame_corners; - var a = corners[0]; - var b = corners[1]; - var c = corners[2]; - var d = corners[3]; - var e = corners[4]; - var f = corners[5]; - var g = corners[6]; - var h = corners[7]; - - // Get corners in current frame - this.getCorners(a, b, c, d, e, f, g, h); - - // Transform them to new local frame - for(var i=0; i !== 8; i++){ - var corner = corners[i]; - frame.pointToWorld(corner, corner); - } - - return target.setFromPoints(corners); -}; - -/** - * Check if the AABB is hit by a ray. - * @param {Ray} ray - * @return {number} - */ -AABB.prototype.overlapsRay = function(ray){ - var t = 0; - - // ray.direction is unit direction vector of ray - var dirFracX = 1 / ray._direction.x; - var dirFracY = 1 / ray._direction.y; - var dirFracZ = 1 / ray._direction.z; - - // this.lowerBound is the corner of AABB with minimal coordinates - left bottom, rt is maximal corner - var t1 = (this.lowerBound.x - ray.from.x) * dirFracX; - var t2 = (this.upperBound.x - ray.from.x) * dirFracX; - var t3 = (this.lowerBound.y - ray.from.y) * dirFracY; - var t4 = (this.upperBound.y - ray.from.y) * dirFracY; - var t5 = (this.lowerBound.z - ray.from.z) * dirFracZ; - var t6 = (this.upperBound.z - ray.from.z) * dirFracZ; - - // var tmin = Math.max(Math.max(Math.min(t1, t2), Math.min(t3, t4))); - // var tmax = Math.min(Math.min(Math.max(t1, t2), Math.max(t3, t4))); - var tmin = Math.max(Math.max(Math.min(t1, t2), Math.min(t3, t4)), Math.min(t5, t6)); - var tmax = Math.min(Math.min(Math.max(t1, t2), Math.max(t3, t4)), Math.max(t5, t6)); - - // if tmax < 0, ray (line) is intersecting AABB, but whole AABB is behing us - if (tmax < 0){ - //t = tmax; - return false; - } - - // if tmin > tmax, ray doesn't intersect AABB - if (tmin > tmax){ - //t = tmax; - return false; - } - - return true; -}; \ No newline at end of file diff --git a/src/collision/ArrayCollisionMatrix.js b/src/collision/ArrayCollisionMatrix.js deleted file mode 100644 index 8065233e2..000000000 --- a/src/collision/ArrayCollisionMatrix.js +++ /dev/null @@ -1,71 +0,0 @@ -module.exports = ArrayCollisionMatrix; - -/** - * Collision "matrix". It's actually a triangular-shaped array of whether two bodies are touching this step, for reference next step - * @class ArrayCollisionMatrix - * @constructor - */ -function ArrayCollisionMatrix() { - - /** - * The matrix storage - * @property matrix - * @type {Array} - */ - this.matrix = []; -} - -/** - * Get an element - * @method get - * @param {Number} i - * @param {Number} j - * @return {Number} - */ -ArrayCollisionMatrix.prototype.get = function(i, j) { - i = i.index; - j = j.index; - if (j > i) { - var temp = j; - j = i; - i = temp; - } - return this.matrix[(i*(i + 1)>>1) + j-1]; -}; - -/** - * Set an element - * @method set - * @param {Number} i - * @param {Number} j - * @param {Number} value - */ -ArrayCollisionMatrix.prototype.set = function(i, j, value) { - i = i.index; - j = j.index; - if (j > i) { - var temp = j; - j = i; - i = temp; - } - this.matrix[(i*(i + 1)>>1) + j-1] = value ? 1 : 0; -}; - -/** - * Sets all elements to zero - * @method reset - */ -ArrayCollisionMatrix.prototype.reset = function() { - for (var i=0, l=this.matrix.length; i!==l; i++) { - this.matrix[i]=0; - } -}; - -/** - * Sets the max number of objects - * @method setNumObjects - * @param {Number} n - */ -ArrayCollisionMatrix.prototype.setNumObjects = function(n) { - this.matrix.length = n*(n-1)>>1; -}; diff --git a/src/collision/Broadphase.js b/src/collision/Broadphase.js deleted file mode 100644 index 07c189a43..000000000 --- a/src/collision/Broadphase.js +++ /dev/null @@ -1,207 +0,0 @@ -var Body = require('../objects/Body'); -var Vec3 = require('../math/Vec3'); -var Quaternion = require('../math/Quaternion'); -var Shape = require('../shapes/Shape'); -var Plane = require('../shapes/Plane'); - -module.exports = Broadphase; - -/** - * Base class for broadphase implementations - * @class Broadphase - * @constructor - * @author schteppe - */ -function Broadphase(){ - /** - * The world to search for collisions in. - * @property world - * @type {World} - */ - this.world = null; - - /** - * If set to true, the broadphase uses bounding boxes for intersection test, else it uses bounding spheres. - * @property useBoundingBoxes - * @type {Boolean} - */ - this.useBoundingBoxes = false; - - /** - * Set to true if the objects in the world moved. - * @property {Boolean} dirty - */ - this.dirty = true; -} - -/** - * Get the collision pairs from the world - * @method collisionPairs - * @param {World} world The world to search in - * @param {Array} p1 Empty array to be filled with body objects - * @param {Array} p2 Empty array to be filled with body objects - */ -Broadphase.prototype.collisionPairs = function(world,p1,p2){ - throw new Error("collisionPairs not implemented for this BroadPhase class!"); -}; - -/** - * Check if a body pair needs to be intersection tested at all. - * @method needBroadphaseCollision - * @param {Body} bodyA - * @param {Body} bodyB - * @return {bool} - */ -Broadphase.prototype.needBroadphaseCollision = function(bodyA,bodyB){ - - // Check collision filter masks - if( (bodyA.collisionFilterGroup & bodyB.collisionFilterMask)===0 || (bodyB.collisionFilterGroup & bodyA.collisionFilterMask)===0){ - return false; - } - - // Check types - if(((bodyA.type & Body.STATIC)!==0 || bodyA.sleepState === Body.SLEEPING) && - ((bodyB.type & Body.STATIC)!==0 || bodyB.sleepState === Body.SLEEPING)) { - // Both bodies are static or sleeping. Skip. - return false; - } - - return true; -}; - -/** - * Check if the bounding volumes of two bodies intersect. - * @method intersectionTest - * @param {Body} bodyA - * @param {Body} bodyB - * @param {array} pairs1 - * @param {array} pairs2 - */ -Broadphase.prototype.intersectionTest = function(bodyA, bodyB, pairs1, pairs2){ - if(this.useBoundingBoxes){ - this.doBoundingBoxBroadphase(bodyA,bodyB,pairs1,pairs2); - } else { - this.doBoundingSphereBroadphase(bodyA,bodyB,pairs1,pairs2); - } -}; - -/** - * Check if the bounding spheres of two bodies are intersecting. - * @method doBoundingSphereBroadphase - * @param {Body} bodyA - * @param {Body} bodyB - * @param {Array} pairs1 bodyA is appended to this array if intersection - * @param {Array} pairs2 bodyB is appended to this array if intersection - */ -var Broadphase_collisionPairs_r = new Vec3(), // Temp objects - Broadphase_collisionPairs_normal = new Vec3(), - Broadphase_collisionPairs_quat = new Quaternion(), - Broadphase_collisionPairs_relpos = new Vec3(); -Broadphase.prototype.doBoundingSphereBroadphase = function(bodyA,bodyB,pairs1,pairs2){ - var r = Broadphase_collisionPairs_r; - bodyB.position.vsub(bodyA.position,r); - var boundingRadiusSum2 = Math.pow(bodyA.boundingRadius + bodyB.boundingRadius, 2); - var norm2 = r.norm2(); - if(norm2 < boundingRadiusSum2){ - pairs1.push(bodyA); - pairs2.push(bodyB); - } -}; - -/** - * Check if the bounding boxes of two bodies are intersecting. - * @method doBoundingBoxBroadphase - * @param {Body} bodyA - * @param {Body} bodyB - * @param {Array} pairs1 - * @param {Array} pairs2 - */ -Broadphase.prototype.doBoundingBoxBroadphase = function(bodyA,bodyB,pairs1,pairs2){ - if(bodyA.aabbNeedsUpdate){ - bodyA.computeAABB(); - } - if(bodyB.aabbNeedsUpdate){ - bodyB.computeAABB(); - } - - // Check AABB / AABB - if(bodyA.aabb.overlaps(bodyB.aabb)){ - pairs1.push(bodyA); - pairs2.push(bodyB); - } -}; - -/** - * Removes duplicate pairs from the pair arrays. - * @method makePairsUnique - * @param {Array} pairs1 - * @param {Array} pairs2 - */ -var Broadphase_makePairsUnique_temp = { keys:[] }, - Broadphase_makePairsUnique_p1 = [], - Broadphase_makePairsUnique_p2 = []; -Broadphase.prototype.makePairsUnique = function(pairs1,pairs2){ - var t = Broadphase_makePairsUnique_temp, - p1 = Broadphase_makePairsUnique_p1, - p2 = Broadphase_makePairsUnique_p2, - N = pairs1.length; - - for(var i=0; i!==N; i++){ - p1[i] = pairs1[i]; - p2[i] = pairs2[i]; - } - - pairs1.length = 0; - pairs2.length = 0; - - for(var i=0; i!==N; i++){ - var id1 = p1[i].id, - id2 = p2[i].id; - var key = id1 < id2 ? id1+","+id2 : id2+","+id1; - t[key] = i; - t.keys.push(key); - } - - for(var i=0; i!==t.keys.length; i++){ - var key = t.keys.pop(), - pairIndex = t[key]; - pairs1.push(p1[pairIndex]); - pairs2.push(p2[pairIndex]); - delete t[key]; - } -}; - -/** - * To be implemented by subcasses - * @method setWorld - * @param {World} world - */ -Broadphase.prototype.setWorld = function(world){ -}; - -/** - * Check if the bounding spheres of two bodies overlap. - * @method boundingSphereCheck - * @param {Body} bodyA - * @param {Body} bodyB - * @return {boolean} - */ -var bsc_dist = new Vec3(); -Broadphase.boundingSphereCheck = function(bodyA,bodyB){ - var dist = bsc_dist; - bodyA.position.vsub(bodyB.position,dist); - return Math.pow(bodyA.shape.boundingSphereRadius + bodyB.shape.boundingSphereRadius,2) > dist.norm2(); -}; - -/** - * Returns all the bodies within the AABB. - * @method aabbQuery - * @param {World} world - * @param {AABB} aabb - * @param {array} result An array to store resulting bodies in. - * @return {array} - */ -Broadphase.prototype.aabbQuery = function(world, aabb, result){ - console.warn('.aabbQuery is not implemented in this Broadphase subclass.'); - return []; -}; \ No newline at end of file diff --git a/src/collision/Broadphase.ts b/src/collision/Broadphase.ts new file mode 100644 index 000000000..f67f65c1f --- /dev/null +++ b/src/collision/Broadphase.ts @@ -0,0 +1,223 @@ +import { Box3, Vector3 } from 'feng3d'; +import { Body } from '../objects/Body'; +import { World } from '../world/World'; + +export class Broadphase +{ + /** + * The world to search for collisions in. + */ + world: World; + + /** + * If set to true, the broadphase uses bounding boxes for intersection test, else it uses bounding spheres. + */ + useBoundingBoxes: boolean; + + /** + * Set to true if the objects in the world moved. + */ + dirty: boolean; + + /** + * Base class for broadphase implementations + * + * @author schteppe + */ + constructor() + { + this.world = null; + this.useBoundingBoxes = false; + this.dirty = true; + } + + /** + * Get the collision pairs from the world + * + * @param _world The world to search in + * @param _p1 Empty array to be filled with body objects + * @param _p2 Empty array to be filled with body objects + */ + collisionPairs(_world: World, _p1: Body[], _p2: Body[]) + { + throw new Error('collisionPairs not implemented for this BroadPhase class!'); + } + + /** + * Check if a body pair needs to be intersection tested at all. + * + * @param bodyA + * @param bodyB + */ + needBroadphaseCollision(bodyA: Body, bodyB: Body) + { + // Check collision filter masks + if ((bodyA.collisionFilterGroup & bodyB.collisionFilterMask) === 0 || (bodyB.collisionFilterGroup & bodyA.collisionFilterMask) === 0) + { + return false; + } + + // Check types + if (((bodyA.type & Body.STATIC) !== 0 || bodyA.sleepState === Body.SLEEPING) + && ((bodyB.type & Body.STATIC) !== 0 || bodyB.sleepState === Body.SLEEPING)) + { + // Both bodies are static or sleeping. Skip. + return false; + } + + return true; + } + + /** + * Check if the bounding volumes of two bodies intersect. + * + * @param bodyA + * @param bodyB + * @param pairs1 + * @param pairs2 + */ + intersectionTest(bodyA: Body, bodyB: Body, pairs1: Body[], pairs2: Body[]) + { + if (this.useBoundingBoxes) + { + this.doBoundingBoxBroadphase(bodyA, bodyB, pairs1, pairs2); + } + else + { + this.doBoundingSphereBroadphase(bodyA, bodyB, pairs1, pairs2); + } + } + + /** + * Check if the bounding spheres of two bodies are intersecting. + * @param bodyA + * @param bodyB + * @param pairs1 bodyA is appended to this array if intersection + * @param pairs2 bodyB is appended to this array if intersection + */ + doBoundingSphereBroadphase(bodyA: Body, bodyB: Body, pairs1: Body[], pairs2: Body[]) + { + const r = BroadphaseCollisionPairsR; + bodyB.position.subTo(bodyA.position, r); + const boundingRadiusSum2 = Math.pow(bodyA.boundingRadius + bodyB.boundingRadius, 2); + const norm2 = r.lengthSquared; + if (norm2 < boundingRadiusSum2) + { + pairs1.push(bodyA); + pairs2.push(bodyB); + } + } + + /** + * Check if the bounding boxes of two bodies are intersecting. + * @param bodyA + * @param bodyB + * @param pairs1 + * @param pairs2 + */ + doBoundingBoxBroadphase(bodyA: Body, bodyB: Body, pairs1: Body[], pairs2: Body[]) + { + if (bodyA.aabbNeedsUpdate) + { + bodyA.computeAABB(); + } + if (bodyB.aabbNeedsUpdate) + { + bodyB.computeAABB(); + } + + // Check AABB / AABB + if (bodyA.aabb.overlaps(bodyB.aabb)) + { + pairs1.push(bodyA); + pairs2.push(bodyB); + } + } + + /** + * Removes duplicate pairs from the pair arrays. + * @param pairs1 + * @param pairs2 + */ + makePairsUnique(pairs1: Body[], pairs2: Body[]) + { + const t = BroadphaseMakePairsUniqueTemp; + const p1 = BroadphaseMakePairsUniqueP1; + const p2 = BroadphaseMakePairsUniqueP2; + const N = pairs1.length; + + for (let i = 0; i !== N; i++) + { + p1[i] = pairs1[i]; + p2[i] = pairs2[i]; + } + + pairs1.length = 0; + pairs2.length = 0; + + for (let i = 0; i !== N; i++) + { + const id1 = p1[i].id; + const id2 = p2[i].id; + const key = id1 < id2 ? `${id1},${id2}` : `${id2},${id1}`; + t[key] = i; + t.keys.push(key); + } + + for (let i = 0; i !== t.keys.length; i++) + { + const key = t.keys.pop(); + const pairIndex = t[key]; + pairs1.push(p1[pairIndex]); + pairs2.push(p2[pairIndex]); + delete t[key]; + } + } + + /** + * To be implemented by subcasses + * @method setWorld + * @param {World} _world + */ + setWorld(_world: World) + { + } + + /** + * Check if the bounding spheres of two bodies overlap. + * @param bodyA + * @param bodyB + */ + static boundingSphereCheck(bodyA: Body, bodyB: Body) + { + const dist = bscDist; + bodyA.position.subTo(bodyB.position, dist); + + return Math.pow(bodyA.shape.boundingSphereRadius + bodyB.shape.boundingSphereRadius, 2) > dist.lengthSquared; + } + + /** + * Returns all the bodies within the AABB. + * + * @param _world + * @param _aabb + * @param _result An array to store resulting bodies in. + */ + aabbQuery(_world: World, _aabb: Box3, _result: Body[]) + { + console.warn('.aabbQuery is not implemented in this Broadphase subclass.'); + + return []; + } +} + +const BroadphaseCollisionPairsR = new Vector3();// Temp objects +// const Broadphase_collisionPairs_normal = new Vector3(); +// const Broadphase_collisionPairs_quat = new Quaternion(); +// const Broadphase_collisionPairs_relpos = new Vector3(); + +const BroadphaseMakePairsUniqueTemp: { keys: string[] } = { keys: [] }; +const BroadphaseMakePairsUniqueP1: Body[] = []; +const BroadphaseMakePairsUniqueP2: Body[] = []; + +const bscDist = new Vector3(); diff --git a/src/collision/GridBroadphase.js b/src/collision/GridBroadphase.js deleted file mode 100644 index 15d3546db..000000000 --- a/src/collision/GridBroadphase.js +++ /dev/null @@ -1,228 +0,0 @@ -module.exports = GridBroadphase; - -var Broadphase = require('./Broadphase'); -var Vec3 = require('../math/Vec3'); -var Shape = require('../shapes/Shape'); - -/** - * Axis aligned uniform grid broadphase. - * @class GridBroadphase - * @constructor - * @extends Broadphase - * @todo Needs support for more than just planes and spheres. - * @param {Vec3} aabbMin - * @param {Vec3} aabbMax - * @param {Number} nx Number of boxes along x - * @param {Number} ny Number of boxes along y - * @param {Number} nz Number of boxes along z - */ -function GridBroadphase(aabbMin,aabbMax,nx,ny,nz){ - Broadphase.apply(this); - this.nx = nx || 10; - this.ny = ny || 10; - this.nz = nz || 10; - this.aabbMin = aabbMin || new Vec3(100,100,100); - this.aabbMax = aabbMax || new Vec3(-100,-100,-100); - var nbins = this.nx * this.ny * this.nz; - if (nbins <= 0) { - throw "GridBroadphase: Each dimension's n must be >0"; - } - this.bins = []; - this.binLengths = []; //Rather than continually resizing arrays (thrashing the memory), just record length and allow them to grow - this.bins.length = nbins; - this.binLengths.length = nbins; - for (var i=0;i= nx) { xoff0 = nx - 1; } - if (yoff0 < 0) { yoff0 = 0; } else if (yoff0 >= ny) { yoff0 = ny - 1; } - if (zoff0 < 0) { zoff0 = 0; } else if (zoff0 >= nz) { zoff0 = nz - 1; } - if (xoff1 < 0) { xoff1 = 0; } else if (xoff1 >= nx) { xoff1 = nx - 1; } - if (yoff1 < 0) { yoff1 = 0; } else if (yoff1 >= ny) { yoff1 = ny - 1; } - if (zoff1 < 0) { zoff1 = 0; } else if (zoff1 >= nz) { zoff1 = nz - 1; } - - xoff0 *= xstep; - yoff0 *= ystep; - zoff0 *= zstep; - xoff1 *= xstep; - yoff1 *= ystep; - zoff1 *= zstep; - - for (var xoff = xoff0; xoff <= xoff1; xoff += xstep) { - for (var yoff = yoff0; yoff <= yoff1; yoff += ystep) { - for (var zoff = zoff0; zoff <= zoff1; zoff += zstep) { - var idx = xoff+yoff+zoff; - bins[idx][binLengths[idx]++] = bi; - } - } - } - } - - // Put all bodies into the bins - for(var i=0; i!==N; i++){ - var bi = bodies[i]; - var si = bi.shape; - - switch(si.type){ - case SPHERE: - // Put in bin - // check if overlap with other bins - var x = bi.position.x, - y = bi.position.y, - z = bi.position.z; - var r = si.radius; - - addBoxToBins(x-r, y-r, z-r, x+r, y+r, z+r, bi); - break; - - case PLANE: - if(si.worldNormalNeedsUpdate){ - si.computeWorldNormal(bi.quaternion); - } - var planeNormal = si.worldNormal; - - //Relative position from origin of plane object to the first bin - //Incremented as we iterate through the bins - var xreset = xmin + binsizeX*0.5 - bi.position.x, - yreset = ymin + binsizeY*0.5 - bi.position.y, - zreset = zmin + binsizeZ*0.5 - bi.position.z; - - var d = GridBroadphase_collisionPairs_d; - d.set(xreset, yreset, zreset); - - for (var xi = 0, xoff = 0; xi !== nx; xi++, xoff += xstep, d.y = yreset, d.x += binsizeX) { - for (var yi = 0, yoff = 0; yi !== ny; yi++, yoff += ystep, d.z = zreset, d.y += binsizeY) { - for (var zi = 0, zoff = 0; zi !== nz; zi++, zoff += zstep, d.z += binsizeZ) { - if (d.dot(planeNormal) < binRadius) { - var idx = xoff + yoff + zoff; - bins[idx][binLengths[idx]++] = bi; - } - } - } - } - break; - - default: - if (bi.aabbNeedsUpdate) { - bi.computeAABB(); - } - - addBoxToBins( - bi.aabb.lowerBound.x, - bi.aabb.lowerBound.y, - bi.aabb.lowerBound.z, - bi.aabb.upperBound.x, - bi.aabb.upperBound.y, - bi.aabb.upperBound.z, - bi); - break; - } - } - - // Check each bin - for(var i=0; i!==Nbins; i++){ - var binLength = binLengths[i]; - //Skip bins with no potential collisions - if (binLength > 1) { - var bin = bins[i]; - - // Do N^2 broadphase inside - for(var xi=0; xi!==binLength; xi++){ - var bi = bin[xi]; - for(var yi=0; yi!==xi; yi++){ - var bj = bin[yi]; - if(this.needBroadphaseCollision(bi,bj)){ - this.intersectionTest(bi,bj,pairs1,pairs2); - } - } - } - } - } - -// for (var zi = 0, zoff=0; zi < nz; zi++, zoff+= zstep) { -// console.log("layer "+zi); -// for (var yi = 0, yoff=0; yi < ny; yi++, yoff += ystep) { -// var row = ''; -// for (var xi = 0, xoff=0; xi < nx; xi++, xoff += xstep) { -// var idx = xoff + yoff + zoff; -// row += ' ' + binLengths[idx]; -// } -// console.log(row); -// } -// } - - this.makePairsUnique(pairs1,pairs2); -}; diff --git a/src/collision/GridBroadphase.ts b/src/collision/GridBroadphase.ts new file mode 100644 index 000000000..52b82252c --- /dev/null +++ b/src/collision/GridBroadphase.ts @@ -0,0 +1,267 @@ +import { Vector3 } from 'feng3d'; +import { Body } from '../objects/Body'; +import { Plane } from '../shapes/Plane'; +import { Shape } from '../shapes/Shape'; +import { World } from '../world/World'; +import { Broadphase } from './Broadphase'; + +export class GridBroadphase extends Broadphase +{ + nx: number; + ny: number; + nz: number; + aabbMin: Vector3; + aabbMax: Vector3; + bins: Body[][]; + binLengths: number[]; // Rather than continually resizing arrays (thrashing the memory), just record length and allow them to grow + + /** + * Axis aligned uniform grid broadphase. + * + * @param aabbMin + * @param aabbMax + * @param nx Number of boxes along x + * @param ny Number of boxes along y + * @param nz Number of boxes along z + * + * @todo Needs support for more than just planes and spheres. + */ + constructor(aabbMin: Vector3, aabbMax: Vector3, nx: number, ny: number, nz: number) + { + super(); + this.nx = nx || 10; + this.ny = ny || 10; + this.nz = nz || 10; + this.aabbMin = aabbMin || new Vector3(100, 100, 100); + this.aabbMax = aabbMax || new Vector3(-100, -100, -100); + const nbins = this.nx * this.ny * this.nz; + if (nbins <= 0) + { + throw 'GridBroadphase: Each dimension\'s n must be >0'; + } + this.bins = []; + this.binLengths = []; // Rather than continually resizing arrays (thrashing the memory), just record length and allow them to grow + this.bins.length = nbins; + this.binLengths.length = nbins; + for (let i = 0; i < nbins; i++) + { + this.bins[i] = []; + this.binLengths[i] = 0; + } + } + + /** + * Get all the collision pairs in the physics world + * + * @param world + * @param pairs1 + * @param pairs2 + */ + collisionPairs(world: World, pairs1: Body[], pairs2: Body[]) + { + const N = world.numObjects(); + const bodies = world.bodies; + + const max = this.aabbMax; + const min = this.aabbMin; + const nx = this.nx; + const ny = this.ny; + const nz = this.nz; + + const xstep = ny * nz; + const ystep = nz; + const zstep = 1; + + const xmax = max.x; + const ymax = max.y; + const zmax = max.z; + const xmin = min.x; + const ymin = min.y; + const zmin = min.z; + + const xmult = nx / (xmax - xmin); + const ymult = ny / (ymax - ymin); + const zmult = nz / (zmax - zmin); + + const binsizeX = (xmax - xmin) / nx; + const binsizeY = (ymax - ymin) / ny; + const binsizeZ = (zmax - zmin) / nz; + + const binRadius = Math.sqrt(binsizeX * binsizeX + binsizeY * binsizeY + binsizeZ * binsizeZ) * 0.5; + + const types = Shape.types; + const SPHERE = types.SPHERE; + const PLANE = types.PLANE; + // const BOX = types.BOX; + // const COMPOUND = types.COMPOUND; + // const CONVEXPOLYHEDRON = types.CONVEXPOLYHEDRON; + + const bins = this.bins; + const binLengths = this.binLengths; + const Nbins = this.bins.length; + + // Reset bins + for (let i = 0; i !== Nbins; i++) + { + binLengths[i] = 0; + } + + const ceil = Math.ceil; + // var min = Math.min; + // var max = Math.max; + + function addBoxToBins(x0, y0, z0, x1, y1, z1, bi) + { + let xoff0 = ((x0 - xmin) * xmult) | 0; + let yoff0 = ((y0 - ymin) * ymult) | 0; + let zoff0 = ((z0 - zmin) * zmult) | 0; + let xoff1 = ceil((x1 - xmin) * xmult); + let yoff1 = ceil((y1 - ymin) * ymult); + let zoff1 = ceil((z1 - zmin) * zmult); + + if (xoff0 < 0) { xoff0 = 0; } + else if (xoff0 >= nx) { xoff0 = nx - 1; } + if (yoff0 < 0) { yoff0 = 0; } + else if (yoff0 >= ny) { yoff0 = ny - 1; } + if (zoff0 < 0) { zoff0 = 0; } + else if (zoff0 >= nz) { zoff0 = nz - 1; } + if (xoff1 < 0) { xoff1 = 0; } + else if (xoff1 >= nx) { xoff1 = nx - 1; } + if (yoff1 < 0) { yoff1 = 0; } + else if (yoff1 >= ny) { yoff1 = ny - 1; } + if (zoff1 < 0) { zoff1 = 0; } + else if (zoff1 >= nz) { zoff1 = nz - 1; } + + xoff0 *= xstep; + yoff0 *= ystep; + zoff0 *= zstep; + xoff1 *= xstep; + yoff1 *= ystep; + zoff1 *= zstep; + + for (let xoff = xoff0; xoff <= xoff1; xoff += xstep) + { + for (let yoff = yoff0; yoff <= yoff1; yoff += ystep) + { + for (let zoff = zoff0; zoff <= zoff1; zoff += zstep) + { + const idx = xoff + yoff + zoff; + bins[idx][binLengths[idx]++] = bi; + } + } + } + } + + // Put all bodies into the bins + for (let i = 0; i !== N; i++) + { + const bi = bodies[i]; + const si = bi.shape; + + switch (si.type) + { + case SPHERE: + // Put in bin + // check if overlap with other bins + const x = bi.position.x; + const y = bi.position.y; + const z = bi.position.z; + const r = si.radius; + + addBoxToBins(x - r, y - r, z - r, x + r, y + r, z + r, bi); + break; + + case PLANE: + const plane = si; + if (plane.worldNormalNeedsUpdate) + { + plane.computeWorldNormal(bi.quaternion); + } + const planeNormal = plane.worldNormal; + + // Relative position from origin of plane object to the first bin + // Incremented as we iterate through the bins + const xreset = xmin + binsizeX * 0.5 - bi.position.x; + const yreset = ymin + binsizeY * 0.5 - bi.position.y; + const zreset = zmin + binsizeZ * 0.5 - bi.position.z; + + const d = GridBroadphaseCollisionPairsD; + d.set(xreset, yreset, zreset); + + for (let xi = 0, xoff = 0; xi !== nx; xi++, xoff += xstep, d.y = yreset, d.x += binsizeX) + { + for (let yi = 0, yoff = 0; yi !== ny; yi++, yoff += ystep, d.z = zreset, d.y += binsizeY) + { + for (let zi = 0, zoff = 0; zi !== nz; zi++, zoff += zstep, d.z += binsizeZ) + { + if (d.dot(planeNormal) < binRadius) + { + const idx = xoff + yoff + zoff; + bins[idx][binLengths[idx]++] = bi; + } + } + } + } + break; + + default: + if (bi.aabbNeedsUpdate) + { + bi.computeAABB(); + } + + addBoxToBins( + bi.aabb.min.x, + bi.aabb.min.y, + bi.aabb.min.z, + bi.aabb.max.x, + bi.aabb.max.y, + bi.aabb.max.z, + bi); + break; + } + } + + // Check each bin + for (let i = 0; i !== Nbins; i++) + { + const binLength = binLengths[i]; + // Skip bins with no potential collisions + if (binLength > 1) + { + const bin = bins[i]; + + // Do N^2 broadphase inside + for (let xi = 0; xi !== binLength; xi++) + { + const bi = bin[xi]; + for (let yi = 0; yi !== xi; yi++) + { + const bj = bin[yi]; + if (this.needBroadphaseCollision(bi, bj)) + { + this.intersectionTest(bi, bj, pairs1, pairs2); + } + } + } + } + } + + // for (var zi = 0, zoff=0; zi < nz; zi++, zoff+= zstep) { + // console.log("layer "+zi); + // for (var yi = 0, yoff=0; yi < ny; yi++, yoff += ystep) { + // var row = ''; + // for (var xi = 0, xoff=0; xi < nx; xi++, xoff += xstep) { + // var idx = xoff + yoff + zoff; + // row += ' ' + binLengths[idx]; + // } + // console.log(row); + // } + // } + + this.makePairsUnique(pairs1, pairs2); + } +} + +const GridBroadphaseCollisionPairsD = new Vector3(); +// const GridBroadphase_collisionPairs_binPos = new Vector3(); diff --git a/src/collision/NaiveBroadphase.js b/src/collision/NaiveBroadphase.js deleted file mode 100644 index 4a2f2e7f9..000000000 --- a/src/collision/NaiveBroadphase.js +++ /dev/null @@ -1,74 +0,0 @@ -module.exports = NaiveBroadphase; - -var Broadphase = require('./Broadphase'); -var AABB = require('./AABB'); - -/** - * Naive broadphase implementation, used in lack of better ones. - * @class NaiveBroadphase - * @constructor - * @description The naive broadphase looks at all possible pairs without restriction, therefore it has complexity N^2 (which is bad) - * @extends Broadphase - */ -function NaiveBroadphase(){ - Broadphase.apply(this); -} -NaiveBroadphase.prototype = new Broadphase(); -NaiveBroadphase.prototype.constructor = NaiveBroadphase; - -/** - * Get all the collision pairs in the physics world - * @method collisionPairs - * @param {World} world - * @param {Array} pairs1 - * @param {Array} pairs2 - */ -NaiveBroadphase.prototype.collisionPairs = function(world,pairs1,pairs2){ - var bodies = world.bodies, - n = bodies.length, - i,j,bi,bj; - - // Naive N^2 ftw! - for(i=0; i!==n; i++){ - for(j=0; j!==i; j++){ - - bi = bodies[i]; - bj = bodies[j]; - - if(!this.needBroadphaseCollision(bi,bj)){ - continue; - } - - this.intersectionTest(bi,bj,pairs1,pairs2); - } - } -}; - -var tmpAABB = new AABB(); - -/** - * Returns all the bodies within an AABB. - * @method aabbQuery - * @param {World} world - * @param {AABB} aabb - * @param {array} result An array to store resulting bodies in. - * @return {array} - */ -NaiveBroadphase.prototype.aabbQuery = function(world, aabb, result){ - result = result || []; - - for(var i = 0; i < world.bodies.length; i++){ - var b = world.bodies[i]; - - if(b.aabbNeedsUpdate){ - b.computeAABB(); - } - - // Ugly hack until Body gets aabb - if(b.aabb.overlaps(aabb)){ - result.push(b); - } - } - - return result; -}; \ No newline at end of file diff --git a/src/collision/NaiveBroadphase.ts b/src/collision/NaiveBroadphase.ts new file mode 100644 index 000000000..e7ecfa74a --- /dev/null +++ b/src/collision/NaiveBroadphase.ts @@ -0,0 +1,78 @@ +import { Box3 } from 'feng3d'; +import { Body } from '../objects/Body'; +import { World } from '../world/World'; +import { Broadphase } from './Broadphase'; + +export class NaiveBroadphase extends Broadphase +{ + /** + * Naive broadphase implementation, used in lack of better ones. + * @description The naive broadphase looks at all possible pairs without restriction, therefore it has complexity N^2 (which is bad) + */ + constructor() + { + super(); + } + + /** + * Get all the collision pairs in the physics world + * @param world + * @param pairs1 + * @param pairs2 + */ + collisionPairs(world: World, pairs1: Body[], pairs2: Body[]) + { + const bodies = world.bodies; + const n = bodies.length; + let i: number; let j: number; let bi: Body; + let bj: Body; + + // Naive N^2 ftw! + for (i = 0; i !== n; i++) + { + for (j = 0; j !== i; j++) + { + bi = bodies[i]; + bj = bodies[j]; + + if (!this.needBroadphaseCollision(bi, bj)) + { + continue; + } + + this.intersectionTest(bi, bj, pairs1, pairs2); + } + } + } + + /** + * Returns all the bodies within an AABB. + * @param world + * @param aabb + * @param result An array to store resulting bodies in. + */ + aabbQuery(world: World, aabb: Box3, result: Body[]) + { + result = result || []; + + for (let i = 0; i < world.bodies.length; i++) + { + const b = world.bodies[i]; + + if (b.aabbNeedsUpdate) + { + b.computeAABB(); + } + + // Ugly hack until Body gets aabb + if (b.aabb.overlaps(aabb)) + { + result.push(b); + } + } + + return result; + } +} + +// const tmpAABB = new Box3(); diff --git a/src/collision/ObjectCollisionMatrix.js b/src/collision/ObjectCollisionMatrix.js deleted file mode 100644 index 2ce129405..000000000 --- a/src/collision/ObjectCollisionMatrix.js +++ /dev/null @@ -1,71 +0,0 @@ -module.exports = ObjectCollisionMatrix; - -/** - * Records what objects are colliding with each other - * @class ObjectCollisionMatrix - * @constructor - */ -function ObjectCollisionMatrix() { - - /** - * The matrix storage - * @property matrix - * @type {Object} - */ - this.matrix = {}; -} - -/** - * @method get - * @param {Number} i - * @param {Number} j - * @return {Number} - */ -ObjectCollisionMatrix.prototype.get = function(i, j) { - i = i.id; - j = j.id; - if (j > i) { - var temp = j; - j = i; - i = temp; - } - return i+'-'+j in this.matrix; -}; - -/** - * @method set - * @param {Number} i - * @param {Number} j - * @param {Number} value - */ -ObjectCollisionMatrix.prototype.set = function(i, j, value) { - i = i.id; - j = j.id; - if (j > i) { - var temp = j; - j = i; - i = temp; - } - if (value) { - this.matrix[i+'-'+j] = true; - } - else { - delete this.matrix[i+'-'+j]; - } -}; - -/** - * Empty the matrix - * @method reset - */ -ObjectCollisionMatrix.prototype.reset = function() { - this.matrix = {}; -}; - -/** - * Set max number of objects - * @method setNumObjects - * @param {Number} n - */ -ObjectCollisionMatrix.prototype.setNumObjects = function(n) { -}; diff --git a/src/collision/OverlapKeeper.js b/src/collision/OverlapKeeper.js deleted file mode 100644 index f8e2be668..000000000 --- a/src/collision/OverlapKeeper.js +++ /dev/null @@ -1,95 +0,0 @@ -module.exports = OverlapKeeper; - -/** - * @class OverlapKeeper - * @constructor - */ -function OverlapKeeper() { - this.current = []; - this.previous = []; -} - -OverlapKeeper.prototype.getKey = function(i, j) { - if (j < i) { - var temp = j; - j = i; - i = temp; - } - return (i << 16) | j; -}; - - -/** - * @method set - * @param {Number} i - * @param {Number} j - */ -OverlapKeeper.prototype.set = function(i, j) { - // Insertion sort. This way the diff will have linear complexity. - var key = this.getKey(i, j); - var current = this.current; - var index = 0; - while(key > current[index]){ - index++; - } - if(key === current[index]){ - return; // Pair was already added - } - for(var j=current.length-1; j>=index; j--){ - current[j + 1] = current[j]; - } - current[index] = key; -}; - -/** - * @method tick - */ -OverlapKeeper.prototype.tick = function() { - var tmp = this.current; - this.current = this.previous; - this.previous = tmp; - this.current.length = 0; -}; - -function unpackAndPush(array, key){ - array.push((key & 0xFFFF0000) >> 16, key & 0x0000FFFF); -} - -/** - * @method getDiff - * @param {array} additions - * @param {array} removals - */ -OverlapKeeper.prototype.getDiff = function(additions, removals) { - var a = this.current; - var b = this.previous; - var al = a.length; - var bl = b.length; - - var j=0; - for (var i = 0; i < al; i++) { - var found = false; - var keyA = a[i]; - while(keyA > b[j]){ - j++; - } - found = keyA === b[j]; - - if(!found){ - unpackAndPush(additions, keyA); - } - } - j = 0; - for (var i = 0; i < bl; i++) { - var found = false; - var keyB = b[i]; - while(keyB > a[j]){ - j++; - } - found = a[j] === keyB; - - if(!found){ - unpackAndPush(removals, keyB); - } - } -}; \ No newline at end of file diff --git a/src/collision/OverlapKeeper.ts b/src/collision/OverlapKeeper.ts new file mode 100644 index 000000000..01490aca0 --- /dev/null +++ b/src/collision/OverlapKeeper.ts @@ -0,0 +1,98 @@ +export class OverlapKeeper +{ + current: number[] = []; + previous: number[] = []; + + constructor() + { + this.current = []; + this.previous = []; + } + + getKey(i: number, j: number) + { + if (j < i) + { + const temp = j; + j = i; + i = temp; + } + + return (i << 16) | j; + } + + set(i: number, j: number) + { + // Insertion sort. This way the diff will have linear complexity. + const key = this.getKey(i, j); + const current = this.current; + let index = 0; + while (key > current[index]) + { + index++; + } + if (key === current[index]) + { + return; // Pair was already added + } + for (let j = current.length - 1; j >= index; j--) + { + current[j + 1] = current[j]; + } + current[index] = key; + } + + tick() + { + const tmp = this.current; + this.current = this.previous; + this.previous = tmp; + this.current.length = 0; + } + + unpackAndPush(array: number[], key: number) + { + array.push((key & 0xFFFF0000) >> 16, key & 0x0000FFFF); + } + + getDiff(additions: number[], removals: number[]) + { + const a = this.current; + const b = this.previous; + const al = a.length; + const bl = b.length; + + let j = 0; + for (let i = 0; i < al; i++) + { + let found = false; + const keyA = a[i]; + while (keyA > b[j]) + { + j++; + } + found = keyA === b[j]; + + if (!found) + { + this.unpackAndPush(additions, keyA); + } + } + j = 0; + for (let i = 0; i < bl; i++) + { + let found = false; + const keyB = b[i]; + while (keyB > a[j]) + { + j++; + } + found = a[j] === keyB; + + if (!found) + { + this.unpackAndPush(removals, keyB); + } + } + } +} diff --git a/src/collision/Ray.js b/src/collision/Ray.js deleted file mode 100644 index 4cbaf353b..000000000 --- a/src/collision/Ray.js +++ /dev/null @@ -1,824 +0,0 @@ -module.exports = Ray; - -var Vec3 = require('../math/Vec3'); -var Quaternion = require('../math/Quaternion'); -var Transform = require('../math/Transform'); -var ConvexPolyhedron = require('../shapes/ConvexPolyhedron'); -var Box = require('../shapes/Box'); -var RaycastResult = require('../collision/RaycastResult'); -var Shape = require('../shapes/Shape'); -var AABB = require('../collision/AABB'); - -/** - * A line in 3D space that intersects bodies and return points. - * @class Ray - * @constructor - * @param {Vec3} from - * @param {Vec3} to - */ -function Ray(from, to){ - /** - * @property {Vec3} from - */ - this.from = from ? from.clone() : new Vec3(); - - /** - * @property {Vec3} to - */ - this.to = to ? to.clone() : new Vec3(); - - /** - * @private - * @property {Vec3} _direction - */ - this._direction = new Vec3(); - - /** - * The precision of the ray. Used when checking parallelity etc. - * @property {Number} precision - */ - this.precision = 0.0001; - - /** - * Set to true if you want the Ray to take .collisionResponse flags into account on bodies and shapes. - * @property {Boolean} checkCollisionResponse - */ - this.checkCollisionResponse = true; - - /** - * If set to true, the ray skips any hits with normal.dot(rayDirection) < 0. - * @property {Boolean} skipBackfaces - */ - this.skipBackfaces = false; - - /** - * @property {number} collisionFilterMask - * @default -1 - */ - this.collisionFilterMask = -1; - - /** - * @property {number} collisionFilterGroup - * @default -1 - */ - this.collisionFilterGroup = -1; - - /** - * The intersection mode. Should be Ray.ANY, Ray.ALL or Ray.CLOSEST. - * @property {number} mode - */ - this.mode = Ray.ANY; - - /** - * Current result object. - * @property {RaycastResult} result - */ - this.result = new RaycastResult(); - - /** - * Will be set to true during intersectWorld() if the ray hit anything. - * @property {Boolean} hasHit - */ - this.hasHit = false; - - /** - * Current, user-provided result callback. Will be used if mode is Ray.ALL. - * @property {Function} callback - */ - this.callback = function(result){}; -} -Ray.prototype.constructor = Ray; - -Ray.CLOSEST = 1; -Ray.ANY = 2; -Ray.ALL = 4; - -var tmpAABB = new AABB(); -var tmpArray = []; - -/** - * Do itersection against all bodies in the given World. - * @method intersectWorld - * @param {World} world - * @param {object} options - * @return {Boolean} True if the ray hit anything, otherwise false. - */ -Ray.prototype.intersectWorld = function (world, options) { - this.mode = options.mode || Ray.ANY; - this.result = options.result || new RaycastResult(); - this.skipBackfaces = !!options.skipBackfaces; - this.collisionFilterMask = typeof(options.collisionFilterMask) !== 'undefined' ? options.collisionFilterMask : -1; - this.collisionFilterGroup = typeof(options.collisionFilterGroup) !== 'undefined' ? options.collisionFilterGroup : -1; - if(options.from){ - this.from.copy(options.from); - } - if(options.to){ - this.to.copy(options.to); - } - this.callback = options.callback || function(){}; - this.hasHit = false; - - this.result.reset(); - this._updateDirection(); - - this.getAABB(tmpAABB); - tmpArray.length = 0; - world.broadphase.aabbQuery(world, tmpAABB, tmpArray); - this.intersectBodies(tmpArray); - - return this.hasHit; -}; - -var v1 = new Vec3(), - v2 = new Vec3(); - -/* - * As per "Barycentric Technique" as named here http://www.blackpawn.com/texts/pointinpoly/default.html But without the division - */ -Ray.pointInTriangle = pointInTriangle; -function pointInTriangle(p, a, b, c) { - c.vsub(a,v0); - b.vsub(a,v1); - p.vsub(a,v2); - - var dot00 = v0.dot( v0 ); - var dot01 = v0.dot( v1 ); - var dot02 = v0.dot( v2 ); - var dot11 = v1.dot( v1 ); - var dot12 = v1.dot( v2 ); - - var u,v; - - return ( (u = dot11 * dot02 - dot01 * dot12) >= 0 ) && - ( (v = dot00 * dot12 - dot01 * dot02) >= 0 ) && - ( u + v < ( dot00 * dot11 - dot01 * dot01 ) ); -} - -/** - * Shoot a ray at a body, get back information about the hit. - * @method intersectBody - * @private - * @param {Body} body - * @param {RaycastResult} [result] Deprecated - set the result property of the Ray instead. - */ -var intersectBody_xi = new Vec3(); -var intersectBody_qi = new Quaternion(); -Ray.prototype.intersectBody = function (body, result) { - if(result){ - this.result = result; - this._updateDirection(); - } - var checkCollisionResponse = this.checkCollisionResponse; - - if(checkCollisionResponse && !body.collisionResponse){ - return; - } - - if((this.collisionFilterGroup & body.collisionFilterMask)===0 || (body.collisionFilterGroup & this.collisionFilterMask)===0){ - return; - } - - var xi = intersectBody_xi; - var qi = intersectBody_qi; - - for (var i = 0, N = body.shapes.length; i < N; i++) { - var shape = body.shapes[i]; - - if(checkCollisionResponse && !shape.collisionResponse){ - continue; // Skip - } - - body.quaternion.mult(body.shapeOrientations[i], qi); - body.quaternion.vmult(body.shapeOffsets[i], xi); - xi.vadd(body.position, xi); - - this.intersectShape( - shape, - qi, - xi, - body - ); - - if(this.result._shouldStop){ - break; - } - } -}; - -/** - * @method intersectBodies - * @param {Array} bodies An array of Body objects. - * @param {RaycastResult} [result] Deprecated - */ -Ray.prototype.intersectBodies = function (bodies, result) { - if(result){ - this.result = result; - this._updateDirection(); - } - - for ( var i = 0, l = bodies.length; !this.result._shouldStop && i < l; i ++ ) { - this.intersectBody(bodies[i]); - } -}; - -/** - * Updates the _direction vector. - * @private - * @method _updateDirection - */ -Ray.prototype._updateDirection = function(){ - this.to.vsub(this.from, this._direction); - this._direction.normalize(); -}; - -/** - * @method intersectShape - * @private - * @param {Shape} shape - * @param {Quaternion} quat - * @param {Vec3} position - * @param {Body} body - */ -Ray.prototype.intersectShape = function(shape, quat, position, body){ - var from = this.from; - - - // Checking boundingSphere - var distance = distanceFromIntersection(from, this._direction, position); - if ( distance > shape.boundingSphereRadius ) { - return; - } - - var intersectMethod = this[shape.type]; - if(intersectMethod){ - intersectMethod.call(this, shape, quat, position, body, shape); - } -}; - -var vector = new Vec3(); -var normal = new Vec3(); -var intersectPoint = new Vec3(); - -var a = new Vec3(); -var b = new Vec3(); -var c = new Vec3(); -var d = new Vec3(); - -var tmpRaycastResult = new RaycastResult(); - -/** - * @method intersectBox - * @private - * @param {Shape} shape - * @param {Quaternion} quat - * @param {Vec3} position - * @param {Body} body - */ -Ray.prototype.intersectBox = function(shape, quat, position, body, reportedShape){ - return this.intersectConvex(shape.convexPolyhedronRepresentation, quat, position, body, reportedShape); -}; -Ray.prototype[Shape.types.BOX] = Ray.prototype.intersectBox; - -/** - * @method intersectPlane - * @private - * @param {Shape} shape - * @param {Quaternion} quat - * @param {Vec3} position - * @param {Body} body - */ -Ray.prototype.intersectPlane = function(shape, quat, position, body, reportedShape){ - var from = this.from; - var to = this.to; - var direction = this._direction; - - // Get plane normal - var worldNormal = new Vec3(0, 0, 1); - quat.vmult(worldNormal, worldNormal); - - var len = new Vec3(); - from.vsub(position, len); - var planeToFrom = len.dot(worldNormal); - to.vsub(position, len); - var planeToTo = len.dot(worldNormal); - - if(planeToFrom * planeToTo > 0){ - // "from" and "to" are on the same side of the plane... bail out - return; - } - - if(from.distanceTo(to) < planeToFrom){ - return; - } - - var n_dot_dir = worldNormal.dot(direction); - - if (Math.abs(n_dot_dir) < this.precision) { - // No intersection - return; - } - - var planePointToFrom = new Vec3(); - var dir_scaled_with_t = new Vec3(); - var hitPointWorld = new Vec3(); - - from.vsub(position, planePointToFrom); - var t = -worldNormal.dot(planePointToFrom) / n_dot_dir; - direction.scale(t, dir_scaled_with_t); - from.vadd(dir_scaled_with_t, hitPointWorld); - - this.reportIntersection(worldNormal, hitPointWorld, reportedShape, body, -1); -}; -Ray.prototype[Shape.types.PLANE] = Ray.prototype.intersectPlane; - -/** - * Get the world AABB of the ray. - * @method getAABB - * @param {AABB} aabb - */ -Ray.prototype.getAABB = function(result){ - var to = this.to; - var from = this.from; - result.lowerBound.x = Math.min(to.x, from.x); - result.lowerBound.y = Math.min(to.y, from.y); - result.lowerBound.z = Math.min(to.z, from.z); - result.upperBound.x = Math.max(to.x, from.x); - result.upperBound.y = Math.max(to.y, from.y); - result.upperBound.z = Math.max(to.z, from.z); -}; - -var intersectConvexOptions = { - faceList: [0] -}; -var worldPillarOffset = new Vec3(); -var intersectHeightfield_localRay = new Ray(); -var intersectHeightfield_index = []; -var intersectHeightfield_minMax = []; - -/** - * @method intersectHeightfield - * @private - * @param {Shape} shape - * @param {Quaternion} quat - * @param {Vec3} position - * @param {Body} body - */ -Ray.prototype.intersectHeightfield = function(shape, quat, position, body, reportedShape){ - var data = shape.data, - w = shape.elementSize; - - // Convert the ray to local heightfield coordinates - var localRay = intersectHeightfield_localRay; //new Ray(this.from, this.to); - localRay.from.copy(this.from); - localRay.to.copy(this.to); - Transform.pointToLocalFrame(position, quat, localRay.from, localRay.from); - Transform.pointToLocalFrame(position, quat, localRay.to, localRay.to); - localRay._updateDirection(); - - // Get the index of the data points to test against - var index = intersectHeightfield_index; - var iMinX, iMinY, iMaxX, iMaxY; - - // Set to max - iMinX = iMinY = 0; - iMaxX = iMaxY = shape.data.length - 1; - - var aabb = new AABB(); - localRay.getAABB(aabb); - - shape.getIndexOfPosition(aabb.lowerBound.x, aabb.lowerBound.y, index, true); - iMinX = Math.max(iMinX, index[0]); - iMinY = Math.max(iMinY, index[1]); - shape.getIndexOfPosition(aabb.upperBound.x, aabb.upperBound.y, index, true); - iMaxX = Math.min(iMaxX, index[0] + 1); - iMaxY = Math.min(iMaxY, index[1] + 1); - - for(var i = iMinX; i < iMaxX; i++){ - for(var j = iMinY; j < iMaxY; j++){ - - if(this.result._shouldStop){ - return; - } - - shape.getAabbAtIndex(i, j, aabb); - if(!aabb.overlapsRay(localRay)){ - continue; - } - - // Lower triangle - shape.getConvexTrianglePillar(i, j, false); - Transform.pointToWorldFrame(position, quat, shape.pillarOffset, worldPillarOffset); - this.intersectConvex(shape.pillarConvex, quat, worldPillarOffset, body, reportedShape, intersectConvexOptions); - - if(this.result._shouldStop){ - return; - } - - // Upper triangle - shape.getConvexTrianglePillar(i, j, true); - Transform.pointToWorldFrame(position, quat, shape.pillarOffset, worldPillarOffset); - this.intersectConvex(shape.pillarConvex, quat, worldPillarOffset, body, reportedShape, intersectConvexOptions); - } - } -}; -Ray.prototype[Shape.types.HEIGHTFIELD] = Ray.prototype.intersectHeightfield; - -var Ray_intersectSphere_intersectionPoint = new Vec3(); -var Ray_intersectSphere_normal = new Vec3(); - -/** - * @method intersectSphere - * @private - * @param {Shape} shape - * @param {Quaternion} quat - * @param {Vec3} position - * @param {Body} body - */ -Ray.prototype.intersectSphere = function(shape, quat, position, body, reportedShape){ - var from = this.from, - to = this.to, - r = shape.radius; - - var a = Math.pow(to.x - from.x, 2) + Math.pow(to.y - from.y, 2) + Math.pow(to.z - from.z, 2); - var b = 2 * ((to.x - from.x) * (from.x - position.x) + (to.y - from.y) * (from.y - position.y) + (to.z - from.z) * (from.z - position.z)); - var c = Math.pow(from.x - position.x, 2) + Math.pow(from.y - position.y, 2) + Math.pow(from.z - position.z, 2) - Math.pow(r, 2); - - var delta = Math.pow(b, 2) - 4 * a * c; - - var intersectionPoint = Ray_intersectSphere_intersectionPoint; - var normal = Ray_intersectSphere_normal; - - if(delta < 0){ - // No intersection - return; - - } else if(delta === 0){ - // single intersection point - from.lerp(to, delta, intersectionPoint); - - intersectionPoint.vsub(position, normal); - normal.normalize(); - - this.reportIntersection(normal, intersectionPoint, reportedShape, body, -1); - - } else { - var d1 = (- b - Math.sqrt(delta)) / (2 * a); - var d2 = (- b + Math.sqrt(delta)) / (2 * a); - - if(d1 >= 0 && d1 <= 1){ - from.lerp(to, d1, intersectionPoint); - intersectionPoint.vsub(position, normal); - normal.normalize(); - this.reportIntersection(normal, intersectionPoint, reportedShape, body, -1); - } - - if(this.result._shouldStop){ - return; - } - - if(d2 >= 0 && d2 <= 1){ - from.lerp(to, d2, intersectionPoint); - intersectionPoint.vsub(position, normal); - normal.normalize(); - this.reportIntersection(normal, intersectionPoint, reportedShape, body, -1); - } - } -}; -Ray.prototype[Shape.types.SPHERE] = Ray.prototype.intersectSphere; - - -var intersectConvex_normal = new Vec3(); -var intersectConvex_minDistNormal = new Vec3(); -var intersectConvex_minDistIntersect = new Vec3(); -var intersectConvex_vector = new Vec3(); - -/** - * @method intersectConvex - * @private - * @param {Shape} shape - * @param {Quaternion} quat - * @param {Vec3} position - * @param {Body} body - * @param {object} [options] - * @param {array} [options.faceList] - */ -Ray.prototype.intersectConvex = function intersectConvex( - shape, - quat, - position, - body, - reportedShape, - options -){ - var minDistNormal = intersectConvex_minDistNormal; - var normal = intersectConvex_normal; - var vector = intersectConvex_vector; - var minDistIntersect = intersectConvex_minDistIntersect; - var faceList = (options && options.faceList) || null; - - // Checking faces - var faces = shape.faces, - vertices = shape.vertices, - normals = shape.faceNormals; - var direction = this._direction; - - var from = this.from; - var to = this.to; - var fromToDistance = from.distanceTo(to); - - var minDist = -1; - var Nfaces = faceList ? faceList.length : faces.length; - var result = this.result; - - for (var j = 0; !result._shouldStop && j < Nfaces; j++) { - var fi = faceList ? faceList[j] : j; - - var face = faces[fi]; - var faceNormal = normals[fi]; - var q = quat; - var x = position; - - // determine if ray intersects the plane of the face - // note: this works regardless of the direction of the face normal - - // Get plane point in world coordinates... - vector.copy(vertices[face[0]]); - q.vmult(vector,vector); - vector.vadd(x,vector); - - // ...but make it relative to the ray from. We'll fix this later. - vector.vsub(from,vector); - - // Get plane normal - q.vmult(faceNormal,normal); - - // If this dot product is negative, we have something interesting - var dot = direction.dot(normal); - - // Bail out if ray and plane are parallel - if ( Math.abs( dot ) < this.precision ){ - continue; - } - - // calc distance to plane - var scalar = normal.dot(vector) / dot; - - // if negative distance, then plane is behind ray - if (scalar < 0){ - continue; - } - - // if (dot < 0) { - - // Intersection point is from + direction * scalar - direction.mult(scalar,intersectPoint); - intersectPoint.vadd(from,intersectPoint); - - // a is the point we compare points b and c with. - a.copy(vertices[face[0]]); - q.vmult(a,a); - x.vadd(a,a); - - for(var i = 1; !result._shouldStop && i < face.length - 1; i++){ - // Transform 3 vertices to world coords - b.copy(vertices[face[i]]); - c.copy(vertices[face[i+1]]); - q.vmult(b,b); - q.vmult(c,c); - x.vadd(b,b); - x.vadd(c,c); - - var distance = intersectPoint.distanceTo(from); - - if(!(pointInTriangle(intersectPoint, a, b, c) || pointInTriangle(intersectPoint, b, a, c)) || distance > fromToDistance){ - continue; - } - - this.reportIntersection(normal, intersectPoint, reportedShape, body, fi); - } - // } - } -}; -Ray.prototype[Shape.types.CONVEXPOLYHEDRON] = Ray.prototype.intersectConvex; - -var intersectTrimesh_normal = new Vec3(); -var intersectTrimesh_localDirection = new Vec3(); -var intersectTrimesh_localFrom = new Vec3(); -var intersectTrimesh_localTo = new Vec3(); -var intersectTrimesh_worldNormal = new Vec3(); -var intersectTrimesh_worldIntersectPoint = new Vec3(); -var intersectTrimesh_localAABB = new AABB(); -var intersectTrimesh_triangles = []; -var intersectTrimesh_treeTransform = new Transform(); - -/** - * @method intersectTrimesh - * @private - * @param {Shape} shape - * @param {Quaternion} quat - * @param {Vec3} position - * @param {Body} body - * @param {object} [options] - * @todo Optimize by transforming the world to local space first. - * @todo Use Octree lookup - */ -Ray.prototype.intersectTrimesh = function intersectTrimesh( - mesh, - quat, - position, - body, - reportedShape, - options -){ - var normal = intersectTrimesh_normal; - var triangles = intersectTrimesh_triangles; - var treeTransform = intersectTrimesh_treeTransform; - var minDistNormal = intersectConvex_minDistNormal; - var vector = intersectConvex_vector; - var minDistIntersect = intersectConvex_minDistIntersect; - var localAABB = intersectTrimesh_localAABB; - var localDirection = intersectTrimesh_localDirection; - var localFrom = intersectTrimesh_localFrom; - var localTo = intersectTrimesh_localTo; - var worldIntersectPoint = intersectTrimesh_worldIntersectPoint; - var worldNormal = intersectTrimesh_worldNormal; - var faceList = (options && options.faceList) || null; - - // Checking faces - var indices = mesh.indices, - vertices = mesh.vertices, - normals = mesh.faceNormals; - - var from = this.from; - var to = this.to; - var direction = this._direction; - - var minDist = -1; - treeTransform.position.copy(position); - treeTransform.quaternion.copy(quat); - - // Transform ray to local space! - Transform.vectorToLocalFrame(position, quat, direction, localDirection); - Transform.pointToLocalFrame(position, quat, from, localFrom); - Transform.pointToLocalFrame(position, quat, to, localTo); - - localTo.x *= mesh.scale.x; - localTo.y *= mesh.scale.y; - localTo.z *= mesh.scale.z; - localFrom.x *= mesh.scale.x; - localFrom.y *= mesh.scale.y; - localFrom.z *= mesh.scale.z; - - localTo.vsub(localFrom, localDirection); - localDirection.normalize(); - - var fromToDistanceSquared = localFrom.distanceSquared(localTo); - - mesh.tree.rayQuery(this, treeTransform, triangles); - - for (var i = 0, N = triangles.length; !this.result._shouldStop && i !== N; i++) { - var trianglesIndex = triangles[i]; - - mesh.getNormal(trianglesIndex, normal); - - // determine if ray intersects the plane of the face - // note: this works regardless of the direction of the face normal - - // Get plane point in world coordinates... - mesh.getVertex(indices[trianglesIndex * 3], a); - - // ...but make it relative to the ray from. We'll fix this later. - a.vsub(localFrom,vector); - - // If this dot product is negative, we have something interesting - var dot = localDirection.dot(normal); - - // Bail out if ray and plane are parallel - // if (Math.abs( dot ) < this.precision){ - // continue; - // } - - // calc distance to plane - var scalar = normal.dot(vector) / dot; - - // if negative distance, then plane is behind ray - if (scalar < 0){ - continue; - } - - // Intersection point is from + direction * scalar - localDirection.scale(scalar,intersectPoint); - intersectPoint.vadd(localFrom,intersectPoint); - - // Get triangle vertices - mesh.getVertex(indices[trianglesIndex * 3 + 1], b); - mesh.getVertex(indices[trianglesIndex * 3 + 2], c); - - var squaredDistance = intersectPoint.distanceSquared(localFrom); - - if(!(pointInTriangle(intersectPoint, b, a, c) || pointInTriangle(intersectPoint, a, b, c)) || squaredDistance > fromToDistanceSquared){ - continue; - } - - // transform intersectpoint and normal to world - Transform.vectorToWorldFrame(quat, normal, worldNormal); - Transform.pointToWorldFrame(position, quat, intersectPoint, worldIntersectPoint); - this.reportIntersection(worldNormal, worldIntersectPoint, reportedShape, body, trianglesIndex); - } - triangles.length = 0; -}; -Ray.prototype[Shape.types.TRIMESH] = Ray.prototype.intersectTrimesh; - - -/** - * @method reportIntersection - * @private - * @param {Vec3} normal - * @param {Vec3} hitPointWorld - * @param {Shape} shape - * @param {Body} body - * @return {boolean} True if the intersections should continue - */ -Ray.prototype.reportIntersection = function(normal, hitPointWorld, shape, body, hitFaceIndex){ - var from = this.from; - var to = this.to; - var distance = from.distanceTo(hitPointWorld); - var result = this.result; - - // Skip back faces? - if(this.skipBackfaces && normal.dot(this._direction) > 0){ - return; - } - - result.hitFaceIndex = typeof(hitFaceIndex) !== 'undefined' ? hitFaceIndex : -1; - - switch(this.mode){ - case Ray.ALL: - this.hasHit = true; - result.set( - from, - to, - normal, - hitPointWorld, - shape, - body, - distance - ); - result.hasHit = true; - this.callback(result); - break; - - case Ray.CLOSEST: - - // Store if closer than current closest - if(distance < result.distance || !result.hasHit){ - this.hasHit = true; - result.hasHit = true; - result.set( - from, - to, - normal, - hitPointWorld, - shape, - body, - distance - ); - } - break; - - case Ray.ANY: - - // Report and stop. - this.hasHit = true; - result.hasHit = true; - result.set( - from, - to, - normal, - hitPointWorld, - shape, - body, - distance - ); - result._shouldStop = true; - break; - } -}; - -var v0 = new Vec3(), - intersect = new Vec3(); -function distanceFromIntersection(from, direction, position) { - - // v0 is vector from from to position - position.vsub(from,v0); - var dot = v0.dot(direction); - - // intersect = direction*dot + from - direction.mult(dot,intersect); - intersect.vadd(from,intersect); - - var distance = position.distanceTo(intersect); - - return distance; -} - diff --git a/src/collision/Ray.ts b/src/collision/Ray.ts new file mode 100644 index 000000000..9d5114c3c --- /dev/null +++ b/src/collision/Ray.ts @@ -0,0 +1,820 @@ +import { Box3, Quaternion, Sphere, Triangle3, Vector3 } from 'feng3d'; +import { worldNormal } from '../common'; +import { Transform } from '../math/Transform'; +import { Body } from '../objects/Body'; +import { Heightfield } from '../shapes/Heightfield'; +import { Shape } from '../shapes/Shape'; +import { Trimesh } from '../shapes/Trimesh'; +import { World } from '../world/World'; +import { RaycastResult } from './RaycastResult'; + +export class Ray +{ + from: Vector3; + + to: Vector3; + + _direction: Vector3; + + /** + * The precision of the ray. Used when checking parallelity etc. + */ + precision: number; + + /** + * Set to true if you want the Ray to take .collisionResponse flags into account on bodies and shapes. + */ + checkCollisionResponse: boolean; + + /** + * If set to true, the ray skips any hits with normal.dot(rayDirection) < 0. + */ + skipBackfaces: boolean; + + collisionFilterMask: number; + + collisionFilterGroup: number; + + /** + * The intersection mode. Should be Ray.ANY, Ray.ALL or Ray.CLOSEST. + */ + mode: number; + + /** + * Current result object. + */ + result: RaycastResult; + + /** + * Will be set to true during intersectWorld() if the ray hit anything. + */ + hasHit: boolean; + + /** + * Current, user-provided result callback. Will be used if mode is Ray.ALL. + */ + callback: Function; + + /** + * A line in 3D space that intersects bodies and return points. + * @param from + * @param to + */ + constructor(from?: Vector3, to?: Vector3) + { + this.from = from ? from.clone() : new Vector3(); + this.to = to ? to.clone() : new Vector3(); + this._direction = new Vector3(); + this.precision = 0.0001; + this.checkCollisionResponse = true; + this.skipBackfaces = false; + this.collisionFilterMask = -1; + this.collisionFilterGroup = -1; + this.mode = Ray.ANY; + this.result = new RaycastResult(); + this.hasHit = false; + this.callback = function (_result) { }; + } + + static CLOSEST = 1; + static ANY = 2; + static ALL = 4; + + /** + * Do itersection against all bodies in the given World. + * @param world + * @param options + * @return True if the ray hit anything, otherwise false. + */ + intersectWorld(world: World, options: { + mode?: number, result?: RaycastResult, skipBackfaces?: boolean, collisionFilterMask?: number, + collisionFilterGroup?: number, from?: Vector3, to?: Vector3, callback?: Function + }) + { + this.mode = options.mode || Ray.ANY; + this.result = options.result || new RaycastResult(); + this.skipBackfaces = !!options.skipBackfaces; + this.collisionFilterMask = typeof (options.collisionFilterMask) !== 'undefined' ? options.collisionFilterMask : -1; + this.collisionFilterGroup = typeof (options.collisionFilterGroup) !== 'undefined' ? options.collisionFilterGroup : -1; + if (options.from) + { + this.from.copy(options.from); + } + if (options.to) + { + this.to.copy(options.to); + } + this.callback = options.callback || function () { }; + this.hasHit = false; + + this.result.reset(); + this._updateDirection(); + + this.getAABB(tmpAABB); + tmpArray.length = 0; + world.broadphase.aabbQuery(world, tmpAABB, tmpArray); + this.intersectBodies(tmpArray); + + return this.hasHit; + } + + /** + * Shoot a ray at a body, get back information about the hit. + * @param body + * @param result Deprecated - set the result property of the Ray instead. + */ + intersectBody(body: Body, result?: RaycastResult) + { + if (result) + { + this.result = result; + this._updateDirection(); + } + const checkCollisionResponse = this.checkCollisionResponse; + + if (checkCollisionResponse && !body.collisionResponse) + { + return; + } + + if ((this.collisionFilterGroup & body.collisionFilterMask) === 0 || (body.collisionFilterGroup & this.collisionFilterMask) === 0) + { + return; + } + + const xi = intersectBodyXi; + const qi = intersectBodyQi; + + for (let i = 0, N = body.shapes.length; i < N; i++) + { + const shape = body.shapes[i]; + + if (checkCollisionResponse && !shape.collisionResponse) + { + continue; // Skip + } + + body.quaternion.multTo(body.shapeOrientations[i], qi); + body.quaternion.vmult(body.shapeOffsets[i], xi); + xi.addTo(body.position, xi); + + this.intersectShape( + shape, + qi, + xi, + body + ); + + if (this.result._shouldStop) + { + break; + } + } + } + + /** + * @param bodies An array of Body objects. + * @param result Deprecated + */ + intersectBodies(bodies: Body[], result?: RaycastResult) + { + if (result) + { + this.result = result; + this._updateDirection(); + } + + for (let i = 0, l = bodies.length; !this.result._shouldStop && i < l; i++) + { + this.intersectBody(bodies[i]); + } + } + + /** + * Updates the _direction vector. + */ + private _updateDirection() + { + this.to.subTo(this.from, this._direction); + this._direction.normalize(); + } + + private intersectShape(shape: Shape, quat: Quaternion, position: Vector3, body: Body) + { + const from = this.from; + + // Checking boundingSphere + const distance = distanceFromIntersection(from, this._direction, position); + if (distance > shape.boundingSphereRadius) + { + return; + } + + const intersectMethod = this[shape.type]; + if (intersectMethod) + { + intersectMethod.call(this, shape, quat, position, body, shape); + } + } + + private intersectBox(shape: Shape, quat: Quaternion, position: Vector3, body: Body, reportedShape: Shape) + { + return this.intersectConvex(shape.convexPolyhedronRepresentation, quat, position, body, reportedShape); + } + + private intersectPlane(_shape: Shape, quat: Quaternion, position: Vector3, body: Body, reportedShape: Shape) + { + const from = this.from; + const to = this.to; + const direction = this._direction; + + // Get plane normal + const worldNormal1 = worldNormal.clone(); + quat.vmult(worldNormal1, worldNormal1); + + const len = new Vector3(); + from.subTo(position, len); + const planeToFrom = len.dot(worldNormal1); + to.subTo(position, len); + const planeToTo = len.dot(worldNormal1); + + if (planeToFrom * planeToTo > 0) + { + // "from" and "to" are on the same side of the plane... bail out + return; + } + + if (from.distance(to) < planeToFrom) + { + return; + } + + const nDotDir = worldNormal1.dot(direction); + + if (Math.abs(nDotDir) < this.precision) + { + // No intersection + return; + } + + const planePointToFrom = new Vector3(); + const dirScaledWithT = new Vector3(); + const hitPointWorld = new Vector3(); + + from.subTo(position, planePointToFrom); + const t = -worldNormal1.dot(planePointToFrom) / nDotDir; + direction.scaleNumberTo(t, dirScaledWithT); + from.addTo(dirScaledWithT, hitPointWorld); + + this.reportIntersection(worldNormal1, hitPointWorld, reportedShape, body, -1); + } + + /** + * Get the world AABB of the ray. + */ + getAABB(result: Box3) + { + const to = this.to; + const from = this.from; + result.min.x = Math.min(to.x, from.x); + result.min.y = Math.min(to.y, from.y); + result.min.z = Math.min(to.z, from.z); + result.max.x = Math.max(to.x, from.x); + result.max.y = Math.max(to.y, from.y); + result.max.z = Math.max(to.z, from.z); + } + + private intersectHeightfield(shape: Heightfield, quat: Quaternion, position: Vector3, body: Body, reportedShape: Shape) + { + // const data = shape.data; + // const w = shape.elementSize; + + // Convert the ray to local heightfield coordinates + const localRay = intersectHeightfieldLocalRay; // new Ray(this.from, this.to); + localRay.from.copy(this.from); + localRay.to.copy(this.to); + Transform.pointToLocalFrame(position, quat, localRay.from, localRay.from); + Transform.pointToLocalFrame(position, quat, localRay.to, localRay.to); + localRay._updateDirection(); + + // Get the index of the data points to test against + const index = intersectHeightfieldIndex; + let iMinX; let iMinY; let iMaxX; let + iMaxY; + + // Set to max + iMinX = iMinY = 0; + iMaxX = iMaxY = shape.data.length - 1; + + const aabb = new Box3(); + localRay.getAABB(aabb); + + shape.getIndexOfPosition(aabb.min.x, aabb.min.y, index, true); + iMinX = Math.max(iMinX, index[0]); + iMinY = Math.max(iMinY, index[1]); + shape.getIndexOfPosition(aabb.max.x, aabb.max.y, index, true); + iMaxX = Math.min(iMaxX, index[0] + 1); + iMaxY = Math.min(iMaxY, index[1] + 1); + + for (let i = iMinX; i < iMaxX; i++) + { + for (let j = iMinY; j < iMaxY; j++) + { + if (this.result._shouldStop) + { + return; + } + + shape.getAabbAtIndex(i, j, aabb); + if (!localRay.overlapsBox3(aabb)) + { + continue; + } + + // Lower triangle + shape.getConvexTrianglePillar(i, j, false); + Transform.pointToWorldFrame(position, quat, shape.pillarOffset, worldPillarOffset); + this.intersectConvex(shape.pillarConvex, quat, worldPillarOffset, body, reportedShape, intersectConvexOptions); + + if (this.result._shouldStop) + { + return; + } + + // Upper triangle + shape.getConvexTrianglePillar(i, j, true); + Transform.pointToWorldFrame(position, quat, shape.pillarOffset, worldPillarOffset); + this.intersectConvex(shape.pillarConvex, quat, worldPillarOffset, body, reportedShape, intersectConvexOptions); + } + } + } + + private intersectSphere(shape: Sphere, _quat: Quaternion, position: Vector3, body: Body, reportedShape: Shape) + { + const from = this.from; + const to = this.to; + const r = shape.radius; + + const a = Math.pow(to.x - from.x, 2) + Math.pow(to.y - from.y, 2) + Math.pow(to.z - from.z, 2); + const b = 2 * ((to.x - from.x) * (from.x - position.x) + (to.y - from.y) * (from.y - position.y) + (to.z - from.z) * (from.z - position.z)); + const c = Math.pow(from.x - position.x, 2) + Math.pow(from.y - position.y, 2) + Math.pow(from.z - position.z, 2) - Math.pow(r, 2); + + const delta = Math.pow(b, 2) - 4 * a * c; + + const intersectionPoint = RayIntersectSphereIntersectionPoint; + const normal = RayIntersectSphereNormal; + + if (delta < 0) + { + // No intersection + return; + } + else if (delta === 0) + { + // single intersection point + from.lerpNumberTo(to, delta, intersectionPoint); + + intersectionPoint.subTo(position, normal); + normal.normalize(); + + this.reportIntersection(normal, intersectionPoint, reportedShape, body, -1); + } + else + { + const d1 = (-b - Math.sqrt(delta)) / (2 * a); + const d2 = (-b + Math.sqrt(delta)) / (2 * a); + + if (d1 >= 0 && d1 <= 1) + { + from.lerpNumberTo(to, d1, intersectionPoint); + intersectionPoint.subTo(position, normal); + normal.normalize(); + this.reportIntersection(normal, intersectionPoint, reportedShape, body, -1); + } + + if (this.result._shouldStop) + { + return; + } + + if (d2 >= 0 && d2 <= 1) + { + from.lerpNumberTo(to, d2, intersectionPoint); + intersectionPoint.subTo(position, normal); + normal.normalize(); + this.reportIntersection(normal, intersectionPoint, reportedShape, body, -1); + } + } + } + + private intersectConvex( + shape: Shape, + quat: Quaternion, + position: Vector3, + body: Body, + reportedShape: Shape, + options: { faceList?: number[] } = {} + ) + { + // const minDistNormal = intersectConvexMinDistNormal; + const normal = intersectConvexNormal; + const vector = intersectConvexVector; + // const minDistIntersect = intersectConvexMinDistIntersect; + const faceList = (options && options.faceList) || null; + + // Checking faces + const faces = shape.faces; + const vertices = shape.vertices; + const normals = shape.faceNormals; + const direction = this._direction; + + const from = this.from; + const to = this.to; + const fromToDistance = from.distance(to); + + // const minDist = -1; + const Nfaces = faceList ? faceList.length : faces.length; + const result = this.result; + + for (let j = 0; !result._shouldStop && j < Nfaces; j++) + { + const fi = faceList ? faceList[j] : j; + + const face = faces[fi]; + const faceNormal = normals[fi]; + const q = quat; + const x = position; + + // determine if ray intersects the plane of the face + // note: this works regardless of the direction of the face normal + + // Get plane point in world coordinates... + vector.copy(vertices[face[0]]); + q.vmult(vector, vector); + vector.addTo(x, vector); + + // ...but make it relative to the ray from. We'll fix this later. + vector.subTo(from, vector); + + // Get plane normal + q.vmult(faceNormal, normal); + + // If this dot product is negative, we have something interesting + const dot = direction.dot(normal); + + // Bail out if ray and plane are parallel + if (Math.abs(dot) < this.precision) + { + continue; + } + + // calc distance to plane + const scalar = normal.dot(vector) / dot; + + // if negative distance, then plane is behind ray + if (scalar < 0) + { + continue; + } + + // if (dot < 0) { + + // Intersection point is from + direction * scalar + direction.scaleNumberTo(scalar, intersectPoint); + intersectPoint.addTo(from, intersectPoint); + + // a is the point we compare points b and c with. + a.copy(vertices[face[0]]); + q.vmult(a, a); + x.addTo(a, a); + + for (let i = 1; !result._shouldStop && i < face.length - 1; i++) + { + // Transform 3 vertices to world coords + b.copy(vertices[face[i]]); + c.copy(vertices[face[i + 1]]); + q.vmult(b, b); + q.vmult(c, c); + x.addTo(b, b); + x.addTo(c, c); + + const distance = intersectPoint.distance(from); + + if (!(Triangle3.containsPoint(a, b, c, intersectPoint) || Triangle3.containsPoint(b, a, c, intersectPoint)) || distance > fromToDistance) + { + continue; + } + + this.reportIntersection(normal, intersectPoint, reportedShape, body, fi); + } + // } + } + } + + /** + * @method intersectTrimesh + * @private + * @param {Shape} shape + * @param {Quaternion} quat + * @param {Vector3} position + * @param {Body} body + * @param {object} [options] + */ + /** + * + * @param mesh + * @param quat + * @param position + * @param body + * @param reportedShape + * @param _options + * + * @todo Optimize by transforming the world to local space first. + * @todo Use Octree lookup + */ + private intersectTrimesh( + mesh: Trimesh, + quat: Quaternion, + position: Vector3, + body: Body, + reportedShape: Shape, + _options: { faceList: number[] } + ) + { + const normal = intersectTrimeshNormal; + const triangles = intersectTrimeshTriangles; + const treeTransform = intersectTrimeshTreeTransform; + // const minDistNormal = intersectConvex_minDistNormal; + const vector = intersectConvexVector; + // const minDistIntersect = intersectConvex_minDistIntersect; + // const localAABB = intersectTrimesh_localAABB; + const localDirection = intersectTrimeshLocalDirection; + const localFrom = intersectTrimeshLocalFrom; + const localTo = intersectTrimeshLocalTo; + const worldIntersectPoint = intersectTrimeshWorldIntersectPoint; + const worldNormal = intersectTrimeshWorldNormal; + // const faceList = (options && options.faceList) || null; + + // Checking faces + const indices = mesh.indices; + // const vertices = mesh.vertices; + // const normals = mesh.faceNormals; + + const from = this.from; + const to = this.to; + const direction = this._direction; + + // const minDist = -1; + treeTransform.position.copy(position); + treeTransform.quaternion.copy(quat); + + // Transform ray to local space! + Transform.vectorToLocalFrame(position, quat, direction, localDirection); + Transform.pointToLocalFrame(position, quat, from, localFrom); + Transform.pointToLocalFrame(position, quat, to, localTo); + + localTo.x *= mesh.scale.x; + localTo.y *= mesh.scale.y; + localTo.z *= mesh.scale.z; + localFrom.x *= mesh.scale.x; + localFrom.y *= mesh.scale.y; + localFrom.z *= mesh.scale.z; + + localTo.subTo(localFrom, localDirection); + localDirection.normalize(); + + const fromToDistanceSquared = localFrom.distanceSquared(localTo); + + mesh.tree.rayQuery(this, treeTransform, triangles); + + for (let i = 0, N = triangles.length; !this.result._shouldStop && i !== N; i++) + { + const trianglesIndex = triangles[i]; + + mesh.getNormal(trianglesIndex, normal); + + // determine if ray intersects the plane of the face + // note: this works regardless of the direction of the face normal + + // Get plane point in world coordinates... + mesh.getVertex(indices[trianglesIndex * 3], a); + + // ...but make it relative to the ray from. We'll fix this later. + a.subTo(localFrom, vector); + + // If this dot product is negative, we have something interesting + const dot = localDirection.dot(normal); + + // Bail out if ray and plane are parallel + // if (Math.abs( dot ) < this.precision){ + // continue; + // } + + // calc distance to plane + const scalar = normal.dot(vector) / dot; + + // if negative distance, then plane is behind ray + if (scalar < 0) + { + continue; + } + + // Intersection point is from + direction * scalar + localDirection.scaleNumberTo(scalar, intersectPoint); + intersectPoint.addTo(localFrom, intersectPoint); + + // Get triangle vertices + mesh.getVertex(indices[trianglesIndex * 3 + 1], b); + mesh.getVertex(indices[trianglesIndex * 3 + 2], c); + + const squaredDistance = intersectPoint.distanceSquared(localFrom); + + if (!(Triangle3.containsPoint(b, a, c, intersectPoint) || Triangle3.containsPoint(a, b, c, intersectPoint)) || squaredDistance > fromToDistanceSquared) + { + continue; + } + + // transform intersectpoint and normal to world + Transform.vectorToWorldFrame(quat, normal, worldNormal); + Transform.pointToWorldFrame(position, quat, intersectPoint, worldIntersectPoint); + this.reportIntersection(worldNormal, worldIntersectPoint, reportedShape, body, trianglesIndex); + } + triangles.length = 0; + } + + private reportIntersection(normal: Vector3, hitPointWorld: Vector3, shape: Shape, body: Body, hitFaceIndex?: number) + { + const from = this.from; + const to = this.to; + const distance = from.distance(hitPointWorld); + const result = this.result; + + // Skip back faces? + if (this.skipBackfaces && normal.dot(this._direction) > 0) + { + return; + } + + result.hitFaceIndex = typeof (hitFaceIndex) !== 'undefined' ? hitFaceIndex : -1; + + switch (this.mode) + { + case Ray.ALL: + this.hasHit = true; + result.set( + from, + to, + normal, + hitPointWorld, + shape, + body, + distance + ); + result.hasHit = true; + this.callback(result); + break; + + case Ray.CLOSEST: + + // Store if closer than current closest + if (distance < result.distance || !result.hasHit) + { + this.hasHit = true; + result.hasHit = true; + result.set( + from, + to, + normal, + hitPointWorld, + shape, + body, + distance + ); + } + break; + + case Ray.ANY: + + // Report and stop. + this.hasHit = true; + result.hasHit = true; + result.set( + from, + to, + normal, + hitPointWorld, + shape, + body, + distance + ); + result._shouldStop = true; + break; + } + } + + /** + * Check if the AABB is hit by a ray. + */ + overlapsBox3(box3: Box3) + { + // const t = 0; + + // ray.direction is unit direction vector of ray + const dirFracX = 1 / this._direction.x; + const dirFracY = 1 / this._direction.y; + const dirFracZ = 1 / this._direction.z; + + // this.lowerBound is the corner of AABB with minimal coordinates - left bottom, rt is maximal corner + const t1 = (box3.min.x - this.from.x) * dirFracX; + const t2 = (box3.max.x - this.from.x) * dirFracX; + const t3 = (box3.min.y - this.from.y) * dirFracY; + const t4 = (box3.max.y - this.from.y) * dirFracY; + const t5 = (box3.min.z - this.from.z) * dirFracZ; + const t6 = (box3.max.z - this.from.z) * dirFracZ; + + // let tmin = Math.max(Math.max(Math.min(t1, t2), Math.min(t3, t4))); + // let tmax = Math.min(Math.min(Math.max(t1, t2), Math.max(t3, t4))); + const tmin = Math.max(Math.max(Math.min(t1, t2), Math.min(t3, t4)), Math.min(t5, t6)); + const tmax = Math.min(Math.min(Math.max(t1, t2), Math.max(t3, t4)), Math.max(t5, t6)); + + // if tmax < 0, ray (line) is intersecting AABB, but whole AABB is behing us + if (tmax < 0) + { + // t = tmax; + return false; + } + + // if tmin > tmax, ray doesn't intersect AABB + if (tmin > tmax) + { + // t = tmax; + return false; + } + + return true; + } +} + +const tmpAABB = new Box3(); +const tmpArray = []; + +const intersectBodyXi = new Vector3(); +const intersectBodyQi = new Quaternion(); + +const intersectPoint = new Vector3(); + +const a = new Vector3(); +const b = new Vector3(); +const c = new Vector3(); + +const v0 = new Vector3(); +const intersect = new Vector3(); + +const intersectTrimeshNormal = new Vector3(); +const intersectTrimeshLocalDirection = new Vector3(); +const intersectTrimeshLocalFrom = new Vector3(); +const intersectTrimeshLocalTo = new Vector3(); +const intersectTrimeshWorldNormal = new Vector3(); +const intersectTrimeshWorldIntersectPoint = new Vector3(); +// const intersectTrimesh_localAABB = new Box3(); +const intersectTrimeshTriangles: number[] = []; +const intersectTrimeshTreeTransform = new Transform(); + +const intersectConvexOptions = { + faceList: [0] +}; +const worldPillarOffset = new Vector3(); +const intersectHeightfieldLocalRay = new Ray(); +const intersectHeightfieldIndex = []; + +const RayIntersectSphereIntersectionPoint = new Vector3(); +const RayIntersectSphereNormal = new Vector3(); + +const intersectConvexNormal = new Vector3(); +// const intersectConvexMinDistNormal = new Vector3(); +// const intersectConvexMinDistIntersect = new Vector3(); +const intersectConvexVector = new Vector3(); + +Ray.prototype[Shape.types.BOX] = Ray.prototype['intersectBox']; +Ray.prototype[Shape.types.PLANE] = Ray.prototype['intersectPlane']; + +Ray.prototype[Shape.types.HEIGHTFIELD] = Ray.prototype['intersectHeightfield']; +Ray.prototype[Shape.types.SPHERE] = Ray.prototype['intersectSphere']; + +Ray.prototype[Shape.types.TRIMESH] = Ray.prototype['intersectTrimesh']; +Ray.prototype[Shape.types.CONVEXPOLYHEDRON] = Ray.prototype['intersectConvex']; + +function distanceFromIntersection(from: Vector3, direction: Vector3, position: Vector3) +{ + // v0 is vector from from to position + position.subTo(from, v0); + const dot = v0.dot(direction); + + // intersect = direction*dot + from + direction.scaleNumberTo(dot, intersect); + intersect.addTo(from, intersect); + + const distance = position.distance(intersect); + + return distance; +} diff --git a/src/collision/RaycastResult.js b/src/collision/RaycastResult.js deleted file mode 100644 index 553feda33..000000000 --- a/src/collision/RaycastResult.js +++ /dev/null @@ -1,122 +0,0 @@ -var Vec3 = require('../math/Vec3'); - -module.exports = RaycastResult; - -/** - * Storage for Ray casting data. - * @class RaycastResult - * @constructor - */ -function RaycastResult(){ - - /** - * @property {Vec3} rayFromWorld - */ - this.rayFromWorld = new Vec3(); - - /** - * @property {Vec3} rayToWorld - */ - this.rayToWorld = new Vec3(); - - /** - * @property {Vec3} hitNormalWorld - */ - this.hitNormalWorld = new Vec3(); - - /** - * @property {Vec3} hitPointWorld - */ - this.hitPointWorld = new Vec3(); - - /** - * @property {boolean} hasHit - */ - this.hasHit = false; - - /** - * The hit shape, or null. - * @property {Shape} shape - */ - this.shape = null; - - /** - * The hit body, or null. - * @property {Body} body - */ - this.body = null; - - /** - * The index of the hit triangle, if the hit shape was a trimesh. - * @property {number} hitFaceIndex - * @default -1 - */ - this.hitFaceIndex = -1; - - /** - * Distance to the hit. Will be set to -1 if there was no hit. - * @property {number} distance - * @default -1 - */ - this.distance = -1; - - /** - * If the ray should stop traversing the bodies. - * @private - * @property {Boolean} _shouldStop - * @default false - */ - this._shouldStop = false; -} - -/** - * Reset all result data. - * @method reset - */ -RaycastResult.prototype.reset = function () { - this.rayFromWorld.setZero(); - this.rayToWorld.setZero(); - this.hitNormalWorld.setZero(); - this.hitPointWorld.setZero(); - this.hasHit = false; - this.shape = null; - this.body = null; - this.hitFaceIndex = -1; - this.distance = -1; - this._shouldStop = false; -}; - -/** - * @method abort - */ -RaycastResult.prototype.abort = function(){ - this._shouldStop = true; -}; - -/** - * @method set - * @param {Vec3} rayFromWorld - * @param {Vec3} rayToWorld - * @param {Vec3} hitNormalWorld - * @param {Vec3} hitPointWorld - * @param {Shape} shape - * @param {Body} body - * @param {number} distance - */ -RaycastResult.prototype.set = function( - rayFromWorld, - rayToWorld, - hitNormalWorld, - hitPointWorld, - shape, - body, - distance -){ - this.rayFromWorld.copy(rayFromWorld); - this.rayToWorld.copy(rayToWorld); - this.hitNormalWorld.copy(hitNormalWorld); - this.hitPointWorld.copy(hitPointWorld); - this.shape = shape; - this.body = body; - this.distance = distance; -}; \ No newline at end of file diff --git a/src/collision/RaycastResult.ts b/src/collision/RaycastResult.ts new file mode 100644 index 000000000..986f6440a --- /dev/null +++ b/src/collision/RaycastResult.ts @@ -0,0 +1,88 @@ +import { Vector3 } from 'feng3d'; +import { Body } from '../objects/Body'; +import { Shape } from '../shapes/Shape'; + +export class RaycastResult +{ + rayFromWorld = new Vector3(); + + rayToWorld = new Vector3(); + + hitNormalWorld = new Vector3(); + + hitPointWorld = new Vector3(); + + hasHit = false; + + shape: Shape = null; + + body: Body = null; + + /** + * The index of the hit triangle, if the hit shape was a trimesh. + */ + hitFaceIndex = -1; + + /** + * Distance to the hit. Will be set to -1 if there was no hit. + */ + distance = -1; + + suspensionLength: number; + directionWorld: Vector3; + + /** + * If the ray should stop traversing the bodies. + */ + _shouldStop = false; + groundObject: number; + + /** + * Storage for Ray casting data. + */ + constructor() + { + + } + + /** + * Reset all result data. + */ + reset() + { + this.rayFromWorld.setZero(); + this.rayToWorld.setZero(); + this.hitNormalWorld.setZero(); + this.hitPointWorld.setZero(); + this.hasHit = false; + this.shape = null; + this.body = null; + this.hitFaceIndex = -1; + this.distance = -1; + this._shouldStop = false; + } + + abort() + { + this._shouldStop = true; + } + + set( + rayFromWorld: Vector3, + rayToWorld: Vector3, + hitNormalWorld: Vector3, + hitPointWorld: Vector3, + shape: Shape, + body: Body, + distance: number + ) + { + this.rayFromWorld.copy(rayFromWorld); + this.rayToWorld.copy(rayToWorld); + this.hitNormalWorld.copy(hitNormalWorld); + this.hitPointWorld.copy(hitPointWorld); + this.shape = shape; + this.body = body; + this.distance = distance; + } +} diff --git a/src/collision/SAPBroadphase.js b/src/collision/SAPBroadphase.js deleted file mode 100644 index ee93bda37..000000000 --- a/src/collision/SAPBroadphase.js +++ /dev/null @@ -1,323 +0,0 @@ -var Shape = require('../shapes/Shape'); -var Broadphase = require('../collision/Broadphase'); - -module.exports = SAPBroadphase; - -/** - * Sweep and prune broadphase along one axis. - * - * @class SAPBroadphase - * @constructor - * @param {World} [world] - * @extends Broadphase - */ -function SAPBroadphase(world){ - Broadphase.apply(this); - - /** - * List of bodies currently in the broadphase. - * @property axisList - * @type {Array} - */ - this.axisList = []; - - /** - * The world to search in. - * @property world - * @type {World} - */ - this.world = null; - - /** - * Axis to sort the bodies along. Set to 0 for x axis, and 1 for y axis. For best performance, choose an axis that the bodies are spread out more on. - * @property axisIndex - * @type {Number} - */ - this.axisIndex = 0; - - var axisList = this.axisList; - - this._addBodyHandler = function(e){ - axisList.push(e.body); - }; - - this._removeBodyHandler = function(e){ - var idx = axisList.indexOf(e.body); - if(idx !== -1){ - axisList.splice(idx,1); - } - }; - - if(world){ - this.setWorld(world); - } -} -SAPBroadphase.prototype = new Broadphase(); - -/** - * Change the world - * @method setWorld - * @param {World} world - */ -SAPBroadphase.prototype.setWorld = function(world){ - // Clear the old axis array - this.axisList.length = 0; - - // Add all bodies from the new world - for(var i=0; i=0;j--) { - if(a[j].aabb.lowerBound.x <= v.aabb.lowerBound.x){ - break; - } - a[j+1] = a[j]; - } - a[j+1] = v; - } - return a; -}; - -/** - * @static - * @method insertionSortY - * @param {Array} a - * @return {Array} - */ -SAPBroadphase.insertionSortY = function(a) { - for(var i=1,l=a.length;i=0;j--) { - if(a[j].aabb.lowerBound.y <= v.aabb.lowerBound.y){ - break; - } - a[j+1] = a[j]; - } - a[j+1] = v; - } - return a; -}; - -/** - * @static - * @method insertionSortZ - * @param {Array} a - * @return {Array} - */ -SAPBroadphase.insertionSortZ = function(a) { - for(var i=1,l=a.length;i=0;j--) { - if(a[j].aabb.lowerBound.z <= v.aabb.lowerBound.z){ - break; - } - a[j+1] = a[j]; - } - a[j+1] = v; - } - return a; -}; - -/** - * Collect all collision pairs - * @method collisionPairs - * @param {World} world - * @param {Array} p1 - * @param {Array} p2 - */ -SAPBroadphase.prototype.collisionPairs = function(world,p1,p2){ - var bodies = this.axisList, - N = bodies.length, - axisIndex = this.axisIndex, - i, j; - - if(this.dirty){ - this.sortList(); - this.dirty = false; - } - - // Look through the list - for(i=0; i !== N; i++){ - var bi = bodies[i]; - - for(j=i+1; j < N; j++){ - var bj = bodies[j]; - - if(!this.needBroadphaseCollision(bi,bj)){ - continue; - } - - if(!SAPBroadphase.checkBounds(bi,bj,axisIndex)){ - break; - } - - this.intersectionTest(bi,bj,p1,p2); - } - } -}; - -SAPBroadphase.prototype.sortList = function(){ - var axisList = this.axisList; - var axisIndex = this.axisIndex; - var N = axisList.length; - - // Update AABBs - for(var i = 0; i!==N; i++){ - var bi = axisList[i]; - if(bi.aabbNeedsUpdate){ - bi.computeAABB(); - } - } - - // Sort the list - if(axisIndex === 0){ - SAPBroadphase.insertionSortX(axisList); - } else if(axisIndex === 1){ - SAPBroadphase.insertionSortY(axisList); - } else if(axisIndex === 2){ - SAPBroadphase.insertionSortZ(axisList); - } -}; - -/** - * Check if the bounds of two bodies overlap, along the given SAP axis. - * @static - * @method checkBounds - * @param {Body} bi - * @param {Body} bj - * @param {Number} axisIndex - * @return {Boolean} - */ -SAPBroadphase.checkBounds = function(bi, bj, axisIndex){ - var biPos; - var bjPos; - - if(axisIndex === 0){ - biPos = bi.position.x; - bjPos = bj.position.x; - } else if(axisIndex === 1){ - biPos = bi.position.y; - bjPos = bj.position.y; - } else if(axisIndex === 2){ - biPos = bi.position.z; - bjPos = bj.position.z; - } - - var ri = bi.boundingRadius, - rj = bj.boundingRadius, - boundA1 = biPos - ri, - boundA2 = biPos + ri, - boundB1 = bjPos - rj, - boundB2 = bjPos + rj; - - return boundB1 < boundA2; -}; - -/** - * Computes the variance of the body positions and estimates the best - * axis to use. Will automatically set property .axisIndex. - * @method autoDetectAxis - */ -SAPBroadphase.prototype.autoDetectAxis = function(){ - var sumX=0, - sumX2=0, - sumY=0, - sumY2=0, - sumZ=0, - sumZ2=0, - bodies = this.axisList, - N = bodies.length, - invN=1/N; - - for(var i=0; i!==N; i++){ - var b = bodies[i]; - - var centerX = b.position.x; - sumX += centerX; - sumX2 += centerX*centerX; - - var centerY = b.position.y; - sumY += centerY; - sumY2 += centerY*centerY; - - var centerZ = b.position.z; - sumZ += centerZ; - sumZ2 += centerZ*centerZ; - } - - var varianceX = sumX2 - sumX*sumX*invN, - varianceY = sumY2 - sumY*sumY*invN, - varianceZ = sumZ2 - sumZ*sumZ*invN; - - if(varianceX > varianceY){ - if(varianceX > varianceZ){ - this.axisIndex = 0; - } else{ - this.axisIndex = 2; - } - } else if(varianceY > varianceZ){ - this.axisIndex = 1; - } else{ - this.axisIndex = 2; - } -}; - -/** - * Returns all the bodies within an AABB. - * @method aabbQuery - * @param {World} world - * @param {AABB} aabb - * @param {array} result An array to store resulting bodies in. - * @return {array} - */ -SAPBroadphase.prototype.aabbQuery = function(world, aabb, result){ - result = result || []; - - if(this.dirty){ - this.sortList(); - this.dirty = false; - } - - var axisIndex = this.axisIndex, axis = 'x'; - if(axisIndex === 1){ axis = 'y'; } - if(axisIndex === 2){ axis = 'z'; } - - var axisList = this.axisList; - var lower = aabb.lowerBound[axis]; - var upper = aabb.upperBound[axis]; - for(var i = 0; i < axisList.length; i++){ - var b = axisList[i]; - - if(b.aabbNeedsUpdate){ - b.computeAABB(); - } - - if(b.aabb.overlaps(aabb)){ - result.push(b); - } - } - - return result; -}; \ No newline at end of file diff --git a/src/collision/SAPBroadphase.ts b/src/collision/SAPBroadphase.ts new file mode 100644 index 000000000..df3941bd9 --- /dev/null +++ b/src/collision/SAPBroadphase.ts @@ -0,0 +1,359 @@ +import { IEvent } from 'feng3d'; +import { Box3 } from 'feng3d'; +import { Body } from '../objects/Body'; +import { World } from '../world/World'; +import { Broadphase } from './Broadphase'; + +export class SAPBroadphase extends Broadphase +{ + /** + * List of bodies currently in the broadphase. + */ + axisList: Body[]; + /** + * Axis to sort the bodies along. Set to 0 for x axis, and 1 for y axis. For best performance, choose an axis that the bodies are spread out more on. + */ + axisIndex: number; + + private _addBodyHandler(event: IEvent) + { + this.axisList.push(event.data); + } + private _removeBodyHandler(event: IEvent) + { + const idx = this.axisList.indexOf(event.data); + if (idx !== -1) + { + this.axisList.splice(idx, 1); + } + } + + /** + * Sweep and prune broadphase along one axis. + * + * @param world + */ + constructor(world: World) + { + super(); + + this.axisList = []; + + this.world = null; + + this.axisIndex = 0; + + // const axisList = this.axisList; + + if (world) + { + this.setWorld(world); + } + } + + /** + * Change the world + * @param world + */ + setWorld(world: World) + { + // Clear the old axis array + this.axisList.length = 0; + + // Add all bodies from the new world + for (let i = 0; i < world.bodies.length; i++) + { + this.axisList.push(world.bodies[i]); + } + + // Remove old handlers, if any + if (this.world) + { + this.world.off('addBody', this._addBodyHandler, this); + this.world.off('removeBody', this._removeBodyHandler, this); + } + + this.world = world; + + // Add handlers to update the list of bodies. + if (this.world) + { + this.world.on('addBody', this._addBodyHandler, this); + this.world.on('removeBody', this._removeBodyHandler, this); + } + + this.dirty = true; + } + + static insertionSortX(a: Body[]) + { + for (let i = 1, l = a.length; i < l; i++) + { + const v = a[i]; + let j = i - 1; + for (; j >= 0; j--) + { + if (a[j].aabb.min.x <= v.aabb.min.x) + { + break; + } + a[j + 1] = a[j]; + } + a[j + 1] = v; + } + + return a; + } + + static insertionSortY(a: Body[]) + { + for (let i = 1, l = a.length; i < l; i++) + { + const v = a[i]; + let j = i - 1; + for (; j >= 0; j--) + { + if (a[j].aabb.min.y <= v.aabb.min.y) + { + break; + } + a[j + 1] = a[j]; + } + a[j + 1] = v; + } + + return a; + } + + static insertionSortZ(a: Body[]) + { + for (let i = 1, l = a.length; i < l; i++) + { + const v = a[i]; + let j = i - 1; + for (; j >= 0; j--) + { + if (a[j].aabb.min.z <= v.aabb.min.z) + { + break; + } + a[j + 1] = a[j]; + } + a[j + 1] = v; + } + + return a; + } + + /** + * Collect all collision pairs + * @param _world + * @param p1 + * @param p2 + */ + collisionPairs(_world: World, p1: Body[], p2: Body[]) + { + const bodies = this.axisList; + const N = bodies.length; + const axisIndex = this.axisIndex; + let i: number; let + j: number; + + if (this.dirty) + { + this.sortList(); + this.dirty = false; + } + + // Look through the list + for (i = 0; i !== N; i++) + { + const bi = bodies[i]; + + for (j = i + 1; j < N; j++) + { + const bj = bodies[j]; + + if (!this.needBroadphaseCollision(bi, bj)) + { + continue; + } + + if (!SAPBroadphase.checkBounds(bi, bj, axisIndex)) + { + break; + } + + this.intersectionTest(bi, bj, p1, p2); + } + } + } + + sortList() + { + const axisList = this.axisList; + const axisIndex = this.axisIndex; + const N = axisList.length; + + // Update AABBs + for (let i = 0; i !== N; i++) + { + const bi = axisList[i]; + if (bi.aabbNeedsUpdate) + { + bi.computeAABB(); + } + } + + // Sort the list + if (axisIndex === 0) + { + SAPBroadphase.insertionSortX(axisList); + } + else if (axisIndex === 1) + { + SAPBroadphase.insertionSortY(axisList); + } + else if (axisIndex === 2) + { + SAPBroadphase.insertionSortZ(axisList); + } + } + + /** + * Check if the bounds of two bodies overlap, along the given SAP axis. + * @param bi + * @param bj + * @param axisIndex + */ + static checkBounds(bi: Body, bj: Body, axisIndex: number) + { + let biPos: number; + let bjPos: number; + + if (axisIndex === 0) + { + biPos = bi.position.x; + bjPos = bj.position.x; + } + else if (axisIndex === 1) + { + biPos = bi.position.y; + bjPos = bj.position.y; + } + else if (axisIndex === 2) + { + biPos = bi.position.z; + bjPos = bj.position.z; + } + + const ri = bi.boundingRadius; + const rj = bj.boundingRadius; + // const boundA1 = biPos - ri; + const boundA2 = biPos + ri; + const boundB1 = bjPos - rj; + // const boundB2 = bjPos + rj; + + return boundB1 < boundA2; + } + + /** + * Computes the variance of the body positions and estimates the best + * axis to use. Will automatically set property .axisIndex. + */ + autoDetectAxis() + { + let sumX = 0; + let sumX2 = 0; + let sumY = 0; + let sumY2 = 0; + let sumZ = 0; + let sumZ2 = 0; + const bodies = this.axisList; + const N = bodies.length; + const invN = 1 / N; + + for (let i = 0; i !== N; i++) + { + const b = bodies[i]; + + const centerX = b.position.x; + sumX += centerX; + sumX2 += centerX * centerX; + + const centerY = b.position.y; + sumY += centerY; + sumY2 += centerY * centerY; + + const centerZ = b.position.z; + sumZ += centerZ; + sumZ2 += centerZ * centerZ; + } + + const varianceX = sumX2 - sumX * sumX * invN; + const varianceY = sumY2 - sumY * sumY * invN; + const varianceZ = sumZ2 - sumZ * sumZ * invN; + + if (varianceX > varianceY) + { + if (varianceX > varianceZ) + { + this.axisIndex = 0; + } + else + { + this.axisIndex = 2; + } + } + else if (varianceY > varianceZ) + { + this.axisIndex = 1; + } + else + { + this.axisIndex = 2; + } + } + + /** + * Returns all the bodies within an AABB. + * @param _world + * @param aabb + * @param result An array to store resulting bodies in. + */ + aabbQuery(_world: World, aabb: Box3, result: Body[]) + { + result = result || []; + + if (this.dirty) + { + this.sortList(); + this.dirty = false; + } + + // const axisIndex = this.axisIndex; let + // axis = 'x'; + // if (axisIndex === 1) { axis = 'y'; } + // if (axisIndex === 2) { axis = 'z'; } + + const axisList = this.axisList; + // const lower = aabb.min[axis]; + // const upper = aabb.max[axis]; + for (let i = 0; i < axisList.length; i++) + { + const b = axisList[i]; + + if (b.aabbNeedsUpdate) + { + b.computeAABB(); + } + + if (b.aabb.overlaps(aabb)) + { + result.push(b); + } + } + + return result; + } +} diff --git a/src/common.ts b/src/common.ts new file mode 100644 index 000000000..350964da5 --- /dev/null +++ b/src/common.ts @@ -0,0 +1,3 @@ +import { Vector3 } from 'feng3d'; + +export const worldNormal = new Vector3(0, 0, 1); diff --git a/src/constraints/ConeTwistConstraint.js b/src/constraints/ConeTwistConstraint.js deleted file mode 100644 index fe70e6287..000000000 --- a/src/constraints/ConeTwistConstraint.js +++ /dev/null @@ -1,89 +0,0 @@ -module.exports = ConeTwistConstraint; - -var Constraint = require('./Constraint'); -var PointToPointConstraint = require('./PointToPointConstraint'); -var ConeEquation = require('../equations/ConeEquation'); -var RotationalEquation = require('../equations/RotationalEquation'); -var ContactEquation = require('../equations/ContactEquation'); -var Vec3 = require('../math/Vec3'); - -/** - * @class ConeTwistConstraint - * @constructor - * @author schteppe - * @param {Body} bodyA - * @param {Body} bodyB - * @param {object} [options] - * @param {Vec3} [options.pivotA] - * @param {Vec3} [options.pivotB] - * @param {Vec3} [options.axisA] - * @param {Vec3} [options.axisB] - * @param {Number} [options.maxForce=1e6] - * @extends PointToPointConstraint - */ -function ConeTwistConstraint(bodyA, bodyB, options){ - options = options || {}; - var maxForce = typeof(options.maxForce) !== 'undefined' ? options.maxForce : 1e6; - - // Set pivot point in between - var pivotA = options.pivotA ? options.pivotA.clone() : new Vec3(); - var pivotB = options.pivotB ? options.pivotB.clone() : new Vec3(); - this.axisA = options.axisA ? options.axisA.clone() : new Vec3(); - this.axisB = options.axisB ? options.axisB.clone() : new Vec3(); - - PointToPointConstraint.call(this, bodyA, pivotA, bodyB, pivotB, maxForce); - - this.collideConnected = !!options.collideConnected; - - this.angle = typeof(options.angle) !== 'undefined' ? options.angle : 0; - - /** - * @property {ConeEquation} coneEquation - */ - var c = this.coneEquation = new ConeEquation(bodyA,bodyB,options); - - /** - * @property {RotationalEquation} twistEquation - */ - var t = this.twistEquation = new RotationalEquation(bodyA,bodyB,options); - this.twistAngle = typeof(options.twistAngle) !== 'undefined' ? options.twistAngle : 0; - - // Make the cone equation push the bodies toward the cone axis, not outward - c.maxForce = 0; - c.minForce = -maxForce; - - // Make the twist equation add torque toward the initial position - t.maxForce = 0; - t.minForce = -maxForce; - - this.equations.push(c, t); -} -ConeTwistConstraint.prototype = new PointToPointConstraint(); -ConeTwistConstraint.constructor = ConeTwistConstraint; - -var ConeTwistConstraint_update_tmpVec1 = new Vec3(); -var ConeTwistConstraint_update_tmpVec2 = new Vec3(); - -ConeTwistConstraint.prototype.update = function(){ - var bodyA = this.bodyA, - bodyB = this.bodyB, - cone = this.coneEquation, - twist = this.twistEquation; - - PointToPointConstraint.prototype.update.call(this); - - // Update the axes to the cone constraint - bodyA.vectorToWorldFrame(this.axisA, cone.axisA); - bodyB.vectorToWorldFrame(this.axisB, cone.axisB); - - // Update the world axes in the twist constraint - this.axisA.tangents(twist.axisA, twist.axisA); - bodyA.vectorToWorldFrame(twist.axisA, twist.axisA); - - this.axisB.tangents(twist.axisB, twist.axisB); - bodyB.vectorToWorldFrame(twist.axisB, twist.axisB); - - cone.angle = this.angle; - twist.maxAngle = this.twistAngle; -}; - diff --git a/src/constraints/ConeTwistConstraint.ts b/src/constraints/ConeTwistConstraint.ts new file mode 100644 index 000000000..55dded632 --- /dev/null +++ b/src/constraints/ConeTwistConstraint.ts @@ -0,0 +1,91 @@ +import { Vector3 } from 'feng3d'; +import { ConeEquation } from '../equations/ConeEquation'; +import { RotationalEquation } from '../equations/RotationalEquation'; +import { Body } from '../objects/Body'; +import { PointToPointConstraint } from './PointToPointConstraint'; + +export class ConeTwistConstraint extends PointToPointConstraint +{ + axisA: Vector3; + axisB: Vector3; + angle: number; + coneEquation: ConeEquation; + twistEquation: RotationalEquation; + twistAngle: number; + + /** + * @class ConeTwistConstraint + * + * @param bodyA + * @param bodyB + * @param options + * + * @author schteppe + */ + constructor(bodyA: Body, bodyB: Body, options: { + pivotA?: Vector3, pivotB?: Vector3, maxForce?: number, axisA?: Vector3, axisB?: Vector3, + collideConnected?: boolean, angle?: number, twistAngle?: number + } = {}) + { + super(bodyA, options.pivotA ? options.pivotA.clone() : new Vector3(), bodyB, options.pivotB ? options.pivotB.clone() : new Vector3(), + typeof (options.maxForce) !== 'undefined' ? options.maxForce : 1e6); + + this.axisA = options.axisA ? options.axisA.clone() : new Vector3(); + this.axisB = options.axisB ? options.axisB.clone() : new Vector3(); + + const maxForce = typeof (options.maxForce) !== 'undefined' ? options.maxForce : 1e6; + + this.collideConnected = !!options.collideConnected; + + this.angle = typeof (options.angle) !== 'undefined' ? options.angle : 0; + + /** + * @property {ConeEquation} coneEquation + */ + const c = this.coneEquation = new ConeEquation(bodyA, bodyB, options); + + /** + * @property {RotationalEquation} twistEquation + */ + const t = this.twistEquation = new RotationalEquation(bodyA, bodyB, options); + this.twistAngle = typeof (options.twistAngle) !== 'undefined' ? options.twistAngle : 0; + + // Make the cone equation push the bodies toward the cone axis, not outward + c.maxForce = 0; + c.minForce = -maxForce; + + // Make the twist equation add torque toward the initial position + t.maxForce = 0; + t.minForce = -maxForce; + + this.equations.push(c, t); + } + + update() + { + const bodyA = this.bodyA; + const bodyB = this.bodyB; + const cone = this.coneEquation; + const twist = this.twistEquation; + + super.update(); + + // Update the axes to the cone constraint + bodyA.vectorToWorldFrame(this.axisA, cone.axisA); + bodyB.vectorToWorldFrame(this.axisB, cone.axisB); + + // Update the world axes in the twist constraint + this.axisA.tangents(twist.axisA, twist.axisA); + bodyA.vectorToWorldFrame(twist.axisA, twist.axisA); + + this.axisB.tangents(twist.axisB, twist.axisB); + bodyB.vectorToWorldFrame(twist.axisB, twist.axisB); + + cone.angle = this.angle; + twist.maxAngle = this.twistAngle; + } +} + +// var ConeTwistConstraint_update_tmpVec1 = new Vector3(); +// var ConeTwistConstraint_update_tmpVec2 = new Vector3(); + diff --git a/src/constraints/Constraint.js b/src/constraints/Constraint.js deleted file mode 100644 index ace2a2f89..000000000 --- a/src/constraints/Constraint.js +++ /dev/null @@ -1,91 +0,0 @@ -module.exports = Constraint; - -var Utils = require('../utils/Utils'); - -/** - * Constraint base class - * @class Constraint - * @author schteppe - * @constructor - * @param {Body} bodyA - * @param {Body} bodyB - * @param {object} [options] - * @param {boolean} [options.collideConnected=true] - * @param {boolean} [options.wakeUpBodies=true] - */ -function Constraint(bodyA, bodyB, options){ - options = Utils.defaults(options,{ - collideConnected : true, - wakeUpBodies : true, - }); - - /** - * Equations to be solved in this constraint - * @property equations - * @type {Array} - */ - this.equations = []; - - /** - * @property {Body} bodyA - */ - this.bodyA = bodyA; - - /** - * @property {Body} bodyB - */ - this.bodyB = bodyB; - - /** - * @property {Number} id - */ - this.id = Constraint.idCounter++; - - /** - * Set to true if you want the bodies to collide when they are connected. - * @property collideConnected - * @type {boolean} - */ - this.collideConnected = options.collideConnected; - - if(options.wakeUpBodies){ - if(bodyA){ - bodyA.wakeUp(); - } - if(bodyB){ - bodyB.wakeUp(); - } - } -} - -/** - * Update all the equations with data. - * @method update - */ -Constraint.prototype.update = function(){ - throw new Error("method update() not implmemented in this Constraint subclass!"); -}; - -/** - * Enables all equations in the constraint. - * @method enable - */ -Constraint.prototype.enable = function(){ - var eqs = this.equations; - for(var i=0; i e.keyCode-49 && !document.activeElement.localName.match(/input/)){ - changeScene(e.keyCode-49); - } - break; - } - } - }); - - - function changeScene(n){ - that.dispatchEvent({ type: 'destroy' }); - settings.paused = false; - updategui(); - buildScene(n); - } - - - function start(){ - buildScene(0); - } - - function buildScene(n){ - // Remove current bodies and visuals - var num = visuals.length; - for(var i=0; iid ? j-1 : j; - bodies[i] = old_b[j]; - visuals[i] = old_v[j]; - bodies[i].visualref = old_b[j].visualref; - bodies[i].visualref.visualId = i; - } - } - body.visualref.visualId = null; - this.scene.remove(body.visualref); - body.visualref = null; - } -}; - -CANNON.Demo.prototype.removeAllVisuals = function(){ - while(this.bodies.length) { - this.removeVisual(this.bodies[0]); - } -}; - -CANNON.Demo.prototype.shape2mesh = function(body){ - var wireframe = this.settings.renderMode === "wireframe"; - var obj = new THREE.Object3D(); - - for (var l = 0; l < body.shapes.length; l++) { - var shape = body.shapes[l]; - - var mesh; - - switch(shape.type){ - - case CANNON.Shape.types.SPHERE: - var sphere_geometry = new THREE.SphereGeometry( shape.radius, 8, 8); - mesh = new THREE.Mesh( sphere_geometry, this.currentMaterial ); - break; - - case CANNON.Shape.types.PARTICLE: - mesh = new THREE.Mesh( this.particleGeo, this.particleMaterial ); - var s = this.settings; - mesh.scale.set(s.particleSize,s.particleSize,s.particleSize); - break; - - case CANNON.Shape.types.PLANE: - var geometry = new THREE.PlaneGeometry(10, 10, 4, 4); - mesh = new THREE.Object3D(); - var submesh = new THREE.Object3D(); - var ground = new THREE.Mesh( geometry, this.currentMaterial ); - ground.scale.set(100, 100, 100); - submesh.add(ground); - - ground.castShadow = true; - ground.receiveShadow = true; - - mesh.add(submesh); - break; - - case CANNON.Shape.types.BOX: - var box_geometry = new THREE.BoxGeometry( shape.halfExtents.x*2, - shape.halfExtents.y*2, - shape.halfExtents.z*2 ); - mesh = new THREE.Mesh( box_geometry, this.currentMaterial ); - break; - - case CANNON.Shape.types.CONVEXPOLYHEDRON: - var geo = new THREE.Geometry(); - - // Add vertices - for (var i = 0; i < shape.vertices.length; i++) { - var v = shape.vertices[i]; - geo.vertices.push(new THREE.Vector3(v.x, v.y, v.z)); - } - - for(var i=0; i < shape.faces.length; i++){ - var face = shape.faces[i]; - - // add triangles - var a = face[0]; - for (var j = 1; j < face.length - 1; j++) { - var b = face[j]; - var c = face[j + 1]; - geo.faces.push(new THREE.Face3(a, b, c)); - } - } - geo.computeBoundingSphere(); - geo.computeFaceNormals(); - mesh = new THREE.Mesh( geo, this.currentMaterial ); - break; - - case CANNON.Shape.types.HEIGHTFIELD: - var geometry = new THREE.Geometry(); - - var v0 = new CANNON.Vec3(); - var v1 = new CANNON.Vec3(); - var v2 = new CANNON.Vec3(); - for (var xi = 0; xi < shape.data.length - 1; xi++) { - for (var yi = 0; yi < shape.data[xi].length - 1; yi++) { - for (var k = 0; k < 2; k++) { - shape.getConvexTrianglePillar(xi, yi, k===0); - v0.copy(shape.pillarConvex.vertices[0]); - v1.copy(shape.pillarConvex.vertices[1]); - v2.copy(shape.pillarConvex.vertices[2]); - v0.vadd(shape.pillarOffset, v0); - v1.vadd(shape.pillarOffset, v1); - v2.vadd(shape.pillarOffset, v2); - geometry.vertices.push( - new THREE.Vector3(v0.x, v0.y, v0.z), - new THREE.Vector3(v1.x, v1.y, v1.z), - new THREE.Vector3(v2.x, v2.y, v2.z) - ); - var i = geometry.vertices.length - 3; - geometry.faces.push(new THREE.Face3(i, i+1, i+2)); - } - } - } - geometry.computeBoundingSphere(); - geometry.computeFaceNormals(); - mesh = new THREE.Mesh(geometry, this.currentMaterial); - break; - - case CANNON.Shape.types.TRIMESH: - var geometry = new THREE.Geometry(); - - var v0 = new CANNON.Vec3(); - var v1 = new CANNON.Vec3(); - var v2 = new CANNON.Vec3(); - for (var i = 0; i < shape.indices.length / 3; i++) { - shape.getTriangleVertices(i, v0, v1, v2); - geometry.vertices.push( - new THREE.Vector3(v0.x, v0.y, v0.z), - new THREE.Vector3(v1.x, v1.y, v1.z), - new THREE.Vector3(v2.x, v2.y, v2.z) - ); - var j = geometry.vertices.length - 3; - geometry.faces.push(new THREE.Face3(j, j+1, j+2)); - } - geometry.computeBoundingSphere(); - geometry.computeFaceNormals(); - mesh = new THREE.Mesh(geometry, this.currentMaterial); - break; - - default: - throw "Visual type not recognized: "+shape.type; - } - - mesh.receiveShadow = true; - mesh.castShadow = true; - if(mesh.children){ - for(var i=0; i - // G = [0 axisA 0 -axisB] - - GA.rotational.copy(axisA); - axisB.negate(GB.rotational); - - var GW = this.computeGW() - this.targetVelocity, - GiMf = this.computeGiMf(); - - var B = - GW * b - h * GiMf; - - return B; -}; diff --git a/src/equations/RotationalMotorEquation.ts b/src/equations/RotationalMotorEquation.ts new file mode 100644 index 000000000..66137c53c --- /dev/null +++ b/src/equations/RotationalMotorEquation.ts @@ -0,0 +1,69 @@ +import { Vector3 } from 'feng3d'; +import { Body } from '../objects/Body'; +import { Equation } from './Equation'; + +export class RotationalMotorEquation extends Equation +{ + /** + * World oriented rotational axis + */ + axisA: Vector3; + + /** + * World oriented rotational axis + */ + axisB: Vector3; // World oriented rotational axis + + /** + * Motor velocity + */ + targetVelocity: number; + + /** + * Rotational motor constraint. Tries to keep the relative angular velocity of the bodies to a given value. + * + * @param bodyA + * @param bodyB + * @param maxForce + * + * @author schteppe + */ + constructor(bodyA: Body, bodyB: Body, maxForce: number) + { + super(bodyA, bodyB, -(typeof (maxForce) !== 'undefined' ? maxForce : 1e6), typeof (maxForce) !== 'undefined' ? maxForce : 1e6); + + this.axisA = new Vector3(); + this.axisB = new Vector3(); // World oriented rotational axis + this.targetVelocity = 0; + } + + computeB(h: number) + { + // const a = this.a; + const b = this.b; + // const bi = this.bi; + // const bj = this.bj; + + const axisA = this.axisA; + const axisB = this.axisB; + + const GA = this.jacobianElementA; + const GB = this.jacobianElementB; + + // g = 0 + // gdot = axisA * wi - axisB * wj + // gdot = G * W = G * [vi wi vj wj] + // => + // G = [0 axisA 0 -axisB] + + GA.rotational.copy(axisA); + axisB.negateTo(GB.rotational); + + const GW = this.computeGW() - this.targetVelocity; + const GiMf = this.computeGiMf(); + + const B = -GW * b - h * GiMf; + + return B; + } +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 000000000..b310b2e5e --- /dev/null +++ b/src/index.ts @@ -0,0 +1,45 @@ +export * from '././math/Transform'; +export * from './collision/Broadphase'; +export * from './collision/GridBroadphase'; +export * from './collision/NaiveBroadphase'; +export * from './collision/OverlapKeeper'; +export * from './collision/Ray'; +export * from './collision/RaycastResult'; +export * from './collision/SAPBroadphase'; +export * from './constraints/ConeTwistConstraint'; +export * from './constraints/Constraint'; +export * from './constraints/DistanceConstraint'; +export * from './constraints/HingeConstraint'; +export * from './constraints/LockConstraint'; +export * from './constraints/PointToPointConstraint'; +export * from './equations/ConeEquation'; +export * from './equations/ContactEquation'; +export * from './equations/Equation'; +export * from './equations/FrictionEquation'; +export * from './equations/RotationalEquation'; +export * from './equations/RotationalMotorEquation'; +export * from './material/ContactMaterial'; +export * from './material/Material'; +export * from './math/JacobianElement'; +export * from './objects/Body'; +export * from './objects/RaycastVehicle'; +export * from './objects/RigidVehicle'; +export * from './objects/SPHSystem'; +export * from './objects/Spring'; +export * from './objects/WheelInfo'; +export * from './shapes/Box'; +export * from './shapes/ConvexPolyhedron'; +export * from './shapes/Cylinder'; +export * from './shapes/Heightfield'; +export * from './shapes/Particle'; +export * from './shapes/Plane'; +export * from './shapes/Shape'; +export * from './shapes/Sphere'; +export * from './shapes/Trimesh'; +export * from './solver/GSSolver'; +export * from './solver/Solver'; +export * from './solver/SplitSolver'; +export * from './utils/Octree'; +export * from './utils/Utils'; +export * from './world/Narrowphase'; +export * from './world/World'; diff --git a/src/material/ContactMaterial.js b/src/material/ContactMaterial.js deleted file mode 100644 index d904b47ad..000000000 --- a/src/material/ContactMaterial.js +++ /dev/null @@ -1,79 +0,0 @@ -var Utils = require('../utils/Utils'); - -module.exports = ContactMaterial; - -/** - * Defines what happens when two materials meet. - * @class ContactMaterial - * @constructor - * @param {Material} m1 - * @param {Material} m2 - * @param {object} [options] - * @param {Number} [options.friction=0.3] - * @param {Number} [options.restitution=0.3] - * @param {number} [options.contactEquationStiffness=1e7] - * @param {number} [options.contactEquationRelaxation=3] - * @param {number} [options.frictionEquationStiffness=1e7] - * @param {Number} [options.frictionEquationRelaxation=3] - */ -function ContactMaterial(m1, m2, options){ - options = Utils.defaults(options, { - friction: 0.3, - restitution: 0.3, - contactEquationStiffness: 1e7, - contactEquationRelaxation: 3, - frictionEquationStiffness: 1e7, - frictionEquationRelaxation: 3 - }); - - /** - * Identifier of this material - * @property {Number} id - */ - this.id = ContactMaterial.idCounter++; - - /** - * Participating materials - * @property {Array} materials - * @todo Should be .materialA and .materialB instead - */ - this.materials = [m1, m2]; - - /** - * Friction coefficient - * @property {Number} friction - */ - this.friction = options.friction; - - /** - * Restitution coefficient - * @property {Number} restitution - */ - this.restitution = options.restitution; - - /** - * Stiffness of the produced contact equations - * @property {Number} contactEquationStiffness - */ - this.contactEquationStiffness = options.contactEquationStiffness; - - /** - * Relaxation time of the produced contact equations - * @property {Number} contactEquationRelaxation - */ - this.contactEquationRelaxation = options.contactEquationRelaxation; - - /** - * Stiffness of the produced friction equations - * @property {Number} frictionEquationStiffness - */ - this.frictionEquationStiffness = options.frictionEquationStiffness; - - /** - * Relaxation time of the produced friction equations - * @property {Number} frictionEquationRelaxation - */ - this.frictionEquationRelaxation = options.frictionEquationRelaxation; -} - -ContactMaterial.idCounter = 0; diff --git a/src/material/ContactMaterial.ts b/src/material/ContactMaterial.ts new file mode 100644 index 000000000..36bb13d59 --- /dev/null +++ b/src/material/ContactMaterial.ts @@ -0,0 +1,82 @@ +import { Utils } from '../utils/Utils'; +import { Material } from './Material'; + +export class ContactMaterial +{ + /** + * Identifier of this material + */ + id: number; + + /** + * Participating materials + * @todo Should be .materialA and .materialB instead + */ + materials: Material[]; + + /** + * Friction coefficient + */ + friction: number; + + /** + * Restitution coefficient + */ + restitution: number; + + /** + * Stiffness of the produced contact equations + */ + contactEquationStiffness: number; + + /** + * Relaxation time of the produced contact equations + */ + contactEquationRelaxation: number; + + /** + * Stiffness of the produced friction equations + */ + frictionEquationStiffness: number; + + /** + * Relaxation time of the produced friction equations + */ + frictionEquationRelaxation: number; + + /** + * Defines what happens when two materials meet. + * + * @param m1 + * @param m2 + * @param options + */ + constructor(m1?: Material, m2?: Material, options: { + friction?: number, restitution?: number, + contactEquationStiffness?: number, + contactEquationRelaxation?: number, + frictionEquationStiffness?: number, + frictionEquationRelaxation?: number + } = {}) + { + options = Utils.defaults(options, { + friction: 0.3, + restitution: 0.3, + contactEquationStiffness: 1e7, + contactEquationRelaxation: 3, + frictionEquationStiffness: 1e7, + frictionEquationRelaxation: 3 + }); + + this.id = ContactMaterial.idCounter++; + this.materials = [m1, m2]; + this.friction = options.friction; + this.restitution = options.restitution; + this.contactEquationStiffness = options.contactEquationStiffness; + this.contactEquationRelaxation = options.contactEquationRelaxation; + this.frictionEquationStiffness = options.frictionEquationStiffness; + this.frictionEquationRelaxation = options.frictionEquationRelaxation; + } + + static idCounter = 0; +} diff --git a/src/material/Material.js b/src/material/Material.js deleted file mode 100644 index 7a5e9f699..000000000 --- a/src/material/Material.js +++ /dev/null @@ -1,48 +0,0 @@ -module.exports = Material; - -/** - * Defines a physics material. - * @class Material - * @constructor - * @param {object} [options] - * @author schteppe - */ -function Material(options){ - var name = ''; - options = options || {}; - - // Backwards compatibility fix - if(typeof(options) === 'string'){ - name = options; - options = {}; - } else if(typeof(options) === 'object') { - name = ''; - } - - /** - * @property name - * @type {String} - */ - this.name = name; - - /** - * material id. - * @property id - * @type {number} - */ - this.id = Material.idCounter++; - - /** - * Friction for this material. If non-negative, it will be used instead of the friction given by ContactMaterials. If there's no matching ContactMaterial, the value from .defaultContactMaterial in the World will be used. - * @property {number} friction - */ - this.friction = typeof(options.friction) !== 'undefined' ? options.friction : -1; - - /** - * Restitution for this material. If non-negative, it will be used instead of the restitution given by ContactMaterials. If there's no matching ContactMaterial, the value from .defaultContactMaterial in the World will be used. - * @property {number} restitution - */ - this.restitution = typeof(options.restitution) !== 'undefined' ? options.restitution : -1; -} - -Material.idCounter = 0; diff --git a/src/material/Material.ts b/src/material/Material.ts new file mode 100644 index 000000000..ffdf97e88 --- /dev/null +++ b/src/material/Material.ts @@ -0,0 +1,48 @@ +export class Material +{ + name: string; + + /** + * material id. + */ + id: number; + + /** + * Friction for this material. If non-negative, it will be used instead of the friction given by ContactMaterials. If there's no matching ContactMaterial, the value from .defaultContactMaterial in the World will be used. + */ + friction: number; + + /** + * Restitution for this material. If non-negative, it will be used instead of the restitution given by ContactMaterials. If there's no matching ContactMaterial, the value from .defaultContactMaterial in the World will be used. + */ + restitution: number; + + /** + * Defines a physics material. + * + * @param options + * @author schteppe + */ + constructor(options: { friction?: number, restitution?: number } | string = {}) + { + let name = ''; + + // Backwards compatibility fix + if (typeof (options) === 'string') + { + name = options; + options = {}; + } + else if (typeof (options) === 'object') + { + name = ''; + } + + this.name = name; + this.id = Material.idCounter++; + this.friction = typeof (options.friction) !== 'undefined' ? options.friction : -1; + this.restitution = typeof (options.restitution) !== 'undefined' ? options.restitution : -1; + } + + static idCounter = 0; +} diff --git a/src/math/JacobianElement.js b/src/math/JacobianElement.js deleted file mode 100644 index 625f5c756..000000000 --- a/src/math/JacobianElement.js +++ /dev/null @@ -1,42 +0,0 @@ -module.exports = JacobianElement; - -var Vec3 = require('./Vec3'); - -/** - * An element containing 6 entries, 3 spatial and 3 rotational degrees of freedom. - * @class JacobianElement - * @constructor - */ -function JacobianElement(){ - - /** - * @property {Vec3} spatial - */ - this.spatial = new Vec3(); - - /** - * @property {Vec3} rotational - */ - this.rotational = new Vec3(); -} - -/** - * Multiply with other JacobianElement - * @method multiplyElement - * @param {JacobianElement} element - * @return {Number} - */ -JacobianElement.prototype.multiplyElement = function(element){ - return element.spatial.dot(this.spatial) + element.rotational.dot(this.rotational); -}; - -/** - * Multiply with two vectors - * @method multiplyVectors - * @param {Vec3} spatial - * @param {Vec3} rotational - * @return {Number} - */ -JacobianElement.prototype.multiplyVectors = function(spatial,rotational){ - return spatial.dot(this.spatial) + rotational.dot(this.rotational); -}; diff --git a/src/math/JacobianElement.ts b/src/math/JacobianElement.ts new file mode 100644 index 000000000..bb43eaa36 --- /dev/null +++ b/src/math/JacobianElement.ts @@ -0,0 +1,35 @@ +import { Vector3 } from 'feng3d'; + +export class JacobianElement +{ + spatial: Vector3; + rotational: Vector3; + + /** + * An element containing 6 entries, 3 spatial and 3 rotational degrees of freedom. + */ + constructor() + { + this.spatial = new Vector3(); + this.rotational = new Vector3(); + } + + /** + * Multiply with other JacobianElement + * @param element + */ + multiplyElement(element: JacobianElement) + { + return element.spatial.dot(this.spatial) + element.rotational.dot(this.rotational); + } + + /** + * Multiply with two vectors + * @param spatial + * @param rotational + */ + multiplyVectors(spatial: Vector3, rotational: Vector3) + { + return spatial.dot(this.spatial) + rotational.dot(this.rotational); + } +} diff --git a/src/math/Mat3.js b/src/math/Mat3.js deleted file mode 100644 index 2fa991c26..000000000 --- a/src/math/Mat3.js +++ /dev/null @@ -1,422 +0,0 @@ -module.exports = Mat3; - -var Vec3 = require('./Vec3'); - -/** - * A 3x3 matrix. - * @class Mat3 - * @constructor - * @param array elements Array of nine elements. Optional. - * @author schteppe / http://github.com/schteppe - */ -function Mat3(elements){ - /** - * A vector of length 9, containing all matrix elements - * @property {Array} elements - */ - if(elements){ - this.elements = elements; - } else { - this.elements = [0,0,0,0,0,0,0,0,0]; - } -} - -/** - * Sets the matrix to identity - * @method identity - * @todo Should perhaps be renamed to setIdentity() to be more clear. - * @todo Create another function that immediately creates an identity matrix eg. eye() - */ -Mat3.prototype.identity = function(){ - var e = this.elements; - e[0] = 1; - e[1] = 0; - e[2] = 0; - - e[3] = 0; - e[4] = 1; - e[5] = 0; - - e[6] = 0; - e[7] = 0; - e[8] = 1; -}; - -/** - * Set all elements to zero - * @method setZero - */ -Mat3.prototype.setZero = function(){ - var e = this.elements; - e[0] = 0; - e[1] = 0; - e[2] = 0; - e[3] = 0; - e[4] = 0; - e[5] = 0; - e[6] = 0; - e[7] = 0; - e[8] = 0; -}; - -/** - * Sets the matrix diagonal elements from a Vec3 - * @method setTrace - * @param {Vec3} vec3 - */ -Mat3.prototype.setTrace = function(vec3){ - var e = this.elements; - e[0] = vec3.x; - e[4] = vec3.y; - e[8] = vec3.z; -}; - -/** - * Gets the matrix diagonal elements - * @method getTrace - * @return {Vec3} - */ -Mat3.prototype.getTrace = function(target){ - var target = target || new Vec3(); - var e = this.elements; - target.x = e[0]; - target.y = e[4]; - target.z = e[8]; -}; - -/** - * Matrix-Vector multiplication - * @method vmult - * @param {Vec3} v The vector to multiply with - * @param {Vec3} target Optional, target to save the result in. - */ -Mat3.prototype.vmult = function(v,target){ - target = target || new Vec3(); - - var e = this.elements, - x = v.x, - y = v.y, - z = v.z; - target.x = e[0]*x + e[1]*y + e[2]*z; - target.y = e[3]*x + e[4]*y + e[5]*z; - target.z = e[6]*x + e[7]*y + e[8]*z; - - return target; -}; - -/** - * Matrix-scalar multiplication - * @method smult - * @param {Number} s - */ -Mat3.prototype.smult = function(s){ - for(var i=0; i1 acos and sqrt will produce errors, this cant happen if quaternion is normalised - var angle = 2 * Math.acos(this.w); - var s = Math.sqrt(1-this.w*this.w); // assuming quaternion normalised then w is less than 1, so term always positive. - if (s < 0.001) { // test to avoid divide by zero, s is always positive due to sqrt - // if s close to zero then direction of axis not important - targetAxis.x = this.x; // if it is important that axis is normalised then replace with x=1; y=z=0; - targetAxis.y = this.y; - targetAxis.z = this.z; - } else { - targetAxis.x = this.x / s; // normalise axis - targetAxis.y = this.y / s; - targetAxis.z = this.z / s; - } - return [targetAxis,angle]; -}; - -var sfv_t1 = new Vec3(), - sfv_t2 = new Vec3(); - -/** - * Set the quaternion value given two vectors. The resulting rotation will be the needed rotation to rotate u to v. - * @method setFromVectors - * @param {Vec3} u - * @param {Vec3} v - */ -Quaternion.prototype.setFromVectors = function(u,v){ - if(u.isAntiparallelTo(v)){ - var t1 = sfv_t1; - var t2 = sfv_t2; - - u.tangents(t1,t2); - this.setFromAxisAngle(t1,Math.PI); - } else { - var a = u.cross(v); - this.x = a.x; - this.y = a.y; - this.z = a.z; - this.w = Math.sqrt(Math.pow(u.norm(),2) * Math.pow(v.norm(),2)) + u.dot(v); - this.normalize(); - } - return this; -}; - -/** - * Quaternion multiplication - * @method mult - * @param {Quaternion} q - * @param {Quaternion} target Optional. - * @return {Quaternion} - */ -var Quaternion_mult_va = new Vec3(); -var Quaternion_mult_vb = new Vec3(); -var Quaternion_mult_vaxvb = new Vec3(); -Quaternion.prototype.mult = function(q,target){ - target = target || new Quaternion(); - - var ax = this.x, ay = this.y, az = this.z, aw = this.w, - bx = q.x, by = q.y, bz = q.z, bw = q.w; - - target.x = ax * bw + aw * bx + ay * bz - az * by; - target.y = ay * bw + aw * by + az * bx - ax * bz; - target.z = az * bw + aw * bz + ax * by - ay * bx; - target.w = aw * bw - ax * bx - ay * by - az * bz; - - return target; -}; - -/** - * Get the inverse quaternion rotation. - * @method inverse - * @param {Quaternion} target - * @return {Quaternion} - */ -Quaternion.prototype.inverse = function(target){ - var x = this.x, y = this.y, z = this.z, w = this.w; - target = target || new Quaternion(); - - this.conjugate(target); - var inorm2 = 1/(x*x + y*y + z*z + w*w); - target.x *= inorm2; - target.y *= inorm2; - target.z *= inorm2; - target.w *= inorm2; - - return target; -}; - -/** - * Get the quaternion conjugate - * @method conjugate - * @param {Quaternion} target - * @return {Quaternion} - */ -Quaternion.prototype.conjugate = function(target){ - target = target || new Quaternion(); - - target.x = -this.x; - target.y = -this.y; - target.z = -this.z; - target.w = this.w; - - return target; -}; - -/** - * Normalize the quaternion. Note that this changes the values of the quaternion. - * @method normalize - */ -Quaternion.prototype.normalize = function(){ - var l = Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w); - if ( l === 0 ) { - this.x = 0; - this.y = 0; - this.z = 0; - this.w = 0; - } else { - l = 1 / l; - this.x *= l; - this.y *= l; - this.z *= l; - this.w *= l; - } - return this; -}; - -/** - * Approximation of quaternion normalization. Works best when quat is already almost-normalized. - * @method normalizeFast - * @see http://jsperf.com/fast-quaternion-normalization - * @author unphased, https://github.com/unphased - */ -Quaternion.prototype.normalizeFast = function () { - var f = (3.0-(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w))/2.0; - if ( f === 0 ) { - this.x = 0; - this.y = 0; - this.z = 0; - this.w = 0; - } else { - this.x *= f; - this.y *= f; - this.z *= f; - this.w *= f; - } - return this; -}; - -/** - * Multiply the quaternion by a vector - * @method vmult - * @param {Vec3} v - * @param {Vec3} target Optional - * @return {Vec3} - */ -Quaternion.prototype.vmult = function(v,target){ - target = target || new Vec3(); - - var x = v.x, - y = v.y, - z = v.z; - - var qx = this.x, - qy = this.y, - qz = this.z, - qw = this.w; - - // q*v - var ix = qw * x + qy * z - qz * y, - iy = qw * y + qz * x - qx * z, - iz = qw * z + qx * y - qy * x, - iw = -qx * x - qy * y - qz * z; - - target.x = ix * qw + iw * -qx + iy * -qz - iz * -qy; - target.y = iy * qw + iw * -qy + iz * -qx - ix * -qz; - target.z = iz * qw + iw * -qz + ix * -qy - iy * -qx; - - return target; -}; - -/** - * Copies value of source to this quaternion. - * @method copy - * @param {Quaternion} source - * @return {Quaternion} this - */ -Quaternion.prototype.copy = function(source){ - this.x = source.x; - this.y = source.y; - this.z = source.z; - this.w = source.w; - return this; -}; - -/** - * Convert the quaternion to euler angle representation. Order: YZX, as this page describes: http://www.euclideanspace.com/maths/standards/index.htm - * @method toEuler - * @param {Vec3} target - * @param string order Three-character string e.g. "YZX", which also is default. - */ -Quaternion.prototype.toEuler = function(target,order){ - order = order || "YZX"; - - var heading, attitude, bank; - var x = this.x, y = this.y, z = this.z, w = this.w; - - switch(order){ - case "YZX": - var test = x*y + z*w; - if (test > 0.499) { // singularity at north pole - heading = 2 * Math.atan2(x,w); - attitude = Math.PI/2; - bank = 0; - } - if (test < -0.499) { // singularity at south pole - heading = -2 * Math.atan2(x,w); - attitude = - Math.PI/2; - bank = 0; - } - if(isNaN(heading)){ - var sqx = x*x; - var sqy = y*y; - var sqz = z*z; - heading = Math.atan2(2*y*w - 2*x*z , 1 - 2*sqy - 2*sqz); // Heading - attitude = Math.asin(2*test); // attitude - bank = Math.atan2(2*x*w - 2*y*z , 1 - 2*sqx - 2*sqz); // bank - } - break; - default: - throw new Error("Euler order "+order+" not supported yet."); - } - - target.y = heading; - target.z = attitude; - target.x = bank; -}; - -/** - * See http://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/content/SpinCalc.m - * @method setFromEuler - * @param {Number} x - * @param {Number} y - * @param {Number} z - * @param {String} order The order to apply angles: 'XYZ' or 'YXZ' or any other combination - */ -Quaternion.prototype.setFromEuler = function ( x, y, z, order ) { - order = order || "XYZ"; - - var c1 = Math.cos( x / 2 ); - var c2 = Math.cos( y / 2 ); - var c3 = Math.cos( z / 2 ); - var s1 = Math.sin( x / 2 ); - var s2 = Math.sin( y / 2 ); - var s3 = Math.sin( z / 2 ); - - if ( order === 'XYZ' ) { - - this.x = s1 * c2 * c3 + c1 * s2 * s3; - this.y = c1 * s2 * c3 - s1 * c2 * s3; - this.z = c1 * c2 * s3 + s1 * s2 * c3; - this.w = c1 * c2 * c3 - s1 * s2 * s3; - - } else if ( order === 'YXZ' ) { - - this.x = s1 * c2 * c3 + c1 * s2 * s3; - this.y = c1 * s2 * c3 - s1 * c2 * s3; - this.z = c1 * c2 * s3 - s1 * s2 * c3; - this.w = c1 * c2 * c3 + s1 * s2 * s3; - - } else if ( order === 'ZXY' ) { - - this.x = s1 * c2 * c3 - c1 * s2 * s3; - this.y = c1 * s2 * c3 + s1 * c2 * s3; - this.z = c1 * c2 * s3 + s1 * s2 * c3; - this.w = c1 * c2 * c3 - s1 * s2 * s3; - - } else if ( order === 'ZYX' ) { - - this.x = s1 * c2 * c3 - c1 * s2 * s3; - this.y = c1 * s2 * c3 + s1 * c2 * s3; - this.z = c1 * c2 * s3 - s1 * s2 * c3; - this.w = c1 * c2 * c3 + s1 * s2 * s3; - - } else if ( order === 'YZX' ) { - - this.x = s1 * c2 * c3 + c1 * s2 * s3; - this.y = c1 * s2 * c3 + s1 * c2 * s3; - this.z = c1 * c2 * s3 - s1 * s2 * c3; - this.w = c1 * c2 * c3 - s1 * s2 * s3; - - } else if ( order === 'XZY' ) { - - this.x = s1 * c2 * c3 - c1 * s2 * s3; - this.y = c1 * s2 * c3 - s1 * c2 * s3; - this.z = c1 * c2 * s3 + s1 * s2 * c3; - this.w = c1 * c2 * c3 + s1 * s2 * s3; - - } - - return this; -}; - -/** - * @method clone - * @return {Quaternion} - */ -Quaternion.prototype.clone = function(){ - return new Quaternion(this.x, this.y, this.z, this.w); -}; - -/** - * Performs a spherical linear interpolation between two quat - * - * @method slerp - * @param {Quaternion} toQuat second operand - * @param {Number} t interpolation amount between the self quaternion and toQuat - * @param {Quaternion} [target] A quaternion to store the result in. If not provided, a new one will be created. - * @returns {Quaternion} The "target" object - */ -Quaternion.prototype.slerp = function (toQuat, t, target) { - target = target || new Quaternion(); - - var ax = this.x, - ay = this.y, - az = this.z, - aw = this.w, - bx = toQuat.x, - by = toQuat.y, - bz = toQuat.z, - bw = toQuat.w; - - var omega, cosom, sinom, scale0, scale1; - - // calc cosine - cosom = ax * bx + ay * by + az * bz + aw * bw; - - // adjust signs (if necessary) - if ( cosom < 0.0 ) { - cosom = -cosom; - bx = - bx; - by = - by; - bz = - bz; - bw = - bw; - } - - // calculate coefficients - if ( (1.0 - cosom) > 0.000001 ) { - // standard case (slerp) - omega = Math.acos(cosom); - sinom = Math.sin(omega); - scale0 = Math.sin((1.0 - t) * omega) / sinom; - scale1 = Math.sin(t * omega) / sinom; - } else { - // "from" and "to" quaternions are very close - // ... so we can do a linear interpolation - scale0 = 1.0 - t; - scale1 = t; - } - - // calculate final values - target.x = scale0 * ax + scale1 * bx; - target.y = scale0 * ay + scale1 * by; - target.z = scale0 * az + scale1 * bz; - target.w = scale0 * aw + scale1 * bw; - - return target; -}; - -/** - * Rotate an absolute orientation quaternion given an angular velocity and a time step. - * @param {Vec3} angularVelocity - * @param {number} dt - * @param {Vec3} angularFactor - * @param {Quaternion} target - * @return {Quaternion} The "target" object - */ -Quaternion.prototype.integrate = function(angularVelocity, dt, angularFactor, target){ - target = target || new Quaternion(); - - var ax = angularVelocity.x * angularFactor.x, - ay = angularVelocity.y * angularFactor.y, - az = angularVelocity.z * angularFactor.z, - bx = this.x, - by = this.y, - bz = this.z, - bw = this.w; - - var half_dt = dt * 0.5; - - target.x += half_dt * (ax * bw + ay * bz - az * by); - target.y += half_dt * (ay * bw + az * bx - ax * bz); - target.z += half_dt * (az * bw + ax * by - ay * bx); - target.w += half_dt * (- ax * bx - ay * by - az * bz); - - return target; -}; \ No newline at end of file diff --git a/src/math/Transform.js b/src/math/Transform.js deleted file mode 100644 index 0c19b05ea..000000000 --- a/src/math/Transform.js +++ /dev/null @@ -1,103 +0,0 @@ -var Vec3 = require('./Vec3'); -var Quaternion = require('./Quaternion'); - -module.exports = Transform; - -/** - * @class Transform - * @constructor - */ -function Transform(options) { - options = options || {}; - - /** - * @property {Vec3} position - */ - this.position = new Vec3(); - if(options.position){ - this.position.copy(options.position); - } - - /** - * @property {Quaternion} quaternion - */ - this.quaternion = new Quaternion(); - if(options.quaternion){ - this.quaternion.copy(options.quaternion); - } -} - -var tmpQuat = new Quaternion(); - -/** - * @static - * @method pointToLocaFrame - * @param {Vec3} position - * @param {Quaternion} quaternion - * @param {Vec3} worldPoint - * @param {Vec3} result - */ -Transform.pointToLocalFrame = function(position, quaternion, worldPoint, result){ - var result = result || new Vec3(); - worldPoint.vsub(position, result); - quaternion.conjugate(tmpQuat); - tmpQuat.vmult(result, result); - return result; -}; - -/** - * Get a global point in local transform coordinates. - * @method pointToLocal - * @param {Vec3} point - * @param {Vec3} result - * @return {Vec3} The "result" vector object - */ -Transform.prototype.pointToLocal = function(worldPoint, result){ - return Transform.pointToLocalFrame(this.position, this.quaternion, worldPoint, result); -}; - -/** - * @static - * @method pointToWorldFrame - * @param {Vec3} position - * @param {Vec3} quaternion - * @param {Vec3} localPoint - * @param {Vec3} result - */ -Transform.pointToWorldFrame = function(position, quaternion, localPoint, result){ - var result = result || new Vec3(); - quaternion.vmult(localPoint, result); - result.vadd(position, result); - return result; -}; - -/** - * Get a local point in global transform coordinates. - * @method pointToWorld - * @param {Vec3} point - * @param {Vec3} result - * @return {Vec3} The "result" vector object - */ -Transform.prototype.pointToWorld = function(localPoint, result){ - return Transform.pointToWorldFrame(this.position, this.quaternion, localPoint, result); -}; - - -Transform.prototype.vectorToWorldFrame = function(localVector, result){ - var result = result || new Vec3(); - this.quaternion.vmult(localVector, result); - return result; -}; - -Transform.vectorToWorldFrame = function(quaternion, localVector, result){ - quaternion.vmult(localVector, result); - return result; -}; - -Transform.vectorToLocalFrame = function(position, quaternion, worldVector, result){ - var result = result || new Vec3(); - quaternion.w *= -1; - quaternion.vmult(worldVector, result); - quaternion.w *= -1; - return result; -}; diff --git a/src/math/Transform.ts b/src/math/Transform.ts new file mode 100644 index 000000000..9c6a5de58 --- /dev/null +++ b/src/math/Transform.ts @@ -0,0 +1,146 @@ +import { Box3, Quaternion, Vector3 } from 'feng3d'; + +export class Transform +{ + position: Vector3; + quaternion: Quaternion; + + constructor(position = new Vector3(), quaternion = new Quaternion()) + { + this.position = position; + this.quaternion = quaternion; + } + + /** + * @param position + * @param quaternion + * @param worldPoint + * @param result + */ + static pointToLocalFrame(position: Vector3, quaternion: Quaternion, worldPoint: Vector3, result = new Vector3()) + { + worldPoint.subTo(position, result); + quaternion.inverseTo(tmpQuat); + tmpQuat.vmult(result, result); + + return result; + } + + /** + * Get a global point in local transform coordinates. + * @param worldPoint + * @param result + * @returnThe "result" vector object + */ + pointToLocal(worldPoint: Vector3, result: Vector3) + { + return Transform.pointToLocalFrame(this.position, this.quaternion, worldPoint, result); + } + + /** + * @param position + * @param quaternion + * @param localPoint + * @param result + */ + static pointToWorldFrame(position: Vector3, quaternion: Quaternion, localPoint: Vector3, result = new Vector3()) + { + quaternion.vmult(localPoint, result); + result.addTo(position, result); + + return result; + } + + /** + * Get a local point in global transform coordinates. + * @param point + * @param result + * @return The "result" vector object + */ + pointToWorld(localPoint: Vector3, result: Vector3) + { + return Transform.pointToWorldFrame(this.position, this.quaternion, localPoint, result); + } + + vectorToWorldFrame(localVector: Vector3, result = new Vector3()) + { + this.quaternion.vmult(localVector, result); + + return result; + } + + /** + * Get the representation of an AABB in another frame. + * @param frame + * @param target + * @return The "target" AABB object. + */ + toLocalFrameBox3(box3: Box3, target: Box3) + { + const corners = transformIntoFrameCorners; + + // Get corners in current frame + box3.toPoints(corners); + + // Transform them to new local frame + for (let i = 0; i !== 8; i++) + { + const corner = corners[i]; + this.pointToLocal(corner, corner); + } + + return target.fromPoints(corners); + } + + /** + * Get the representation of an AABB in the global frame. + * @param frame + * @param target + * @return The "target" AABB object. + */ + toWorldFrameBox3(box3: Box3, target: Box3) + { + const corners = transformIntoFrameCorners; + + // Get corners in current frame + box3.toPoints(corners); + + // Transform them to new local frame + for (let i = 0; i !== 8; i++) + { + const corner = corners[i]; + this.pointToWorld(corner, corner); + } + + return target.fromPoints(corners); + } + + static vectorToWorldFrame(quaternion: Quaternion, localVector: Vector3, result: Vector3) + { + quaternion.vmult(localVector, result); + + return result; + } + + static vectorToLocalFrame(position: Vector3, quaternion: Quaternion, worldVector: Vector3, result = new Vector3()) + { + quaternion.w *= -1; + quaternion.vmult(worldVector, result); + quaternion.w *= -1; + + return result; + } +} + +const tmpQuat = new Quaternion(); + +const transformIntoFrameCorners = [ + new Vector3(), + new Vector3(), + new Vector3(), + new Vector3(), + new Vector3(), + new Vector3(), + new Vector3(), + new Vector3() +]; diff --git a/src/math/Vec3.js b/src/math/Vec3.js deleted file mode 100644 index 444f1cd21..000000000 --- a/src/math/Vec3.js +++ /dev/null @@ -1,483 +0,0 @@ -module.exports = Vec3; - -var Mat3 = require('./Mat3'); - -/** - * 3-dimensional vector - * @class Vec3 - * @constructor - * @param {Number} x - * @param {Number} y - * @param {Number} z - * @author schteppe - * @example - * var v = new Vec3(1, 2, 3); - * console.log('x=' + v.x); // x=1 - */ -function Vec3(x,y,z){ - /** - * @property x - * @type {Number} - */ - this.x = x||0.0; - - /** - * @property y - * @type {Number} - */ - this.y = y||0.0; - - /** - * @property z - * @type {Number} - */ - this.z = z||0.0; -} - -/** - * @static - * @property {Vec3} ZERO - */ -Vec3.ZERO = new Vec3(0, 0, 0); - -/** - * @static - * @property {Vec3} UNIT_X - */ -Vec3.UNIT_X = new Vec3(1, 0, 0); - -/** - * @static - * @property {Vec3} UNIT_Y - */ -Vec3.UNIT_Y = new Vec3(0, 1, 0); - -/** - * @static - * @property {Vec3} UNIT_Z - */ -Vec3.UNIT_Z = new Vec3(0, 0, 1); - -/** - * Vector cross product - * @method cross - * @param {Vec3} v - * @param {Vec3} target Optional. Target to save in. - * @return {Vec3} - */ -Vec3.prototype.cross = function(v,target){ - var vx=v.x, vy=v.y, vz=v.z, x=this.x, y=this.y, z=this.z; - target = target || new Vec3(); - - target.x = (y * vz) - (z * vy); - target.y = (z * vx) - (x * vz); - target.z = (x * vy) - (y * vx); - - return target; -}; - -/** - * Set the vectors' 3 elements - * @method set - * @param {Number} x - * @param {Number} y - * @param {Number} z - * @return Vec3 - */ -Vec3.prototype.set = function(x,y,z){ - this.x = x; - this.y = y; - this.z = z; - return this; -}; - -/** - * Set all components of the vector to zero. - * @method setZero - */ -Vec3.prototype.setZero = function(){ - this.x = this.y = this.z = 0; -}; - -/** - * Vector addition - * @method vadd - * @param {Vec3} v - * @param {Vec3} target Optional. - * @return {Vec3} - */ -Vec3.prototype.vadd = function(v,target){ - if(target){ - target.x = v.x + this.x; - target.y = v.y + this.y; - target.z = v.z + this.z; - } else { - return new Vec3(this.x + v.x, - this.y + v.y, - this.z + v.z); - } -}; - -/** - * Vector subtraction - * @method vsub - * @param {Vec3} v - * @param {Vec3} target Optional. Target to save in. - * @return {Vec3} - */ -Vec3.prototype.vsub = function(v,target){ - if(target){ - target.x = this.x - v.x; - target.y = this.y - v.y; - target.z = this.z - v.z; - } else { - return new Vec3(this.x-v.x, - this.y-v.y, - this.z-v.z); - } -}; - -/** - * Get the cross product matrix a_cross from a vector, such that a x b = a_cross * b = c - * @method crossmat - * @see http://www8.cs.umu.se/kurser/TDBD24/VT06/lectures/Lecture6.pdf - * @return {Mat3} - */ -Vec3.prototype.crossmat = function(){ - return new Mat3([ 0, -this.z, this.y, - this.z, 0, -this.x, - -this.y, this.x, 0]); -}; - -/** - * Normalize the vector. Note that this changes the values in the vector. - * @method normalize - * @return {Number} Returns the norm of the vector - */ -Vec3.prototype.normalize = function(){ - var x=this.x, y=this.y, z=this.z; - var n = Math.sqrt(x*x + y*y + z*z); - if(n>0.0){ - var invN = 1/n; - this.x *= invN; - this.y *= invN; - this.z *= invN; - } else { - // Make something up - this.x = 0; - this.y = 0; - this.z = 0; - } - return n; -}; - -/** - * Get the version of this vector that is of length 1. - * @method unit - * @param {Vec3} target Optional target to save in - * @return {Vec3} Returns the unit vector - */ -Vec3.prototype.unit = function(target){ - target = target || new Vec3(); - var x=this.x, y=this.y, z=this.z; - var ninv = Math.sqrt(x*x + y*y + z*z); - if(ninv>0.0){ - ninv = 1.0/ninv; - target.x = x * ninv; - target.y = y * ninv; - target.z = z * ninv; - } else { - target.x = 1; - target.y = 0; - target.z = 0; - } - return target; -}; - -/** - * Get the length of the vector - * @method norm - * @return {Number} - * @deprecated Use .length() instead - */ -Vec3.prototype.norm = function(){ - var x=this.x, y=this.y, z=this.z; - return Math.sqrt(x*x + y*y + z*z); -}; - -/** - * Get the length of the vector - * @method length - * @return {Number} - */ -Vec3.prototype.length = Vec3.prototype.norm; - -/** - * Get the squared length of the vector - * @method norm2 - * @return {Number} - * @deprecated Use .lengthSquared() instead. - */ -Vec3.prototype.norm2 = function(){ - return this.dot(this); -}; - -/** - * Get the squared length of the vector. - * @method lengthSquared - * @return {Number} - */ -Vec3.prototype.lengthSquared = Vec3.prototype.norm2; - -/** - * Get distance from this point to another point - * @method distanceTo - * @param {Vec3} p - * @return {Number} - */ -Vec3.prototype.distanceTo = function(p){ - var x=this.x, y=this.y, z=this.z; - var px=p.x, py=p.y, pz=p.z; - return Math.sqrt((px-x)*(px-x)+ - (py-y)*(py-y)+ - (pz-z)*(pz-z)); -}; - -/** - * Get squared distance from this point to another point - * @method distanceSquared - * @param {Vec3} p - * @return {Number} - */ -Vec3.prototype.distanceSquared = function(p){ - var x=this.x, y=this.y, z=this.z; - var px=p.x, py=p.y, pz=p.z; - return (px-x)*(px-x) + (py-y)*(py-y) + (pz-z)*(pz-z); -}; - -/** - * Multiply all the components of the vector with a scalar. - * @deprecated Use .scale instead - * @method mult - * @param {Number} scalar - * @param {Vec3} target The vector to save the result in. - * @return {Vec3} - * @deprecated Use .scale() instead - */ -Vec3.prototype.mult = function(scalar,target){ - target = target || new Vec3(); - var x = this.x, - y = this.y, - z = this.z; - target.x = scalar * x; - target.y = scalar * y; - target.z = scalar * z; - return target; -}; - -/** - * Multiply the vector with an other vector, component-wise. - * @method mult - * @param {Number} vector - * @param {Vec3} target The vector to save the result in. - * @return {Vec3} - */ -Vec3.prototype.vmul = function(vector, target){ - target = target || new Vec3(); - target.x = vector.x * this.x; - target.y = vector.y * this.y; - target.z = vector.z * this.z; - return target; -}; - -/** - * Multiply the vector with a scalar. - * @method scale - * @param {Number} scalar - * @param {Vec3} target - * @return {Vec3} - */ -Vec3.prototype.scale = Vec3.prototype.mult; - -/** - * Scale a vector and add it to this vector. Save the result in "target". (target = this + vector * scalar) - * @method addScaledVector - * @param {Number} scalar - * @param {Vec3} vector - * @param {Vec3} target The vector to save the result in. - * @return {Vec3} - */ -Vec3.prototype.addScaledVector = function(scalar, vector, target){ - target = target || new Vec3(); - target.x = this.x + scalar * vector.x; - target.y = this.y + scalar * vector.y; - target.z = this.z + scalar * vector.z; - return target; -}; - -/** - * Calculate dot product - * @method dot - * @param {Vec3} v - * @return {Number} - */ -Vec3.prototype.dot = function(v){ - return this.x * v.x + this.y * v.y + this.z * v.z; -}; - -/** - * @method isZero - * @return bool - */ -Vec3.prototype.isZero = function(){ - return this.x===0 && this.y===0 && this.z===0; -}; - -/** - * Make the vector point in the opposite direction. - * @method negate - * @param {Vec3} target Optional target to save in - * @return {Vec3} - */ -Vec3.prototype.negate = function(target){ - target = target || new Vec3(); - target.x = -this.x; - target.y = -this.y; - target.z = -this.z; - return target; -}; - -/** - * Compute two artificial tangents to the vector - * @method tangents - * @param {Vec3} t1 Vector object to save the first tangent in - * @param {Vec3} t2 Vector object to save the second tangent in - */ -var Vec3_tangents_n = new Vec3(); -var Vec3_tangents_randVec = new Vec3(); -Vec3.prototype.tangents = function(t1,t2){ - var norm = this.norm(); - if(norm>0.0){ - var n = Vec3_tangents_n; - var inorm = 1/norm; - n.set(this.x*inorm,this.y*inorm,this.z*inorm); - var randVec = Vec3_tangents_randVec; - if(Math.abs(n.x) < 0.9){ - randVec.set(1,0,0); - n.cross(randVec,t1); - } else { - randVec.set(0,1,0); - n.cross(randVec,t1); - } - n.cross(t1,t2); - } else { - // The normal length is zero, make something up - t1.set(1, 0, 0); - t2.set(0, 1, 0); - } -}; - -/** - * Converts to a more readable format - * @method toString - * @return string - */ -Vec3.prototype.toString = function(){ - return this.x+","+this.y+","+this.z; -}; - -/** - * Converts to an array - * @method toArray - * @return Array - */ -Vec3.prototype.toArray = function(){ - return [this.x, this.y, this.z]; -}; - -/** - * Copies value of source to this vector. - * @method copy - * @param {Vec3} source - * @return {Vec3} this - */ -Vec3.prototype.copy = function(source){ - this.x = source.x; - this.y = source.y; - this.z = source.z; - return this; -}; - - -/** - * Do a linear interpolation between two vectors - * @method lerp - * @param {Vec3} v - * @param {Number} t A number between 0 and 1. 0 will make this function return u, and 1 will make it return v. Numbers in between will generate a vector in between them. - * @param {Vec3} target - */ -Vec3.prototype.lerp = function(v,t,target){ - var x=this.x, y=this.y, z=this.z; - target.x = x + (v.x-x)*t; - target.y = y + (v.y-y)*t; - target.z = z + (v.z-z)*t; -}; - -/** - * Check if a vector equals is almost equal to another one. - * @method almostEquals - * @param {Vec3} v - * @param {Number} precision - * @return bool - */ -Vec3.prototype.almostEquals = function(v,precision){ - if(precision===undefined){ - precision = 1e-6; - } - if( Math.abs(this.x-v.x)>precision || - Math.abs(this.y-v.y)>precision || - Math.abs(this.z-v.z)>precision){ - return false; - } - return true; -}; - -/** - * Check if a vector is almost zero - * @method almostZero - * @param {Number} precision - */ -Vec3.prototype.almostZero = function(precision){ - if(precision===undefined){ - precision = 1e-6; - } - if( Math.abs(this.x)>precision || - Math.abs(this.y)>precision || - Math.abs(this.z)>precision){ - return false; - } - return true; -}; - -var antip_neg = new Vec3(); - -/** - * Check if the vector is anti-parallel to another vector. - * @method isAntiparallelTo - * @param {Vec3} v - * @param {Number} precision Set to zero for exact comparisons - * @return {Boolean} - */ -Vec3.prototype.isAntiparallelTo = function(v,precision){ - this.negate(antip_neg); - return antip_neg.almostEquals(v,precision); -}; - -/** - * Clone the vector - * @method clone - * @return {Vec3} - */ -Vec3.prototype.clone = function(){ - return new Vec3(this.x, this.y, this.z); -}; \ No newline at end of file diff --git a/src/objects/Body.js b/src/objects/Body.js deleted file mode 100644 index 671905db3..000000000 --- a/src/objects/Body.js +++ /dev/null @@ -1,928 +0,0 @@ -module.exports = Body; - -var EventTarget = require('../utils/EventTarget'); -var Shape = require('../shapes/Shape'); -var Vec3 = require('../math/Vec3'); -var Mat3 = require('../math/Mat3'); -var Quaternion = require('../math/Quaternion'); -var Material = require('../material/Material'); -var AABB = require('../collision/AABB'); -var Box = require('../shapes/Box'); - -/** - * Base class for all body types. - * @class Body - * @constructor - * @extends EventTarget - * @param {object} [options] - * @param {Vec3} [options.position] - * @param {Vec3} [options.velocity] - * @param {Vec3} [options.angularVelocity] - * @param {Quaternion} [options.quaternion] - * @param {number} [options.mass] - * @param {Material} [options.material] - * @param {number} [options.type] - * @param {number} [options.linearDamping=0.01] - * @param {number} [options.angularDamping=0.01] - * @param {boolean} [options.allowSleep=true] - * @param {number} [options.sleepSpeedLimit=0.1] - * @param {number} [options.sleepTimeLimit=1] - * @param {number} [options.collisionFilterGroup=1] - * @param {number} [options.collisionFilterMask=-1] - * @param {boolean} [options.fixedRotation=false] - * @param {Vec3} [options.linearFactor] - * @param {Vec3} [options.angularFactor] - * @param {Shape} [options.shape] - * @example - * var body = new Body({ - * mass: 1 - * }); - * var shape = new Sphere(1); - * body.addShape(shape); - * world.addBody(body); - */ -function Body(options){ - options = options || {}; - - EventTarget.apply(this); - - this.id = Body.idCounter++; - - /** - * Reference to the world the body is living in - * @property world - * @type {World} - */ - this.world = null; - - /** - * Callback function that is used BEFORE stepping the system. Use it to apply forces, for example. Inside the function, "this" will refer to this Body object. - * @property preStep - * @type {Function} - * @deprecated Use World events instead - */ - this.preStep = null; - - /** - * Callback function that is used AFTER stepping the system. Inside the function, "this" will refer to this Body object. - * @property postStep - * @type {Function} - * @deprecated Use World events instead - */ - this.postStep = null; - - this.vlambda = new Vec3(); - - /** - * @property {Number} collisionFilterGroup - */ - this.collisionFilterGroup = typeof(options.collisionFilterGroup) === 'number' ? options.collisionFilterGroup : 1; - - /** - * @property {Number} collisionFilterMask - */ - this.collisionFilterMask = typeof(options.collisionFilterMask) === 'number' ? options.collisionFilterMask : -1; - - /** - * Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled. - * @property {Number} collisionResponse - */ - this.collisionResponse = true; - - /** - * World space position of the body. - * @property position - * @type {Vec3} - */ - this.position = new Vec3(); - - /** - * @property {Vec3} previousPosition - */ - this.previousPosition = new Vec3(); - - /** - * Interpolated position of the body. - * @property {Vec3} interpolatedPosition - */ - this.interpolatedPosition = new Vec3(); - - /** - * Initial position of the body - * @property initPosition - * @type {Vec3} - */ - this.initPosition = new Vec3(); - - if(options.position){ - this.position.copy(options.position); - this.previousPosition.copy(options.position); - this.interpolatedPosition.copy(options.position); - this.initPosition.copy(options.position); - } - - /** - * World space velocity of the body. - * @property velocity - * @type {Vec3} - */ - this.velocity = new Vec3(); - - if(options.velocity){ - this.velocity.copy(options.velocity); - } - - /** - * @property initVelocity - * @type {Vec3} - */ - this.initVelocity = new Vec3(); - - /** - * Linear force on the body in world space. - * @property force - * @type {Vec3} - */ - this.force = new Vec3(); - - var mass = typeof(options.mass) === 'number' ? options.mass : 0; - - /** - * @property mass - * @type {Number} - * @default 0 - */ - this.mass = mass; - - /** - * @property invMass - * @type {Number} - */ - this.invMass = mass > 0 ? 1.0 / mass : 0; - - /** - * @property material - * @type {Material} - */ - this.material = options.material || null; - - /** - * @property linearDamping - * @type {Number} - */ - this.linearDamping = typeof(options.linearDamping) === 'number' ? options.linearDamping : 0.01; - - /** - * One of: Body.DYNAMIC, Body.STATIC and Body.KINEMATIC. - * @property type - * @type {Number} - */ - this.type = (mass <= 0.0 ? Body.STATIC : Body.DYNAMIC); - if(typeof(options.type) === typeof(Body.STATIC)){ - this.type = options.type; - } - - /** - * If true, the body will automatically fall to sleep. - * @property allowSleep - * @type {Boolean} - * @default true - */ - this.allowSleep = typeof(options.allowSleep) !== 'undefined' ? options.allowSleep : true; - - /** - * Current sleep state. - * @property sleepState - * @type {Number} - */ - this.sleepState = 0; - - /** - * If the speed (the norm of the velocity) is smaller than this value, the body is considered sleepy. - * @property sleepSpeedLimit - * @type {Number} - * @default 0.1 - */ - this.sleepSpeedLimit = typeof(options.sleepSpeedLimit) !== 'undefined' ? options.sleepSpeedLimit : 0.1; - - /** - * If the body has been sleepy for this sleepTimeLimit seconds, it is considered sleeping. - * @property sleepTimeLimit - * @type {Number} - * @default 1 - */ - this.sleepTimeLimit = typeof(options.sleepTimeLimit) !== 'undefined' ? options.sleepTimeLimit : 1; - - this.timeLastSleepy = 0; - - this._wakeUpAfterNarrowphase = false; - - /** - * World space rotational force on the body, around center of mass. - * @property {Vec3} torque - */ - this.torque = new Vec3(); - - /** - * World space orientation of the body. - * @property quaternion - * @type {Quaternion} - */ - this.quaternion = new Quaternion(); - - /** - * @property initQuaternion - * @type {Quaternion} - */ - this.initQuaternion = new Quaternion(); - - /** - * @property {Quaternion} previousQuaternion - */ - this.previousQuaternion = new Quaternion(); - - /** - * Interpolated orientation of the body. - * @property {Quaternion} interpolatedQuaternion - */ - this.interpolatedQuaternion = new Quaternion(); - - if(options.quaternion){ - this.quaternion.copy(options.quaternion); - this.initQuaternion.copy(options.quaternion); - this.previousQuaternion.copy(options.quaternion); - this.interpolatedQuaternion.copy(options.quaternion); - } - - /** - * Angular velocity of the body, in world space. Think of the angular velocity as a vector, which the body rotates around. The length of this vector determines how fast (in radians per second) the body rotates. - * @property angularVelocity - * @type {Vec3} - */ - this.angularVelocity = new Vec3(); - - if(options.angularVelocity){ - this.angularVelocity.copy(options.angularVelocity); - } - - /** - * @property initAngularVelocity - * @type {Vec3} - */ - this.initAngularVelocity = new Vec3(); - - /** - * @property shapes - * @type {array} - */ - this.shapes = []; - - /** - * Position of each Shape in the body, given in local Body space. - * @property shapeOffsets - * @type {array} - */ - this.shapeOffsets = []; - - /** - * Orientation of each Shape, given in local Body space. - * @property shapeOrientations - * @type {array} - */ - this.shapeOrientations = []; - - /** - * @property inertia - * @type {Vec3} - */ - this.inertia = new Vec3(); - - /** - * @property {Vec3} invInertia - */ - this.invInertia = new Vec3(); - - /** - * @property {Mat3} invInertiaWorld - */ - this.invInertiaWorld = new Mat3(); - - this.invMassSolve = 0; - - /** - * @property {Vec3} invInertiaSolve - */ - this.invInertiaSolve = new Vec3(); - - /** - * @property {Mat3} invInertiaWorldSolve - */ - this.invInertiaWorldSolve = new Mat3(); - - /** - * Set to true if you don't want the body to rotate. Make sure to run .updateMassProperties() after changing this. - * @property {Boolean} fixedRotation - * @default false - */ - this.fixedRotation = typeof(options.fixedRotation) !== "undefined" ? options.fixedRotation : false; - - /** - * @property {Number} angularDamping - */ - this.angularDamping = typeof(options.angularDamping) !== 'undefined' ? options.angularDamping : 0.01; - - /** - * Use this property to limit the motion along any world axis. (1,1,1) will allow motion along all axes while (0,0,0) allows none. - * @property {Vec3} linearFactor - */ - this.linearFactor = new Vec3(1,1,1); - if(options.linearFactor){ - this.linearFactor.copy(options.linearFactor); - } - - /** - * Use this property to limit the rotational motion along any world axis. (1,1,1) will allow rotation along all axes while (0,0,0) allows none. - * @property {Vec3} angularFactor - */ - this.angularFactor = new Vec3(1,1,1); - if(options.angularFactor){ - this.angularFactor.copy(options.angularFactor); - } - - /** - * World space bounding box of the body and its shapes. - * @property aabb - * @type {AABB} - */ - this.aabb = new AABB(); - - /** - * Indicates if the AABB needs to be updated before use. - * @property aabbNeedsUpdate - * @type {Boolean} - */ - this.aabbNeedsUpdate = true; - - /** - * Total bounding radius of the Body including its shapes, relative to body.position. - * @property boundingRadius - * @type {Number} - */ - this.boundingRadius = 0; - - this.wlambda = new Vec3(); - - if(options.shape){ - this.addShape(options.shape); - } - - this.updateMassProperties(); -} -Body.prototype = new EventTarget(); -Body.prototype.constructor = Body; - -/** - * Dispatched after two bodies collide. This event is dispatched on each - * of the two bodies involved in the collision. - * @event collide - * @param {Body} body The body that was involved in the collision. - * @param {ContactEquation} contact The details of the collision. - */ -Body.COLLIDE_EVENT_NAME = "collide"; - -/** - * A dynamic body is fully simulated. Can be moved manually by the user, but normally they move according to forces. A dynamic body can collide with all body types. A dynamic body always has finite, non-zero mass. - * @static - * @property DYNAMIC - * @type {Number} - */ -Body.DYNAMIC = 1; - -/** - * A static body does not move during simulation and behaves as if it has infinite mass. Static bodies can be moved manually by setting the position of the body. The velocity of a static body is always zero. Static bodies do not collide with other static or kinematic bodies. - * @static - * @property STATIC - * @type {Number} - */ -Body.STATIC = 2; - -/** - * A kinematic body moves under simulation according to its velocity. They do not respond to forces. They can be moved manually, but normally a kinematic body is moved by setting its velocity. A kinematic body behaves as if it has infinite mass. Kinematic bodies do not collide with other static or kinematic bodies. - * @static - * @property KINEMATIC - * @type {Number} - */ -Body.KINEMATIC = 4; - - - -/** - * @static - * @property AWAKE - * @type {number} - */ -Body.AWAKE = 0; - -/** - * @static - * @property SLEEPY - * @type {number} - */ -Body.SLEEPY = 1; - -/** - * @static - * @property SLEEPING - * @type {number} - */ -Body.SLEEPING = 2; - -Body.idCounter = 0; - -/** - * Dispatched after a sleeping body has woken up. - * @event wakeup - */ -Body.wakeupEvent = { - type: "wakeup" -}; - -/** - * Wake the body up. - * @method wakeUp - */ -Body.prototype.wakeUp = function(){ - var s = this.sleepState; - this.sleepState = 0; - this._wakeUpAfterNarrowphase = false; - if(s === Body.SLEEPING){ - this.dispatchEvent(Body.wakeupEvent); - } -}; - -/** - * Force body sleep - * @method sleep - */ -Body.prototype.sleep = function(){ - this.sleepState = Body.SLEEPING; - this.velocity.set(0,0,0); - this.angularVelocity.set(0,0,0); - this._wakeUpAfterNarrowphase = false; -}; - -/** - * Dispatched after a body has gone in to the sleepy state. - * @event sleepy - */ -Body.sleepyEvent = { - type: "sleepy" -}; - -/** - * Dispatched after a body has fallen asleep. - * @event sleep - */ -Body.sleepEvent = { - type: "sleep" -}; - -/** - * Called every timestep to update internal sleep timer and change sleep state if needed. - * @method sleepTick - * @param {Number} time The world time in seconds - */ -Body.prototype.sleepTick = function(time){ - if(this.allowSleep){ - var sleepState = this.sleepState; - var speedSquared = this.velocity.norm2() + this.angularVelocity.norm2(); - var speedLimitSquared = Math.pow(this.sleepSpeedLimit,2); - if(sleepState===Body.AWAKE && speedSquared < speedLimitSquared){ - this.sleepState = Body.SLEEPY; // Sleepy - this.timeLastSleepy = time; - this.dispatchEvent(Body.sleepyEvent); - } else if(sleepState===Body.SLEEPY && speedSquared > speedLimitSquared){ - this.wakeUp(); // Wake up - } else if(sleepState===Body.SLEEPY && (time - this.timeLastSleepy ) > this.sleepTimeLimit){ - this.sleep(); // Sleeping - this.dispatchEvent(Body.sleepEvent); - } - } -}; - -/** - * If the body is sleeping, it should be immovable / have infinite mass during solve. We solve it by having a separate "solve mass". - * @method updateSolveMassProperties - */ -Body.prototype.updateSolveMassProperties = function(){ - if(this.sleepState === Body.SLEEPING || this.type === Body.KINEMATIC){ - this.invMassSolve = 0; - this.invInertiaSolve.setZero(); - this.invInertiaWorldSolve.setZero(); - } else { - this.invMassSolve = this.invMass; - this.invInertiaSolve.copy(this.invInertia); - this.invInertiaWorldSolve.copy(this.invInertiaWorld); - } -}; - -/** - * Convert a world point to local body frame. - * @method pointToLocalFrame - * @param {Vec3} worldPoint - * @param {Vec3} result - * @return {Vec3} - */ -Body.prototype.pointToLocalFrame = function(worldPoint,result){ - var result = result || new Vec3(); - worldPoint.vsub(this.position,result); - this.quaternion.conjugate().vmult(result,result); - return result; -}; - -/** - * Convert a world vector to local body frame. - * @method vectorToLocalFrame - * @param {Vec3} worldPoint - * @param {Vec3} result - * @return {Vec3} - */ -Body.prototype.vectorToLocalFrame = function(worldVector, result){ - var result = result || new Vec3(); - this.quaternion.conjugate().vmult(worldVector,result); - return result; -}; - -/** - * Convert a local body point to world frame. - * @method pointToWorldFrame - * @param {Vec3} localPoint - * @param {Vec3} result - * @return {Vec3} - */ -Body.prototype.pointToWorldFrame = function(localPoint,result){ - var result = result || new Vec3(); - this.quaternion.vmult(localPoint,result); - result.vadd(this.position,result); - return result; -}; - -/** - * Convert a local body point to world frame. - * @method vectorToWorldFrame - * @param {Vec3} localVector - * @param {Vec3} result - * @return {Vec3} - */ -Body.prototype.vectorToWorldFrame = function(localVector, result){ - var result = result || new Vec3(); - this.quaternion.vmult(localVector, result); - return result; -}; - -var tmpVec = new Vec3(); -var tmpQuat = new Quaternion(); - -/** - * Add a shape to the body with a local offset and orientation. - * @method addShape - * @param {Shape} shape - * @param {Vec3} [_offset] - * @param {Quaternion} [_orientation] - * @return {Body} The body object, for chainability. - */ -Body.prototype.addShape = function(shape, _offset, _orientation){ - var offset = new Vec3(); - var orientation = new Quaternion(); - - if(_offset){ - offset.copy(_offset); - } - if(_orientation){ - orientation.copy(_orientation); - } - - this.shapes.push(shape); - this.shapeOffsets.push(offset); - this.shapeOrientations.push(orientation); - this.updateMassProperties(); - this.updateBoundingRadius(); - - this.aabbNeedsUpdate = true; - - shape.body = this; - - return this; -}; - -/** - * Update the bounding radius of the body. Should be done if any of the shapes are changed. - * @method updateBoundingRadius - */ -Body.prototype.updateBoundingRadius = function(){ - var shapes = this.shapes, - shapeOffsets = this.shapeOffsets, - N = shapes.length, - radius = 0; - - for(var i=0; i!==N; i++){ - var shape = shapes[i]; - shape.updateBoundingSphereRadius(); - var offset = shapeOffsets[i].norm(), - r = shape.boundingSphereRadius; - if(offset + r > radius){ - radius = offset + r; - } - } - - this.boundingRadius = radius; -}; - -var computeAABB_shapeAABB = new AABB(); - -/** - * Updates the .aabb - * @method computeAABB - * @todo rename to updateAABB() - */ -Body.prototype.computeAABB = function(){ - var shapes = this.shapes, - shapeOffsets = this.shapeOffsets, - shapeOrientations = this.shapeOrientations, - N = shapes.length, - offset = tmpVec, - orientation = tmpQuat, - bodyQuat = this.quaternion, - aabb = this.aabb, - shapeAABB = computeAABB_shapeAABB; - - for(var i=0; i!==N; i++){ - var shape = shapes[i]; - - // Get shape world position - bodyQuat.vmult(shapeOffsets[i], offset); - offset.vadd(this.position, offset); - - // Get shape world quaternion - shapeOrientations[i].mult(bodyQuat, orientation); - - // Get shape AABB - shape.calculateWorldAABB(offset, orientation, shapeAABB.lowerBound, shapeAABB.upperBound); - - if(i === 0){ - aabb.copy(shapeAABB); - } else { - aabb.extend(shapeAABB); - } - } - - this.aabbNeedsUpdate = false; -}; - -var uiw_m1 = new Mat3(), - uiw_m2 = new Mat3(), - uiw_m3 = new Mat3(); - -/** - * Update .inertiaWorld and .invInertiaWorld - * @method updateInertiaWorld - */ -Body.prototype.updateInertiaWorld = function(force){ - var I = this.invInertia; - if (I.x === I.y && I.y === I.z && !force) { - // If inertia M = s*I, where I is identity and s a scalar, then - // R*M*R' = R*(s*I)*R' = s*R*I*R' = s*R*R' = s*I = M - // where R is the rotation matrix. - // In other words, we don't have to transform the inertia if all - // inertia diagonal entries are equal. - } else { - var m1 = uiw_m1, - m2 = uiw_m2, - m3 = uiw_m3; - m1.setRotationFromQuaternion(this.quaternion); - m1.transpose(m2); - m1.scale(I,m1); - m1.mmult(m2,this.invInertiaWorld); - } -}; - -/** - * Apply force to a world point. This could for example be a point on the Body surface. Applying force this way will add to Body.force and Body.torque. - * @method applyForce - * @param {Vec3} force The amount of force to add. - * @param {Vec3} relativePoint A point relative to the center of mass to apply the force on. - */ -var Body_applyForce_r = new Vec3(); -var Body_applyForce_rotForce = new Vec3(); -Body.prototype.applyForce = function(force,relativePoint){ - if(this.type !== Body.DYNAMIC){ // Needed? - return; - } - - // Compute produced rotational force - var rotForce = Body_applyForce_rotForce; - relativePoint.cross(force,rotForce); - - // Add linear force - this.force.vadd(force,this.force); - - // Add rotational force - this.torque.vadd(rotForce,this.torque); -}; - -/** - * Apply force to a local point in the body. - * @method applyLocalForce - * @param {Vec3} force The force vector to apply, defined locally in the body frame. - * @param {Vec3} localPoint A local point in the body to apply the force on. - */ -var Body_applyLocalForce_worldForce = new Vec3(); -var Body_applyLocalForce_relativePointWorld = new Vec3(); -Body.prototype.applyLocalForce = function(localForce, localPoint){ - if(this.type !== Body.DYNAMIC){ - return; - } - - var worldForce = Body_applyLocalForce_worldForce; - var relativePointWorld = Body_applyLocalForce_relativePointWorld; - - // Transform the force vector to world space - this.vectorToWorldFrame(localForce, worldForce); - this.vectorToWorldFrame(localPoint, relativePointWorld); - - this.applyForce(worldForce, relativePointWorld); -}; - -/** - * Apply impulse to a world point. This could for example be a point on the Body surface. An impulse is a force added to a body during a short period of time (impulse = force * time). Impulses will be added to Body.velocity and Body.angularVelocity. - * @method applyImpulse - * @param {Vec3} impulse The amount of impulse to add. - * @param {Vec3} relativePoint A point relative to the center of mass to apply the force on. - */ -var Body_applyImpulse_r = new Vec3(); -var Body_applyImpulse_velo = new Vec3(); -var Body_applyImpulse_rotVelo = new Vec3(); -Body.prototype.applyImpulse = function(impulse, relativePoint){ - if(this.type !== Body.DYNAMIC){ - return; - } - - // Compute point position relative to the body center - var r = relativePoint; - - // Compute produced central impulse velocity - var velo = Body_applyImpulse_velo; - velo.copy(impulse); - velo.mult(this.invMass,velo); - - // Add linear impulse - this.velocity.vadd(velo, this.velocity); - - // Compute produced rotational impulse velocity - var rotVelo = Body_applyImpulse_rotVelo; - r.cross(impulse,rotVelo); - - /* - rotVelo.x *= this.invInertia.x; - rotVelo.y *= this.invInertia.y; - rotVelo.z *= this.invInertia.z; - */ - this.invInertiaWorld.vmult(rotVelo,rotVelo); - - // Add rotational Impulse - this.angularVelocity.vadd(rotVelo, this.angularVelocity); -}; - -/** - * Apply locally-defined impulse to a local point in the body. - * @method applyLocalImpulse - * @param {Vec3} force The force vector to apply, defined locally in the body frame. - * @param {Vec3} localPoint A local point in the body to apply the force on. - */ -var Body_applyLocalImpulse_worldImpulse = new Vec3(); -var Body_applyLocalImpulse_relativePoint = new Vec3(); -Body.prototype.applyLocalImpulse = function(localImpulse, localPoint){ - if(this.type !== Body.DYNAMIC){ - return; - } - - var worldImpulse = Body_applyLocalImpulse_worldImpulse; - var relativePointWorld = Body_applyLocalImpulse_relativePoint; - - // Transform the force vector to world space - this.vectorToWorldFrame(localImpulse, worldImpulse); - this.vectorToWorldFrame(localPoint, relativePointWorld); - - this.applyImpulse(worldImpulse, relativePointWorld); -}; - -var Body_updateMassProperties_halfExtents = new Vec3(); - -/** - * Should be called whenever you change the body shape or mass. - * @method updateMassProperties - */ -Body.prototype.updateMassProperties = function(){ - var halfExtents = Body_updateMassProperties_halfExtents; - - this.invMass = this.mass > 0 ? 1.0 / this.mass : 0; - var I = this.inertia; - var fixed = this.fixedRotation; - - // Approximate with AABB box - this.computeAABB(); - halfExtents.set( - (this.aabb.upperBound.x-this.aabb.lowerBound.x) / 2, - (this.aabb.upperBound.y-this.aabb.lowerBound.y) / 2, - (this.aabb.upperBound.z-this.aabb.lowerBound.z) / 2 - ); - Box.calculateInertia(halfExtents, this.mass, I); - - this.invInertia.set( - I.x > 0 && !fixed ? 1.0 / I.x : 0, - I.y > 0 && !fixed ? 1.0 / I.y : 0, - I.z > 0 && !fixed ? 1.0 / I.z : 0 - ); - this.updateInertiaWorld(true); -}; - -/** - * Get world velocity of a point in the body. - * @method getVelocityAtWorldPoint - * @param {Vec3} worldPoint - * @param {Vec3} result - * @return {Vec3} The result vector. - */ -Body.prototype.getVelocityAtWorldPoint = function(worldPoint, result){ - var r = new Vec3(); - worldPoint.vsub(this.position, r); - this.angularVelocity.cross(r, result); - this.velocity.vadd(result, result); - return result; -}; - -var torque = new Vec3(); -var invI_tau_dt = new Vec3(); -var w = new Quaternion(); -var wq = new Quaternion(); - -/** - * Move the body forward in time. - * @param {number} dt Time step - * @param {boolean} quatNormalize Set to true to normalize the body quaternion - * @param {boolean} quatNormalizeFast If the quaternion should be normalized using "fast" quaternion normalization - */ -Body.prototype.integrate = function(dt, quatNormalize, quatNormalizeFast){ - - // Save previous position - this.previousPosition.copy(this.position); - this.previousQuaternion.copy(this.quaternion); - - if(!(this.type === Body.DYNAMIC || this.type === Body.KINEMATIC) || this.sleepState === Body.SLEEPING){ // Only for dynamic - return; - } - - var velo = this.velocity, - angularVelo = this.angularVelocity, - pos = this.position, - force = this.force, - torque = this.torque, - quat = this.quaternion, - invMass = this.invMass, - invInertia = this.invInertiaWorld, - linearFactor = this.linearFactor; - - var iMdt = invMass * dt; - velo.x += force.x * iMdt * linearFactor.x; - velo.y += force.y * iMdt * linearFactor.y; - velo.z += force.z * iMdt * linearFactor.z; - - var e = invInertia.elements; - var angularFactor = this.angularFactor; - var tx = torque.x * angularFactor.x; - var ty = torque.y * angularFactor.y; - var tz = torque.z * angularFactor.z; - angularVelo.x += dt * (e[0] * tx + e[1] * ty + e[2] * tz); - angularVelo.y += dt * (e[3] * tx + e[4] * ty + e[5] * tz); - angularVelo.z += dt * (e[6] * tx + e[7] * ty + e[8] * tz); - - // Use new velocity - leap frog - pos.x += velo.x * dt; - pos.y += velo.y * dt; - pos.z += velo.z * dt; - - quat.integrate(this.angularVelocity, dt, this.angularFactor, quat); - - if(quatNormalize){ - if(quatNormalizeFast){ - quat.normalizeFast(); - } else { - quat.normalize(); - } - } - - this.aabbNeedsUpdate = true; - - // Update world inertia - this.updateInertiaWorld(); -}; diff --git a/src/objects/Body.ts b/src/objects/Body.ts new file mode 100644 index 000000000..b5ee3d4ee --- /dev/null +++ b/src/objects/Body.ts @@ -0,0 +1,841 @@ +import { EventEmitter } from 'feng3d'; +import { Box3, Matrix3x3, Quaternion, Vector3 } from 'feng3d'; +import { ContactEquation } from '../equations/ContactEquation'; +import { Material } from '../material/Material'; +import { Box } from '../shapes/Box'; +import { Shape } from '../shapes/Shape'; +import { World } from '../world/World'; + +export interface BodyEventMap +{ + wakeup: any + sleepy: any + sleep: any + + collide: { body: Body, contact: ContactEquation } +} + +export class Body extends EventEmitter +{ + id: number; + + /** + * Reference to the world the body is living in + */ + world: World; + + vlambda: Vector3; + + collisionFilterGroup: number; + + collisionFilterMask: number; + + /** + * Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled. + */ + collisionResponse: boolean; + + /** + * World space position of the body. + */ + position: Vector3; + + previousPosition: Vector3; + + /** + * Interpolated position of the body. + */ + interpolatedPosition: Vector3; + + /** + * Initial position of the body + */ + initPosition: Vector3; + + /** + * World space velocity of the body. + */ + velocity: Vector3; + + initVelocity: Vector3; + + /** + * Linear force on the body in world space. + */ + force: Vector3; + + mass: number; + + invMass: number; + + material: Material; + + linearDamping: number; + + /** + * One of: Body.DYNAMIC, Body.STATIC and Body.KINEMATIC. + */ + type: number; + + /** + * If true, the body will automatically fall to sleep. + */ + allowSleep: boolean; + + /** + * Current sleep state. + */ + sleepState: number; + + /** + * If the speed (the norm of the velocity) is smaller than this value, the body is considered sleepy. + */ + sleepSpeedLimit: number; + + /** + * If the body has been sleepy for this sleepTimeLimit seconds, it is considered sleeping. + */ + sleepTimeLimit: number; + + timeLastSleepy: number; + + _wakeUpAfterNarrowphase: boolean; + + /** + * World space rotational force on the body, around center of mass. + */ + torque: Vector3; + + /** + * World space orientation of the body. + */ + quaternion: Quaternion; + + initQuaternion: Quaternion; + + previousQuaternion: Quaternion; + + /** + * Interpolated orientation of the body. + */ + interpolatedQuaternion: Quaternion; + + /** + * Angular velocity of the body, in world space. Think of the angular velocity as a vector, which the body rotates around. The length of this vector determines how fast (in radians per second) the body rotates. + */ + angularVelocity: Vector3; + + initAngularVelocity: Vector3; + + shapes: Shape[]; + + /** + * Position of each Shape in the body, given in local Body space. + */ + shapeOffsets: Vector3[]; + + /** + * Orientation of each Shape, given in local Body space. + */ + shapeOrientations: Quaternion[]; + + inertia: Vector3; + + invInertia: Vector3; + + invInertiaWorld: Matrix3x3; + + invMassSolve: number; + + invInertiaSolve: Vector3; + + invInertiaWorldSolve: Matrix3x3; + + /** + * Set to true if you don't want the body to rotate. Make sure to run .updateMassProperties() after changing this. + */ + fixedRotation: boolean; + + angularDamping: number; + + /** + * Use this property to limit the motion along any world axis. (1,1,1) will allow motion along all axes while (0,0,0) allows none. + */ + linearFactor: Vector3; + + /** + * Use this property to limit the rotational motion along any world axis. (1,1,1) will allow rotation along all axes while (0,0,0) allows none. + */ + angularFactor: Vector3; + + /** + * World space bounding box of the body and its shapes. + */ + aabb: Box3; + + /** + * Indicates if the AABB needs to be updated before use. + */ + aabbNeedsUpdate: boolean; + + /** + * Total bounding radius of the Body including its shapes, relative to body.position. + */ + boundingRadius: number; + + wlambda: Vector3; + + shape: Shape; + + index: number; + + /** + * Base class for all body types. + * + * @param options + * @param a + * + * @example + * let body = new Body({ + * mass: 1 + * }); + * let shape = new Sphere(1); + * body.addShape(shape); + * world.addBody(body); + */ + constructor(options: { + collisionFilterGroup?: number, collisionFilterMask?: number, position?: Vector3, velocity?: Vector3, + material?: Material, mass?: number, linearDamping?: number, type?: number, allowSleep?: boolean, + sleepSpeedLimit?: number, sleepTimeLimit?: number, quaternion?: Quaternion, angularVelocity?: Vector3, + fixedRotation?: boolean, angularDamping?: number, linearFactor?: Vector3, angularFactor?: Vector3, shape?: Shape, + } = {}) + { + super(); + + this.id = Body.idCounter++; + this.world = null; + this.vlambda = new Vector3(); + this.collisionFilterGroup = typeof (options.collisionFilterGroup) === 'number' ? options.collisionFilterGroup : 1; + this.collisionFilterMask = typeof (options.collisionFilterMask) === 'number' ? options.collisionFilterMask : -1; + this.collisionResponse = true; + this.position = new Vector3(); + this.previousPosition = new Vector3(); + this.interpolatedPosition = new Vector3(); + this.initPosition = new Vector3(); + + if (options.position) + { + this.position.copy(options.position); + this.previousPosition.copy(options.position); + this.interpolatedPosition.copy(options.position); + this.initPosition.copy(options.position); + } + this.velocity = new Vector3(); + + if (options.velocity) + { + this.velocity.copy(options.velocity); + } + + this.initVelocity = new Vector3(); + this.force = new Vector3(); + + const mass = typeof (options.mass) === 'number' ? options.mass : 0; + this.mass = mass; + this.invMass = mass > 0 ? 1.0 / mass : 0; + this.material = options.material || null; + this.linearDamping = typeof (options.linearDamping) === 'number' ? options.linearDamping : 0.01; + this.type = (mass <= 0.0 ? Body.STATIC : Body.DYNAMIC); + if (typeof (options.type) === typeof (Body.STATIC)) + { + this.type = options.type; + } + this.allowSleep = typeof (options.allowSleep) !== 'undefined' ? options.allowSleep : true; + this.sleepState = 0; + this.sleepSpeedLimit = typeof (options.sleepSpeedLimit) !== 'undefined' ? options.sleepSpeedLimit : 0.1; + this.sleepTimeLimit = typeof (options.sleepTimeLimit) !== 'undefined' ? options.sleepTimeLimit : 1; + + this.timeLastSleepy = 0; + + this._wakeUpAfterNarrowphase = false; + this.torque = new Vector3(); + this.quaternion = new Quaternion(); + this.initQuaternion = new Quaternion(); + this.previousQuaternion = new Quaternion(); + this.interpolatedQuaternion = new Quaternion(); + + if (options.quaternion) + { + this.quaternion.copy(options.quaternion); + this.initQuaternion.copy(options.quaternion); + this.previousQuaternion.copy(options.quaternion); + this.interpolatedQuaternion.copy(options.quaternion); + } + this.angularVelocity = new Vector3(); + + if (options.angularVelocity) + { + this.angularVelocity.copy(options.angularVelocity); + } + this.initAngularVelocity = new Vector3(); + this.shapes = []; + this.shapeOffsets = []; + this.shapeOrientations = []; + this.inertia = new Vector3(); + this.invInertia = new Vector3(); + this.invInertiaWorld = new Matrix3x3(); + + this.invMassSolve = 0; + this.invInertiaSolve = new Vector3(); + this.invInertiaWorldSolve = new Matrix3x3(); + this.fixedRotation = typeof (options.fixedRotation) !== 'undefined' ? options.fixedRotation : false; + this.angularDamping = typeof (options.angularDamping) !== 'undefined' ? options.angularDamping : 0.01; + this.linearFactor = new Vector3(1, 1, 1); + if (options.linearFactor) + { + this.linearFactor.copy(options.linearFactor); + } + this.angularFactor = new Vector3(1, 1, 1); + if (options.angularFactor) + { + this.angularFactor.copy(options.angularFactor); + } + this.aabb = new Box3(); + this.aabbNeedsUpdate = true; + this.boundingRadius = 0; + + this.wlambda = new Vector3(); + + if (options.shape) + { + this.addShape(options.shape); + } + + this.updateMassProperties(); + } + + /** + * A dynamic body is fully simulated. Can be moved manually by the user, but normally they move according to forces. A dynamic body can collide with all body types. A dynamic body always has finite, non-zero mass. + */ + static DYNAMIC = 1; + + /** + * A static body does not move during simulation and behaves as if it has infinite mass. Static bodies can be moved manually by setting the position of the body. The velocity of a static body is always zero. Static bodies do not collide with other static or kinematic bodies. + */ + static STATIC = 2; + + /** + * A kinematic body moves under simulation according to its velocity. They do not respond to forces. They can be moved manually, but normally a kinematic body is moved by setting its velocity. A kinematic body behaves as if it has infinite mass. Kinematic bodies do not collide with other static or kinematic bodies. + */ + static KINEMATIC = 4; + + static AWAKE = 0; + + static SLEEPY = 1; + + static SLEEPING = 2; + + static idCounter = 0; + + /** + * Wake the body up. + */ + wakeUp() + { + const s = this.sleepState; + this.sleepState = 0; + this._wakeUpAfterNarrowphase = false; + if (s === Body.SLEEPING) + { + this.emit('wakeup'); + } + } + + /** + * Force body sleep + */ + sleep() + { + this.sleepState = Body.SLEEPING; + this.velocity.set(0, 0, 0); + this.angularVelocity.set(0, 0, 0); + this._wakeUpAfterNarrowphase = false; + } + + /** + * Called every timestep to update internal sleep timer and change sleep state if needed. + */ + sleepTick(time: number) + { + if (this.allowSleep) + { + const sleepState = this.sleepState; + const speedSquared = this.velocity.lengthSquared + this.angularVelocity.lengthSquared; + const speedLimitSquared = Math.pow(this.sleepSpeedLimit, 2); + if (sleepState === Body.AWAKE && speedSquared < speedLimitSquared) + { + this.sleepState = Body.SLEEPY; // Sleepy + this.timeLastSleepy = time; + this.emit('sleepy'); + } + else if (sleepState === Body.SLEEPY && speedSquared > speedLimitSquared) + { + this.wakeUp(); // Wake up + } + else if (sleepState === Body.SLEEPY && (time - this.timeLastSleepy) > this.sleepTimeLimit) + { + this.sleep(); // Sleeping + this.emit('sleep'); + } + } + } + + /** + * If the body is sleeping, it should be immovable / have infinite mass during solve. We solve it by having a separate "solve mass". + */ + updateSolveMassProperties() + { + if (this.sleepState === Body.SLEEPING || this.type === Body.KINEMATIC) + { + this.invMassSolve = 0; + this.invInertiaSolve.setZero(); + this.invInertiaWorldSolve.setZero(); + } + else + { + this.invMassSolve = this.invMass; + this.invInertiaSolve.copy(this.invInertia); + this.invInertiaWorldSolve.copy(this.invInertiaWorld); + } + } + + /** + * Convert a world point to local body frame. + * + * @param worldPoint + * @param result + */ + pointToLocalFrame(worldPoint: Vector3, result = new Vector3()) + { + worldPoint.subTo(this.position, result); + this.quaternion.inverseTo().vmult(result, result); + + return result; + } + + /** + * Convert a world vector to local body frame. + * + * @param worldPoint + * @param result + */ + vectorToLocalFrame(worldVector, result = new Vector3()) + { + this.quaternion.inverseTo().vmult(worldVector, result); + + return result; + } + + /** + * Convert a local body point to world frame. + * + * @param localPoint + * @param result + */ + pointToWorldFrame(localPoint: Vector3, result = new Vector3()) + { + this.quaternion.vmult(localPoint, result); + result.addTo(this.position, result); + + return result; + } + + /** + * Convert a local body point to world frame. + * + * @param localVector + * @param result + */ + vectorToWorldFrame(localVector: Vector3, result = new Vector3()) + { + this.quaternion.vmult(localVector, result); + + return result; + } + + /** + * Add a shape to the body with a local offset and orientation. + * + * @param shape + * @param _offset + * @param_orientation + * @return The body object, for chainability. + */ + addShape(shape: Shape, _offset?: Vector3, _orientation?: Quaternion) + { + const offset = new Vector3(); + const orientation = new Quaternion(); + + if (_offset) + { + offset.copy(_offset); + } + if (_orientation) + { + orientation.copy(_orientation); + } + + this.shapes.push(shape); + this.shapeOffsets.push(offset); + this.shapeOrientations.push(orientation); + this.updateMassProperties(); + this.updateBoundingRadius(); + + this.aabbNeedsUpdate = true; + + shape.body = this; + + return this; + } + + /** + * Update the bounding radius of the body. Should be done if any of the shapes are changed. + */ + updateBoundingRadius() + { + const shapes = this.shapes; + const shapeOffsets = this.shapeOffsets; + const N = shapes.length; + let radius = 0; + + for (let i = 0; i !== N; i++) + { + const shape = shapes[i]; + shape.updateBoundingSphereRadius(); + const offset = shapeOffsets[i].length; + const r = shape.boundingSphereRadius; + if (offset + r > radius) + { + radius = offset + r; + } + } + + this.boundingRadius = radius; + } + + /** + * Updates the .aabb + * + * @todo rename to updateAABB() + */ + computeAABB() + { + const shapes = this.shapes; + const shapeOffsets = this.shapeOffsets; + const shapeOrientations = this.shapeOrientations; + const N = shapes.length; + const offset = tmpVec; + const orientation = tmpQuat; + const bodyQuat = this.quaternion; + const aabb = this.aabb; + const shapeAABB = computeAABB$shapeAABB; + + for (let i = 0; i !== N; i++) + { + const shape = shapes[i]; + + // Get shape world position + bodyQuat.vmult(shapeOffsets[i], offset); + offset.addTo(this.position, offset); + + // Get shape world quaternion + shapeOrientations[i].multTo(bodyQuat, orientation); + + // Get shape AABB + shape.calculateWorldAABB(offset, orientation, shapeAABB.min, shapeAABB.max); + + if (i === 0) + { + aabb.copy(shapeAABB); + } + else + { + aabb.union(shapeAABB); + } + } + + this.aabbNeedsUpdate = false; + } + + /** + * Update .inertiaWorld and .invInertiaWorld + */ + updateInertiaWorld(force?) + { + const I = this.invInertia; + if (I.x === I.y && I.y === I.z && !force) + { + // If inertia M = s*I, where I is identity and s a scalar, then + // R*M*R' = R*(s*I)*R' = s*R*I*R' = s*R*R' = s*I = M + // where R is the rotation matrix. + // In other words, we don't have to transform the inertia if all + // inertia diagonal entries are equal. + } + else + { + const m1 = uiw$m1; + const m2 = uiw$m2; + // const m3 = uiw$m3; + m1.setRotationFromQuaternion(this.quaternion); + m1.transposeTo(m2); + m1.scale(I, m1); + m1.mmult(m2, this.invInertiaWorld); + } + } + + /** + * Apply force to a world point. This could for example be a point on the Body surface. Applying force this way will add to Body.force and Body.torque. + * + * @param force The amount of force to add. + * @param relativePoint A point relative to the center of mass to apply the force on. + */ + applyForce(force: Vector3, relativePoint: Vector3) + { + if (this.type !== Body.DYNAMIC) + { // Needed? + return; + } + + // Compute produced rotational force + const rotForce = Body$applyForce$rotForce; + relativePoint.crossTo(force, rotForce); + + // Add linear force + this.force.addTo(force, this.force); + + // Add rotational force + this.torque.addTo(rotForce, this.torque); + } + + /** + * Apply force to a local point in the body. + * + * @param force The force vector to apply, defined locally in the body frame. + * @param localPoint A local point in the body to apply the force on. + */ + applyLocalForce(localForce: Vector3, localPoint: Vector3) + { + if (this.type !== Body.DYNAMIC) + { + return; + } + + const worldForce = Body$applyLocalForce$worldForce; + const relativePointWorld = Body$applyLocalForce$relativePointWorld; + + // Transform the force vector to world space + this.vectorToWorldFrame(localForce, worldForce); + this.vectorToWorldFrame(localPoint, relativePointWorld); + + this.applyForce(worldForce, relativePointWorld); + } + + /** + * Apply impulse to a world point. This could for example be a point on the Body surface. An impulse is a force added to a body during a short period of time (impulse = force * time). Impulses will be added to Body.velocity and Body.angularVelocity. + * + * @param impulse The amount of impulse to add. + * @param relativePoint A point relative to the center of mass to apply the force on. + */ + applyImpulse(impulse: Vector3, relativePoint: Vector3) + { + if (this.type !== Body.DYNAMIC) + { + return; + } + + // Compute point position relative to the body center + const r = relativePoint; + + // Compute produced central impulse velocity + const velo = Body$applyImpulse$velo; + velo.copy(impulse); + velo.scaleNumberTo(this.invMass, velo); + + // Add linear impulse + this.velocity.addTo(velo, this.velocity); + + // Compute produced rotational impulse velocity + const rotVelo = Body$applyImpulse$rotVelo; + r.crossTo(impulse, rotVelo); + + /* + rotVelo.x *= this.invInertia.x; + rotVelo.y *= this.invInertia.y; + rotVelo.z *= this.invInertia.z; + */ + this.invInertiaWorld.vmult(rotVelo, rotVelo); + + // Add rotational Impulse + this.angularVelocity.addTo(rotVelo, this.angularVelocity); + } + + /** + * Apply locally-defined impulse to a local point in the body. + * + * @param force The force vector to apply, defined locally in the body frame. + * @param localPoint A local point in the body to apply the force on. + */ + applyLocalImpulse(localImpulse: Vector3, localPoint: Vector3) + { + if (this.type !== Body.DYNAMIC) + { + return; + } + + const worldImpulse = Body$applyLocalImpulse$worldImpulse; + const relativePointWorld = Body$applyLocalImpulse$relativePoint; + + // Transform the force vector to world space + this.vectorToWorldFrame(localImpulse, worldImpulse); + this.vectorToWorldFrame(localPoint, relativePointWorld); + + this.applyImpulse(worldImpulse, relativePointWorld); + } + + /** + * Should be called whenever you change the body shape or mass. + */ + updateMassProperties() + { + const halfExtents = BodyUpdateMassPropertiesHalfExtents; + + this.invMass = this.mass > 0 ? 1.0 / this.mass : 0; + const I = this.inertia; + const fixed = this.fixedRotation; + + // Approximate with AABB box + this.computeAABB(); + halfExtents.set( + (this.aabb.max.x - this.aabb.min.x) / 2, + (this.aabb.max.y - this.aabb.min.y) / 2, + (this.aabb.max.z - this.aabb.min.z) / 2 + ); + Box.calculateInertia(halfExtents, this.mass, I); + + this.invInertia.set( + I.x > 0 && !fixed ? 1.0 / I.x : 0, + I.y > 0 && !fixed ? 1.0 / I.y : 0, + I.z > 0 && !fixed ? 1.0 / I.z : 0 + ); + this.updateInertiaWorld(true); + } + + /** + * Get world velocity of a point in the body. + * @method getVelocityAtWorldPoint + * @param {Vector3} worldPoint + * @param {Vector3} result + * @return {Vector3} The result vector. + */ + getVelocityAtWorldPoint(worldPoint: Vector3, result: Vector3) + { + const r = new Vector3(); + worldPoint.subTo(this.position, r); + this.angularVelocity.crossTo(r, result); + this.velocity.addTo(result, result); + + return result; + } + + /** + * Move the body forward in time. + * @param dt Time step + * @param quatNormalize Set to true to normalize the body quaternion + * @param quatNormalizeFast If the quaternion should be normalized using "fast" quaternion normalization + */ + integrate(dt: number, quatNormalize: boolean, quatNormalizeFast: boolean) + { + // Save previous position + this.previousPosition.copy(this.position); + this.previousQuaternion.copy(this.quaternion); + + if (!(this.type === Body.DYNAMIC || this.type === Body.KINEMATIC) || this.sleepState === Body.SLEEPING) + { // Only for dynamic + return; + } + + const velo = this.velocity; + const angularVelo = this.angularVelocity; + const pos = this.position; + const force = this.force; + const torque = this.torque; + const quat = this.quaternion; + const invMass = this.invMass; + const invInertia = this.invInertiaWorld; + const linearFactor = this.linearFactor; + + const iMdt = invMass * dt; + velo.x += force.x * iMdt * linearFactor.x; + velo.y += force.y * iMdt * linearFactor.y; + velo.z += force.z * iMdt * linearFactor.z; + + const e = invInertia.elements; + const angularFactor = this.angularFactor; + const tx = torque.x * angularFactor.x; + const ty = torque.y * angularFactor.y; + const tz = torque.z * angularFactor.z; + angularVelo.x += dt * (e[0] * tx + e[1] * ty + e[2] * tz); + angularVelo.y += dt * (e[3] * tx + e[4] * ty + e[5] * tz); + angularVelo.z += dt * (e[6] * tx + e[7] * ty + e[8] * tz); + + // Use new velocity - leap frog + pos.x += velo.x * dt; + pos.y += velo.y * dt; + pos.z += velo.z * dt; + + quat.integrateTo(this.angularVelocity, dt, this.angularFactor, quat); + + if (quatNormalize) + { + if (quatNormalizeFast) + { + quat.normalizeFast(); + } + else + { + quat.normalize(); + } + } + + this.aabbNeedsUpdate = true; + + // Update world inertia + this.updateInertiaWorld(); + } +} + +const tmpVec = new Vector3(); +const tmpQuat = new Quaternion(); + +// const torque = new Vector3(); +// const invI_tau_dt = new Vector3(); +// const w = new Quaternion(); +// const wq = new Quaternion(); + +const BodyUpdateMassPropertiesHalfExtents = new Vector3(); + +// const Body_applyForce_r = new Vector3(); +const Body$applyForce$rotForce = new Vector3(); +const Body$applyLocalForce$worldForce = new Vector3(); +const Body$applyLocalForce$relativePointWorld = new Vector3(); +// const Body_applyImpulse_r = new Vector3(); +const Body$applyImpulse$velo = new Vector3(); +const Body$applyImpulse$rotVelo = new Vector3(); +const Body$applyLocalImpulse$worldImpulse = new Vector3(); +const Body$applyLocalImpulse$relativePoint = new Vector3(); + +const uiw$m1 = new Matrix3x3(); +const uiw$m2 = new Matrix3x3(); +// const uiw$m3 = new Matrix3x3(); + +const computeAABB$shapeAABB = new Box3(); diff --git a/src/objects/RaycastVehicle.js b/src/objects/RaycastVehicle.js deleted file mode 100644 index b0c648eef..000000000 --- a/src/objects/RaycastVehicle.js +++ /dev/null @@ -1,703 +0,0 @@ -var Body = require('./Body'); -var Vec3 = require('../math/Vec3'); -var Quaternion = require('../math/Quaternion'); -var RaycastResult = require('../collision/RaycastResult'); -var Ray = require('../collision/Ray'); -var WheelInfo = require('../objects/WheelInfo'); - -module.exports = RaycastVehicle; - -/** - * Vehicle helper class that casts rays from the wheel positions towards the ground and applies forces. - * @class RaycastVehicle - * @constructor - * @param {object} [options] - * @param {Body} [options.chassisBody] The car chassis body. - * @param {integer} [options.indexRightAxis] Axis to use for right. x=0, y=1, z=2 - * @param {integer} [options.indexLeftAxis] - * @param {integer} [options.indexUpAxis] - */ -function RaycastVehicle(options){ - - /** - * @property {Body} chassisBody - */ - this.chassisBody = options.chassisBody; - - /** - * An array of WheelInfo objects. - * @property {array} wheelInfos - */ - this.wheelInfos = []; - - /** - * Will be set to true if the car is sliding. - * @property {boolean} sliding - */ - this.sliding = false; - - /** - * @property {World} world - */ - this.world = null; - - /** - * Index of the right axis, 0=x, 1=y, 2=z - * @property {integer} indexRightAxis - * @default 1 - */ - this.indexRightAxis = typeof(options.indexRightAxis) !== 'undefined' ? options.indexRightAxis : 1; - - /** - * Index of the forward axis, 0=x, 1=y, 2=z - * @property {integer} indexForwardAxis - * @default 0 - */ - this.indexForwardAxis = typeof(options.indexForwardAxis) !== 'undefined' ? options.indexForwardAxis : 0; - - /** - * Index of the up axis, 0=x, 1=y, 2=z - * @property {integer} indexUpAxis - * @default 2 - */ - this.indexUpAxis = typeof(options.indexUpAxis) !== 'undefined' ? options.indexUpAxis : 2; -} - -var tmpVec1 = new Vec3(); -var tmpVec2 = new Vec3(); -var tmpVec3 = new Vec3(); -var tmpVec4 = new Vec3(); -var tmpVec5 = new Vec3(); -var tmpVec6 = new Vec3(); -var tmpRay = new Ray(); - -/** - * Add a wheel. For information about the options, see WheelInfo. - * @method addWheel - * @param {object} [options] - */ -RaycastVehicle.prototype.addWheel = function(options){ - options = options || {}; - - var info = new WheelInfo(options); - var index = this.wheelInfos.length; - this.wheelInfos.push(info); - - return index; -}; - -/** - * Set the steering value of a wheel. - * @method setSteeringValue - * @param {number} value - * @param {integer} wheelIndex - */ -RaycastVehicle.prototype.setSteeringValue = function(value, wheelIndex){ - var wheel = this.wheelInfos[wheelIndex]; - wheel.steering = value; -}; - -var torque = new Vec3(); - -/** - * Set the wheel force to apply on one of the wheels each time step - * @method applyEngineForce - * @param {number} value - * @param {integer} wheelIndex - */ -RaycastVehicle.prototype.applyEngineForce = function(value, wheelIndex){ - this.wheelInfos[wheelIndex].engineForce = value; -}; - -/** - * Set the braking force of a wheel - * @method setBrake - * @param {number} brake - * @param {integer} wheelIndex - */ -RaycastVehicle.prototype.setBrake = function(brake, wheelIndex){ - this.wheelInfos[wheelIndex].brake = brake; -}; - -/** - * Add the vehicle including its constraints to the world. - * @method addToWorld - * @param {World} world - */ -RaycastVehicle.prototype.addToWorld = function(world){ - var constraints = this.constraints; - world.addBody(this.chassisBody); - var that = this; - this.preStepCallback = function(){ - that.updateVehicle(world.dt); - }; - world.addEventListener('preStep', this.preStepCallback); - this.world = world; -}; - -/** - * Get one of the wheel axles, world-oriented. - * @private - * @method getVehicleAxisWorld - * @param {integer} axisIndex - * @param {Vec3} result - */ -RaycastVehicle.prototype.getVehicleAxisWorld = function(axisIndex, result){ - result.set( - axisIndex === 0 ? 1 : 0, - axisIndex === 1 ? 1 : 0, - axisIndex === 2 ? 1 : 0 - ); - this.chassisBody.vectorToWorldFrame(result, result); -}; - -RaycastVehicle.prototype.updateVehicle = function(timeStep){ - var wheelInfos = this.wheelInfos; - var numWheels = wheelInfos.length; - var chassisBody = this.chassisBody; - - for (var i = 0; i < numWheels; i++) { - this.updateWheelTransform(i); - } - - this.currentVehicleSpeedKmHour = 3.6 * chassisBody.velocity.norm(); - - var forwardWorld = new Vec3(); - this.getVehicleAxisWorld(this.indexForwardAxis, forwardWorld); - - if (forwardWorld.dot(chassisBody.velocity) < 0){ - this.currentVehicleSpeedKmHour *= -1; - } - - // simulate suspension - for (var i = 0; i < numWheels; i++) { - this.castRay(wheelInfos[i]); - } - - this.updateSuspension(timeStep); - - var impulse = new Vec3(); - var relpos = new Vec3(); - for (var i = 0; i < numWheels; i++) { - //apply suspension force - var wheel = wheelInfos[i]; - var suspensionForce = wheel.suspensionForce; - if (suspensionForce > wheel.maxSuspensionForce) { - suspensionForce = wheel.maxSuspensionForce; - } - wheel.raycastResult.hitNormalWorld.scale(suspensionForce * timeStep, impulse); - - wheel.raycastResult.hitPointWorld.vsub(chassisBody.position, relpos); - chassisBody.applyImpulse(impulse, relpos); - } - - this.updateFriction(timeStep); - - var hitNormalWorldScaledWithProj = new Vec3(); - var fwd = new Vec3(); - var vel = new Vec3(); - for (i = 0; i < numWheels; i++) { - var wheel = wheelInfos[i]; - //var relpos = new Vec3(); - //wheel.chassisConnectionPointWorld.vsub(chassisBody.position, relpos); - chassisBody.getVelocityAtWorldPoint(wheel.chassisConnectionPointWorld, vel); - - // Hack to get the rotation in the correct direction - var m = 1; - switch(this.indexUpAxis){ - case 1: - m = -1; - break; - } - - if (wheel.isInContact) { - - this.getVehicleAxisWorld(this.indexForwardAxis, fwd); - var proj = fwd.dot(wheel.raycastResult.hitNormalWorld); - wheel.raycastResult.hitNormalWorld.scale(proj, hitNormalWorldScaledWithProj); - - fwd.vsub(hitNormalWorldScaledWithProj, fwd); - - var proj2 = fwd.dot(vel); - wheel.deltaRotation = m * proj2 * timeStep / wheel.radius; - } - - if((wheel.sliding || !wheel.isInContact) && wheel.engineForce !== 0 && wheel.useCustomSlidingRotationalSpeed){ - // Apply custom rotation when accelerating and sliding - wheel.deltaRotation = (wheel.engineForce > 0 ? 1 : -1) * wheel.customSlidingRotationalSpeed * timeStep; - } - - // Lock wheels - if(Math.abs(wheel.brake) > Math.abs(wheel.engineForce)){ - wheel.deltaRotation = 0; - } - - wheel.rotation += wheel.deltaRotation; // Use the old value - wheel.deltaRotation *= 0.99; // damping of rotation when not in contact - } -}; - -RaycastVehicle.prototype.updateSuspension = function(deltaTime) { - var chassisBody = this.chassisBody; - var chassisMass = chassisBody.mass; - var wheelInfos = this.wheelInfos; - var numWheels = wheelInfos.length; - - for (var w_it = 0; w_it < numWheels; w_it++){ - var wheel = wheelInfos[w_it]; - - if (wheel.isInContact){ - var force; - - // Spring - var susp_length = wheel.suspensionRestLength; - var current_length = wheel.suspensionLength; - var length_diff = (susp_length - current_length); - - force = wheel.suspensionStiffness * length_diff * wheel.clippedInvContactDotSuspension; - - // Damper - var projected_rel_vel = wheel.suspensionRelativeVelocity; - var susp_damping; - if (projected_rel_vel < 0) { - susp_damping = wheel.dampingCompression; - } else { - susp_damping = wheel.dampingRelaxation; - } - force -= susp_damping * projected_rel_vel; - - wheel.suspensionForce = force * chassisMass; - if (wheel.suspensionForce < 0) { - wheel.suspensionForce = 0; - } - } else { - wheel.suspensionForce = 0; - } - } -}; - -/** - * Remove the vehicle including its constraints from the world. - * @method removeFromWorld - * @param {World} world - */ -RaycastVehicle.prototype.removeFromWorld = function(world){ - var constraints = this.constraints; - world.remove(this.chassisBody); - world.removeEventListener('preStep', this.preStepCallback); - this.world = null; -}; - -var castRay_rayvector = new Vec3(); -var castRay_target = new Vec3(); -RaycastVehicle.prototype.castRay = function(wheel) { - var rayvector = castRay_rayvector; - var target = castRay_target; - - this.updateWheelTransformWorld(wheel); - var chassisBody = this.chassisBody; - - var depth = -1; - - var raylen = wheel.suspensionRestLength + wheel.radius; - - wheel.directionWorld.scale(raylen, rayvector); - var source = wheel.chassisConnectionPointWorld; - source.vadd(rayvector, target); - var raycastResult = wheel.raycastResult; - - var param = 0; - - raycastResult.reset(); - // Turn off ray collision with the chassis temporarily - var oldState = chassisBody.collisionResponse; - chassisBody.collisionResponse = false; - - // Cast ray against world - this.world.rayTest(source, target, raycastResult); - chassisBody.collisionResponse = oldState; - - var object = raycastResult.body; - - wheel.raycastResult.groundObject = 0; - - if (object) { - depth = raycastResult.distance; - wheel.raycastResult.hitNormalWorld = raycastResult.hitNormalWorld; - wheel.isInContact = true; - - var hitDistance = raycastResult.distance; - wheel.suspensionLength = hitDistance - wheel.radius; - - // clamp on max suspension travel - var minSuspensionLength = wheel.suspensionRestLength - wheel.maxSuspensionTravel; - var maxSuspensionLength = wheel.suspensionRestLength + wheel.maxSuspensionTravel; - if (wheel.suspensionLength < minSuspensionLength) { - wheel.suspensionLength = minSuspensionLength; - } - if (wheel.suspensionLength > maxSuspensionLength) { - wheel.suspensionLength = maxSuspensionLength; - wheel.raycastResult.reset(); - } - - var denominator = wheel.raycastResult.hitNormalWorld.dot(wheel.directionWorld); - - var chassis_velocity_at_contactPoint = new Vec3(); - chassisBody.getVelocityAtWorldPoint(wheel.raycastResult.hitPointWorld, chassis_velocity_at_contactPoint); - - var projVel = wheel.raycastResult.hitNormalWorld.dot( chassis_velocity_at_contactPoint ); - - if (denominator >= -0.1) { - wheel.suspensionRelativeVelocity = 0; - wheel.clippedInvContactDotSuspension = 1 / 0.1; - } else { - var inv = -1 / denominator; - wheel.suspensionRelativeVelocity = projVel * inv; - wheel.clippedInvContactDotSuspension = inv; - } - - } else { - - //put wheel info as in rest position - wheel.suspensionLength = wheel.suspensionRestLength + 0 * wheel.maxSuspensionTravel; - wheel.suspensionRelativeVelocity = 0.0; - wheel.directionWorld.scale(-1, wheel.raycastResult.hitNormalWorld); - wheel.clippedInvContactDotSuspension = 1.0; - } - - return depth; -}; - -RaycastVehicle.prototype.updateWheelTransformWorld = function(wheel){ - wheel.isInContact = false; - var chassisBody = this.chassisBody; - chassisBody.pointToWorldFrame(wheel.chassisConnectionPointLocal, wheel.chassisConnectionPointWorld); - chassisBody.vectorToWorldFrame(wheel.directionLocal, wheel.directionWorld); - chassisBody.vectorToWorldFrame(wheel.axleLocal, wheel.axleWorld); -}; - - -/** - * Update one of the wheel transform. - * Note when rendering wheels: during each step, wheel transforms are updated BEFORE the chassis; ie. their position becomes invalid after the step. Thus when you render wheels, you must update wheel transforms before rendering them. See raycastVehicle demo for an example. - * @method updateWheelTransform - * @param {integer} wheelIndex The wheel index to update. - */ -RaycastVehicle.prototype.updateWheelTransform = function(wheelIndex){ - var up = tmpVec4; - var right = tmpVec5; - var fwd = tmpVec6; - - var wheel = this.wheelInfos[wheelIndex]; - this.updateWheelTransformWorld(wheel); - - wheel.directionLocal.scale(-1, up); - right.copy(wheel.axleLocal); - up.cross(right, fwd); - fwd.normalize(); - right.normalize(); - - // Rotate around steering over the wheelAxle - var steering = wheel.steering; - var steeringOrn = new Quaternion(); - steeringOrn.setFromAxisAngle(up, steering); - - var rotatingOrn = new Quaternion(); - rotatingOrn.setFromAxisAngle(right, wheel.rotation); - - // World rotation of the wheel - var q = wheel.worldTransform.quaternion; - this.chassisBody.quaternion.mult(steeringOrn, q); - q.mult(rotatingOrn, q); - - q.normalize(); - - // world position of the wheel - var p = wheel.worldTransform.position; - p.copy(wheel.directionWorld); - p.scale(wheel.suspensionLength, p); - p.vadd(wheel.chassisConnectionPointWorld, p); -}; - -var directions = [ - new Vec3(1, 0, 0), - new Vec3(0, 1, 0), - new Vec3(0, 0, 1) -]; - -/** - * Get the world transform of one of the wheels - * @method getWheelTransformWorld - * @param {integer} wheelIndex - * @return {Transform} - */ -RaycastVehicle.prototype.getWheelTransformWorld = function(wheelIndex) { - return this.wheelInfos[wheelIndex].worldTransform; -}; - - -var updateFriction_surfNormalWS_scaled_proj = new Vec3(); -var updateFriction_axle = []; -var updateFriction_forwardWS = []; -var sideFrictionStiffness2 = 1; -RaycastVehicle.prototype.updateFriction = function(timeStep) { - var surfNormalWS_scaled_proj = updateFriction_surfNormalWS_scaled_proj; - - //calculate the impulse, so that the wheels don't move sidewards - var wheelInfos = this.wheelInfos; - var numWheels = wheelInfos.length; - var chassisBody = this.chassisBody; - var forwardWS = updateFriction_forwardWS; - var axle = updateFriction_axle; - - var numWheelsOnGround = 0; - - for (var i = 0; i < numWheels; i++) { - var wheel = wheelInfos[i]; - - var groundObject = wheel.raycastResult.body; - if (groundObject){ - numWheelsOnGround++; - } - - wheel.sideImpulse = 0; - wheel.forwardImpulse = 0; - if(!forwardWS[i]){ - forwardWS[i] = new Vec3(); - } - if(!axle[i]){ - axle[i] = new Vec3(); - } - } - - for (var i = 0; i < numWheels; i++){ - var wheel = wheelInfos[i]; - - var groundObject = wheel.raycastResult.body; - - if (groundObject) { - var axlei = axle[i]; - var wheelTrans = this.getWheelTransformWorld(i); - - // Get world axle - wheelTrans.vectorToWorldFrame(directions[this.indexRightAxis], axlei); - - var surfNormalWS = wheel.raycastResult.hitNormalWorld; - var proj = axlei.dot(surfNormalWS); - surfNormalWS.scale(proj, surfNormalWS_scaled_proj); - axlei.vsub(surfNormalWS_scaled_proj, axlei); - axlei.normalize(); - - surfNormalWS.cross(axlei, forwardWS[i]); - forwardWS[i].normalize(); - - wheel.sideImpulse = resolveSingleBilateral( - chassisBody, - wheel.raycastResult.hitPointWorld, - groundObject, - wheel.raycastResult.hitPointWorld, - axlei - ); - - wheel.sideImpulse *= sideFrictionStiffness2; - } - } - - var sideFactor = 1; - var fwdFactor = 0.5; - - this.sliding = false; - for (var i = 0; i < numWheels; i++) { - var wheel = wheelInfos[i]; - var groundObject = wheel.raycastResult.body; - - var rollingFriction = 0; - - wheel.slipInfo = 1; - if (groundObject) { - var defaultRollingFrictionImpulse = 0; - var maxImpulse = wheel.brake ? wheel.brake : defaultRollingFrictionImpulse; - - // btWheelContactPoint contactPt(chassisBody,groundObject,wheelInfraycastInfo.hitPointWorld,forwardWS[wheel],maxImpulse); - // rollingFriction = calcRollingFriction(contactPt); - rollingFriction = calcRollingFriction(chassisBody, groundObject, wheel.raycastResult.hitPointWorld, forwardWS[i], maxImpulse); - - rollingFriction += wheel.engineForce * timeStep; - - // rollingFriction = 0; - var factor = maxImpulse / rollingFriction; - wheel.slipInfo *= factor; - } - - //switch between active rolling (throttle), braking and non-active rolling friction (nthrottle/break) - - wheel.forwardImpulse = 0; - wheel.skidInfo = 1; - - if (groundObject) { - wheel.skidInfo = 1; - - var maximp = wheel.suspensionForce * timeStep * wheel.frictionSlip; - var maximpSide = maximp; - - var maximpSquared = maximp * maximpSide; - - wheel.forwardImpulse = rollingFriction;//wheelInfo.engineForce* timeStep; - - var x = wheel.forwardImpulse * fwdFactor; - var y = wheel.sideImpulse * sideFactor; - - var impulseSquared = x * x + y * y; - - wheel.sliding = false; - if (impulseSquared > maximpSquared) { - this.sliding = true; - wheel.sliding = true; - - var factor = maximp / Math.sqrt(impulseSquared); - - wheel.skidInfo *= factor; - } - } - } - - if (this.sliding) { - for (var i = 0; i < numWheels; i++) { - var wheel = wheelInfos[i]; - if (wheel.sideImpulse !== 0) { - if (wheel.skidInfo < 1){ - wheel.forwardImpulse *= wheel.skidInfo; - wheel.sideImpulse *= wheel.skidInfo; - } - } - } - } - - // apply the impulses - for (var i = 0; i < numWheels; i++) { - var wheel = wheelInfos[i]; - - var rel_pos = new Vec3(); - wheel.raycastResult.hitPointWorld.vsub(chassisBody.position, rel_pos); - // cannons applyimpulse is using world coord for the position - //rel_pos.copy(wheel.raycastResult.hitPointWorld); - - if (wheel.forwardImpulse !== 0) { - var impulse = new Vec3(); - forwardWS[i].scale(wheel.forwardImpulse, impulse); - chassisBody.applyImpulse(impulse, rel_pos); - } - - if (wheel.sideImpulse !== 0){ - var groundObject = wheel.raycastResult.body; - - var rel_pos2 = new Vec3(); - wheel.raycastResult.hitPointWorld.vsub(groundObject.position, rel_pos2); - //rel_pos2.copy(wheel.raycastResult.hitPointWorld); - var sideImp = new Vec3(); - axle[i].scale(wheel.sideImpulse, sideImp); - - // Scale the relative position in the up direction with rollInfluence. - // If rollInfluence is 1, the impulse will be applied on the hitPoint (easy to roll over), if it is zero it will be applied in the same plane as the center of mass (not easy to roll over). - chassisBody.vectorToLocalFrame(rel_pos, rel_pos); - rel_pos['xyz'[this.indexUpAxis]] *= wheel.rollInfluence; - chassisBody.vectorToWorldFrame(rel_pos, rel_pos); - chassisBody.applyImpulse(sideImp, rel_pos); - - //apply friction impulse on the ground - sideImp.scale(-1, sideImp); - groundObject.applyImpulse(sideImp, rel_pos2); - } - } -}; - -var calcRollingFriction_vel1 = new Vec3(); -var calcRollingFriction_vel2 = new Vec3(); -var calcRollingFriction_vel = new Vec3(); - -function calcRollingFriction(body0, body1, frictionPosWorld, frictionDirectionWorld, maxImpulse) { - var j1 = 0; - var contactPosWorld = frictionPosWorld; - - // var rel_pos1 = new Vec3(); - // var rel_pos2 = new Vec3(); - var vel1 = calcRollingFriction_vel1; - var vel2 = calcRollingFriction_vel2; - var vel = calcRollingFriction_vel; - // contactPosWorld.vsub(body0.position, rel_pos1); - // contactPosWorld.vsub(body1.position, rel_pos2); - - body0.getVelocityAtWorldPoint(contactPosWorld, vel1); - body1.getVelocityAtWorldPoint(contactPosWorld, vel2); - vel1.vsub(vel2, vel); - - var vrel = frictionDirectionWorld.dot(vel); - - var denom0 = computeImpulseDenominator(body0, frictionPosWorld, frictionDirectionWorld); - var denom1 = computeImpulseDenominator(body1, frictionPosWorld, frictionDirectionWorld); - var relaxation = 1; - var jacDiagABInv = relaxation / (denom0 + denom1); - - // calculate j that moves us to zero relative velocity - j1 = -vrel * jacDiagABInv; - - if (maxImpulse < j1) { - j1 = maxImpulse; - } - if (j1 < -maxImpulse) { - j1 = -maxImpulse; - } - - return j1; -} - -var computeImpulseDenominator_r0 = new Vec3(); -var computeImpulseDenominator_c0 = new Vec3(); -var computeImpulseDenominator_vec = new Vec3(); -var computeImpulseDenominator_m = new Vec3(); -function computeImpulseDenominator(body, pos, normal) { - var r0 = computeImpulseDenominator_r0; - var c0 = computeImpulseDenominator_c0; - var vec = computeImpulseDenominator_vec; - var m = computeImpulseDenominator_m; - - pos.vsub(body.position, r0); - r0.cross(normal, c0); - body.invInertiaWorld.vmult(c0, m); - m.cross(r0, vec); - - return body.invMass + normal.dot(vec); -} - - -var resolveSingleBilateral_vel1 = new Vec3(); -var resolveSingleBilateral_vel2 = new Vec3(); -var resolveSingleBilateral_vel = new Vec3(); - -//bilateral constraint between two dynamic objects -function resolveSingleBilateral(body1, pos1, body2, pos2, normal, impulse){ - var normalLenSqr = normal.norm2(); - if (normalLenSqr > 1.1){ - return 0; // no impulse - } - // var rel_pos1 = new Vec3(); - // var rel_pos2 = new Vec3(); - // pos1.vsub(body1.position, rel_pos1); - // pos2.vsub(body2.position, rel_pos2); - - var vel1 = resolveSingleBilateral_vel1; - var vel2 = resolveSingleBilateral_vel2; - var vel = resolveSingleBilateral_vel; - body1.getVelocityAtWorldPoint(pos1, vel1); - body2.getVelocityAtWorldPoint(pos2, vel2); - - vel1.vsub(vel2, vel); - - var rel_vel = normal.dot(vel); - - var contactDamping = 0.2; - var massTerm = 1 / (body1.invMass + body2.invMass); - var impulse = - contactDamping * rel_vel * massTerm; - - return impulse; -} \ No newline at end of file diff --git a/src/objects/RaycastVehicle.ts b/src/objects/RaycastVehicle.ts new file mode 100644 index 000000000..3a0f71876 --- /dev/null +++ b/src/objects/RaycastVehicle.ts @@ -0,0 +1,751 @@ +import { Quaternion, Vector3 } from 'feng3d'; +import { Body } from '../objects/Body'; +import { World } from '../world/World'; +import { WheelInfo } from './WheelInfo'; + +export class RaycastVehicle +{ + chassisBody: Body; + + /** + * An array of WheelInfo objects. + */ + wheelInfos: WheelInfo[]; + + /** + * Will be set to true if the car is sliding. + */ + sliding: boolean; + + world: World; + + /** + * Index of the right axis, 0=x, 1=y, 2=z + */ + indexRightAxis: number; + + /** + * Index of the forward axis, 0=x, 1=y, 2=z + */ + indexForwardAxis: number; + + /** + * Index of the up axis, 0=x, 1=y, 2=z + */ + indexUpAxis: number; + + currentVehicleSpeedKmHour: number; + + constraints; + + /** + * Vehicle helper class that casts rays from the wheel positions towards the ground and applies forces. + * + * @param options + */ + constructor(options: { chassisBody?: Body, indexRightAxis?: number, indexForwardAxis?: number, indexUpAxis?: number } = {}) + { + this.chassisBody = options.chassisBody; + this.wheelInfos = []; + this.sliding = false; + this.world = null; + this.indexRightAxis = typeof (options.indexRightAxis) !== 'undefined' ? options.indexRightAxis : 1; + this.indexForwardAxis = typeof (options.indexForwardAxis) !== 'undefined' ? options.indexForwardAxis : 0; + this.indexUpAxis = typeof (options.indexUpAxis) !== 'undefined' ? options.indexUpAxis : 2; + } + + /** + * Add a wheel. For information about the options, see WheelInfo. + * + * @param options + */ + addWheel(options = {}) + { + const info = new WheelInfo(options); + const index = this.wheelInfos.length; + this.wheelInfos.push(info); + + return index; + } + + /** + * Set the steering value of a wheel. + * + * @param value + * @param wheelIndex + */ + setSteeringValue(value: number, wheelIndex: number) + { + const wheel = this.wheelInfos[wheelIndex]; + wheel.steering = value; + } + + /** + * Set the wheel force to apply on one of the wheels each time step + * + * @param value + * @param wheelIndex + */ + applyEngineForce(value: number, wheelIndex: number) + { + this.wheelInfos[wheelIndex].engineForce = value; + } + + /** + * Set the braking force of a wheel + * + * @param brake + * @param wheelIndex + */ + setBrake(brake: number, wheelIndex: number) + { + this.wheelInfos[wheelIndex].brake = brake; + } + + /** + * Add the vehicle including its constraints to the world. + * + * @param world + */ + addToWorld(world: World) + { + world.addBody(this.chassisBody); + + world.on('preStep', this._preStepCallback, this); + this.world = world; + } + + _preStepCallback() + { + this.updateVehicle(this.world.dt); + } + + /** + * Get one of the wheel axles, world-oriented. + * @param axisIndex + * @param result + */ + getVehicleAxisWorld(axisIndex: number, result: Vector3) + { + result.set( + axisIndex === 0 ? 1 : 0, + axisIndex === 1 ? 1 : 0, + axisIndex === 2 ? 1 : 0 + ); + this.chassisBody.vectorToWorldFrame(result, result); + } + + updateVehicle(timeStep: number) + { + const wheelInfos = this.wheelInfos; + const numWheels = wheelInfos.length; + const chassisBody = this.chassisBody; + + for (let i = 0; i < numWheels; i++) + { + this.updateWheelTransform(i); + } + + this.currentVehicleSpeedKmHour = 3.6 * chassisBody.velocity.length; + + const forwardWorld = new Vector3(); + this.getVehicleAxisWorld(this.indexForwardAxis, forwardWorld); + + if (forwardWorld.dot(chassisBody.velocity) < 0) + { + this.currentVehicleSpeedKmHour *= -1; + } + + // simulate suspension + for (let i = 0; i < numWheels; i++) + { + this.castRay(wheelInfos[i]); + } + + this.updateSuspension(timeStep); + + const impulse = new Vector3(); + const relpos = new Vector3(); + for (let i = 0; i < numWheels; i++) + { + // apply suspension force + const wheel = wheelInfos[i]; + let suspensionForce = wheel.suspensionForce; + if (suspensionForce > wheel.maxSuspensionForce) + { + suspensionForce = wheel.maxSuspensionForce; + } + wheel.raycastResult.hitNormalWorld.scaleNumberTo(suspensionForce * timeStep, impulse); + + wheel.raycastResult.hitPointWorld.subTo(chassisBody.position, relpos); + chassisBody.applyImpulse(impulse, relpos); + } + + this.updateFriction(timeStep); + + const hitNormalWorldScaledWithProj = new Vector3(); + const fwd = new Vector3(); + const vel = new Vector3(); + for (let i = 0; i < numWheels; i++) + { + const wheel = wheelInfos[i]; + // let relpos = new Vector3(); + // wheel.chassisConnectionPointWorld.subTo(chassisBody.position, relpos); + chassisBody.getVelocityAtWorldPoint(wheel.chassisConnectionPointWorld, vel); + + // Hack to get the rotation in the correct direction + let m = 1; + switch (this.indexUpAxis) + { + case 1: + m = -1; + break; + } + + if (wheel.isInContact) + { + this.getVehicleAxisWorld(this.indexForwardAxis, fwd); + const proj = fwd.dot(wheel.raycastResult.hitNormalWorld); + wheel.raycastResult.hitNormalWorld.scaleNumberTo(proj, hitNormalWorldScaledWithProj); + + fwd.subTo(hitNormalWorldScaledWithProj, fwd); + + const proj2 = fwd.dot(vel); + wheel.deltaRotation = m * proj2 * timeStep / wheel.radius; + } + + if ((wheel.sliding || !wheel.isInContact) && wheel.engineForce !== 0 && wheel.useCustomSlidingRotationalSpeed) + { + // Apply custom rotation when accelerating and sliding + wheel.deltaRotation = (wheel.engineForce > 0 ? 1 : -1) * wheel.customSlidingRotationalSpeed * timeStep; + } + + // Lock wheels + if (Math.abs(wheel.brake) > Math.abs(wheel.engineForce)) + { + wheel.deltaRotation = 0; + } + + wheel.rotation += wheel.deltaRotation; // Use the old value + wheel.deltaRotation *= 0.99; // damping of rotation when not in contact + } + } + + updateSuspension(_deltaTime: number) + { + const chassisBody = this.chassisBody; + const chassisMass = chassisBody.mass; + const wheelInfos = this.wheelInfos; + const numWheels = wheelInfos.length; + + for (let wIt = 0; wIt < numWheels; wIt++) + { + const wheel = wheelInfos[wIt]; + + if (wheel.isInContact) + { + let force; + + // Spring + const suspLength = wheel.suspensionRestLength; + const currentLength = wheel.suspensionLength; + const lengthDiff = (suspLength - currentLength); + + force = wheel.suspensionStiffness * lengthDiff * wheel.clippedInvContactDotSuspension; + + // Damper + const projectedRelVel = wheel.suspensionRelativeVelocity; + let suspDamping; + if (projectedRelVel < 0) + { + suspDamping = wheel.dampingCompression; + } + else + { + suspDamping = wheel.dampingRelaxation; + } + force -= suspDamping * projectedRelVel; + + wheel.suspensionForce = force * chassisMass; + if (wheel.suspensionForce < 0) + { + wheel.suspensionForce = 0; + } + } + else + { + wheel.suspensionForce = 0; + } + } + } + + /** + * Remove the vehicle including its constraints from the world. + * + * @param world + */ + removeFromWorld(world: World) + { + world.removeBody(this.chassisBody); + world.off('preStep', this._preStepCallback, this); + this.world = null; + } + + castRay(wheel: WheelInfo) + { + const rayvector = castRay$rayvector; + const target = castRay$target; + + this.updateWheelTransformWorld(wheel); + const chassisBody = this.chassisBody; + + let depth = -1; + + const raylen = wheel.suspensionRestLength + wheel.radius; + + wheel.directionWorld.scaleNumberTo(raylen, rayvector); + const source = wheel.chassisConnectionPointWorld; + source.addTo(rayvector, target); + const raycastResult = wheel.raycastResult; + + // const param = 0; + + raycastResult.reset(); + // Turn off ray collision with the chassis temporarily + const oldState = chassisBody.collisionResponse; + chassisBody.collisionResponse = false; + + // Cast ray against world + this.world.raycastClosest(source, target, { + skipBackfaces: true + }, raycastResult); + chassisBody.collisionResponse = oldState; + + const object = raycastResult.body; + + wheel.raycastResult.groundObject = 0;// ? + + if (object) + { + depth = raycastResult.distance; + wheel.raycastResult.hitNormalWorld = raycastResult.hitNormalWorld; + wheel.isInContact = true; + + const hitDistance = raycastResult.distance; + wheel.suspensionLength = hitDistance - wheel.radius; + + // clamp on max suspension travel + const minSuspensionLength = wheel.suspensionRestLength - wheel.maxSuspensionTravel; + const maxSuspensionLength = wheel.suspensionRestLength + wheel.maxSuspensionTravel; + if (wheel.suspensionLength < minSuspensionLength) + { + wheel.suspensionLength = minSuspensionLength; + } + if (wheel.suspensionLength > maxSuspensionLength) + { + wheel.suspensionLength = maxSuspensionLength; + wheel.raycastResult.reset(); + } + + const denominator = wheel.raycastResult.hitNormalWorld.dot(wheel.directionWorld); + + const chassisVelocityAtContactPoint = new Vector3(); + chassisBody.getVelocityAtWorldPoint(wheel.raycastResult.hitPointWorld, chassisVelocityAtContactPoint); + + const projVel = wheel.raycastResult.hitNormalWorld.dot(chassisVelocityAtContactPoint); + + if (denominator >= -0.1) + { + wheel.suspensionRelativeVelocity = 0; + wheel.clippedInvContactDotSuspension = 1 / 0.1; + } + else + { + const inv = -1 / denominator; + wheel.suspensionRelativeVelocity = projVel * inv; + wheel.clippedInvContactDotSuspension = inv; + } + } + else + { + // put wheel info as in rest position + wheel.suspensionLength = wheel.suspensionRestLength + 0 * wheel.maxSuspensionTravel; + wheel.suspensionRelativeVelocity = 0.0; + wheel.directionWorld.scaleNumberTo(-1, wheel.raycastResult.hitNormalWorld); + wheel.clippedInvContactDotSuspension = 1.0; + } + + return depth; + } + + updateWheelTransformWorld(wheel: WheelInfo) + { + wheel.isInContact = false; + const chassisBody = this.chassisBody; + chassisBody.pointToWorldFrame(wheel.chassisConnectionPointLocal, wheel.chassisConnectionPointWorld); + chassisBody.vectorToWorldFrame(wheel.directionLocal, wheel.directionWorld); + chassisBody.vectorToWorldFrame(wheel.axleLocal, wheel.axleWorld); + } + + /** + * Update one of the wheel transform. + * Note when rendering wheels: during each step, wheel transforms are updated BEFORE the chassis; ie. their position becomes invalid after the step. Thus when you render wheels, you must update wheel transforms before rendering them. See raycastVehicle demo for an example. + * + * @param wheelIndex The wheel index to update. + */ + updateWheelTransform(wheelIndex: number) + { + const up = tmpVec4; + const right = tmpVec5; + const fwd = tmpVec6; + + const wheel = this.wheelInfos[wheelIndex]; + this.updateWheelTransformWorld(wheel); + + wheel.directionLocal.scaleNumberTo(-1, up); + right.copy(wheel.axleLocal); + up.crossTo(right, fwd); + fwd.normalize(); + right.normalize(); + + // Rotate around steering over the wheelAxle + const steering = wheel.steering; + const steeringOrn = new Quaternion(); + steeringOrn.fromAxisAngle(up, steering); + + const rotatingOrn = new Quaternion(); + rotatingOrn.fromAxisAngle(right, wheel.rotation); + + // World rotation of the wheel + const q = wheel.worldTransform.quaternion; + this.chassisBody.quaternion.multTo(steeringOrn, q); + q.multTo(rotatingOrn, q); + + q.normalize(); + + // world position of the wheel + const p = wheel.worldTransform.position; + p.copy(wheel.directionWorld); + p.scaleNumberTo(wheel.suspensionLength, p); + p.addTo(wheel.chassisConnectionPointWorld, p); + } + + /** + * Get the world transform of one of the wheels + * + * @param wheelIndex + */ + getWheelTransformWorld(wheelIndex: number) + { + return this.wheelInfos[wheelIndex].worldTransform; + } + + updateFriction(timeStep: number) + { + const surfNormalWS$scaled$proj = updateFriction$surfNormalWS$scaled$proj; + + // calculate the impulse, so that the wheels don't move sidewards + const wheelInfos = this.wheelInfos; + const numWheels = wheelInfos.length; + const chassisBody = this.chassisBody; + const forwardWS = updateFriction$forwardWS; + const axle = updateFriction$axle; + + let numWheelsOnGround = 0; + + for (let i = 0; i < numWheels; i++) + { + const wheel = wheelInfos[i]; + + const groundObject = wheel.raycastResult.body; + if (groundObject) + { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + numWheelsOnGround++; + } + + wheel.sideImpulse = 0; + wheel.forwardImpulse = 0; + if (!forwardWS[i]) + { + forwardWS[i] = new Vector3(); + } + if (!axle[i]) + { + axle[i] = new Vector3(); + } + } + + for (let i = 0; i < numWheels; i++) + { + const wheel = wheelInfos[i]; + + const groundObject = wheel.raycastResult.body; + + if (groundObject) + { + const axlei = axle[i]; + const wheelTrans = this.getWheelTransformWorld(i); + + // Get world axle + wheelTrans.vectorToWorldFrame(directions[this.indexRightAxis], axlei); + + const surfNormalWS = wheel.raycastResult.hitNormalWorld; + const proj = axlei.dot(surfNormalWS); + surfNormalWS.scaleNumberTo(proj, surfNormalWS$scaled$proj); + axlei.subTo(surfNormalWS$scaled$proj, axlei); + axlei.normalize(); + + surfNormalWS.crossTo(axlei, forwardWS[i]); + forwardWS[i].normalize(); + + wheel.sideImpulse = resolveSingleBilateral( + chassisBody, + wheel.raycastResult.hitPointWorld, + groundObject, + wheel.raycastResult.hitPointWorld, + axlei + ); + + wheel.sideImpulse *= sideFrictionStiffness2; + } + } + + const sideFactor = 1; + const fwdFactor = 0.5; + + this.sliding = false; + for (let i = 0; i < numWheels; i++) + { + const wheel = wheelInfos[i]; + const groundObject = wheel.raycastResult.body; + + let rollingFriction = 0; + + wheel.slipInfo = 1; + if (groundObject) + { + const defaultRollingFrictionImpulse = 0; + const maxImpulse = wheel.brake ? wheel.brake : defaultRollingFrictionImpulse; + + // btWheelContactPoint contactPt(chassisBody,groundObject,wheelInfraycastInfo.hitPointWorld,forwardWS[wheel],maxImpulse); + // rollingFriction = calcRollingFriction(contactPt); + rollingFriction = calcRollingFriction(chassisBody, groundObject, wheel.raycastResult.hitPointWorld, forwardWS[i], maxImpulse); + + rollingFriction += wheel.engineForce * timeStep; + + // rollingFriction = 0; + const factor = maxImpulse / rollingFriction; + wheel.slipInfo *= factor; + } + + // switch between active rolling (throttle), braking and non-active rolling friction (nthrottle/break) + + wheel.forwardImpulse = 0; + wheel.skidInfo = 1; + + if (groundObject) + { + wheel.skidInfo = 1; + + const maximp = wheel.suspensionForce * timeStep * wheel.frictionSlip; + const maximpSide = maximp; + + const maximpSquared = maximp * maximpSide; + + wheel.forwardImpulse = rollingFriction;// wheelInfo.engineForce* timeStep; + + const x = wheel.forwardImpulse * fwdFactor; + const y = wheel.sideImpulse * sideFactor; + + const impulseSquared = x * x + y * y; + + wheel.sliding = false; + if (impulseSquared > maximpSquared) + { + this.sliding = true; + wheel.sliding = true; + + const factor = maximp / Math.sqrt(impulseSquared); + + wheel.skidInfo *= factor; + } + } + } + + if (this.sliding) + { + for (let i = 0; i < numWheels; i++) + { + const wheel = wheelInfos[i]; + if (wheel.sideImpulse !== 0) + { + if (wheel.skidInfo < 1) + { + wheel.forwardImpulse *= wheel.skidInfo; + wheel.sideImpulse *= wheel.skidInfo; + } + } + } + } + + // apply the impulses + for (let i = 0; i < numWheels; i++) + { + const wheel = wheelInfos[i]; + + const relPos = new Vector3(); + wheel.raycastResult.hitPointWorld.subTo(chassisBody.position, relPos); + // cannons applyimpulse is using world coord for the position + // rel_pos.copy(wheel.raycastResult.hitPointWorld); + + if (wheel.forwardImpulse !== 0) + { + const impulse = new Vector3(); + forwardWS[i].scaleNumberTo(wheel.forwardImpulse, impulse); + chassisBody.applyImpulse(impulse, relPos); + } + + if (wheel.sideImpulse !== 0) + { + const groundObject = wheel.raycastResult.body; + + const relPos2 = new Vector3(); + wheel.raycastResult.hitPointWorld.subTo(groundObject.position, relPos2); + // rel_pos2.copy(wheel.raycastResult.hitPointWorld); + const sideImp = new Vector3(); + axle[i].scaleNumberTo(wheel.sideImpulse, sideImp); + + // Scale the relative position in the up direction with rollInfluence. + // If rollInfluence is 1, the impulse will be applied on the hitPoint (easy to roll over), if it is zero it will be applied in the same plane as the center of mass (not easy to roll over). + chassisBody.vectorToLocalFrame(relPos, relPos); + relPos['xyz'[this.indexUpAxis]] *= wheel.rollInfluence; + chassisBody.vectorToWorldFrame(relPos, relPos); + chassisBody.applyImpulse(sideImp, relPos); + + // apply friction impulse on the ground + sideImp.scaleNumberTo(-1, sideImp); + groundObject.applyImpulse(sideImp, relPos2); + } + } + } +} + +// const tmpVec1 = new Vector3(); +// const tmpVec2 = new Vector3(); +// const tmpVec3 = new Vector3(); +const tmpVec4 = new Vector3(); +const tmpVec5 = new Vector3(); +const tmpVec6 = new Vector3(); +// const tmpRay = new Ray(); + +// const torque = new Vector3(); + +const castRay$rayvector = new Vector3(); +const castRay$target = new Vector3(); + +const directions = [ + new Vector3(1, 0, 0), + new Vector3(0, 1, 0), + new Vector3(0, 0, 1) +]; +const updateFriction$surfNormalWS$scaled$proj = new Vector3(); +const updateFriction$axle: Vector3[] = []; +const updateFriction$forwardWS: Vector3[] = []; +const sideFrictionStiffness2 = 1; + +const calcRollingFriction$vel1 = new Vector3(); +const calcRollingFriction$vel2 = new Vector3(); +const calcRollingFriction$vel = new Vector3(); + +function calcRollingFriction(body0: Body, body1: Body, frictionPosWorld: Vector3, frictionDirectionWorld: Vector3, maxImpulse: number) +{ + let j1 = 0; + const contactPosWorld = frictionPosWorld; + + // let rel_pos1 = new Vector3(); + // let rel_pos2 = new Vector3(); + const vel1 = calcRollingFriction$vel1; + const vel2 = calcRollingFriction$vel2; + const vel = calcRollingFriction$vel; + // contactPosWorld.subTo(body0.position, rel_pos1); + // contactPosWorld.subTo(body1.position, rel_pos2); + + body0.getVelocityAtWorldPoint(contactPosWorld, vel1); + body1.getVelocityAtWorldPoint(contactPosWorld, vel2); + vel1.subTo(vel2, vel); + + const vrel = frictionDirectionWorld.dot(vel); + + const denom0 = computeImpulseDenominator(body0, frictionPosWorld, frictionDirectionWorld); + const denom1 = computeImpulseDenominator(body1, frictionPosWorld, frictionDirectionWorld); + const relaxation = 1; + const jacDiagABInv = relaxation / (denom0 + denom1); + + // calculate j that moves us to zero relative velocity + j1 = -vrel * jacDiagABInv; + + if (maxImpulse < j1) + { + j1 = maxImpulse; + } + if (j1 < -maxImpulse) + { + j1 = -maxImpulse; + } + + return j1; +} + +const computeImpulseDenominator$r0 = new Vector3(); +const computeImpulseDenominator$c0 = new Vector3(); +const computeImpulseDenominator$vec = new Vector3(); +const computeImpulseDenominator$m = new Vector3(); +function computeImpulseDenominator(body: Body, pos: Vector3, normal: Vector3) +{ + const r0 = computeImpulseDenominator$r0; + const c0 = computeImpulseDenominator$c0; + const vec = computeImpulseDenominator$vec; + const m = computeImpulseDenominator$m; + + pos.subTo(body.position, r0); + r0.crossTo(normal, c0); + body.invInertiaWorld.vmult(c0, m); + m.crossTo(r0, vec); + + return body.invMass + normal.dot(vec); +} + +const resolveSingleBilateral$vel1 = new Vector3(); +const resolveSingleBilateral$vel2 = new Vector3(); +const resolveSingleBilateral$vel = new Vector3(); + +// bilateral constraint between two dynamic objects +function resolveSingleBilateral(body1: Body, pos1: Vector3, body2: Body, pos2: Vector3, normal: Vector3) +{ + const normalLenSqr = normal.lengthSquared; + if (normalLenSqr > 1.1) + { + return 0; // no impulse + } + // let rel_pos1 = new Vector3(); + // let rel_pos2 = new Vector3(); + // pos1.subTo(body1.position, rel_pos1); + // pos2.subTo(body2.position, rel_pos2); + + const vel1 = resolveSingleBilateral$vel1; + const vel2 = resolveSingleBilateral$vel2; + const vel = resolveSingleBilateral$vel; + body1.getVelocityAtWorldPoint(pos1, vel1); + body2.getVelocityAtWorldPoint(pos2, vel2); + + vel1.subTo(vel2, vel); + + const relVel = normal.dot(vel); + + const contactDamping = 0.2; + const massTerm = 1 / (body1.invMass + body2.invMass); + const impulse = -contactDamping * relVel * massTerm; + + return impulse; +} diff --git a/src/objects/RigidVehicle.js b/src/objects/RigidVehicle.js deleted file mode 100644 index a4c0b27ce..000000000 --- a/src/objects/RigidVehicle.js +++ /dev/null @@ -1,220 +0,0 @@ -var Body = require('./Body'); -var Sphere = require('../shapes/Sphere'); -var Box = require('../shapes/Box'); -var Vec3 = require('../math/Vec3'); -var HingeConstraint = require('../constraints/HingeConstraint'); - -module.exports = RigidVehicle; - -/** - * Simple vehicle helper class with spherical rigid body wheels. - * @class RigidVehicle - * @constructor - * @param {Body} [options.chassisBody] - */ -function RigidVehicle(options){ - this.wheelBodies = []; - - /** - * @property coordinateSystem - * @type {Vec3} - */ - this.coordinateSystem = typeof(options.coordinateSystem)==='undefined' ? new Vec3(1, 2, 3) : options.coordinateSystem.clone(); - - /** - * @property {Body} chassisBody - */ - this.chassisBody = options.chassisBody; - - if(!this.chassisBody){ - // No chassis body given. Create it! - var chassisShape = new Box(new Vec3(5, 2, 0.5)); - this.chassisBody = new Body(1, chassisShape); - } - - /** - * @property constraints - * @type {Array} - */ - this.constraints = []; - - this.wheelAxes = []; - this.wheelForces = []; -} - -/** - * Add a wheel - * @method addWheel - * @param {object} options - * @param {boolean} [options.isFrontWheel] - * @param {Vec3} [options.position] Position of the wheel, locally in the chassis body. - * @param {Vec3} [options.direction] Slide direction of the wheel along the suspension. - * @param {Vec3} [options.axis] Axis of rotation of the wheel, locally defined in the chassis. - * @param {Body} [options.body] The wheel body. - */ -RigidVehicle.prototype.addWheel = function(options){ - options = options || {}; - var wheelBody = options.body; - if(!wheelBody){ - wheelBody = new Body(1, new Sphere(1.2)); - } - this.wheelBodies.push(wheelBody); - this.wheelForces.push(0); - - // Position constrain wheels - var zero = new Vec3(); - var position = typeof(options.position) !== 'undefined' ? options.position.clone() : new Vec3(); - - // Set position locally to the chassis - var worldPosition = new Vec3(); - this.chassisBody.pointToWorldFrame(position, worldPosition); - wheelBody.position.set(worldPosition.x, worldPosition.y, worldPosition.z); - - // Constrain wheel - var axis = typeof(options.axis) !== 'undefined' ? options.axis.clone() : new Vec3(0, 1, 0); - this.wheelAxes.push(axis); - - var hingeConstraint = new HingeConstraint(this.chassisBody, wheelBody, { - pivotA: position, - axisA: axis, - pivotB: Vec3.ZERO, - axisB: axis, - collideConnected: false - }); - this.constraints.push(hingeConstraint); - - return this.wheelBodies.length - 1; -}; - -/** - * Set the steering value of a wheel. - * @method setSteeringValue - * @param {number} value - * @param {integer} wheelIndex - * @todo check coordinateSystem - */ -RigidVehicle.prototype.setSteeringValue = function(value, wheelIndex){ - // Set angle of the hinge axis - var axis = this.wheelAxes[wheelIndex]; - - var c = Math.cos(value), - s = Math.sin(value), - x = axis.x, - y = axis.y; - this.constraints[wheelIndex].axisA.set( - c*x -s*y, - s*x +c*y, - 0 - ); -}; - -/** - * Set the target rotational speed of the hinge constraint. - * @method setMotorSpeed - * @param {number} value - * @param {integer} wheelIndex - */ -RigidVehicle.prototype.setMotorSpeed = function(value, wheelIndex){ - var hingeConstraint = this.constraints[wheelIndex]; - hingeConstraint.enableMotor(); - hingeConstraint.motorTargetVelocity = value; -}; - -/** - * Set the target rotational speed of the hinge constraint. - * @method disableMotor - * @param {number} value - * @param {integer} wheelIndex - */ -RigidVehicle.prototype.disableMotor = function(wheelIndex){ - var hingeConstraint = this.constraints[wheelIndex]; - hingeConstraint.disableMotor(); -}; - -var torque = new Vec3(); - -/** - * Set the wheel force to apply on one of the wheels each time step - * @method setWheelForce - * @param {number} value - * @param {integer} wheelIndex - */ -RigidVehicle.prototype.setWheelForce = function(value, wheelIndex){ - this.wheelForces[wheelIndex] = value; -}; - -/** - * Apply a torque on one of the wheels. - * @method applyWheelForce - * @param {number} value - * @param {integer} wheelIndex - */ -RigidVehicle.prototype.applyWheelForce = function(value, wheelIndex){ - var axis = this.wheelAxes[wheelIndex]; - var wheelBody = this.wheelBodies[wheelIndex]; - var bodyTorque = wheelBody.torque; - - axis.scale(value, torque); - wheelBody.vectorToWorldFrame(torque, torque); - bodyTorque.vadd(torque, bodyTorque); -}; - -/** - * Add the vehicle including its constraints to the world. - * @method addToWorld - * @param {World} world - */ -RigidVehicle.prototype.addToWorld = function(world){ - var constraints = this.constraints; - var bodies = this.wheelBodies.concat([this.chassisBody]); - - for (var i = 0; i < bodies.length; i++) { - world.addBody(bodies[i]); - } - - for (var i = 0; i < constraints.length; i++) { - world.addConstraint(constraints[i]); - } - - world.addEventListener('preStep', this._update.bind(this)); -}; - -RigidVehicle.prototype._update = function(){ - var wheelForces = this.wheelForces; - for (var i = 0; i < wheelForces.length; i++) { - this.applyWheelForce(wheelForces[i], i); - } -}; - -/** - * Remove the vehicle including its constraints from the world. - * @method removeFromWorld - * @param {World} world - */ -RigidVehicle.prototype.removeFromWorld = function(world){ - var constraints = this.constraints; - var bodies = this.wheelBodies.concat([this.chassisBody]); - - for (var i = 0; i < bodies.length; i++) { - world.remove(bodies[i]); - } - - for (var i = 0; i < constraints.length; i++) { - world.removeConstraint(constraints[i]); - } -}; - -var worldAxis = new Vec3(); - -/** - * Get current rotational velocity of a wheel - * @method getWheelSpeed - * @param {integer} wheelIndex - */ -RigidVehicle.prototype.getWheelSpeed = function(wheelIndex){ - var axis = this.wheelAxes[wheelIndex]; - var wheelBody = this.wheelBodies[wheelIndex]; - var w = wheelBody.angularVelocity; - this.chassisBody.vectorToWorldFrame(axis, worldAxis); - return w.dot(worldAxis); -}; diff --git a/src/objects/RigidVehicle.ts b/src/objects/RigidVehicle.ts new file mode 100644 index 000000000..84ccee117 --- /dev/null +++ b/src/objects/RigidVehicle.ts @@ -0,0 +1,228 @@ +import { Vector3 } from 'feng3d'; +import { HingeConstraint } from '../constraints/HingeConstraint'; +import { Body } from '../objects/Body'; +import { World } from '../world/World'; + +export class RigidVehicle +{ + wheelBodies: Body[]; + coordinateSystem: Vector3; + chassisBody: Body; + constraints: HingeConstraint[]; + wheelAxes: Vector3[]; + wheelForces: number[]; + + /** + * Simple vehicle helper class with spherical rigid body wheels. + * + * @param options + */ + constructor(options: { coordinateSystem?: Vector3, chassisBody?: Body } = {}) + { + this.wheelBodies = []; + + this.coordinateSystem = typeof (options.coordinateSystem) === 'undefined' ? new Vector3(1, 2, 3) : options.coordinateSystem.clone(); + + this.chassisBody = options.chassisBody; + + if (!this.chassisBody) + { + // No chassis body given. Create it! + // const chassisShape = new Box(new Vector3(5, 2, 0.5)); + throw '下一行代码有问题?!'; + // this.chassisBody = new Body(1, chassisShape); + } + + this.constraints = []; + this.wheelAxes = []; + this.wheelForces = []; + } + + /** + * Add a wheel + * + * @param options + */ + addWheel(options: { body?: Body, isFrontWheel?: boolean, position?: Vector3, axis?: Vector3 } = {}) + { + const wheelBody = options.body; + if (!wheelBody) + { + throw '下一行代码有问题?!'; + // wheelBody = new Body(1, new Sphere(1.2)); + } + this.wheelBodies.push(wheelBody); + this.wheelForces.push(0); + + // Position constrain wheels + // const zero = new Vector3(); + const position = typeof (options.position) !== 'undefined' ? options.position.clone() : new Vector3(); + + // Set position locally to the chassis + const worldPosition = new Vector3(); + this.chassisBody.pointToWorldFrame(position, worldPosition); + wheelBody.position.set(worldPosition.x, worldPosition.y, worldPosition.z); + + // Constrain wheel + const axis = typeof (options.axis) !== 'undefined' ? options.axis.clone() : new Vector3(0, 1, 0); + this.wheelAxes.push(axis); + + const hingeConstraint = new HingeConstraint(this.chassisBody, wheelBody, { + pivotA: position, + axisA: axis, + pivotB: Vector3.ZERO, + axisB: axis, + collideConnected: false + }); + this.constraints.push(hingeConstraint); + + return this.wheelBodies.length - 1; + } + + /** + * Set the steering value of a wheel. + * + * @param value + * @param wheelIndex + * + * @todo check coordinateSystem + */ + setSteeringValue(value: number, wheelIndex: number) + { + // Set angle of the hinge axis + const axis = this.wheelAxes[wheelIndex]; + + const c = Math.cos(value); + const s = Math.sin(value); + const x = axis.x; + const y = axis.y; + this.constraints[wheelIndex].axisA.set( + c * x - s * y, + s * x + c * y, + 0 + ); + } + + /** + * Set the target rotational speed of the hinge constraint. + * + * @param value + * @param wheelIndex + */ + setMotorSpeed(value: number, wheelIndex: number) + { + const hingeConstraint = this.constraints[wheelIndex]; + hingeConstraint.enableMotor(); + hingeConstraint.motorTargetVelocity = value; + } + + /** + * Set the target rotational speed of the hinge constraint. + * + * @param wheelIndex + */ + disableMotor(wheelIndex: number) + { + const hingeConstraint = this.constraints[wheelIndex]; + hingeConstraint.disableMotor(); + } + + /** + * Set the wheel force to apply on one of the wheels each time step + * + * @param value + * @param wheelIndex + */ + setWheelForce(value: number, wheelIndex: number) + { + this.wheelForces[wheelIndex] = value; + } + + /** + * Apply a torque on one of the wheels. + * + * @param value + * @param wheelIndex + */ + applyWheelForce(value: number, wheelIndex: number) + { + const axis = this.wheelAxes[wheelIndex]; + const wheelBody = this.wheelBodies[wheelIndex]; + const bodyTorque = wheelBody.torque; + + axis.scaleNumberTo(value, torque); + wheelBody.vectorToWorldFrame(torque, torque); + bodyTorque.addTo(torque, bodyTorque); + } + + /** + * Add the vehicle including its constraints to the world. + * + * @param world + */ + addToWorld(world: World) + { + const constraints = this.constraints; + const bodies = this.wheelBodies.concat([this.chassisBody]); + + for (let i = 0; i < bodies.length; i++) + { + world.addBody(bodies[i]); + } + + for (let i = 0; i < constraints.length; i++) + { + world.addConstraint(constraints[i]); + } + + world.on('preStep', this._update, this); + } + + private _update() + { + const wheelForces = this.wheelForces; + for (let i = 0; i < wheelForces.length; i++) + { + this.applyWheelForce(wheelForces[i], i); + } + } + + /** + * Remove the vehicle including its constraints from the world. + * @param world + */ + removeFromWorld(world: World) + { + const constraints = this.constraints; + const bodies = this.wheelBodies.concat([this.chassisBody]); + + for (let i = 0; i < bodies.length; i++) + { + world.removeBody(bodies[i]); + } + + for (let i = 0; i < constraints.length; i++) + { + world.removeConstraint(constraints[i]); + } + } + + /** + * Get current rotational velocity of a wheel + * + * @param wheelIndex + */ + getWheelSpeed(wheelIndex: number) + { + const axis = this.wheelAxes[wheelIndex]; + const wheelBody = this.wheelBodies[wheelIndex]; + const w = wheelBody.angularVelocity; + this.chassisBody.vectorToWorldFrame(axis, worldAxis); + + return w.dot(worldAxis); + } +} + +const torque = new Vector3(); + +const worldAxis = new Vector3(); diff --git a/src/objects/SPHSystem.js b/src/objects/SPHSystem.js deleted file mode 100644 index 41d21d6ad..000000000 --- a/src/objects/SPHSystem.js +++ /dev/null @@ -1,213 +0,0 @@ -module.exports = SPHSystem; - -var Shape = require('../shapes/Shape'); -var Vec3 = require('../math/Vec3'); -var Quaternion = require('../math/Quaternion'); -var Particle = require('../shapes/Particle'); -var Body = require('../objects/Body'); -var Material = require('../material/Material'); - -/** - * Smoothed-particle hydrodynamics system - * @class SPHSystem - * @constructor - */ -function SPHSystem(){ - this.particles = []; - - /** - * Density of the system (kg/m3). - * @property {number} density - */ - this.density = 1; - - /** - * Distance below which two particles are considered to be neighbors. - * It should be adjusted so there are about 15-20 neighbor particles within this radius. - * @property {number} smoothingRadius - */ - this.smoothingRadius = 1; - this.speedOfSound = 1; - - /** - * Viscosity of the system. - * @property {number} viscosity - */ - this.viscosity = 0.01; - this.eps = 0.000001; - - // Stuff Computed per particle - this.pressures = []; - this.densities = []; - this.neighbors = []; -} - -/** - * Add a particle to the system. - * @method add - * @param {Body} particle - */ -SPHSystem.prototype.add = function(particle){ - this.particles.push(particle); - if(this.neighbors.length < this.particles.length){ - this.neighbors.push([]); - } -}; - -/** - * Remove a particle from the system. - * @method remove - * @param {Body} particle - */ -SPHSystem.prototype.remove = function(particle){ - var idx = this.particles.indexOf(particle); - if(idx !== -1){ - this.particles.splice(idx,1); - if(this.neighbors.length > this.particles.length){ - this.neighbors.pop(); - } - } -}; - -/** - * Get neighbors within smoothing volume, save in the array neighbors - * @method getNeighbors - * @param {Body} particle - * @param {Array} neighbors - */ -var SPHSystem_getNeighbors_dist = new Vec3(); -SPHSystem.prototype.getNeighbors = function(particle,neighbors){ - var N = this.particles.length, - id = particle.id, - R2 = this.smoothingRadius * this.smoothingRadius, - dist = SPHSystem_getNeighbors_dist; - for(var i=0; i!==N; i++){ - var p = this.particles[i]; - p.position.vsub(particle.position,dist); - if(id!==p.id && dist.norm2() < R2){ - neighbors.push(p); - } - } -}; - -// Temp vectors for calculation -var SPHSystem_update_dist = new Vec3(), - SPHSystem_update_a_pressure = new Vec3(), - SPHSystem_update_a_visc = new Vec3(), - SPHSystem_update_gradW = new Vec3(), - SPHSystem_update_r_vec = new Vec3(), - SPHSystem_update_u = new Vec3(); // Relative velocity -SPHSystem.prototype.update = function(){ - var N = this.particles.length, - dist = SPHSystem_update_dist, - cs = this.speedOfSound, - eps = this.eps; - - for(var i=0; i!==N; i++){ - var p = this.particles[i]; // Current particle - var neighbors = this.neighbors[i]; - - // Get neighbors - neighbors.length = 0; - this.getNeighbors(p,neighbors); - neighbors.push(this.particles[i]); // Add current too - var numNeighbors = neighbors.length; - - // Accumulate density for the particle - var sum = 0.0; - for(var j=0; j!==numNeighbors; j++){ - - //printf("Current particle has position %f %f %f\n",objects[id].pos.x(),objects[id].pos.y(),objects[id].pos.z()); - p.position.vsub(neighbors[j].position, dist); - var len = dist.norm(); - - var weight = this.w(len); - sum += neighbors[j].mass * weight; - } - - // Save - this.densities[i] = sum; - this.pressures[i] = cs * cs * (this.densities[i] - this.density); - } - - // Add forces - - // Sum to these accelerations - var a_pressure= SPHSystem_update_a_pressure; - var a_visc = SPHSystem_update_a_visc; - var gradW = SPHSystem_update_gradW; - var r_vec = SPHSystem_update_r_vec; - var u = SPHSystem_update_u; - - for(var i=0; i!==N; i++){ - - var particle = this.particles[i]; - - a_pressure.set(0,0,0); - a_visc.set(0,0,0); - - // Init vars - var Pij; - var nabla; - var Vij; - - // Sum up for all other neighbors - var neighbors = this.neighbors[i]; - var numNeighbors = neighbors.length; - - //printf("Neighbors: "); - for(var j=0; j!==numNeighbors; j++){ - - var neighbor = neighbors[j]; - //printf("%d ",nj); - - // Get r once for all.. - particle.position.vsub(neighbor.position,r_vec); - var r = r_vec.norm(); - - // Pressure contribution - Pij = -neighbor.mass * (this.pressures[i] / (this.densities[i]*this.densities[i] + eps) + this.pressures[j] / (this.densities[j]*this.densities[j] + eps)); - this.gradw(r_vec, gradW); - // Add to pressure acceleration - gradW.mult(Pij , gradW); - a_pressure.vadd(gradW, a_pressure); - - // Viscosity contribution - neighbor.velocity.vsub(particle.velocity, u); - u.mult( 1.0 / (0.0001+this.densities[i] * this.densities[j]) * this.viscosity * neighbor.mass , u ); - nabla = this.nablaw(r); - u.mult(nabla,u); - // Add to viscosity acceleration - a_visc.vadd( u, a_visc ); - } - - // Calculate force - a_visc.mult(particle.mass, a_visc); - a_pressure.mult(particle.mass, a_pressure); - - // Add force to particles - particle.force.vadd(a_visc, particle.force); - particle.force.vadd(a_pressure, particle.force); - } -}; - -// Calculate the weight using the W(r) weightfunction -SPHSystem.prototype.w = function(r){ - // 315 - var h = this.smoothingRadius; - return 315.0/(64.0*Math.PI*Math.pow(h,9)) * Math.pow(h*h-r*r,3); -}; - -// calculate gradient of the weight function -SPHSystem.prototype.gradw = function(rVec,resultVec){ - var r = rVec.norm(), - h = this.smoothingRadius; - rVec.mult(945.0/(32.0*Math.PI*Math.pow(h,9)) * Math.pow((h*h-r*r),2) , resultVec); -}; - -// Calculate nabla(W) -SPHSystem.prototype.nablaw = function(r){ - var h = this.smoothingRadius; - var nabla = 945.0/(32.0*Math.PI*Math.pow(h,9)) * (h*h-r*r)*(7*r*r - 3*h*h); - return nabla; -}; diff --git a/src/objects/SPHSystem.ts b/src/objects/SPHSystem.ts new file mode 100644 index 000000000..f69940634 --- /dev/null +++ b/src/objects/SPHSystem.ts @@ -0,0 +1,231 @@ +import { Vector3 } from 'feng3d'; +import { Body } from '../objects/Body'; + +export class SPHSystem +{ + particles: Body[]; + /** + * Density of the system (kg/m3). + */ + density: number; + /** + * Distance below which two particles are considered to be neighbors. + * It should be adjusted so there are about 15-20 neighbor particles within this radius. + */ + smoothingRadius: number; + speedOfSound: number; + /** + * Viscosity of the system. + */ + viscosity: number; + eps: number; + pressures: number[]; + densities: number[]; + neighbors: Body[][]; + + /** + * Smoothed-particle hydrodynamics system + */ + constructor() + { + this.particles = []; + + this.density = 1; + + this.smoothingRadius = 1; + this.speedOfSound = 1; + + this.viscosity = 0.01; + this.eps = 0.000001; + + // Stuff Computed per particle + this.pressures = []; + this.densities = []; + this.neighbors = []; + } + + /** + * Add a particle to the system. + * + * @param particle + */ + add(particle: Body) + { + this.particles.push(particle); + if (this.neighbors.length < this.particles.length) + { + this.neighbors.push([]); + } + } + + /** + * Remove a particle from the system. + * + * @param particle + */ + remove(particle: Body) + { + const idx = this.particles.indexOf(particle); + if (idx !== -1) + { + this.particles.splice(idx, 1); + if (this.neighbors.length > this.particles.length) + { + this.neighbors.pop(); + } + } + } + + /** + * Get neighbors within smoothing volume, save in the array neighbors + * + * @param particle + * @param neighbors + */ + getNeighbors(particle: Body, neighbors: Body[]) + { + const N = this.particles.length; + const id = particle.id; + const R2 = this.smoothingRadius * this.smoothingRadius; + const dist = SPHSystemGetNeighborsDist; + for (let i = 0; i !== N; i++) + { + const p = this.particles[i]; + p.position.subTo(particle.position, dist); + if (id !== p.id && dist.lengthSquared < R2) + { + neighbors.push(p); + } + } + } + + update() + { + const N = this.particles.length; + const dist = SPHSystemUpdateDist; + const cs = this.speedOfSound; + const eps = this.eps; + + for (let i = 0; i !== N; i++) + { + const p = this.particles[i]; // Current particle + const neighbors = this.neighbors[i]; + + // Get neighbors + neighbors.length = 0; + this.getNeighbors(p, neighbors); + neighbors.push(this.particles[i]); // Add current too + const numNeighbors = neighbors.length; + + // Accumulate density for the particle + let sum = 0.0; + for (let j = 0; j !== numNeighbors; j++) + { + // printf("Current particle has position %f %f %f\n",objects[id].pos.x(),objects[id].pos.y(),objects[id].pos.z()); + p.position.subTo(neighbors[j].position, dist); + const len = dist.length; + + const weight = this.w(len); + sum += neighbors[j].mass * weight; + } + + // Save + this.densities[i] = sum; + this.pressures[i] = cs * cs * (this.densities[i] - this.density); + } + + // Add forces + + // Sum to these accelerations + const aPressure = SPHSystemUpdateAPressure; + const aVisc = SPHSystemUpdateAVisc; + const gradW = SPHSystemUpdateGradW; + const rVec = SPHSystemUpdateRVec; + const u = SPHSystemUpdateU; + + for (let i = 0; i !== N; i++) + { + const particle = this.particles[i]; + + aPressure.set(0, 0, 0); + aVisc.set(0, 0, 0); + + // Init vars + let Pij; + let nabla; + // var Vij; + + // Sum up for all other neighbors + const neighbors = this.neighbors[i]; + const numNeighbors = neighbors.length; + + // printf("Neighbors: "); + for (let j = 0; j !== numNeighbors; j++) + { + const neighbor = neighbors[j]; + // printf("%d ",nj); + + // Get r once for all.. + particle.position.subTo(neighbor.position, rVec); + const r = rVec.length; + + // Pressure contribution + Pij = -neighbor.mass * (this.pressures[i] / (this.densities[i] * this.densities[i] + eps) + this.pressures[j] / (this.densities[j] * this.densities[j] + eps)); + this.gradw(rVec, gradW); + // Add to pressure acceleration + gradW.scaleNumberTo(Pij, gradW); + aPressure.addTo(gradW, aPressure); + + // Viscosity contribution + neighbor.velocity.subTo(particle.velocity, u); + u.scaleNumberTo(1.0 / (0.0001 + this.densities[i] * this.densities[j]) * this.viscosity * neighbor.mass, u); + nabla = this.nablaw(r); + u.scaleNumberTo(nabla, u); + // Add to viscosity acceleration + aVisc.addTo(u, aVisc); + } + + // Calculate force + aVisc.scaleNumberTo(particle.mass, aVisc); + aPressure.scaleNumberTo(particle.mass, aPressure); + + // Add force to particles + particle.force.addTo(aVisc, particle.force); + particle.force.addTo(aPressure, particle.force); + } + } + + // Calculate the weight using the W(r) weightfunction + w(r: number) + { + // 315 + const h = this.smoothingRadius; + + return 315.0 / (64.0 * Math.PI * Math.pow(h, 9)) * Math.pow(h * h - r * r, 3); + } + + // calculate gradient of the weight function + gradw(rVec: Vector3, resultVec: Vector3) + { + const r = rVec.length; + const h = this.smoothingRadius; + rVec.scaleNumberTo(945.0 / (32.0 * Math.PI * Math.pow(h, 9)) * Math.pow((h * h - r * r), 2), resultVec); + } + + // Calculate nabla(W) + nablaw(r: number) + { + const h = this.smoothingRadius; + const nabla = 945.0 / (32.0 * Math.PI * Math.pow(h, 9)) * (h * h - r * r) * (7 * r * r - 3 * h * h); + + return nabla; + } +} + +const SPHSystemGetNeighborsDist = new Vector3(); +const SPHSystemUpdateDist = new Vector3(); +const SPHSystemUpdateAPressure = new Vector3(); +const SPHSystemUpdateAVisc = new Vector3(); +const SPHSystemUpdateGradW = new Vector3(); +const SPHSystemUpdateRVec = new Vector3(); +const SPHSystemUpdateU = new Vector3(); // Relative velocity diff --git a/src/objects/Spring.js b/src/objects/Spring.js deleted file mode 100644 index 194ce1380..000000000 --- a/src/objects/Spring.js +++ /dev/null @@ -1,193 +0,0 @@ -var Vec3 = require('../math/Vec3'); - -module.exports = Spring; - -/** - * A spring, connecting two bodies. - * - * @class Spring - * @constructor - * @param {Body} bodyA - * @param {Body} bodyB - * @param {Object} [options] - * @param {number} [options.restLength] A number > 0. Default: 1 - * @param {number} [options.stiffness] A number >= 0. Default: 100 - * @param {number} [options.damping] A number >= 0. Default: 1 - * @param {Vec3} [options.worldAnchorA] Where to hook the spring to body A, in world coordinates. - * @param {Vec3} [options.worldAnchorB] - * @param {Vec3} [options.localAnchorA] Where to hook the spring to body A, in local body coordinates. - * @param {Vec3} [options.localAnchorB] - */ -function Spring(bodyA,bodyB,options){ - options = options || {}; - - /** - * Rest length of the spring. - * @property restLength - * @type {number} - */ - this.restLength = typeof(options.restLength) === "number" ? options.restLength : 1; - - /** - * Stiffness of the spring. - * @property stiffness - * @type {number} - */ - this.stiffness = options.stiffness || 100; - - /** - * Damping of the spring. - * @property damping - * @type {number} - */ - this.damping = options.damping || 1; - - /** - * First connected body. - * @property bodyA - * @type {Body} - */ - this.bodyA = bodyA; - - /** - * Second connected body. - * @property bodyB - * @type {Body} - */ - this.bodyB = bodyB; - - /** - * Anchor for bodyA in local bodyA coordinates. - * @property localAnchorA - * @type {Vec3} - */ - this.localAnchorA = new Vec3(); - - /** - * Anchor for bodyB in local bodyB coordinates. - * @property localAnchorB - * @type {Vec3} - */ - this.localAnchorB = new Vec3(); - - if(options.localAnchorA){ - this.localAnchorA.copy(options.localAnchorA); - } - if(options.localAnchorB){ - this.localAnchorB.copy(options.localAnchorB); - } - if(options.worldAnchorA){ - this.setWorldAnchorA(options.worldAnchorA); - } - if(options.worldAnchorB){ - this.setWorldAnchorB(options.worldAnchorB); - } -} - -/** - * Set the anchor point on body A, using world coordinates. - * @method setWorldAnchorA - * @param {Vec3} worldAnchorA - */ -Spring.prototype.setWorldAnchorA = function(worldAnchorA){ - this.bodyA.pointToLocalFrame(worldAnchorA,this.localAnchorA); -}; - -/** - * Set the anchor point on body B, using world coordinates. - * @method setWorldAnchorB - * @param {Vec3} worldAnchorB - */ -Spring.prototype.setWorldAnchorB = function(worldAnchorB){ - this.bodyB.pointToLocalFrame(worldAnchorB,this.localAnchorB); -}; - -/** - * Get the anchor point on body A, in world coordinates. - * @method getWorldAnchorA - * @param {Vec3} result The vector to store the result in. - */ -Spring.prototype.getWorldAnchorA = function(result){ - this.bodyA.pointToWorldFrame(this.localAnchorA,result); -}; - -/** - * Get the anchor point on body B, in world coordinates. - * @method getWorldAnchorB - * @param {Vec3} result The vector to store the result in. - */ -Spring.prototype.getWorldAnchorB = function(result){ - this.bodyB.pointToWorldFrame(this.localAnchorB,result); -}; - -var applyForce_r = new Vec3(), - applyForce_r_unit = new Vec3(), - applyForce_u = new Vec3(), - applyForce_f = new Vec3(), - applyForce_worldAnchorA = new Vec3(), - applyForce_worldAnchorB = new Vec3(), - applyForce_ri = new Vec3(), - applyForce_rj = new Vec3(), - applyForce_ri_x_f = new Vec3(), - applyForce_rj_x_f = new Vec3(), - applyForce_tmp = new Vec3(); - -/** - * Apply the spring force to the connected bodies. - * @method applyForce - */ -Spring.prototype.applyForce = function(){ - var k = this.stiffness, - d = this.damping, - l = this.restLength, - bodyA = this.bodyA, - bodyB = this.bodyB, - r = applyForce_r, - r_unit = applyForce_r_unit, - u = applyForce_u, - f = applyForce_f, - tmp = applyForce_tmp; - - var worldAnchorA = applyForce_worldAnchorA, - worldAnchorB = applyForce_worldAnchorB, - ri = applyForce_ri, - rj = applyForce_rj, - ri_x_f = applyForce_ri_x_f, - rj_x_f = applyForce_rj_x_f; - - // Get world anchors - this.getWorldAnchorA(worldAnchorA); - this.getWorldAnchorB(worldAnchorB); - - // Get offset points - worldAnchorA.vsub(bodyA.position,ri); - worldAnchorB.vsub(bodyB.position,rj); - - // Compute distance vector between world anchor points - worldAnchorB.vsub(worldAnchorA,r); - var rlen = r.norm(); - r_unit.copy(r); - r_unit.normalize(); - - // Compute relative velocity of the anchor points, u - bodyB.velocity.vsub(bodyA.velocity,u); - // Add rotational velocity - - bodyB.angularVelocity.cross(rj,tmp); - u.vadd(tmp,u); - bodyA.angularVelocity.cross(ri,tmp); - u.vsub(tmp,u); - - // F = - k * ( x - L ) - D * ( u ) - r_unit.mult(-k*(rlen-l) - d*u.dot(r_unit), f); - - // Add forces to bodies - bodyA.force.vsub(f,bodyA.force); - bodyB.force.vadd(f,bodyB.force); - - // Angular force - ri.cross(f,ri_x_f); - rj.cross(f,rj_x_f); - bodyA.torque.vsub(ri_x_f,bodyA.torque); - bodyB.torque.vadd(rj_x_f,bodyB.torque); -}; diff --git a/src/objects/Spring.ts b/src/objects/Spring.ts new file mode 100644 index 000000000..5afd1c040 --- /dev/null +++ b/src/objects/Spring.ts @@ -0,0 +1,188 @@ +import { Vector3 } from 'feng3d'; +import { Body } from '../objects/Body'; + +export class Spring +{ + /** + * Rest length of the spring. + */ + restLength: number; + + /** + * Stiffness of the spring. + */ + stiffness: number; + + /** + * Damping of the spring. + */ + damping: number; + + /** + * First connected body. + */ + bodyA: Body; + + /** + * Second connected body. + */ + bodyB: Body; + + /** + * Anchor for bodyA in local bodyA coordinates. + */ + localAnchorA: Vector3; + + /** + * Anchor for bodyB in local bodyB coordinates. + */ + localAnchorB: Vector3; + + /** + * A spring, connecting two bodies. + * + * @param bodyA + * @param bodyB + * @param options + */ + constructor(bodyA: Body, bodyB: Body, options: { restLength?: number, stiffness?: number, damping?: number, localAnchorA?: Vector3, localAnchorB?: Vector3, worldAnchorA?: Vector3, worldAnchorB?: Vector3 } = {}) + { + this.restLength = typeof (options.restLength) === 'number' ? options.restLength : 1; + + this.stiffness = options.stiffness || 100; + + this.damping = options.damping || 1; + + this.bodyA = bodyA; + + this.bodyB = bodyB; + + this.localAnchorA = new Vector3(); + this.localAnchorB = new Vector3(); + + if (options.localAnchorA) + { + this.localAnchorA.copy(options.localAnchorA); + } + if (options.localAnchorB) + { + this.localAnchorB.copy(options.localAnchorB); + } + if (options.worldAnchorA) + { + this.setWorldAnchorA(options.worldAnchorA); + } + if (options.worldAnchorB) + { + this.setWorldAnchorB(options.worldAnchorB); + } + } + + /** + * Set the anchor point on body A, using world coordinates. + * @param worldAnchorA + */ + setWorldAnchorA(worldAnchorA: Vector3) + { + this.bodyA.pointToLocalFrame(worldAnchorA, this.localAnchorA); + } + + /** + * Set the anchor point on body B, using world coordinates. + * @param worldAnchorB + */ + setWorldAnchorB(worldAnchorB: Vector3) + { + this.bodyB.pointToLocalFrame(worldAnchorB, this.localAnchorB); + } + + /** + * Get the anchor point on body A, in world coordinates. + * @param result The vector to store the result in. + */ + getWorldAnchorA(result: Vector3) + { + this.bodyA.pointToWorldFrame(this.localAnchorA, result); + } + + /** + * Get the anchor point on body B, in world coordinates. + * @param result The vector to store the result in. + */ + getWorldAnchorB(result: Vector3) + { + this.bodyB.pointToWorldFrame(this.localAnchorB, result); + } + + /** + * Apply the spring force to the connected bodies. + */ + applyForce() + { + const k = this.stiffness; + const d = this.damping; + const l = this.restLength; + const bodyA = this.bodyA; + const bodyB = this.bodyB; + const r = applyForceR; + const rUnit = applyForceRUnit; + const u = applyForceU; + const f = applyForceF; + const tmp = applyForceTmp; + + const worldAnchorA = applyForceWorldAnchorA; + const worldAnchorB = applyForceWorldAnchorB; + const ri = applyForceRi; + const rj = applyForceRj; + const riXF = applyForceRiXF; + const rjXF = applyForceRjXF; + + // Get world anchors + this.getWorldAnchorA(worldAnchorA); + this.getWorldAnchorB(worldAnchorB); + + // Get offset points + worldAnchorA.subTo(bodyA.position, ri); + worldAnchorB.subTo(bodyB.position, rj); + + // Compute distance vector between world anchor points + worldAnchorB.subTo(worldAnchorA, r); + const rlen = r.length; + rUnit.copy(r); + rUnit.normalize(); + + // Compute relative velocity of the anchor points, u + bodyB.velocity.subTo(bodyA.velocity, u); + // Add rotational velocity + + bodyB.angularVelocity.crossTo(rj, tmp); + u.addTo(tmp, u); + bodyA.angularVelocity.crossTo(ri, tmp); + u.subTo(tmp, u); + + // F = - k * ( x - L ) - D * ( u ) + rUnit.scaleNumberTo(-k * (rlen - l) - d * u.dot(rUnit), f); + + // Add forces to bodies + bodyA.force.subTo(f, bodyA.force); + bodyB.force.addTo(f, bodyB.force); + + // Angular force + ri.crossTo(f, riXF); + rj.crossTo(f, rjXF); + bodyA.torque.subTo(riXF, bodyA.torque); + bodyB.torque.addTo(rjXF, bodyB.torque); + } +} + +const applyForceR = new Vector3(); +const applyForceRUnit = new Vector3(); +const applyForceU = new Vector3(); +const applyForceF = new Vector3(); +const applyForceWorldAnchorA = new Vector3(); +const applyForceWorldAnchorB = new Vector3(); +const applyForceRi = new Vector3(); +const applyForceRj = new Vector3(); +const applyForceRiXF = new Vector3(); +const applyForceRjXF = new Vector3(); +const applyForceTmp = new Vector3(); diff --git a/src/objects/WheelInfo.js b/src/objects/WheelInfo.js deleted file mode 100644 index 0c7f9bdd3..000000000 --- a/src/objects/WheelInfo.js +++ /dev/null @@ -1,282 +0,0 @@ -var Vec3 = require('../math/Vec3'); -var Transform = require('../math/Transform'); -var RaycastResult = require('../collision/RaycastResult'); -var Utils = require('../utils/Utils'); - -module.exports = WheelInfo; - -/** - * @class WheelInfo - * @constructor - * @param {Object} [options] - * - * @param {Vec3} [options.chassisConnectionPointLocal] - * @param {Vec3} [options.chassisConnectionPointWorld] - * @param {Vec3} [options.directionLocal] - * @param {Vec3} [options.directionWorld] - * @param {Vec3} [options.axleLocal] - * @param {Vec3} [options.axleWorld] - * @param {number} [options.suspensionRestLength=1] - * @param {number} [options.suspensionMaxLength=2] - * @param {number} [options.radius=1] - * @param {number} [options.suspensionStiffness=100] - * @param {number} [options.dampingCompression=10] - * @param {number} [options.dampingRelaxation=10] - * @param {number} [options.frictionSlip=10000] - * @param {number} [options.steering=0] - * @param {number} [options.rotation=0] - * @param {number} [options.deltaRotation=0] - * @param {number} [options.rollInfluence=0.01] - * @param {number} [options.maxSuspensionForce] - * @param {boolean} [options.isFrontWheel=true] - * @param {number} [options.clippedInvContactDotSuspension=1] - * @param {number} [options.suspensionRelativeVelocity=0] - * @param {number} [options.suspensionForce=0] - * @param {number} [options.skidInfo=0] - * @param {number} [options.suspensionLength=0] - * @param {number} [options.maxSuspensionTravel=1] - * @param {boolean} [options.useCustomSlidingRotationalSpeed=false] - * @param {number} [options.customSlidingRotationalSpeed=-0.1] - */ -function WheelInfo(options){ - options = Utils.defaults(options, { - chassisConnectionPointLocal: new Vec3(), - chassisConnectionPointWorld: new Vec3(), - directionLocal: new Vec3(), - directionWorld: new Vec3(), - axleLocal: new Vec3(), - axleWorld: new Vec3(), - suspensionRestLength: 1, - suspensionMaxLength: 2, - radius: 1, - suspensionStiffness: 100, - dampingCompression: 10, - dampingRelaxation: 10, - frictionSlip: 10000, - steering: 0, - rotation: 0, - deltaRotation: 0, - rollInfluence: 0.01, - maxSuspensionForce: Number.MAX_VALUE, - isFrontWheel: true, - clippedInvContactDotSuspension: 1, - suspensionRelativeVelocity: 0, - suspensionForce: 0, - skidInfo: 0, - suspensionLength: 0, - maxSuspensionTravel: 1, - useCustomSlidingRotationalSpeed: false, - customSlidingRotationalSpeed: -0.1 - }); - - /** - * Max travel distance of the suspension, in meters. - * @property {number} maxSuspensionTravel - */ - this.maxSuspensionTravel = options.maxSuspensionTravel; - - /** - * Speed to apply to the wheel rotation when the wheel is sliding. - * @property {number} customSlidingRotationalSpeed - */ - this.customSlidingRotationalSpeed = options.customSlidingRotationalSpeed; - - /** - * If the customSlidingRotationalSpeed should be used. - * @property {Boolean} useCustomSlidingRotationalSpeed - */ - this.useCustomSlidingRotationalSpeed = options.useCustomSlidingRotationalSpeed; - - /** - * @property {Boolean} sliding - */ - this.sliding = false; - - /** - * Connection point, defined locally in the chassis body frame. - * @property {Vec3} chassisConnectionPointLocal - */ - this.chassisConnectionPointLocal = options.chassisConnectionPointLocal.clone(); - - /** - * @property {Vec3} chassisConnectionPointWorld - */ - this.chassisConnectionPointWorld = options.chassisConnectionPointWorld.clone(); - - /** - * @property {Vec3} directionLocal - */ - this.directionLocal = options.directionLocal.clone(); - - /** - * @property {Vec3} directionWorld - */ - this.directionWorld = options.directionWorld.clone(); - - /** - * @property {Vec3} axleLocal - */ - this.axleLocal = options.axleLocal.clone(); - - /** - * @property {Vec3} axleWorld - */ - this.axleWorld = options.axleWorld.clone(); - - /** - * @property {number} suspensionRestLength - */ - this.suspensionRestLength = options.suspensionRestLength; - - /** - * @property {number} suspensionMaxLength - */ - this.suspensionMaxLength = options.suspensionMaxLength; - - /** - * @property {number} radius - */ - this.radius = options.radius; - - /** - * @property {number} suspensionStiffness - */ - this.suspensionStiffness = options.suspensionStiffness; - - /** - * @property {number} dampingCompression - */ - this.dampingCompression = options.dampingCompression; - - /** - * @property {number} dampingRelaxation - */ - this.dampingRelaxation = options.dampingRelaxation; - - /** - * @property {number} frictionSlip - */ - this.frictionSlip = options.frictionSlip; - - /** - * @property {number} steering - */ - this.steering = 0; - - /** - * Rotation value, in radians. - * @property {number} rotation - */ - this.rotation = 0; - - /** - * @property {number} deltaRotation - */ - this.deltaRotation = 0; - - /** - * @property {number} rollInfluence - */ - this.rollInfluence = options.rollInfluence; - - /** - * @property {number} maxSuspensionForce - */ - this.maxSuspensionForce = options.maxSuspensionForce; - - /** - * @property {number} engineForce - */ - this.engineForce = 0; - - /** - * @property {number} brake - */ - this.brake = 0; - - /** - * @property {number} isFrontWheel - */ - this.isFrontWheel = options.isFrontWheel; - - /** - * @property {number} clippedInvContactDotSuspension - */ - this.clippedInvContactDotSuspension = 1; - - /** - * @property {number} suspensionRelativeVelocity - */ - this.suspensionRelativeVelocity = 0; - - /** - * @property {number} suspensionForce - */ - this.suspensionForce = 0; - - /** - * @property {number} skidInfo - */ - this.skidInfo = 0; - - /** - * @property {number} suspensionLength - */ - this.suspensionLength = 0; - - /** - * @property {number} sideImpulse - */ - this.sideImpulse = 0; - - /** - * @property {number} forwardImpulse - */ - this.forwardImpulse = 0; - - /** - * The result from raycasting - * @property {RaycastResult} raycastResult - */ - this.raycastResult = new RaycastResult(); - - /** - * Wheel world transform - * @property {Transform} worldTransform - */ - this.worldTransform = new Transform(); - - /** - * @property {boolean} isInContact - */ - this.isInContact = false; -} - -var chassis_velocity_at_contactPoint = new Vec3(); -var relpos = new Vec3(); -var chassis_velocity_at_contactPoint = new Vec3(); -WheelInfo.prototype.updateWheel = function(chassis){ - var raycastResult = this.raycastResult; - - if (this.isInContact){ - var project= raycastResult.hitNormalWorld.dot(raycastResult.directionWorld); - raycastResult.hitPointWorld.vsub(chassis.position, relpos); - chassis.getVelocityAtWorldPoint(relpos, chassis_velocity_at_contactPoint); - var projVel = raycastResult.hitNormalWorld.dot( chassis_velocity_at_contactPoint ); - if (project >= -0.1) { - this.suspensionRelativeVelocity = 0.0; - this.clippedInvContactDotSuspension = 1.0 / 0.1; - } else { - var inv = -1 / project; - this.suspensionRelativeVelocity = projVel * inv; - this.clippedInvContactDotSuspension = inv; - } - - } else { - // Not in contact : position wheel in a nice (rest length) position - raycastResult.suspensionLength = this.suspensionRestLength; - this.suspensionRelativeVelocity = 0.0; - raycastResult.directionWorld.scale(-1, raycastResult.hitNormalWorld); - this.clippedInvContactDotSuspension = 1.0; - } -}; \ No newline at end of file diff --git a/src/objects/WheelInfo.ts b/src/objects/WheelInfo.ts new file mode 100644 index 000000000..1a3f79d4f --- /dev/null +++ b/src/objects/WheelInfo.ts @@ -0,0 +1,215 @@ +import { Vector3 } from 'feng3d'; +import { RaycastResult } from '../collision/RaycastResult'; +import { Transform } from '../math/Transform'; +import { Body } from '../objects/Body'; +import { Utils } from '../utils/Utils'; + +export class WheelInfo +{ + /** + * Max travel distance of the suspension, in meters. + */ + maxSuspensionTravel: number; + + /** + * Speed to apply to the wheel rotation when the wheel is sliding. + */ + customSlidingRotationalSpeed: number; + + /** + * If the customSlidingRotationalSpeed should be used. + */ + useCustomSlidingRotationalSpeed: boolean; + + sliding: boolean; + + /** + * Connection point, defined locally in the chassis body frame. + */ + chassisConnectionPointLocal: Vector3; + + chassisConnectionPointWorld: Vector3; + + directionLocal: Vector3; + + directionWorld: Vector3; + + axleLocal: Vector3; + + axleWorld: Vector3; + + suspensionRestLength: number; + + suspensionMaxLength: number; + + radius: number; + + suspensionStiffness: number; + + dampingCompression: number; + + dampingRelaxation: number; + + frictionSlip: number; + + steering: number; + + /** + * Rotation value, in radians. + */ + rotation: number; + + deltaRotation: number; + + rollInfluence: number; + + maxSuspensionForce: number; + + engineForce: number; + + brake: number; + + isFrontWheel: number; + + clippedInvContactDotSuspension: number; + + suspensionRelativeVelocity: number; + + suspensionForce: number; + + skidInfo: number; + + suspensionLength: number; + + sideImpulse: number; + + forwardImpulse: number; + + /** + * The result from raycasting + */ + raycastResult: RaycastResult; + + /** + * Wheel world transform + */ + worldTransform: Transform; + + isInContact: boolean; + + slipInfo: number; + + /** + * + * @param options + */ + constructor(options: { + maxSuspensionTravel?: number, customSlidingRotationalSpeed?: number, useCustomSlidingRotationalSpeed?: boolean, + chassisConnectionPointLocal?: Vector3, chassisConnectionPointWorld?: Vector3, directionLocal?: Vector3, directionWorld?: Vector3, + axleLocal?: Vector3, axleWorld?: Vector3, suspensionRestLength?: number, suspensionMaxLength?: number, radius?: number, + suspensionStiffness?: number, dampingCompression?: number, dampingRelaxation?: number, frictionSlip?: number, + rollInfluence?: number, maxSuspensionForce?: number, isFrontWheel?: number, + } = {}) + { + options = Utils.defaults(options, { + chassisConnectionPointLocal: new Vector3(), + chassisConnectionPointWorld: new Vector3(), + directionLocal: new Vector3(), + directionWorld: new Vector3(), + axleLocal: new Vector3(), + axleWorld: new Vector3(), + suspensionRestLength: 1, + suspensionMaxLength: 2, + radius: 1, + suspensionStiffness: 100, + dampingCompression: 10, + dampingRelaxation: 10, + frictionSlip: 10000, + steering: 0, + rotation: 0, + deltaRotation: 0, + rollInfluence: 0.01, + maxSuspensionForce: Number.MAX_VALUE, + isFrontWheel: true, + clippedInvContactDotSuspension: 1, + suspensionRelativeVelocity: 0, + suspensionForce: 0, + skidInfo: 0, + suspensionLength: 0, + maxSuspensionTravel: 1, + useCustomSlidingRotationalSpeed: false, + customSlidingRotationalSpeed: -0.1 + }); + + this.maxSuspensionTravel = options.maxSuspensionTravel; + this.customSlidingRotationalSpeed = options.customSlidingRotationalSpeed; + this.useCustomSlidingRotationalSpeed = options.useCustomSlidingRotationalSpeed; + this.sliding = false; + this.chassisConnectionPointLocal = options.chassisConnectionPointLocal.clone(); + this.chassisConnectionPointWorld = options.chassisConnectionPointWorld.clone(); + this.directionLocal = options.directionLocal.clone(); + this.directionWorld = options.directionWorld.clone(); + this.axleLocal = options.axleLocal.clone(); + this.axleWorld = options.axleWorld.clone(); + this.suspensionRestLength = options.suspensionRestLength; + this.suspensionMaxLength = options.suspensionMaxLength; + this.radius = options.radius; + this.suspensionStiffness = options.suspensionStiffness; + this.dampingCompression = options.dampingCompression; + this.dampingRelaxation = options.dampingRelaxation; + this.frictionSlip = options.frictionSlip; + this.steering = 0; + this.rotation = 0; + this.deltaRotation = 0; + this.rollInfluence = options.rollInfluence; + this.maxSuspensionForce = options.maxSuspensionForce; + this.engineForce = 0; + this.brake = 0; + this.isFrontWheel = options.isFrontWheel; + this.clippedInvContactDotSuspension = 1; + this.suspensionRelativeVelocity = 0; + this.suspensionForce = 0; + this.skidInfo = 0; + this.suspensionLength = 0; + this.sideImpulse = 0; + this.forwardImpulse = 0; + this.raycastResult = new RaycastResult(); + this.worldTransform = new Transform(); + this.isInContact = false; + } + + updateWheel(chassis: Body) + { + const raycastResult = this.raycastResult; + + if (this.isInContact) + { + const project = raycastResult.hitNormalWorld.dot(raycastResult.directionWorld); + raycastResult.hitPointWorld.subTo(chassis.position, relpos); + chassis.getVelocityAtWorldPoint(relpos, chassisVelocityAtContactPoint); + const projVel = raycastResult.hitNormalWorld.dot(chassisVelocityAtContactPoint); + if (project >= -0.1) + { + this.suspensionRelativeVelocity = 0.0; + this.clippedInvContactDotSuspension = 1.0 / 0.1; + } + else + { + const inv = -1 / project; + this.suspensionRelativeVelocity = projVel * inv; + this.clippedInvContactDotSuspension = inv; + } + } + else + { + // Not in contact : position wheel in a nice (rest length) position + raycastResult.suspensionLength = this.suspensionRestLength; + this.suspensionRelativeVelocity = 0.0; + raycastResult.directionWorld.scaleNumberTo(-1, raycastResult.hitNormalWorld); + this.clippedInvContactDotSuspension = 1.0; + } + } +} + +const chassisVelocityAtContactPoint = new Vector3(); +const relpos = new Vector3(); diff --git a/src/shapes/Box.js b/src/shapes/Box.js deleted file mode 100644 index d1305cd95..000000000 --- a/src/shapes/Box.js +++ /dev/null @@ -1,235 +0,0 @@ -module.exports = Box; - -var Shape = require('./Shape'); -var Vec3 = require('../math/Vec3'); -var ConvexPolyhedron = require('./ConvexPolyhedron'); - -/** - * A 3d box shape. - * @class Box - * @constructor - * @param {Vec3} halfExtents - * @author schteppe - * @extends Shape - */ -function Box(halfExtents){ - Shape.call(this, { - type: Shape.types.BOX - }); - - /** - * @property halfExtents - * @type {Vec3} - */ - this.halfExtents = halfExtents; - - /** - * Used by the contact generator to make contacts with other convex polyhedra for example - * @property convexPolyhedronRepresentation - * @type {ConvexPolyhedron} - */ - this.convexPolyhedronRepresentation = null; - - this.updateConvexPolyhedronRepresentation(); - this.updateBoundingSphereRadius(); -} -Box.prototype = new Shape(); -Box.prototype.constructor = Box; - -/** - * Updates the local convex polyhedron representation used for some collisions. - * @method updateConvexPolyhedronRepresentation - */ -Box.prototype.updateConvexPolyhedronRepresentation = function(){ - var sx = this.halfExtents.x; - var sy = this.halfExtents.y; - var sz = this.halfExtents.z; - var V = Vec3; - - var vertices = [ - new V(-sx,-sy,-sz), - new V( sx,-sy,-sz), - new V( sx, sy,-sz), - new V(-sx, sy,-sz), - new V(-sx,-sy, sz), - new V( sx,-sy, sz), - new V( sx, sy, sz), - new V(-sx, sy, sz) - ]; - - var indices = [ - [3,2,1,0], // -z - [4,5,6,7], // +z - [5,4,0,1], // -y - [2,3,7,6], // +y - [0,4,7,3], // -x - [1,2,6,5], // +x - ]; - - var axes = [ - new V(0, 0, 1), - new V(0, 1, 0), - new V(1, 0, 0) - ]; - - var h = new ConvexPolyhedron(vertices, indices); - this.convexPolyhedronRepresentation = h; - h.material = this.material; -}; - -/** - * @method calculateLocalInertia - * @param {Number} mass - * @param {Vec3} target - * @return {Vec3} - */ -Box.prototype.calculateLocalInertia = function(mass,target){ - target = target || new Vec3(); - Box.calculateInertia(this.halfExtents, mass, target); - return target; -}; - -Box.calculateInertia = function(halfExtents,mass,target){ - var e = halfExtents; - target.x = 1.0 / 12.0 * mass * ( 2*e.y*2*e.y + 2*e.z*2*e.z ); - target.y = 1.0 / 12.0 * mass * ( 2*e.x*2*e.x + 2*e.z*2*e.z ); - target.z = 1.0 / 12.0 * mass * ( 2*e.y*2*e.y + 2*e.x*2*e.x ); -}; - -/** - * Get the box 6 side normals - * @method getSideNormals - * @param {array} sixTargetVectors An array of 6 vectors, to store the resulting side normals in. - * @param {Quaternion} quat Orientation to apply to the normal vectors. If not provided, the vectors will be in respect to the local frame. - * @return {array} - */ -Box.prototype.getSideNormals = function(sixTargetVectors,quat){ - var sides = sixTargetVectors; - var ex = this.halfExtents; - sides[0].set( ex.x, 0, 0); - sides[1].set( 0, ex.y, 0); - sides[2].set( 0, 0, ex.z); - sides[3].set( -ex.x, 0, 0); - sides[4].set( 0, -ex.y, 0); - sides[5].set( 0, 0, -ex.z); - - if(quat!==undefined){ - for(var i=0; i!==sides.length; i++){ - quat.vmult(sides[i],sides[i]); - } - } - - return sides; -}; - -Box.prototype.volume = function(){ - return 8.0 * this.halfExtents.x * this.halfExtents.y * this.halfExtents.z; -}; - -Box.prototype.updateBoundingSphereRadius = function(){ - this.boundingSphereRadius = this.halfExtents.norm(); -}; - -var worldCornerTempPos = new Vec3(); -var worldCornerTempNeg = new Vec3(); -Box.prototype.forEachWorldCorner = function(pos,quat,callback){ - - var e = this.halfExtents; - var corners = [[ e.x, e.y, e.z], - [ -e.x, e.y, e.z], - [ -e.x, -e.y, e.z], - [ -e.x, -e.y, -e.z], - [ e.x, -e.y, -e.z], - [ e.x, e.y, -e.z], - [ -e.x, e.y, -e.z], - [ e.x, -e.y, e.z]]; - for(var i=0; i max.x){ - max.x = x; - } - if(y > max.y){ - max.y = y; - } - if(z > max.z){ - max.z = z; - } - - if(x < min.x){ - min.x = x; - } - if(y < min.y){ - min.y = y; - } - if(z < min.z){ - min.z = z; - } - } - - // Get each axis max - // min.set(Infinity,Infinity,Infinity); - // max.set(-Infinity,-Infinity,-Infinity); - // this.forEachWorldCorner(pos,quat,function(x,y,z){ - // if(x > max.x){ - // max.x = x; - // } - // if(y > max.y){ - // max.y = y; - // } - // if(z > max.z){ - // max.z = z; - // } - - // if(x < min.x){ - // min.x = x; - // } - // if(y < min.y){ - // min.y = y; - // } - // if(z < min.z){ - // min.z = z; - // } - // }); -}; diff --git a/src/shapes/Box.ts b/src/shapes/Box.ts new file mode 100644 index 000000000..1e24c4c8d --- /dev/null +++ b/src/shapes/Box.ts @@ -0,0 +1,239 @@ +import { Quaternion, Vector3 } from 'feng3d'; +import { ConvexPolyhedron } from './ConvexPolyhedron'; +import { Shape } from './Shape'; + +export class Box extends Shape +{ + halfExtents: Vector3; + + /** + * Used by the contact generator to make contacts with other convex polyhedra for example + */ + declare convexPolyhedronRepresentation: ConvexPolyhedron; + + /** + * A 3d box shape. + * @param halfExtents + * @author schteppe + */ + constructor(halfExtents: Vector3) + { + super({ + type: Shape.types.BOX + }); + + this.halfExtents = halfExtents; + this.convexPolyhedronRepresentation = null; + + this.updateConvexPolyhedronRepresentation(); + this.updateBoundingSphereRadius(); + } + + /** + * Updates the local convex polyhedron representation used for some collisions. + */ + updateConvexPolyhedronRepresentation() + { + const sx = this.halfExtents.x; + const sy = this.halfExtents.y; + const sz = this.halfExtents.z; + const V = Vector3; + + const vertices = [ + new V(-sx, -sy, -sz), + new V(sx, -sy, -sz), + new V(sx, sy, -sz), + new V(-sx, sy, -sz), + new V(-sx, -sy, sz), + new V(sx, -sy, sz), + new V(sx, sy, sz), + new V(-sx, sy, sz) + ]; + + const indices = [ + [3, 2, 1, 0], // -z + [4, 5, 6, 7], // +z + [5, 4, 0, 1], // -y + [2, 3, 7, 6], // +y + [0, 4, 7, 3], // -x + [1, 2, 6, 5], // +x + ]; + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const axes = [ + new V(0, 0, 1), + new V(0, 1, 0), + new V(1, 0, 0) + ]; + + const h = new ConvexPolyhedron(vertices, indices); + this.convexPolyhedronRepresentation = h; + h.material = this.material; + } + + calculateLocalInertia(mass: number, target = new Vector3()) + { + Box.calculateInertia(this.halfExtents, mass, target); + + return target; + } + + static calculateInertia(halfExtents: Vector3, mass: number, target: Vector3) + { + const e = halfExtents; + target.x = 1.0 / 12.0 * mass * (2 * e.y * 2 * e.y + 2 * e.z * 2 * e.z); + target.y = 1.0 / 12.0 * mass * (2 * e.x * 2 * e.x + 2 * e.z * 2 * e.z); + target.z = 1.0 / 12.0 * mass * (2 * e.y * 2 * e.y + 2 * e.x * 2 * e.x); + } + + /** + * Get the box 6 side normals + * @param sixTargetVectors An array of 6 vectors, to store the resulting side normals in. + * @param quat Orientation to apply to the normal vectors. If not provided, the vectors will be in respect to the local frame. + */ + getSideNormals(sixTargetVectors: Vector3[], quat: Quaternion) + { + const sides = sixTargetVectors; + const ex = this.halfExtents; + sides[0].set(ex.x, 0, 0); + sides[1].set(0, ex.y, 0); + sides[2].set(0, 0, ex.z); + sides[3].set(-ex.x, 0, 0); + sides[4].set(0, -ex.y, 0); + sides[5].set(0, 0, -ex.z); + + if (quat !== undefined) + { + for (let i = 0; i !== sides.length; i++) + { + quat.vmult(sides[i], sides[i]); + } + } + + return sides; + } + + volume() + { + return 8.0 * this.halfExtents.x * this.halfExtents.y * this.halfExtents.z; + } + + updateBoundingSphereRadius() + { + this.boundingSphereRadius = this.halfExtents.length; + } + + forEachWorldCorner(pos: Vector3, quat: Quaternion, callback: Function) + { + const e = this.halfExtents; + const corners = [[e.x, e.y, e.z], + [-e.x, e.y, e.z], + [-e.x, -e.y, e.z], + [-e.x, -e.y, -e.z], + [e.x, -e.y, -e.z], + [e.x, e.y, -e.z], + [-e.x, e.y, -e.z], + [e.x, -e.y, e.z]]; + for (let i = 0; i < corners.length; i++) + { + worldCornerTempPos.set(corners[i][0], corners[i][1], corners[i][2]); + quat.vmult(worldCornerTempPos, worldCornerTempPos); + pos.addTo(worldCornerTempPos, worldCornerTempPos); + callback(worldCornerTempPos.x, + worldCornerTempPos.y, + worldCornerTempPos.z); + } + } + + calculateWorldAABB(pos: Vector3, quat: Quaternion, min: Vector3, max: Vector3) + { + const e = this.halfExtents; + worldCornersTemp[0].set(e.x, e.y, e.z); + worldCornersTemp[1].set(-e.x, e.y, e.z); + worldCornersTemp[2].set(-e.x, -e.y, e.z); + worldCornersTemp[3].set(-e.x, -e.y, -e.z); + worldCornersTemp[4].set(e.x, -e.y, -e.z); + worldCornersTemp[5].set(e.x, e.y, -e.z); + worldCornersTemp[6].set(-e.x, e.y, -e.z); + worldCornersTemp[7].set(e.x, -e.y, e.z); + + let wc = worldCornersTemp[0]; + quat.vmult(wc, wc); + pos.addTo(wc, wc); + max.copy(wc); + min.copy(wc); + for (let i = 1; i < 8; i++) + { + wc = worldCornersTemp[i]; + quat.vmult(wc, wc); + pos.addTo(wc, wc); + const x = wc.x; + const y = wc.y; + const z = wc.z; + if (x > max.x) + { + max.x = x; + } + if (y > max.y) + { + max.y = y; + } + if (z > max.z) + { + max.z = z; + } + + if (x < min.x) + { + min.x = x; + } + if (y < min.y) + { + min.y = y; + } + if (z < min.z) + { + min.z = z; + } + } + + // Get each axis max + // min.set(Infinity,Infinity,Infinity); + // max.set(-Infinity,-Infinity,-Infinity); + // this.forEachWorldCorner(pos,quat,function(x,y,z){ + // if(x > max.x){ + // max.x = x; + // } + // if(y > max.y){ + // max.y = y; + // } + // if(z > max.z){ + // max.z = z; + // } + + // if(x < min.x){ + // min.x = x; + // } + // if(y < min.y){ + // min.y = y; + // } + // if(z < min.z){ + // min.z = z; + // } + // }); + } +} + +const worldCornerTempPos = new Vector3(); +// const worldCornerTempNeg = new Vector3(); + +const worldCornersTemp = [ + new Vector3(), + new Vector3(), + new Vector3(), + new Vector3(), + new Vector3(), + new Vector3(), + new Vector3(), + new Vector3() +]; diff --git a/src/shapes/ConvexPolyhedron.js b/src/shapes/ConvexPolyhedron.js deleted file mode 100644 index 841727350..000000000 --- a/src/shapes/ConvexPolyhedron.js +++ /dev/null @@ -1,926 +0,0 @@ -module.exports = ConvexPolyhedron; - -var Shape = require('./Shape'); -var Vec3 = require('../math/Vec3'); -var Quaternion = require('../math/Quaternion'); -var Transform = require('../math/Transform'); - -/** - * A set of polygons describing a convex shape. - * @class ConvexPolyhedron - * @constructor - * @extends Shape - * @description The shape MUST be convex for the code to work properly. No polygons may be coplanar (contained - * in the same 3D plane), instead these should be merged into one polygon. - * - * @param {array} points An array of Vec3's - * @param {array} faces Array of integer arrays, describing which vertices that is included in each face. - * - * @author qiao / https://github.com/qiao (original author, see https://github.com/qiao/three.js/commit/85026f0c769e4000148a67d45a9e9b9c5108836f) - * @author schteppe / https://github.com/schteppe - * @see http://www.altdevblogaday.com/2011/05/13/contact-generation-between-3d-convex-meshes/ - * @see http://bullet.googlecode.com/svn/trunk/src/BulletCollision/NarrowPhaseCollision/btPolyhedralContactClipping.cpp - * - * @todo Move the clipping functions to ContactGenerator? - * @todo Automatically merge coplanar polygons in constructor. - */ -function ConvexPolyhedron(points, faces, uniqueAxes) { - Shape.call(this, { - type: Shape.types.CONVEXPOLYHEDRON - }); - - /** - * Array of Vec3 - * @property vertices - * @type {Array} - */ - this.vertices = points||[]; - - this.worldVertices = []; // World transformed version of .vertices - this.worldVerticesNeedsUpdate = true; - - /** - * Array of integer arrays, indicating which vertices each face consists of - * @property faces - * @type {Array} - */ - this.faces = faces||[]; - - /** - * Array of Vec3 - * @property faceNormals - * @type {Array} - */ - this.faceNormals = []; - this.computeNormals(); - - this.worldFaceNormalsNeedsUpdate = true; - this.worldFaceNormals = []; // World transformed version of .faceNormals - - /** - * Array of Vec3 - * @property uniqueEdges - * @type {Array} - */ - this.uniqueEdges = []; - - /** - * If given, these locally defined, normalized axes are the only ones being checked when doing separating axis check. - * @property {Array} uniqueAxes - */ - this.uniqueAxes = uniqueAxes ? uniqueAxes.slice() : null; - - this.computeEdges(); - this.updateBoundingSphereRadius(); -} -ConvexPolyhedron.prototype = new Shape(); -ConvexPolyhedron.prototype.constructor = ConvexPolyhedron; - -var computeEdges_tmpEdge = new Vec3(); -/** - * Computes uniqueEdges - * @method computeEdges - */ -ConvexPolyhedron.prototype.computeEdges = function(){ - var faces = this.faces; - var vertices = this.vertices; - var nv = vertices.length; - var edges = this.uniqueEdges; - - edges.length = 0; - - var edge = computeEdges_tmpEdge; - - for(var i=0; i !== faces.length; i++){ - var face = faces[i]; - var numVertices = face.length; - for(var j = 0; j !== numVertices; j++){ - var k = ( j+1 ) % numVertices; - vertices[face[j]].vsub(vertices[face[k]], edge); - edge.normalize(); - var found = false; - for(var p=0; p !== edges.length; p++){ - if (edges[p].almostEquals(edge) || edges[p].almostEquals(edge)){ - found = true; - break; - } - } - - if (!found){ - edges.push(edge.clone()); - } - } - } -}; - -/** - * Compute the normals of the faces. Will reuse existing Vec3 objects in the .faceNormals array if they exist. - * @method computeNormals - */ -ConvexPolyhedron.prototype.computeNormals = function(){ - this.faceNormals.length = this.faces.length; - - // Generate normals - for(var i=0; i dmax){ - dmax = d; - closestFaceB = face; - } - } - var worldVertsB1 = []; - var polyB = hullB.faces[closestFaceB]; - var numVertices = polyB.length; - for(var e0=0; e0=0){ - this.clipFaceAgainstHull(separatingNormal, - posA, - quatA, - worldVertsB1, - minDist, - maxDist, - result); - } -}; - -/** - * Find the separating axis between this hull and another - * @method findSeparatingAxis - * @param {ConvexPolyhedron} hullB - * @param {Vec3} posA - * @param {Quaternion} quatA - * @param {Vec3} posB - * @param {Quaternion} quatB - * @param {Vec3} target The target vector to save the axis in - * @return {bool} Returns false if a separation is found, else true - */ -var fsa_faceANormalWS3 = new Vec3(), - fsa_Worldnormal1 = new Vec3(), - fsa_deltaC = new Vec3(), - fsa_worldEdge0 = new Vec3(), - fsa_worldEdge1 = new Vec3(), - fsa_Cross = new Vec3(); -ConvexPolyhedron.prototype.findSeparatingAxis = function(hullB,posA,quatA,posB,quatB,target, faceListA, faceListB){ - var faceANormalWS3 = fsa_faceANormalWS3, - Worldnormal1 = fsa_Worldnormal1, - deltaC = fsa_deltaC, - worldEdge0 = fsa_worldEdge0, - worldEdge1 = fsa_worldEdge1, - Cross = fsa_Cross; - - var dmin = Number.MAX_VALUE; - var hullA = this; - var curPlaneTests=0; - - if(!hullA.uniqueAxes){ - - var numFacesA = faceListA ? faceListA.length : hullA.faces.length; - - // Test face normals from hullA - for(var i=0; i0.0){ - target.negate(target); - } - - return true; -}; - -var maxminA=[], maxminB=[]; - -/** - * Test separating axis against two hulls. Both hulls are projected onto the axis and the overlap size is returned if there is one. - * @method testSepAxis - * @param {Vec3} axis - * @param {ConvexPolyhedron} hullB - * @param {Vec3} posA - * @param {Quaternion} quatA - * @param {Vec3} posB - * @param {Quaternion} quatB - * @return {number} The overlap depth, or FALSE if no penetration. - */ -ConvexPolyhedron.prototype.testSepAxis = function(axis, hullB, posA, quatA, posB, quatB){ - var hullA=this; - ConvexPolyhedron.project(hullA, axis, posA, quatA, maxminA); - ConvexPolyhedron.project(hullB, axis, posB, quatB, maxminB); - var maxA = maxminA[0]; - var minA = maxminA[1]; - var maxB = maxminB[0]; - var minB = maxminB[1]; - if(maxA= 0, so output intersection - var newv = new Vec3(); - firstVertex.lerp(lastVertex, - n_dot_first / (n_dot_first - n_dot_last), - newv); - outVertices.push(newv); - } - } else { - if(n_dot_last<0){ - // Start >= 0, end < 0 so output intersection and end - var newv = new Vec3(); - firstVertex.lerp(lastVertex, - n_dot_first / (n_dot_first - n_dot_last), - newv); - outVertices.push(newv); - outVertices.push(lastVertex); - } - } - firstVertex = lastVertex; - n_dot_first = n_dot_last; - } - return outVertices; -}; - -// Updates .worldVertices and sets .worldVerticesNeedsUpdate to false. -ConvexPolyhedron.prototype.computeWorldVertices = function(position,quat){ - var N = this.vertices.length; - while(this.worldVertices.length < N){ - this.worldVertices.push( new Vec3() ); - } - - var verts = this.vertices, - worldVerts = this.worldVertices; - for(var i=0; i!==N; i++){ - quat.vmult( verts[i] , worldVerts[i] ); - position.vadd( worldVerts[i] , worldVerts[i] ); - } - - this.worldVerticesNeedsUpdate = false; -}; - -var computeLocalAABB_worldVert = new Vec3(); -ConvexPolyhedron.prototype.computeLocalAABB = function(aabbmin,aabbmax){ - var n = this.vertices.length, - vertices = this.vertices, - worldVert = computeLocalAABB_worldVert; - - aabbmin.set(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE); - aabbmax.set(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE); - - for(var i=0; i aabbmax.x){ - aabbmax.x = v.x; - } - if (v.y < aabbmin.y){ - aabbmin.y = v.y; - } else if(v.y > aabbmax.y){ - aabbmax.y = v.y; - } - if (v.z < aabbmin.z){ - aabbmin.z = v.z; - } else if(v.z > aabbmax.z){ - aabbmax.z = v.z; - } - } -}; - -/** - * Updates .worldVertices and sets .worldVerticesNeedsUpdate to false. - * @method computeWorldFaceNormals - * @param {Quaternion} quat - */ -ConvexPolyhedron.prototype.computeWorldFaceNormals = function(quat){ - var N = this.faceNormals.length; - while(this.worldFaceNormals.length < N){ - this.worldFaceNormals.push( new Vec3() ); - } - - var normals = this.faceNormals, - worldNormals = this.worldFaceNormals; - for(var i=0; i!==N; i++){ - quat.vmult( normals[i] , worldNormals[i] ); - } - - this.worldFaceNormalsNeedsUpdate = false; -}; - -/** - * @method updateBoundingSphereRadius - */ -ConvexPolyhedron.prototype.updateBoundingSphereRadius = function(){ - // Assume points are distributed with local (0,0,0) as center - var max2 = 0; - var verts = this.vertices; - for(var i=0, N=verts.length; i!==N; i++) { - var norm2 = verts[i].norm2(); - if(norm2 > max2){ - max2 = norm2; - } - } - this.boundingSphereRadius = Math.sqrt(max2); -}; - -var tempWorldVertex = new Vec3(); - -/** - * @method calculateWorldAABB - * @param {Vec3} pos - * @param {Quaternion} quat - * @param {Vec3} min - * @param {Vec3} max - */ -ConvexPolyhedron.prototype.calculateWorldAABB = function(pos,quat,min,max){ - var n = this.vertices.length, verts = this.vertices; - var minx,miny,minz,maxx,maxy,maxz; - for(var i=0; i maxx || maxx===undefined){ - maxx = v.x; - } - - if (v.y < miny || miny===undefined){ - miny = v.y; - } else if(v.y > maxy || maxy===undefined){ - maxy = v.y; - } - - if (v.z < minz || minz===undefined){ - minz = v.z; - } else if(v.z > maxz || maxz===undefined){ - maxz = v.z; - } - } - min.set(minx,miny,minz); - max.set(maxx,maxy,maxz); -}; - -/** - * Get approximate convex volume - * @method volume - * @return {Number} - */ -ConvexPolyhedron.prototype.volume = function(){ - return 4.0 * Math.PI * this.boundingSphereRadius / 3.0; -}; - -/** - * Get an average of all the vertices positions - * @method getAveragePointLocal - * @param {Vec3} target - * @return {Vec3} - */ -ConvexPolyhedron.prototype.getAveragePointLocal = function(target){ - target = target || new Vec3(); - var n = this.vertices.length, - verts = this.vertices; - for(var i=0; i0) || (r1>0 && r2<0)){ - return false; // Encountered some other sign. Exit. - } else { - } - } - - // If we got here, all dot products were of the same sign. - return positiveResult ? 1 : -1; -}; - -/** - * Get max and min dot product of a convex hull at position (pos,quat) projected onto an axis. Results are saved in the array maxmin. - * @static - * @method project - * @param {ConvexPolyhedron} hull - * @param {Vec3} axis - * @param {Vec3} pos - * @param {Quaternion} quat - * @param {array} result result[0] and result[1] will be set to maximum and minimum, respectively. - */ -var project_worldVertex = new Vec3(); -var project_localAxis = new Vec3(); -var project_localOrigin = new Vec3(); -ConvexPolyhedron.project = function(hull, axis, pos, quat, result){ - var n = hull.vertices.length, - worldVertex = project_worldVertex, - localAxis = project_localAxis, - max = 0, - min = 0, - localOrigin = project_localOrigin, - vs = hull.vertices; - - localOrigin.setZero(); - - // Transform the axis to local - Transform.vectorToLocalFrame(pos, quat, axis, localAxis); - Transform.pointToLocalFrame(pos, quat, localOrigin, localOrigin); - var add = localOrigin.dot(localAxis); - - min = max = vs[0].dot(localAxis); - - for(var i = 1; i < n; i++){ - var val = vs[i].dot(localAxis); - - if(val > max){ - max = val; - } - - if(val < min){ - min = val; - } - } - - min -= add; - max -= add; - - if(min > max){ - // Inconsistent - swap - var temp = min; - min = max; - max = temp; - } - // Output - result[0] = max; - result[1] = min; -}; diff --git a/src/shapes/ConvexPolyhedron.ts b/src/shapes/ConvexPolyhedron.ts new file mode 100644 index 000000000..79bdb5809 --- /dev/null +++ b/src/shapes/ConvexPolyhedron.ts @@ -0,0 +1,1050 @@ +import { Quaternion, Vector3 } from 'feng3d'; +import { Transform } from '../math/Transform'; +import { Shape } from './Shape'; + +export class ConvexPolyhedron extends Shape +{ + declare vertices: Vector3[]; + + worldVertices: Vector3[]; + worldVerticesNeedsUpdate: boolean; + + /** + * Array of integer arrays, indicating which vertices each face consists of + */ + declare faces: ({ connectedFaces: number[] } & (number[]))[]; + + declare faceNormals: Vector3[]; + + worldFaceNormalsNeedsUpdate: boolean; + worldFaceNormals: Vector3[]; + + uniqueEdges: Vector3[]; + + /** + * If given, these locally defined, normalized axes are the only ones being checked when doing separating axis check. + */ + uniqueAxes: Vector3[]; + + /** + * A set of polygons describing a convex shape. + * @class ConvexPolyhedron + * @constructor + * @extends Shape + * @description The shape MUST be convex for the code to work properly. No polygons may be coplanar (contained + * in the same 3D plane), instead these should be merged into one polygon. + * + * @param {array} points An array of Vec3's + * @param {array} faces Array of integer arrays, describing which vertices that is included in each face. + * + * @author qiao / https://github.com/qiao (original author, see https://github.com/qiao/three.js/commit/85026f0c769e4000148a67d45a9e9b9c5108836f) + * @author schteppe / https://github.com/schteppe + * @see http://www.altdevblogaday.com/2011/05/13/contact-generation-between-3d-convex-meshes/ + * @see http://bullet.googlecode.com/svn/trunk/src/BulletCollision/NarrowPhaseCollision/btPolyhedralContactClipping.cpp + * + * @todo Move the clipping functions to ContactGenerator? + * @todo Automatically merge coplanar polygons in constructor. + */ + constructor(points?: Vector3[], faces?: number[][], uniqueAxes?: Vector3[]) + { + super({ + type: Shape.types.CONVEXPOLYHEDRON + }); + + this.vertices = points || []; + + this.worldVertices = []; // World transformed version of .vertices + this.worldVerticesNeedsUpdate = true; + + this.faces = faces || []; + + this.faceNormals = []; + this.computeNormals(); + + this.worldFaceNormalsNeedsUpdate = true; + this.worldFaceNormals = []; // World transformed version of .faceNormals + + this.uniqueEdges = []; + + this.uniqueAxes = uniqueAxes ? uniqueAxes.slice() : null; + + this.computeEdges(); + this.updateBoundingSphereRadius(); + } + + /** + * Computes uniqueEdges + */ + computeEdges() + { + const faces = this.faces; + const vertices = this.vertices; + // const nv = vertices.length; + const edges = this.uniqueEdges; + + edges.length = 0; + + const edge = computeEdgesTmpEdge; + + for (let i = 0; i !== faces.length; i++) + { + const face = faces[i]; + const numVertices = face.length; + for (let j = 0; j !== numVertices; j++) + { + const k = (j + 1) % numVertices; + vertices[face[j]].subTo(vertices[face[k]], edge); + edge.normalize(); + let found = false; + for (let p = 0; p !== edges.length; p++) + { + if (edges[p].equals(edge) || edges[p].equals(edge)) + { + found = true; + break; + } + } + + if (!found) + { + edges.push(edge.clone()); + } + } + } + } + + /** + * Compute the normals of the faces. Will reuse existing Vec3 objects in the .faceNormals array if they exist. + */ + computeNormals() + { + this.faceNormals.length = this.faces.length; + + // Generate normals + for (let i = 0; i < this.faces.length; i++) + { + // Check so all vertices exists for this face + for (let j = 0; j < this.faces[i].length; j++) + { + if (!this.vertices[this.faces[i][j]]) + { + throw new Error(`Vertex ${this.faces[i][j]} not found!`); + } + } + + const n = this.faceNormals[i] || new Vector3(); + this.getFaceNormal(i, n); + n.negateTo(n); + this.faceNormals[i] = n; + const vertex = this.vertices[this.faces[i][0]]; + if (n.dot(vertex) < 0) + { + console.error(`.faceNormals[${i}] = Vec3(${n.toString()}) looks like it points into the shape? The vertices follow. Make sure they are ordered CCW around the normal, using the right hand rule.`); + for (let j = 0; j < this.faces[i].length; j++) + { + console.warn(`.vertices[${this.faces[i][j]}] = Vec3(${this.vertices[this.faces[i][j]].toString()})`); + } + } + } + } + + /** + * Get face normal given 3 vertices + * + * @param va + * @param vb + * @param vc + * @param target + */ + static computeNormal(va: Vector3, vb: Vector3, vc: Vector3, target: Vector3) + { + vb.subTo(va, ab); + vc.subTo(vb, cb); + cb.crossTo(ab, target); + if (!target.isZero()) + { + target.normalize(); + } + } + + /** + * Compute the normal of a face from its vertices + * + * @param i + * @param target + */ + getFaceNormal(i: number, target: Vector3) + { + const f = this.faces[i]; + const va = this.vertices[f[0]]; + const vb = this.vertices[f[1]]; + const vc = this.vertices[f[2]]; + + return ConvexPolyhedron.computeNormal(va, vb, vc, target); + } + + /** + * @param posA + * @param quatA + * @param hullB + * @param posB + * @param quatB + * @param separatingNormal + * @param minDist Clamp distance + * @param maxDist + * @param result The an array of contact point objects, see clipFaceAgainstHull + * @see http://bullet.googlecode.com/svn/trunk/src/BulletCollision/NarrowPhaseCollision/btPolyhedralContactClipping.cpp + */ + clipAgainstHull(posA: Vector3, quatA: Quaternion, hullB: ConvexPolyhedron, posB: Vector3, quatB: Quaternion, separatingNormal: Vector3, minDist: number, maxDist: number, result: { + point: Vector3; + normal: Vector3; + depth: number; + }[]) + { + const WorldNormal = cahWorldNormal; + // const hullA = this; + // const curMaxDist = maxDist; + let closestFaceB = -1; + let dmax = -Number.MAX_VALUE; + for (let face = 0; face < hullB.faces.length; face++) + { + WorldNormal.copy(hullB.faceNormals[face]); + quatB.vmult(WorldNormal, WorldNormal); + // posB.addTo(WorldNormal,WorldNormal); + const d = WorldNormal.dot(separatingNormal); + if (d > dmax) + { + dmax = d; + closestFaceB = face; + } + } + const worldVertsB1 = []; + const polyB = hullB.faces[closestFaceB]; + const numVertices = polyB.length; + for (let e0 = 0; e0 < numVertices; e0++) + { + const b = hullB.vertices[polyB[e0]]; + const worldb = new Vector3(); + worldb.copy(b); + quatB.vmult(worldb, worldb); + posB.addTo(worldb, worldb); + worldVertsB1.push(worldb); + } + + if (closestFaceB >= 0) + { + this.clipFaceAgainstHull(separatingNormal, + posA, + quatA, + worldVertsB1, + minDist, + maxDist, + result); + } + } + + /** + * Find the separating axis between this hull and another + * + * @param hullB + * @param posA + * @param quatA + * @param posB + * @param quatB + * @param target The target vector to save the axis in + * @param faceListA + * @param faceListB + * @returns Returns false if a separation is found, else true + */ + findSeparatingAxis(hullB: ConvexPolyhedron, posA: Vector3, quatA: Quaternion, posB: Vector3, quatB: Quaternion, target: Vector3, faceListA?: number[], faceListB?: number[]) + { + const faceANormalWS3 = fsaFaceANormalWS3; + const Worldnormal1 = fsaWorldnormal1; + const deltaC = fsaDeltaC; + const worldEdge0 = fsaWorldEdge0; + const worldEdge1 = fsaWorldEdge1; + const Cross = fsaCross; + + let dmin = Number.MAX_VALUE; + // eslint-disable-next-line @typescript-eslint/no-this-alias + const hullA = this; + let curPlaneTests = 0; + + if (!hullA.uniqueAxes) + { + const numFacesA = faceListA ? faceListA.length : hullA.faces.length; + + // Test face normals from hullA + for (let i = 0; i < numFacesA; i++) + { + const fi = faceListA ? faceListA[i] : i; + + // Get world face normal + faceANormalWS3.copy(hullA.faceNormals[fi]); + quatA.vmult(faceANormalWS3, faceANormalWS3); + + const d = hullA.testSepAxis(faceANormalWS3, hullB, posA, quatA, posB, quatB); + if (d === false) + { + return false; + } + + if (d < dmin) + { + dmin = d; + target.copy(faceANormalWS3); + } + } + } + else + { + // Test unique axes + for (let i = 0; i !== hullA.uniqueAxes.length; i++) + { + // Get world axis + quatA.vmult(hullA.uniqueAxes[i], faceANormalWS3); + + const d = hullA.testSepAxis(faceANormalWS3, hullB, posA, quatA, posB, quatB); + if (d === false) + { + return false; + } + + if (d < dmin) + { + dmin = d; + target.copy(faceANormalWS3); + } + } + } + + if (!hullB.uniqueAxes) + { + // Test face normals from hullB + const numFacesB = faceListB ? faceListB.length : hullB.faces.length; + for (let i = 0; i < numFacesB; i++) + { + const fi = faceListB ? faceListB[i] : i; + + Worldnormal1.copy(hullB.faceNormals[fi]); + quatB.vmult(Worldnormal1, Worldnormal1); + curPlaneTests++; + const d = hullA.testSepAxis(Worldnormal1, hullB, posA, quatA, posB, quatB); + if (d === false) + { + return false; + } + + if (d < dmin) + { + dmin = d; + target.copy(Worldnormal1); + } + } + } + else + { + // Test unique axes in B + for (let i = 0; i !== hullB.uniqueAxes.length; i++) + { + quatB.vmult(hullB.uniqueAxes[i], Worldnormal1); + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + curPlaneTests++; + const d = hullA.testSepAxis(Worldnormal1, hullB, posA, quatA, posB, quatB); + if (d === false) + { + return false; + } + + if (d < dmin) + { + dmin = d; + target.copy(Worldnormal1); + } + } + } + + // Test edges + for (let e0 = 0; e0 !== hullA.uniqueEdges.length; e0++) + { + // Get world edge + quatA.vmult(hullA.uniqueEdges[e0], worldEdge0); + + for (let e1 = 0; e1 !== hullB.uniqueEdges.length; e1++) + { + // Get world edge 2 + quatB.vmult(hullB.uniqueEdges[e1], worldEdge1); + worldEdge0.crossTo(worldEdge1, Cross); + + if (!Cross.equals(Vector3.ZERO)) + { + Cross.normalize(); + const dist = hullA.testSepAxis(Cross, hullB, posA, quatA, posB, quatB); + if (dist === false) + { + return false; + } + if (dist < dmin) + { + dmin = dist; + target.copy(Cross); + } + } + } + } + + posB.subTo(posA, deltaC); + if ((deltaC.dot(target)) > 0.0) + { + target.negateTo(target); + } + + return true; + } + + /** + * Test separating axis against two hulls. Both hulls are projected onto the axis and the overlap size is returned if there is one. + * + * @param axis + * @param hullB + * @param posA + * @param quatA + * @param posB + * @param quatB + * @return The overlap depth, or FALSE if no penetration. + */ + testSepAxis(axis: Vector3, hullB: ConvexPolyhedron, posA: Vector3, quatA: Quaternion, posB: Vector3, quatB: Quaternion) + { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const hullA = this; + ConvexPolyhedron.project(hullA, axis, posA, quatA, maxminA); + ConvexPolyhedron.project(hullB, axis, posB, quatB, maxminB); + const maxA = maxminA[0]; + const minA = maxminA[1]; + const maxB = maxminB[0]; + const minB = maxminB[1]; + if (maxA < minB || maxB < minA) + { + return false; // Separated + } + const d0 = maxA - minB; + const d1 = maxB - minA; + const depth = d0 < d1 ? d0 : d1; + + return depth; + } + + /** + * + * @param mass + * @param target + */ + calculateLocalInertia(mass: number, target: Vector3) + { + // Approximate with box inertia + // Exact inertia calculation is overkill, but see http://geometrictools.com/Documentation/PolyhedralMassProperties.pdf for the correct way to do it + this.computeLocalAABB(cliAabbmin, cliAabbmax); + const x = cliAabbmax.x - cliAabbmin.x; + const y = cliAabbmax.y - cliAabbmin.y; + const z = cliAabbmax.z - cliAabbmin.z; + target.x = 1.0 / 12.0 * mass * (2 * y * 2 * y + 2 * z * 2 * z); + target.y = 1.0 / 12.0 * mass * (2 * x * 2 * x + 2 * z * 2 * z); + target.z = 1.0 / 12.0 * mass * (2 * y * 2 * y + 2 * x * 2 * x); + } + + /** + * + * @param faceI Index of the face + */ + getPlaneConstantOfFace(faceI: number) + { + const f = this.faces[faceI]; + const n = this.faceNormals[faceI]; + const v = this.vertices[f[0]]; + const c = -n.dot(v); + + return c; + } + + /** + * Clip a face against a hull. + * + * @param separatingNormal + * @param posA + * @param quatA + * @param worldVertsB1 An array of Vec3 with vertices in the world frame. + * @param minDist Distance clamping + * @param maxDist + * @param result Array to store resulting contact points in. Will be objects with properties: point, depth, normal. These are represented in world coordinates. + */ + clipFaceAgainstHull(separatingNormal: Vector3, posA: Vector3, quatA: Quaternion, worldVertsB1: Vector3[], minDist: number, maxDist: number, result: { + point: Vector3; + normal: Vector3; + depth: number; + }[]) + { + const faceANormalWS = cfahFaceANormalWS; + const edge0 = cfahEdge0; + const WorldEdge0 = cfahWorldEdge0; + const worldPlaneAnormal1 = cfahWorldPlaneAnormal1; + const planeNormalWS1 = cfahPlaneNormalWS1; + const worldA1 = cfahWorldA1; + const localPlaneNormal = cfahLocalPlaneNormal; + const planeNormalWS = cfahPlaneNormalWS; + + // eslint-disable-next-line @typescript-eslint/no-this-alias + const hullA = this; + const worldVertsB2 = []; + const pVtxIn = worldVertsB1; + const pVtxOut = worldVertsB2; + // Find the face with normal closest to the separating axis + let closestFaceA = -1; + let dmin = Number.MAX_VALUE; + for (let face = 0; face < hullA.faces.length; face++) + { + faceANormalWS.copy(hullA.faceNormals[face]); + quatA.vmult(faceANormalWS, faceANormalWS); + // posA.addTo(faceANormalWS,faceANormalWS); + const d = faceANormalWS.dot(separatingNormal); + if (d < dmin) + { + dmin = d; + closestFaceA = face; + } + } + if (closestFaceA < 0) + { + // console.log("--- did not find any closest face... ---"); + return; + } + // console.log("closest A: ",closestFaceA); + // Get the face and construct connected faces + const polyA = hullA.faces[closestFaceA]; + polyA.connectedFaces = []; + for (let i = 0; i < hullA.faces.length; i++) + { + for (let j = 0; j < hullA.faces[i].length; j++) + { + if (polyA.indexOf(hullA.faces[i][j]) !== -1 /* Sharing a vertex*/ && i !== closestFaceA /* Not the one we are looking for connections from */ && polyA.connectedFaces.indexOf(i) === -1 /* Not already added */) + { + polyA.connectedFaces.push(i); + } + } + } + // Clip the polygon to the back of the planes of all faces of hull A, that are adjacent to the witness face + // const numContacts = pVtxIn.length; + const numVerticesA = polyA.length; + // const res = []; + for (let e0 = 0; e0 < numVerticesA; e0++) + { + const a = hullA.vertices[polyA[e0]]; + const b = hullA.vertices[polyA[(e0 + 1) % numVerticesA]]; + a.subTo(b, edge0); + WorldEdge0.copy(edge0); + quatA.vmult(WorldEdge0, WorldEdge0); + posA.addTo(WorldEdge0, WorldEdge0); + worldPlaneAnormal1.copy(this.faceNormals[closestFaceA]);// transA.getBasis()* btVector3(polyA.m_plane[0],polyA.m_plane[1],polyA.m_plane[2]); + quatA.vmult(worldPlaneAnormal1, worldPlaneAnormal1); + posA.addTo(worldPlaneAnormal1, worldPlaneAnormal1); + WorldEdge0.crossTo(worldPlaneAnormal1, planeNormalWS1); + planeNormalWS1.negateTo(planeNormalWS1); + worldA1.copy(a); + quatA.vmult(worldA1, worldA1); + posA.addTo(worldA1, worldA1); + const planeEqWS1 = -worldA1.dot(planeNormalWS1); + let planeEqWS: number; + // eslint-disable-next-line no-constant-condition + if (true) + { + const otherFace = polyA.connectedFaces[e0]; + localPlaneNormal.copy(this.faceNormals[otherFace]); + // const localPlaneEq = this.getPlaneConstantOfFace(otherFace); + + planeNormalWS.copy(localPlaneNormal); + quatA.vmult(planeNormalWS, planeNormalWS); + // posA.addTo(planeNormalWS,planeNormalWS); + // const planeEqWS = localPlaneEq - planeNormalWS.dot(posA); + } + else + { + planeNormalWS.copy(planeNormalWS1); + planeEqWS = planeEqWS1; + } + + // Clip face against our constructed plane + this.clipFaceAgainstPlane(pVtxIn, pVtxOut, planeNormalWS, planeEqWS); + + // Throw away all clipped points, but save the reamining until next clip + while (pVtxIn.length) + { + pVtxIn.shift(); + } + while (pVtxOut.length) + { + pVtxIn.push(pVtxOut.shift()); + } + } + + // console.log("Resulting points after clip:",pVtxIn); + + // only keep contact points that are behind the witness face + localPlaneNormal.copy(this.faceNormals[closestFaceA]); + + const localPlaneEq = this.getPlaneConstantOfFace(closestFaceA); + planeNormalWS.copy(localPlaneNormal); + quatA.vmult(planeNormalWS, planeNormalWS); + + const planeEqWS = localPlaneEq - planeNormalWS.dot(posA); + for (let i = 0; i < pVtxIn.length; i++) + { + let depth = planeNormalWS.dot(pVtxIn[i]) + planeEqWS; // ??? + /* console.log("depth calc from normal=",planeNormalWS.toString()," and constant "+planeEqWS+" and vertex ",pVtxIn[i].toString()," gives "+depth);*/ + if (depth <= minDist) + { + console.log(`clamped: depth=${depth} to minDist=${String(minDist)}`); + depth = minDist; + } + + if (depth <= maxDist) + { + const point = pVtxIn[i]; + if (depth <= 0) + { + /* console.log("Got contact point ",point.toString(), + ", depth=",depth, + "contact normal=",separatingNormal.toString(), + "plane",planeNormalWS.toString(), + "planeConstant",planeEqWS);*/ + const p = { + point, + normal: planeNormalWS, + depth, + }; + result.push(p); + } + } + } + } + + /** + * Clip a face in a hull against the back of a plane. + * + * @param inVertices + * @param outVertices + * @param planeNormal + * @param planeConstant The constant in the mathematical plane equation + */ + clipFaceAgainstPlane(inVertices: Vector3[], outVertices: Vector3[], planeNormal: Vector3, planeConstant: number) + { + let nDotFirst: number; let nDotLast: number; + const numVerts = inVertices.length; + + if (numVerts < 2) + { + return outVertices; + } + + let firstVertex = inVertices[inVertices.length - 1]; + let lastVertex = inVertices[0]; + + nDotFirst = planeNormal.dot(firstVertex) + planeConstant; + + for (let vi = 0; vi < numVerts; vi++) + { + lastVertex = inVertices[vi]; + nDotLast = planeNormal.dot(lastVertex) + planeConstant; + if (nDotFirst < 0) + { + if (nDotLast < 0) + { + // Start < 0, end < 0, so output lastVertex + const newv = new Vector3(); + newv.copy(lastVertex); + outVertices.push(newv); + } + else + { + // Start < 0, end >= 0, so output intersection + const newv = new Vector3(); + firstVertex.lerpNumberTo(lastVertex, + nDotFirst / (nDotFirst - nDotLast), + newv); + outVertices.push(newv); + } + } + else + if (nDotLast < 0) + { + // Start >= 0, end < 0 so output intersection and end + const newv = new Vector3(); + firstVertex.lerpNumberTo(lastVertex, + nDotFirst / (nDotFirst - nDotLast), + newv); + outVertices.push(newv); + outVertices.push(lastVertex); + } + firstVertex = lastVertex; + nDotFirst = nDotLast; + } + + return outVertices; + } + + // Updates .worldVertices and sets .worldVerticesNeedsUpdate to false. + computeWorldVertices(position: Vector3, quat: Quaternion) + { + const N = this.vertices.length; + while (this.worldVertices.length < N) + { + this.worldVertices.push(new Vector3()); + } + + const verts = this.vertices; + const worldVerts = this.worldVertices; + for (let i = 0; i !== N; i++) + { + quat.vmult(verts[i], worldVerts[i]); + position.addTo(worldVerts[i], worldVerts[i]); + } + + this.worldVerticesNeedsUpdate = false; + } + + computeLocalAABB(aabbmin: Vector3, aabbmax: Vector3) + { + const n = this.vertices.length; + const vertices = this.vertices; + // const worldVert = computeLocalAABBWorldVert; + + aabbmin.set(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE); + aabbmax.set(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE); + + for (let i = 0; i < n; i++) + { + const v = vertices[i]; + if (v.x < aabbmin.x) + { + aabbmin.x = v.x; + } + else if (v.x > aabbmax.x) + { + aabbmax.x = v.x; + } + if (v.y < aabbmin.y) + { + aabbmin.y = v.y; + } + else if (v.y > aabbmax.y) + { + aabbmax.y = v.y; + } + if (v.z < aabbmin.z) + { + aabbmin.z = v.z; + } + else if (v.z > aabbmax.z) + { + aabbmax.z = v.z; + } + } + } + + /** + * Updates .worldVertices and sets .worldVerticesNeedsUpdate to false. + * + * @param quat + */ + computeWorldFaceNormals(quat: Quaternion) + { + const N = this.faceNormals.length; + while (this.worldFaceNormals.length < N) + { + this.worldFaceNormals.push(new Vector3()); + } + + const normals = this.faceNormals; + const worldNormals = this.worldFaceNormals; + for (let i = 0; i !== N; i++) + { + quat.vmult(normals[i], worldNormals[i]); + } + + this.worldFaceNormalsNeedsUpdate = false; + } + + updateBoundingSphereRadius() + { + // Assume points are distributed with local (0,0,0) as center + let max2 = 0; + const verts = this.vertices; + for (let i = 0, N = verts.length; i !== N; i++) + { + const norm2 = verts[i].lengthSquared; + if (norm2 > max2) + { + max2 = norm2; + } + } + this.boundingSphereRadius = Math.sqrt(max2); + } + + /** + * + * @param pos + * @param quat + * @param min + * @param max + */ + calculateWorldAABB(pos: Vector3, quat: Quaternion, min: Vector3, max: Vector3) + { + const n = this.vertices.length; const + verts = this.vertices; + let minx: number; let miny: number; let minz: number; let maxx: number; let maxy: number; let + maxz: number; + for (let i = 0; i < n; i++) + { + tempWorldVertex.copy(verts[i]); + quat.vmult(tempWorldVertex, tempWorldVertex); + pos.addTo(tempWorldVertex, tempWorldVertex); + const v = tempWorldVertex; + if (v.x < minx || minx === undefined) + { + minx = v.x; + } + else if (v.x > maxx || maxx === undefined) + { + maxx = v.x; + } + + if (v.y < miny || miny === undefined) + { + miny = v.y; + } + else if (v.y > maxy || maxy === undefined) + { + maxy = v.y; + } + + if (v.z < minz || minz === undefined) + { + minz = v.z; + } + else if (v.z > maxz || maxz === undefined) + { + maxz = v.z; + } + } + min.set(minx, miny, minz); + max.set(maxx, maxy, maxz); + } + + /** + * Get approximate convex volume + */ + volume() + { + return 4.0 * Math.PI * this.boundingSphereRadius / 3.0; + } + + /** + * Get an average of all the vertices positions + * + * @param target + */ + getAveragePointLocal(target: Vector3) + { + target = target || new Vector3(); + const n = this.vertices.length; + const verts = this.vertices; + for (let i = 0; i < n; i++) + { + target.addTo(verts[i], target); + } + target.scaleNumberTo(1 / n, target); + + return target; + } + + /** + * Transform all local points. Will change the .vertices + * + * @param offset + * @param quat + */ + transformAllPoints(offset: Vector3, quat: Quaternion) + { + const n = this.vertices.length; + const verts = this.vertices; + + // Apply rotation + if (quat) + { + // Rotate vertices + for (let i = 0; i < n; i++) + { + const v = verts[i]; + quat.vmult(v, v); + } + // Rotate face normals + for (let i = 0; i < this.faceNormals.length; i++) + { + const v = this.faceNormals[i]; + quat.vmult(v, v); + } + /* + // Rotate edges + for(let i=0; i 0) || (r1 > 0 && r2 < 0)) + { + return false; // Encountered some other sign. Exit. + } + } + + // If we got here, all dot products were of the same sign. + return positiveResult ? 1 : -1; + } + + /** + * Get max and min dot product of a convex hull at position (pos,quat) projected onto an axis. Results are saved in the array maxmin. + * + * @param hull + * @param axis + * @param pos + * @param quat + * @param result result[0] and result[1] will be set to maximum and minimum, respectively. + */ + static project(hull: ConvexPolyhedron, axis: Vector3, pos: Vector3, quat: Quaternion, result: number[]) + { + const n = hull.vertices.length; + // const worldVertex = project_worldVertex; + const localAxis = projectLocalAxis; + let max = 0; + let min = 0; + const localOrigin = projectLocalOrigin; + const vs = hull.vertices; + + localOrigin.setZero(); + + // Transform the axis to local + Transform.vectorToLocalFrame(pos, quat, axis, localAxis); + Transform.pointToLocalFrame(pos, quat, localOrigin, localOrigin); + const add = localOrigin.dot(localAxis); + + min = max = vs[0].dot(localAxis); + + for (let i = 1; i < n; i++) + { + const val = vs[i].dot(localAxis); + + if (val > max) + { + max = val; + } + + if (val < min) + { + min = val; + } + } + + min -= add; + max -= add; + + if (min > max) + { + // Inconsistent - swap + const temp = min; + min = max; + max = temp; + } + // Output + result[0] = max; + result[1] = min; + } +} + +const computeEdgesTmpEdge = new Vector3(); + +const cb = new Vector3(); +const ab = new Vector3(); +const cahWorldNormal = new Vector3(); + +const fsaFaceANormalWS3 = new Vector3(); +const fsaWorldnormal1 = new Vector3(); +const fsaDeltaC = new Vector3(); +const fsaWorldEdge0 = new Vector3(); +const fsaWorldEdge1 = new Vector3(); +const fsaCross = new Vector3(); + +const maxminA = []; const + maxminB = []; + +const cliAabbmin = new Vector3(); +const cliAabbmax = new Vector3(); + +const cfahFaceANormalWS = new Vector3(); +const cfahEdge0 = new Vector3(); +const cfahWorldEdge0 = new Vector3(); +const cfahWorldPlaneAnormal1 = new Vector3(); +const cfahPlaneNormalWS1 = new Vector3(); +const cfahWorldA1 = new Vector3(); +const cfahLocalPlaneNormal = new Vector3(); +const cfahPlaneNormalWS = new Vector3(); + +// const computeLocalAABBWorldVert = new Vector3(); + +const tempWorldVertex = new Vector3(); + +const ConvexPolyhedronPointIsInside = new Vector3(); +const ConvexPolyhedronVToP = new Vector3(); +const ConvexPolyhedronVToPointInside = new Vector3(); +// const project_worldVertex = new Vector3(); +const projectLocalAxis = new Vector3(); +const projectLocalOrigin = new Vector3(); diff --git a/src/shapes/Cylinder.js b/src/shapes/Cylinder.js deleted file mode 100644 index 39698be07..000000000 --- a/src/shapes/Cylinder.js +++ /dev/null @@ -1,79 +0,0 @@ -module.exports = Cylinder; - -var Shape = require('./Shape'); -var Vec3 = require('../math/Vec3'); -var Quaternion = require('../math/Quaternion'); -var ConvexPolyhedron = require('./ConvexPolyhedron'); - -/** - * @class Cylinder - * @constructor - * @extends ConvexPolyhedron - * @author schteppe / https://github.com/schteppe - * @param {Number} radiusTop - * @param {Number} radiusBottom - * @param {Number} height - * @param {Number} numSegments The number of segments to build the cylinder out of - */ -function Cylinder( radiusTop, radiusBottom, height , numSegments ) { - var N = numSegments, - verts = [], - axes = [], - faces = [], - bottomface = [], - topface = [], - cos = Math.cos, - sin = Math.sin; - - // First bottom point - verts.push(new Vec3(radiusBottom*cos(0), - radiusBottom*sin(0), - -height*0.5)); - bottomface.push(0); - - // First top point - verts.push(new Vec3(radiusTop*cos(0), - radiusTop*sin(0), - height*0.5)); - topface.push(1); - - for(var i=0; i { convex: ..., offset: ... } - // for example: - // _cachedPillars["0_2_1"] - this._cachedPillars = {}; -} -Heightfield.prototype = new Shape(); - -/** - * Call whenever you change the data array. - * @method update - */ -Heightfield.prototype.update = function(){ - this._cachedPillars = {}; -}; - -/** - * Update the .minValue property - * @method updateMinValue - */ -Heightfield.prototype.updateMinValue = function(){ - var data = this.data; - var minValue = data[0][0]; - for(var i=0; i !== data.length; i++){ - for(var j=0; j !== data[i].length; j++){ - var v = data[i][j]; - if(v < minValue){ - minValue = v; - } - } - } - this.minValue = minValue; -}; - -/** - * Update the .maxValue property - * @method updateMaxValue - */ -Heightfield.prototype.updateMaxValue = function(){ - var data = this.data; - var maxValue = data[0][0]; - for(var i=0; i !== data.length; i++){ - for(var j=0; j !== data[i].length; j++){ - var v = data[i][j]; - if(v > maxValue){ - maxValue = v; - } - } - } - this.maxValue = maxValue; -}; - -/** - * Set the height value at an index. Don't forget to update maxValue and minValue after you're done. - * @method setHeightValueAtIndex - * @param {integer} xi - * @param {integer} yi - * @param {number} value - */ -Heightfield.prototype.setHeightValueAtIndex = function(xi, yi, value){ - var data = this.data; - data[xi][yi] = value; - - // Invalidate cache - this.clearCachedConvexTrianglePillar(xi, yi, false); - if(xi > 0){ - this.clearCachedConvexTrianglePillar(xi - 1, yi, true); - this.clearCachedConvexTrianglePillar(xi - 1, yi, false); - } - if(yi > 0){ - this.clearCachedConvexTrianglePillar(xi, yi - 1, true); - this.clearCachedConvexTrianglePillar(xi, yi - 1, false); - } - if(yi > 0 && xi > 0){ - this.clearCachedConvexTrianglePillar(xi - 1, yi - 1, true); - } -}; - -/** - * Get max/min in a rectangle in the matrix data - * @method getRectMinMax - * @param {integer} iMinX - * @param {integer} iMinY - * @param {integer} iMaxX - * @param {integer} iMaxY - * @param {array} [result] An array to store the results in. - * @return {array} The result array, if it was passed in. Minimum will be at position 0 and max at 1. - */ -Heightfield.prototype.getRectMinMax = function (iMinX, iMinY, iMaxX, iMaxY, result) { - result = result || []; - - // Get max and min of the data - var data = this.data, - max = this.minValue; // Set first value - for(var i = iMinX; i <= iMaxX; i++){ - for(var j = iMinY; j <= iMaxY; j++){ - var height = data[i][j]; - if(height > max){ - max = height; - } - } - } - - result[0] = this.minValue; - result[1] = max; -}; - - - -/** - * Get the index of a local position on the heightfield. The indexes indicate the rectangles, so if your terrain is made of N x N height data points, you will have rectangle indexes ranging from 0 to N-1. - * @method getIndexOfPosition - * @param {number} x - * @param {number} y - * @param {array} result Two-element array - * @param {boolean} clamp If the position should be clamped to the heightfield edge. - * @return {boolean} - */ -Heightfield.prototype.getIndexOfPosition = function (x, y, result, clamp) { - - // Get the index of the data points to test against - var w = this.elementSize; - var data = this.data; - var xi = Math.floor(x / w); - var yi = Math.floor(y / w); - - result[0] = xi; - result[1] = yi; - - if(clamp){ - // Clamp index to edges - if(xi < 0){ xi = 0; } - if(yi < 0){ yi = 0; } - if(xi >= data.length - 1){ xi = data.length - 1; } - if(yi >= data[0].length - 1){ yi = data[0].length - 1; } - } - - // Bail out if we are out of the terrain - if(xi < 0 || yi < 0 || xi >= data.length-1 || yi >= data[0].length-1){ - return false; - } - - return true; -}; - - -var getHeightAt_idx = []; -var getHeightAt_weights = new Vec3(); -var getHeightAt_a = new Vec3(); -var getHeightAt_b = new Vec3(); -var getHeightAt_c = new Vec3(); - -Heightfield.prototype.getTriangleAt = function(x, y, edgeClamp, a, b, c){ - var idx = getHeightAt_idx; - this.getIndexOfPosition(x, y, idx, edgeClamp); - var xi = idx[0]; - var yi = idx[1]; - - var data = this.data; - if(edgeClamp){ - xi = Math.min(data.length - 2, Math.max(0, xi)); - yi = Math.min(data[0].length - 2, Math.max(0, yi)); - } - - var elementSize = this.elementSize; - var lowerDist2 = Math.pow(x / elementSize - xi, 2) + Math.pow(y / elementSize - yi, 2); - var upperDist2 = Math.pow(x / elementSize - (xi + 1), 2) + Math.pow(y / elementSize - (yi + 1), 2); - var upper = lowerDist2 > upperDist2; - this.getTriangle(xi, yi, upper, a, b, c); - return upper; -}; - -var getNormalAt_a = new Vec3(); -var getNormalAt_b = new Vec3(); -var getNormalAt_c = new Vec3(); -var getNormalAt_e0 = new Vec3(); -var getNormalAt_e1 = new Vec3(); -Heightfield.prototype.getNormalAt = function(x, y, edgeClamp, result){ - var a = getNormalAt_a; - var b = getNormalAt_b; - var c = getNormalAt_c; - var e0 = getNormalAt_e0; - var e1 = getNormalAt_e1; - this.getTriangleAt(x, y, edgeClamp, a, b, c); - b.vsub(a, e0); - c.vsub(a, e1); - e0.cross(e1, result); - result.normalize(); -}; - - -/** - * Get an AABB of a square in the heightfield - * @param {number} xi - * @param {number} yi - * @param {AABB} result - */ -Heightfield.prototype.getAabbAtIndex = function(xi, yi, result){ - var data = this.data; - var elementSize = this.elementSize; - - result.lowerBound.set( - xi * elementSize, - yi * elementSize, - data[xi][yi] - ); - result.upperBound.set( - (xi + 1) * elementSize, - (yi + 1) * elementSize, - data[xi + 1][yi + 1] - ); -}; - - -/** - * Get the height in the heightfield at a given position - * @param {number} x - * @param {number} y - * @param {boolean} edgeClamp - * @return {number} - */ -Heightfield.prototype.getHeightAt = function(x, y, edgeClamp){ - var data = this.data; - var a = getHeightAt_a; - var b = getHeightAt_b; - var c = getHeightAt_c; - var idx = getHeightAt_idx; - - this.getIndexOfPosition(x, y, idx, edgeClamp); - var xi = idx[0]; - var yi = idx[1]; - if(edgeClamp){ - xi = Math.min(data.length - 2, Math.max(0, xi)); - yi = Math.min(data[0].length - 2, Math.max(0, yi)); - } - var upper = this.getTriangleAt(x, y, edgeClamp, a, b, c); - barycentricWeights(x, y, a.x, a.y, b.x, b.y, c.x, c.y, getHeightAt_weights); - - var w = getHeightAt_weights; - - if(upper){ - - // Top triangle verts - return data[xi + 1][yi + 1] * w.x + data[xi][yi + 1] * w.y + data[xi + 1][yi] * w.z; - - } else { - - // Top triangle verts - return data[xi][yi] * w.x + data[xi + 1][yi] * w.y + data[xi][yi + 1] * w.z; - } -}; - -// from https://en.wikipedia.org/wiki/Barycentric_coordinate_system -function barycentricWeights(x, y, ax, ay, bx, by, cx, cy, result){ - result.x = ((by - cy) * (x - cx) + (cx - bx) * (y - cy)) / ((by - cy) * (ax - cx) + (cx - bx) * (ay - cy)); - result.y = ((cy - ay) * (x - cx) + (ax - cx) * (y - cy)) / ((by - cy) * (ax - cx) + (cx - bx) * (ay - cy)); - result.z = 1 - result.x - result.y; -} - -Heightfield.prototype.getCacheConvexTrianglePillarKey = function(xi, yi, getUpperTriangle){ - return xi + '_' + yi + '_' + (getUpperTriangle ? 1 : 0); -}; - -Heightfield.prototype.getCachedConvexTrianglePillar = function(xi, yi, getUpperTriangle){ - return this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi, yi, getUpperTriangle)]; -}; - -Heightfield.prototype.setCachedConvexTrianglePillar = function(xi, yi, getUpperTriangle, convex, offset){ - this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi, yi, getUpperTriangle)] = { - convex: convex, - offset: offset - }; -}; - -Heightfield.prototype.clearCachedConvexTrianglePillar = function(xi, yi, getUpperTriangle){ - delete this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi, yi, getUpperTriangle)]; -}; - -/** - * Get a triangle from the heightfield - * @param {number} xi - * @param {number} yi - * @param {boolean} upper - * @param {Vec3} a - * @param {Vec3} b - * @param {Vec3} c - */ -Heightfield.prototype.getTriangle = function(xi, yi, upper, a, b, c){ - var data = this.data; - var elementSize = this.elementSize; - - if(upper){ - - // Top triangle verts - a.set( - (xi + 1) * elementSize, - (yi + 1) * elementSize, - data[xi + 1][yi + 1] - ); - b.set( - xi * elementSize, - (yi + 1) * elementSize, - data[xi][yi + 1] - ); - c.set( - (xi + 1) * elementSize, - yi * elementSize, - data[xi + 1][yi] - ); - - } else { - - // Top triangle verts - a.set( - xi * elementSize, - yi * elementSize, - data[xi][yi] - ); - b.set( - (xi + 1) * elementSize, - yi * elementSize, - data[xi + 1][yi] - ); - c.set( - xi * elementSize, - (yi + 1) * elementSize, - data[xi][yi + 1] - ); - } -}; - -/** - * Get a triangle in the terrain in the form of a triangular convex shape. - * @method getConvexTrianglePillar - * @param {integer} i - * @param {integer} j - * @param {boolean} getUpperTriangle - */ -Heightfield.prototype.getConvexTrianglePillar = function(xi, yi, getUpperTriangle){ - var result = this.pillarConvex; - var offsetResult = this.pillarOffset; - - if(this.cacheEnabled){ - var data = this.getCachedConvexTrianglePillar(xi, yi, getUpperTriangle); - if(data){ - this.pillarConvex = data.convex; - this.pillarOffset = data.offset; - return; - } - - result = new ConvexPolyhedron(); - offsetResult = new Vec3(); - - this.pillarConvex = result; - this.pillarOffset = offsetResult; - } - - var data = this.data; - var elementSize = this.elementSize; - var faces = result.faces; - - // Reuse verts if possible - result.vertices.length = 6; - for (var i = 0; i < 6; i++) { - if(!result.vertices[i]){ - result.vertices[i] = new Vec3(); - } - } - - // Reuse faces if possible - faces.length = 5; - for (var i = 0; i < 5; i++) { - if(!faces[i]){ - faces[i] = []; - } - } - - var verts = result.vertices; - - var h = (Math.min( - data[xi][yi], - data[xi+1][yi], - data[xi][yi+1], - data[xi+1][yi+1] - ) - this.minValue ) / 2 + this.minValue; - - if (!getUpperTriangle) { - - // Center of the triangle pillar - all polygons are given relative to this one - offsetResult.set( - (xi + 0.25) * elementSize, // sort of center of a triangle - (yi + 0.25) * elementSize, - h // vertical center - ); - - // Top triangle verts - verts[0].set( - -0.25 * elementSize, - -0.25 * elementSize, - data[xi][yi] - h - ); - verts[1].set( - 0.75 * elementSize, - -0.25 * elementSize, - data[xi + 1][yi] - h - ); - verts[2].set( - -0.25 * elementSize, - 0.75 * elementSize, - data[xi][yi + 1] - h - ); - - // bottom triangle verts - verts[3].set( - -0.25 * elementSize, - -0.25 * elementSize, - -h-1 - ); - verts[4].set( - 0.75 * elementSize, - -0.25 * elementSize, - -h-1 - ); - verts[5].set( - -0.25 * elementSize, - 0.75 * elementSize, - -h-1 - ); - - // top triangle - faces[0][0] = 0; - faces[0][1] = 1; - faces[0][2] = 2; - - // bottom triangle - faces[1][0] = 5; - faces[1][1] = 4; - faces[1][2] = 3; - - // -x facing quad - faces[2][0] = 0; - faces[2][1] = 2; - faces[2][2] = 5; - faces[2][3] = 3; - - // -y facing quad - faces[3][0] = 1; - faces[3][1] = 0; - faces[3][2] = 3; - faces[3][3] = 4; - - // +xy facing quad - faces[4][0] = 4; - faces[4][1] = 5; - faces[4][2] = 2; - faces[4][3] = 1; - - - } else { - - // Center of the triangle pillar - all polygons are given relative to this one - offsetResult.set( - (xi + 0.75) * elementSize, // sort of center of a triangle - (yi + 0.75) * elementSize, - h // vertical center - ); - - // Top triangle verts - verts[0].set( - 0.25 * elementSize, - 0.25 * elementSize, - data[xi + 1][yi + 1] - h - ); - verts[1].set( - -0.75 * elementSize, - 0.25 * elementSize, - data[xi][yi + 1] - h - ); - verts[2].set( - 0.25 * elementSize, - -0.75 * elementSize, - data[xi + 1][yi] - h - ); - - // bottom triangle verts - verts[3].set( - 0.25 * elementSize, - 0.25 * elementSize, - - h-1 - ); - verts[4].set( - -0.75 * elementSize, - 0.25 * elementSize, - - h-1 - ); - verts[5].set( - 0.25 * elementSize, - -0.75 * elementSize, - - h-1 - ); - - // Top triangle - faces[0][0] = 0; - faces[0][1] = 1; - faces[0][2] = 2; - - // bottom triangle - faces[1][0] = 5; - faces[1][1] = 4; - faces[1][2] = 3; - - // +x facing quad - faces[2][0] = 2; - faces[2][1] = 5; - faces[2][2] = 3; - faces[2][3] = 0; - - // +y facing quad - faces[3][0] = 3; - faces[3][1] = 4; - faces[3][2] = 1; - faces[3][3] = 0; - - // -xy facing quad - faces[4][0] = 1; - faces[4][1] = 4; - faces[4][2] = 5; - faces[4][3] = 2; - } - - result.computeNormals(); - result.computeEdges(); - result.updateBoundingSphereRadius(); - - this.setCachedConvexTrianglePillar(xi, yi, getUpperTriangle, result, offsetResult); -}; - -Heightfield.prototype.calculateLocalInertia = function(mass, target){ - target = target || new Vec3(); - target.set(0, 0, 0); - return target; -}; - -Heightfield.prototype.volume = function(){ - return Number.MAX_VALUE; // The terrain is infinite -}; - -Heightfield.prototype.calculateWorldAABB = function(pos, quat, min, max){ - // TODO: do it properly - min.set(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE); - max.set(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE); -}; - -Heightfield.prototype.updateBoundingSphereRadius = function(){ - // Use the bounding box of the min/max values - var data = this.data, - s = this.elementSize; - this.boundingSphereRadius = new Vec3(data.length * s, data[0].length * s, Math.max(Math.abs(this.maxValue), Math.abs(this.minValue))).norm(); -}; - -/** - * Sets the height values from an image. Currently only supported in browser. - * @method setHeightsFromImage - * @param {Image} image - * @param {Vec3} scale - */ -Heightfield.prototype.setHeightsFromImage = function(image, scale){ - var canvas = document.createElement('canvas'); - canvas.width = image.width; - canvas.height = image.height; - var context = canvas.getContext('2d'); - context.drawImage(image, 0, 0); - var imageData = context.getImageData(0, 0, image.width, image.height); - - var matrix = this.data; - matrix.length = 0; - this.elementSize = Math.abs(scale.x) / imageData.width; - for(var i=0; i { convex: ..., offset: ... } + // for example: + // _cachedPillars["0_2_1"] + this._cachedPillars = {}; + } + + /** + * Call whenever you change the data array. + */ + update() + { + this._cachedPillars = {}; + } + + /** + * Update the .minValue property + */ + updateMinValue() + { + const data = this.data; + let minValue = data[0][0]; + for (let i = 0; i !== data.length; i++) + { + for (let j = 0; j !== data[i].length; j++) + { + const v = data[i][j]; + if (v < minValue) + { + minValue = v; + } + } + } + this.minValue = minValue; + } + + /** + * Update the .maxValue property + */ + updateMaxValue() + { + const data = this.data; + let maxValue = data[0][0]; + for (let i = 0; i !== data.length; i++) + { + for (let j = 0; j !== data[i].length; j++) + { + const v = data[i][j]; + if (v > maxValue) + { + maxValue = v; + } + } + } + this.maxValue = maxValue; + } + + /** + * Set the height value at an index. Don't forget to update maxValue and minValue after you're done. + * + * @param xi + * @param yi + * @param value + */ + setHeightValueAtIndex(xi: number, yi: number, value: number) + { + const data = this.data; + data[xi][yi] = value; + + // Invalidate cache + this.clearCachedConvexTrianglePillar(xi, yi, false); + if (xi > 0) + { + this.clearCachedConvexTrianglePillar(xi - 1, yi, true); + this.clearCachedConvexTrianglePillar(xi - 1, yi, false); + } + if (yi > 0) + { + this.clearCachedConvexTrianglePillar(xi, yi - 1, true); + this.clearCachedConvexTrianglePillar(xi, yi - 1, false); + } + if (yi > 0 && xi > 0) + { + this.clearCachedConvexTrianglePillar(xi - 1, yi - 1, true); + } + } + + /** + * Get max/min in a rectangle in the matrix data + * + * @param iMinX + * @param iMinY + * @param iMaxX + * @param iMaxY + * @param result An array to store the results in. + * @return The result array, if it was passed in. Minimum will be at position 0 and max at 1. + */ + getRectMinMax(iMinX: number, iMinY: number, iMaxX: number, iMaxY: number, result: number[]) + { + result = result || []; + + // Get max and min of the data + const data = this.data; + let max = this.minValue; // Set first value + for (let i = iMinX; i <= iMaxX; i++) + { + for (let j = iMinY; j <= iMaxY; j++) + { + const height = data[i][j]; + if (height > max) + { + max = height; + } + } + } + + result[0] = this.minValue; + result[1] = max; + } + + /** + * Get the index of a local position on the heightfield. The indexes indicate the rectangles, so if your terrain is made of N x N height data points, you will have rectangle indexes ranging from 0 to N-1. + * + * @param x + * @param y + * @param result Two-element array + * @param clamp If the position should be clamped to the heightfield edge. + */ + getIndexOfPosition(x: number, y: number, result: number[], clamp?: boolean) + { + // Get the index of the data points to test against + const w = this.elementSize; + const data = this.data; + let xi = Math.floor(x / w); + let yi = Math.floor(y / w); + + result[0] = xi; + result[1] = yi; + + if (clamp) + { + // Clamp index to edges + if (xi < 0) { xi = 0; } + if (yi < 0) { yi = 0; } + if (xi >= data.length - 1) { xi = data.length - 1; } + if (yi >= data[0].length - 1) { yi = data[0].length - 1; } + } + + // Bail out if we are out of the terrain + if (xi < 0 || yi < 0 || xi >= data.length - 1 || yi >= data[0].length - 1) + { + return false; + } + + return true; + } + + getTriangleAt(x: number, y: number, edgeClamp: boolean, a: Vector3, b: Vector3, c: Vector3) + { + const idx = getHeightAtIdx; + this.getIndexOfPosition(x, y, idx, edgeClamp); + let xi = idx[0]; + let yi = idx[1]; + + const data = this.data; + if (edgeClamp) + { + xi = Math.min(data.length - 2, Math.max(0, xi)); + yi = Math.min(data[0].length - 2, Math.max(0, yi)); + } + + const elementSize = this.elementSize; + const lowerDist2 = Math.pow(x / elementSize - xi, 2) + Math.pow(y / elementSize - yi, 2); + const upperDist2 = Math.pow(x / elementSize - (xi + 1), 2) + Math.pow(y / elementSize - (yi + 1), 2); + const upper = lowerDist2 > upperDist2; + this.getTriangle(xi, yi, upper, a, b, c); + + return upper; + } + + getNormalAt(x: number, y: number, edgeClamp: boolean, result: Vector3) + { + const a = getNormalAtA; + const b = getNormalAtB; + const c = getNormalAtC; + const e0 = getNormalAtE0; + const e1 = getNormalAtE1; + this.getTriangleAt(x, y, edgeClamp, a, b, c); + b.subTo(a, e0); + c.subTo(a, e1); + e0.crossTo(e1, result); + result.normalize(); + } + + /** + * Get an AABB of a square in the heightfield + * + * @param xi + * @param yi + * @param result + */ + getAabbAtIndex(xi: number, yi: number, result: Box3) + { + const data = this.data; + const elementSize = this.elementSize; + + result.min.set( + xi * elementSize, + yi * elementSize, + data[xi][yi] + ); + result.max.set( + (xi + 1) * elementSize, + (yi + 1) * elementSize, + data[xi + 1][yi + 1] + ); + } + + /** + * Get the height in the heightfield at a given position + * + * @param x + * @param y + * @param edgeClamp + */ + getHeightAt(x: number, y: number, edgeClamp?: boolean) + { + const data = this.data; + const a = getHeightAtA; + const b = getHeightAtB; + const c = getHeightAtC; + const idx = getHeightAtIdx; + + this.getIndexOfPosition(x, y, idx, edgeClamp); + let xi = idx[0]; + let yi = idx[1]; + if (edgeClamp) + { + xi = Math.min(data.length - 2, Math.max(0, xi)); + yi = Math.min(data[0].length - 2, Math.max(0, yi)); + } + const upper = this.getTriangleAt(x, y, edgeClamp, a, b, c); + barycentricWeights(x, y, a.x, a.y, b.x, b.y, c.x, c.y, getHeightAtWeights); + + const w = getHeightAtWeights; + + if (upper) + { + // Top triangle verts + return data[xi + 1][yi + 1] * w.x + data[xi][yi + 1] * w.y + data[xi + 1][yi] * w.z; + } + + // Top triangle verts + return data[xi][yi] * w.x + data[xi + 1][yi] * w.y + data[xi][yi + 1] * w.z; + } + + getCacheConvexTrianglePillarKey(xi: number, yi: number, getUpperTriangle: boolean) + { + return `${xi}_${yi}_${getUpperTriangle ? 1 : 0}`; + } + + getCachedConvexTrianglePillar(xi: number, yi: number, getUpperTriangle: boolean) + { + return this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi, yi, getUpperTriangle)]; + } + + setCachedConvexTrianglePillar(xi: number, yi: number, getUpperTriangle: boolean, convex: ConvexPolyhedron, offset: Vector3) + { + this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi, yi, getUpperTriangle)] = { + convex, + offset + }; + } + + clearCachedConvexTrianglePillar(xi: number, yi: number, getUpperTriangle: boolean) + { + delete this._cachedPillars[this.getCacheConvexTrianglePillarKey(xi, yi, getUpperTriangle)]; + } + + /** + * Get a triangle from the heightfield + * + * @param xi + * @param yi + * @param upper + * @param a + * @param b + * @param c + */ + getTriangle(xi: number, yi: number, upper: boolean, a: Vector3, b: Vector3, c: Vector3) + { + const data = this.data; + const elementSize = this.elementSize; + + if (upper) + { + // Top triangle verts + a.set( + (xi + 1) * elementSize, + (yi + 1) * elementSize, + data[xi + 1][yi + 1] + ); + b.set( + xi * elementSize, + (yi + 1) * elementSize, + data[xi][yi + 1] + ); + c.set( + (xi + 1) * elementSize, + yi * elementSize, + data[xi + 1][yi] + ); + } + else + { + // Top triangle verts + a.set( + xi * elementSize, + yi * elementSize, + data[xi][yi] + ); + b.set( + (xi + 1) * elementSize, + yi * elementSize, + data[xi + 1][yi] + ); + c.set( + xi * elementSize, + (yi + 1) * elementSize, + data[xi][yi + 1] + ); + } + } + + /** + * Get a triangle in the terrain in the form of a triangular convex shape. + * + * @param i + * @param j + * @param getUpperTriangle + */ + getConvexTrianglePillar(xi: number, yi: number, getUpperTriangle: boolean) + { + let result = this.pillarConvex; + let offsetResult = this.pillarOffset; + + if (this.cacheEnabled) + { + const data0 = this.getCachedConvexTrianglePillar(xi, yi, getUpperTriangle); + if (data0) + { + this.pillarConvex = data0.convex; + this.pillarOffset = data0.offset; + + return; + } + + result = new ConvexPolyhedron(); + offsetResult = new Vector3(); + + this.pillarConvex = result; + this.pillarOffset = offsetResult; + } + + const data = this.data; + const elementSize = this.elementSize; + const faces = result.faces; + + // Reuse verts if possible + result.vertices.length = 6; + for (let i = 0; i < 6; i++) + { + if (!result.vertices[i]) + { + result.vertices[i] = new Vector3(); + } + } + + // Reuse faces if possible + faces.length = 5; + for (let i = 0; i < 5; i++) + { + if (!faces[i]) + { + faces[i] = []; + } + } + + const verts = result.vertices; + + const h = (Math.min( + data[xi][yi], + data[xi + 1][yi], + data[xi][yi + 1], + data[xi + 1][yi + 1] + ) - this.minValue) / 2 + this.minValue; + + if (!getUpperTriangle) + { + // Center of the triangle pillar - all polygons are given relative to this one + offsetResult.set( + (xi + 0.25) * elementSize, // sort of center of a triangle + (yi + 0.25) * elementSize, + h // vertical center + ); + + // Top triangle verts + verts[0].set( + -0.25 * elementSize, + -0.25 * elementSize, + data[xi][yi] - h + ); + verts[1].set( + 0.75 * elementSize, + -0.25 * elementSize, + data[xi + 1][yi] - h + ); + verts[2].set( + -0.25 * elementSize, + 0.75 * elementSize, + data[xi][yi + 1] - h + ); + + // bottom triangle verts + verts[3].set( + -0.25 * elementSize, + -0.25 * elementSize, + -h - 1 + ); + verts[4].set( + 0.75 * elementSize, + -0.25 * elementSize, + -h - 1 + ); + verts[5].set( + -0.25 * elementSize, + 0.75 * elementSize, + -h - 1 + ); + + // top triangle + faces[0][0] = 0; + faces[0][1] = 1; + faces[0][2] = 2; + + // bottom triangle + faces[1][0] = 5; + faces[1][1] = 4; + faces[1][2] = 3; + + // -x facing quad + faces[2][0] = 0; + faces[2][1] = 2; + faces[2][2] = 5; + faces[2][3] = 3; + + // -y facing quad + faces[3][0] = 1; + faces[3][1] = 0; + faces[3][2] = 3; + faces[3][3] = 4; + + // +xy facing quad + faces[4][0] = 4; + faces[4][1] = 5; + faces[4][2] = 2; + faces[4][3] = 1; + } + else + { + // Center of the triangle pillar - all polygons are given relative to this one + offsetResult.set( + (xi + 0.75) * elementSize, // sort of center of a triangle + (yi + 0.75) * elementSize, + h // vertical center + ); + + // Top triangle verts + verts[0].set( + 0.25 * elementSize, + 0.25 * elementSize, + data[xi + 1][yi + 1] - h + ); + verts[1].set( + -0.75 * elementSize, + 0.25 * elementSize, + data[xi][yi + 1] - h + ); + verts[2].set( + 0.25 * elementSize, + -0.75 * elementSize, + data[xi + 1][yi] - h + ); + + // bottom triangle verts + verts[3].set( + 0.25 * elementSize, + 0.25 * elementSize, + -h - 1 + ); + verts[4].set( + -0.75 * elementSize, + 0.25 * elementSize, + -h - 1 + ); + verts[5].set( + 0.25 * elementSize, + -0.75 * elementSize, + -h - 1 + ); + + // Top triangle + faces[0][0] = 0; + faces[0][1] = 1; + faces[0][2] = 2; + + // bottom triangle + faces[1][0] = 5; + faces[1][1] = 4; + faces[1][2] = 3; + + // +x facing quad + faces[2][0] = 2; + faces[2][1] = 5; + faces[2][2] = 3; + faces[2][3] = 0; + + // +y facing quad + faces[3][0] = 3; + faces[3][1] = 4; + faces[3][2] = 1; + faces[3][3] = 0; + + // -xy facing quad + faces[4][0] = 1; + faces[4][1] = 4; + faces[4][2] = 5; + faces[4][3] = 2; + } + + result.computeNormals(); + result.computeEdges(); + result.updateBoundingSphereRadius(); + + this.setCachedConvexTrianglePillar(xi, yi, getUpperTriangle, result, offsetResult); + } + + calculateLocalInertia(mass: number, target = new Vector3()) + { + target.set(0, 0, 0); + + return target; + } + + volume() + { + return Number.MAX_VALUE; // The terrain is infinite + } + + calculateWorldAABB(pos: Vector3, quat: Quaternion, min: Vector3, max: Vector3) + { + // TODO: do it properly + min.set(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE); + max.set(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE); + } + + updateBoundingSphereRadius() + { + // Use the bounding box of the min/max values + const data = this.data; + const s = this.elementSize; + this.boundingSphereRadius = new Vector3(data.length * s, data[0].length * s, Math.max(Math.abs(this.maxValue), Math.abs(this.minValue))).length; + } + + /** + * Sets the height values from an image. Currently only supported in browser. + * + * @param image + * @param scale + */ + setHeightsFromImage(image: HTMLImageElement, scale: Vector3) + { + const canvas = document.createElement('canvas'); + canvas.width = image.width; + canvas.height = image.height; + const context = canvas.getContext('2d'); + context.drawImage(image, 0, 0); + const imageData = context.getImageData(0, 0, image.width, image.height); + + const matrix = this.data; + matrix.length = 0; + this.elementSize = Math.abs(scale.x) / imageData.width; + for (let i = 0; i < imageData.height; i++) + { + const row = []; + for (let j = 0; j < imageData.width; j++) + { + const a = imageData.data[(i * imageData.height + j) * 4]; + const b = imageData.data[(i * imageData.height + j) * 4 + 1]; + const c = imageData.data[(i * imageData.height + j) * 4 + 2]; + const height = (a + b + c) / 4 / 255 * scale.z; + if (scale.x < 0) + { + row.push(height); + } + else + { + row.unshift(height); + } + } + if (scale.y < 0) + { + matrix.unshift(row); + } + else + { + matrix.push(row); + } + } + this.updateMaxValue(); + this.updateMinValue(); + this.update(); + } +} + +const getHeightAtIdx = []; +const getHeightAtWeights = new Vector3(); +const getHeightAtA = new Vector3(); +const getHeightAtB = new Vector3(); +const getHeightAtC = new Vector3(); + +const getNormalAtA = new Vector3(); +const getNormalAtB = new Vector3(); +const getNormalAtC = new Vector3(); +const getNormalAtE0 = new Vector3(); +const getNormalAtE1 = new Vector3(); + +// from https://en.wikipedia.org/wiki/Barycentric_coordinate_system +function barycentricWeights(x: number, y: number, ax: number, ay: number, bx: number, by: number, cx: number, cy: number, result: Vector3) +{ + result.x = ((by - cy) * (x - cx) + (cx - bx) * (y - cy)) / ((by - cy) * (ax - cx) + (cx - bx) * (ay - cy)); + result.y = ((cy - ay) * (x - cx) + (ax - cx) * (y - cy)) / ((by - cy) * (ax - cx) + (cx - bx) * (ay - cy)); + result.z = 1 - result.x - result.y; +} diff --git a/src/shapes/Particle.js b/src/shapes/Particle.js deleted file mode 100644 index 5f38ef5b1..000000000 --- a/src/shapes/Particle.js +++ /dev/null @@ -1,45 +0,0 @@ -module.exports = Particle; - -var Shape = require('./Shape'); -var Vec3 = require('../math/Vec3'); - -/** - * Particle shape. - * @class Particle - * @constructor - * @author schteppe - * @extends Shape - */ -function Particle(){ - Shape.call(this, { - type: Shape.types.PARTICLE - }); -} -Particle.prototype = new Shape(); -Particle.prototype.constructor = Particle; - -/** - * @method calculateLocalInertia - * @param {Number} mass - * @param {Vec3} target - * @return {Vec3} - */ -Particle.prototype.calculateLocalInertia = function(mass,target){ - target = target || new Vec3(); - target.set(0, 0, 0); - return target; -}; - -Particle.prototype.volume = function(){ - return 0; -}; - -Particle.prototype.updateBoundingSphereRadius = function(){ - this.boundingSphereRadius = 0; -}; - -Particle.prototype.calculateWorldAABB = function(pos,quat,min,max){ - // Get each axis max - min.copy(pos); - max.copy(pos); -}; diff --git a/src/shapes/Particle.ts b/src/shapes/Particle.ts new file mode 100644 index 000000000..8cabf2a74 --- /dev/null +++ b/src/shapes/Particle.ts @@ -0,0 +1,46 @@ +import { Vector3, Quaternion } from 'feng3d'; +import { Shape } from './Shape'; + +export class Particle extends Shape +{ + /** + * Particle shape. + * + * @author schteppe + */ + constructor() + { + super({ + type: Shape.types.PARTICLE + }); + } + + /** + * @param mass + * @param target + */ + calculateLocalInertia(mass: number, target: Vector3) + { + target = target || new Vector3(); + target.set(0, 0, 0); + + return target; + } + + volume() + { + return 0; + } + + updateBoundingSphereRadius() + { + this.boundingSphereRadius = 0; + } + + calculateWorldAABB(pos: Vector3, quat: Quaternion, min: Vector3, max: Vector3) + { + // Get each axis max + min.copy(pos); + max.copy(pos); + } +} diff --git a/src/shapes/Plane.js b/src/shapes/Plane.js deleted file mode 100644 index df046aa83..000000000 --- a/src/shapes/Plane.js +++ /dev/null @@ -1,63 +0,0 @@ -module.exports = Plane; - -var Shape = require('./Shape'); -var Vec3 = require('../math/Vec3'); - -/** - * A plane, facing in the Z direction. The plane has its surface at z=0 and everything below z=0 is assumed to be solid plane. To make the plane face in some other direction than z, you must put it inside a Body and rotate that body. See the demos. - * @class Plane - * @constructor - * @extends Shape - * @author schteppe - */ -function Plane(){ - Shape.call(this, { - type: Shape.types.PLANE - }); - - // World oriented normal - this.worldNormal = new Vec3(); - this.worldNormalNeedsUpdate = true; - - this.boundingSphereRadius = Number.MAX_VALUE; -} -Plane.prototype = new Shape(); -Plane.prototype.constructor = Plane; - -Plane.prototype.computeWorldNormal = function(quat){ - var n = this.worldNormal; - n.set(0,0,1); - quat.vmult(n,n); - this.worldNormalNeedsUpdate = false; -}; - -Plane.prototype.calculateLocalInertia = function(mass,target){ - target = target || new Vec3(); - return target; -}; - -Plane.prototype.volume = function(){ - return Number.MAX_VALUE; // The plane is infinite... -}; - -var tempNormal = new Vec3(); -Plane.prototype.calculateWorldAABB = function(pos, quat, min, max){ - // The plane AABB is infinite, except if the normal is pointing along any axis - tempNormal.set(0,0,1); // Default plane normal is z - quat.vmult(tempNormal,tempNormal); - var maxVal = Number.MAX_VALUE; - min.set(-maxVal, -maxVal, -maxVal); - max.set(maxVal, maxVal, maxVal); - - if(tempNormal.x === 1){ max.x = pos.x; } - if(tempNormal.y === 1){ max.y = pos.y; } - if(tempNormal.z === 1){ max.z = pos.z; } - - if(tempNormal.x === -1){ min.x = pos.x; } - if(tempNormal.y === -1){ min.y = pos.y; } - if(tempNormal.z === -1){ min.z = pos.z; } -}; - -Plane.prototype.updateBoundingSphereRadius = function(){ - this.boundingSphereRadius = Number.MAX_VALUE; -}; \ No newline at end of file diff --git a/src/shapes/Plane.ts b/src/shapes/Plane.ts new file mode 100644 index 000000000..71c7bb982 --- /dev/null +++ b/src/shapes/Plane.ts @@ -0,0 +1,71 @@ +import { Quaternion, Vector3 } from 'feng3d'; +import { World } from '../world/World'; +import { Shape } from './Shape'; + +export class Plane extends Shape +{ + worldNormal: Vector3; + worldNormalNeedsUpdate: boolean; + + /** + * A plane, facing in the Z direction. The plane has its surface at z=0 and everything below z=0 is assumed to be solid plane. To make the plane face in some other direction than z, you must put it inside a Body and rotate that body. See the demos. + * + * @author schteppe + */ + constructor() + { + super({ + type: Shape.types.PLANE + }); + + // World oriented normal + this.worldNormal = new Vector3(); + this.worldNormalNeedsUpdate = true; + + this.boundingSphereRadius = Number.MAX_VALUE; + } + + computeWorldNormal(quat: Quaternion) + { + const n = this.worldNormal; + n.copy(World.worldNormal); + quat.vmult(n, n); + this.worldNormalNeedsUpdate = false; + } + + calculateLocalInertia(mass: number, target = new Vector3()) + { + return target; + } + + volume() + { + return Number.MAX_VALUE; // The plane is infinite... + } + + calculateWorldAABB(pos: Vector3, quat: Quaternion, min: Vector3, max: Vector3) + { + // The plane AABB is infinite, except if the normal is pointing along any axis + tempNormal.copy(World.worldNormal); // Default plane normal is z + quat.vmult(tempNormal, tempNormal); + const maxVal = Number.MAX_VALUE; + min.set(-maxVal, -maxVal, -maxVal); + max.set(maxVal, maxVal, maxVal); + + if (tempNormal.x === 1) { max.x = pos.x; } + if (tempNormal.y === 1) { max.y = pos.y; } + if (tempNormal.z === 1) { max.z = pos.z; } + + if (tempNormal.x === -1) { min.x = pos.x; } + if (tempNormal.y === -1) { min.y = pos.y; } + if (tempNormal.z === -1) { min.z = pos.z; } + } + + updateBoundingSphereRadius() + { + this.boundingSphereRadius = Number.MAX_VALUE; + } +} + +const tempNormal = new Vector3(); + diff --git a/src/shapes/Shape.js b/src/shapes/Shape.js deleted file mode 100644 index b64df4b68..000000000 --- a/src/shapes/Shape.js +++ /dev/null @@ -1,117 +0,0 @@ -module.exports = Shape; - -var Shape = require('./Shape'); -var Vec3 = require('../math/Vec3'); -var Quaternion = require('../math/Quaternion'); -var Material = require('../material/Material'); - -/** - * Base class for shapes - * @class Shape - * @constructor - * @param {object} [options] - * @param {number} [options.collisionFilterGroup=1] - * @param {number} [options.collisionFilterMask=-1] - * @param {number} [options.collisionResponse=true] - * @param {number} [options.material=null] - * @author schteppe - */ -function Shape(options){ - options = options || {}; - - /** - * Identifyer of the Shape. - * @property {number} id - */ - this.id = Shape.idCounter++; - - /** - * The type of this shape. Must be set to an int > 0 by subclasses. - * @property type - * @type {Number} - * @see Shape.types - */ - this.type = options.type || 0; - - /** - * The local bounding sphere radius of this shape. - * @property {Number} boundingSphereRadius - */ - this.boundingSphereRadius = 0; - - /** - * Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled. - * @property {boolean} collisionResponse - */ - this.collisionResponse = options.collisionResponse ? options.collisionResponse : true; - - /** - * @property {Number} collisionFilterGroup - */ - this.collisionFilterGroup = options.collisionFilterGroup !== undefined ? options.collisionFilterGroup : 1; - - /** - * @property {Number} collisionFilterMask - */ - this.collisionFilterMask = options.collisionFilterMask !== undefined ? options.collisionFilterMask : -1; - - /** - * @property {Material} material - */ - this.material = options.material ? options.material : null; - - /** - * @property {Body} body - */ - this.body = null; -} -Shape.prototype.constructor = Shape; - -/** - * Computes the bounding sphere radius. The result is stored in the property .boundingSphereRadius - * @method updateBoundingSphereRadius - */ -Shape.prototype.updateBoundingSphereRadius = function(){ - throw "computeBoundingSphereRadius() not implemented for shape type "+this.type; -}; - -/** - * Get the volume of this shape - * @method volume - * @return {Number} - */ -Shape.prototype.volume = function(){ - throw "volume() not implemented for shape type "+this.type; -}; - -/** - * Calculates the inertia in the local frame for this shape. - * @method calculateLocalInertia - * @param {Number} mass - * @param {Vec3} target - * @see http://en.wikipedia.org/wiki/List_of_moments_of_inertia - */ -Shape.prototype.calculateLocalInertia = function(mass,target){ - throw "calculateLocalInertia() not implemented for shape type "+this.type; -}; - -Shape.idCounter = 0; - -/** - * The available shape types. - * @static - * @property types - * @type {Object} - */ -Shape.types = { - SPHERE:1, - PLANE:2, - BOX:4, - COMPOUND:8, - CONVEXPOLYHEDRON:16, - HEIGHTFIELD:32, - PARTICLE:64, - CYLINDER:128, - TRIMESH:256 -}; - diff --git a/src/shapes/Shape.ts b/src/shapes/Shape.ts new file mode 100644 index 000000000..f5db07183 --- /dev/null +++ b/src/shapes/Shape.ts @@ -0,0 +1,113 @@ +import { Quaternion, Vector3 } from 'feng3d'; +import { Material } from '../material/Material'; +import { Body } from '../objects/Body'; + +export class Shape +{ + /** + * Identifyer of the Shape. + */ + id: number; + + /** + * The type of this shape. Must be set to an int > 0 by subclasses. + */ + type: number; + + /** + * The local bounding sphere radius of this shape. + */ + boundingSphereRadius: number; + + /** + * Whether to produce contact forces when in contact with other bodies. Note that contacts will be generated, but they will be disabled. + */ + collisionResponse: boolean; + + collisionFilterGroup: number; + + collisionFilterMask: number; + + material: Material; + + body: Body; + + faces: number[][]; + indices: number[]; + vertices: Vector3[] | number[]; + faceNormals: Vector3[]; + + convexPolyhedronRepresentation: Shape; + radius: number; + + /** + * Base class for shapes + * + * @param options + * @author schteppe + */ + constructor(options: { type?: number, collisionFilterGroup?: number, collisionFilterMask?: number, collisionResponse?: boolean, material?: Material } = {}) + { + this.id = Shape.idCounter++; + this.type = options.type || 0; + + this.boundingSphereRadius = 0; + this.collisionResponse = options.collisionResponse ? options.collisionResponse : true; + + this.collisionFilterGroup = options.collisionFilterGroup !== undefined ? options.collisionFilterGroup : 1; + + this.collisionFilterMask = options.collisionFilterMask !== undefined ? options.collisionFilterMask : -1; + + this.material = options.material ? options.material : null; + this.body = null; + } + + /** + * Computes the bounding sphere radius. The result is stored in the property .boundingSphereRadius + */ + updateBoundingSphereRadius() + { + throw `computeBoundingSphereRadius() not implemented for shape type ${this.type}`; + } + + /** + * Get the volume of this shape + */ + volume() + { + throw `volume() not implemented for shape type ${this.type}`; + } + + /** + * Calculates the inertia in the local frame for this shape. + * @param _mass + * @param _target + * @see http://en.wikipedia.org/wiki/List_of_moments_of_inertia + */ + calculateLocalInertia(_mass: number, _target: Vector3) + { + throw `calculateLocalInertia() not implemented for shape type ${this.type}`; + } + + calculateWorldAABB(_pos: Vector3, _quat: Quaternion, _min: Vector3, _max: Vector3) + { + throw '未实现'; + } + + static idCounter = 0; + + /** + * The available shape types. + */ + static types = { + SPHERE: 1, + PLANE: 2, + BOX: 4, + COMPOUND: 8, + CONVEXPOLYHEDRON: 16, + HEIGHTFIELD: 32, + PARTICLE: 64, + CYLINDER: 128, + TRIMESH: 256 + }; +} diff --git a/src/shapes/Sphere.js b/src/shapes/Sphere.js deleted file mode 100644 index 68658ea98..000000000 --- a/src/shapes/Sphere.js +++ /dev/null @@ -1,58 +0,0 @@ -module.exports = Sphere; - -var Shape = require('./Shape'); -var Vec3 = require('../math/Vec3'); - -/** - * Spherical shape - * @class Sphere - * @constructor - * @extends Shape - * @param {Number} radius The radius of the sphere, a non-negative number. - * @author schteppe / http://github.com/schteppe - */ -function Sphere(radius){ - Shape.call(this, { - type: Shape.types.SPHERE - }); - - /** - * @property {Number} radius - */ - this.radius = radius !== undefined ? radius : 1.0; - - if(this.radius < 0){ - throw new Error('The sphere radius cannot be negative.'); - } - - this.updateBoundingSphereRadius(); -} -Sphere.prototype = new Shape(); -Sphere.prototype.constructor = Sphere; - -Sphere.prototype.calculateLocalInertia = function(mass,target){ - target = target || new Vec3(); - var I = 2.0*mass*this.radius*this.radius/5.0; - target.x = I; - target.y = I; - target.z = I; - return target; -}; - -Sphere.prototype.volume = function(){ - return 4.0 * Math.PI * this.radius / 3.0; -}; - -Sphere.prototype.updateBoundingSphereRadius = function(){ - this.boundingSphereRadius = this.radius; -}; - -Sphere.prototype.calculateWorldAABB = function(pos,quat,min,max){ - var r = this.radius; - var axes = ['x','y','z']; - for(var i=0; i u.x){ - u.x = v.x; - } - - if(v.y < l.y){ - l.y = v.y; - } else if(v.y > u.y){ - u.y = v.y; - } - - if(v.z < l.z){ - l.z = v.z; - } else if(v.z > u.z){ - u.z = v.z; - } - } -}; - - -/** - * Update the .aabb property - * @method updateAABB - */ -Trimesh.prototype.updateAABB = function(){ - this.computeLocalAABB(this.aabb); -}; - -/** - * Will update the .boundingSphereRadius property - * @method updateBoundingSphereRadius - */ -Trimesh.prototype.updateBoundingSphereRadius = function(){ - // Assume points are distributed with local (0,0,0) as center - var max2 = 0; - var vertices = this.vertices; - var v = new Vec3(); - for(var i=0, N=vertices.length / 3; i !== N; i++) { - this.getVertex(i, v); - var norm2 = v.norm2(); - if(norm2 > max2){ - max2 = norm2; - } - } - this.boundingSphereRadius = Math.sqrt(max2); -}; - -var tempWorldVertex = new Vec3(); -var calculateWorldAABB_frame = new Transform(); -var calculateWorldAABB_aabb = new AABB(); - -/** - * @method calculateWorldAABB - * @param {Vec3} pos - * @param {Quaternion} quat - * @param {Vec3} min - * @param {Vec3} max - */ -Trimesh.prototype.calculateWorldAABB = function(pos,quat,min,max){ - /* - var n = this.vertices.length / 3, - verts = this.vertices; - var minx,miny,minz,maxx,maxy,maxz; - - var v = tempWorldVertex; - for(var i=0; i maxx || maxx===undefined){ - maxx = v.x; - } - - if (v.y < miny || miny===undefined){ - miny = v.y; - } else if(v.y > maxy || maxy===undefined){ - maxy = v.y; - } - - if (v.z < minz || minz===undefined){ - minz = v.z; - } else if(v.z > maxz || maxz===undefined){ - maxz = v.z; - } - } - min.set(minx,miny,minz); - max.set(maxx,maxy,maxz); - */ - - // Faster approximation using local AABB - var frame = calculateWorldAABB_frame; - var result = calculateWorldAABB_aabb; - frame.position = pos; - frame.quaternion = quat; - this.aabb.toWorldFrame(frame, result); - min.copy(result.lowerBound); - max.copy(result.upperBound); -}; - -/** - * Get approximate volume - * @method volume - * @return {Number} - */ -Trimesh.prototype.volume = function(){ - return 4.0 * Math.PI * this.boundingSphereRadius / 3.0; -}; - -/** - * Create a Trimesh instance, shaped as a torus. - * @static - * @method createTorus - * @param {number} [radius=1] - * @param {number} [tube=0.5] - * @param {number} [radialSegments=8] - * @param {number} [tubularSegments=6] - * @param {number} [arc=6.283185307179586] - * @return {Trimesh} A torus - */ -Trimesh.createTorus = function (radius, tube, radialSegments, tubularSegments, arc) { - radius = radius || 1; - tube = tube || 0.5; - radialSegments = radialSegments || 8; - tubularSegments = tubularSegments || 6; - arc = arc || Math.PI * 2; - - var vertices = []; - var indices = []; - - for ( var j = 0; j <= radialSegments; j ++ ) { - for ( var i = 0; i <= tubularSegments; i ++ ) { - var u = i / tubularSegments * arc; - var v = j / radialSegments * Math.PI * 2; - - var x = ( radius + tube * Math.cos( v ) ) * Math.cos( u ); - var y = ( radius + tube * Math.cos( v ) ) * Math.sin( u ); - var z = tube * Math.sin( v ); - - vertices.push( x, y, z ); - } - } - - for ( var j = 1; j <= radialSegments; j ++ ) { - for ( var i = 1; i <= tubularSegments; i ++ ) { - var a = ( tubularSegments + 1 ) * j + i - 1; - var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1; - var c = ( tubularSegments + 1 ) * ( j - 1 ) + i; - var d = ( tubularSegments + 1 ) * j + i; - - indices.push(a, b, d); - indices.push(b, c, d); - } - } - - return new Trimesh(vertices, indices); -}; diff --git a/src/shapes/Trimesh.ts b/src/shapes/Trimesh.ts new file mode 100644 index 000000000..44192e50b --- /dev/null +++ b/src/shapes/Trimesh.ts @@ -0,0 +1,580 @@ +import { Box3, Quaternion, Vector3 } from 'feng3d'; +import { Transform } from '../math/Transform'; +import { Octree } from '../utils/Octree'; +import { Shape } from './Shape'; + +export class Trimesh extends Shape +{ + declare vertices: number[]; + /** + * The normals data. + */ + normals: number[]; + /** + * The local AABB of the mesh. + */ + aabb: Box3; + /** + * References to vertex pairs, making up all unique edges in the trimesh. + */ + edges: number[]; + /** + * Local scaling of the mesh. Use .setScale() to set it. + */ + scale: Vector3; + + /** + * The indexed triangles. Use .updateTree() to update it. + */ + tree: Octree; + + /** + * @param vertices + * @param indices + * + * @example + * // How to make a mesh with a single triangle + * let vertices = [ + * 0, 0, 0, // vertex 0 + * 1, 0, 0, // vertex 1 + * 0, 1, 0 // vertex 2 + * ]; + * let indices = [ + * 0, 1, 2 // triangle 0 + * ]; + * let trimeshShape = new Trimesh(vertices, indices); + */ + constructor(vertices: number[], indices: number[]) + { + super({ + type: Shape.types.TRIMESH + }); + + this.vertices = vertices.concat(); + + /** + * Array of integers, indicating which vertices each triangle consists of. The length of this array is thus 3 times the number of triangles. + */ + this.indices = indices.concat(); + + this.normals = []; + + this.aabb = new Box3(); + + this.edges = null; + + this.scale = new Vector3(1, 1, 1); + + this.tree = new Octree(); + + this.updateEdges(); + this.updateNormals(); + this.updateAABB(); + this.updateBoundingSphereRadius(); + this.updateTree(); + } + + updateTree() + { + const tree = this.tree; + + tree.reset(); + tree.aabb.copy(this.aabb); + const scale = this.scale; // The local mesh AABB is scaled, but the octree AABB should be unscaled + tree.aabb.min.x *= 1 / scale.x; + tree.aabb.min.y *= 1 / scale.y; + tree.aabb.min.z *= 1 / scale.z; + tree.aabb.max.x *= 1 / scale.x; + tree.aabb.max.y *= 1 / scale.y; + tree.aabb.max.z *= 1 / scale.z; + + // Insert all triangles + const triangleAABB = new Box3(); + const a = new Vector3(); + const b = new Vector3(); + const c = new Vector3(); + const points = [a, b, c]; + for (let i = 0; i < this.indices.length / 3; i++) + { + // this.getTriangleVertices(i, a, b, c); + + // Get unscaled triangle verts + const i3 = i * 3; + this._getUnscaledVertex(this.indices[i3], a); + this._getUnscaledVertex(this.indices[i3 + 1], b); + this._getUnscaledVertex(this.indices[i3 + 2], c); + + triangleAABB.fromPoints(points); + tree.insert(triangleAABB, i); + } + tree.removeEmptyNodes(); + } + + /** + * Get triangles in a local AABB from the trimesh. + * + * @param aabb + * @param result An array of integers, referencing the queried triangles. + */ + getTrianglesInAABB(aabb: Box3, result: number[]) + { + unscaledAABB.copy(aabb); + + // Scale it to local + const scale = this.scale; + const isx = scale.x; + const isy = scale.y; + const isz = scale.z; + const l = unscaledAABB.min; + const u = unscaledAABB.max; + l.x /= isx; + l.y /= isy; + l.z /= isz; + u.x /= isx; + u.y /= isy; + u.z /= isz; + + return this.tree.aabbQuery(unscaledAABB, result); + } + + /** + * @param scale + */ + setScale(scale: Vector3) + { + // let wasUniform = this.scale.x === this.scale.y === this.scale.z;// 等价下面代码? + const wasUniform = this.scale.x === this.scale.y && this.scale.y === this.scale.z;// ? + + // let isUniform = scale.x === scale.y === scale.z;// 等价下面代码? + const isUniform = scale.x === scale.y && scale.y === scale.z;// ? + + if (!(wasUniform && isUniform)) + { + // Non-uniform scaling. Need to update normals. + this.updateNormals(); + } + this.scale.copy(scale); + this.updateAABB(); + this.updateBoundingSphereRadius(); + } + + /** + * Compute the normals of the faces. Will save in the .normals array. + */ + updateNormals() + { + const n = computeNormalsN; + + // Generate normals + const normals = this.normals; + for (let i = 0; i < this.indices.length / 3; i++) + { + const i3 = i * 3; + + const a = this.indices[i3]; + const b = this.indices[i3 + 1]; + const c = this.indices[i3 + 2]; + + this.getVertex(a, va); + this.getVertex(b, vb); + this.getVertex(c, vc); + + Trimesh.computeNormal(vb, va, vc, n); + + normals[i3] = n.x; + normals[i3 + 1] = n.y; + normals[i3 + 2] = n.z; + } + } + + /** + * Update the .edges property + */ + updateEdges() + { + const edges = {}; + // eslint-disable-next-line func-style + const add = function (a: number, b: number) + { + const key = a < b ? `${a}_${b}` : `${b}_${a}`; + edges[key] = true; + }; + for (let i = 0; i < this.indices.length / 3; i++) + { + const i3 = i * 3; + const a = this.indices[i3]; + const b = this.indices[i3 + 1]; + const c = this.indices[i3 + 2]; + add(a, b); + add(b, c); + add(c, a); + } + const keys = Object.keys(edges); + this.edges = []; + for (let i = 0; i < keys.length; i++) + { + const indices = keys[i].split('_'); + this.edges[2 * i] = parseInt(indices[0], 10); + this.edges[2 * i + 1] = parseInt(indices[1], 10); + } + } + + /** + * Get an edge vertex + * + * @param edgeIndex + * @param firstOrSecond 0 or 1, depending on which one of the vertices you need. + * @param vertexStore Where to store the result + */ + getEdgeVertex(edgeIndex: number, firstOrSecond: number, vertexStore: Vector3) + { + const vertexIndex = this.edges[edgeIndex * 2 + (firstOrSecond ? 1 : 0)]; + this.getVertex(vertexIndex, vertexStore); + } + + /** + * Get a vector along an edge. + * + * @param edgeIndex + * @param vectorStore + */ + getEdgeVector(edgeIndex: number, vectorStore: Vector3) + { + const va = getEdgeVectorVa; + const vb = getEdgeVectorVb; + this.getEdgeVertex(edgeIndex, 0, va); + this.getEdgeVertex(edgeIndex, 1, vb); + vb.subTo(va, vectorStore); + } + + /** + * Get face normal given 3 vertices + * + * @param va + * @param vb + * @param vc + * @param target + */ + static computeNormal(va: Vector3, vb: Vector3, vc: Vector3, target: Vector3) + { + vb.subTo(va, ab); + vc.subTo(vb, cb); + cb.crossTo(ab, target); + if (!target.isZero()) + { + target.normalize(); + } + } + + /** + * Get vertex i. + * + * @param i + * @param out + * @return The "out" vector object + */ + getVertex(i: number, out: Vector3) + { + const scale = this.scale; + this._getUnscaledVertex(i, out); + out.x *= scale.x; + out.y *= scale.y; + out.z *= scale.z; + + return out; + } + + /** + * Get raw vertex i + * + * @param i + * @param out + * @return The "out" vector object + */ + private _getUnscaledVertex(i: number, out: Vector3) + { + const i3 = i * 3; + const vertices = this.vertices; + + return out.set( + vertices[i3], + vertices[i3 + 1], + vertices[i3 + 2] + ); + } + + /** + * Get a vertex from the trimesh,transformed by the given position and quaternion. + * + * @param i + * @param pos + * @param quat + * @param out + * @return The "out" vector object + */ + getWorldVertex(i: number, pos: Vector3, quat: Quaternion, out: Vector3) + { + this.getVertex(i, out); + Transform.pointToWorldFrame(pos, quat, out, out); + + return out; + } + + /** + * Get the three vertices for triangle i. + * + * @param i + * @param a + * @param b + * @param c + */ + getTriangleVertices(i: number, a: Vector3, b: Vector3, c: Vector3) + { + const i3 = i * 3; + this.getVertex(this.indices[i3], a); + this.getVertex(this.indices[i3 + 1], b); + this.getVertex(this.indices[i3 + 2], c); + } + + /** + * Compute the normal of triangle i. + * + * @param i + * @param target + * @return The "target" vector object + */ + getNormal(i: number, target: Vector3) + { + const i3 = i * 3; + + return target.set( + this.normals[i3], + this.normals[i3 + 1], + this.normals[i3 + 2] + ); + } + + /** + * + * @param mass + * @param target + * @return The "target" vector object + */ + calculateLocalInertia(mass: number, target: Vector3) + { + // Approximate with box inertia + // Exact inertia calculation is overkill, but see http://geometrictools.com/Documentation/PolyhedralMassProperties.pdf for the correct way to do it + this.computeLocalAABB(cliAabb); + const x = cliAabb.max.x - cliAabb.min.x; + const y = cliAabb.max.y - cliAabb.min.y; + const z = cliAabb.max.z - cliAabb.min.z; + + return target.set( + 1.0 / 12.0 * mass * (2 * y * 2 * y + 2 * z * 2 * z), + 1.0 / 12.0 * mass * (2 * x * 2 * x + 2 * z * 2 * z), + 1.0 / 12.0 * mass * (2 * y * 2 * y + 2 * x * 2 * x) + ); + } + + /** + * Compute the local AABB for the trimesh + * + * @param aabb + */ + computeLocalAABB(aabb: Box3) + { + const l = aabb.min; + const u = aabb.max; + const n = this.vertices.length; + // const vertices = this.vertices; + const v = computeLocalAABBWorldVert; + + this.getVertex(0, v); + l.copy(v); + u.copy(v); + + for (let i = 0; i !== n; i++) + { + this.getVertex(i, v); + + if (v.x < l.x) + { + l.x = v.x; + } + else if (v.x > u.x) + { + u.x = v.x; + } + + if (v.y < l.y) + { + l.y = v.y; + } + else if (v.y > u.y) + { + u.y = v.y; + } + + if (v.z < l.z) + { + l.z = v.z; + } + else if (v.z > u.z) + { + u.z = v.z; + } + } + } + + /** + * Update the .aabb property + */ + updateAABB() + { + this.computeLocalAABB(this.aabb); + } + + /** + * Will update the .boundingSphereRadius property + */ + updateBoundingSphereRadius() + { + // Assume points are distributed with local (0,0,0) as center + let max2 = 0; + const vertices = this.vertices; + const v = new Vector3(); + for (let i = 0, N = vertices.length / 3; i !== N; i++) + { + this.getVertex(i, v); + const norm2 = v.lengthSquared; + if (norm2 > max2) + { + max2 = norm2; + } + } + this.boundingSphereRadius = Math.sqrt(max2); + } + + calculateWorldAABB(pos: Vector3, quat: Quaternion, min: Vector3, max: Vector3) + { + /* + let n = this.vertices.length / 3, + verts = this.vertices; + let minx,miny,minz,maxx,maxy,maxz; + + let v = tempWorldVertex; + for(let i=0; i maxx || maxx===undefined){ + maxx = v.x; + } + + if (v.y < miny || miny===undefined){ + miny = v.y; + } else if(v.y > maxy || maxy===undefined){ + maxy = v.y; + } + + if (v.z < minz || minz===undefined){ + minz = v.z; + } else if(v.z > maxz || maxz===undefined){ + maxz = v.z; + } + } + min.set(minx,miny,minz); + max.set(maxx,maxy,maxz); + */ + + // Faster approximation using local AABB + const frame = calculateWorldAABBFrame; + const result = calculateWorldAABBAabb; + frame.position = pos; + frame.quaternion = quat; + frame.toWorldFrameBox3(this.aabb, result); + min.copy(result.min); + max.copy(result.max); + } + + /** + * Get approximate volume + */ + volume() + { + return 4.0 * Math.PI * this.boundingSphereRadius / 3.0; + } + + /** + * Create a Trimesh instance, shaped as a torus. + * + * @param radius + * @param tube + * @param radialSegments + * @param tubularSegments + * @param arc + * + * @return A torus + */ + static createTorus(radius = 1, tube = 0.5, radialSegments = 8, tubularSegments = 6, arc = Math.PI * 2) + { + const vertices: number[] = []; + const indices: number[] = []; + + for (let j = 0; j <= radialSegments; j++) + { + for (let i = 0; i <= tubularSegments; i++) + { + const u = i / tubularSegments * arc; + const v = j / radialSegments * Math.PI * 2; + + const x = (radius + tube * Math.cos(v)) * Math.cos(u); + const y = (radius + tube * Math.cos(v)) * Math.sin(u); + const z = tube * Math.sin(v); + + vertices.push(x, y, z); + } + } + + for (let j = 1; j <= radialSegments; j++) + { + for (let i = 1; i <= tubularSegments; i++) + { + const a = (tubularSegments + 1) * j + i - 1; + const b = (tubularSegments + 1) * (j - 1) + i - 1; + const c = (tubularSegments + 1) * (j - 1) + i; + const d = (tubularSegments + 1) * j + i; + + indices.push(a, b, d); + indices.push(b, c, d); + } + } + + return new Trimesh(vertices, indices); + } +} + +const computeNormalsN = new Vector3(); + +const unscaledAABB = new Box3(); + +const getEdgeVectorVa = new Vector3(); +const getEdgeVectorVb = new Vector3(); + +const cb = new Vector3(); +const ab = new Vector3(); + +const va = new Vector3(); +const vb = new Vector3(); +const vc = new Vector3(); + +const cliAabb = new Box3(); + +const computeLocalAABBWorldVert = new Vector3(); + +// const tempWorldVertex = new Vector3(); +const calculateWorldAABBFrame = new Transform(); +const calculateWorldAABBAabb = new Box3(); + diff --git a/src/solver/GSSolver.js b/src/solver/GSSolver.js deleted file mode 100644 index 311820ba9..000000000 --- a/src/solver/GSSolver.js +++ /dev/null @@ -1,140 +0,0 @@ -module.exports = GSSolver; - -var Vec3 = require('../math/Vec3'); -var Quaternion = require('../math/Quaternion'); -var Solver = require('./Solver'); - -/** - * Constraint equation Gauss-Seidel solver. - * @class GSSolver - * @constructor - * @todo The spook parameters should be specified for each constraint, not globally. - * @author schteppe / https://github.com/schteppe - * @see https://www8.cs.umu.se/kurser/5DV058/VT09/lectures/spooknotes.pdf - * @extends Solver - */ -function GSSolver(){ - Solver.call(this); - - /** - * The number of solver iterations determines quality of the constraints in the world. The more iterations, the more correct simulation. More iterations need more computations though. If you have a large gravity force in your world, you will need more iterations. - * @property iterations - * @type {Number} - * @todo write more about solver and iterations in the wiki - */ - this.iterations = 10; - - /** - * When tolerance is reached, the system is assumed to be converged. - * @property tolerance - * @type {Number} - */ - this.tolerance = 1e-7; -} -GSSolver.prototype = new Solver(); - -var GSSolver_solve_lambda = []; // Just temporary number holders that we want to reuse each solve. -var GSSolver_solve_invCs = []; -var GSSolver_solve_Bs = []; -GSSolver.prototype.solve = function(dt,world){ - var iter = 0, - maxIter = this.iterations, - tolSquared = this.tolerance*this.tolerance, - equations = this.equations, - Neq = equations.length, - bodies = world.bodies, - Nbodies = bodies.length, - h = dt, - q, B, invC, deltalambda, deltalambdaTot, GWlambda, lambdaj; - - // Update solve mass - if(Neq !== 0){ - for(var i=0; i!==Nbodies; i++){ - bodies[i].updateSolveMassProperties(); - } - } - - // Things that does not change during iteration can be computed once - var invCs = GSSolver_solve_invCs, - Bs = GSSolver_solve_Bs, - lambda = GSSolver_solve_lambda; - invCs.length = Neq; - Bs.length = Neq; - lambda.length = Neq; - for(var i=0; i!==Neq; i++){ - var c = equations[i]; - lambda[i] = 0.0; - Bs[i] = c.computeB(h); - invCs[i] = 1.0 / c.computeC(); - } - - if(Neq !== 0){ - - // Reset vlambda - for(var i=0; i!==Nbodies; i++){ - var b=bodies[i], - vlambda=b.vlambda, - wlambda=b.wlambda; - vlambda.set(0,0,0); - wlambda.set(0,0,0); - } - - // Iterate over equations - for(iter=0; iter!==maxIter; iter++){ - - // Accumulate the total error for each iteration. - deltalambdaTot = 0.0; - - for(var j=0; j!==Neq; j++){ - - var c = equations[j]; - - // Compute iteration - B = Bs[j]; - invC = invCs[j]; - lambdaj = lambda[j]; - GWlambda = c.computeGWlambda(); - deltalambda = invC * ( B - GWlambda - c.eps * lambdaj ); - - // Clamp if we are not within the min/max interval - if(lambdaj + deltalambda < c.minForce){ - deltalambda = c.minForce - lambdaj; - } else if(lambdaj + deltalambda > c.maxForce){ - deltalambda = c.maxForce - lambdaj; - } - lambda[j] += deltalambda; - - deltalambdaTot += deltalambda > 0.0 ? deltalambda : -deltalambda; // abs(deltalambda) - - c.addToWlambda(deltalambda); - } - - // If the total error is small enough - stop iterate - if(deltalambdaTot*deltalambdaTot < tolSquared){ - break; - } - } - - // Add result to velocity - for(var i=0; i!==Nbodies; i++){ - var b=bodies[i], - v=b.velocity, - w=b.angularVelocity; - - b.vlambda.vmul(b.linearFactor, b.vlambda); - v.vadd(b.vlambda, v); - - b.wlambda.vmul(b.angularFactor, b.wlambda); - w.vadd(b.wlambda, w); - } - - // Set the .multiplier property of each equation - var l = equations.length; - var invDt = 1 / h; - while(l--){ - equations[l].multiplier = lambda[l] * invDt; - } - } - - return iter; -}; diff --git a/src/solver/GSSolver.ts b/src/solver/GSSolver.ts new file mode 100644 index 000000000..a40beeaf6 --- /dev/null +++ b/src/solver/GSSolver.ts @@ -0,0 +1,139 @@ +import { World } from '../world/World'; +import { Solver } from './Solver'; + +export class GSSolver extends Solver +{ + /** + * Constraint equation Gauss-Seidel solver. + * @todo The spook parameters should be specified for each constraint, not globally. + * @author schteppe / https://github.com/schteppe + * @see https://www8.cs.umu.se/kurser/5DV058/VT09/lectures/spooknotes.pdf + */ + constructor() + { + super(); + + this.iterations = 10; + this.tolerance = 1e-7; + } + + solve(dt: number, world: World) + { + let iter = 0; + const maxIter = this.iterations; + const tolSquared = this.tolerance * this.tolerance; + const equations = this.equations; + const Neq = equations.length; + const bodies = world.bodies; + const Nbodies = bodies.length; + const h = dt; + // let q: any; + let B: number; let invC: number; let deltalambda: number; let deltalambdaTot: number; let GWlambda: number; + let lambdaj: number; + + // Update solve mass + if (Neq !== 0) + { + for (let i = 0; i !== Nbodies; i++) + { + bodies[i].updateSolveMassProperties(); + } + } + + // Things that does not change during iteration can be computed once + const invCs = GSSolverSolveInvCs; + const Bs = GSSolverSolveBs; + const lambda = GSSolverSolveLambda; + invCs.length = Neq; + Bs.length = Neq; + lambda.length = Neq; + for (let i = 0; i !== Neq; i++) + { + const c = equations[i]; + lambda[i] = 0.0; + Bs[i] = c.computeB(h, 0, 0); + invCs[i] = 1.0 / c.computeC(); + } + + if (Neq !== 0) + { + // Reset vlambda + for (let i = 0; i !== Nbodies; i++) + { + const b = bodies[i]; + const vlambda = b.vlambda; + const wlambda = b.wlambda; + vlambda.set(0, 0, 0); + wlambda.set(0, 0, 0); + } + + // Iterate over equations + for (iter = 0; iter !== maxIter; iter++) + { + // Accumulate the total error for each iteration. + deltalambdaTot = 0.0; + + for (let j = 0; j !== Neq; j++) + { + const c = equations[j]; + + // Compute iteration + B = Bs[j]; + invC = invCs[j]; + lambdaj = lambda[j]; + GWlambda = c.computeGWlambda(); + deltalambda = invC * (B - GWlambda - c.eps * lambdaj); + + // Clamp if we are not within the min/max interval + if (lambdaj + deltalambda < c.minForce) + { + deltalambda = c.minForce - lambdaj; + } + else if (lambdaj + deltalambda > c.maxForce) + { + deltalambda = c.maxForce - lambdaj; + } + lambda[j] += deltalambda; + + deltalambdaTot += deltalambda > 0.0 ? deltalambda : -deltalambda; // abs(deltalambda) + + c.addToWlambda(deltalambda); + } + + // If the total error is small enough - stop iterate + if (deltalambdaTot * deltalambdaTot < tolSquared) + { + break; + } + } + + // Add result to velocity + for (let i = 0; i !== Nbodies; i++) + { + const b = bodies[i]; + const v = b.velocity; + const w = b.angularVelocity; + + b.vlambda.scaleTo(b.linearFactor, b.vlambda); + v.addTo(b.vlambda, v); + + b.wlambda.scaleTo(b.angularFactor, b.wlambda); + w.addTo(b.wlambda, w); + } + + // Set the .multiplier property of each equation + let l = equations.length; + const invDt = 1 / h; + while (l--) + { + equations[l].multiplier = lambda[l] * invDt; + } + } + + return iter; + } +} + +const GSSolverSolveLambda = []; // Just temporary number holders that we want to reuse each solve. +const GSSolverSolveInvCs = []; +const GSSolverSolveBs = []; diff --git a/src/solver/Solver.js b/src/solver/Solver.js deleted file mode 100644 index 38c6d2a4a..000000000 --- a/src/solver/Solver.js +++ /dev/null @@ -1,59 +0,0 @@ -module.exports = Solver; - -/** - * Constraint equation solver base class. - * @class Solver - * @constructor - * @author schteppe / https://github.com/schteppe - */ -function Solver(){ - /** - * All equations to be solved - * @property {Array} equations - */ - this.equations = []; -} - -/** - * Should be implemented in subclasses! - * @method solve - * @param {Number} dt - * @param {World} world - */ -Solver.prototype.solve = function(dt,world){ - // Should return the number of iterations done! - return 0; -}; - -/** - * Add an equation - * @method addEquation - * @param {Equation} eq - */ -Solver.prototype.addEquation = function(eq){ - if (eq.enabled) { - this.equations.push(eq); - } -}; - -/** - * Remove an equation - * @method removeEquation - * @param {Equation} eq - */ -Solver.prototype.removeEquation = function(eq){ - var eqs = this.equations; - var i = eqs.indexOf(eq); - if(i !== -1){ - eqs.splice(i,1); - } -}; - -/** - * Add all equations - * @method removeAllEquations - */ -Solver.prototype.removeAllEquations = function(){ - this.equations.length = 0; -}; - diff --git a/src/solver/Solver.ts b/src/solver/Solver.ts new file mode 100644 index 000000000..88e8910c9 --- /dev/null +++ b/src/solver/Solver.ts @@ -0,0 +1,75 @@ +import { Equation } from '../equations/Equation'; +import { Body } from '../objects/Body'; + +export class Solver +{ + /** + * The number of solver iterations determines quality of the constraints in the world. The more iterations, the more correct simulation. More iterations need more computations though. If you have a large gravity force in your world, you will need more iterations. + * @todo write more about solver and iterations in the wiki + */ + iterations: number; + + /** + * When tolerance is reached, the system is assumed to be converged. + */ + tolerance: number; + + /** + * All equations to be solved + */ + equations: Equation[]; + + /** + * Constraint equation solver base class. + * @author schteppe / https://github.com/schteppe + */ + constructor() + { + this.equations = []; + } + + /** + * Should be implemented in subclasses! + * @param _dt + * @param _world + */ + solve(_dt: number, _world: { bodies: Body[] }) + { + // Should return the number of iterations done! + return 0; + } + + /** + * Add an equation + * @param eq + */ + addEquation(eq: Equation) + { + if (eq.enabled) + { + this.equations.push(eq); + } + } + + /** + * Remove an equation + * @param eq + */ + removeEquation(eq: Equation) + { + const eqs = this.equations; + const i = eqs.indexOf(eq); + if (i !== -1) + { + eqs.splice(i, 1); + } + } + + /** + * Add all equations + */ + removeAllEquations() + { + this.equations.length = 0; + } +} diff --git a/src/solver/SplitSolver.js b/src/solver/SplitSolver.js deleted file mode 100644 index e65ded977..000000000 --- a/src/solver/SplitSolver.js +++ /dev/null @@ -1,154 +0,0 @@ -module.exports = SplitSolver; - -var Vec3 = require('../math/Vec3'); -var Quaternion = require('../math/Quaternion'); -var Solver = require('./Solver'); -var Body = require('../objects/Body'); - -/** - * Splits the equations into islands and solves them independently. Can improve performance. - * @class SplitSolver - * @constructor - * @extends Solver - * @param {Solver} subsolver - */ -function SplitSolver(subsolver){ - Solver.call(this); - this.iterations = 10; - this.tolerance = 1e-7; - this.subsolver = subsolver; - this.nodes = []; - this.nodePool = []; - - // Create needed nodes, reuse if possible - while(this.nodePool.length < 128){ - this.nodePool.push(this.createNode()); - } -} -SplitSolver.prototype = new Solver(); - -// Returns the number of subsystems -var SplitSolver_solve_nodes = []; // All allocated node objects -var SplitSolver_solve_nodePool = []; // All allocated node objects -var SplitSolver_solve_eqs = []; // Temp array -var SplitSolver_solve_bds = []; // Temp array -var SplitSolver_solve_dummyWorld = {bodies:[]}; // Temp object - -var STATIC = Body.STATIC; -function getUnvisitedNode(nodes){ - var Nnodes = nodes.length; - for(var i=0; i!==Nnodes; i++){ - var node = nodes[i]; - if(!node.visited && !(node.body.type & STATIC)){ - return node; - } - } - return false; -} - -var queue = []; -function bfs(root,visitFunc,bds,eqs){ - queue.push(root); - root.visited = true; - visitFunc(root,bds,eqs); - while(queue.length) { - var node = queue.pop(); - // Loop over unvisited child nodes - var child; - while((child = getUnvisitedNode(node.children))) { - child.visited = true; - visitFunc(child,bds,eqs); - queue.push(child); - } - } -} - -function visitFunc(node,bds,eqs){ - bds.push(node.body); - var Neqs = node.eqs.length; - for(var i=0; i!==Neqs; i++){ - var eq = node.eqs[i]; - if(eqs.indexOf(eq) === -1){ - eqs.push(eq); - } - } -} - -SplitSolver.prototype.createNode = function(){ - return { body:null, children:[], eqs:[], visited:false }; -}; - -/** - * Solve the subsystems - * @method solve - * @param {Number} dt - * @param {World} world - */ -SplitSolver.prototype.solve = function(dt,world){ - var nodes=SplitSolver_solve_nodes, - nodePool=this.nodePool, - bodies=world.bodies, - equations=this.equations, - Neq=equations.length, - Nbodies=bodies.length, - subsolver=this.subsolver; - - // Create needed nodes, reuse if possible - while(nodePool.length < Nbodies){ - nodePool.push(this.createNode()); - } - nodes.length = Nbodies; - for (var i = 0; i < Nbodies; i++) { - nodes[i] = nodePool[i]; - } - - // Reset node values - for(var i=0; i!==Nbodies; i++){ - var node = nodes[i]; - node.body = bodies[i]; - node.children.length = 0; - node.eqs.length = 0; - node.visited = false; - } - for(var k=0; k!==Neq; k++){ - var eq=equations[k], - i=bodies.indexOf(eq.bi), - j=bodies.indexOf(eq.bj), - ni=nodes[i], - nj=nodes[j]; - ni.children.push(nj); - ni.eqs.push(eq); - nj.children.push(ni); - nj.eqs.push(eq); - } - - var child, n=0, eqs=SplitSolver_solve_eqs; - - subsolver.tolerance = this.tolerance; - subsolver.iterations = this.iterations; - - var dummyWorld = SplitSolver_solve_dummyWorld; - while((child = getUnvisitedNode(nodes))){ - eqs.length = 0; - dummyWorld.bodies.length = 0; - bfs(child, visitFunc, dummyWorld.bodies, eqs); - - var Neqs = eqs.length; - - eqs = eqs.sort(sortById); - - for(var i=0; i!==Neqs; i++){ - subsolver.addEquation(eqs[i]); - } - - var iter = subsolver.solve(dt,dummyWorld); - subsolver.removeAllEquations(); - n++; - } - - return n; -}; - -function sortById(a, b){ - return b.id - a.id; -} \ No newline at end of file diff --git a/src/solver/SplitSolver.ts b/src/solver/SplitSolver.ts new file mode 100644 index 000000000..e853c4752 --- /dev/null +++ b/src/solver/SplitSolver.ts @@ -0,0 +1,187 @@ +import { Equation } from '../equations/Equation'; +import { Body } from '../objects/Body'; +import { World } from '../world/World'; +import { Solver } from './Solver'; + +interface SSNode +{ + body: Body; + children: SSNode[]; + eqs: Equation[]; + visited: boolean; +} + +export class SplitSolver extends Solver +{ + subsolver: Solver; + nodes: SSNode[]; + nodePool: SSNode[]; + + /** + * Splits the equations into islands and solves them independently. Can improve performance. + * + * @param subsolver + */ + constructor(subsolver: Solver) + { + super(); + this.iterations = 10; + this.tolerance = 1e-7; + this.subsolver = subsolver; + this.nodes = []; + this.nodePool = []; + + // Create needed nodes, reuse if possible + while (this.nodePool.length < 128) + { + this.nodePool.push(this.createNode()); + } + } + + createNode(): SSNode + { + return { body: null, children: [], eqs: [], visited: false }; + } + + /** + * Solve the subsystems + * @method solve + * @param {Number} dt + * @param {World} world + */ + solve(dt: number, world: World) + { + const nodes = SplitSolverSolveNodes; + const nodePool = this.nodePool; + const bodies = world.bodies; + const equations = this.equations; + const Neq = equations.length; + const Nbodies = bodies.length; + const subsolver = this.subsolver; + + // Create needed nodes, reuse if possible + while (nodePool.length < Nbodies) + { + nodePool.push(this.createNode()); + } + nodes.length = Nbodies; + for (let i = 0; i < Nbodies; i++) + { + nodes[i] = nodePool[i]; + } + + // Reset node values + for (let i = 0; i !== Nbodies; i++) + { + const node = nodes[i]; + node.body = bodies[i]; + node.children.length = 0; + node.eqs.length = 0; + node.visited = false; + } + for (let k = 0; k !== Neq; k++) + { + const eq = equations[k]; + const i0 = bodies.indexOf(eq.bi); + const j = bodies.indexOf(eq.bj); + const ni = nodes[i0]; + const nj = nodes[j]; + ni.children.push(nj); + ni.eqs.push(eq); + nj.children.push(ni); + nj.eqs.push(eq); + } + + let child; let n = 0; let + eqs = SplitSolverSolveEqs; + + subsolver.tolerance = this.tolerance; + subsolver.iterations = this.iterations; + + const dummyWorld = SplitSolverSolveDummyWorld; + while ((child = getUnvisitedNode(nodes))) + { + eqs.length = 0; + dummyWorld.bodies.length = 0; + bfs(child, visitFunc, dummyWorld.bodies, eqs); + + const Neqs = eqs.length; + + eqs = eqs.sort(sortById); + + for (let i = 0; i !== Neqs; i++) + { + subsolver.addEquation(eqs[i]); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const iter = subsolver.solve(dt, dummyWorld); + subsolver.removeAllEquations(); + n++; + } + + return n; + } +} + +// Returns the number of subsystems +const SplitSolverSolveNodes: SSNode[] = []; // All allocated node objects +// const SplitSolver_solve_nodePool = []; // All allocated node objects +const SplitSolverSolveEqs = []; // Temp array +// const SplitSolver_solve_bds = []; // Temp array +const SplitSolverSolveDummyWorld: { bodies: Body[] } = { bodies: [] }; // Temp object + +const STATIC = Body.STATIC; +function getUnvisitedNode(nodes) +{ + const Nnodes = nodes.length; + for (let i = 0; i !== Nnodes; i++) + { + const node = nodes[i]; + if (!node.visited && !(node.body.type & STATIC)) + { + return node; + } + } + + return false; +} + +const queue = []; +function bfs(root, visitFunc, bds, eqs) +{ + queue.push(root); + root.visited = true; + visitFunc(root, bds, eqs); + while (queue.length) + { + const node = queue.pop(); + // Loop over unvisited child nodes + let child; + while ((child = getUnvisitedNode(node.children))) + { + child.visited = true; + visitFunc(child, bds, eqs); + queue.push(child); + } + } +} + +function visitFunc(node: SSNode, bds: Body[], eqs: Equation[]) +{ + bds.push(node.body); + const Neqs = node.eqs.length; + for (let i = 0; i !== Neqs; i++) + { + const eq = node.eqs[i]; + if (eqs.indexOf(eq) === -1) + { + eqs.push(eq); + } + } +} + +function sortById(a, b) +{ + return b.id - a.id; +} diff --git a/src/utils/EventTarget.js b/src/utils/EventTarget.js deleted file mode 100644 index ece936842..000000000 --- a/src/utils/EventTarget.js +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Base class for objects that dispatches events. - * @class EventTarget - * @constructor - */ -var EventTarget = function () { - -}; - -module.exports = EventTarget; - -EventTarget.prototype = { - constructor: EventTarget, - - /** - * Add an event listener - * @method addEventListener - * @param {String} type - * @param {Function} listener - * @return {EventTarget} The self object, for chainability. - */ - addEventListener: function ( type, listener ) { - if ( this._listeners === undefined ){ this._listeners = {}; } - var listeners = this._listeners; - if ( listeners[ type ] === undefined ) { - listeners[ type ] = []; - } - if ( listeners[ type ].indexOf( listener ) === - 1 ) { - listeners[ type ].push( listener ); - } - return this; - }, - - /** - * Check if an event listener is added - * @method hasEventListener - * @param {String} type - * @param {Function} listener - * @return {Boolean} - */ - hasEventListener: function ( type, listener ) { - if ( this._listeners === undefined ){ return false; } - var listeners = this._listeners; - if ( listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1 ) { - return true; - } - return false; - }, - - /** - * Check if any event listener of the given type is added - * @method hasAnyEventListener - * @param {String} type - * @return {Boolean} - */ - hasAnyEventListener: function ( type ) { - if ( this._listeners === undefined ){ return false; } - var listeners = this._listeners; - return ( listeners[ type ] !== undefined ); - }, - - /** - * Remove an event listener - * @method removeEventListener - * @param {String} type - * @param {Function} listener - * @return {EventTarget} The self object, for chainability. - */ - removeEventListener: function ( type, listener ) { - if ( this._listeners === undefined ){ return this; } - var listeners = this._listeners; - if ( listeners[type] === undefined ){ return this; } - var index = listeners[ type ].indexOf( listener ); - if ( index !== - 1 ) { - listeners[ type ].splice( index, 1 ); - } - return this; - }, - - /** - * Emit an event. - * @method dispatchEvent - * @param {Object} event - * @param {String} event.type - * @return {EventTarget} The self object, for chainability. - */ - dispatchEvent: function ( event ) { - if ( this._listeners === undefined ){ return this; } - var listeners = this._listeners; - var listenerArray = listeners[ event.type ]; - if ( listenerArray !== undefined ) { - event.target = this; - for ( var i = 0, l = listenerArray.length; i < l; i ++ ) { - listenerArray[ i ].call( this, event ); - } - } - return this; - } -}; diff --git a/src/utils/Octree.js b/src/utils/Octree.js deleted file mode 100644 index e0de9d306..000000000 --- a/src/utils/Octree.js +++ /dev/null @@ -1,233 +0,0 @@ -var AABB = require('../collision/AABB'); -var Vec3 = require('../math/Vec3'); - -module.exports = Octree; - -/** - * @class OctreeNode - * @param {object} [options] - * @param {Octree} [options.root] - * @param {AABB} [options.aabb] - */ -function OctreeNode(options){ - options = options || {}; - - /** - * The root node - * @property {OctreeNode} root - */ - this.root = options.root || null; - - /** - * Boundary of this node - * @property {AABB} aabb - */ - this.aabb = options.aabb ? options.aabb.clone() : new AABB(); - - /** - * Contained data at the current node level. - * @property {Array} data - */ - this.data = []; - - /** - * Children to this node - * @property {Array} children - */ - this.children = []; -} - -/** - * @class Octree - * @param {AABB} aabb The total AABB of the tree - * @param {object} [options] - * @param {number} [options.maxDepth=8] - * @extends OctreeNode - */ -function Octree(aabb, options){ - options = options || {}; - options.root = null; - options.aabb = aabb; - OctreeNode.call(this, options); - - /** - * Maximum subdivision depth - * @property {number} maxDepth - */ - this.maxDepth = typeof(options.maxDepth) !== 'undefined' ? options.maxDepth : 8; -} -Octree.prototype = new OctreeNode(); - -OctreeNode.prototype.reset = function(aabb, options){ - this.children.length = this.data.length = 0; -}; - -/** - * Insert data into this node - * @method insert - * @param {AABB} aabb - * @param {object} elementData - * @return {boolean} True if successful, otherwise false - */ -OctreeNode.prototype.insert = function(aabb, elementData, level){ - var nodeData = this.data; - level = level || 0; - - // Ignore objects that do not belong in this node - if (!this.aabb.contains(aabb)){ - return false; // object cannot be added - } - - var children = this.children; - - if(level < (this.maxDepth || this.root.maxDepth)){ - // Subdivide if there are no children yet - var subdivided = false; - if (!children.length){ - this.subdivide(); - subdivided = true; - } - - // add to whichever node will accept it - for (var i = 0; i !== 8; i++) { - if (children[i].insert(aabb, elementData, level + 1)){ - return true; - } - } - - if(subdivided){ - // No children accepted! Might as well just remove em since they contain none - children.length = 0; - } - } - - // Too deep, or children didnt want it. add it in current node - nodeData.push(elementData); - - return true; -}; - -var halfDiagonal = new Vec3(); - -/** - * Create 8 equally sized children nodes and put them in the .children array. - * @method subdivide - */ -OctreeNode.prototype.subdivide = function() { - var aabb = this.aabb; - var l = aabb.lowerBound; - var u = aabb.upperBound; - - var children = this.children; - - children.push( - new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(0,0,0) }) }), - new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(1,0,0) }) }), - new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(1,1,0) }) }), - new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(1,1,1) }) }), - new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(0,1,1) }) }), - new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(0,0,1) }) }), - new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(1,0,1) }) }), - new OctreeNode({ aabb: new AABB({ lowerBound: new Vec3(0,1,0) }) }) - ); - - u.vsub(l, halfDiagonal); - halfDiagonal.scale(0.5, halfDiagonal); - - var root = this.root || this; - - for (var i = 0; i !== 8; i++) { - var child = children[i]; - - // Set current node as root - child.root = root; - - // Compute bounds - var lowerBound = child.aabb.lowerBound; - lowerBound.x *= halfDiagonal.x; - lowerBound.y *= halfDiagonal.y; - lowerBound.z *= halfDiagonal.z; - - lowerBound.vadd(l, lowerBound); - - // Upper bound is always lower bound + halfDiagonal - lowerBound.vadd(halfDiagonal, child.aabb.upperBound); - } -}; - -/** - * Get all data, potentially within an AABB - * @method aabbQuery - * @param {AABB} aabb - * @param {array} result - * @return {array} The "result" object - */ -OctreeNode.prototype.aabbQuery = function(aabb, result) { - - var nodeData = this.data; - - // abort if the range does not intersect this node - // if (!this.aabb.overlaps(aabb)){ - // return result; - // } - - // Add objects at this level - // Array.prototype.push.apply(result, nodeData); - - // Add child data - // @todo unwrap recursion into a queue / loop, that's faster in JS - var children = this.children; - - - // for (var i = 0, N = this.children.length; i !== N; i++) { - // children[i].aabbQuery(aabb, result); - // } - - var queue = [this]; - while (queue.length) { - var node = queue.pop(); - if (node.aabb.overlaps(aabb)){ - Array.prototype.push.apply(result, node.data); - } - Array.prototype.push.apply(queue, node.children); - } - - return result; -}; - -var tmpAABB = new AABB(); - -/** - * Get all data, potentially intersected by a ray. - * @method rayQuery - * @param {Ray} ray - * @param {Transform} treeTransform - * @param {array} result - * @return {array} The "result" object - */ -OctreeNode.prototype.rayQuery = function(ray, treeTransform, result) { - - // Use aabb query for now. - // @todo implement real ray query which needs less lookups - ray.getAABB(tmpAABB); - tmpAABB.toLocalFrame(treeTransform, tmpAABB); - this.aabbQuery(tmpAABB, result); - - return result; -}; - -/** - * @method removeEmptyNodes - */ -OctreeNode.prototype.removeEmptyNodes = function() { - var queue = [this]; - while (queue.length) { - var node = queue.pop(); - for (var i = node.children.length - 1; i >= 0; i--) { - if(!node.children[i].data.length){ - node.children.splice(i, 1); - } - } - Array.prototype.push.apply(queue, node.children); - } -}; diff --git a/src/utils/Octree.ts b/src/utils/Octree.ts new file mode 100644 index 000000000..20f4ae0ca --- /dev/null +++ b/src/utils/Octree.ts @@ -0,0 +1,247 @@ +import { Box3, Vector3 } from 'feng3d'; +import { Ray } from '../collision/Ray'; +import { Transform } from '../math/Transform'; + +export class OctreeNode +{ + /** + * The root node + */ + root: OctreeNode; + + /** + * Boundary of this node + */ + aabb: Box3; + /** + * Contained data at the current node level. + * @property {Array} data + */ + data: T[]; + + /** + * Children to this node + */ + children: OctreeNode[]; + maxDepth: number; + + /** + * + * @param options + */ + constructor(options: { root?: OctreeNode, aabb?: Box3 } = {}) + { + this.root = options.root || null; + this.aabb = options.aabb ? options.aabb.clone() : new Box3(); + this.data = []; + this.children = []; + } + + reset() + { + this.children.length = this.data.length = 0; + } + + /** + * Insert data into this node + * + * @param aabb + * @param elementData + * @return True if successful, otherwise false + */ + insert(aabb: Box3, elementData: T, level = 0) + { + const nodeData = this.data; + + // Ignore objects that do not belong in this node + if (!this.aabb.contains(aabb)) + { + return false; // object cannot be added + } + + const children = this.children; + + if (level < (this.maxDepth || this.root.maxDepth)) + { + // Subdivide if there are no children yet + let subdivided = false; + if (!children.length) + { + this.subdivide(); + subdivided = true; + } + + // add to whichever node will accept it + for (let i = 0; i !== 8; i++) + { + if (children[i].insert(aabb, elementData, level + 1)) + { + return true; + } + } + + if (subdivided) + { + // No children accepted! Might as well just remove em since they contain none + children.length = 0; + } + } + + // Too deep, or children didnt want it. add it in current node + nodeData.push(elementData); + + return true; + } + + /** + * Create 8 equally sized children nodes and put them in the .children array. + */ + subdivide() + { + const aabb = this.aabb; + const l = aabb.min; + const u = aabb.max; + + const children = this.children; + + children.push( + new OctreeNode({ aabb: new Box3(new Vector3(0, 0, 0)) }), + new OctreeNode({ aabb: new Box3(new Vector3(1, 0, 0)) }), + new OctreeNode({ aabb: new Box3(new Vector3(1, 1, 0)) }), + new OctreeNode({ aabb: new Box3(new Vector3(1, 1, 1)) }), + new OctreeNode({ aabb: new Box3(new Vector3(0, 1, 1)) }), + new OctreeNode({ aabb: new Box3(new Vector3(0, 0, 1)) }), + new OctreeNode({ aabb: new Box3(new Vector3(1, 0, 1)) }), + new OctreeNode({ aabb: new Box3(new Vector3(0, 1, 0)) }) + ); + + u.subTo(l, halfDiagonal); + halfDiagonal.scaleNumberTo(0.5, halfDiagonal); + + const root = this.root || this; + + for (let i = 0; i !== 8; i++) + { + const child = children[i]; + + // Set current node as root + child.root = root; + + // Compute bounds + const lowerBound = child.aabb.min; + lowerBound.x *= halfDiagonal.x; + lowerBound.y *= halfDiagonal.y; + lowerBound.z *= halfDiagonal.z; + + lowerBound.addTo(l, lowerBound); + + // Upper bound is always lower bound + halfDiagonal + lowerBound.addTo(halfDiagonal, child.aabb.max); + } + } + + /** + * Get all data, potentially within an AABB + * + * @param aabb + * @param result + * @return The "result" object + */ + aabbQuery(aabb: Box3, result: T[]) + { + // const nodeData = this.data; + + // abort if the range does not intersect this node + // if (!this.aabb.overlaps(aabb)){ + // return result; + // } + + // Add objects at this level + // Array.prototype.push.apply(result, nodeData); + + // Add child data + // @todo unwrap recursion into a queue / loop, that's faster in JS + // const children = this.children; + + // for (var i = 0, N = this.children.length; i !== N; i++) { + // children[i].aabbQuery(aabb, result); + // } + + const queue = [this]; + while (queue.length) + { + const node = queue.pop(); + if (node.aabb.overlaps(aabb)) + { + Array.prototype.push.apply(result, node.data); + } + Array.prototype.push.apply(queue, node.children); + } + + return result; + } + + /** + * Get all data, potentially intersected by a ray. + * + * @param ray + * @param treeTransform + * @param result + * @return The "result" object + */ + rayQuery(ray: Ray, treeTransform: Transform, result: T[]) + { + // Use aabb query for now. + // @todo implement real ray query which needs less lookups + ray.getAABB(tmpAABB); + treeTransform.toLocalFrameBox3(tmpAABB, tmpAABB); + this.aabbQuery(tmpAABB, result); + + return result; + } + + removeEmptyNodes() + { + const queue = [this]; + while (queue.length) + { + const node = queue.pop(); + for (let i = node.children.length - 1; i >= 0; i--) + { + if (!node.children[i].data.length) + { + node.children.splice(i, 1); + } + } + Array.prototype.push.apply(queue, node.children); + } + } +} + +export class Octree extends OctreeNode +{ + /** + * Maximum subdivision depth + */ + declare maxDepth: number; + + /** + * @class Octree + * @param {Box3} aabb The total AABB of the tree + * @param {object} [options] + * @param {number} [options.maxDepth=8] + * @extends OctreeNode + */ + constructor(aabb?: Box3, options: { root?: OctreeNode, aabb?: Box3, maxDepth?: number } = {}) + { + options.root = null; + options.aabb = aabb; + super(options); + + this.maxDepth = typeof (options.maxDepth) !== 'undefined' ? options.maxDepth : 8; + } +} + +const halfDiagonal = new Vector3(); + +const tmpAABB = new Box3(); diff --git a/src/utils/Pool.js b/src/utils/Pool.js deleted file mode 100644 index c0d8db52a..000000000 --- a/src/utils/Pool.js +++ /dev/null @@ -1,75 +0,0 @@ -module.exports = Pool; - -/** - * For pooling objects that can be reused. - * @class Pool - * @constructor - */ -function Pool(){ - /** - * The pooled objects - * @property {Array} objects - */ - this.objects = []; - - /** - * Constructor of the objects - * @property {mixed} type - */ - this.type = Object; -} - -/** - * Release an object after use - * @method release - * @param {Object} obj - */ -Pool.prototype.release = function(){ - var Nargs = arguments.length; - for(var i=0; i!==Nargs; i++){ - this.objects.push(arguments[i]); - } - return this; -}; - -/** - * Get an object - * @method get - * @return {mixed} - */ -Pool.prototype.get = function(){ - if(this.objects.length===0){ - return this.constructObject(); - } else { - return this.objects.pop(); - } -}; - -/** - * Construct an object. Should be implmented in each subclass. - * @method constructObject - * @return {mixed} - */ -Pool.prototype.constructObject = function(){ - throw new Error("constructObject() not implemented in this Pool subclass yet!"); -}; - -/** - * @method resize - * @param {number} size - * @return {Pool} Self, for chaining - */ -Pool.prototype.resize = function (size) { - var objects = this.objects; - - while (objects.length > size) { - objects.pop(); - } - - while (objects.length < size) { - objects.push(this.constructObject()); - } - - return this; -}; - diff --git a/src/utils/TupleDictionary.js b/src/utils/TupleDictionary.js deleted file mode 100644 index 6a236259f..000000000 --- a/src/utils/TupleDictionary.js +++ /dev/null @@ -1,65 +0,0 @@ -module.exports = TupleDictionary; - -/** - * @class TupleDictionary - * @constructor - */ -function TupleDictionary() { - - /** - * The data storage - * @property data - * @type {Object} - */ - this.data = { keys:[] }; -} - -/** - * @method get - * @param {Number} i - * @param {Number} j - * @return {Number} - */ -TupleDictionary.prototype.get = function(i, j) { - if (i > j) { - // swap - var temp = j; - j = i; - i = temp; - } - return this.data[i+'-'+j]; -}; - -/** - * @method set - * @param {Number} i - * @param {Number} j - * @param {Number} value - */ -TupleDictionary.prototype.set = function(i, j, value) { - if (i > j) { - var temp = j; - j = i; - i = temp; - } - var key = i+'-'+j; - - // Check if key already exists - if(!this.get(i,j)){ - this.data.keys.push(key); - } - - this.data[key] = value; -}; - -/** - * @method reset - */ -TupleDictionary.prototype.reset = function() { - var data = this.data, - keys = data.keys; - while(keys.length > 0){ - var key = keys.pop(); - delete data[key]; - } -}; diff --git a/src/utils/Utils.js b/src/utils/Utils.js deleted file mode 100644 index 5c3bde7cf..000000000 --- a/src/utils/Utils.js +++ /dev/null @@ -1,23 +0,0 @@ -function Utils(){} - -module.exports = Utils; - -/** - * Extend an options object with default values. - * @static - * @method defaults - * @param {object} options The options object. May be falsy: in this case, a new object is created and returned. - * @param {object} defaults An object containing default values. - * @return {object} The modified options object. - */ -Utils.defaults = function(options, defaults){ - options = options || {}; - - for(var key in defaults){ - if(!(key in options)){ - options[key] = defaults[key]; - } - } - - return options; -}; diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts new file mode 100644 index 000000000..f90bf36a4 --- /dev/null +++ b/src/utils/Utils.ts @@ -0,0 +1,22 @@ +export class Utils +{ + /** + * Extend an options object with default values. + * @param options The options object. May be falsy: in this case, a new object is created and returned. + * @param defaults An object containing default values. + * @return The modified options object. + */ + static defaults(options: Object, defaults: Object) + { + options = options || {}; + for (const key in defaults) + { + if (!(key in options)) + { + options[key] = defaults[key]; + } + } + + return options; + } +} diff --git a/src/utils/Vec3Pool.js b/src/utils/Vec3Pool.js deleted file mode 100644 index 254f400f0..000000000 --- a/src/utils/Vec3Pool.js +++ /dev/null @@ -1,24 +0,0 @@ -module.exports = Vec3Pool; - -var Vec3 = require('../math/Vec3'); -var Pool = require('./Pool'); - -/** - * @class Vec3Pool - * @constructor - * @extends Pool - */ -function Vec3Pool(){ - Pool.call(this); - this.type = Vec3; -} -Vec3Pool.prototype = new Pool(); - -/** - * Construct a vector - * @method constructObject - * @return {Vec3} - */ -Vec3Pool.prototype.constructObject = function(){ - return new Vec3(); -}; diff --git a/src/world/Narrowphase.js b/src/world/Narrowphase.js deleted file mode 100644 index 1d4bb462a..000000000 --- a/src/world/Narrowphase.js +++ /dev/null @@ -1,1864 +0,0 @@ -module.exports = Narrowphase; - -var AABB = require('../collision/AABB'); -var Body = require('../objects/Body'); -var Shape = require('../shapes/Shape'); -var Ray = require('../collision/Ray'); -var Vec3 = require('../math/Vec3'); -var Transform = require('../math/Transform'); -var ConvexPolyhedron = require('../shapes/ConvexPolyhedron'); -var Quaternion = require('../math/Quaternion'); -var Solver = require('../solver/Solver'); -var Vec3Pool = require('../utils/Vec3Pool'); -var ContactEquation = require('../equations/ContactEquation'); -var FrictionEquation = require('../equations/FrictionEquation'); - -/** - * Helper class for the World. Generates ContactEquations. - * @class Narrowphase - * @constructor - * @todo Sphere-ConvexPolyhedron contacts - * @todo Contact reduction - * @todo should move methods to prototype - */ -function Narrowphase(world){ - - /** - * Internal storage of pooled contact points. - * @property {Array} contactPointPool - */ - this.contactPointPool = []; - - this.frictionEquationPool = []; - - this.result = []; - this.frictionResult = []; - - /** - * Pooled vectors. - * @property {Vec3Pool} v3pool - */ - this.v3pool = new Vec3Pool(); - - this.world = world; - this.currentContactMaterial = null; - - /** - * @property {Boolean} enableFrictionReduction - */ - this.enableFrictionReduction = false; -} - -/** - * Make a contact object, by using the internal pool or creating a new one. - * @method createContactEquation - * @param {Body} bi - * @param {Body} bj - * @param {Shape} si - * @param {Shape} sj - * @param {Shape} overrideShapeA - * @param {Shape} overrideShapeB - * @return {ContactEquation} - */ -Narrowphase.prototype.createContactEquation = function(bi, bj, si, sj, overrideShapeA, overrideShapeB){ - var c; - if(this.contactPointPool.length){ - c = this.contactPointPool.pop(); - c.bi = bi; - c.bj = bj; - } else { - c = new ContactEquation(bi, bj); - } - - c.enabled = bi.collisionResponse && bj.collisionResponse && si.collisionResponse && sj.collisionResponse; - - var cm = this.currentContactMaterial; - - c.restitution = cm.restitution; - - c.setSpookParams( - cm.contactEquationStiffness, - cm.contactEquationRelaxation, - this.world.dt - ); - - var matA = si.material || bi.material; - var matB = sj.material || bj.material; - if(matA && matB && matA.restitution >= 0 && matB.restitution >= 0){ - c.restitution = matA.restitution * matB.restitution; - } - - c.si = overrideShapeA || si; - c.sj = overrideShapeB || sj; - - return c; -}; - -Narrowphase.prototype.createFrictionEquationsFromContact = function(contactEquation, outArray){ - var bodyA = contactEquation.bi; - var bodyB = contactEquation.bj; - var shapeA = contactEquation.si; - var shapeB = contactEquation.sj; - - var world = this.world; - var cm = this.currentContactMaterial; - - // If friction or restitution were specified in the material, use them - var friction = cm.friction; - var matA = shapeA.material || bodyA.material; - var matB = shapeB.material || bodyB.material; - if(matA && matB && matA.friction >= 0 && matB.friction >= 0){ - friction = matA.friction * matB.friction; - } - - if(friction > 0){ - - // Create 2 tangent equations - var mug = friction * world.gravity.length(); - var reducedMass = (bodyA.invMass + bodyB.invMass); - if(reducedMass > 0){ - reducedMass = 1/reducedMass; - } - var pool = this.frictionEquationPool; - var c1 = pool.length ? pool.pop() : new FrictionEquation(bodyA,bodyB,mug*reducedMass); - var c2 = pool.length ? pool.pop() : new FrictionEquation(bodyA,bodyB,mug*reducedMass); - - c1.bi = c2.bi = bodyA; - c1.bj = c2.bj = bodyB; - c1.minForce = c2.minForce = -mug*reducedMass; - c1.maxForce = c2.maxForce = mug*reducedMass; - - // Copy over the relative vectors - c1.ri.copy(contactEquation.ri); - c1.rj.copy(contactEquation.rj); - c2.ri.copy(contactEquation.ri); - c2.rj.copy(contactEquation.rj); - - // Construct tangents - contactEquation.ni.tangents(c1.t, c2.t); - - // Set spook params - c1.setSpookParams(cm.frictionEquationStiffness, cm.frictionEquationRelaxation, world.dt); - c2.setSpookParams(cm.frictionEquationStiffness, cm.frictionEquationRelaxation, world.dt); - - c1.enabled = c2.enabled = contactEquation.enabled; - - outArray.push(c1, c2); - - return true; - } - - return false; -}; - -var averageNormal = new Vec3(); -var averageContactPointA = new Vec3(); -var averageContactPointB = new Vec3(); - -// Take the average N latest contact point on the plane. -Narrowphase.prototype.createFrictionFromAverage = function(numContacts){ - // The last contactEquation - var c = this.result[this.result.length - 1]; - - // Create the result: two "average" friction equations - if (!this.createFrictionEquationsFromContact(c, this.frictionResult) || numContacts === 1) { - return; - } - - var f1 = this.frictionResult[this.frictionResult.length - 2]; - var f2 = this.frictionResult[this.frictionResult.length - 1]; - - averageNormal.setZero(); - averageContactPointA.setZero(); - averageContactPointB.setZero(); - - var bodyA = c.bi; - var bodyB = c.bj; - for(var i=0; i!==numContacts; i++){ - c = this.result[this.result.length - 1 - i]; - if(c.bodyA !== bodyA){ - averageNormal.vadd(c.ni, averageNormal); - averageContactPointA.vadd(c.ri, averageContactPointA); - averageContactPointB.vadd(c.rj, averageContactPointB); - } else { - averageNormal.vsub(c.ni, averageNormal); - averageContactPointA.vadd(c.rj, averageContactPointA); - averageContactPointB.vadd(c.ri, averageContactPointB); - } - } - - var invNumContacts = 1 / numContacts; - averageContactPointA.scale(invNumContacts, f1.ri); - averageContactPointB.scale(invNumContacts, f1.rj); - f2.ri.copy(f1.ri); // Should be the same - f2.rj.copy(f1.rj); - averageNormal.normalize(); - averageNormal.tangents(f1.t, f2.t); - // return eq; -}; - - -var tmpVec1 = new Vec3(); -var tmpVec2 = new Vec3(); -var tmpQuat1 = new Quaternion(); -var tmpQuat2 = new Quaternion(); - -/** - * Generate all contacts between a list of body pairs - * @method getContacts - * @param {array} p1 Array of body indices - * @param {array} p2 Array of body indices - * @param {World} world - * @param {array} result Array to store generated contacts - * @param {array} oldcontacts Optional. Array of reusable contact objects - */ -Narrowphase.prototype.getContacts = function(p1, p2, world, result, oldcontacts, frictionResult, frictionPool){ - // Save old contact objects - this.contactPointPool = oldcontacts; - this.frictionEquationPool = frictionPool; - this.result = result; - this.frictionResult = frictionResult; - - var qi = tmpQuat1; - var qj = tmpQuat2; - var xi = tmpVec1; - var xj = tmpVec2; - - for(var k=0, N=p1.length; k!==N; k++){ - - // Get current collision bodies - var bi = p1[k], - bj = p2[k]; - - // Get contact material - var bodyContactMaterial = null; - if(bi.material && bj.material){ - bodyContactMaterial = world.getContactMaterial(bi.material,bj.material) || null; - } - - var justTest = ( - ( - (bi.type & Body.KINEMATIC) && (bj.type & Body.STATIC) - ) || ( - (bi.type & Body.STATIC) && (bj.type & Body.KINEMATIC) - ) || ( - (bi.type & Body.KINEMATIC) && (bj.type & Body.KINEMATIC) - ) - ); - - for (var i = 0; i < bi.shapes.length; i++) { - bi.quaternion.mult(bi.shapeOrientations[i], qi); - bi.quaternion.vmult(bi.shapeOffsets[i], xi); - xi.vadd(bi.position, xi); - var si = bi.shapes[i]; - - for (var j = 0; j < bj.shapes.length; j++) { - - // Compute world transform of shapes - bj.quaternion.mult(bj.shapeOrientations[j], qj); - bj.quaternion.vmult(bj.shapeOffsets[j], xj); - xj.vadd(bj.position, xj); - var sj = bj.shapes[j]; - - if(!((si.collisionFilterMask & sj.collisionFilterGroup) && (sj.collisionFilterMask & si.collisionFilterGroup))){ - continue; - } - - if(xi.distanceTo(xj) > si.boundingSphereRadius + sj.boundingSphereRadius){ - continue; - } - - // Get collision material - var shapeContactMaterial = null; - if(si.material && sj.material){ - shapeContactMaterial = world.getContactMaterial(si.material,sj.material) || null; - } - - this.currentContactMaterial = shapeContactMaterial || bodyContactMaterial || world.defaultContactMaterial; - - // Get contacts - var resolver = this[si.type | sj.type]; - if(resolver){ - var retval = false; - if (si.type < sj.type) { - retval = resolver.call(this, si, sj, xi, xj, qi, qj, bi, bj, si, sj, justTest); - } else { - retval = resolver.call(this, sj, si, xj, xi, qj, qi, bj, bi, si, sj, justTest); - } - - if(retval && justTest){ - // Register overlap - world.shapeOverlapKeeper.set(si.id, sj.id); - world.bodyOverlapKeeper.set(bi.id, bj.id); - } - } - } - } - } -}; - -var numWarnings = 0; -var maxWarnings = 10; - -function warn(msg){ - if(numWarnings > maxWarnings){ - return; - } - - numWarnings++; - - console.warn(msg); -} - -Narrowphase.prototype[Shape.types.BOX | Shape.types.BOX] = -Narrowphase.prototype.boxBox = function(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,justTest){ - si.convexPolyhedronRepresentation.material = si.material; - sj.convexPolyhedronRepresentation.material = sj.material; - si.convexPolyhedronRepresentation.collisionResponse = si.collisionResponse; - sj.convexPolyhedronRepresentation.collisionResponse = sj.collisionResponse; - return this.convexConvex(si.convexPolyhedronRepresentation,sj.convexPolyhedronRepresentation,xi,xj,qi,qj,bi,bj,si,sj,justTest); -}; - -Narrowphase.prototype[Shape.types.BOX | Shape.types.CONVEXPOLYHEDRON] = -Narrowphase.prototype.boxConvex = function(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,justTest){ - si.convexPolyhedronRepresentation.material = si.material; - si.convexPolyhedronRepresentation.collisionResponse = si.collisionResponse; - return this.convexConvex(si.convexPolyhedronRepresentation,sj,xi,xj,qi,qj,bi,bj,si,sj,justTest); -}; - -Narrowphase.prototype[Shape.types.BOX | Shape.types.PARTICLE] = -Narrowphase.prototype.boxParticle = function(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,justTest){ - si.convexPolyhedronRepresentation.material = si.material; - si.convexPolyhedronRepresentation.collisionResponse = si.collisionResponse; - return this.convexParticle(si.convexPolyhedronRepresentation,sj,xi,xj,qi,qj,bi,bj,si,sj,justTest); -}; - -/** - * @method sphereSphere - * @param {Shape} si - * @param {Shape} sj - * @param {Vec3} xi - * @param {Vec3} xj - * @param {Quaternion} qi - * @param {Quaternion} qj - * @param {Body} bi - * @param {Body} bj - */ -Narrowphase.prototype[Shape.types.SPHERE] = -Narrowphase.prototype.sphereSphere = function(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,justTest){ - if(justTest){ - return xi.distanceSquared(xj) < Math.pow(si.radius + sj.radius, 2); - } - - // We will have only one contact in this case - var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj); - - // Contact normal - xj.vsub(xi, r.ni); - r.ni.normalize(); - - // Contact point locations - r.ri.copy(r.ni); - r.rj.copy(r.ni); - r.ri.mult(si.radius, r.ri); - r.rj.mult(-sj.radius, r.rj); - - r.ri.vadd(xi, r.ri); - r.ri.vsub(bi.position, r.ri); - - r.rj.vadd(xj, r.rj); - r.rj.vsub(bj.position, r.rj); - - this.result.push(r); - - this.createFrictionEquationsFromContact(r, this.frictionResult); -}; - -/** - * @method planeTrimesh - * @param {Shape} si - * @param {Shape} sj - * @param {Vec3} xi - * @param {Vec3} xj - * @param {Quaternion} qi - * @param {Quaternion} qj - * @param {Body} bi - * @param {Body} bj - */ -var planeTrimesh_normal = new Vec3(); -var planeTrimesh_relpos = new Vec3(); -var planeTrimesh_projected = new Vec3(); -Narrowphase.prototype[Shape.types.PLANE | Shape.types.TRIMESH] = -Narrowphase.prototype.planeTrimesh = function( - planeShape, - trimeshShape, - planePos, - trimeshPos, - planeQuat, - trimeshQuat, - planeBody, - trimeshBody, - rsi, - rsj, - justTest -){ - // Make contacts! - var v = new Vec3(); - - var normal = planeTrimesh_normal; - normal.set(0,0,1); - planeQuat.vmult(normal,normal); // Turn normal according to plane - - for(var i=0; i 0 && positionAlongEdgeB < 0){ - - // Now check the orthogonal distance from edge to sphere center - localSpherePos.vsub(edgeVertexA, tmp); - - edgeVectorUnit.copy(edgeVector); - edgeVectorUnit.normalize(); - positionAlongEdgeA = tmp.dot(edgeVectorUnit); - - edgeVectorUnit.scale(positionAlongEdgeA, tmp); - tmp.vadd(edgeVertexA, tmp); - - // tmp is now the sphere center position projected to the edge, defined locally in the trimesh frame - var dist = tmp.distanceTo(localSpherePos); - if(dist < sphereShape.radius){ - - if(justTest){ - return true; - } - - var r = this.createContactEquation(sphereBody, trimeshBody, sphereShape, trimeshShape,rsi,rsj); - - tmp.vsub(localSpherePos, r.ni); - r.ni.normalize(); - r.ni.scale(sphereShape.radius, r.ri); - - Transform.pointToWorldFrame(trimeshPos, trimeshQuat, tmp, tmp); - tmp.vsub(trimeshBody.position, r.rj); - - Transform.vectorToWorldFrame(trimeshQuat, r.ni, r.ni); - Transform.vectorToWorldFrame(trimeshQuat, r.ri, r.ri); - - this.result.push(r); - this.createFrictionEquationsFromContact(r, this.frictionResult); - } - } - } - } - - // Triangle faces - var va = sphereTrimesh_va; - var vb = sphereTrimesh_vb; - var vc = sphereTrimesh_vc; - var normal = sphereTrimesh_normal; - for(var i=0, N = triangles.length; i !== N; i++){ - trimeshShape.getTriangleVertices(triangles[i], va, vb, vc); - trimeshShape.getNormal(triangles[i], normal); - localSpherePos.vsub(va, tmp); - var dist = tmp.dot(normal); - normal.scale(dist, tmp); - localSpherePos.vsub(tmp, tmp); - - // tmp is now the sphere position projected to the triangle plane - dist = tmp.distanceTo(localSpherePos); - if(Ray.pointInTriangle(tmp, va, vb, vc) && dist < sphereShape.radius){ - if(justTest){ - return true; - } - var r = this.createContactEquation(sphereBody, trimeshBody, sphereShape, trimeshShape,rsi,rsj); - - tmp.vsub(localSpherePos, r.ni); - r.ni.normalize(); - r.ni.scale(sphereShape.radius, r.ri); - - Transform.pointToWorldFrame(trimeshPos, trimeshQuat, tmp, tmp); - tmp.vsub(trimeshBody.position, r.rj); - - Transform.vectorToWorldFrame(trimeshQuat, r.ni, r.ni); - Transform.vectorToWorldFrame(trimeshQuat, r.ri, r.ri); - - this.result.push(r); - this.createFrictionEquationsFromContact(r, this.frictionResult); - } - } - - triangles.length = 0; -}; - -var point_on_plane_to_sphere = new Vec3(); -var plane_to_sphere_ortho = new Vec3(); - -/** - * @method spherePlane - * @param {Shape} si - * @param {Shape} sj - * @param {Vec3} xi - * @param {Vec3} xj - * @param {Quaternion} qi - * @param {Quaternion} qj - * @param {Body} bi - * @param {Body} bj - */ -Narrowphase.prototype[Shape.types.SPHERE | Shape.types.PLANE] = -Narrowphase.prototype.spherePlane = function(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,justTest){ - // We will have one contact in this case - var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj); - - // Contact normal - r.ni.set(0,0,1); - qj.vmult(r.ni, r.ni); - r.ni.negate(r.ni); // body i is the sphere, flip normal - r.ni.normalize(); // Needed? - - // Vector from sphere center to contact point - r.ni.mult(si.radius, r.ri); - - // Project down sphere on plane - xi.vsub(xj, point_on_plane_to_sphere); - r.ni.mult(r.ni.dot(point_on_plane_to_sphere), plane_to_sphere_ortho); - point_on_plane_to_sphere.vsub(plane_to_sphere_ortho,r.rj); // The sphere position projected to plane - - if(-point_on_plane_to_sphere.dot(r.ni) <= si.radius){ - - if(justTest){ - return true; - } - - // Make it relative to the body - var ri = r.ri; - var rj = r.rj; - ri.vadd(xi, ri); - ri.vsub(bi.position, ri); - rj.vadd(xj, rj); - rj.vsub(bj.position, rj); - - this.result.push(r); - this.createFrictionEquationsFromContact(r, this.frictionResult); - } -}; - -// See http://bulletphysics.com/Bullet/BulletFull/SphereTriangleDetector_8cpp_source.html -var pointInPolygon_edge = new Vec3(); -var pointInPolygon_edge_x_normal = new Vec3(); -var pointInPolygon_vtp = new Vec3(); -function pointInPolygon(verts, normal, p){ - var positiveResult = null; - var N = verts.length; - for(var i=0; i!==N; i++){ - var v = verts[i]; - - // Get edge to the next vertex - var edge = pointInPolygon_edge; - verts[(i+1) % (N)].vsub(v,edge); - - // Get cross product between polygon normal and the edge - var edge_x_normal = pointInPolygon_edge_x_normal; - //var edge_x_normal = new Vec3(); - edge.cross(normal,edge_x_normal); - - // Get vector between point and current vertex - var vertex_to_p = pointInPolygon_vtp; - p.vsub(v,vertex_to_p); - - // This dot product determines which side of the edge the point is - var r = edge_x_normal.dot(vertex_to_p); - - // If all such dot products have same sign, we are inside the polygon. - if(positiveResult===null || (r>0 && positiveResult===true) || (r<=0 && positiveResult===false)){ - if(positiveResult===null){ - positiveResult = r>0; - } - continue; - } else { - return false; // Encountered some other sign. Exit. - } - } - - // If we got here, all dot products were of the same sign. - return true; -} - -var box_to_sphere = new Vec3(); -var sphereBox_ns = new Vec3(); -var sphereBox_ns1 = new Vec3(); -var sphereBox_ns2 = new Vec3(); -var sphereBox_sides = [new Vec3(),new Vec3(),new Vec3(),new Vec3(),new Vec3(),new Vec3()]; -var sphereBox_sphere_to_corner = new Vec3(); -var sphereBox_side_ns = new Vec3(); -var sphereBox_side_ns1 = new Vec3(); -var sphereBox_side_ns2 = new Vec3(); - -/** - * @method sphereBox - * @param {Shape} si - * @param {Shape} sj - * @param {Vec3} xi - * @param {Vec3} xj - * @param {Quaternion} qi - * @param {Quaternion} qj - * @param {Body} bi - * @param {Body} bj - */ -Narrowphase.prototype[Shape.types.SPHERE | Shape.types.BOX] = -Narrowphase.prototype.sphereBox = function(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,justTest){ - var v3pool = this.v3pool; - - // we refer to the box as body j - var sides = sphereBox_sides; - xi.vsub(xj,box_to_sphere); - sj.getSideNormals(sides,qj); - var R = si.radius; - var penetrating_sides = []; - - // Check side (plane) intersections - var found = false; - - // Store the resulting side penetration info - var side_ns = sphereBox_side_ns; - var side_ns1 = sphereBox_side_ns1; - var side_ns2 = sphereBox_side_ns2; - var side_h = null; - var side_penetrations = 0; - var side_dot1 = 0; - var side_dot2 = 0; - var side_distance = null; - for(var idx=0,nsides=sides.length; idx!==nsides && found===false; idx++){ - // Get the plane side normal (ns) - var ns = sphereBox_ns; - ns.copy(sides[idx]); - - var h = ns.norm(); - ns.normalize(); - - // The normal/distance dot product tells which side of the plane we are - var dot = box_to_sphere.dot(ns); - - if(dot0){ - // Intersects plane. Now check the other two dimensions - var ns1 = sphereBox_ns1; - var ns2 = sphereBox_ns2; - ns1.copy(sides[(idx+1)%3]); - ns2.copy(sides[(idx+2)%3]); - var h1 = ns1.norm(); - var h2 = ns2.norm(); - ns1.normalize(); - ns2.normalize(); - var dot1 = box_to_sphere.dot(ns1); - var dot2 = box_to_sphere.dot(ns2); - if(dot1

    -h1 && dot2

    -h2){ - var dist = Math.abs(dot-h-R); - if(side_distance===null || dist < side_distance){ - side_distance = dist; - side_dot1 = dot1; - side_dot2 = dot2; - side_h = h; - side_ns.copy(ns); - side_ns1.copy(ns1); - side_ns2.copy(ns2); - side_penetrations++; - - if(justTest){ - return true; - } - } - } - } - } - if(side_penetrations){ - found = true; - var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj); - side_ns.mult(-R,r.ri); // Sphere r - r.ni.copy(side_ns); - r.ni.negate(r.ni); // Normal should be out of sphere - side_ns.mult(side_h,side_ns); - side_ns1.mult(side_dot1,side_ns1); - side_ns.vadd(side_ns1,side_ns); - side_ns2.mult(side_dot2,side_ns2); - side_ns.vadd(side_ns2,r.rj); - - // Make relative to bodies - r.ri.vadd(xi, r.ri); - r.ri.vsub(bi.position, r.ri); - r.rj.vadd(xj, r.rj); - r.rj.vsub(bj.position, r.rj); - - this.result.push(r); - this.createFrictionEquationsFromContact(r, this.frictionResult); - } - - // Check corners - var rj = v3pool.get(); - var sphere_to_corner = sphereBox_sphere_to_corner; - for(var j=0; j!==2 && !found; j++){ - for(var k=0; k!==2 && !found; k++){ - for(var l=0; l!==2 && !found; l++){ - rj.set(0,0,0); - if(j){ - rj.vadd(sides[0],rj); - } else { - rj.vsub(sides[0],rj); - } - if(k){ - rj.vadd(sides[1],rj); - } else { - rj.vsub(sides[1],rj); - } - if(l){ - rj.vadd(sides[2],rj); - } else { - rj.vsub(sides[2],rj); - } - - // World position of corner - xj.vadd(rj,sphere_to_corner); - sphere_to_corner.vsub(xi,sphere_to_corner); - - if(sphere_to_corner.norm2() < R*R){ - if(justTest){ - return true; - } - found = true; - var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj); - r.ri.copy(sphere_to_corner); - r.ri.normalize(); - r.ni.copy(r.ri); - r.ri.mult(R,r.ri); - r.rj.copy(rj); - - // Make relative to bodies - r.ri.vadd(xi, r.ri); - r.ri.vsub(bi.position, r.ri); - r.rj.vadd(xj, r.rj); - r.rj.vsub(bj.position, r.rj); - - this.result.push(r); - this.createFrictionEquationsFromContact(r, this.frictionResult); - } - } - } - } - v3pool.release(rj); - rj = null; - - // Check edges - var edgeTangent = v3pool.get(); - var edgeCenter = v3pool.get(); - var r = v3pool.get(); // r = edge center to sphere center - var orthogonal = v3pool.get(); - var dist = v3pool.get(); - var Nsides = sides.length; - for(var j=0; j!==Nsides && !found; j++){ - for(var k=0; k!==Nsides && !found; k++){ - if(j%3 !== k%3){ - // Get edge tangent - sides[k].cross(sides[j],edgeTangent); - edgeTangent.normalize(); - sides[j].vadd(sides[k], edgeCenter); - r.copy(xi); - r.vsub(edgeCenter,r); - r.vsub(xj,r); - var orthonorm = r.dot(edgeTangent); // distance from edge center to sphere center in the tangent direction - edgeTangent.mult(orthonorm,orthogonal); // Vector from edge center to sphere center in the tangent direction - - // Find the third side orthogonal to this one - var l = 0; - while(l===j%3 || l===k%3){ - l++; - } - - // vec from edge center to sphere projected to the plane orthogonal to the edge tangent - dist.copy(xi); - dist.vsub(orthogonal,dist); - dist.vsub(edgeCenter,dist); - dist.vsub(xj,dist); - - // Distances in tangent direction and distance in the plane orthogonal to it - var tdist = Math.abs(orthonorm); - var ndist = dist.norm(); - - if(tdist < sides[l].norm() && ndist si.boundingSphereRadius + sj.boundingSphereRadius){ - // return; - // } - - // Check corners - for(var i=0; i!==verts.length; i++){ - var v = verts[i]; - - // World position of corner - var worldCorner = sphereConvex_worldCorner; - qj.vmult(v,worldCorner); - xj.vadd(worldCorner,worldCorner); - var sphere_to_corner = sphereConvex_sphereToCorner; - worldCorner.vsub(xi, sphere_to_corner); - if(sphere_to_corner.norm2() < R * R){ - if(justTest){ - return true; - } - found = true; - var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj); - r.ri.copy(sphere_to_corner); - r.ri.normalize(); - r.ni.copy(r.ri); - r.ri.mult(R,r.ri); - worldCorner.vsub(xj,r.rj); - - // Should be relative to the body. - r.ri.vadd(xi, r.ri); - r.ri.vsub(bi.position, r.ri); - - // Should be relative to the body. - r.rj.vadd(xj, r.rj); - r.rj.vsub(bj.position, r.rj); - - this.result.push(r); - this.createFrictionEquationsFromContact(r, this.frictionResult); - return; - } - } - - // Check side (plane) intersections - var found = false; - for(var i=0, nfaces=faces.length; i!==nfaces && found===false; i++){ - var normal = normals[i]; - var face = faces[i]; - - // Get world-transformed normal of the face - var worldNormal = sphereConvex_worldNormal; - qj.vmult(normal,worldNormal); - - // Get a world vertex from the face - var worldPoint = sphereConvex_worldPoint; - qj.vmult(verts[face[0]],worldPoint); - worldPoint.vadd(xj,worldPoint); - - // Get a point on the sphere, closest to the face normal - var worldSpherePointClosestToPlane = sphereConvex_worldSpherePointClosestToPlane; - worldNormal.mult(-R, worldSpherePointClosestToPlane); - xi.vadd(worldSpherePointClosestToPlane, worldSpherePointClosestToPlane); - - // Vector from a face point to the closest point on the sphere - var penetrationVec = sphereConvex_penetrationVec; - worldSpherePointClosestToPlane.vsub(worldPoint,penetrationVec); - - // The penetration. Negative value means overlap. - var penetration = penetrationVec.dot(worldNormal); - - var worldPointToSphere = sphereConvex_sphereToWorldPoint; - xi.vsub(worldPoint, worldPointToSphere); - - if(penetration < 0 && worldPointToSphere.dot(worldNormal)>0){ - // Intersects plane. Now check if the sphere is inside the face polygon - var faceVerts = []; // Face vertices, in world coords - for(var j=0, Nverts=face.length; j!==Nverts; j++){ - var worldVertex = v3pool.get(); - qj.vmult(verts[face[j]], worldVertex); - xj.vadd(worldVertex,worldVertex); - faceVerts.push(worldVertex); - } - - if(pointInPolygon(faceVerts,worldNormal,xi)){ // Is the sphere center in the face polygon? - if(justTest){ - return true; - } - found = true; - var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj); - - worldNormal.mult(-R, r.ri); // Contact offset, from sphere center to contact - worldNormal.negate(r.ni); // Normal pointing out of sphere - - var penetrationVec2 = v3pool.get(); - worldNormal.mult(-penetration, penetrationVec2); - var penetrationSpherePoint = v3pool.get(); - worldNormal.mult(-R, penetrationSpherePoint); - - //xi.vsub(xj).vadd(penetrationSpherePoint).vadd(penetrationVec2 , r.rj); - xi.vsub(xj,r.rj); - r.rj.vadd(penetrationSpherePoint,r.rj); - r.rj.vadd(penetrationVec2 , r.rj); - - // Should be relative to the body. - r.rj.vadd(xj, r.rj); - r.rj.vsub(bj.position, r.rj); - - // Should be relative to the body. - r.ri.vadd(xi, r.ri); - r.ri.vsub(bi.position, r.ri); - - v3pool.release(penetrationVec2); - v3pool.release(penetrationSpherePoint); - - this.result.push(r); - this.createFrictionEquationsFromContact(r, this.frictionResult); - - // Release world vertices - for(var j=0, Nfaceverts=faceVerts.length; j!==Nfaceverts; j++){ - v3pool.release(faceVerts[j]); - } - - return; // We only expect *one* face contact - } else { - // Edge? - for(var j=0; j!==face.length; j++){ - - // Get two world transformed vertices - var v1 = v3pool.get(); - var v2 = v3pool.get(); - qj.vmult(verts[face[(j+1)%face.length]], v1); - qj.vmult(verts[face[(j+2)%face.length]], v2); - xj.vadd(v1, v1); - xj.vadd(v2, v2); - - // Construct edge vector - var edge = sphereConvex_edge; - v2.vsub(v1,edge); - - // Construct the same vector, but normalized - var edgeUnit = sphereConvex_edgeUnit; - edge.unit(edgeUnit); - - // p is xi projected onto the edge - var p = v3pool.get(); - var v1_to_xi = v3pool.get(); - xi.vsub(v1, v1_to_xi); - var dot = v1_to_xi.dot(edgeUnit); - edgeUnit.mult(dot, p); - p.vadd(v1, p); - - // Compute a vector from p to the center of the sphere - var xi_to_p = v3pool.get(); - p.vsub(xi, xi_to_p); - - // Collision if the edge-sphere distance is less than the radius - // AND if p is in between v1 and v2 - if(dot > 0 && dot*dot si.boundingSphereRadius + sj.boundingSphereRadius){ - return; - } - - if(si.findSeparatingAxis(sj,xi,qi,xj,qj,sepAxis,faceListA,faceListB)){ - var res = []; - var q = convexConvex_q; - si.clipAgainstHull(xi,qi,sj,xj,qj,sepAxis,-100,100,res); - var numContacts = 0; - for(var j = 0; j !== res.length; j++){ - if(justTest){ - return true; - } - var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj), - ri = r.ri, - rj = r.rj; - sepAxis.negate(r.ni); - res[j].normal.negate(q); - q.mult(res[j].depth, q); - res[j].point.vadd(q, ri); - rj.copy(res[j].point); - - // Contact points are in world coordinates. Transform back to relative - ri.vsub(xi,ri); - rj.vsub(xj,rj); - - // Make relative to bodies - ri.vadd(xi, ri); - ri.vsub(bi.position, ri); - rj.vadd(xj, rj); - rj.vsub(bj.position, rj); - - this.result.push(r); - numContacts++; - if(!this.enableFrictionReduction){ - this.createFrictionEquationsFromContact(r, this.frictionResult); - } - } - if(this.enableFrictionReduction && numContacts){ - this.createFrictionFromAverage(numContacts); - } - } -}; - - -/** - * @method convexTrimesh - * @param {Array} result - * @param {Shape} si - * @param {Shape} sj - * @param {Vec3} xi - * @param {Vec3} xj - * @param {Quaternion} qi - * @param {Quaternion} qj - * @param {Body} bi - * @param {Body} bj - */ -// Narrowphase.prototype[Shape.types.CONVEXPOLYHEDRON | Shape.types.TRIMESH] = -// Narrowphase.prototype.convexTrimesh = function(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,faceListA,faceListB){ -// var sepAxis = convexConvex_sepAxis; - -// if(xi.distanceTo(xj) > si.boundingSphereRadius + sj.boundingSphereRadius){ -// return; -// } - -// // Construct a temp hull for each triangle -// var hullB = new ConvexPolyhedron(); - -// hullB.faces = [[0,1,2]]; -// var va = new Vec3(); -// var vb = new Vec3(); -// var vc = new Vec3(); -// hullB.vertices = [ -// va, -// vb, -// vc -// ]; - -// for (var i = 0; i < sj.indices.length / 3; i++) { - -// var triangleNormal = new Vec3(); -// sj.getNormal(i, triangleNormal); -// hullB.faceNormals = [triangleNormal]; - -// sj.getTriangleVertices(i, va, vb, vc); - -// var d = si.testSepAxis(triangleNormal, hullB, xi, qi, xj, qj); -// if(!d){ -// triangleNormal.scale(-1, triangleNormal); -// d = si.testSepAxis(triangleNormal, hullB, xi, qi, xj, qj); - -// if(!d){ -// continue; -// } -// } - -// var res = []; -// var q = convexConvex_q; -// si.clipAgainstHull(xi,qi,hullB,xj,qj,triangleNormal,-100,100,res); -// for(var j = 0; j !== res.length; j++){ -// var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj), -// ri = r.ri, -// rj = r.rj; -// r.ni.copy(triangleNormal); -// r.ni.negate(r.ni); -// res[j].normal.negate(q); -// q.mult(res[j].depth, q); -// res[j].point.vadd(q, ri); -// rj.copy(res[j].point); - -// // Contact points are in world coordinates. Transform back to relative -// ri.vsub(xi,ri); -// rj.vsub(xj,rj); - -// // Make relative to bodies -// ri.vadd(xi, ri); -// ri.vsub(bi.position, ri); -// rj.vadd(xj, rj); -// rj.vsub(bj.position, rj); - -// result.push(r); -// } -// } -// }; - -var particlePlane_normal = new Vec3(); -var particlePlane_relpos = new Vec3(); -var particlePlane_projected = new Vec3(); - -/** - * @method particlePlane - * @param {Array} result - * @param {Shape} si - * @param {Shape} sj - * @param {Vec3} xi - * @param {Vec3} xj - * @param {Quaternion} qi - * @param {Quaternion} qj - * @param {Body} bi - * @param {Body} bj - */ -Narrowphase.prototype[Shape.types.PLANE | Shape.types.PARTICLE] = -Narrowphase.prototype.planeParticle = function(sj,si,xj,xi,qj,qi,bj,bi,rsi,rsj,justTest){ - var normal = particlePlane_normal; - normal.set(0,0,1); - bj.quaternion.vmult(normal,normal); // Turn normal according to plane orientation - var relpos = particlePlane_relpos; - xi.vsub(bj.position,relpos); - var dot = normal.dot(relpos); - if(dot <= 0.0){ - - if(justTest){ - return true; - } - - var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj); - r.ni.copy(normal); // Contact normal is the plane normal - r.ni.negate(r.ni); - r.ri.set(0,0,0); // Center of particle - - // Get particle position projected on plane - var projected = particlePlane_projected; - normal.mult(normal.dot(xi),projected); - xi.vsub(projected,projected); - //projected.vadd(bj.position,projected); - - // rj is now the projected world position minus plane position - r.rj.copy(projected); - this.result.push(r); - this.createFrictionEquationsFromContact(r, this.frictionResult); - } -}; - -var particleSphere_normal = new Vec3(); - -/** - * @method particleSphere - * @param {Array} result - * @param {Shape} si - * @param {Shape} sj - * @param {Vec3} xi - * @param {Vec3} xj - * @param {Quaternion} qi - * @param {Quaternion} qj - * @param {Body} bi - * @param {Body} bj - */ -Narrowphase.prototype[Shape.types.PARTICLE | Shape.types.SPHERE] = -Narrowphase.prototype.sphereParticle = function(sj,si,xj,xi,qj,qi,bj,bi,rsi,rsj,justTest){ - // The normal is the unit vector from sphere center to particle center - var normal = particleSphere_normal; - normal.set(0,0,1); - xi.vsub(xj,normal); - var lengthSquared = normal.norm2(); - - if(lengthSquared <= sj.radius * sj.radius){ - if(justTest){ - return true; - } - var r = this.createContactEquation(bi,bj,si,sj,rsi,rsj); - normal.normalize(); - r.rj.copy(normal); - r.rj.mult(sj.radius,r.rj); - r.ni.copy(normal); // Contact normal - r.ni.negate(r.ni); - r.ri.set(0,0,0); // Center of particle - this.result.push(r); - this.createFrictionEquationsFromContact(r, this.frictionResult); - } -}; - -// WIP -var cqj = new Quaternion(); -var convexParticle_local = new Vec3(); -var convexParticle_normal = new Vec3(); -var convexParticle_penetratedFaceNormal = new Vec3(); -var convexParticle_vertexToParticle = new Vec3(); -var convexParticle_worldPenetrationVec = new Vec3(); - -/** - * @method convexParticle - * @param {Array} result - * @param {Shape} si - * @param {Shape} sj - * @param {Vec3} xi - * @param {Vec3} xj - * @param {Quaternion} qi - * @param {Quaternion} qj - * @param {Body} bi - * @param {Body} bj - */ -Narrowphase.prototype[Shape.types.PARTICLE | Shape.types.CONVEXPOLYHEDRON] = -Narrowphase.prototype.convexParticle = function(sj,si,xj,xi,qj,qi,bj,bi,rsi,rsj,justTest){ - var penetratedFaceIndex = -1; - var penetratedFaceNormal = convexParticle_penetratedFaceNormal; - var worldPenetrationVec = convexParticle_worldPenetrationVec; - var minPenetration = null; - var numDetectedFaces = 0; - - // Convert particle position xi to local coords in the convex - var local = convexParticle_local; - local.copy(xi); - local.vsub(xj,local); // Convert position to relative the convex origin - qj.conjugate(cqj); - cqj.vmult(local,local); - - if(sj.pointIsInside(local)){ - - if(sj.worldVerticesNeedsUpdate){ - sj.computeWorldVertices(xj,qj); - } - if(sj.worldFaceNormalsNeedsUpdate){ - sj.computeWorldFaceNormals(qj); - } - - // For each world polygon in the polyhedra - for(var i=0,nfaces=sj.faces.length; i!==nfaces; i++){ - - // Construct world face vertices - var verts = [ sj.worldVertices[ sj.faces[i][0] ] ]; - var normal = sj.worldFaceNormals[i]; - - // Check how much the particle penetrates the polygon plane. - xi.vsub(verts[0],convexParticle_vertexToParticle); - var penetration = -normal.dot(convexParticle_vertexToParticle); - if(minPenetration===null || Math.abs(penetration) data.length || iMinY > data[0].length){ - return; - } - - // Clamp index to edges - if(iMinX < 0){ iMinX = 0; } - if(iMaxX < 0){ iMaxX = 0; } - if(iMinY < 0){ iMinY = 0; } - if(iMaxY < 0){ iMaxY = 0; } - if(iMinX >= data.length){ iMinX = data.length - 1; } - if(iMaxX >= data.length){ iMaxX = data.length - 1; } - if(iMaxY >= data[0].length){ iMaxY = data[0].length - 1; } - if(iMinY >= data[0].length){ iMinY = data[0].length - 1; } - - var minMax = []; - hfShape.getRectMinMax(iMinX, iMinY, iMaxX, iMaxY, minMax); - var min = minMax[0]; - var max = minMax[1]; - - // Bail out if we're cant touch the bounding height box - if(localConvexPos.z - radius > max || localConvexPos.z + radius < min){ - return; - } - - for(var i = iMinX; i < iMaxX; i++){ - for(var j = iMinY; j < iMaxY; j++){ - - var intersecting = false; - - // Lower triangle - hfShape.getConvexTrianglePillar(i, j, false); - Transform.pointToWorldFrame(hfPos, hfQuat, hfShape.pillarOffset, worldPillarOffset); - if (convexPos.distanceTo(worldPillarOffset) < hfShape.pillarConvex.boundingSphereRadius + convexShape.boundingSphereRadius) { - intersecting = this.convexConvex(convexShape, hfShape.pillarConvex, convexPos, worldPillarOffset, convexQuat, hfQuat, convexBody, hfBody, null, null, justTest, faceList, null); - } - - if(justTest && intersecting){ - return true; - } - - // Upper triangle - hfShape.getConvexTrianglePillar(i, j, true); - Transform.pointToWorldFrame(hfPos, hfQuat, hfShape.pillarOffset, worldPillarOffset); - if (convexPos.distanceTo(worldPillarOffset) < hfShape.pillarConvex.boundingSphereRadius + convexShape.boundingSphereRadius) { - intersecting = this.convexConvex(convexShape, hfShape.pillarConvex, convexPos, worldPillarOffset, convexQuat, hfQuat, convexBody, hfBody, null, null, justTest, faceList, null); - } - - if(justTest && intersecting){ - return true; - } - } - } -}; - -var sphereHeightfield_tmp1 = new Vec3(); -var sphereHeightfield_tmp2 = new Vec3(); - -/** - * @method sphereHeightfield - */ -Narrowphase.prototype[Shape.types.SPHERE | Shape.types.HEIGHTFIELD] = -Narrowphase.prototype.sphereHeightfield = function ( - sphereShape, - hfShape, - spherePos, - hfPos, - sphereQuat, - hfQuat, - sphereBody, - hfBody, - rsi, - rsj, - justTest -){ - var data = hfShape.data, - radius = sphereShape.radius, - w = hfShape.elementSize, - worldPillarOffset = sphereHeightfield_tmp2; - - // Get sphere position to heightfield local! - var localSpherePos = sphereHeightfield_tmp1; - Transform.pointToLocalFrame(hfPos, hfQuat, spherePos, localSpherePos); - - // Get the index of the data points to test against - var iMinX = Math.floor((localSpherePos.x - radius) / w) - 1, - iMaxX = Math.ceil((localSpherePos.x + radius) / w) + 1, - iMinY = Math.floor((localSpherePos.y - radius) / w) - 1, - iMaxY = Math.ceil((localSpherePos.y + radius) / w) + 1; - - // Bail out if we are out of the terrain - if(iMaxX < 0 || iMaxY < 0 || iMinX > data.length || iMaxY > data[0].length){ - return; - } - - // Clamp index to edges - if(iMinX < 0){ iMinX = 0; } - if(iMaxX < 0){ iMaxX = 0; } - if(iMinY < 0){ iMinY = 0; } - if(iMaxY < 0){ iMaxY = 0; } - if(iMinX >= data.length){ iMinX = data.length - 1; } - if(iMaxX >= data.length){ iMaxX = data.length - 1; } - if(iMaxY >= data[0].length){ iMaxY = data[0].length - 1; } - if(iMinY >= data[0].length){ iMinY = data[0].length - 1; } - - var minMax = []; - hfShape.getRectMinMax(iMinX, iMinY, iMaxX, iMaxY, minMax); - var min = minMax[0]; - var max = minMax[1]; - - // Bail out if we're cant touch the bounding height box - if(localSpherePos.z - radius > max || localSpherePos.z + radius < min){ - return; - } - - var result = this.result; - for(var i = iMinX; i < iMaxX; i++){ - for(var j = iMinY; j < iMaxY; j++){ - - var numContactsBefore = result.length; - - var intersecting = false; - - // Lower triangle - hfShape.getConvexTrianglePillar(i, j, false); - Transform.pointToWorldFrame(hfPos, hfQuat, hfShape.pillarOffset, worldPillarOffset); - if (spherePos.distanceTo(worldPillarOffset) < hfShape.pillarConvex.boundingSphereRadius + sphereShape.boundingSphereRadius) { - intersecting = this.sphereConvex(sphereShape, hfShape.pillarConvex, spherePos, worldPillarOffset, sphereQuat, hfQuat, sphereBody, hfBody, sphereShape, hfShape, justTest); - } - - if(justTest && intersecting){ - return true; - } - - // Upper triangle - hfShape.getConvexTrianglePillar(i, j, true); - Transform.pointToWorldFrame(hfPos, hfQuat, hfShape.pillarOffset, worldPillarOffset); - if (spherePos.distanceTo(worldPillarOffset) < hfShape.pillarConvex.boundingSphereRadius + sphereShape.boundingSphereRadius) { - intersecting = this.sphereConvex(sphereShape, hfShape.pillarConvex, spherePos, worldPillarOffset, sphereQuat, hfQuat, sphereBody, hfBody, sphereShape, hfShape, justTest); - } - - if(justTest && intersecting){ - return true; - } - - var numContacts = result.length - numContactsBefore; - - if(numContacts > 2){ - return; - } - /* - // Skip all but 1 - for (var k = 0; k < numContacts - 1; k++) { - result.pop(); - } - */ - } - } -}; diff --git a/src/world/Narrowphase.ts b/src/world/Narrowphase.ts new file mode 100644 index 000000000..70f9dc41d --- /dev/null +++ b/src/world/Narrowphase.ts @@ -0,0 +1,1856 @@ +/* eslint-disable max-params */ +import { Box3, Quaternion, Vector3 } from 'feng3d'; +import { ContactEquation } from '../equations/ContactEquation'; +import { FrictionEquation } from '../equations/FrictionEquation'; +import { ContactMaterial } from '../material/ContactMaterial'; +import { Transform } from '../math/Transform'; +import { Body } from '../objects/Body'; +import { Box } from '../shapes/Box'; +import { ConvexPolyhedron } from '../shapes/ConvexPolyhedron'; +import { Heightfield } from '../shapes/Heightfield'; +import { Particle } from '../shapes/Particle'; +import { Plane } from '../shapes/Plane'; +import { Shape } from '../shapes/Shape'; +import { Sphere } from '../shapes/Sphere'; +import { Trimesh } from '../shapes/Trimesh'; +import { World } from './World'; +import feng3d = require('feng3d'); + +export class Narrowphase +{ + /** + * Internal storage of pooled contact points. + */ + contactPointPool: ContactEquation[]; + frictionEquationPool: FrictionEquation[]; + result: ContactEquation[]; + frictionResult: FrictionEquation[]; + world: World; + currentContactMaterial: ContactMaterial; + enableFrictionReduction: boolean; + + /** + * Helper class for the World. Generates ContactEquations. + * @class Narrowphase + * @constructor + * @todo Sphere-ConvexPolyhedron contacts + * @todo Contact reduction + * @todo should move methods to prototype + */ + constructor(world: World) + { + this.contactPointPool = []; + + this.frictionEquationPool = []; + + this.result = []; + this.frictionResult = []; + + this.world = world; + this.currentContactMaterial = null; + + this.enableFrictionReduction = false; + } + + /** + * Make a contact object, by using the internal pool or creating a new one. + * + * @param bi + * @param bj + * @param si + * @param sj + * @param overrideShapeA + * @param overrideShapeB + */ + createContactEquation(bi: Body, bj: Body, si: Shape, sj: Shape, overrideShapeA: Shape, overrideShapeB: Shape) + { + let c: ContactEquation; + if (this.contactPointPool.length) + { + c = this.contactPointPool.pop(); + c.bi = bi; + c.bj = bj; + } + else + { + c = new ContactEquation(bi, bj); + } + + c.enabled = bi.collisionResponse && bj.collisionResponse && si.collisionResponse && sj.collisionResponse; + + const cm = this.currentContactMaterial; + + c.restitution = cm.restitution; + + c.setSpookParams( + cm.contactEquationStiffness, + cm.contactEquationRelaxation, + this.world.dt + ); + + const matA = si.material || bi.material; + const matB = sj.material || bj.material; + if (matA && matB && matA.restitution >= 0 && matB.restitution >= 0) + { + c.restitution = matA.restitution * matB.restitution; + } + + c.si = overrideShapeA || si; + c.sj = overrideShapeB || sj; + + return c; + } + + createFrictionEquationsFromContact(contactEquation: ContactEquation, outArray: FrictionEquation[]) + { + const bodyA = contactEquation.bi; + const bodyB = contactEquation.bj; + const shapeA = contactEquation.si; + const shapeB = contactEquation.sj; + + const world = this.world; + const cm = this.currentContactMaterial; + + // If friction or restitution were specified in the material, use them + let friction = cm.friction; + const matA = shapeA.material || bodyA.material; + const matB = shapeB.material || bodyB.material; + if (matA && matB && matA.friction >= 0 && matB.friction >= 0) + { + friction = matA.friction * matB.friction; + } + + if (friction > 0) + { + // Create 2 tangent equations + const mug = friction * world.gravity.length; + let reducedMass = (bodyA.invMass + bodyB.invMass); + if (reducedMass > 0) + { + reducedMass = 1 / reducedMass; + } + const pool = this.frictionEquationPool; + const c1: FrictionEquation = pool.length ? pool.pop() : new FrictionEquation(bodyA, bodyB, mug * reducedMass); + const c2: FrictionEquation = pool.length ? pool.pop() : new FrictionEquation(bodyA, bodyB, mug * reducedMass); + + c1.bi = c2.bi = bodyA; + c1.bj = c2.bj = bodyB; + c1.minForce = c2.minForce = -mug * reducedMass; + c1.maxForce = c2.maxForce = mug * reducedMass; + + // Copy over the relative vectors + c1.ri.copy(contactEquation.ri); + c1.rj.copy(contactEquation.rj); + c2.ri.copy(contactEquation.ri); + c2.rj.copy(contactEquation.rj); + + // Construct tangents + contactEquation.ni.tangents(c1.t, c2.t); + + // Set spook params + c1.setSpookParams(cm.frictionEquationStiffness, cm.frictionEquationRelaxation, world.dt); + c2.setSpookParams(cm.frictionEquationStiffness, cm.frictionEquationRelaxation, world.dt); + + c1.enabled = c2.enabled = contactEquation.enabled; + + outArray.push(c1, c2); + + return true; + } + + return false; + } + + // Take the average N latest contact point on the plane. + createFrictionFromAverage(numContacts: number) + { + // The last contactEquation + let c = this.result[this.result.length - 1]; + + // Create the result: two "average" friction equations + if (!this.createFrictionEquationsFromContact(c, this.frictionResult) || numContacts === 1) + { + return; + } + + const f1 = this.frictionResult[this.frictionResult.length - 2]; + const f2 = this.frictionResult[this.frictionResult.length - 1]; + + averageNormal.setZero(); + averageContactPointA.setZero(); + averageContactPointB.setZero(); + + const bodyA = c.bi; + // const bodyB = c.bj; + for (let i = 0; i !== numContacts; i++) + { + c = this.result[this.result.length - 1 - i]; + if (c.bodyA !== bodyA) + { + averageNormal.addTo(c.ni, averageNormal); + averageContactPointA.addTo(c.ri, averageContactPointA); + averageContactPointB.addTo(c.rj, averageContactPointB); + } + else + { + averageNormal.subTo(c.ni, averageNormal); + averageContactPointA.addTo(c.rj, averageContactPointA); + averageContactPointB.addTo(c.ri, averageContactPointB); + } + } + + const invNumContacts = 1 / numContacts; + averageContactPointA.scaleNumberTo(invNumContacts, f1.ri); + averageContactPointB.scaleNumberTo(invNumContacts, f1.rj); + f2.ri.copy(f1.ri); // Should be the same + f2.rj.copy(f1.rj); + averageNormal.normalize(); + averageNormal.tangents(f1.t, f2.t); + // return eq; + } + + /** + * Generate all contacts between a list of body pairs + * @method getContacts + * @param {array} p1 Array of body indices + * @param {array} p2 Array of body indices + * @param {World} world + * @param {array} result Array to store generated contacts + * @param {array} oldcontacts Optional. Array of reusable contact objects + */ + getContacts(p1: Body[], p2: Body[], world: World, result: ContactEquation[], oldcontacts: ContactEquation[], frictionResult: FrictionEquation[], frictionPool: FrictionEquation[]) + { + // Save old contact objects + this.contactPointPool = oldcontacts; + this.frictionEquationPool = frictionPool; + this.result = result; + this.frictionResult = frictionResult; + + const qi = tmpQuat1; + const qj = tmpQuat2; + const xi = tmpVec1; + const xj = tmpVec2; + + for (let k = 0, N = p1.length; k !== N; k++) + { + // Get current collision bodies + const bi = p1[k]; + const bj = p2[k]; + + // Get contact material + let bodyContactMaterial: ContactMaterial = null; + if (bi.material && bj.material) + { + bodyContactMaterial = world.getContactMaterial(bi.material, bj.material) || null; + } + + const justTest = ( + ( + (bi.type & Body.KINEMATIC) && (bj.type & Body.STATIC) + ) || ( + (bi.type & Body.STATIC) && (bj.type & Body.KINEMATIC) + ) || ( + (bi.type & Body.KINEMATIC) && (bj.type & Body.KINEMATIC) + ) + ); + + for (let i = 0; i < bi.shapes.length; i++) + { + bi.quaternion.multTo(bi.shapeOrientations[i], qi); + bi.quaternion.vmult(bi.shapeOffsets[i], xi); + xi.addTo(bi.position, xi); + const si = bi.shapes[i]; + + for (let j = 0; j < bj.shapes.length; j++) + { + // Compute world transform of shapes + bj.quaternion.multTo(bj.shapeOrientations[j], qj); + bj.quaternion.vmult(bj.shapeOffsets[j], xj); + xj.addTo(bj.position, xj); + const sj = bj.shapes[j]; + + if (!((si.collisionFilterMask & sj.collisionFilterGroup) && (sj.collisionFilterMask & si.collisionFilterGroup))) + { + continue; + } + + if (xi.distance(xj) > si.boundingSphereRadius + sj.boundingSphereRadius) + { + continue; + } + + // Get collision material + let shapeContactMaterial: ContactMaterial = null; + if (si.material && sj.material) + { + shapeContactMaterial = world.getContactMaterial(si.material, sj.material) || null; + } + + this.currentContactMaterial = shapeContactMaterial || bodyContactMaterial || world.defaultContactMaterial; + + // Get contacts + const resolver = this[si.type | sj.type]; + if (resolver) + { + let retval = false; + if (si.type < sj.type) + { + retval = resolver.call(this, si, sj, xi, xj, qi, qj, bi, bj, si, sj, justTest); + } + else + { + retval = resolver.call(this, sj, si, xj, xi, qj, qi, bj, bi, si, sj, justTest); + } + + if (retval && justTest) + { + // Register overlap + world.shapeOverlapKeeper.set(si.id, sj.id); + world.bodyOverlapKeeper.set(bi.id, bj.id); + } + } + } + } + } + } + + boxBox(si: Box, sj: Box, xi: Vector3, xj: Vector3, qi: Quaternion, qj: Quaternion, bi: Body, bj: Body, rsi: Shape, rsj: Shape, justTest: boolean) + { + si.convexPolyhedronRepresentation.material = si.material; + sj.convexPolyhedronRepresentation.material = sj.material; + si.convexPolyhedronRepresentation.collisionResponse = si.collisionResponse; + sj.convexPolyhedronRepresentation.collisionResponse = sj.collisionResponse; + + return this.convexConvex(si.convexPolyhedronRepresentation, sj.convexPolyhedronRepresentation, xi, xj, qi, qj, bi, bj, si, sj, justTest); + } + + boxConvex(si: Box, sj: ConvexPolyhedron, xi: Vector3, xj: Vector3, qi: Quaternion, qj: Quaternion, bi: Body, bj: Body, rsi: Shape, rsj: Shape, justTest: boolean) + { + si.convexPolyhedronRepresentation.material = si.material; + si.convexPolyhedronRepresentation.collisionResponse = si.collisionResponse; + + return this.convexConvex(si.convexPolyhedronRepresentation, sj, xi, xj, qi, qj, bi, bj, si, sj, justTest); + } + + boxParticle(si: Box, sj: Particle, xi: Vector3, xj: Vector3, qi: Quaternion, qj: Quaternion, bi: Body, bj: Body, rsi: Shape, rsj: Shape, justTest: boolean) + { + si.convexPolyhedronRepresentation.material = si.material; + si.convexPolyhedronRepresentation.collisionResponse = si.collisionResponse; + + return this.convexParticle(si.convexPolyhedronRepresentation, sj, xi, xj, qi, qj, bi, bj, si, sj, justTest); + } + + sphereSphere(si: Sphere, sj: Sphere, xi: Vector3, xj: Vector3, qi: Quaternion, qj: Quaternion, bi: Body, bj: Body, rsi?: Shape, rsj?: Shape, justTest?: boolean) + { + if (justTest) + { + return xi.distanceSquared(xj) < Math.pow(si.radius + sj.radius, 2); + } + + // We will have only one contact in this case + const r = this.createContactEquation(bi, bj, si, sj, rsi, rsj); + + // Contact normal + xj.subTo(xi, r.ni); + r.ni.normalize(); + + // Contact point locations + r.ri.copy(r.ni); + r.rj.copy(r.ni); + r.ri.scaleNumberTo(si.radius, r.ri); + r.rj.scaleNumberTo(-sj.radius, r.rj); + + r.ri.addTo(xi, r.ri); + r.ri.subTo(bi.position, r.ri); + + r.rj.addTo(xj, r.rj); + r.rj.subTo(bj.position, r.rj); + + this.result.push(r); + + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + + /** + * @method planeTrimesh + * @param {Shape} si + * @param {Shape} sj + * @param {Vector3} xi + * @param {Vector3} xj + * @param {Quaternion} qi + * @param {Quaternion} qj + * @param {Body} bi + * @param {Body} bj + */ + planeTrimesh( + planeShape: Plane, + trimeshShape: Trimesh, + planePos: Vector3, + trimeshPos: Vector3, + planeQuat: Quaternion, + trimeshQuat: Quaternion, + planeBody: Body, + trimeshBody: Body, + rsi: Shape, + rsj: Shape, + justTest: boolean + ) + { + // Make contacts! + const v = new Vector3(); + + const normal = planeTrimeshNormal; + normal.copy(World.worldNormal); + planeQuat.vmult(normal, normal); // Turn normal according to plane + + for (let i = 0; i < trimeshShape.vertices.length / 3; i++) + { + // Get world vertex from trimesh + trimeshShape.getVertex(i, v); + + // Safe up + const v2 = new Vector3(); + v2.copy(v); + Transform.pointToWorldFrame(trimeshPos, trimeshQuat, v2, v); + + // Check plane side + const relpos = planeTrimeshRelpos; + v.subTo(planePos, relpos); + const dot = normal.dot(relpos); + + if (dot <= 0.0) + { + if (justTest) + { + return true; + } + + const r = this.createContactEquation(planeBody, trimeshBody, planeShape, trimeshShape, rsi, rsj); + + r.ni.copy(normal); // Contact normal is the plane normal + + // Get vertex position projected on plane + const projected = planeTrimeshProjected; + normal.scaleNumberTo(relpos.dot(normal), projected); + v.subTo(projected, projected); + + // ri is the projected world position minus plane position + r.ri.copy(projected); + r.ri.subTo(planeBody.position, r.ri); + + r.rj.copy(v); + r.rj.subTo(trimeshBody.position, r.rj); + + // Store result + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + } + } + + sphereTrimesh( + sphereShape: Sphere, + trimeshShape: Trimesh, + spherePos: Vector3, + trimeshPos: Vector3, + sphereQuat: Quaternion, + trimeshQuat: Quaternion, + sphereBody: Body, + trimeshBody: Body, + rsi: Shape, + rsj: Shape, + justTest: boolean + ) + { + const edgeVertexA = sphereTrimeshEdgeVertexA; + const edgeVertexB = sphereTrimeshEdgeVertexB; + const edgeVector = sphereTrimeshEdgeVector; + const edgeVectorUnit = sphereTrimeshEdgeVectorUnit; + const localSpherePos = sphereTrimeshLocalSpherePos; + const tmp = sphereTrimeshTmp; + const localSphereAABB = sphereTrimeshLocalSphereAABB; + const v2 = sphereTrimeshV2; + const relpos = sphereTrimeshRelpos; + const triangles = sphereTrimeshTriangles; + + // Convert sphere position to local in the trimesh + Transform.pointToLocalFrame(trimeshPos, trimeshQuat, spherePos, localSpherePos); + + // Get the aabb of the sphere locally in the trimesh + const sphereRadius = sphereShape.radius; + localSphereAABB.min.set( + localSpherePos.x - sphereRadius, + localSpherePos.y - sphereRadius, + localSpherePos.z - sphereRadius + ); + localSphereAABB.max.set( + localSpherePos.x + sphereRadius, + localSpherePos.y + sphereRadius, + localSpherePos.z + sphereRadius + ); + + trimeshShape.getTrianglesInAABB(localSphereAABB, triangles); + // for (let i = 0; i < trimeshShape.indices.length / 3; i++) triangles.push(i); // All + + // Vertices + const v = sphereTrimeshV; + const radiusSquared = sphereShape.radius * sphereShape.radius; + for (let i = 0; i < triangles.length; i++) + { + for (let j = 0; j < 3; j++) + { + trimeshShape.getVertex(trimeshShape.indices[triangles[i] * 3 + j], v); + + // Check vertex overlap in sphere + v.subTo(localSpherePos, relpos); + + if (relpos.lengthSquared <= radiusSquared) + { + // Safe up + v2.copy(v); + Transform.pointToWorldFrame(trimeshPos, trimeshQuat, v2, v); + + v.subTo(spherePos, relpos); + + if (justTest) + { + return true; + } + + const r = this.createContactEquation(sphereBody, trimeshBody, sphereShape, trimeshShape, rsi, rsj); + r.ni.copy(relpos); + r.ni.normalize(); + + // ri is the vector from sphere center to the sphere surface + r.ri.copy(r.ni); + r.ri.scaleNumberTo(sphereShape.radius, r.ri); + r.ri.addTo(spherePos, r.ri); + r.ri.subTo(sphereBody.position, r.ri); + + r.rj.copy(v); + r.rj.subTo(trimeshBody.position, r.rj); + + // Store result + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + } + } + + // Check all edges + for (let i = 0; i < triangles.length; i++) + { + for (let j = 0; j < 3; j++) + { + trimeshShape.getVertex(trimeshShape.indices[triangles[i] * 3 + j], edgeVertexA); + trimeshShape.getVertex(trimeshShape.indices[triangles[i] * 3 + ((j + 1) % 3)], edgeVertexB); + edgeVertexB.subTo(edgeVertexA, edgeVector); + + // Project sphere position to the edge + localSpherePos.subTo(edgeVertexB, tmp); + const positionAlongEdgeB = tmp.dot(edgeVector); + + localSpherePos.subTo(edgeVertexA, tmp); + let positionAlongEdgeA = tmp.dot(edgeVector); + + if (positionAlongEdgeA > 0 && positionAlongEdgeB < 0) + { + // Now check the orthogonal distance from edge to sphere center + localSpherePos.subTo(edgeVertexA, tmp); + + edgeVectorUnit.copy(edgeVector); + edgeVectorUnit.normalize(); + positionAlongEdgeA = tmp.dot(edgeVectorUnit); + + edgeVectorUnit.scaleNumberTo(positionAlongEdgeA, tmp); + tmp.addTo(edgeVertexA, tmp); + + // tmp is now the sphere center position projected to the edge, defined locally in the trimesh frame + const dist = tmp.distance(localSpherePos); + if (dist < sphereShape.radius) + { + if (justTest) + { + return true; + } + + const r = this.createContactEquation(sphereBody, trimeshBody, sphereShape, trimeshShape, rsi, rsj); + + tmp.subTo(localSpherePos, r.ni); + r.ni.normalize(); + r.ni.scaleNumberTo(sphereShape.radius, r.ri); + + Transform.pointToWorldFrame(trimeshPos, trimeshQuat, tmp, tmp); + tmp.subTo(trimeshBody.position, r.rj); + + Transform.vectorToWorldFrame(trimeshQuat, r.ni, r.ni); + Transform.vectorToWorldFrame(trimeshQuat, r.ri, r.ri); + + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + } + } + } + + // Triangle faces + const va = sphereTrimeshVa; + const vb = sphereTrimeshVb; + const vc = sphereTrimeshVc; + const normal = sphereTrimeshNormal; + for (let i = 0, N = triangles.length; i !== N; i++) + { + trimeshShape.getTriangleVertices(triangles[i], va, vb, vc); + trimeshShape.getNormal(triangles[i], normal); + localSpherePos.subTo(va, tmp); + let dist = tmp.dot(normal); + normal.scaleNumberTo(dist, tmp); + localSpherePos.subTo(tmp, tmp); + + // tmp is now the sphere position projected to the triangle plane + dist = tmp.distance(localSpherePos); + if (feng3d.Triangle3.containsPoint(va, vb, vc, tmp) && dist < sphereShape.radius) + { + if (justTest) + { + return true; + } + const r = this.createContactEquation(sphereBody, trimeshBody, sphereShape, trimeshShape, rsi, rsj); + + tmp.subTo(localSpherePos, r.ni); + r.ni.normalize(); + r.ni.scaleNumberTo(sphereShape.radius, r.ri); + + Transform.pointToWorldFrame(trimeshPos, trimeshQuat, tmp, tmp); + tmp.subTo(trimeshBody.position, r.rj); + + Transform.vectorToWorldFrame(trimeshQuat, r.ni, r.ni); + Transform.vectorToWorldFrame(trimeshQuat, r.ri, r.ri); + + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + } + + triangles.length = 0; + } + + spherePlane(si: Sphere, sj: Plane, xi: Vector3, xj: Vector3, qi: Quaternion, qj: Quaternion, bi: Body, bj: Body, rsi: Shape, rsj: Shape, justTest: boolean) + { + // We will have one contact in this case + const r = this.createContactEquation(bi, bj, si, sj, rsi, rsj); + + // Contact normal + r.ni.copy(World.worldNormal); + qj.vmult(r.ni, r.ni); + r.ni.negateTo(r.ni); // body i is the sphere, flip normal + r.ni.normalize(); // Needed? + + // Vector from sphere center to contact point + r.ni.scaleNumberTo(si.radius, r.ri); + + // Project down sphere on plane + xi.subTo(xj, pointOnPlaneToSphere); + r.ni.scaleNumberTo(r.ni.dot(pointOnPlaneToSphere), planeToSphereOrtho); + pointOnPlaneToSphere.subTo(planeToSphereOrtho, r.rj); // The sphere position projected to plane + + if (-pointOnPlaneToSphere.dot(r.ni) <= si.radius) + { + if (justTest) + { + return true; + } + + // Make it relative to the body + const ri = r.ri; + const rj = r.rj; + ri.addTo(xi, ri); + ri.subTo(bi.position, ri); + rj.addTo(xj, rj); + rj.subTo(bj.position, rj); + + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + } + + sphereBox(si: Sphere, sj: Box, xi: Vector3, xj: Vector3, qi: Quaternion, qj: Quaternion, bi: Body, bj: Body, rsi: Shape, rsj: Shape, justTest: boolean) + { + // we refer to the box as body j + const sides = sphereBoxSides; + xi.subTo(xj, boxToSphere); + sj.getSideNormals(sides, qj); + const R = si.radius; + // const penetrating_sides = []; + + // Check side (plane) intersections + let found = false; + + // Store the resulting side penetration info + const sideNs = sphereBoxSideNs; + const sideNs1 = sphereBoxSideNs1; + const sideNs2 = sphereBoxSideNs2; + let sideH = null; + let sidePenetrations = 0; + let sideDot1 = 0; + let sideDot2 = 0; + let sideDistance = null; + // eslint-disable-next-line no-unmodified-loop-condition + for (let idx = 0, nsides = sides.length; idx !== nsides && found === false; idx++) + { + // Get the plane side normal (ns) + const ns = sphereBoxNs; + ns.copy(sides[idx]); + + const h = ns.length; + ns.normalize(); + + // The normal/distance dot product tells which side of the plane we are + const dot = boxToSphere.dot(ns); + + if (dot < h + R && dot > 0) + { + // Intersects plane. Now check the other two dimensions + const ns1 = sphereBoxNs1; + const ns2 = sphereBoxNs2; + ns1.copy(sides[(idx + 1) % 3]); + ns2.copy(sides[(idx + 2) % 3]); + const h1 = ns1.length; + const h2 = ns2.length; + ns1.normalize(); + ns2.normalize(); + const dot1 = boxToSphere.dot(ns1); + const dot2 = boxToSphere.dot(ns2); + if (dot1 < h1 && dot1 > -h1 && dot2 < h2 && dot2 > -h2) + { + const dist = Math.abs(dot - h - R); + if (sideDistance === null || dist < sideDistance) + { + sideDistance = dist; + sideDot1 = dot1; + sideDot2 = dot2; + sideH = h; + sideNs.copy(ns); + sideNs1.copy(ns1); + sideNs2.copy(ns2); + sidePenetrations++; + + if (justTest) + { + return true; + } + } + } + } + } + if (sidePenetrations) + { + found = true; + const r = this.createContactEquation(bi, bj, si, sj, rsi, rsj); + sideNs.scaleNumberTo(-R, r.ri); // Sphere r + r.ni.copy(sideNs); + r.ni.negateTo(r.ni); // Normal should be out of sphere + sideNs.scaleNumberTo(sideH, sideNs); + sideNs1.scaleNumberTo(sideDot1, sideNs1); + sideNs.addTo(sideNs1, sideNs); + sideNs2.scaleNumberTo(sideDot2, sideNs2); + sideNs.addTo(sideNs2, r.rj); + + // Make relative to bodies + r.ri.addTo(xi, r.ri); + r.ri.subTo(bi.position, r.ri); + r.rj.addTo(xj, r.rj); + r.rj.subTo(bj.position, r.rj); + + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + + // Check corners + let rj = new Vector3(); + const sphereToCorner = sphereBoxSphereToCorner; + for (let j = 0; j !== 2 && !found; j++) + { + for (let k = 0; k !== 2 && !found; k++) + { + for (let l = 0; l !== 2 && !found; l++) + { + rj.set(0, 0, 0); + if (j) + { + rj.addTo(sides[0], rj); + } + else + { + rj.subTo(sides[0], rj); + } + if (k) + { + rj.addTo(sides[1], rj); + } + else + { + rj.subTo(sides[1], rj); + } + if (l) + { + rj.addTo(sides[2], rj); + } + else + { + rj.subTo(sides[2], rj); + } + + // World position of corner + xj.addTo(rj, sphereToCorner); + sphereToCorner.subTo(xi, sphereToCorner); + + if (sphereToCorner.lengthSquared < R * R) + { + if (justTest) + { + return true; + } + found = true; + const r = this.createContactEquation(bi, bj, si, sj, rsi, rsj); + r.ri.copy(sphereToCorner); + r.ri.normalize(); + r.ni.copy(r.ri); + r.ri.scaleNumberTo(R, r.ri); + r.rj.copy(rj); + + // Make relative to bodies + r.ri.addTo(xi, r.ri); + r.ri.subTo(bi.position, r.ri); + r.rj.addTo(xj, r.rj); + r.rj.subTo(bj.position, r.rj); + + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + } + } + } + rj = null; + + // Check edges + const edgeTangent = new Vector3(); + const edgeCenter = new Vector3(); + const r = new Vector3(); // r = edge center to sphere center + const orthogonal = new Vector3(); + const dist1: Vector3 = new Vector3(); + const Nsides = sides.length; + for (let j = 0; j !== Nsides && !found; j++) + { + for (let k = 0; k !== Nsides && !found; k++) + { + if (j % 3 !== k % 3) + { + // Get edge tangent + sides[k].crossTo(sides[j], edgeTangent); + edgeTangent.normalize(); + sides[j].addTo(sides[k], edgeCenter); + r.copy(xi); + r.subTo(edgeCenter, r); + r.subTo(xj, r); + const orthonorm = r.dot(edgeTangent); // distance from edge center to sphere center in the tangent direction + edgeTangent.scaleNumberTo(orthonorm, orthogonal); // Vector from edge center to sphere center in the tangent direction + + // Find the third side orthogonal to this one + let l = 0; + while (l === j % 3 || l === k % 3) + { + l++; + } + + // vec from edge center to sphere projected to the plane orthogonal to the edge tangent + dist1.copy(xi); + dist1.subTo(orthogonal, dist1); + dist1.subTo(edgeCenter, dist1); + dist1.subTo(xj, dist1); + + // Distances in tangent direction and distance in the plane orthogonal to it + const tdist = Math.abs(orthonorm); + const ndist = dist1.length; + + if (tdist < sides[l].length && ndist < R) + { + if (justTest) + { + return true; + } + found = true; + const res = this.createContactEquation(bi, bj, si, sj, rsi, rsj); + edgeCenter.addTo(orthogonal, res.rj); // box rj + res.rj.copy(res.rj); + dist1.negateTo(res.ni); + res.ni.normalize(); + + res.ri.copy(res.rj); + res.ri.addTo(xj, res.ri); + res.ri.subTo(xi, res.ri); + res.ri.normalize(); + res.ri.scaleNumberTo(R, res.ri); + + // Make relative to bodies + res.ri.addTo(xi, res.ri); + res.ri.subTo(bi.position, res.ri); + res.rj.addTo(xj, res.rj); + res.rj.subTo(bj.position, res.rj); + + this.result.push(res); + this.createFrictionEquationsFromContact(res, this.frictionResult); + } + } + } + } + } + + sphereConvex(si: Sphere, sj: ConvexPolyhedron, xi: Vector3, xj: Vector3, qi: Quaternion, qj: Quaternion, bi: Body, bj: Body, rsi: Shape, rsj: Shape, justTest: boolean) + { + xi.subTo(xj, convexToSphere); + const normals = sj.faceNormals; + const faces = sj.faces; + const verts = sj.vertices; + const R = si.radius; + // const penetrating_sides = []; + + // if(convex_to_sphere.lengthSquared > si.boundingSphereRadius + sj.boundingSphereRadius){ + // return; + // } + let found = false; + + // Check corners + for (let i = 0; i !== verts.length; i++) + { + const v = verts[i]; + + // World position of corner + const worldCorner = sphereConvexWorldCorner; + qj.vmult(v, worldCorner); + xj.addTo(worldCorner, worldCorner); + const sphereToCorner = sphereConvexSphereToCorner; + worldCorner.subTo(xi, sphereToCorner); + if (sphereToCorner.lengthSquared < R * R) + { + if (justTest) + { + return true; + } + found = true; + const r = this.createContactEquation(bi, bj, si, sj, rsi, rsj); + r.ri.copy(sphereToCorner); + r.ri.normalize(); + r.ni.copy(r.ri); + r.ri.scaleNumberTo(R, r.ri); + worldCorner.subTo(xj, r.rj); + + // Should be relative to the body. + r.ri.addTo(xi, r.ri); + r.ri.subTo(bi.position, r.ri); + + // Should be relative to the body. + r.rj.addTo(xj, r.rj); + r.rj.subTo(bj.position, r.rj); + + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + + return; + } + } + + // Check side (plane) intersections + for (let i = 0, nfaces = faces.length; i !== nfaces && found === false; i++) + { + const normal = normals[i]; + const face = faces[i]; + + // Get world-transformed normal of the face + const worldNormal = sphereConvexWorldNormal; + qj.vmult(normal, worldNormal); + + // Get a world vertex from the face + const worldPoint = sphereConvexWorldPoint; + qj.vmult(verts[face[0]], worldPoint); + worldPoint.addTo(xj, worldPoint); + + // Get a point on the sphere, closest to the face normal + const worldSpherePointClosestToPlane = sphereConvexWorldSpherePointClosestToPlane; + worldNormal.scaleNumberTo(-R, worldSpherePointClosestToPlane); + xi.addTo(worldSpherePointClosestToPlane, worldSpherePointClosestToPlane); + + // Vector from a face point to the closest point on the sphere + const penetrationVec = sphereConvexPenetrationVec; + worldSpherePointClosestToPlane.subTo(worldPoint, penetrationVec); + + // The penetration. Negative value means overlap. + const penetration = penetrationVec.dot(worldNormal); + + const worldPointToSphere = sphereConvexSphereToWorldPoint; + xi.subTo(worldPoint, worldPointToSphere); + + if (penetration < 0 && worldPointToSphere.dot(worldNormal) > 0) + { + // Intersects plane. Now check if the sphere is inside the face polygon + const faceVerts = []; // Face vertices, in world coords + for (let j = 0, Nverts = face.length; j !== Nverts; j++) + { + const worldVertex = new Vector3(); + qj.vmult(verts[face[j]], worldVertex); + xj.addTo(worldVertex, worldVertex); + faceVerts.push(worldVertex); + } + + if (pointInPolygon(faceVerts, worldNormal, xi)) + { // Is the sphere center in the face polygon? + if (justTest) + { + return true; + } + found = true; + const r = this.createContactEquation(bi, bj, si, sj, rsi, rsj); + + worldNormal.scaleNumberTo(-R, r.ri); // Contact offset, from sphere center to contact + worldNormal.negateTo(r.ni); // Normal pointing out of sphere + + const penetrationVec2 = new Vector3(); + worldNormal.scaleNumberTo(-penetration, penetrationVec2); + const penetrationSpherePoint = new Vector3(); + worldNormal.scaleNumberTo(-R, penetrationSpherePoint); + + // xi.subTo(xj).addTo(penetrationSpherePoint).addTo(penetrationVec2 , r.rj); + xi.subTo(xj, r.rj); + r.rj.addTo(penetrationSpherePoint, r.rj); + r.rj.addTo(penetrationVec2, r.rj); + + // Should be relative to the body. + r.rj.addTo(xj, r.rj); + r.rj.subTo(bj.position, r.rj); + + // Should be relative to the body. + r.ri.addTo(xi, r.ri); + r.ri.subTo(bi.position, r.ri); + + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + + return; // We only expect *one* face contact + } + // Edge? + for (let j = 0; j !== face.length; j++) + { + // Get two world transformed vertices + const v1 = new Vector3(); + const v2 = new Vector3(); + qj.vmult(verts[face[(j + 1) % face.length]], v1); + qj.vmult(verts[face[(j + 2) % face.length]], v2); + xj.addTo(v1, v1); + xj.addTo(v2, v2); + + // Construct edge vector + const edge = sphereConvexEdge; + v2.subTo(v1, edge); + + // Construct the same vector, but normalized + const edgeUnit = sphereConvexEdgeUnit; + edge.unit(edgeUnit); + + // p is xi projected onto the edge + const p = new Vector3(); + const v1ToXi = new Vector3(); + xi.subTo(v1, v1ToXi); + const dot = v1ToXi.dot(edgeUnit); + edgeUnit.scaleNumberTo(dot, p); + p.addTo(v1, p); + + // Compute a vector from p to the center of the sphere + const xiToP = new Vector3(); + p.subTo(xi, xiToP); + + // Collision if the edge-sphere distance is less than the radius + // AND if p is in between v1 and v2 + if (dot > 0 && dot * dot < edge.lengthSquared && xiToP.lengthSquared < R * R) + { // Collision if the edge-sphere distance is less than the radius + // Edge contact! + if (justTest) + { + return true; + } + const r = this.createContactEquation(bi, bj, si, sj, rsi, rsj); + p.subTo(xj, r.rj); + + p.subTo(xi, r.ni); + r.ni.normalize(); + + r.ni.scaleNumberTo(R, r.ri); + + // Should be relative to the body. + r.rj.addTo(xj, r.rj); + r.rj.subTo(bj.position, r.rj); + + // Should be relative to the body. + r.ri.addTo(xi, r.ri); + r.ri.subTo(bi.position, r.ri); + + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + + return; + } + } + } + } + } + + planeBox(si: Plane, sj: Box, xi: Vector3, xj: Vector3, qi: Quaternion, qj: Quaternion, bi: Body, bj: Body, rsi: Shape, rsj: Shape, justTest: boolean) + { + sj.convexPolyhedronRepresentation.material = sj.material; + sj.convexPolyhedronRepresentation.collisionResponse = sj.collisionResponse; + sj.convexPolyhedronRepresentation.id = sj.id; + + return this.planeConvex(si, sj.convexPolyhedronRepresentation, xi, xj, qi, qj, bi, bj, si, sj, justTest); + } + + planeConvex( + planeShape: Plane, + convexShape: ConvexPolyhedron, + planePosition: Vector3, + convexPosition: Vector3, + planeQuat: Quaternion, + convexQuat: Quaternion, + planeBody: Body, + convexBody: Body, + si: Shape, + sj: Shape, + justTest: boolean + ) + { + // Simply return the points behind the plane. + const worldVertex = planeConvexV; + const worldNormal = planeConvexNormal; + worldNormal.copy(World.worldNormal); + planeQuat.vmult(worldNormal, worldNormal); // Turn normal according to plane orientation + + let numContacts = 0; + const relpos = planeConvexRelpos; + for (let i = 0; i !== convexShape.vertices.length; i++) + { + // Get world convex vertex + worldVertex.copy(convexShape.vertices[i]); + convexQuat.vmult(worldVertex, worldVertex); + convexPosition.addTo(worldVertex, worldVertex); + worldVertex.subTo(planePosition, relpos); + + const dot = worldNormal.dot(relpos); + if (dot <= 0.0) + { + if (justTest) + { + return true; + } + + const r = this.createContactEquation(planeBody, convexBody, planeShape, convexShape, si, sj); + + // Get vertex position projected on plane + const projected = planeConvexProjected; + worldNormal.scaleNumberTo(worldNormal.dot(relpos), projected); + worldVertex.subTo(projected, projected); + projected.subTo(planePosition, r.ri); // From plane to vertex projected on plane + + r.ni.copy(worldNormal); // Contact normal is the plane normal out from plane + + // rj is now just the vector from the convex center to the vertex + worldVertex.subTo(convexPosition, r.rj); + + // Make it relative to the body + r.ri.addTo(planePosition, r.ri); + r.ri.subTo(planeBody.position, r.ri); + r.rj.addTo(convexPosition, r.rj); + r.rj.subTo(convexBody.position, r.rj); + + this.result.push(r); + numContacts++; + if (!this.enableFrictionReduction) + { + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + } + } + + if (this.enableFrictionReduction && numContacts) + { + this.createFrictionFromAverage(numContacts); + } + } + + convexConvex(si: ConvexPolyhedron, sj: ConvexPolyhedron, xi: Vector3, xj: Vector3, qi: Quaternion, qj: Quaternion, bi: Body, bj: Body, rsi: Shape, rsj: Shape, justTest: boolean, faceListA?: number[], faceListB?: number[]) + { + const sepAxis = convexConvexSepAxis; + + if (xi.distance(xj) > si.boundingSphereRadius + sj.boundingSphereRadius) + { + return; + } + + if (si.findSeparatingAxis(sj, xi, qi, xj, qj, sepAxis, faceListA, faceListB)) + { + const res: { + point: Vector3; + normal: Vector3; + depth: number; + }[] = []; + const q = convexConvexQ; + si.clipAgainstHull(xi, qi, sj, xj, qj, sepAxis, -100, 100, res); + let numContacts = 0; + for (let j = 0; j !== res.length; j++) + { + if (justTest) + { + return true; + } + const r = this.createContactEquation(bi, bj, si, sj, rsi, rsj); + const ri = r.ri; + const rj = r.rj; + sepAxis.negateTo(r.ni); + res[j].normal.negateTo(q); + q.scaleNumberTo(res[j].depth, q); + res[j].point.addTo(q, ri); + rj.copy(res[j].point); + + // Contact points are in world coordinates. Transform back to relative + ri.subTo(xi, ri); + rj.subTo(xj, rj); + + // Make relative to bodies + ri.addTo(xi, ri); + ri.subTo(bi.position, ri); + rj.addTo(xj, rj); + rj.subTo(bj.position, rj); + + this.result.push(r); + numContacts++; + if (!this.enableFrictionReduction) + { + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + } + if (this.enableFrictionReduction && numContacts) + { + this.createFrictionFromAverage(numContacts); + } + } + } + + /** + * @method convexTrimesh + * @param {Array} result + * @param {Shape} si + * @param {Shape} sj + * @param {Vector3} xi + * @param {Vector3} xj + * @param {Quaternion} qi + * @param {Quaternion} qj + * @param {Body} bi + * @param {Body} bj + */ + // Narrowphase.prototype[Shape.types.CONVEXPOLYHEDRON | Shape.types.TRIMESH] = + // Narrowphase.prototype.convexTrimesh = function(si,sj,xi,xj,qi,qj,bi,bj,rsi,rsj,faceListA,faceListB){ + // let sepAxis = convexConvex_sepAxis; + + // if(xi.distance(xj) > si.boundingSphereRadius + sj.boundingSphereRadius){ + // return; + // } + + // // Construct a temp hull for each triangle + // let hullB = new ConvexPolyhedron(); + + // hullB.faces = [[0,1,2]]; + // let va = new Vector3(); + // let vb = new Vector3(); + // let vc = new Vector3(); + // hullB.vertices = [ + // va, + // vb, + // vc + // ]; + + // for (let i = 0; i < sj.indices.length / 3; i++) { + + // let triangleNormal = new Vector3(); + // sj.getNormal(i, triangleNormal); + // hullB.faceNormals = [triangleNormal]; + + // sj.getTriangleVertices(i, va, vb, vc); + + // let d = si.testSepAxis(triangleNormal, hullB, xi, qi, xj, qj); + // if(!d){ + // triangleNormal.scaleNumberTo(-1, triangleNormal); + // d = si.testSepAxis(triangleNormal, hullB, xi, qi, xj, qj); + + // if(!d){ + // continue; + // } + // } + + // let res = []; + // let q = convexConvex_q; + // si.clipAgainstHull(xi,qi,hullB,xj,qj,triangleNormal,-100,100,res); + // for(let j = 0; j !== res.length; j++){ + // let r = this.createContactEquation(bi,bj,si,sj,rsi,rsj), + // ri = r.ri, + // rj = r.rj; + // r.ni.copy(triangleNormal); + // r.ni.negateTo(r.ni); + // res[j].normal.negateTo(q); + // q.multTo(res[j].depth, q); + // res[j].point.addTo(q, ri); + // rj.copy(res[j].point); + + // // Contact points are in world coordinates. Transform back to relative + // ri.subTo(xi,ri); + // rj.subTo(xj,rj); + + // // Make relative to bodies + // ri.addTo(xi, ri); + // ri.subTo(bi.position, ri); + // rj.addTo(xj, rj); + // rj.subTo(bj.position, rj); + + // result.push(r); + // } + // } + // }; + + planeParticle(sj: Plane, si: Particle, xj: Vector3, xi: Vector3, qj: Quaternion, qi: Quaternion, bj: Body, bi: Body, rsi: Shape, rsj: Shape, justTest: boolean) + { + const normal = particlePlaneNormal; + normal.copy(World.worldNormal); + bj.quaternion.vmult(normal, normal); // Turn normal according to plane orientation + const relpos = particlePlaneRelpos; + xi.subTo(bj.position, relpos); + const dot = normal.dot(relpos); + if (dot <= 0.0) + { + if (justTest) + { + return true; + } + + const r = this.createContactEquation(bi, bj, si, sj, rsi, rsj); + r.ni.copy(normal); // Contact normal is the plane normal + r.ni.negateTo(r.ni); + r.ri.set(0, 0, 0); // Center of particle + + // Get particle position projected on plane + const projected = particlePlaneProjected; + normal.scaleNumberTo(normal.dot(xi), projected); + xi.subTo(projected, projected); + // projected.addTo(bj.position,projected); + + // rj is now the projected world position minus plane position + r.rj.copy(projected); + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + } + + sphereParticle(sj: Sphere, si: Particle, xj: Vector3, xi: Vector3, qj: Quaternion, qi: Quaternion, bj: Body, bi: Body, rsi: Shape, rsj: Shape, justTest: boolean) + { + // The normal is the unit vector from sphere center to particle center + const normal = particleSphereNormal; + normal.copy(World.worldNormal); + xi.subTo(xj, normal); + const lengthSquared = normal.lengthSquared; + + if (lengthSquared <= sj.radius * sj.radius) + { + if (justTest) + { + return true; + } + const r = this.createContactEquation(bi, bj, si, sj, rsi, rsj); + normal.normalize(); + r.rj.copy(normal); + r.rj.scaleNumberTo(sj.radius, r.rj); + r.ni.copy(normal); // Contact normal + r.ni.negateTo(r.ni); + r.ri.set(0, 0, 0); // Center of particle + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + } + + convexParticle(sj: ConvexPolyhedron, si: Particle, xj: Vector3, xi: Vector3, qj: Quaternion, qi: Quaternion, bj: Body, bi: Body, rsi: Shape, rsj: Shape, justTest: boolean) + { + let penetratedFaceIndex = -1; + const penetratedFaceNormal = convexParticlePenetratedFaceNormal; + const worldPenetrationVec = convexParticleWorldPenetrationVec; + let minPenetration = null; + let numDetectedFaces = 0; + + // Convert particle position xi to local coords in the convex + const local = convexParticleLocal; + local.copy(xi); + local.subTo(xj, local); // Convert position to relative the convex origin + qj.inverseTo(cqj); + cqj.vmult(local, local); + + if (sj.pointIsInside(local)) + { + if (sj.worldVerticesNeedsUpdate) + { + sj.computeWorldVertices(xj, qj); + } + if (sj.worldFaceNormalsNeedsUpdate) + { + sj.computeWorldFaceNormals(qj); + } + + // For each world polygon in the polyhedra + for (let i = 0, nfaces = sj.faces.length; i !== nfaces; i++) + { + // Construct world face vertices + const verts = [sj.worldVertices[sj.faces[i][0]]]; + const normal = sj.worldFaceNormals[i]; + + // Check how much the particle penetrates the polygon plane. + xi.subTo(verts[0], convexParticleVertexToParticle); + const penetration = -normal.dot(convexParticleVertexToParticle); + if (minPenetration === null || Math.abs(penetration) < Math.abs(minPenetration)) + { + if (justTest) + { + return true; + } + + minPenetration = penetration; + penetratedFaceIndex = i; + penetratedFaceNormal.copy(normal); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + numDetectedFaces++; + } + } + + if (penetratedFaceIndex !== -1) + { + // Setup contact + const r = this.createContactEquation(bi, bj, si, sj, rsi, rsj); + penetratedFaceNormal.scaleNumberTo(minPenetration, worldPenetrationVec); + + // rj is the particle position projected to the face + worldPenetrationVec.addTo(xi, worldPenetrationVec); + worldPenetrationVec.subTo(xj, worldPenetrationVec); + r.rj.copy(worldPenetrationVec); + // let projectedToFace = xi.subTo(xj).addTo(worldPenetrationVec); + // projectedToFace.copy(r.rj); + + // qj.vmult(r.rj,r.rj); + penetratedFaceNormal.negateTo(r.ni); // Contact normal + r.ri.set(0, 0, 0); // Center of particle + + const ri = r.ri; + const rj = r.rj; + + // Make relative to bodies + ri.addTo(xi, ri); + ri.subTo(bi.position, ri); + rj.addTo(xj, rj); + rj.subTo(bj.position, rj); + + this.result.push(r); + this.createFrictionEquationsFromContact(r, this.frictionResult); + } + else + { + console.warn('Point found inside convex, but did not find penetrating face!'); + } + } + } + + boxHeightfield(si: Box, sj: Heightfield, xi: Vector3, xj: Vector3, qi: Quaternion, qj: Quaternion, bi: Body, bj: Body, rsi: Shape, rsj: Shape, justTest: boolean) + { + si.convexPolyhedronRepresentation.material = si.material; + si.convexPolyhedronRepresentation.collisionResponse = si.collisionResponse; + + return this.convexHeightfield(si.convexPolyhedronRepresentation, sj, xi, xj, qi, qj, bi, bj, si, sj, justTest); + } + + convexHeightfield( + convexShape: ConvexPolyhedron, + hfShape: Heightfield, + convexPos: Vector3, + hfPos: Vector3, + convexQuat: Quaternion, + hfQuat: Quaternion, + convexBody: Body, + hfBody: Body, + rsi: Shape, + rsj: Shape, + justTest: boolean + ) + { + const data = hfShape.data; + const w = hfShape.elementSize; + const radius = convexShape.boundingSphereRadius; + const worldPillarOffset = convexHeightfieldTmp2; + const faceList = convexHeightfieldFaceList; + + // Get sphere position to heightfield local! + const localConvexPos = convexHeightfieldTmp1; + Transform.pointToLocalFrame(hfPos, hfQuat, convexPos, localConvexPos); + + // Get the index of the data points to test against + let iMinX = Math.floor((localConvexPos.x - radius) / w) - 1; + let iMaxX = Math.ceil((localConvexPos.x + radius) / w) + 1; + let iMinY = Math.floor((localConvexPos.y - radius) / w) - 1; + let iMaxY = Math.ceil((localConvexPos.y + radius) / w) + 1; + + // Bail out if we are out of the terrain + if (iMaxX < 0 || iMaxY < 0 || iMinX > data.length || iMinY > data[0].length) + { + return; + } + + // Clamp index to edges + if (iMinX < 0) { iMinX = 0; } + if (iMaxX < 0) { iMaxX = 0; } + if (iMinY < 0) { iMinY = 0; } + if (iMaxY < 0) { iMaxY = 0; } + if (iMinX >= data.length) { iMinX = data.length - 1; } + if (iMaxX >= data.length) { iMaxX = data.length - 1; } + if (iMaxY >= data[0].length) { iMaxY = data[0].length - 1; } + if (iMinY >= data[0].length) { iMinY = data[0].length - 1; } + + const minMax = []; + hfShape.getRectMinMax(iMinX, iMinY, iMaxX, iMaxY, minMax); + const min = minMax[0]; + const max = minMax[1]; + + // Bail out if we're cant touch the bounding height box + if (localConvexPos.z - radius > max || localConvexPos.z + radius < min) + { + return; + } + + for (let i = iMinX; i < iMaxX; i++) + { + for (let j = iMinY; j < iMaxY; j++) + { + let intersecting = false; + + // Lower triangle + hfShape.getConvexTrianglePillar(i, j, false); + Transform.pointToWorldFrame(hfPos, hfQuat, hfShape.pillarOffset, worldPillarOffset); + if (convexPos.distance(worldPillarOffset) < hfShape.pillarConvex.boundingSphereRadius + convexShape.boundingSphereRadius) + { + intersecting = this.convexConvex(convexShape, hfShape.pillarConvex, convexPos, worldPillarOffset, convexQuat, hfQuat, convexBody, hfBody, null, null, justTest, faceList, null); + } + + if (justTest && intersecting) + { + return true; + } + + // Upper triangle + hfShape.getConvexTrianglePillar(i, j, true); + Transform.pointToWorldFrame(hfPos, hfQuat, hfShape.pillarOffset, worldPillarOffset); + if (convexPos.distance(worldPillarOffset) < hfShape.pillarConvex.boundingSphereRadius + convexShape.boundingSphereRadius) + { + intersecting = this.convexConvex(convexShape, hfShape.pillarConvex, convexPos, worldPillarOffset, convexQuat, hfQuat, convexBody, hfBody, null, null, justTest, faceList, null); + } + + if (justTest && intersecting) + { + return true; + } + } + } + } + + sphereHeightfield( + sphereShape: Sphere, + hfShape: Heightfield, + spherePos: Vector3, + hfPos: Vector3, + sphereQuat: Quaternion, + hfQuat: Quaternion, + sphereBody: Body, + hfBody: Body, + rsi?: Shape, + rsj?: Shape, + justTest?: boolean + ) + { + const data = hfShape.data; + const radius = sphereShape.radius; + const w = hfShape.elementSize; + const worldPillarOffset = sphereHeightfieldTmp2; + + // Get sphere position to heightfield local! + const localSpherePos = sphereHeightfieldTmp1; + Transform.pointToLocalFrame(hfPos, hfQuat, spherePos, localSpherePos); + + // Get the index of the data points to test against + let iMinX = Math.floor((localSpherePos.x - radius) / w) - 1; + let iMaxX = Math.ceil((localSpherePos.x + radius) / w) + 1; + let iMinY = Math.floor((localSpherePos.y - radius) / w) - 1; + let iMaxY = Math.ceil((localSpherePos.y + radius) / w) + 1; + + // Bail out if we are out of the terrain + if (iMaxX < 0 || iMaxY < 0 || iMinX > data.length || iMaxY > data[0].length) + { + return; + } + + // Clamp index to edges + if (iMinX < 0) { iMinX = 0; } + if (iMaxX < 0) { iMaxX = 0; } + if (iMinY < 0) { iMinY = 0; } + if (iMaxY < 0) { iMaxY = 0; } + if (iMinX >= data.length) { iMinX = data.length - 1; } + if (iMaxX >= data.length) { iMaxX = data.length - 1; } + if (iMaxY >= data[0].length) { iMaxY = data[0].length - 1; } + if (iMinY >= data[0].length) { iMinY = data[0].length - 1; } + + const minMax = []; + hfShape.getRectMinMax(iMinX, iMinY, iMaxX, iMaxY, minMax); + const min = minMax[0]; + const max = minMax[1]; + + // Bail out if we're cant touch the bounding height box + if (localSpherePos.z - radius > max || localSpherePos.z + radius < min) + { + return; + } + + const result = this.result; + for (let i = iMinX; i < iMaxX; i++) + { + for (let j = iMinY; j < iMaxY; j++) + { + const numContactsBefore = result.length; + + let intersecting = false; + + // Lower triangle + hfShape.getConvexTrianglePillar(i, j, false); + Transform.pointToWorldFrame(hfPos, hfQuat, hfShape.pillarOffset, worldPillarOffset); + if (spherePos.distance(worldPillarOffset) < hfShape.pillarConvex.boundingSphereRadius + sphereShape.boundingSphereRadius) + { + intersecting = this.sphereConvex(sphereShape, hfShape.pillarConvex, spherePos, worldPillarOffset, sphereQuat, hfQuat, sphereBody, hfBody, sphereShape, hfShape, justTest); + } + + if (justTest && intersecting) + { + return true; + } + + // Upper triangle + hfShape.getConvexTrianglePillar(i, j, true); + Transform.pointToWorldFrame(hfPos, hfQuat, hfShape.pillarOffset, worldPillarOffset); + if (spherePos.distance(worldPillarOffset) < hfShape.pillarConvex.boundingSphereRadius + sphereShape.boundingSphereRadius) + { + intersecting = this.sphereConvex(sphereShape, hfShape.pillarConvex, spherePos, worldPillarOffset, sphereQuat, hfQuat, sphereBody, hfBody, sphereShape, hfShape, justTest); + } + + if (justTest && intersecting) + { + return true; + } + + const numContacts = result.length - numContactsBefore; + + if (numContacts > 2) + { + return; + } + /* + // Skip all but 1 + for (let k = 0; k < numContacts - 1; k++) { + result.pop(); + } + */ + } + } + } +} + +const averageNormal = new Vector3(); +const averageContactPointA = new Vector3(); +const averageContactPointB = new Vector3(); + +const tmpVec1 = new Vector3(); +const tmpVec2 = new Vector3(); +const tmpQuat1 = new Quaternion(); +const tmpQuat2 = new Quaternion(); + +let numWarnings = 0; +const maxWarnings = 10; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function warn(msg) +{ + if (numWarnings > maxWarnings) + { + return; + } + + numWarnings++; + + console.warn(msg); +} + +const planeTrimeshNormal = new Vector3(); +const planeTrimeshRelpos = new Vector3(); +const planeTrimeshProjected = new Vector3(); + +const sphereTrimeshNormal = new Vector3(); +const sphereTrimeshRelpos = new Vector3(); +// const sphereTrimesh_projected = new Vector3(); +const sphereTrimeshV = new Vector3(); +const sphereTrimeshV2 = new Vector3(); +const sphereTrimeshEdgeVertexA = new Vector3(); +const sphereTrimeshEdgeVertexB = new Vector3(); +const sphereTrimeshEdgeVector = new Vector3(); +const sphereTrimeshEdgeVectorUnit = new Vector3(); +const sphereTrimeshLocalSpherePos = new Vector3(); +const sphereTrimeshTmp = new Vector3(); +const sphereTrimeshVa = new Vector3(); +const sphereTrimeshVb = new Vector3(); +const sphereTrimeshVc = new Vector3(); +const sphereTrimeshLocalSphereAABB = new Box3(); +const sphereTrimeshTriangles = []; + +const pointOnPlaneToSphere = new Vector3(); +const planeToSphereOrtho = new Vector3(); + +// See http://bulletphysics.com/Bullet/BulletFull/SphereTriangleDetector_8cpp_source.html +const pointInPolygonEdge = new Vector3(); +const pointInPolygonEdgeXNormal = new Vector3(); +const pointInPolygonVtp = new Vector3(); +function pointInPolygon(verts: Vector3[], normal: Vector3, p: Vector3) +{ + let positiveResult = null; + const N = verts.length; + for (let i = 0; i !== N; i++) + { + const v = verts[i]; + + // Get edge to the next vertex + const edge = pointInPolygonEdge; + verts[(i + 1) % (N)].subTo(v, edge); + + // Get cross product between polygon normal and the edge + const edgeXNormal = pointInPolygonEdgeXNormal; + // let edge_x_normal = new Vector3(); + edge.crossTo(normal, edgeXNormal); + + // Get vector between point and current vertex + const vertexToP = pointInPolygonVtp; + p.subTo(v, vertexToP); + + // This dot product determines which side of the edge the point is + const r = edgeXNormal.dot(vertexToP); + + // If all such dot products have same sign, we are inside the polygon. + if (positiveResult === null || (r > 0 && positiveResult === true) || (r <= 0 && positiveResult === false)) + { + if (positiveResult === null) + { + positiveResult = r > 0; + } + continue; + } + else + { + return false; // Encountered some other sign. Exit. + } + } + + // If we got here, all dot products were of the same sign. + return true; +} + +const boxToSphere = new Vector3(); +const sphereBoxNs = new Vector3(); +const sphereBoxNs1 = new Vector3(); +const sphereBoxNs2 = new Vector3(); +const sphereBoxSides = [new Vector3(), new Vector3(), new Vector3(), new Vector3(), new Vector3(), new Vector3()]; +const sphereBoxSphereToCorner = new Vector3(); +const sphereBoxSideNs = new Vector3(); +const sphereBoxSideNs1 = new Vector3(); +const sphereBoxSideNs2 = new Vector3(); + +const convexToSphere = new Vector3(); +const sphereConvexEdge = new Vector3(); +const sphereConvexEdgeUnit = new Vector3(); +const sphereConvexSphereToCorner = new Vector3(); +const sphereConvexWorldCorner = new Vector3(); +const sphereConvexWorldNormal = new Vector3(); +const sphereConvexWorldPoint = new Vector3(); +const sphereConvexWorldSpherePointClosestToPlane = new Vector3(); +const sphereConvexPenetrationVec = new Vector3(); +const sphereConvexSphereToWorldPoint = new Vector3(); + +// const planeBox_normal = new Vector3(); +// const plane_to_corner = new Vector3(); + +const planeConvexV = new Vector3(); +const planeConvexNormal = new Vector3(); +const planeConvexRelpos = new Vector3(); +const planeConvexProjected = new Vector3(); + +const convexConvexSepAxis = new Vector3(); +const convexConvexQ = new Vector3(); + +const particlePlaneNormal = new Vector3(); +const particlePlaneRelpos = new Vector3(); +const particlePlaneProjected = new Vector3(); + +const particleSphereNormal = new Vector3(); + +// WIP +const cqj = new Quaternion(); +const convexParticleLocal = new Vector3(); +// const convexParticle_normal = new Vector3(); +const convexParticlePenetratedFaceNormal = new Vector3(); +const convexParticleVertexToParticle = new Vector3(); +const convexParticleWorldPenetrationVec = new Vector3(); + +const convexHeightfieldTmp1 = new Vector3(); +const convexHeightfieldTmp2 = new Vector3(); +const convexHeightfieldFaceList = [0]; + +const sphereHeightfieldTmp1 = new Vector3(); +const sphereHeightfieldTmp2 = new Vector3(); + +Narrowphase.prototype[Shape.types.BOX | Shape.types.BOX] = Narrowphase.prototype.boxBox; +Narrowphase.prototype[Shape.types.BOX | Shape.types.CONVEXPOLYHEDRON] = Narrowphase.prototype.boxConvex; +Narrowphase.prototype[Shape.types.BOX | Shape.types.PARTICLE] = Narrowphase.prototype.boxParticle; +Narrowphase.prototype[Shape.types.SPHERE] = Narrowphase.prototype.sphereSphere; +Narrowphase.prototype[Shape.types.PLANE | Shape.types.TRIMESH] = Narrowphase.prototype.planeTrimesh; +Narrowphase.prototype[Shape.types.SPHERE | Shape.types.TRIMESH] = Narrowphase.prototype.sphereTrimesh; + +Narrowphase.prototype[Shape.types.SPHERE | Shape.types.PLANE] = Narrowphase.prototype.spherePlane; + +Narrowphase.prototype[Shape.types.SPHERE | Shape.types.BOX] = Narrowphase.prototype.sphereBox; +Narrowphase.prototype[Shape.types.SPHERE | Shape.types.CONVEXPOLYHEDRON] = Narrowphase.prototype.sphereConvex; + +Narrowphase.prototype[Shape.types.PLANE | Shape.types.BOX] = Narrowphase.prototype.planeBox; + +Narrowphase.prototype[Shape.types.PLANE | Shape.types.CONVEXPOLYHEDRON] = Narrowphase.prototype.planeConvex; + +Narrowphase.prototype[Shape.types.CONVEXPOLYHEDRON] = Narrowphase.prototype.convexConvex; + +Narrowphase.prototype[Shape.types.PLANE | Shape.types.PARTICLE] = Narrowphase.prototype.planeParticle; + +Narrowphase.prototype[Shape.types.PARTICLE | Shape.types.SPHERE] = Narrowphase.prototype.sphereParticle; + +Narrowphase.prototype[Shape.types.PARTICLE | Shape.types.CONVEXPOLYHEDRON] = Narrowphase.prototype.convexParticle; + +Narrowphase.prototype[Shape.types.BOX | Shape.types.HEIGHTFIELD] = Narrowphase.prototype.boxHeightfield; +Narrowphase.prototype[Shape.types.SPHERE | Shape.types.HEIGHTFIELD] = Narrowphase.prototype.sphereHeightfield; + +Narrowphase.prototype[Shape.types.CONVEXPOLYHEDRON | Shape.types.HEIGHTFIELD] = Narrowphase.prototype.convexHeightfield; + diff --git a/src/world/World.js b/src/world/World.js deleted file mode 100644 index 2a0d4a21e..000000000 --- a/src/world/World.js +++ /dev/null @@ -1,1032 +0,0 @@ -/* global performance */ - -module.exports = World; - -var Shape = require('../shapes/Shape'); -var Vec3 = require('../math/Vec3'); -var Quaternion = require('../math/Quaternion'); -var GSSolver = require('../solver/GSSolver'); -var ContactEquation = require('../equations/ContactEquation'); -var FrictionEquation = require('../equations/FrictionEquation'); -var Narrowphase = require('./Narrowphase'); -var EventTarget = require('../utils/EventTarget'); -var ArrayCollisionMatrix = require('../collision/ArrayCollisionMatrix'); -var OverlapKeeper = require('../collision/OverlapKeeper'); -var Material = require('../material/Material'); -var ContactMaterial = require('../material/ContactMaterial'); -var Body = require('../objects/Body'); -var TupleDictionary = require('../utils/TupleDictionary'); -var RaycastResult = require('../collision/RaycastResult'); -var AABB = require('../collision/AABB'); -var Ray = require('../collision/Ray'); -var NaiveBroadphase = require('../collision/NaiveBroadphase'); - -/** - * The physics world - * @class World - * @constructor - * @extends EventTarget - * @param {object} [options] - * @param {Vec3} [options.gravity] - * @param {boolean} [options.allowSleep] - * @param {Broadphase} [options.broadphase] - * @param {Solver} [options.solver] - * @param {boolean} [options.quatNormalizeFast] - * @param {number} [options.quatNormalizeSkip] - */ -function World(options){ - options = options || {}; - EventTarget.apply(this); - - /** - * Currently / last used timestep. Is set to -1 if not available. This value is updated before each internal step, which means that it is "fresh" inside event callbacks. - * @property {Number} dt - */ - this.dt = -1; - - /** - * Makes bodies go to sleep when they've been inactive - * @property allowSleep - * @type {Boolean} - * @default false - */ - this.allowSleep = !!options.allowSleep; - - /** - * All the current contacts (instances of ContactEquation) in the world. - * @property contacts - * @type {Array} - */ - this.contacts = []; - this.frictionEquations = []; - - /** - * How often to normalize quaternions. Set to 0 for every step, 1 for every second etc.. A larger value increases performance. If bodies tend to explode, set to a smaller value (zero to be sure nothing can go wrong). - * @property quatNormalizeSkip - * @type {Number} - * @default 0 - */ - this.quatNormalizeSkip = options.quatNormalizeSkip !== undefined ? options.quatNormalizeSkip : 0; - - /** - * Set to true to use fast quaternion normalization. It is often enough accurate to use. If bodies tend to explode, set to false. - * @property quatNormalizeFast - * @type {Boolean} - * @see Quaternion.normalizeFast - * @see Quaternion.normalize - * @default false - */ - this.quatNormalizeFast = options.quatNormalizeFast !== undefined ? options.quatNormalizeFast : false; - - /** - * The wall-clock time since simulation start - * @property time - * @type {Number} - */ - this.time = 0.0; - - /** - * Number of timesteps taken since start - * @property stepnumber - * @type {Number} - */ - this.stepnumber = 0; - - /// Default and last timestep sizes - this.default_dt = 1/60; - - this.nextId = 0; - /** - * @property gravity - * @type {Vec3} - */ - this.gravity = new Vec3(); - if(options.gravity){ - this.gravity.copy(options.gravity); - } - - /** - * The broadphase algorithm to use. Default is NaiveBroadphase - * @property broadphase - * @type {Broadphase} - */ - this.broadphase = options.broadphase !== undefined ? options.broadphase : new NaiveBroadphase(); - - /** - * @property bodies - * @type {Array} - */ - this.bodies = []; - - /** - * The solver algorithm to use. Default is GSSolver - * @property solver - * @type {Solver} - */ - this.solver = options.solver !== undefined ? options.solver : new GSSolver(); - - /** - * @property constraints - * @type {Array} - */ - this.constraints = []; - - /** - * @property narrowphase - * @type {Narrowphase} - */ - this.narrowphase = new Narrowphase(this); - - /** - * @property {ArrayCollisionMatrix} collisionMatrix - * @type {ArrayCollisionMatrix} - */ - this.collisionMatrix = new ArrayCollisionMatrix(); - - /** - * CollisionMatrix from the previous step. - * @property {ArrayCollisionMatrix} collisionMatrixPrevious - * @type {ArrayCollisionMatrix} - */ - this.collisionMatrixPrevious = new ArrayCollisionMatrix(); - - this.bodyOverlapKeeper = new OverlapKeeper(); - this.shapeOverlapKeeper = new OverlapKeeper(); - - /** - * All added materials - * @property materials - * @type {Array} - */ - this.materials = []; - - /** - * @property contactmaterials - * @type {Array} - */ - this.contactmaterials = []; - - /** - * Used to look up a ContactMaterial given two instances of Material. - * @property {TupleDictionary} contactMaterialTable - */ - this.contactMaterialTable = new TupleDictionary(); - - this.defaultMaterial = new Material("default"); - - /** - * This contact material is used if no suitable contactmaterial is found for a contact. - * @property defaultContactMaterial - * @type {ContactMaterial} - */ - this.defaultContactMaterial = new ContactMaterial(this.defaultMaterial, this.defaultMaterial, { friction: 0.3, restitution: 0.0 }); - - /** - * @property doProfiling - * @type {Boolean} - */ - this.doProfiling = false; - - /** - * @property profile - * @type {Object} - */ - this.profile = { - solve:0, - makeContactConstraints:0, - broadphase:0, - integrate:0, - narrowphase:0, - }; - - /** - * Time accumulator for interpolation. See http://gafferongames.com/game-physics/fix-your-timestep/ - * @property {Number} accumulator - */ - this.accumulator = 0; - - /** - * @property subsystems - * @type {Array} - */ - this.subsystems = []; - - /** - * Dispatched after a body has been added to the world. - * @event addBody - * @param {Body} body The body that has been added to the world. - */ - this.addBodyEvent = { - type:"addBody", - body : null - }; - - /** - * Dispatched after a body has been removed from the world. - * @event removeBody - * @param {Body} body The body that has been removed from the world. - */ - this.removeBodyEvent = { - type:"removeBody", - body : null - }; - - this.idToBodyMap = {}; - - this.broadphase.setWorld(this); -} -World.prototype = new EventTarget(); - -// Temp stuff -var tmpAABB1 = new AABB(); -var tmpArray1 = []; -var tmpRay = new Ray(); - -/** - * Get the contact material between materials m1 and m2 - * @method getContactMaterial - * @param {Material} m1 - * @param {Material} m2 - * @return {ContactMaterial} The contact material if it was found. - */ -World.prototype.getContactMaterial = function(m1,m2){ - return this.contactMaterialTable.get(m1.id,m2.id); //this.contactmaterials[this.mats2cmat[i+j*this.materials.length]]; -}; - -/** - * Get number of objects in the world. - * @method numObjects - * @return {Number} - * @deprecated - */ -World.prototype.numObjects = function(){ - return this.bodies.length; -}; - -/** - * Store old collision state info - * @method collisionMatrixTick - */ -World.prototype.collisionMatrixTick = function(){ - var temp = this.collisionMatrixPrevious; - this.collisionMatrixPrevious = this.collisionMatrix; - this.collisionMatrix = temp; - this.collisionMatrix.reset(); - - this.bodyOverlapKeeper.tick(); - this.shapeOverlapKeeper.tick(); -}; - -/** - * Add a rigid body to the simulation. - * @method add - * @param {Body} body - * @todo If the simulation has not yet started, why recrete and copy arrays for each body? Accumulate in dynamic arrays in this case. - * @todo Adding an array of bodies should be possible. This would save some loops too - * @deprecated Use .addBody instead - */ -World.prototype.add = World.prototype.addBody = function(body){ - if(this.bodies.indexOf(body) !== -1){ - return; - } - body.index = this.bodies.length; - this.bodies.push(body); - body.world = this; - body.initPosition.copy(body.position); - body.initVelocity.copy(body.velocity); - body.timeLastSleepy = this.time; - if(body instanceof Body){ - body.initAngularVelocity.copy(body.angularVelocity); - body.initQuaternion.copy(body.quaternion); - } - this.collisionMatrix.setNumObjects(this.bodies.length); - this.addBodyEvent.body = body; - this.idToBodyMap[body.id] = body; - this.dispatchEvent(this.addBodyEvent); -}; - -/** - * Add a constraint to the simulation. - * @method addConstraint - * @param {Constraint} c - */ -World.prototype.addConstraint = function(c){ - this.constraints.push(c); -}; - -/** - * Removes a constraint - * @method removeConstraint - * @param {Constraint} c - */ -World.prototype.removeConstraint = function(c){ - var idx = this.constraints.indexOf(c); - if(idx!==-1){ - this.constraints.splice(idx,1); - } -}; - -/** - * Raycast test - * @method rayTest - * @param {Vec3} from - * @param {Vec3} to - * @param {RaycastResult} result - * @deprecated Use .raycastAll, .raycastClosest or .raycastAny instead. - */ -World.prototype.rayTest = function(from, to, result){ - if(result instanceof RaycastResult){ - // Do raycastclosest - this.raycastClosest(from, to, { - skipBackfaces: true - }, result); - } else { - // Do raycastAll - this.raycastAll(from, to, { - skipBackfaces: true - }, result); - } -}; - -/** - * Ray cast against all bodies. The provided callback will be executed for each hit with a RaycastResult as single argument. - * @method raycastAll - * @param {Vec3} from - * @param {Vec3} to - * @param {Object} options - * @param {number} [options.collisionFilterMask=-1] - * @param {number} [options.collisionFilterGroup=-1] - * @param {boolean} [options.skipBackfaces=false] - * @param {boolean} [options.checkCollisionResponse=true] - * @param {Function} callback - * @return {boolean} True if any body was hit. - */ -World.prototype.raycastAll = function(from, to, options, callback){ - options.mode = Ray.ALL; - options.from = from; - options.to = to; - options.callback = callback; - return tmpRay.intersectWorld(this, options); -}; - -/** - * Ray cast, and stop at the first result. Note that the order is random - but the method is fast. - * @method raycastAny - * @param {Vec3} from - * @param {Vec3} to - * @param {Object} options - * @param {number} [options.collisionFilterMask=-1] - * @param {number} [options.collisionFilterGroup=-1] - * @param {boolean} [options.skipBackfaces=false] - * @param {boolean} [options.checkCollisionResponse=true] - * @param {RaycastResult} result - * @return {boolean} True if any body was hit. - */ -World.prototype.raycastAny = function(from, to, options, result){ - options.mode = Ray.ANY; - options.from = from; - options.to = to; - options.result = result; - return tmpRay.intersectWorld(this, options); -}; - -/** - * Ray cast, and return information of the closest hit. - * @method raycastClosest - * @param {Vec3} from - * @param {Vec3} to - * @param {Object} options - * @param {number} [options.collisionFilterMask=-1] - * @param {number} [options.collisionFilterGroup=-1] - * @param {boolean} [options.skipBackfaces=false] - * @param {boolean} [options.checkCollisionResponse=true] - * @param {RaycastResult} result - * @return {boolean} True if any body was hit. - */ -World.prototype.raycastClosest = function(from, to, options, result){ - options.mode = Ray.CLOSEST; - options.from = from; - options.to = to; - options.result = result; - return tmpRay.intersectWorld(this, options); -}; - -/** - * Remove a rigid body from the simulation. - * @method remove - * @param {Body} body - * @deprecated Use .removeBody instead - */ -World.prototype.remove = function(body){ - body.world = null; - var n = this.bodies.length - 1, - bodies = this.bodies, - idx = bodies.indexOf(body); - if(idx !== -1){ - bodies.splice(idx, 1); // Todo: should use a garbage free method - - // Recompute index - for(var i=0; i!==bodies.length; i++){ - bodies[i].index = i; - } - - this.collisionMatrix.setNumObjects(n); - this.removeBodyEvent.body = body; - delete this.idToBodyMap[body.id]; - this.dispatchEvent(this.removeBodyEvent); - } -}; - -/** - * Remove a rigid body from the simulation. - * @method removeBody - * @param {Body} body - */ -World.prototype.removeBody = World.prototype.remove; - -World.prototype.getBodyById = function(id){ - return this.idToBodyMap[id]; -}; - -// TODO Make a faster map -World.prototype.getShapeById = function(id){ - var bodies = this.bodies; - for(var i=0, bl = bodies.length; i= dt && substeps < maxSubSteps) { - // Do fixed steps to catch up - this.internalStep(dt); - this.accumulator -= dt; - substeps++; - } - - var t = (this.accumulator % dt) / dt; - for(var j=0; j !== this.bodies.length; j++){ - var b = this.bodies[j]; - b.previousPosition.lerp(b.position, t, b.interpolatedPosition); - b.previousQuaternion.slerp(b.quaternion, t, b.interpolatedQuaternion); - b.previousQuaternion.normalize(); - } - this.time += timeSinceLastCalled; - } -}; - -var - /** - * Dispatched after the world has stepped forward in time. - * @event postStep - */ - World_step_postStepEvent = {type:"postStep"}, // Reusable event objects to save memory - /** - * Dispatched before the world steps forward in time. - * @event preStep - */ - World_step_preStepEvent = {type:"preStep"}, - World_step_collideEvent = {type:Body.COLLIDE_EVENT_NAME, body:null, contact:null }, - World_step_oldContacts = [], // Pools for unused objects - World_step_frictionEquationPool = [], - World_step_p1 = [], // Reusable arrays for collision pairs - World_step_p2 = [], - World_step_gvec = new Vec3(), // Temporary vectors and quats - World_step_vi = new Vec3(), - World_step_vj = new Vec3(), - World_step_wi = new Vec3(), - World_step_wj = new Vec3(), - World_step_t1 = new Vec3(), - World_step_t2 = new Vec3(), - World_step_rixn = new Vec3(), - World_step_rjxn = new Vec3(), - World_step_step_q = new Quaternion(), - World_step_step_w = new Quaternion(), - World_step_step_wq = new Quaternion(), - invI_tau_dt = new Vec3(); -World.prototype.internalStep = function(dt){ - this.dt = dt; - - var world = this, - that = this, - contacts = this.contacts, - p1 = World_step_p1, - p2 = World_step_p2, - N = this.numObjects(), - bodies = this.bodies, - solver = this.solver, - gravity = this.gravity, - doProfiling = this.doProfiling, - profile = this.profile, - DYNAMIC = Body.DYNAMIC, - profilingStart, - constraints = this.constraints, - frictionEquationPool = World_step_frictionEquationPool, - gnorm = gravity.norm(), - gx = gravity.x, - gy = gravity.y, - gz = gravity.z, - i=0; - - if(doProfiling){ - profilingStart = performance.now(); - } - - // Add gravity to all objects - for(i=0; i!==N; i++){ - var bi = bodies[i]; - if(bi.type === DYNAMIC){ // Only for dynamic bodies - var f = bi.force, m = bi.mass; - f.x += m*gx; - f.y += m*gy; - f.z += m*gz; - } - } - - // Update subsystems - for(var i=0, Nsubsystems=this.subsystems.length; i!==Nsubsystems; i++){ - this.subsystems[i].update(); - } - - // Collision detection - if(doProfiling){ profilingStart = performance.now(); } - p1.length = 0; // Clean up pair arrays from last step - p2.length = 0; - this.broadphase.collisionPairs(this,p1,p2); - if(doProfiling){ profile.broadphase = performance.now() - profilingStart; } - - // Remove constrained pairs with collideConnected == false - var Nconstraints = constraints.length; - for(i=0; i!==Nconstraints; i++){ - var c = constraints[i]; - if(!c.collideConnected){ - for(var j = p1.length-1; j>=0; j-=1){ - if( (c.bodyA === p1[j] && c.bodyB === p2[j]) || - (c.bodyB === p1[j] && c.bodyA === p2[j])){ - p1.splice(j, 1); - p2.splice(j, 1); - } - } - } - } - - this.collisionMatrixTick(); - - // Generate contacts - if(doProfiling){ profilingStart = performance.now(); } - var oldcontacts = World_step_oldContacts; - var NoldContacts = contacts.length; - - for(i=0; i!==NoldContacts; i++){ - oldcontacts.push(contacts[i]); - } - contacts.length = 0; - - // Transfer FrictionEquation from current list to the pool for reuse - var NoldFrictionEquations = this.frictionEquations.length; - for(i=0; i!==NoldFrictionEquations; i++){ - frictionEquationPool.push(this.frictionEquations[i]); - } - this.frictionEquations.length = 0; - - this.narrowphase.getContacts( - p1, - p2, - this, - contacts, - oldcontacts, // To be reused - this.frictionEquations, - frictionEquationPool - ); - - if(doProfiling){ - profile.narrowphase = performance.now() - profilingStart; - } - - // Loop over all collisions - if(doProfiling){ - profilingStart = performance.now(); - } - - // Add all friction eqs - for (var i = 0; i < this.frictionEquations.length; i++) { - solver.addEquation(this.frictionEquations[i]); - } - - var ncontacts = contacts.length; - for(var k=0; k!==ncontacts; k++){ - - // Current contact - var c = contacts[k]; - - // Get current collision indeces - var bi = c.bi, - bj = c.bj, - si = c.si, - sj = c.sj; - - // Get collision properties - var cm; - if(bi.material && bj.material){ - cm = this.getContactMaterial(bi.material,bj.material) || this.defaultContactMaterial; - } else { - cm = this.defaultContactMaterial; - } - - // c.enabled = bi.collisionResponse && bj.collisionResponse && si.collisionResponse && sj.collisionResponse; - - var mu = cm.friction; - // c.restitution = cm.restitution; - - // If friction or restitution were specified in the material, use them - if(bi.material && bj.material){ - if(bi.material.friction >= 0 && bj.material.friction >= 0){ - mu = bi.material.friction * bj.material.friction; - } - - if(bi.material.restitution >= 0 && bj.material.restitution >= 0){ - c.restitution = bi.material.restitution * bj.material.restitution; - } - } - - // c.setSpookParams( - // cm.contactEquationStiffness, - // cm.contactEquationRelaxation, - // dt - // ); - - solver.addEquation(c); - - // // Add friction constraint equation - // if(mu > 0){ - - // // Create 2 tangent equations - // var mug = mu * gnorm; - // var reducedMass = (bi.invMass + bj.invMass); - // if(reducedMass > 0){ - // reducedMass = 1/reducedMass; - // } - // var pool = frictionEquationPool; - // var c1 = pool.length ? pool.pop() : new FrictionEquation(bi,bj,mug*reducedMass); - // var c2 = pool.length ? pool.pop() : new FrictionEquation(bi,bj,mug*reducedMass); - // this.frictionEquations.push(c1, c2); - - // c1.bi = c2.bi = bi; - // c1.bj = c2.bj = bj; - // c1.minForce = c2.minForce = -mug*reducedMass; - // c1.maxForce = c2.maxForce = mug*reducedMass; - - // // Copy over the relative vectors - // c1.ri.copy(c.ri); - // c1.rj.copy(c.rj); - // c2.ri.copy(c.ri); - // c2.rj.copy(c.rj); - - // // Construct tangents - // c.ni.tangents(c1.t, c2.t); - - // // Set spook params - // c1.setSpookParams(cm.frictionEquationStiffness, cm.frictionEquationRelaxation, dt); - // c2.setSpookParams(cm.frictionEquationStiffness, cm.frictionEquationRelaxation, dt); - - // c1.enabled = c2.enabled = c.enabled; - - // // Add equations to solver - // solver.addEquation(c1); - // solver.addEquation(c2); - // } - - if( bi.allowSleep && - bi.type === Body.DYNAMIC && - bi.sleepState === Body.SLEEPING && - bj.sleepState === Body.AWAKE && - bj.type !== Body.STATIC - ){ - var speedSquaredB = bj.velocity.norm2() + bj.angularVelocity.norm2(); - var speedLimitSquaredB = Math.pow(bj.sleepSpeedLimit,2); - if(speedSquaredB >= speedLimitSquaredB*2){ - bi._wakeUpAfterNarrowphase = true; - } - } - - if( bj.allowSleep && - bj.type === Body.DYNAMIC && - bj.sleepState === Body.SLEEPING && - bi.sleepState === Body.AWAKE && - bi.type !== Body.STATIC - ){ - var speedSquaredA = bi.velocity.norm2() + bi.angularVelocity.norm2(); - var speedLimitSquaredA = Math.pow(bi.sleepSpeedLimit,2); - if(speedSquaredA >= speedLimitSquaredA*2){ - bj._wakeUpAfterNarrowphase = true; - } - } - - // Now we know that i and j are in contact. Set collision matrix state - this.collisionMatrix.set(bi, bj, true); - - if (!this.collisionMatrixPrevious.get(bi, bj)) { - // First contact! - // We reuse the collideEvent object, otherwise we will end up creating new objects for each new contact, even if there's no event listener attached. - World_step_collideEvent.body = bj; - World_step_collideEvent.contact = c; - bi.dispatchEvent(World_step_collideEvent); - - World_step_collideEvent.body = bi; - bj.dispatchEvent(World_step_collideEvent); - } - - this.bodyOverlapKeeper.set(bi.id, bj.id); - this.shapeOverlapKeeper.set(si.id, sj.id); - } - - this.emitContactEvents(); - - if(doProfiling){ - profile.makeContactConstraints = performance.now() - profilingStart; - profilingStart = performance.now(); - } - - // Wake up bodies - for(i=0; i!==N; i++){ - var bi = bodies[i]; - if(bi._wakeUpAfterNarrowphase){ - bi.wakeUp(); - bi._wakeUpAfterNarrowphase = false; - } - } - - // Add user-added constraints - var Nconstraints = constraints.length; - for(i=0; i!==Nconstraints; i++){ - var c = constraints[i]; - c.update(); - for(var j=0, Neq=c.equations.length; j!==Neq; j++){ - var eq = c.equations[j]; - solver.addEquation(eq); - } - } - - // Solve the constrained system - solver.solve(dt,this); - - if(doProfiling){ - profile.solve = performance.now() - profilingStart; - } - - // Remove all contacts from solver - solver.removeAllEquations(); - - // Apply damping, see http://code.google.com/p/bullet/issues/detail?id=74 for details - var pow = Math.pow; - for(i=0; i!==N; i++){ - var bi = bodies[i]; - if(bi.type & DYNAMIC){ // Only for dynamic bodies - var ld = pow(1.0 - bi.linearDamping,dt); - var v = bi.velocity; - v.mult(ld,v); - var av = bi.angularVelocity; - if(av){ - var ad = pow(1.0 - bi.angularDamping,dt); - av.mult(ad,av); - } - } - } - - this.dispatchEvent(World_step_preStepEvent); - - // Invoke pre-step callbacks - for(i=0; i!==N; i++){ - var bi = bodies[i]; - if(bi.preStep){ - bi.preStep.call(bi); - } - } - - // Leap frog - // vnew = v + h*f/m - // xnew = x + h*vnew - if(doProfiling){ - profilingStart = performance.now(); - } - var stepnumber = this.stepnumber; - var quatNormalize = stepnumber % (this.quatNormalizeSkip + 1) === 0; - var quatNormalizeFast = this.quatNormalizeFast; - - for(i=0; i!==N; i++){ - bodies[i].integrate(dt, quatNormalize, quatNormalizeFast); - } - this.clearForces(); - - this.broadphase.dirty = true; - - if(doProfiling){ - profile.integrate = performance.now() - profilingStart; - } - - // Update world time - this.time += dt; - this.stepnumber += 1; - - this.dispatchEvent(World_step_postStepEvent); - - // Invoke post-step callbacks - for(i=0; i!==N; i++){ - var bi = bodies[i]; - var postStep = bi.postStep; - if(postStep){ - postStep.call(bi); - } - } - - // Sleeping update - if(this.allowSleep){ - for(i=0; i!==N; i++){ - bodies[i].sleepTick(this.time); - } - } -}; - -World.prototype.emitContactEvents = (function(){ - var additions = []; - var removals = []; - var beginContactEvent = { - type: 'beginContact', - bodyA: null, - bodyB: null - }; - var endContactEvent = { - type: 'endContact', - bodyA: null, - bodyB: null - }; - var beginShapeContactEvent = { - type: 'beginShapeContact', - bodyA: null, - bodyB: null, - shapeA: null, - shapeB: null - }; - var endShapeContactEvent = { - type: 'endShapeContact', - bodyA: null, - bodyB: null, - shapeA: null, - shapeB: null - }; - return function(){ - var hasBeginContact = this.hasAnyEventListener('beginContact'); - var hasEndContact = this.hasAnyEventListener('endContact'); - - if(hasBeginContact || hasEndContact){ - this.bodyOverlapKeeper.getDiff(additions, removals); - } - - if(hasBeginContact){ - for (var i = 0, l = additions.length; i < l; i += 2) { - beginContactEvent.bodyA = this.getBodyById(additions[i]); - beginContactEvent.bodyB = this.getBodyById(additions[i+1]); - this.dispatchEvent(beginContactEvent); - } - beginContactEvent.bodyA = beginContactEvent.bodyB = null; - } - - if(hasEndContact){ - for (var i = 0, l = removals.length; i < l; i += 2) { - endContactEvent.bodyA = this.getBodyById(removals[i]); - endContactEvent.bodyB = this.getBodyById(removals[i+1]); - this.dispatchEvent(endContactEvent); - } - endContactEvent.bodyA = endContactEvent.bodyB = null; - } - - additions.length = removals.length = 0; - - var hasBeginShapeContact = this.hasAnyEventListener('beginShapeContact'); - var hasEndShapeContact = this.hasAnyEventListener('endShapeContact'); - - if(hasBeginShapeContact || hasEndShapeContact){ - this.shapeOverlapKeeper.getDiff(additions, removals); - } - - if(hasBeginShapeContact){ - for (var i = 0, l = additions.length; i < l; i += 2) { - var shapeA = this.getShapeById(additions[i]); - var shapeB = this.getShapeById(additions[i+1]); - beginShapeContactEvent.shapeA = shapeA; - beginShapeContactEvent.shapeB = shapeB; - beginShapeContactEvent.bodyA = shapeA.body; - beginShapeContactEvent.bodyB = shapeB.body; - this.dispatchEvent(beginShapeContactEvent); - } - beginShapeContactEvent.bodyA = beginShapeContactEvent.bodyB = beginShapeContactEvent.shapeA = beginShapeContactEvent.shapeB = null; - } - - if(hasEndShapeContact){ - for (var i = 0, l = removals.length; i < l; i += 2) { - var shapeA = this.getShapeById(removals[i]); - var shapeB = this.getShapeById(removals[i+1]); - endShapeContactEvent.shapeA = shapeA; - endShapeContactEvent.shapeB = shapeB; - endShapeContactEvent.bodyA = shapeA.body; - endShapeContactEvent.bodyB = shapeB.body; - this.dispatchEvent(endShapeContactEvent); - } - endShapeContactEvent.bodyA = endShapeContactEvent.bodyB = endShapeContactEvent.shapeA = endShapeContactEvent.shapeB = null; - } - - }; -})(); - -/** - * Sets all body forces in the world to zero. - * @method clearForces - */ -World.prototype.clearForces = function(){ - var bodies = this.bodies; - var N = bodies.length; - for(var i=0; i !== N; i++){ - var b = bodies[i], - force = b.force, - tau = b.torque; - - b.force.set(0,0,0); - b.torque.set(0,0,0); - } -}; diff --git a/src/world/World.ts b/src/world/World.ts new file mode 100644 index 000000000..d3a251b39 --- /dev/null +++ b/src/world/World.ts @@ -0,0 +1,974 @@ +import { EventEmitter } from 'feng3d'; +import { Vector3 } from 'feng3d'; +import { Broadphase } from '../collision/Broadphase'; +import { NaiveBroadphase } from '../collision/NaiveBroadphase'; +import { OverlapKeeper } from '../collision/OverlapKeeper'; +import { Ray } from '../collision/Ray'; +import { RaycastResult } from '../collision/RaycastResult'; +import { worldNormal } from '../common'; +import { Constraint } from '../constraints/Constraint'; +import { ContactEquation } from '../equations/ContactEquation'; +import { FrictionEquation } from '../equations/FrictionEquation'; +import { ContactMaterial } from '../material/ContactMaterial'; +import { Material } from '../material/Material'; +import { Body } from '../objects/Body'; +import { SPHSystem } from '../objects/SPHSystem'; +import { Shape } from '../shapes/Shape'; +import { GSSolver } from '../solver/GSSolver'; +import { Solver } from '../solver/Solver'; +import { Narrowphase } from './Narrowphase'; + +export interface WorldEventMap +{ + addBody: Body + removeBody: Body + preStep: any; + /** + * Dispatched after the world has stepped forward in time. + */ + postStep: any; + + beginContact: { bodyA: Body; bodyB: Body; }; + + endContact: { bodyA: Body; bodyB: Body; }; + + beginShapeContact: { bodyA: Body; bodyB: Body; shapeA: Shape; shapeB: Shape; } + + endShapeContact: { bodyA: Body; bodyB: Body; shapeA: Shape; shapeB: Shape; } + +} + +export class World extends EventEmitter +{ + // static worldNormal = new Vector3(0, 0, 1); + static get worldNormal() + { + return worldNormal; + } + static set worldNormal(v) + { + worldNormal.copy(v); + } + + /** + * Currently / last used timestep. Is set to -1 if not available. This value is updated before each internal step, which means that it is "fresh" inside event callbacks. + */ + dt: number; + + /** + * Makes bodies go to sleep when they've been inactive + */ + allowSleep: boolean; + + /** + * All the current contacts (instances of ContactEquation) in the world. + */ + contacts: ContactEquation[]; + frictionEquations: FrictionEquation[]; + + /** + * How often to normalize quaternions. Set to 0 for every step, 1 for every second etc.. A larger value increases performance. If bodies tend to explode, set to a smaller value (zero to be sure nothing can go wrong). + */ + quatNormalizeSkip: number; + + /** + * Set to true to use fast quaternion normalization. It is often enough accurate to use. If bodies tend to explode, set to false. + */ + quatNormalizeFast: boolean; + + /** + * The wall-clock time since simulation start + */ + time: number; + + /** + * Number of timesteps taken since start + */ + stepnumber: number; + + // / Default and last timestep sizes + default_dt: number; + + nextId: number; + gravity: Vector3; + + /** + * The broadphase algorithm to use. Default is NaiveBroadphase + */ + broadphase: Broadphase; + + bodies: Body[]; + + /** + * The solver algorithm to use. Default is GSSolver + */ + solver: Solver; + + constraints: Constraint[]; + + narrowphase: Narrowphase; + + collisionMatrix = {}; + + /** + * CollisionMatrix from the previous step. + */ + collisionMatrixPrevious = {}; + + bodyOverlapKeeper: OverlapKeeper; + shapeOverlapKeeper: OverlapKeeper; + + /** + * All added materials + */ + materials: Material[]; + + contactmaterials: ContactMaterial[]; + + /** + * Used to look up a ContactMaterial given two instances of Material. + */ + contactMaterialTable: { [key: string]: ContactMaterial }; + + defaultMaterial: Material; + + /** + * This contact material is used if no suitable contactmaterial is found for a contact. + */ + defaultContactMaterial: ContactMaterial; + + doProfiling: boolean; + + profile = { + solve: 0, + makeContactConstraints: 0, + broadphase: 0, + integrate: 0, + narrowphase: 0, + }; + + /** + * Time accumulator for interpolation. See http://gafferongames.com/game-physics/fix-your-timestep/ + */ + accumulator: number; + + subsystems: SPHSystem[]; + + idToBodyMap: { [id: string]: Body } = {}; + + /** + * The physics world + * @param options + */ + constructor(options: { gravity?: Vector3, allowSleep?: boolean, broadphase?: Broadphase, solver?: Solver, quatNormalizeFast?: boolean, quatNormalizeSkip?: number } = {}) + { + super(); + + this.dt = -1; + this.allowSleep = !!options.allowSleep; + this.contacts = []; + this.frictionEquations = []; + this.quatNormalizeSkip = options.quatNormalizeSkip !== undefined ? options.quatNormalizeSkip : 0; + this.quatNormalizeFast = options.quatNormalizeFast !== undefined ? options.quatNormalizeFast : false; + this.time = 0.0; + this.stepnumber = 0; + this.default_dt = 1 / 60; + this.nextId = 0; + this.gravity = new Vector3(); + if (options.gravity) + { + this.gravity.copy(options.gravity); + } + this.broadphase = options.broadphase !== undefined ? options.broadphase : new NaiveBroadphase(); + this.bodies = []; + this.solver = options.solver !== undefined ? options.solver : new GSSolver(); + this.constraints = []; + this.narrowphase = new Narrowphase(this); + this.collisionMatrix = {}; + this.collisionMatrixPrevious = {}; + + this.bodyOverlapKeeper = new OverlapKeeper(); + this.shapeOverlapKeeper = new OverlapKeeper(); + this.materials = []; + this.contactmaterials = []; + this.contactMaterialTable = {}; + + this.defaultMaterial = new Material('default'); + this.defaultContactMaterial = new ContactMaterial(this.defaultMaterial, this.defaultMaterial, { friction: 0.3, restitution: 0.0 }); + this.doProfiling = false; + this.profile = { + solve: 0, + makeContactConstraints: 0, + broadphase: 0, + integrate: 0, + narrowphase: 0, + }; + this.accumulator = 0; + this.subsystems = []; + + this.idToBodyMap = {}; + + this.broadphase.setWorld(this); + } + + /** + * Get the contact material between materials m1 and m2 + * @param m1 + * @param m2 + * @return The contact material if it was found. + */ + getContactMaterial(m1: Material, m2: Material) + { + return this.contactMaterialTable[`${m1.id}_${m2.id}`]; // this.contactmaterials[this.mats2cmat[i+j*this.materials.length]]; + } + + /** + * Get number of objects in the world. + */ + numObjects() + { + return this.bodies.length; + } + + /** + * Store old collision state info + */ + collisionMatrixTick() + { + const temp = this.collisionMatrixPrevious; + this.collisionMatrixPrevious = this.collisionMatrix; + this.collisionMatrix = temp; + this.collisionMatrix = {}; + + this.bodyOverlapKeeper.tick(); + this.shapeOverlapKeeper.tick(); + } + + /** + * Add a rigid body to the simulation. + * @method add + * @param {Body} body + * @todo If the simulation has not yet started, why recrete and copy arrays for each body? Accumulate in dynamic arrays in this case. + * @todo Adding an array of bodies should be possible. This would save some loops too + */ + addBody(body: Body) + { + if (this.bodies.indexOf(body) !== -1) + { + return; + } + body.index = this.bodies.length; + this.bodies.push(body); + body.world = this; + body.initPosition.copy(body.position); + body.initVelocity.copy(body.velocity); + body.timeLastSleepy = this.time; + if (body instanceof Body) + { + body.initAngularVelocity.copy(body.angularVelocity); + body.initQuaternion.copy(body.quaternion); + } + this.idToBodyMap[body.id] = body; + this.emit('addBody', body); + } + + /** + * Add a constraint to the simulation. + * @param c + */ + addConstraint(c: Constraint) + { + this.constraints.push(c); + } + + /** + * Removes a constraint + * @param c + */ + removeConstraint(c: Constraint) + { + const idx = this.constraints.indexOf(c); + if (idx !== -1) + { + this.constraints.splice(idx, 1); + } + } + + /** + * Ray cast against all bodies. The provided callback will be executed for each hit with a RaycastResult as single argument. + * @param from + * @param to + * @param options + * @param callback + * @return True if any body was hit. + */ + raycastAll(from: Vector3, to: Vector3, options: { collisionFilterMask?: number, collisionFilterGroup?: number, skipBackfaces?: boolean, checkCollisionResponse?: boolean, mode?: number, from?: Vector3, to?: Vector3, callback?: Function } = {}, callback: Function) + { + options.mode = Ray.ALL; + options.from = from; + options.to = to; + options.callback = callback; + + return tmpRay.intersectWorld(this, options); + } + + /** + * Ray cast, and stop at the first result. Note that the order is random - but the method is fast. + * + * @param from + * @param to + * @param options + * @param result + * + * @return True if any body was hit. + */ + raycastAny(from: Vector3, to: Vector3, options: { collisionFilterMask?: number, collisionFilterGroup?: number, skipBackfaces?: boolean, checkCollisionResponse?: boolean, mode?: number, from?: Vector3, to?: Vector3, callback?: Function, result?: RaycastResult }, result: RaycastResult) + { + options.mode = Ray.ANY; + options.from = from; + options.to = to; + options.result = result; + + return tmpRay.intersectWorld(this, options); + } + + /** + * Ray cast, and return information of the closest hit. + * + * @param from + * @param to + * @param options + * @param result + * + * @return True if any body was hit. + */ + raycastClosest(from: Vector3, to: Vector3, options: { collisionFilterMask?: number, collisionFilterGroup?: number, skipBackfaces?: boolean, checkCollisionResponse?: boolean, mode?: number, from?: Vector3, to?: Vector3, callback?: Function, result?: RaycastResult }, result: RaycastResult) + { + options.mode = Ray.CLOSEST; + options.from = from; + options.to = to; + options.result = result; + + return tmpRay.intersectWorld(this, options); + } + + /** + * Remove a rigid body from the simulation. + * @param body + */ + removeBody(body: Body) + { + body.world = null; + // const n = this.bodies.length - 1; + const bodies = this.bodies; + const idx = bodies.indexOf(body); + if (idx !== -1) + { + bodies.splice(idx, 1); // Todo: should use a garbage free method + + // Recompute index + for (let i = 0; i !== bodies.length; i++) + { + bodies[i].index = i; + } + + delete this.idToBodyMap[body.id]; + this.emit('removeBody', body); + } + } + + getBodyById(id: number) + { + return this.idToBodyMap[id]; + } + + // TODO Make a faster map + getShapeById(id: number) + { + const bodies = this.bodies; + for (let i = 0, bl = bodies.length; i < bl; i++) + { + const shapes = bodies[i].shapes; + for (let j = 0, sl = shapes.length; j < sl; j++) + { + const shape = shapes[j]; + if (shape.id === id) + { + return shape; + } + } + } + } + + /** + * Adds a material to the World. + * @param m + * @todo Necessary? + */ + addMaterial(m: Material) + { + this.materials.push(m); + } + + /** + * Adds a contact material to the World + * @param cmat + */ + addContactMaterial(cmat: ContactMaterial) + { + // Add contact material + this.contactmaterials.push(cmat); + + // Add current contact material to the material table + this.contactMaterialTable[`${cmat.materials[0].id}_${cmat.materials[1].id}`] = cmat; + } + + /** + * Step the physics world forward in time. + * + * There are two modes. The simple mode is fixed timestepping without interpolation. In this case you only use the first argument. The second case uses interpolation. In that you also provide the time since the function was last used, as well as the maximum fixed timesteps to take. + * + * @param dt The fixed time step size to use. + * @param timeSinceLastCalled The time elapsed since the function was last called. + * @param maxSubSteps Maximum number of fixed steps to take per function call. + * + * @example + * // fixed timestepping without interpolation + * world.step(1/60); + * + * @see http://bulletphysics.org/mediawiki-1.5.8/index.php/Stepping_The_World + */ + step(dt: number, timeSinceLastCalled = 0, maxSubSteps = 10) + { + if (timeSinceLastCalled === 0) + { // Fixed, simple stepping + this.internalStep(dt); + + // Increment time + this.time += dt; + } + else + { + this.accumulator += timeSinceLastCalled; + let substeps = 0; + while (this.accumulator >= dt && substeps < maxSubSteps) + { + // Do fixed steps to catch up + this.internalStep(dt); + this.accumulator -= dt; + substeps++; + } + + const t = (this.accumulator % dt) / dt; + for (let j = 0; j !== this.bodies.length; j++) + { + const b = this.bodies[j]; + b.previousPosition.lerpNumberTo(b.position, t, b.interpolatedPosition); + b.previousQuaternion.slerpTo(b.quaternion, t, b.interpolatedQuaternion); + b.previousQuaternion.normalize(); + } + this.time += timeSinceLastCalled; + } + } + + internalStep(dt: number) + { + this.dt = dt; + + // const world = this; + // const that = this; + const contacts = this.contacts; + const p1 = WorldStepP1; + const p2 = WorldStepP2; + const N = this.numObjects(); + const bodies = this.bodies; + const solver = this.solver; + const gravity = this.gravity; + const doProfiling = this.doProfiling; + const profile = this.profile; + const DYNAMIC = Body.DYNAMIC; + let profilingStart; + const constraints = this.constraints; + const frictionEquationPool = WorldStepFrictionEquationPool; + // const gnorm = gravity.length; + const gx = gravity.x; + const gy = gravity.y; + const gz = gravity.z; + + if (doProfiling) + { + profilingStart = performance.now(); + } + + // Add gravity to all objects + for (let i = 0; i !== N; i++) + { + const bi = bodies[i]; + if (bi.type === DYNAMIC) + { // Only for dynamic bodies + const f = bi.force; const + m = bi.mass; + f.x += m * gx; + f.y += m * gy; + f.z += m * gz; + } + } + + // Update subsystems + for (let i = 0, Nsubsystems = this.subsystems.length; i !== Nsubsystems; i++) + { + this.subsystems[i].update(); + } + + // Collision detection + if (doProfiling) { profilingStart = performance.now(); } + p1.length = 0; // Clean up pair arrays from last step + p2.length = 0; + this.broadphase.collisionPairs(this, p1, p2); + if (doProfiling) { profile.broadphase = performance.now() - profilingStart; } + + // Remove constrained pairs with collideConnected == false + const Nconstraints = constraints.length; + for (let i = 0; i !== Nconstraints; i++) + { + const c = constraints[i]; + if (!c.collideConnected) + { + for (let j = p1.length - 1; j >= 0; j -= 1) + { + if ((c.bodyA === p1[j] && c.bodyB === p2[j]) + || (c.bodyB === p1[j] && c.bodyA === p2[j])) + { + p1.splice(j, 1); + p2.splice(j, 1); + } + } + } + } + + this.collisionMatrixTick(); + + // Generate contacts + if (doProfiling) { profilingStart = performance.now(); } + const oldcontacts = WorldStepOldContacts; + const NoldContacts = contacts.length; + + for (let i = 0; i !== NoldContacts; i++) + { + oldcontacts.push(contacts[i]); + } + contacts.length = 0; + + // Transfer FrictionEquation from current list to the pool for reuse + const NoldFrictionEquations = this.frictionEquations.length; + for (let i = 0; i !== NoldFrictionEquations; i++) + { + frictionEquationPool.push(this.frictionEquations[i]); + } + this.frictionEquations.length = 0; + + this.narrowphase.getContacts( + p1, + p2, + this, + contacts, + oldcontacts, // To be reused + this.frictionEquations, + frictionEquationPool + ); + + if (doProfiling) + { + profile.narrowphase = performance.now() - profilingStart; + } + + // Loop over all collisions + if (doProfiling) + { + profilingStart = performance.now(); + } + + // Add all friction eqs + for (let i = 0; i < this.frictionEquations.length; i++) + { + solver.addEquation(this.frictionEquations[i]); + } + + const ncontacts = contacts.length; + for (let k = 0; k !== ncontacts; k++) + { + // Current contact + const c = contacts[k]; + + // Get current collision indeces + const bi = c.bi; + const bj = c.bj; + const si = c.si; + const sj = c.sj; + + // Get collision properties + let cm: ContactMaterial; + if (bi.material && bj.material) + { + cm = this.getContactMaterial(bi.material, bj.material) || this.defaultContactMaterial; + } + else + { + cm = this.defaultContactMaterial; + } + + // c.enabled = bi.collisionResponse && bj.collisionResponse && si.collisionResponse && sj.collisionResponse; + + let mu = cm.friction; + // c.restitution = cm.restitution; + + // If friction or restitution were specified in the material, use them + if (bi.material && bj.material) + { + if (bi.material.friction >= 0 && bj.material.friction >= 0) + { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + mu = bi.material.friction * bj.material.friction; + } + + if (bi.material.restitution >= 0 && bj.material.restitution >= 0) + { + c.restitution = bi.material.restitution * bj.material.restitution; + } + } + + // c.setSpookParams( + // cm.contactEquationStiffness, + // cm.contactEquationRelaxation, + // dt + // ); + + solver.addEquation(c); + + // // Add friction constraint equation + // if(mu > 0){ + + // // Create 2 tangent equations + // let mug = mu * gnorm; + // let reducedMass = (bi.invMass + bj.invMass); + // if(reducedMass > 0){ + // reducedMass = 1/reducedMass; + // } + // let pool = frictionEquationPool; + // let c1 = pool.length ? pool.pop() : new FrictionEquation(bi,bj,mug*reducedMass); + // let c2 = pool.length ? pool.pop() : new FrictionEquation(bi,bj,mug*reducedMass); + // this.frictionEquations.push(c1, c2); + + // c1.bi = c2.bi = bi; + // c1.bj = c2.bj = bj; + // c1.minForce = c2.minForce = -mug*reducedMass; + // c1.maxForce = c2.maxForce = mug*reducedMass; + + // // Copy over the relative vectors + // c1.ri.copy(c.ri); + // c1.rj.copy(c.rj); + // c2.ri.copy(c.ri); + // c2.rj.copy(c.rj); + + // // Construct tangents + // c.ni.tangents(c1.t, c2.t); + + // // Set spook params + // c1.setSpookParams(cm.frictionEquationStiffness, cm.frictionEquationRelaxation, dt); + // c2.setSpookParams(cm.frictionEquationStiffness, cm.frictionEquationRelaxation, dt); + + // c1.enabled = c2.enabled = c.enabled; + + // // Add equations to solver + // solver.addEquation(c1); + // solver.addEquation(c2); + // } + + if (bi.allowSleep + && bi.type === Body.DYNAMIC + && bi.sleepState === Body.SLEEPING + && bj.sleepState === Body.AWAKE + && bj.type !== Body.STATIC + ) + { + const speedSquaredB = bj.velocity.lengthSquared + bj.angularVelocity.lengthSquared; + const speedLimitSquaredB = Math.pow(bj.sleepSpeedLimit, 2); + if (speedSquaredB >= speedLimitSquaredB * 2) + { + bi._wakeUpAfterNarrowphase = true; + } + } + + if (bj.allowSleep + && bj.type === Body.DYNAMIC + && bj.sleepState === Body.SLEEPING + && bi.sleepState === Body.AWAKE + && bi.type !== Body.STATIC + ) + { + const speedSquaredA = bi.velocity.lengthSquared + bi.angularVelocity.lengthSquared; + const speedLimitSquaredA = Math.pow(bi.sleepSpeedLimit, 2); + if (speedSquaredA >= speedLimitSquaredA * 2) + { + bj._wakeUpAfterNarrowphase = true; + } + } + + // Now we know that i and j are in contact. Set collision matrix state + this.collisionMatrix[`${bi.index}_${bj.index}`] = true; + + if (!this.collisionMatrixPrevious[`${bi.index}_${bj.index}`]) + { + // First contact! + // We reuse the collideEvent object, otherwise we will end up creating new objects for each new contact, even if there's no event listener attached. + bi.emit('collide', { body: bj, contact: c }); + bj.emit('collide', { body: bi, contact: c }); + } + + this.bodyOverlapKeeper.set(bi.id, bj.id); + this.shapeOverlapKeeper.set(si.id, sj.id); + } + + this.emitContactEvents(); + + if (doProfiling) + { + profile.makeContactConstraints = performance.now() - profilingStart; + profilingStart = performance.now(); + } + + // Wake up bodies + for (let i = 0; i !== N; i++) + { + const bi = bodies[i]; + if (bi._wakeUpAfterNarrowphase) + { + bi.wakeUp(); + bi._wakeUpAfterNarrowphase = false; + } + } + + // Add user-added constraints + const Nconstraints1 = constraints.length; + for (let i = 0; i !== Nconstraints1; i++) + { + const c = constraints[i]; + c.update(); + for (let j = 0, Neq = c.equations.length; j !== Neq; j++) + { + const eq = c.equations[j]; + solver.addEquation(eq); + } + } + + // Solve the constrained system + solver.solve(dt, this); + + if (doProfiling) + { + profile.solve = performance.now() - profilingStart; + } + + // Remove all contacts from solver + solver.removeAllEquations(); + + // Apply damping, see http://code.google.com/p/bullet/issues/detail?id=74 for details + const pow = Math.pow; + for (let i = 0; i !== N; i++) + { + const bi = bodies[i]; + if (bi.type & DYNAMIC) + { // Only for dynamic bodies + const ld = pow(1.0 - bi.linearDamping, dt); + const v = bi.velocity; + v.scaleNumberTo(ld, v); + const av = bi.angularVelocity; + if (av) + { + const ad = pow(1.0 - bi.angularDamping, dt); + av.scaleNumberTo(ad, av); + } + } + } + + this.emit('preStep'); + + // Leap frog + // vnew = v + h*f/m + // xnew = x + h*vnew + if (doProfiling) + { + profilingStart = performance.now(); + } + const stepnumber = this.stepnumber; + const quatNormalize = stepnumber % (this.quatNormalizeSkip + 1) === 0; + const quatNormalizeFast = this.quatNormalizeFast; + + for (let i = 0; i !== N; i++) + { + bodies[i].integrate(dt, quatNormalize, quatNormalizeFast); + } + this.clearForces(); + + this.broadphase.dirty = true; + + if (doProfiling) + { + profile.integrate = performance.now() - profilingStart; + } + + // Update world time + this.time += dt; + this.stepnumber += 1; + + this.emit('postStep'); + + // Sleeping update + if (this.allowSleep) + { + for (let i = 0; i !== N; i++) + { + bodies[i].sleepTick(this.time); + } + } + } + + emitContactEvents = (function () + { + const additions = []; + const removals = []; + + return function () + { + const _this = this as World; + + const hasBeginContact = _this.has('beginContact'); + const hasEndContact = _this.has('endContact'); + + if (hasBeginContact || hasEndContact) + { + _this.bodyOverlapKeeper.getDiff(additions, removals); + } + + if (hasBeginContact) + { + for (let i = 0, l = additions.length; i < l; i += 2) + { + _this.emit('beginContact', { + bodyA: _this.getBodyById(additions[i]), + bodyB: _this.getBodyById(additions[i + 1]) + }); + } + } + + if (hasEndContact) + { + for (let i = 0, l = removals.length; i < l; i += 2) + { + _this.emit('endContact', { + bodyA: _this.getBodyById(removals[i]), + bodyB: _this.getBodyById(removals[i + 1]) + }); + } + } + + additions.length = removals.length = 0; + + const hasBeginShapeContact = _this.has('beginShapeContact'); + const hasEndShapeContact = _this.has('endShapeContact'); + + if (hasBeginShapeContact || hasEndShapeContact) + { + _this.shapeOverlapKeeper.getDiff(additions, removals); + } + + if (hasBeginShapeContact) + { + for (let i = 0, l = additions.length; i < l; i += 2) + { + const shapeA = _this.getShapeById(additions[i]); + const shapeB = _this.getShapeById(additions[i + 1]); + + _this.emit('beginShapeContact', { shapeA, shapeB, bodyA: shapeA.body, bodyB: shapeB.body }); + } + } + + if (hasEndShapeContact) + { + for (let i = 0, l = removals.length; i < l; i += 2) + { + const shapeA = _this.getShapeById(removals[i]); + const shapeB = _this.getShapeById(removals[i + 1]); + + _this.emit('endShapeContact', { shapeA, shapeB, bodyA: shapeA.body, bodyB: shapeB.body }); + } + } + }; + })(); + + /** + * Sets all body forces in the world to zero. + * @method clearForces + */ + clearForces() + { + const bodies = this.bodies; + const N = bodies.length; + for (let i = 0; i !== N; i++) + { + const b = bodies[i]; + // const force = b.force; + // const tau = b.torque; + + b.force.set(0, 0, 0); + b.torque.set(0, 0, 0); + } + } +} + +// Temp stuff +// const tmpAABB1 = new Box3(); +// const tmpArray1 = []; +const tmpRay = new Ray(); + +// performance.now() +if (typeof performance === 'undefined') +{ + throw 'performance'; + + // performance = {}; +} +if (!performance.now) +{ + let nowOffset = Date.now(); + if (performance.timing && performance.timing.navigationStart) + { + nowOffset = performance.timing.navigationStart; + } + performance.now = function () + { + return Date.now() - nowOffset; + }; +} + +// const step_tmp1 = new Vector3(); +/** + * Dispatched before the world steps forward in time. + */ +const WorldStepOldContacts: ContactEquation[] = [];// Pools for unused objects +const WorldStepFrictionEquationPool = []; +const WorldStepP1 = []; // Reusable arrays for collision pairs +const WorldStepP2 = []; +// const World_step_gvec = new Vector3(); // Temporary vectors and quats +// const World_step_vi = new Vector3(); +// const World_step_vj = new Vector3(); +// const World_step_wi = new Vector3(); +// const World_step_wj = new Vector3(); +// const World_step_t1 = new Vector3(); +// const World_step_t2 = new Vector3(); +// const World_step_rixn = new Vector3(); +// const World_step_rjxn = new Vector3(); +// const World_step_step_q = new Quaternion(); +// const World_step_step_w = new Quaternion(); +// const World_step_step_wq = new Quaternion(); +// const invI_tau_dt = new Vector3(); diff --git a/test/AABB.js b/test/AABB.js deleted file mode 100644 index 9b9ae727b..000000000 --- a/test/AABB.js +++ /dev/null @@ -1,192 +0,0 @@ -var AABB = require('../src/collision/AABB'); -var Vec3 = require('../src/math/Vec3'); -var Transform = require('../src/math/Transform'); - -module.exports = { - construct: function(test){ - new AABB(); - test.done(); - }, - - copy: function(test){ - var a = new AABB(), - b = new AABB(); - a.upperBound.set(1, 2, 3); - b.copy(a); - test.deepEqual(a, b); - test.done(); - }, - - clone: function(test){ - var a = new AABB({ - lowerBound: new Vec3(-1,-2,-3), - upperBound: new Vec3(1,2,3) - }); - var b = a.clone(); - - test.deepEqual(a,b); - - test.equal(a === b, false); - - test.done(); - }, - - extend: function(test){ - var a = new AABB({ - lowerBound: new Vec3(-1,-1,-1), - upperBound: new Vec3(1,1,1) - }); - var b = new AABB({ - lowerBound: new Vec3(-2,-2,-2), - upperBound: new Vec3(2,2,2) - }); - a.extend(b); - test.deepEqual(a,b); - - a = new AABB({ - lowerBound: new Vec3(-1,-1,-1), - upperBound: new Vec3(1,1,1) - }); - b = new AABB({ - lowerBound: new Vec3(-2,-2,-2), - upperBound: new Vec3(2,2,2) - }); - b.extend(a); - test.deepEqual(b.lowerBound, new Vec3(-2,-2,-2)); - test.deepEqual(b.upperBound, new Vec3(2,2,2)); - - a = new AABB({ - lowerBound: new Vec3(-2,-1,-1), - upperBound: new Vec3(2,1,1) - }); - b = new AABB({ - lowerBound: new Vec3(-1,-1,-1), - upperBound: new Vec3(1,1,1) - }); - b.extend(a); - test.deepEqual(a.lowerBound, new Vec3(-2,-1,-1)); - test.deepEqual(a.upperBound, new Vec3(2,1,1)); - - test.done(); - }, - - overlaps: function(test){ - var a = new AABB(), - b = new AABB(); - - // Same aabb - a.lowerBound.set(-1, -1, 0); - a.upperBound.set( 1, 1, 0); - b.lowerBound.set(-1, -1, 0); - b.upperBound.set( 1, 1, 0); - test.ok(a.overlaps(b),'should detect overlap'); - - // Corner overlaps - b.lowerBound.set( 1, 1, 0); - b.upperBound.set( 2, 2, 0); - test.ok(a.overlaps(b),'should detect corner overlap'); - - // Separate - b.lowerBound.set( 1.1, 1.1, 0); - test.ok(!a.overlaps(b),'should detect separated'); - - // fully inside - b.lowerBound.set(-0.5, -0.5, 0); - b.upperBound.set( 0.5, 0.5, 0); - test.ok(a.overlaps(b),'should detect if aabb is fully inside other aabb'); - b.lowerBound.set(-1.5, -1.5, 0); - b.upperBound.set( 1.5, 1.5, 0); - test.ok(a.overlaps(b),'should detect if aabb is fully inside other aabb'); - - // Translated - b.lowerBound.set(-3, -0.5, 0); - b.upperBound.set(-2, 0.5, 0); - test.ok(!a.overlaps(b),'should detect translated'); - - test.done(); - }, - - contains: function(test){ - var a = new AABB(), - b = new AABB(); - - a.lowerBound.set(-1, -1, -1); - a.upperBound.set( 1, 1, 1); - b.lowerBound.set(-1, -1, -1); - b.upperBound.set( 1, 1, 1); - - test.ok(a.contains(b)); - - a.lowerBound.set(-2, -2, -2); - a.upperBound.set( 2, 2, 2); - - test.ok(a.contains(b)); - - b.lowerBound.set(-3, -3, -3); - b.upperBound.set( 3, 3, 3); - - test.equal(a.contains(b), false); - - a.lowerBound.set(0, 0, 0); - a.upperBound.set( 2, 2, 2); - b.lowerBound.set(-1, -1, -1); - b.upperBound.set( 1, 1, 1); - - test.equal(a.contains(b), false); - - test.done(); - }, - - toLocalFrame: function(test){ - var worldAABB = new AABB(); - var localAABB = new AABB(); - var frame = new Transform(); - - worldAABB.lowerBound.set(-1, -1, -1); - worldAABB.upperBound.set(1, 1, 1); - - // No transform - should stay the same - worldAABB.toLocalFrame(frame, localAABB); - test.deepEqual(localAABB, worldAABB); - - // Some translation - frame.position.set(-1,0,0); - worldAABB.toLocalFrame(frame, localAABB); - test.deepEqual( - localAABB, - new AABB({ - lowerBound: new Vec3(0, -1, -1), - upperBound: new Vec3(2, 1, 1) - }) - ); - - test.done(); - }, - - toWorldFrame: function(test){ - var localAABB = new AABB(); - var worldAABB = new AABB(); - var frame = new Transform(); - - localAABB.lowerBound.set(-1, -1, -1); - localAABB.upperBound.set(1, 1, 1); - - // No transform - should stay the same - localAABB.toLocalFrame(frame, worldAABB); - test.deepEqual(localAABB, worldAABB); - - // Some translation on the frame - frame.position.set(1,0,0); - localAABB.toWorldFrame(frame, worldAABB); - test.deepEqual( - worldAABB, - new AABB({ - lowerBound: new Vec3(0, -1, -1), - upperBound: new Vec3(2, 1, 1) - }) - ); - - test.done(); - }, -}; - diff --git a/test/Body.js b/test/Body.js deleted file mode 100644 index 6a8feeb8b..000000000 --- a/test/Body.js +++ /dev/null @@ -1,163 +0,0 @@ -var Vec3 = require("../src/math/Vec3"); -var Mat3 = require("../src/math/Mat3"); -var Quaternion = require("../src/math/Quaternion"); -var Box = require('../src/shapes/Box'); -var Sphere = require('../src/shapes/Sphere'); -var Body = require('../src/objects/Body'); - -module.exports = { - computeAABB : { - box: function(test){ - var body = new Body({ mass: 1 }); - body.addShape(new Box(new Vec3(1,1,1))); - body.computeAABB(); - test.equal(body.aabb.lowerBound.x,-1); - test.equal(body.aabb.lowerBound.y,-1); - test.equal(body.aabb.lowerBound.z,-1); - test.equal(body.aabb.upperBound.x,1); - test.equal(body.aabb.upperBound.y,1); - test.equal(body.aabb.upperBound.z,1); - - body.position.x = 1; - body.computeAABB(); - - test.equal(body.aabb.lowerBound.x,0); - test.equal(body.aabb.upperBound.x,2); - - test.done(); - }, - boxOffset: function(test){ - var quaternion = new Quaternion(); - quaternion.setFromAxisAngle(new Vec3(0,0,1), Math.PI / 2); - var body = new Body({ mass: 1 }); - body.addShape(new Box(new Vec3(1,1,1)), new Vec3(1,1,1)); - body.computeAABB(); - test.equal(body.aabb.lowerBound.x,0); - test.equal(body.aabb.lowerBound.y,0); - test.equal(body.aabb.lowerBound.z,0); - test.equal(body.aabb.upperBound.x,2); - test.equal(body.aabb.upperBound.y,2); - test.equal(body.aabb.upperBound.z,2); - - body.position.x = 1; - body.computeAABB(); - - test.equal(body.aabb.lowerBound.x,1); - test.equal(body.aabb.upperBound.x,3); - - test.done(); - } - }, - - updateInertiaWorld : function(test){ - var body = new Body({ mass: 1 }); - body.addShape(new Box(new Vec3(1,1,1))); - body.quaternion.setFromEuler(Math.PI/2,0,0); - body.updateInertiaWorld(); - test.done(); - }, - - pointToLocalFrame : function(test){ - var body = new Body({ mass: 1 }); - body.addShape(new Sphere(1)); - body.position.set(1,2,2); - var localPoint = body.pointToLocalFrame(new Vec3(1,2,3)); - test.ok(localPoint.almostEquals(new Vec3(0,0,1))); - test.done(); - }, - - pointToWorldFrame : function(test){ - var body = new Body({ mass: 1 }); - body.addShape(new Sphere(1)); - body.position.set(1,2,2); - var worldPoint = body.pointToWorldFrame(new Vec3(1,0,0)); - test.ok(worldPoint.almostEquals(new Vec3(2,2,2))); - test.done(); - }, - - addShape : function(test){ - var sphereShape = new Sphere(1); - - var bodyA = new Body({ - mass: 1, - shape: sphereShape - }); - var bodyB = new Body({ - mass: 1 - }); - bodyB.addShape(sphereShape); - - test.deepEqual(bodyA.shapes, bodyB.shapes, 'Adding shape via options did not work.'); - test.deepEqual(bodyA.inertia, bodyB.inertia); - - test.done(); - }, - - applyForce : function(test){ - var sphereShape = new Sphere(1); - var body = new Body({ - mass: 1, - shape: sphereShape - }); - - var worldPoint = new Vec3(1,0,0); - var forceVector = new Vec3(0,1,0); - body.applyForce(forceVector, worldPoint); - test.deepEqual(body.force, forceVector); - test.deepEqual(body.torque, new Vec3(0,0,1)); - - test.done(); - }, - - applyLocalForce : function(test){ - var sphereShape = new Sphere(1); - var body = new Body({ - mass: 1, - shape: sphereShape - }); - body.quaternion.setFromAxisAngle(new Vec3(1, 0, 0), Math.PI / 2); - - var localPoint = new Vec3(1,0,0); - var localForceVector = new Vec3(0,1,0); - body.applyLocalForce(localForceVector, localPoint); - test.ok(body.force.almostEquals(new Vec3(0,0,1))); // The force is rotated to world space - - test.done(); - }, - - applyImpulse : function(test){ - var sphereShape = new Sphere(1); - var body = new Body({ - mass: 1, - shape: sphereShape - }); - - var f = 1000; - var dt = 1 / 60; - var worldPoint = new Vec3(0,0,0); - var impulse = new Vec3(f*dt,0,0); - body.applyImpulse(impulse, worldPoint); - - test.ok(body.velocity.almostEquals(new Vec3(f*dt,0,0))); - - test.done(); - }, - - applyLocalImpulse : function(test){ - var sphereShape = new Sphere(1); - var body = new Body({ - mass: 1, - shape: sphereShape - }); - body.quaternion.setFromAxisAngle(new Vec3(1, 0, 0), Math.PI / 2); - - var f = 1000; - var dt = 1 / 60; - var localPoint = new Vec3(1,0,0); - var localImpulseVector = new Vec3(0,f*dt,0); - body.applyLocalImpulse(localImpulseVector, localPoint); - test.ok(body.velocity.almostEquals(new Vec3(0,0,f*dt))); // The force is rotated to world space - - test.done(); - }, -}; diff --git a/test/Box.js b/test/Box.js deleted file mode 100644 index 5c8f6a172..000000000 --- a/test/Box.js +++ /dev/null @@ -1,39 +0,0 @@ -var Vec3 = require("../src/math/Vec3") -, Quaternion = require("../src/math/Quaternion") -, Box = require('../src/shapes/Box') - -module.exports = { - forEachWOrldCorner : function(test){ - var box = new Box(new Vec3(1,1,1)); - var pos = new Vec3(); - var quat = new Quaternion(); - quat.setFromAxisAngle(new Vec3(0,0,1),Math.PI*0.25); - var numCorners = 0; - var unique = []; - box.forEachWorldCorner(pos,quat,function(x,y,z){ - var corner = new Vec3(x,y,z); - for(var i=0; i 0); - test.done(); - }, - - narrowphaseAgainstPlane: function(test){ - var world = new World(); - - var torusShape = Trimesh.createTorus(); - var torusBody = new Body({ - mass: 1 - }); - torusBody.addShape(torusShape); - - var planeBody = new Body({ - mass: 1 - }); - planeBody.addShape(new Plane()); - - world.addBody(torusBody); - world.addBody(planeBody); - - world.step(1 / 60); - - test.done(); - } -}; diff --git a/test/TupleDictionary.js b/test/TupleDictionary.js deleted file mode 100644 index 0254eb8f7..000000000 --- a/test/TupleDictionary.js +++ /dev/null @@ -1,40 +0,0 @@ -var TupleDictionary = require('../src/utils/TupleDictionary'); - -exports.set = function(test){ - var t = new TupleDictionary(); - - t.set(1,2,'lol'); - test.equal(t.data['1-2'],'lol'); - - t.set(2,1,'lol2'); - test.equal(t.data['1-2'],'lol2'); - - test.done(); -}; - -exports.get = function(test){ - var t = new TupleDictionary(); - - t.set(1,2,'1'); - t.set(3,2,'2'); - - test.equal(t.data['1-2'],t.get(1,2)); - test.equal(t.data['1-2'],t.get(2,1)); - - test.equal(t.data['2-3'],t.get(2,3)); - test.equal(t.data['2-3'],t.get(3,2)); - - test.done(); -}; - -exports.reset = function(test){ - var t = new TupleDictionary(), - empty = new TupleDictionary(); - - t.reset(); - t.set(1,2,'1'); - t.reset(); - test.deepEqual(t.data,empty.data); - - test.done(); -}; diff --git a/test/Vec3.js b/test/Vec3.js deleted file mode 100644 index 88985f095..000000000 --- a/test/Vec3.js +++ /dev/null @@ -1,85 +0,0 @@ -var Vec3 = require("../src/math/Vec3") -, Mat3 = require("../src/math/Mat3") -, Quaternion = require("../src/math/Quaternion") - -module.exports = { - creation : function(test) { - test.expect(3); - - var v = new Vec3(1, 2, 3); - test.equal(v.x, 1, "Creating a vec3 should set the first parameter to the x value"); - test.equal(v.y, 2, "Creating a vec3 should set the second parameter to the y value"); - test.equal(v.z, 3, "Creating a vec3 should set the third parameter to the z value"); - - test.done(); - }, - - cross : function(test) { - test.expect(3); - - var v = new Vec3(1, 2, 3); - var u = new Vec3(4, 5, 6); - v = v.cross(u); - - test.equal(v.x, -3, "Calculating cross product x"); - test.equal(v.y, 6, "Calculating cross product x"); - test.equal(v.z, -3, "Calculating cross product x"); - - test.done(); - }, - - dot : function(test) { - test.expect(2); - - var v = new Vec3(1, 2, 3); - var u = new Vec3(4, 5, 6); - var dot = v.dot(u); - - test.equal(dot, 4 + 10 + 18, "Calculating dot product x"); - - v = new Vec3(3, 2, 1); - u = new Vec3(4, 5, 6); - dot = v.dot(u); - - test.equal(dot, 12 + 10 + 6, "Calculating dot product x"); - - test.done(); - }, - - set : function(test) { - test.expect(3); - - var v = new Vec3(1, 2, 3); - v.set(4, 5, 6); - - test.equal(v.x, 4, "Setting values from x, y, z"); - test.equal(v.y, 5, "Setting values from x, y, z"); - test.equal(v.z, 6, "Setting values from x, y, z"); - - test.done(); - }, - - vadd : function(test) { - test.expect(3); - - var v = new Vec3(1, 2, 3); - var u = new Vec3(4, 5, 6); - v = v.vadd(u); - - test.equal(v.x, 5, "Adding a vector (x)"); - test.equal(v.y, 7, "Adding a vector (y)"); - test.equal(v.z, 9, "Adding a vector (z)"); - - test.done(); - }, - - isAntiparallelTo : function(test){ - test.ok(new Vec3(1,0,0).isAntiparallelTo(new Vec3(-1,0,0))); - test.done(); - }, - - almostEquals : function(test){ - test.ok(new Vec3(1,0,0).almostEquals(new Vec3(1,0,0))); - test.done(); - }, -}; diff --git a/test/World.js b/test/World.js deleted file mode 100644 index af5b215a3..000000000 --- a/test/World.js +++ /dev/null @@ -1,350 +0,0 @@ -var Vec3 = require("../src/math/Vec3"); -var Mat3 = require("../src/math/Mat3"); -var Quaternion = require("../src/math/Quaternion"); -var Box = require('../src/shapes/Box'); -var Body = require('../src/objects/Body'); -var Sphere = require('../src/shapes/Sphere'); -var World = require('../src/world/World'); -var NaiveBroadphase = require('../src/collision/NaiveBroadphase'); -var ArrayCollisionMatrix = require('../src/collision/ArrayCollisionMatrix'); -var ObjectCollisionMatrix = require('../src/collision/ObjectCollisionMatrix'); -var RaycastResult = require('../src/collision/RaycastResult'); - -module.exports = { - - clearForces: function(test){ - var world = new World(); - var body = new Body(); - world.addBody(body); - body.force.set(1,2,3); - body.torque.set(4,5,6); - - world.clearForces(); - - test.ok(body.force.almostEquals(new Vec3(0,0,0))); - test.ok(body.torque.almostEquals(new Vec3(0,0,0))); - - test.done(); - }, - - rayTestBox: function(test){ - var world = new World(); - - var body = new Body(); - body.addShape(new Box(new Vec3(1, 1, 1))); - world.addBody(body); - - var from = new Vec3(-10, 0, 0); - var to = new Vec3(10, 0, 0); - - var result = new RaycastResult(); - world.rayTest(from, to, result); - - test.equal(result.hasHit, true); - - test.done(); - }, - - rayTestSphere: function(test){ - var world = new World(); - - var body = new Body(); - body.addShape(new Sphere(1)); - world.addBody(body); - - var from = new Vec3(-10, 0, 0); - var to = new Vec3(10, 0, 0); - - var result = new RaycastResult(); - world.rayTest(from, to, result); - - test.equal(result.hasHit, true); - - test.done(); - }, - - raycastClosest: { - single: function(test){ - var world = new World(); - var body = new Body({ - shape: new Sphere(1) - }); - world.addBody(body); - - var from = new Vec3(-10, 0, 0); - var to = new Vec3(10, 0, 0); - - var result = new RaycastResult(); - world.raycastClosest(from, to, {}, result); - - test.equal(result.hasHit, true); - test.equal(result.body, body); - test.equal(result.shape, body.shapes[0]); - - test.done(); - }, - - order: function(test){ - var world = new World(); - var bodyA = new Body({ shape: new Sphere(1), position: new Vec3(-1,0,0) }); - var bodyB = new Body({ shape: new Sphere(1), position: new Vec3(1,0,0) }); - world.addBody(bodyA); - world.addBody(bodyB); - - var from = new Vec3(-10, 0, 0); - var to = new Vec3(10, 0, 0); - - var result = new RaycastResult(); - world.raycastClosest(from, to, {}, result); - - test.equal(result.hasHit, true); - test.equal(result.body, bodyA); - test.equal(result.shape, bodyA.shapes[0]); - - from.set(10, 0, 0); - to.set(-10, 0, 0); - - result = new RaycastResult(); - world.raycastClosest(from, to, {}, result); - - test.equal(result.hasHit, true); - test.equal(result.body, bodyB); - test.equal(result.shape, bodyB.shapes[0]); - - test.done(); - } - }, - - raycastAll: { - simple: function(test){ - var world = new World(); - var body = new Body({ shape: new Sphere(1) }); - world.addBody(body); - - var from = new Vec3(-10, 0, 0); - var to = new Vec3(10, 0, 0); - - var hasHit; - var numResults=0; - var resultBody; - var resultShape; - - var returnVal = world.raycastAll(from, to, {}, function (result){ - hasHit = result.hasHit; - resultShape = result.shape; - resultBody = result.body; - numResults++; - }); - - test.equal(returnVal, true, 'should return true on hit'); - test.equal(hasHit, true); - test.equal(resultBody, body); - test.equal(numResults, 2); - test.equal(resultShape, resultBody.shapes[0]); - - test.done(); - }, - - twoSpheres: function(test){ - - var world = new World(); - var body = new Body({ shape: new Sphere(1) }); - world.addBody(body); - - var body2 = new Body({ shape: new Sphere(1) }); - world.addBody(body2); - - var from = new Vec3(-10, 0, 0); - var to = new Vec3(10, 0, 0); - - var hasHit = false; - var numResults = 0; - var resultBody; - var resultShape; - - world.raycastAll(from, to, {}, function (result){ - hasHit = result.hasHit; - resultShape = result.shape; - resultBody = result.body; - numResults++; - }); - - test.equal(hasHit, true); - test.equal(numResults, 4); - - test.done(); - }, - - skipBackFaces: function(test){ - var world = new World(); - var body = new Body({ shape: new Sphere(1) }); - world.addBody(body); - - var hasHit = false; - var numResults = 0; - var resultBody; - var resultShape; - - world.raycastAll(new Vec3(-10, 0, 0), new Vec3(10, 0, 0), { skipBackfaces: true }, function (result){ - hasHit = result.hasHit; - resultShape = result.shape; - resultBody = result.body; - numResults++; - }); - - test.equal(hasHit, true); - test.equal(numResults, 1); - - test.done(); - }, - - collisionFilters: function(test){ - var world = new World(); - var body = new Body({ - shape: new Sphere(1) - }); - world.addBody(body); - body.collisionFilterGroup = 2; - body.collisionFilterMask = 2; - - var numResults = 0; - - world.raycastAll(new Vec3(-10, 0, 0), new Vec3(10, 0, 0), { - collisionFilterGroup: 2, - collisionFilterMask: 2 - }, function (result){ - numResults++; - }); - - test.equal(numResults, 2); - - numResults = 0; - - world.raycastAll(new Vec3(-10, 0, 0), new Vec3(10, 0, 0), { - collisionFilterGroup: 1, - collisionFilterMask: 1 - }, function (result){ - numResults++; - }); - - test.equal(numResults, 0, 'should use collision groups!'); - - test.done(); - } - }, - - raycastAny: function(test){ - var world = new World(); - world.addBody(new Body({ shape: new Sphere(1) })); - - var from = new Vec3(-10, 0, 0); - var to = new Vec3(10, 0, 0); - - var result = new RaycastResult(); - world.raycastAny(from, to, {}, result); - - test.ok(result.hasHit); - - test.done(); - }, - - collisionMatrix : function(test) { - function testCollisionMatrix(CollisionMatrix) { - var test_configs = [ - { - positions: [ - [0,0,0], - [2,0,0], - [0,4,0], - [2,4,0], - [0,8,0], - [2,8,0] - ], - colliding: { - '0-1':true, - '2-3':true, - '4-5':true - } - }, - { - positions: [ - [0,0,0], - [0,4,0], - [0,8,0], - [2,0,0], - [2,4,0], - [2,8,0] - ], - colliding: { - '0-3':true, - '1-4':true, - '2-5':true - } - }, - { - positions: [ - [ 0, 0, 0], - [ 0, 1, 0], - [ 0,10, 0], - [ 0,20, 0], - [ 0,30, 0], - [ 0,40, 0], - [ 0,50, 0], - [ 0,51, 0] - ], - colliding: { - '0-1':true, - '6-7':true - } - } - ]; - - for (var config_idx = 0 ; config_idx < test_configs.length; config_idx++) { - var test_config = test_configs[config_idx]; - - var world = new World(); - world.broadphase = new NaiveBroadphase(); - world.collisionMatrix = new CollisionMatrix(); - world.collisionMatrixPrevious = new CollisionMatrix(); - - for (var position_idx = 0; position_idx < test_config.positions.length; position_idx++) { - var body = new Body({ mass: 1 }); - body.addShape(new Sphere(1.1)); - body.position.set.apply(body.position, test_config.positions[position_idx]); - world.addBody(body); - } - - for (var step_idx = 0; step_idx < 2; step_idx++) { - world.step(0.1); - var is_first_step = (step_idx === 0); - - for (var coll_i = 0; coll_i < world.bodies.length; coll_i++) { - for (var coll_j = coll_i + 1; coll_j < world.bodies.length; coll_j++) { - var is_colliding_pair = test_config.colliding[coll_i+'-'+coll_j] === true; - var expected = is_colliding_pair; - var is_colliding = is_first_step ? - !!world.collisionMatrix.get(world.bodies[coll_i], world.bodies[coll_j]) : - !!world.collisionMatrixPrevious.get(world.bodies[coll_i], world.bodies[coll_j]); - test.ok(is_colliding === expected, - (expected ? "Should be colliding" : "Should not be colliding") + - ': cfg=' + config_idx + - ' is_first_step=' + is_first_step + - ' is_colliding_pair=' + is_colliding_pair + - ' expected=' + expected + - ' is_colliding=' + is_colliding + - ' i=' + coll_i + - ' j=' + coll_j); - } - } - } - } - } - - testCollisionMatrix(ArrayCollisionMatrix); - testCollisionMatrix(ObjectCollisionMatrix); - - test.done(); - }, - - -}; diff --git a/test/index.spec.ts b/test/index.spec.ts new file mode 100644 index 000000000..53fddfa3b --- /dev/null +++ b/test/index.spec.ts @@ -0,0 +1,11 @@ +/* eslint-disable camelcase */ +/* eslint-disable func-style */ +import { ok } from 'assert'; + +describe('test', () => +{ + it('test', () => + { + ok(true); + }); +}); diff --git a/tests/index.html b/tests/index.html new file mode 100644 index 000000000..56a76f832 --- /dev/null +++ b/tests/index.html @@ -0,0 +1,23 @@ + + + + + feng3d / tests + + + + + + + + + + + + + +
    +
    + + + \ No newline at end of file diff --git a/tests/libs/qunit/qunit-2.5.0.css b/tests/libs/qunit/qunit-2.5.0.css new file mode 100644 index 000000000..05f5a5b81 --- /dev/null +++ b/tests/libs/qunit/qunit-2.5.0.css @@ -0,0 +1,436 @@ +/*! + * QUnit 2.5.0 + * https://qunitjs.com/ + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2018-01-10T02:56Z + */ + +/** Font Family and Sizes */ + +#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult { + font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; +} + +#qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } +#qunit-tests { font-size: smaller; } + + +/** Resets */ + +#qunit-tests, #qunit-header, #qunit-banner, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter { + margin: 0; + padding: 0; +} + + +/** Header (excluding toolbar) */ + +#qunit-header { + padding: 0.5em 0 0.5em 1em; + + color: #8699A4; + background-color: #0D3349; + + font-size: 1.5em; + line-height: 1em; + font-weight: 400; + + border-radius: 5px 5px 0 0; +} + +#qunit-header a { + text-decoration: none; + color: #C2CCD1; +} + +#qunit-header a:hover, +#qunit-header a:focus { + color: #FFF; +} + +#qunit-banner { + height: 5px; +} + +#qunit-filteredTest { + padding: 0.5em 1em 0.5em 1em; + color: #366097; + background-color: #F4FF77; +} + +#qunit-userAgent { + padding: 0.5em 1em 0.5em 1em; + color: #FFF; + background-color: #2B81AF; + text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; +} + + +/** Toolbar */ + +#qunit-testrunner-toolbar { + padding: 0.5em 1em 0.5em 1em; + color: #5E740B; + background-color: #EEE; +} + +#qunit-testrunner-toolbar .clearfix { + height: 0; + clear: both; +} + +#qunit-testrunner-toolbar label { + display: inline-block; +} + +#qunit-testrunner-toolbar input[type=checkbox], +#qunit-testrunner-toolbar input[type=radio] { + margin: 3px; + vertical-align: -2px; +} + +#qunit-testrunner-toolbar input[type=text] { + box-sizing: border-box; + height: 1.6em; +} + +.qunit-url-config, +.qunit-filter, +#qunit-modulefilter { + display: inline-block; + line-height: 2.1em; +} + +.qunit-filter, +#qunit-modulefilter { + float: right; + position: relative; + margin-left: 1em; +} + +.qunit-url-config label { + margin-right: 0.5em; +} + +#qunit-modulefilter-search { + box-sizing: border-box; + width: 400px; +} + +#qunit-modulefilter-search-container:after { + position: absolute; + right: 0.3em; + content: "\25bc"; + color: black; +} + +#qunit-modulefilter-dropdown { + /* align with #qunit-modulefilter-search */ + box-sizing: border-box; + width: 400px; + position: absolute; + right: 0; + top: 50%; + margin-top: 0.8em; + + border: 1px solid #D3D3D3; + border-top: none; + border-radius: 0 0 .25em .25em; + color: #000; + background-color: #F5F5F5; + z-index: 99; +} + +#qunit-modulefilter-dropdown a { + color: inherit; + text-decoration: none; +} + +#qunit-modulefilter-dropdown .clickable.checked { + font-weight: bold; + color: #000; + background-color: #D2E0E6; +} + +#qunit-modulefilter-dropdown .clickable:hover { + color: #FFF; + background-color: #0D3349; +} + +#qunit-modulefilter-actions { + display: block; + overflow: auto; + + /* align with #qunit-modulefilter-dropdown-list */ + font: smaller/1.5em sans-serif; +} + +#qunit-modulefilter-dropdown #qunit-modulefilter-actions > * { + box-sizing: border-box; + max-height: 2.8em; + display: block; + padding: 0.4em; +} + +#qunit-modulefilter-dropdown #qunit-modulefilter-actions > button { + float: right; + font: inherit; +} + +#qunit-modulefilter-dropdown #qunit-modulefilter-actions > :last-child { + /* insert padding to align with checkbox margins */ + padding-left: 3px; +} + +#qunit-modulefilter-dropdown-list { + max-height: 200px; + overflow-y: auto; + margin: 0; + border-top: 2px groove threedhighlight; + padding: 0.4em 0 0; + font: smaller/1.5em sans-serif; +} + +#qunit-modulefilter-dropdown-list li { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +#qunit-modulefilter-dropdown-list .clickable { + display: block; + padding-left: 0.15em; +} + + +/** Tests: Pass/Fail */ + +#qunit-tests { + list-style-position: inside; +} + +#qunit-tests li { + padding: 0.4em 1em 0.4em 1em; + border-bottom: 1px solid #FFF; + list-style-position: inside; +} + +#qunit-tests > li { + display: none; +} + +#qunit-tests li.running, +#qunit-tests li.pass, +#qunit-tests li.fail, +#qunit-tests li.skipped, +#qunit-tests li.aborted { + display: list-item; +} + +#qunit-tests.hidepass { + position: relative; +} + +#qunit-tests.hidepass li.running, +#qunit-tests.hidepass li.pass:not(.todo) { + visibility: hidden; + position: absolute; + width: 0; + height: 0; + padding: 0; + border: 0; + margin: 0; +} + +#qunit-tests li strong { + cursor: pointer; +} + +#qunit-tests li.skipped strong { + cursor: default; +} + +#qunit-tests li a { + padding: 0.5em; + color: #C2CCD1; + text-decoration: none; +} + +#qunit-tests li p a { + padding: 0.25em; + color: #6B6464; +} +#qunit-tests li a:hover, +#qunit-tests li a:focus { + color: #000; +} + +#qunit-tests li .runtime { + float: right; + font-size: smaller; +} + +.qunit-assert-list { + margin-top: 0.5em; + padding: 0.5em; + + background-color: #FFF; + + border-radius: 5px; +} + +.qunit-source { + margin: 0.6em 0 0.3em; +} + +.qunit-collapsed { + display: none; +} + +#qunit-tests table { + border-collapse: collapse; + margin-top: 0.2em; +} + +#qunit-tests th { + text-align: right; + vertical-align: top; + padding: 0 0.5em 0 0; +} + +#qunit-tests td { + vertical-align: top; +} + +#qunit-tests pre { + margin: 0; + white-space: pre-wrap; + word-wrap: break-word; +} + +#qunit-tests del { + color: #374E0C; + background-color: #E0F2BE; + text-decoration: none; +} + +#qunit-tests ins { + color: #500; + background-color: #FFCACA; + text-decoration: none; +} + +/*** Test Counts */ + +#qunit-tests b.counts { color: #000; } +#qunit-tests b.passed { color: #5E740B; } +#qunit-tests b.failed { color: #710909; } + +#qunit-tests li li { + padding: 5px; + background-color: #FFF; + border-bottom: none; + list-style-position: inside; +} + +/*** Passing Styles */ + +#qunit-tests li li.pass { + color: #3C510C; + background-color: #FFF; + border-left: 10px solid #C6E746; +} + +#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } +#qunit-tests .pass .test-name { color: #366097; } + +#qunit-tests .pass .test-actual, +#qunit-tests .pass .test-expected { color: #999; } + +#qunit-banner.qunit-pass { background-color: #C6E746; } + +/*** Failing Styles */ + +#qunit-tests li li.fail { + color: #710909; + background-color: #FFF; + border-left: 10px solid #EE5757; + white-space: pre; +} + +#qunit-tests > li:last-child { + border-radius: 0 0 5px 5px; +} + +#qunit-tests .fail { color: #000; background-color: #EE5757; } +#qunit-tests .fail .test-name, +#qunit-tests .fail .module-name { color: #000; } + +#qunit-tests .fail .test-actual { color: #EE5757; } +#qunit-tests .fail .test-expected { color: #008000; } + +#qunit-banner.qunit-fail { background-color: #EE5757; } + + +/*** Aborted tests */ +#qunit-tests .aborted { color: #000; background-color: orange; } +/*** Skipped tests */ + +#qunit-tests .skipped { + background-color: #EBECE9; +} + +#qunit-tests .qunit-todo-label, +#qunit-tests .qunit-skipped-label { + background-color: #F4FF77; + display: inline-block; + font-style: normal; + color: #366097; + line-height: 1.8em; + padding: 0 0.5em; + margin: -0.4em 0.4em -0.4em 0; +} + +#qunit-tests .qunit-todo-label { + background-color: #EEE; +} + +/** Result */ + +#qunit-testresult { + color: #2B81AF; + background-color: #D2E0E6; + + border-bottom: 1px solid #FFF; +} +#qunit-testresult .clearfix { + height: 0; + clear: both; +} +#qunit-testresult .module-name { + font-weight: 700; +} +#qunit-testresult-display { + padding: 0.5em 1em 0.5em 1em; + width: 85%; + float:left; +} +#qunit-testresult-controls { + padding: 0.5em 1em 0.5em 1em; + width: 10%; + float:left; +} + +/** Fixture */ + +#qunit-fixture { + position: absolute; + top: -10000px; + left: -10000px; + width: 1000px; + height: 1000px; +} \ No newline at end of file diff --git a/tests/libs/qunit/qunit-2.5.0.js b/tests/libs/qunit/qunit-2.5.0.js new file mode 100644 index 000000000..642df13b7 --- /dev/null +++ b/tests/libs/qunit/qunit-2.5.0.js @@ -0,0 +1,5188 @@ +/*! + * QUnit 2.5.0 + * https://qunitjs.com/ + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2018-01-10T02:56Z + */ +(function (global$1) { + 'use strict'; + + global$1 = global$1 && global$1.hasOwnProperty('default') ? global$1['default'] : global$1; + + var window = global$1.window; + var self$1 = global$1.self; + var console = global$1.console; + var setTimeout = global$1.setTimeout; + var clearTimeout = global$1.clearTimeout; + + var document = window && window.document; + var navigator = window && window.navigator; + + var localSessionStorage = function () { + var x = "qunit-test-string"; + try { + global$1.sessionStorage.setItem(x, x); + global$1.sessionStorage.removeItem(x); + return global$1.sessionStorage; + } catch (e) { + return undefined; + } + }(); + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { + return typeof obj; + } : function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; + }; + + + + + + + + + + + + var classCallCheck = function (instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + }; + + var createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; + }(); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + var toConsumableArray = function (arr) { + if (Array.isArray(arr)) { + for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; + + return arr2; + } else { + return Array.from(arr); + } + }; + + var toString = Object.prototype.toString; + var hasOwn = Object.prototype.hasOwnProperty; + var now = Date.now || function () { + return new Date().getTime(); + }; + + var defined = { + document: window && window.document !== undefined, + setTimeout: setTimeout !== undefined + }; + + // Returns a new Array with the elements that are in a but not in b + function diff(a, b) { + var i, + j, + result = a.slice(); + + for (i = 0; i < result.length; i++) { + for (j = 0; j < b.length; j++) { + if (result[i] === b[j]) { + result.splice(i, 1); + i--; + break; + } + } + } + return result; + } + + /** + * Determines whether an element exists in a given array or not. + * + * @method inArray + * @param {Any} elem + * @param {Array} array + * @return {Boolean} + */ + function inArray(elem, array) { + return array.indexOf(elem) !== -1; + } + + /** + * Makes a clone of an object using only Array or Object as base, + * and copies over the own enumerable properties. + * + * @param {Object} obj + * @return {Object} New object with only the own properties (recursively). + */ + function objectValues(obj) { + var key, + val, + vals = is("array", obj) ? [] : {}; + for (key in obj) { + if (hasOwn.call(obj, key)) { + val = obj[key]; + vals[key] = val === Object(val) ? objectValues(val) : val; + } + } + return vals; + } + + function extend(a, b, undefOnly) { + for (var prop in b) { + if (hasOwn.call(b, prop)) { + if (b[prop] === undefined) { + delete a[prop]; + } else if (!(undefOnly && typeof a[prop] !== "undefined")) { + a[prop] = b[prop]; + } + } + } + + return a; + } + + function objectType(obj) { + if (typeof obj === "undefined") { + return "undefined"; + } + + // Consider: typeof null === object + if (obj === null) { + return "null"; + } + + var match = toString.call(obj).match(/^\[object\s(.*)\]$/), + type = match && match[1]; + + switch (type) { + case "Number": + if (isNaN(obj)) { + return "nan"; + } + return "number"; + case "String": + case "Boolean": + case "Array": + case "Set": + case "Map": + case "Date": + case "RegExp": + case "Function": + case "Symbol": + return type.toLowerCase(); + default: + return typeof obj === "undefined" ? "undefined" : _typeof(obj); + } + } + + // Safe object type checking + function is(type, obj) { + return objectType(obj) === type; + } + + // Based on Java's String.hashCode, a simple but not + // rigorously collision resistant hashing function + function generateHash(module, testName) { + var str = module + "\x1C" + testName; + var hash = 0; + + for (var i = 0; i < str.length; i++) { + hash = (hash << 5) - hash + str.charCodeAt(i); + hash |= 0; + } + + // Convert the possibly negative integer hash code into an 8 character hex string, which isn't + // strictly necessary but increases user understanding that the id is a SHA-like hash + var hex = (0x100000000 + hash).toString(16); + if (hex.length < 8) { + hex = "0000000" + hex; + } + + return hex.slice(-8); + } + + // Test for equality any JavaScript type. + // Authors: Philippe Rathé , David Chan + var equiv = (function () { + + // Value pairs queued for comparison. Used for breadth-first processing order, recursion + // detection and avoiding repeated comparison (see below for details). + // Elements are { a: val, b: val }. + var pairs = []; + + var getProto = Object.getPrototypeOf || function (obj) { + return obj.__proto__; + }; + + function useStrictEquality(a, b) { + + // This only gets called if a and b are not strict equal, and is used to compare on + // the primitive values inside object wrappers. For example: + // `var i = 1;` + // `var j = new Number(1);` + // Neither a nor b can be null, as a !== b and they have the same type. + if ((typeof a === "undefined" ? "undefined" : _typeof(a)) === "object") { + a = a.valueOf(); + } + if ((typeof b === "undefined" ? "undefined" : _typeof(b)) === "object") { + b = b.valueOf(); + } + + return a === b; + } + + function compareConstructors(a, b) { + var protoA = getProto(a); + var protoB = getProto(b); + + // Comparing constructors is more strict than using `instanceof` + if (a.constructor === b.constructor) { + return true; + } + + // Ref #851 + // If the obj prototype descends from a null constructor, treat it + // as a null prototype. + if (protoA && protoA.constructor === null) { + protoA = null; + } + if (protoB && protoB.constructor === null) { + protoB = null; + } + + // Allow objects with no prototype to be equivalent to + // objects with Object as their constructor. + if (protoA === null && protoB === Object.prototype || protoB === null && protoA === Object.prototype) { + return true; + } + + return false; + } + + function getRegExpFlags(regexp) { + return "flags" in regexp ? regexp.flags : regexp.toString().match(/[gimuy]*$/)[0]; + } + + function isContainer(val) { + return ["object", "array", "map", "set"].indexOf(objectType(val)) !== -1; + } + + function breadthFirstCompareChild(a, b) { + + // If a is a container not reference-equal to b, postpone the comparison to the + // end of the pairs queue -- unless (a, b) has been seen before, in which case skip + // over the pair. + if (a === b) { + return true; + } + if (!isContainer(a)) { + return typeEquiv(a, b); + } + if (pairs.every(function (pair) { + return pair.a !== a || pair.b !== b; + })) { + + // Not yet started comparing this pair + pairs.push({ a: a, b: b }); + } + return true; + } + + var callbacks = { + "string": useStrictEquality, + "boolean": useStrictEquality, + "number": useStrictEquality, + "null": useStrictEquality, + "undefined": useStrictEquality, + "symbol": useStrictEquality, + "date": useStrictEquality, + + "nan": function nan() { + return true; + }, + + "regexp": function regexp(a, b) { + return a.source === b.source && + + // Include flags in the comparison + getRegExpFlags(a) === getRegExpFlags(b); + }, + + // abort (identical references / instance methods were skipped earlier) + "function": function _function() { + return false; + }, + + "array": function array(a, b) { + var i, len; + + len = a.length; + if (len !== b.length) { + + // Safe and faster + return false; + } + + for (i = 0; i < len; i++) { + + // Compare non-containers; queue non-reference-equal containers + if (!breadthFirstCompareChild(a[i], b[i])) { + return false; + } + } + return true; + }, + + // Define sets a and b to be equivalent if for each element aVal in a, there + // is some element bVal in b such that aVal and bVal are equivalent. Element + // repetitions are not counted, so these are equivalent: + // a = new Set( [ {}, [], [] ] ); + // b = new Set( [ {}, {}, [] ] ); + "set": function set$$1(a, b) { + var innerEq, + outerEq = true; + + if (a.size !== b.size) { + + // This optimization has certain quirks because of the lack of + // repetition counting. For instance, adding the same + // (reference-identical) element to two equivalent sets can + // make them non-equivalent. + return false; + } + + a.forEach(function (aVal) { + + // Short-circuit if the result is already known. (Using for...of + // with a break clause would be cleaner here, but it would cause + // a syntax error on older Javascript implementations even if + // Set is unused) + if (!outerEq) { + return; + } + + innerEq = false; + + b.forEach(function (bVal) { + var parentPairs; + + // Likewise, short-circuit if the result is already known + if (innerEq) { + return; + } + + // Swap out the global pairs list, as the nested call to + // innerEquiv will clobber its contents + parentPairs = pairs; + if (innerEquiv(bVal, aVal)) { + innerEq = true; + } + + // Replace the global pairs list + pairs = parentPairs; + }); + + if (!innerEq) { + outerEq = false; + } + }); + + return outerEq; + }, + + // Define maps a and b to be equivalent if for each key-value pair (aKey, aVal) + // in a, there is some key-value pair (bKey, bVal) in b such that + // [ aKey, aVal ] and [ bKey, bVal ] are equivalent. Key repetitions are not + // counted, so these are equivalent: + // a = new Map( [ [ {}, 1 ], [ {}, 1 ], [ [], 1 ] ] ); + // b = new Map( [ [ {}, 1 ], [ [], 1 ], [ [], 1 ] ] ); + "map": function map(a, b) { + var innerEq, + outerEq = true; + + if (a.size !== b.size) { + + // This optimization has certain quirks because of the lack of + // repetition counting. For instance, adding the same + // (reference-identical) key-value pair to two equivalent maps + // can make them non-equivalent. + return false; + } + + a.forEach(function (aVal, aKey) { + + // Short-circuit if the result is already known. (Using for...of + // with a break clause would be cleaner here, but it would cause + // a syntax error on older Javascript implementations even if + // Map is unused) + if (!outerEq) { + return; + } + + innerEq = false; + + b.forEach(function (bVal, bKey) { + var parentPairs; + + // Likewise, short-circuit if the result is already known + if (innerEq) { + return; + } + + // Swap out the global pairs list, as the nested call to + // innerEquiv will clobber its contents + parentPairs = pairs; + if (innerEquiv([bVal, bKey], [aVal, aKey])) { + innerEq = true; + } + + // Replace the global pairs list + pairs = parentPairs; + }); + + if (!innerEq) { + outerEq = false; + } + }); + + return outerEq; + }, + + "object": function object(a, b) { + var i, + aProperties = [], + bProperties = []; + + if (compareConstructors(a, b) === false) { + return false; + } + + // Be strict: don't ensure hasOwnProperty and go deep + for (i in a) { + + // Collect a's properties + aProperties.push(i); + + // Skip OOP methods that look the same + if (a.constructor !== Object && typeof a.constructor !== "undefined" && typeof a[i] === "function" && typeof b[i] === "function" && a[i].toString() === b[i].toString()) { + continue; + } + + // Compare non-containers; queue non-reference-equal containers + if (!breadthFirstCompareChild(a[i], b[i])) { + return false; + } + } + + for (i in b) { + + // Collect b's properties + bProperties.push(i); + } + + // Ensures identical properties name + return typeEquiv(aProperties.sort(), bProperties.sort()); + } + }; + + function typeEquiv(a, b) { + var type = objectType(a); + + // Callbacks for containers will append to the pairs queue to achieve breadth-first + // search order. The pairs queue is also used to avoid reprocessing any pair of + // containers that are reference-equal to a previously visited pair (a special case + // this being recursion detection). + // + // Because of this approach, once typeEquiv returns a false value, it should not be + // called again without clearing the pair queue else it may wrongly report a visited + // pair as being equivalent. + return objectType(b) === type && callbacks[type](a, b); + } + + function innerEquiv(a, b) { + var i, pair; + + // We're done when there's nothing more to compare + if (arguments.length < 2) { + return true; + } + + // Clear the global pair queue and add the top-level values being compared + pairs = [{ a: a, b: b }]; + + for (i = 0; i < pairs.length; i++) { + pair = pairs[i]; + + // Perform type-specific comparison on any pairs that are not strictly + // equal. For container types, that comparison will postpone comparison + // of any sub-container pair to the end of the pair queue. This gives + // breadth-first search order. It also avoids the reprocessing of + // reference-equal siblings, cousins etc, which can have a significant speed + // impact when comparing a container of small objects each of which has a + // reference to the same (singleton) large object. + if (pair.a !== pair.b && !typeEquiv(pair.a, pair.b)) { + return false; + } + } + + // ...across all consecutive argument pairs + return arguments.length === 2 || innerEquiv.apply(this, [].slice.call(arguments, 1)); + } + + return function () { + var result = innerEquiv.apply(undefined, arguments); + + // Release any retained objects + pairs.length = 0; + return result; + }; + })(); + + /** + * Config object: Maintain internal state + * Later exposed as QUnit.config + * `config` initialized at top of scope + */ + var config = { + + // The queue of tests to run + queue: [], + + // Block until document ready + blocking: true, + + // By default, run previously failed tests first + // very useful in combination with "Hide passed tests" checked + reorder: true, + + // By default, modify document.title when suite is done + altertitle: true, + + // HTML Reporter: collapse every test except the first failing test + // If false, all failing tests will be expanded + collapse: true, + + // By default, scroll to top of the page when suite is done + scrolltop: true, + + // Depth up-to which object will be dumped + maxDepth: 5, + + // When enabled, all tests must call expect() + requireExpects: false, + + // Placeholder for user-configurable form-exposed URL parameters + urlConfig: [], + + // Set of all modules. + modules: [], + + // The first unnamed module + currentModule: { + name: "", + tests: [], + childModules: [], + testsRun: 0, + unskippedTestsRun: 0, + hooks: { + before: [], + beforeEach: [], + afterEach: [], + after: [] + } + }, + + callbacks: {}, + + // The storage module to use for reordering tests + storage: localSessionStorage + }; + + // take a predefined QUnit.config and extend the defaults + var globalConfig = window && window.QUnit && window.QUnit.config; + + // only extend the global config if there is no QUnit overload + if (window && window.QUnit && !window.QUnit.version) { + extend(config, globalConfig); + } + + // Push a loose unnamed module to the modules collection + config.modules.push(config.currentModule); + + // Based on jsDump by Ariel Flesler + // http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html + var dump = (function () { + function quote(str) { + return "\"" + str.toString().replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\""; + } + function literal(o) { + return o + ""; + } + function join(pre, arr, post) { + var s = dump.separator(), + base = dump.indent(), + inner = dump.indent(1); + if (arr.join) { + arr = arr.join("," + s + inner); + } + if (!arr) { + return pre + post; + } + return [pre, inner + arr, base + post].join(s); + } + function array(arr, stack) { + var i = arr.length, + ret = new Array(i); + + if (dump.maxDepth && dump.depth > dump.maxDepth) { + return "[object Array]"; + } + + this.up(); + while (i--) { + ret[i] = this.parse(arr[i], undefined, stack); + } + this.down(); + return join("[", ret, "]"); + } + + function isArray(obj) { + return ( + + //Native Arrays + toString.call(obj) === "[object Array]" || + + // NodeList objects + typeof obj.length === "number" && obj.item !== undefined && (obj.length ? obj.item(0) === obj[0] : obj.item(0) === null && obj[0] === undefined) + ); + } + + var reName = /^function (\w+)/, + dump = { + + // The objType is used mostly internally, you can fix a (custom) type in advance + parse: function parse(obj, objType, stack) { + stack = stack || []; + var res, + parser, + parserType, + objIndex = stack.indexOf(obj); + + if (objIndex !== -1) { + return "recursion(" + (objIndex - stack.length) + ")"; + } + + objType = objType || this.typeOf(obj); + parser = this.parsers[objType]; + parserType = typeof parser === "undefined" ? "undefined" : _typeof(parser); + + if (parserType === "function") { + stack.push(obj); + res = parser.call(this, obj, stack); + stack.pop(); + return res; + } + return parserType === "string" ? parser : this.parsers.error; + }, + typeOf: function typeOf(obj) { + var type; + + if (obj === null) { + type = "null"; + } else if (typeof obj === "undefined") { + type = "undefined"; + } else if (is("regexp", obj)) { + type = "regexp"; + } else if (is("date", obj)) { + type = "date"; + } else if (is("function", obj)) { + type = "function"; + } else if (obj.setInterval !== undefined && obj.document !== undefined && obj.nodeType === undefined) { + type = "window"; + } else if (obj.nodeType === 9) { + type = "document"; + } else if (obj.nodeType) { + type = "node"; + } else if (isArray(obj)) { + type = "array"; + } else if (obj.constructor === Error.prototype.constructor) { + type = "error"; + } else { + type = typeof obj === "undefined" ? "undefined" : _typeof(obj); + } + return type; + }, + + separator: function separator() { + if (this.multiline) { + return this.HTML ? "
    " : "\n"; + } else { + return this.HTML ? " " : " "; + } + }, + + // Extra can be a number, shortcut for increasing-calling-decreasing + indent: function indent(extra) { + if (!this.multiline) { + return ""; + } + var chr = this.indentChar; + if (this.HTML) { + chr = chr.replace(/\t/g, " ").replace(/ /g, " "); + } + return new Array(this.depth + (extra || 0)).join(chr); + }, + up: function up(a) { + this.depth += a || 1; + }, + down: function down(a) { + this.depth -= a || 1; + }, + setParser: function setParser(name, parser) { + this.parsers[name] = parser; + }, + + // The next 3 are exposed so you can use them + quote: quote, + literal: literal, + join: join, + depth: 1, + maxDepth: config.maxDepth, + + // This is the list of parsers, to modify them, use dump.setParser + parsers: { + window: "[Window]", + document: "[Document]", + error: function error(_error) { + return "Error(\"" + _error.message + "\")"; + }, + unknown: "[Unknown]", + "null": "null", + "undefined": "undefined", + "function": function _function(fn) { + var ret = "function", + + + // Functions never have name in IE + name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1]; + + if (name) { + ret += " " + name; + } + ret += "("; + + ret = [ret, dump.parse(fn, "functionArgs"), "){"].join(""); + return join(ret, dump.parse(fn, "functionCode"), "}"); + }, + array: array, + nodelist: array, + "arguments": array, + object: function object(map, stack) { + var keys, + key, + val, + i, + nonEnumerableProperties, + ret = []; + + if (dump.maxDepth && dump.depth > dump.maxDepth) { + return "[object Object]"; + } + + dump.up(); + keys = []; + for (key in map) { + keys.push(key); + } + + // Some properties are not always enumerable on Error objects. + nonEnumerableProperties = ["message", "name"]; + for (i in nonEnumerableProperties) { + key = nonEnumerableProperties[i]; + if (key in map && !inArray(key, keys)) { + keys.push(key); + } + } + keys.sort(); + for (i = 0; i < keys.length; i++) { + key = keys[i]; + val = map[key]; + ret.push(dump.parse(key, "key") + ": " + dump.parse(val, undefined, stack)); + } + dump.down(); + return join("{", ret, "}"); + }, + node: function node(_node) { + var len, + i, + val, + open = dump.HTML ? "<" : "<", + close = dump.HTML ? ">" : ">", + tag = _node.nodeName.toLowerCase(), + ret = open + tag, + attrs = _node.attributes; + + if (attrs) { + for (i = 0, len = attrs.length; i < len; i++) { + val = attrs[i].nodeValue; + + // IE6 includes all attributes in .attributes, even ones not explicitly + // set. Those have values like undefined, null, 0, false, "" or + // "inherit". + if (val && val !== "inherit") { + ret += " " + attrs[i].nodeName + "=" + dump.parse(val, "attribute"); + } + } + } + ret += close; + + // Show content of TextNode or CDATASection + if (_node.nodeType === 3 || _node.nodeType === 4) { + ret += _node.nodeValue; + } + + return ret + open + "/" + tag + close; + }, + + // Function calls it internally, it's the arguments part of the function + functionArgs: function functionArgs(fn) { + var args, + l = fn.length; + + if (!l) { + return ""; + } + + args = new Array(l); + while (l--) { + + // 97 is 'a' + args[l] = String.fromCharCode(97 + l); + } + return " " + args.join(", ") + " "; + }, + + // Object calls it internally, the key part of an item in a map + key: quote, + + // Function calls it internally, it's the content of the function + functionCode: "[code]", + + // Node calls it internally, it's a html attribute value + attribute: quote, + string: quote, + date: quote, + regexp: literal, + number: literal, + "boolean": literal, + symbol: function symbol(sym) { + return sym.toString(); + } + }, + + // If true, entities are escaped ( <, >, \t, space and \n ) + HTML: false, + + // Indentation unit + indentChar: " ", + + // If true, items in a collection, are separated by a \n, else just a space. + multiline: true + }; + + return dump; + })(); + + var LISTENERS = Object.create(null); + var SUPPORTED_EVENTS = ["runStart", "suiteStart", "testStart", "assertion", "testEnd", "suiteEnd", "runEnd"]; + + /** + * Emits an event with the specified data to all currently registered listeners. + * Callbacks will fire in the order in which they are registered (FIFO). This + * function is not exposed publicly; it is used by QUnit internals to emit + * logging events. + * + * @private + * @method emit + * @param {String} eventName + * @param {Object} data + * @return {Void} + */ + function emit(eventName, data) { + if (objectType(eventName) !== "string") { + throw new TypeError("eventName must be a string when emitting an event"); + } + + // Clone the callbacks in case one of them registers a new callback + var originalCallbacks = LISTENERS[eventName]; + var callbacks = originalCallbacks ? [].concat(toConsumableArray(originalCallbacks)) : []; + + for (var i = 0; i < callbacks.length; i++) { + callbacks[i](data); + } + } + + /** + * Registers a callback as a listener to the specified event. + * + * @public + * @method on + * @param {String} eventName + * @param {Function} callback + * @return {Void} + */ + function on(eventName, callback) { + if (objectType(eventName) !== "string") { + throw new TypeError("eventName must be a string when registering a listener"); + } else if (!inArray(eventName, SUPPORTED_EVENTS)) { + var events = SUPPORTED_EVENTS.join(", "); + throw new Error("\"" + eventName + "\" is not a valid event; must be one of: " + events + "."); + } else if (objectType(callback) !== "function") { + throw new TypeError("callback must be a function when registering a listener"); + } + + if (!LISTENERS[eventName]) { + LISTENERS[eventName] = []; + } + + // Don't register the same callback more than once + if (!inArray(callback, LISTENERS[eventName])) { + LISTENERS[eventName].push(callback); + } + } + + // Register logging callbacks + function registerLoggingCallbacks(obj) { + var i, + l, + key, + callbackNames = ["begin", "done", "log", "testStart", "testDone", "moduleStart", "moduleDone"]; + + function registerLoggingCallback(key) { + var loggingCallback = function loggingCallback(callback) { + if (objectType(callback) !== "function") { + throw new Error("QUnit logging methods require a callback function as their first parameters."); + } + + config.callbacks[key].push(callback); + }; + + return loggingCallback; + } + + for (i = 0, l = callbackNames.length; i < l; i++) { + key = callbackNames[i]; + + // Initialize key collection of logging callback + if (objectType(config.callbacks[key]) === "undefined") { + config.callbacks[key] = []; + } + + obj[key] = registerLoggingCallback(key); + } + } + + function runLoggingCallbacks(key, args) { + var i, l, callbacks; + + callbacks = config.callbacks[key]; + for (i = 0, l = callbacks.length; i < l; i++) { + callbacks[i](args); + } + } + + // Doesn't support IE9, it will return undefined on these browsers + // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack + var fileName = (sourceFromStacktrace(0) || "").replace(/(:\d+)+\)?/, "").replace(/.+\//, ""); + + function extractStacktrace(e, offset) { + offset = offset === undefined ? 4 : offset; + + var stack, include, i; + + if (e && e.stack) { + stack = e.stack.split("\n"); + if (/^error$/i.test(stack[0])) { + stack.shift(); + } + if (fileName) { + include = []; + for (i = offset; i < stack.length; i++) { + if (stack[i].indexOf(fileName) !== -1) { + break; + } + include.push(stack[i]); + } + if (include.length) { + return include.join("\n"); + } + } + return stack[offset]; + } + } + + function sourceFromStacktrace(offset) { + var error = new Error(); + + // Support: Safari <=7 only, IE <=10 - 11 only + // Not all browsers generate the `stack` property for `new Error()`, see also #636 + if (!error.stack) { + try { + throw error; + } catch (err) { + error = err; + } + } + + return extractStacktrace(error, offset); + } + + var priorityCount = 0; + var unitSampler = void 0; + + /** + * Advances the ProcessingQueue to the next item if it is ready. + * @param {Boolean} last + */ + function advance() { + var start = now(); + config.depth = (config.depth || 0) + 1; + + while (config.queue.length && !config.blocking) { + var elapsedTime = now() - start; + + if (!defined.setTimeout || config.updateRate <= 0 || elapsedTime < config.updateRate) { + if (priorityCount > 0) { + priorityCount--; + } + + config.queue.shift()(); + } else { + setTimeout(advance); + break; + } + } + + config.depth--; + + if (!config.blocking && !config.queue.length && config.depth === 0) { + done(); + } + } + + function addToQueueImmediate(callback) { + if (objectType(callback) === "array") { + while (callback.length) { + addToQueueImmediate(callback.pop()); + } + + return; + } + + config.queue.unshift(callback); + priorityCount++; + } + + /** + * Adds a function to the ProcessingQueue for execution. + * @param {Function|Array} callback + * @param {Boolean} priority + * @param {String} seed + */ + function addToQueue(callback, prioritize, seed) { + if (prioritize) { + config.queue.splice(priorityCount++, 0, callback); + } else if (seed) { + if (!unitSampler) { + unitSampler = unitSamplerGenerator(seed); + } + + // Insert into a random position after all prioritized items + var index = Math.floor(unitSampler() * (config.queue.length - priorityCount + 1)); + config.queue.splice(priorityCount + index, 0, callback); + } else { + config.queue.push(callback); + } + } + + /** + * Creates a seeded "sample" generator which is used for randomizing tests. + */ + function unitSamplerGenerator(seed) { + + // 32-bit xorshift, requires only a nonzero seed + // http://excamera.com/sphinx/article-xorshift.html + var sample = parseInt(generateHash(seed), 16) || -1; + return function () { + sample ^= sample << 13; + sample ^= sample >>> 17; + sample ^= sample << 5; + + // ECMAScript has no unsigned number type + if (sample < 0) { + sample += 0x100000000; + } + + return sample / 0x100000000; + }; + } + + /** + * This function is called when the ProcessingQueue is done processing all + * items. It handles emitting the final run events. + */ + function done() { + var storage = config.storage; + + ProcessingQueue.finished = true; + + var runtime = now() - config.started; + var passed = config.stats.all - config.stats.bad; + + emit("runEnd", globalSuite.end(true)); + runLoggingCallbacks("done", { + passed: passed, + failed: config.stats.bad, + total: config.stats.all, + runtime: runtime + }); + + // Clear own storage items if all tests passed + if (storage && config.stats.bad === 0) { + for (var i = storage.length - 1; i >= 0; i--) { + var key = storage.key(i); + + if (key.indexOf("qunit-test-") === 0) { + storage.removeItem(key); + } + } + } + } + + var ProcessingQueue = { + finished: false, + add: addToQueue, + addImmediate: addToQueueImmediate, + advance: advance + }; + + var TestReport = function () { + function TestReport(name, suite, options) { + classCallCheck(this, TestReport); + + this.name = name; + this.suiteName = suite.name; + this.fullName = suite.fullName.concat(name); + this.runtime = 0; + this.assertions = []; + + this.skipped = !!options.skip; + this.todo = !!options.todo; + + this.valid = options.valid; + + this._startTime = 0; + this._endTime = 0; + + suite.pushTest(this); + } + + createClass(TestReport, [{ + key: "start", + value: function start(recordTime) { + if (recordTime) { + this._startTime = Date.now(); + } + + return { + name: this.name, + suiteName: this.suiteName, + fullName: this.fullName.slice() + }; + } + }, { + key: "end", + value: function end(recordTime) { + if (recordTime) { + this._endTime = Date.now(); + } + + return extend(this.start(), { + runtime: this.getRuntime(), + status: this.getStatus(), + errors: this.getFailedAssertions(), + assertions: this.getAssertions() + }); + } + }, { + key: "pushAssertion", + value: function pushAssertion(assertion) { + this.assertions.push(assertion); + } + }, { + key: "getRuntime", + value: function getRuntime() { + return this._endTime - this._startTime; + } + }, { + key: "getStatus", + value: function getStatus() { + if (this.skipped) { + return "skipped"; + } + + var testPassed = this.getFailedAssertions().length > 0 ? this.todo : !this.todo; + + if (!testPassed) { + return "failed"; + } else if (this.todo) { + return "todo"; + } else { + return "passed"; + } + } + }, { + key: "getFailedAssertions", + value: function getFailedAssertions() { + return this.assertions.filter(function (assertion) { + return !assertion.passed; + }); + } + }, { + key: "getAssertions", + value: function getAssertions() { + return this.assertions.slice(); + } + + // Remove actual and expected values from assertions. This is to prevent + // leaking memory throughout a test suite. + + }, { + key: "slimAssertions", + value: function slimAssertions() { + this.assertions = this.assertions.map(function (assertion) { + delete assertion.actual; + delete assertion.expected; + return assertion; + }); + } + }]); + return TestReport; + }(); + + var focused$1 = false; + + function Test(settings) { + var i, l; + + ++Test.count; + + this.expected = null; + this.assertions = []; + this.semaphore = 0; + this.module = config.currentModule; + this.stack = sourceFromStacktrace(3); + this.steps = []; + this.timeout = undefined; + + // If a module is skipped, all its tests and the tests of the child suites + // should be treated as skipped even if they are defined as `only` or `todo`. + // As for `todo` module, all its tests will be treated as `todo` except for + // tests defined as `skip` which will be left intact. + // + // So, if a test is defined as `todo` and is inside a skipped module, we should + // then treat that test as if was defined as `skip`. + if (this.module.skip) { + settings.skip = true; + settings.todo = false; + + // Skipped tests should be left intact + } else if (this.module.todo && !settings.skip) { + settings.todo = true; + } + + extend(this, settings); + + this.testReport = new TestReport(settings.testName, this.module.suiteReport, { + todo: settings.todo, + skip: settings.skip, + valid: this.valid() + }); + + // Register unique strings + for (i = 0, l = this.module.tests; i < l.length; i++) { + if (this.module.tests[i].name === this.testName) { + this.testName += " "; + } + } + + this.testId = generateHash(this.module.name, this.testName); + + this.module.tests.push({ + name: this.testName, + testId: this.testId, + skip: !!settings.skip + }); + + if (settings.skip) { + + // Skipped tests will fully ignore any sent callback + this.callback = function () {}; + this.async = false; + this.expected = 0; + } else { + if (typeof this.callback !== "function") { + var method = this.todo ? "todo" : "test"; + + // eslint-disable-next-line max-len + throw new TypeError("You must provide a function as a test callback to QUnit." + method + "(\"" + settings.testName + "\")"); + } + + this.assert = new Assert(this); + } + } + + Test.count = 0; + + function getNotStartedModules(startModule) { + var module = startModule, + modules = []; + + while (module && module.testsRun === 0) { + modules.push(module); + module = module.parentModule; + } + + return modules; + } + + Test.prototype = { + before: function before() { + var i, + startModule, + module = this.module, + notStartedModules = getNotStartedModules(module); + + for (i = notStartedModules.length - 1; i >= 0; i--) { + startModule = notStartedModules[i]; + startModule.stats = { all: 0, bad: 0, started: now() }; + emit("suiteStart", startModule.suiteReport.start(true)); + runLoggingCallbacks("moduleStart", { + name: startModule.name, + tests: startModule.tests + }); + } + + config.current = this; + + this.testEnvironment = extend({}, module.testEnvironment); + + this.started = now(); + emit("testStart", this.testReport.start(true)); + runLoggingCallbacks("testStart", { + name: this.testName, + module: module.name, + testId: this.testId, + previousFailure: this.previousFailure + }); + + if (!config.pollution) { + saveGlobal(); + } + }, + + run: function run() { + var promise; + + config.current = this; + + this.callbackStarted = now(); + + if (config.notrycatch) { + runTest(this); + return; + } + + try { + runTest(this); + } catch (e) { + this.pushFailure("Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + (e.message || e), extractStacktrace(e, 0)); + + // Else next test will carry the responsibility + saveGlobal(); + + // Restart the tests if they're blocking + if (config.blocking) { + internalRecover(this); + } + } + + function runTest(test) { + promise = test.callback.call(test.testEnvironment, test.assert); + test.resolvePromise(promise); + + // If the test has a "lock" on it, but the timeout is 0, then we push a + // failure as the test should be synchronous. + if (test.timeout === 0 && test.semaphore !== 0) { + pushFailure("Test did not finish synchronously even though assert.timeout( 0 ) was used.", sourceFromStacktrace(2)); + } + } + }, + + after: function after() { + checkPollution(); + }, + + queueHook: function queueHook(hook, hookName, hookOwner) { + var _this = this; + + var callHook = function callHook() { + var promise = hook.call(_this.testEnvironment, _this.assert); + _this.resolvePromise(promise, hookName); + }; + + var runHook = function runHook() { + if (hookName === "before") { + if (hookOwner.unskippedTestsRun !== 0) { + return; + } + + _this.preserveEnvironment = true; + } + + if (hookName === "after" && hookOwner.unskippedTestsRun !== numberOfUnskippedTests(hookOwner) - 1 && config.queue.length > 2) { + return; + } + + config.current = _this; + if (config.notrycatch) { + callHook(); + return; + } + try { + callHook(); + } catch (error) { + _this.pushFailure(hookName + " failed on " + _this.testName + ": " + (error.message || error), extractStacktrace(error, 0)); + } + }; + + return runHook; + }, + + + // Currently only used for module level hooks, can be used to add global level ones + hooks: function hooks(handler) { + var hooks = []; + + function processHooks(test, module) { + if (module.parentModule) { + processHooks(test, module.parentModule); + } + + if (module.hooks[handler].length) { + for (var i = 0; i < module.hooks[handler].length; i++) { + hooks.push(test.queueHook(module.hooks[handler][i], handler, module)); + } + } + } + + // Hooks are ignored on skipped tests + if (!this.skip) { + processHooks(this, this.module); + } + + return hooks; + }, + + + finish: function finish() { + config.current = this; + if (config.requireExpects && this.expected === null) { + this.pushFailure("Expected number of assertions to be defined, but expect() was " + "not called.", this.stack); + } else if (this.expected !== null && this.expected !== this.assertions.length) { + this.pushFailure("Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack); + } else if (this.expected === null && !this.assertions.length) { + this.pushFailure("Expected at least one assertion, but none were run - call " + "expect(0) to accept zero assertions.", this.stack); + } + + var i, + module = this.module, + moduleName = module.name, + testName = this.testName, + skipped = !!this.skip, + todo = !!this.todo, + bad = 0, + storage = config.storage; + + this.runtime = now() - this.started; + + config.stats.all += this.assertions.length; + module.stats.all += this.assertions.length; + + for (i = 0; i < this.assertions.length; i++) { + if (!this.assertions[i].result) { + bad++; + config.stats.bad++; + module.stats.bad++; + } + } + + notifyTestsRan(module, skipped); + + // Store result when possible + if (storage) { + if (bad) { + storage.setItem("qunit-test-" + moduleName + "-" + testName, bad); + } else { + storage.removeItem("qunit-test-" + moduleName + "-" + testName); + } + } + + // After emitting the js-reporters event we cleanup the assertion data to + // avoid leaking it. It is not used by the legacy testDone callbacks. + emit("testEnd", this.testReport.end(true)); + this.testReport.slimAssertions(); + + runLoggingCallbacks("testDone", { + name: testName, + module: moduleName, + skipped: skipped, + todo: todo, + failed: bad, + passed: this.assertions.length - bad, + total: this.assertions.length, + runtime: skipped ? 0 : this.runtime, + + // HTML Reporter use + assertions: this.assertions, + testId: this.testId, + + // Source of Test + source: this.stack + }); + + if (module.testsRun === numberOfTests(module)) { + logSuiteEnd(module); + + // Check if the parent modules, iteratively, are done. If that the case, + // we emit the `suiteEnd` event and trigger `moduleDone` callback. + var parent = module.parentModule; + while (parent && parent.testsRun === numberOfTests(parent)) { + logSuiteEnd(parent); + parent = parent.parentModule; + } + } + + config.current = undefined; + + function logSuiteEnd(module) { + emit("suiteEnd", module.suiteReport.end(true)); + runLoggingCallbacks("moduleDone", { + name: module.name, + tests: module.tests, + failed: module.stats.bad, + passed: module.stats.all - module.stats.bad, + total: module.stats.all, + runtime: now() - module.stats.started + }); + } + }, + + preserveTestEnvironment: function preserveTestEnvironment() { + if (this.preserveEnvironment) { + this.module.testEnvironment = this.testEnvironment; + this.testEnvironment = extend({}, this.module.testEnvironment); + } + }, + + queue: function queue() { + var test = this; + + if (!this.valid()) { + return; + } + + function runTest() { + + // Each of these can by async + ProcessingQueue.addImmediate([function () { + test.before(); + }, test.hooks("before"), function () { + test.preserveTestEnvironment(); + }, test.hooks("beforeEach"), function () { + test.run(); + }, test.hooks("afterEach").reverse(), test.hooks("after").reverse(), function () { + test.after(); + }, function () { + test.finish(); + }]); + } + + var previousFailCount = config.storage && +config.storage.getItem("qunit-test-" + this.module.name + "-" + this.testName); + + // Prioritize previously failed tests, detected from storage + var prioritize = config.reorder && !!previousFailCount; + + this.previousFailure = !!previousFailCount; + + ProcessingQueue.add(runTest, prioritize, config.seed); + + // If the queue has already finished, we manually process the new test + if (ProcessingQueue.finished) { + ProcessingQueue.advance(); + } + }, + + + pushResult: function pushResult(resultInfo) { + if (this !== config.current) { + throw new Error("Assertion occurred after test had finished."); + } + + // Destructure of resultInfo = { result, actual, expected, message, negative } + var source, + details = { + module: this.module.name, + name: this.testName, + result: resultInfo.result, + message: resultInfo.message, + actual: resultInfo.actual, + testId: this.testId, + negative: resultInfo.negative || false, + runtime: now() - this.started, + todo: !!this.todo + }; + + if (hasOwn.call(resultInfo, "expected")) { + details.expected = resultInfo.expected; + } + + if (!resultInfo.result) { + source = resultInfo.source || sourceFromStacktrace(); + + if (source) { + details.source = source; + } + } + + this.logAssertion(details); + + this.assertions.push({ + result: !!resultInfo.result, + message: resultInfo.message + }); + }, + + pushFailure: function pushFailure(message, source, actual) { + if (!(this instanceof Test)) { + throw new Error("pushFailure() assertion outside test context, was " + sourceFromStacktrace(2)); + } + + this.pushResult({ + result: false, + message: message || "error", + actual: actual || null, + source: source + }); + }, + + /** + * Log assertion details using both the old QUnit.log interface and + * QUnit.on( "assertion" ) interface. + * + * @private + */ + logAssertion: function logAssertion(details) { + runLoggingCallbacks("log", details); + + var assertion = { + passed: details.result, + actual: details.actual, + expected: details.expected, + message: details.message, + stack: details.source, + todo: details.todo + }; + this.testReport.pushAssertion(assertion); + emit("assertion", assertion); + }, + + + resolvePromise: function resolvePromise(promise, phase) { + var then, + resume, + message, + test = this; + if (promise != null) { + then = promise.then; + if (objectType(then) === "function") { + resume = internalStop(test); + if (config.notrycatch) { + then.call(promise, function () { + resume(); + }); + } else { + then.call(promise, function () { + resume(); + }, function (error) { + message = "Promise rejected " + (!phase ? "during" : phase.replace(/Each$/, "")) + " \"" + test.testName + "\": " + (error && error.message || error); + test.pushFailure(message, extractStacktrace(error, 0)); + + // Else next test will carry the responsibility + saveGlobal(); + + // Unblock + resume(); + }); + } + } + } + }, + + valid: function valid() { + var filter = config.filter, + regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec(filter), + module = config.module && config.module.toLowerCase(), + fullName = this.module.name + ": " + this.testName; + + function moduleChainNameMatch(testModule) { + var testModuleName = testModule.name ? testModule.name.toLowerCase() : null; + if (testModuleName === module) { + return true; + } else if (testModule.parentModule) { + return moduleChainNameMatch(testModule.parentModule); + } else { + return false; + } + } + + function moduleChainIdMatch(testModule) { + return inArray(testModule.moduleId, config.moduleId) || testModule.parentModule && moduleChainIdMatch(testModule.parentModule); + } + + // Internally-generated tests are always valid + if (this.callback && this.callback.validTest) { + return true; + } + + if (config.moduleId && config.moduleId.length > 0 && !moduleChainIdMatch(this.module)) { + + return false; + } + + if (config.testId && config.testId.length > 0 && !inArray(this.testId, config.testId)) { + + return false; + } + + if (module && !moduleChainNameMatch(this.module)) { + return false; + } + + if (!filter) { + return true; + } + + return regexFilter ? this.regexFilter(!!regexFilter[1], regexFilter[2], regexFilter[3], fullName) : this.stringFilter(filter, fullName); + }, + + regexFilter: function regexFilter(exclude, pattern, flags, fullName) { + var regex = new RegExp(pattern, flags); + var match = regex.test(fullName); + + return match !== exclude; + }, + + stringFilter: function stringFilter(filter, fullName) { + filter = filter.toLowerCase(); + fullName = fullName.toLowerCase(); + + var include = filter.charAt(0) !== "!"; + if (!include) { + filter = filter.slice(1); + } + + // If the filter matches, we need to honour include + if (fullName.indexOf(filter) !== -1) { + return include; + } + + // Otherwise, do the opposite + return !include; + } + }; + + function pushFailure() { + if (!config.current) { + throw new Error("pushFailure() assertion outside test context, in " + sourceFromStacktrace(2)); + } + + // Gets current test obj + var currentTest = config.current; + + return currentTest.pushFailure.apply(currentTest, arguments); + } + + function saveGlobal() { + config.pollution = []; + + if (config.noglobals) { + for (var key in global$1) { + if (hasOwn.call(global$1, key)) { + + // In Opera sometimes DOM element ids show up here, ignore them + if (/^qunit-test-output/.test(key)) { + continue; + } + config.pollution.push(key); + } + } + } + } + + function checkPollution() { + var newGlobals, + deletedGlobals, + old = config.pollution; + + saveGlobal(); + + newGlobals = diff(config.pollution, old); + if (newGlobals.length > 0) { + pushFailure("Introduced global variable(s): " + newGlobals.join(", ")); + } + + deletedGlobals = diff(old, config.pollution); + if (deletedGlobals.length > 0) { + pushFailure("Deleted global variable(s): " + deletedGlobals.join(", ")); + } + } + + // Will be exposed as QUnit.test + function test(testName, callback) { + if (focused$1) { + return; + } + + var newTest = new Test({ + testName: testName, + callback: callback + }); + + newTest.queue(); + } + + function todo(testName, callback) { + if (focused$1) { + return; + } + + var newTest = new Test({ + testName: testName, + callback: callback, + todo: true + }); + + newTest.queue(); + } + + // Will be exposed as QUnit.skip + function skip(testName) { + if (focused$1) { + return; + } + + var test = new Test({ + testName: testName, + skip: true + }); + + test.queue(); + } + + // Will be exposed as QUnit.only + function only(testName, callback) { + if (focused$1) { + return; + } + + config.queue.length = 0; + focused$1 = true; + + var newTest = new Test({ + testName: testName, + callback: callback + }); + + newTest.queue(); + } + + // Put a hold on processing and return a function that will release it. + function internalStop(test) { + test.semaphore += 1; + config.blocking = true; + + // Set a recovery timeout, if so configured. + if (defined.setTimeout) { + var timeoutDuration = void 0; + + if (typeof test.timeout === "number") { + timeoutDuration = test.timeout; + } else if (typeof config.testTimeout === "number") { + timeoutDuration = config.testTimeout; + } + + if (typeof timeoutDuration === "number" && timeoutDuration > 0) { + clearTimeout(config.timeout); + config.timeout = setTimeout(function () { + pushFailure("Test took longer than " + timeoutDuration + "ms; test timed out.", sourceFromStacktrace(2)); + internalRecover(test); + }, timeoutDuration); + } + } + + var released = false; + return function resume() { + if (released) { + return; + } + + released = true; + test.semaphore -= 1; + internalStart(test); + }; + } + + // Forcefully release all processing holds. + function internalRecover(test) { + test.semaphore = 0; + internalStart(test); + } + + // Release a processing hold, scheduling a resumption attempt if no holds remain. + function internalStart(test) { + + // If semaphore is non-numeric, throw error + if (isNaN(test.semaphore)) { + test.semaphore = 0; + + pushFailure("Invalid value on test.semaphore", sourceFromStacktrace(2)); + return; + } + + // Don't start until equal number of stop-calls + if (test.semaphore > 0) { + return; + } + + // Throw an Error if start is called more often than stop + if (test.semaphore < 0) { + test.semaphore = 0; + + pushFailure("Tried to restart test while already started (test's semaphore was 0 already)", sourceFromStacktrace(2)); + return; + } + + // Add a slight delay to allow more assertions etc. + if (defined.setTimeout) { + if (config.timeout) { + clearTimeout(config.timeout); + } + config.timeout = setTimeout(function () { + if (test.semaphore > 0) { + return; + } + + if (config.timeout) { + clearTimeout(config.timeout); + } + + begin(); + }); + } else { + begin(); + } + } + + function collectTests(module) { + var tests = [].concat(module.tests); + var modules = [].concat(toConsumableArray(module.childModules)); + + // Do a breadth-first traversal of the child modules + while (modules.length) { + var nextModule = modules.shift(); + tests.push.apply(tests, nextModule.tests); + modules.push.apply(modules, toConsumableArray(nextModule.childModules)); + } + + return tests; + } + + function numberOfTests(module) { + return collectTests(module).length; + } + + function numberOfUnskippedTests(module) { + return collectTests(module).filter(function (test) { + return !test.skip; + }).length; + } + + function notifyTestsRan(module, skipped) { + module.testsRun++; + if (!skipped) { + module.unskippedTestsRun++; + } + while (module = module.parentModule) { + module.testsRun++; + if (!skipped) { + module.unskippedTestsRun++; + } + } + } + + /** + * Returns a function that proxies to the given method name on the globals + * console object. The proxy will also detect if the console doesn't exist and + * will appropriately no-op. This allows support for IE9, which doesn't have a + * console if the developer tools are not open. + */ + function consoleProxy(method) { + return function () { + if (console) { + console[method].apply(console, arguments); + } + }; + } + + var Logger = { + warn: consoleProxy("warn") + }; + + var Assert = function () { + function Assert(testContext) { + classCallCheck(this, Assert); + + this.test = testContext; + } + + // Assert helpers + + createClass(Assert, [{ + key: "timeout", + value: function timeout(duration) { + if (typeof duration !== "number") { + throw new Error("You must pass a number as the duration to assert.timeout"); + } + + this.test.timeout = duration; + } + + // Documents a "step", which is a string value, in a test as a passing assertion + + }, { + key: "step", + value: function step(message) { + var result = !!message; + + this.test.steps.push(message); + + return this.pushResult({ + result: result, + message: message || "You must provide a message to assert.step" + }); + } + + // Verifies the steps in a test match a given array of string values + + }, { + key: "verifySteps", + value: function verifySteps(steps, message) { + this.deepEqual(this.test.steps, steps, message); + this.test.steps.length = 0; + } + + // Specify the number of expected assertions to guarantee that failed test + // (no assertions are run at all) don't slip through. + + }, { + key: "expect", + value: function expect(asserts) { + if (arguments.length === 1) { + this.test.expected = asserts; + } else { + return this.test.expected; + } + } + + // Put a hold on processing and return a function that will release it a maximum of once. + + }, { + key: "async", + value: function async(count) { + var test$$1 = this.test; + + var popped = false, + acceptCallCount = count; + + if (typeof acceptCallCount === "undefined") { + acceptCallCount = 1; + } + + var resume = internalStop(test$$1); + + return function done() { + if (config.current !== test$$1) { + throw Error("assert.async callback called after test finished."); + } + + if (popped) { + test$$1.pushFailure("Too many calls to the `assert.async` callback", sourceFromStacktrace(2)); + return; + } + + acceptCallCount -= 1; + if (acceptCallCount > 0) { + return; + } + + popped = true; + resume(); + }; + } + + // Exports test.push() to the user API + // Alias of pushResult. + + }, { + key: "push", + value: function push(result, actual, expected, message, negative) { + Logger.warn("assert.push is deprecated and will be removed in QUnit 3.0." + " Please use assert.pushResult instead (https://api.qunitjs.com/assert/pushResult)."); + + var currentAssert = this instanceof Assert ? this : config.current.assert; + return currentAssert.pushResult({ + result: result, + actual: actual, + expected: expected, + message: message, + negative: negative + }); + } + }, { + key: "pushResult", + value: function pushResult(resultInfo) { + + // Destructure of resultInfo = { result, actual, expected, message, negative } + var assert = this; + var currentTest = assert instanceof Assert && assert.test || config.current; + + // Backwards compatibility fix. + // Allows the direct use of global exported assertions and QUnit.assert.* + // Although, it's use is not recommended as it can leak assertions + // to other tests from async tests, because we only get a reference to the current test, + // not exactly the test where assertion were intended to be called. + if (!currentTest) { + throw new Error("assertion outside test context, in " + sourceFromStacktrace(2)); + } + + if (!(assert instanceof Assert)) { + assert = currentTest.assert; + } + + return assert.test.pushResult(resultInfo); + } + }, { + key: "ok", + value: function ok(result, message) { + if (!message) { + message = result ? "okay" : "failed, expected argument to be truthy, was: " + dump.parse(result); + } + + this.pushResult({ + result: !!result, + actual: result, + expected: true, + message: message + }); + } + }, { + key: "notOk", + value: function notOk(result, message) { + if (!message) { + message = !result ? "okay" : "failed, expected argument to be falsy, was: " + dump.parse(result); + } + + this.pushResult({ + result: !result, + actual: result, + expected: false, + message: message + }); + } + }, { + key: "equal", + value: function equal(actual, expected, message) { + + // eslint-disable-next-line eqeqeq + var result = expected == actual; + + this.pushResult({ + result: result, + actual: actual, + expected: expected, + message: message + }); + } + }, { + key: "notEqual", + value: function notEqual(actual, expected, message) { + + // eslint-disable-next-line eqeqeq + var result = expected != actual; + + this.pushResult({ + result: result, + actual: actual, + expected: expected, + message: message, + negative: true + }); + } + }, { + key: "propEqual", + value: function propEqual(actual, expected, message) { + actual = objectValues(actual); + expected = objectValues(expected); + + this.pushResult({ + result: equiv(actual, expected), + actual: actual, + expected: expected, + message: message + }); + } + }, { + key: "notPropEqual", + value: function notPropEqual(actual, expected, message) { + actual = objectValues(actual); + expected = objectValues(expected); + + this.pushResult({ + result: !equiv(actual, expected), + actual: actual, + expected: expected, + message: message, + negative: true + }); + } + }, { + key: "deepEqual", + value: function deepEqual(actual, expected, message) { + this.pushResult({ + result: equiv(actual, expected), + actual: actual, + expected: expected, + message: message + }); + } + }, { + key: "notDeepEqual", + value: function notDeepEqual(actual, expected, message) { + this.pushResult({ + result: !equiv(actual, expected), + actual: actual, + expected: expected, + message: message, + negative: true + }); + } + }, { + key: "strictEqual", + value: function strictEqual(actual, expected, message) { + this.pushResult({ + result: expected === actual, + actual: actual, + expected: expected, + message: message + }); + } + }, { + key: "notStrictEqual", + value: function notStrictEqual(actual, expected, message) { + this.pushResult({ + result: expected !== actual, + actual: actual, + expected: expected, + message: message, + negative: true + }); + } + }, { + key: "throws", + value: function throws(block, expected, message) { + var actual = void 0, + result = false; + + var currentTest = this instanceof Assert && this.test || config.current; + + // 'expected' is optional unless doing string comparison + if (objectType(expected) === "string") { + if (message == null) { + message = expected; + expected = null; + } else { + throw new Error("throws/raises does not accept a string value for the expected argument.\n" + "Use a non-string object value (e.g. regExp) instead if it's necessary."); + } + } + + currentTest.ignoreGlobalErrors = true; + try { + block.call(currentTest.testEnvironment); + } catch (e) { + actual = e; + } + currentTest.ignoreGlobalErrors = false; + + if (actual) { + var expectedType = objectType(expected); + + // We don't want to validate thrown error + if (!expected) { + result = true; + expected = null; + + // Expected is a regexp + } else if (expectedType === "regexp") { + result = expected.test(errorString(actual)); + + // Expected is a constructor, maybe an Error constructor + } else if (expectedType === "function" && actual instanceof expected) { + result = true; + + // Expected is an Error object + } else if (expectedType === "object") { + result = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message; + + // Expected is a validation function which returns true if validation passed + } else if (expectedType === "function" && expected.call({}, actual) === true) { + expected = null; + result = true; + } + } + + currentTest.assert.pushResult({ + result: result, + actual: actual, + expected: expected, + message: message + }); + } + }, { + key: "rejects", + value: function rejects(promise, expected, message) { + var result = false; + + var currentTest = this instanceof Assert && this.test || config.current; + + // 'expected' is optional unless doing string comparison + if (objectType(expected) === "string") { + if (message === undefined) { + message = expected; + expected = undefined; + } else { + message = "assert.rejects does not accept a string value for the expected " + "argument.\nUse a non-string object value (e.g. validator function) instead " + "if necessary."; + + currentTest.assert.pushResult({ + result: false, + message: message + }); + + return; + } + } + + var then = promise && promise.then; + if (objectType(then) !== "function") { + var _message = "The value provided to `assert.rejects` in " + "\"" + currentTest.testName + "\" was not a promise."; + + currentTest.assert.pushResult({ + result: false, + message: _message, + actual: promise + }); + + return; + } + + var done = this.async(); + + return then.call(promise, function handleFulfillment() { + var message = "The promise returned by the `assert.rejects` callback in " + "\"" + currentTest.testName + "\" did not reject."; + + currentTest.assert.pushResult({ + result: false, + message: message, + actual: promise + }); + + done(); + }, function handleRejection(actual) { + if (actual) { + var expectedType = objectType(expected); + + // We don't want to validate + if (expected === undefined) { + result = true; + expected = null; + + // Expected is a regexp + } else if (expectedType === "regexp") { + result = expected.test(errorString(actual)); + + // Expected is a constructor, maybe an Error constructor + } else if (expectedType === "function" && actual instanceof expected) { + result = true; + + // Expected is an Error object + } else if (expectedType === "object") { + result = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message; + + // Expected is a validation function which returns true if validation passed + } else { + if (expectedType === "function") { + result = expected.call({}, actual) === true; + expected = null; + + // Expected is some other invalid type + } else { + result = false; + message = "invalid expected value provided to `assert.rejects` " + "callback in \"" + currentTest.testName + "\": " + expectedType + "."; + } + } + } + + currentTest.assert.pushResult({ + result: result, + actual: actual, + expected: expected, + message: message + }); + + done(); + }); + } + }]); + return Assert; + }(); + + // Provide an alternative to assert.throws(), for environments that consider throws a reserved word + // Known to us are: Closure Compiler, Narwhal + // eslint-disable-next-line dot-notation + + + Assert.prototype.raises = Assert.prototype["throws"]; + + /** + * Converts an error into a simple string for comparisons. + * + * @param {Error} error + * @return {String} + */ + function errorString(error) { + var resultErrorString = error.toString(); + + if (resultErrorString.substring(0, 7) === "[object") { + var name = error.name ? error.name.toString() : "Error"; + var message = error.message ? error.message.toString() : ""; + + if (name && message) { + return name + ": " + message; + } else if (name) { + return name; + } else if (message) { + return message; + } else { + return "Error"; + } + } else { + return resultErrorString; + } + } + + /* global module, exports, define */ + function exportQUnit(QUnit) { + + if (defined.document) { + + // QUnit may be defined when it is preconfigured but then only QUnit and QUnit.config may be defined. + if (window.QUnit && window.QUnit.version) { + throw new Error("QUnit has already been defined."); + } + + window.QUnit = QUnit; + } + + // For nodejs + if (typeof module !== "undefined" && module && module.exports) { + module.exports = QUnit; + + // For consistency with CommonJS environments' exports + module.exports.QUnit = QUnit; + } + + // For CommonJS with exports, but without module.exports, like Rhino + if (typeof exports !== "undefined" && exports) { + exports.QUnit = QUnit; + } + + if (typeof define === "function" && define.amd) { + define(function () { + return QUnit; + }); + QUnit.config.autostart = false; + } + + // For Web/Service Workers + if (self$1 && self$1.WorkerGlobalScope && self$1 instanceof self$1.WorkerGlobalScope) { + self$1.QUnit = QUnit; + } + } + + var SuiteReport = function () { + function SuiteReport(name, parentSuite) { + classCallCheck(this, SuiteReport); + + this.name = name; + this.fullName = parentSuite ? parentSuite.fullName.concat(name) : []; + + this.tests = []; + this.childSuites = []; + + if (parentSuite) { + parentSuite.pushChildSuite(this); + } + } + + createClass(SuiteReport, [{ + key: "start", + value: function start(recordTime) { + if (recordTime) { + this._startTime = Date.now(); + } + + return { + name: this.name, + fullName: this.fullName.slice(), + tests: this.tests.map(function (test) { + return test.start(); + }), + childSuites: this.childSuites.map(function (suite) { + return suite.start(); + }), + testCounts: { + total: this.getTestCounts().total + } + }; + } + }, { + key: "end", + value: function end(recordTime) { + if (recordTime) { + this._endTime = Date.now(); + } + + return { + name: this.name, + fullName: this.fullName.slice(), + tests: this.tests.map(function (test) { + return test.end(); + }), + childSuites: this.childSuites.map(function (suite) { + return suite.end(); + }), + testCounts: this.getTestCounts(), + runtime: this.getRuntime(), + status: this.getStatus() + }; + } + }, { + key: "pushChildSuite", + value: function pushChildSuite(suite) { + this.childSuites.push(suite); + } + }, { + key: "pushTest", + value: function pushTest(test) { + this.tests.push(test); + } + }, { + key: "getRuntime", + value: function getRuntime() { + return this._endTime - this._startTime; + } + }, { + key: "getTestCounts", + value: function getTestCounts() { + var counts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { passed: 0, failed: 0, skipped: 0, todo: 0, total: 0 }; + + counts = this.tests.reduce(function (counts, test) { + if (test.valid) { + counts[test.getStatus()]++; + counts.total++; + } + + return counts; + }, counts); + + return this.childSuites.reduce(function (counts, suite) { + return suite.getTestCounts(counts); + }, counts); + } + }, { + key: "getStatus", + value: function getStatus() { + var _getTestCounts = this.getTestCounts(), + total = _getTestCounts.total, + failed = _getTestCounts.failed, + skipped = _getTestCounts.skipped, + todo = _getTestCounts.todo; + + if (failed) { + return "failed"; + } else { + if (skipped === total) { + return "skipped"; + } else if (todo === total) { + return "todo"; + } else { + return "passed"; + } + } + } + }]); + return SuiteReport; + }(); + + // Handle an unhandled exception. By convention, returns true if further + // error handling should be suppressed and false otherwise. + // In this case, we will only suppress further error handling if the + // "ignoreGlobalErrors" configuration option is enabled. + function onError(error) { + for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + + if (config.current) { + if (config.current.ignoreGlobalErrors) { + return true; + } + pushFailure.apply(undefined, [error.message, error.fileName + ":" + error.lineNumber].concat(args)); + } else { + test("global failure", extend(function () { + pushFailure.apply(undefined, [error.message, error.fileName + ":" + error.lineNumber].concat(args)); + }, { validTest: true })); + } + + return false; + } + + // Handle an unhandled rejection + function onUnhandledRejection(reason) { + var resultInfo = { + result: false, + message: reason.message || "error", + actual: reason, + source: reason.stack || sourceFromStacktrace(3) + }; + + var currentTest = config.current; + if (currentTest) { + currentTest.assert.pushResult(resultInfo); + } else { + test("global failure", extend(function (assert) { + assert.pushResult(resultInfo); + }, { validTest: true })); + } + } + + var focused = false; + var QUnit = {}; + var globalSuite = new SuiteReport(); + + // The initial "currentModule" represents the global (or top-level) module that + // is not explicitly defined by the user, therefore we add the "globalSuite" to + // it since each module has a suiteReport associated with it. + config.currentModule.suiteReport = globalSuite; + + var moduleStack = []; + var globalStartCalled = false; + var runStarted = false; + + // Figure out if we're running the tests from a server or not + QUnit.isLocal = !(defined.document && window.location.protocol !== "file:"); + + // Expose the current QUnit version + QUnit.version = "2.5.0"; + + function createModule(name, testEnvironment, modifiers) { + var parentModule = moduleStack.length ? moduleStack.slice(-1)[0] : null; + var moduleName = parentModule !== null ? [parentModule.name, name].join(" > ") : name; + var parentSuite = parentModule ? parentModule.suiteReport : globalSuite; + + var skip$$1 = parentModule !== null && parentModule.skip || modifiers.skip; + var todo$$1 = parentModule !== null && parentModule.todo || modifiers.todo; + + var module = { + name: moduleName, + parentModule: parentModule, + tests: [], + moduleId: generateHash(moduleName), + testsRun: 0, + unskippedTestsRun: 0, + childModules: [], + suiteReport: new SuiteReport(name, parentSuite), + + // Pass along `skip` and `todo` properties from parent module, in case + // there is one, to childs. And use own otherwise. + // This property will be used to mark own tests and tests of child suites + // as either `skipped` or `todo`. + skip: skip$$1, + todo: skip$$1 ? false : todo$$1 + }; + + var env = {}; + if (parentModule) { + parentModule.childModules.push(module); + extend(env, parentModule.testEnvironment); + } + extend(env, testEnvironment); + module.testEnvironment = env; + + config.modules.push(module); + return module; + } + + function processModule(name, options, executeNow) { + var modifiers = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; + + var module = createModule(name, options, modifiers); + + // Move any hooks to a 'hooks' object + var testEnvironment = module.testEnvironment; + var hooks = module.hooks = {}; + + setHookFromEnvironment(hooks, testEnvironment, "before"); + setHookFromEnvironment(hooks, testEnvironment, "beforeEach"); + setHookFromEnvironment(hooks, testEnvironment, "afterEach"); + setHookFromEnvironment(hooks, testEnvironment, "after"); + + function setHookFromEnvironment(hooks, environment, name) { + var potentialHook = environment[name]; + hooks[name] = typeof potentialHook === "function" ? [potentialHook] : []; + delete environment[name]; + } + + var moduleFns = { + before: setHookFunction(module, "before"), + beforeEach: setHookFunction(module, "beforeEach"), + afterEach: setHookFunction(module, "afterEach"), + after: setHookFunction(module, "after") + }; + + var currentModule = config.currentModule; + if (objectType(executeNow) === "function") { + moduleStack.push(module); + config.currentModule = module; + executeNow.call(module.testEnvironment, moduleFns); + moduleStack.pop(); + module = module.parentModule || currentModule; + } + + config.currentModule = module; + } + + // TODO: extract this to a new file alongside its related functions + function module$1(name, options, executeNow) { + if (focused) { + return; + } + + if (arguments.length === 2) { + if (objectType(options) === "function") { + executeNow = options; + options = undefined; + } + } + + processModule(name, options, executeNow); + } + + module$1.only = function () { + if (focused) { + return; + } + + config.modules.length = 0; + config.queue.length = 0; + + module$1.apply(undefined, arguments); + + focused = true; + }; + + module$1.skip = function (name, options, executeNow) { + if (focused) { + return; + } + + if (arguments.length === 2) { + if (objectType(options) === "function") { + executeNow = options; + options = undefined; + } + } + + processModule(name, options, executeNow, { skip: true }); + }; + + module$1.todo = function (name, options, executeNow) { + if (focused) { + return; + } + + if (arguments.length === 2) { + if (objectType(options) === "function") { + executeNow = options; + options = undefined; + } + } + + processModule(name, options, executeNow, { todo: true }); + }; + + extend(QUnit, { + on: on, + + module: module$1, + + test: test, + + todo: todo, + + skip: skip, + + only: only, + + start: function start(count) { + var globalStartAlreadyCalled = globalStartCalled; + + if (!config.current) { + globalStartCalled = true; + + if (runStarted) { + throw new Error("Called start() while test already started running"); + } else if (globalStartAlreadyCalled || count > 1) { + throw new Error("Called start() outside of a test context too many times"); + } else if (config.autostart) { + throw new Error("Called start() outside of a test context when " + "QUnit.config.autostart was true"); + } else if (!config.pageLoaded) { + + // The page isn't completely loaded yet, so we set autostart and then + // load if we're in Node or wait for the browser's load event. + config.autostart = true; + + // Starts from Node even if .load was not previously called. We still return + // early otherwise we'll wind up "beginning" twice. + if (!defined.document) { + QUnit.load(); + } + + return; + } + } else { + throw new Error("QUnit.start cannot be called inside a test context."); + } + + scheduleBegin(); + }, + + config: config, + + is: is, + + objectType: objectType, + + extend: extend, + + load: function load() { + config.pageLoaded = true; + + // Initialize the configuration options + extend(config, { + stats: { all: 0, bad: 0 }, + started: 0, + updateRate: 1000, + autostart: true, + filter: "" + }, true); + + if (!runStarted) { + config.blocking = false; + + if (config.autostart) { + scheduleBegin(); + } + } + }, + + stack: function stack(offset) { + offset = (offset || 0) + 2; + return sourceFromStacktrace(offset); + }, + + onError: onError, + + onUnhandledRejection: onUnhandledRejection + }); + + QUnit.pushFailure = pushFailure; + QUnit.assert = Assert.prototype; + QUnit.equiv = equiv; + QUnit.dump = dump; + + registerLoggingCallbacks(QUnit); + + function scheduleBegin() { + + runStarted = true; + + // Add a slight delay to allow definition of more modules and tests. + if (defined.setTimeout) { + setTimeout(function () { + begin(); + }); + } else { + begin(); + } + } + + function begin() { + var i, + l, + modulesLog = []; + + // If the test run hasn't officially begun yet + if (!config.started) { + + // Record the time of the test run's beginning + config.started = now(); + + // Delete the loose unnamed module if unused. + if (config.modules[0].name === "" && config.modules[0].tests.length === 0) { + config.modules.shift(); + } + + // Avoid unnecessary information by not logging modules' test environments + for (i = 0, l = config.modules.length; i < l; i++) { + modulesLog.push({ + name: config.modules[i].name, + tests: config.modules[i].tests + }); + } + + // The test run is officially beginning now + emit("runStart", globalSuite.start(true)); + runLoggingCallbacks("begin", { + totalTests: Test.count, + modules: modulesLog + }); + } + + config.blocking = false; + ProcessingQueue.advance(); + } + + function setHookFunction(module, hookName) { + return function setHook(callback) { + module.hooks[hookName].push(callback); + }; + } + + exportQUnit(QUnit); + + (function () { + + if (typeof window === "undefined" || typeof document === "undefined") { + return; + } + + var config = QUnit.config, + hasOwn = Object.prototype.hasOwnProperty; + + // Stores fixture HTML for resetting later + function storeFixture() { + + // Avoid overwriting user-defined values + if (hasOwn.call(config, "fixture")) { + return; + } + + var fixture = document.getElementById("qunit-fixture"); + if (fixture) { + config.fixture = fixture.innerHTML; + } + } + + QUnit.begin(storeFixture); + + // Resets the fixture DOM element if available. + function resetFixture() { + if (config.fixture == null) { + return; + } + + var fixture = document.getElementById("qunit-fixture"); + if (fixture) { + fixture.innerHTML = config.fixture; + } + } + + QUnit.testStart(resetFixture); + })(); + + (function () { + + // Only interact with URLs via window.location + var location = typeof window !== "undefined" && window.location; + if (!location) { + return; + } + + var urlParams = getUrlParams(); + + QUnit.urlParams = urlParams; + + // Match module/test by inclusion in an array + QUnit.config.moduleId = [].concat(urlParams.moduleId || []); + QUnit.config.testId = [].concat(urlParams.testId || []); + + // Exact case-insensitive match of the module name + QUnit.config.module = urlParams.module; + + // Regular expression or case-insenstive substring match against "moduleName: testName" + QUnit.config.filter = urlParams.filter; + + // Test order randomization + if (urlParams.seed === true) { + + // Generate a random seed if the option is specified without a value + QUnit.config.seed = Math.random().toString(36).slice(2); + } else if (urlParams.seed) { + QUnit.config.seed = urlParams.seed; + } + + // Add URL-parameter-mapped config values with UI form rendering data + QUnit.config.urlConfig.push({ + id: "hidepassed", + label: "Hide passed tests", + tooltip: "Only show tests and assertions that fail. Stored as query-strings." + }, { + id: "noglobals", + label: "Check for Globals", + tooltip: "Enabling this will test if any test introduces new properties on the " + "global object (`window` in Browsers). Stored as query-strings." + }, { + id: "notrycatch", + label: "No try-catch", + tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " + "exceptions in IE reasonable. Stored as query-strings." + }); + + QUnit.begin(function () { + var i, + option, + urlConfig = QUnit.config.urlConfig; + + for (i = 0; i < urlConfig.length; i++) { + + // Options can be either strings or objects with nonempty "id" properties + option = QUnit.config.urlConfig[i]; + if (typeof option !== "string") { + option = option.id; + } + + if (QUnit.config[option] === undefined) { + QUnit.config[option] = urlParams[option]; + } + } + }); + + function getUrlParams() { + var i, param, name, value; + var urlParams = Object.create(null); + var params = location.search.slice(1).split("&"); + var length = params.length; + + for (i = 0; i < length; i++) { + if (params[i]) { + param = params[i].split("="); + name = decodeQueryParam(param[0]); + + // Allow just a key to turn on a flag, e.g., test.html?noglobals + value = param.length === 1 || decodeQueryParam(param.slice(1).join("=")); + if (name in urlParams) { + urlParams[name] = [].concat(urlParams[name], value); + } else { + urlParams[name] = value; + } + } + } + + return urlParams; + } + + function decodeQueryParam(param) { + return decodeURIComponent(param.replace(/\+/g, "%20")); + } + })(); + + var stats = { + passedTests: 0, + failedTests: 0, + skippedTests: 0, + todoTests: 0 + }; + + // Escape text for attribute or text content. + function escapeText(s) { + if (!s) { + return ""; + } + s = s + ""; + + // Both single quotes and double quotes (for attributes) + return s.replace(/['"<>&]/g, function (s) { + switch (s) { + case "'": + return "'"; + case "\"": + return """; + case "<": + return "<"; + case ">": + return ">"; + case "&": + return "&"; + } + }); + } + + (function () { + + // Don't load the HTML Reporter on non-browser environments + if (typeof window === "undefined" || !window.document) { + return; + } + + var config = QUnit.config, + document$$1 = window.document, + collapseNext = false, + hasOwn = Object.prototype.hasOwnProperty, + unfilteredUrl = setUrl({ filter: undefined, module: undefined, + moduleId: undefined, testId: undefined }), + modulesList = []; + + function addEvent(elem, type, fn) { + elem.addEventListener(type, fn, false); + } + + function removeEvent(elem, type, fn) { + elem.removeEventListener(type, fn, false); + } + + function addEvents(elems, type, fn) { + var i = elems.length; + while (i--) { + addEvent(elems[i], type, fn); + } + } + + function hasClass(elem, name) { + return (" " + elem.className + " ").indexOf(" " + name + " ") >= 0; + } + + function addClass(elem, name) { + if (!hasClass(elem, name)) { + elem.className += (elem.className ? " " : "") + name; + } + } + + function toggleClass(elem, name, force) { + if (force || typeof force === "undefined" && !hasClass(elem, name)) { + addClass(elem, name); + } else { + removeClass(elem, name); + } + } + + function removeClass(elem, name) { + var set = " " + elem.className + " "; + + // Class name may appear multiple times + while (set.indexOf(" " + name + " ") >= 0) { + set = set.replace(" " + name + " ", " "); + } + + // Trim for prettiness + elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, ""); + } + + function id(name) { + return document$$1.getElementById && document$$1.getElementById(name); + } + + function abortTests() { + var abortButton = id("qunit-abort-tests-button"); + if (abortButton) { + abortButton.disabled = true; + abortButton.innerHTML = "Aborting..."; + } + QUnit.config.queue.length = 0; + return false; + } + + function interceptNavigation(ev) { + applyUrlParams(); + + if (ev && ev.preventDefault) { + ev.preventDefault(); + } + + return false; + } + + function getUrlConfigHtml() { + var i, + j, + val, + escaped, + escapedTooltip, + selection = false, + urlConfig = config.urlConfig, + urlConfigHtml = ""; + + for (i = 0; i < urlConfig.length; i++) { + + // Options can be either strings or objects with nonempty "id" properties + val = config.urlConfig[i]; + if (typeof val === "string") { + val = { + id: val, + label: val + }; + } + + escaped = escapeText(val.id); + escapedTooltip = escapeText(val.tooltip); + + if (!val.value || typeof val.value === "string") { + urlConfigHtml += ""; + } else { + urlConfigHtml += ""; + } + } + + return urlConfigHtml; + } + + // Handle "click" events on toolbar checkboxes and "change" for select menus. + // Updates the URL with the new state of `config.urlConfig` values. + function toolbarChanged() { + var updatedUrl, + value, + tests, + field = this, + params = {}; + + // Detect if field is a select menu or a checkbox + if ("selectedIndex" in field) { + value = field.options[field.selectedIndex].value || undefined; + } else { + value = field.checked ? field.defaultValue || true : undefined; + } + + params[field.name] = value; + updatedUrl = setUrl(params); + + // Check if we can apply the change without a page refresh + if ("hidepassed" === field.name && "replaceState" in window.history) { + QUnit.urlParams[field.name] = value; + config[field.name] = value || false; + tests = id("qunit-tests"); + if (tests) { + toggleClass(tests, "hidepass", value || false); + } + window.history.replaceState(null, "", updatedUrl); + } else { + window.location = updatedUrl; + } + } + + function setUrl(params) { + var key, + arrValue, + i, + querystring = "?", + location = window.location; + + params = QUnit.extend(QUnit.extend({}, QUnit.urlParams), params); + + for (key in params) { + + // Skip inherited or undefined properties + if (hasOwn.call(params, key) && params[key] !== undefined) { + + // Output a parameter for each value of this key + // (but usually just one) + arrValue = [].concat(params[key]); + for (i = 0; i < arrValue.length; i++) { + querystring += encodeURIComponent(key); + if (arrValue[i] !== true) { + querystring += "=" + encodeURIComponent(arrValue[i]); + } + querystring += "&"; + } + } + } + return location.protocol + "//" + location.host + location.pathname + querystring.slice(0, -1); + } + + function applyUrlParams() { + var i, + selectedModules = [], + modulesList = id("qunit-modulefilter-dropdown-list").getElementsByTagName("input"), + filter = id("qunit-filter-input").value; + + for (i = 0; i < modulesList.length; i++) { + if (modulesList[i].checked) { + selectedModules.push(modulesList[i].value); + } + } + + window.location = setUrl({ + filter: filter === "" ? undefined : filter, + moduleId: selectedModules.length === 0 ? undefined : selectedModules, + + // Remove module and testId filter + module: undefined, + testId: undefined + }); + } + + function toolbarUrlConfigContainer() { + var urlConfigContainer = document$$1.createElement("span"); + + urlConfigContainer.innerHTML = getUrlConfigHtml(); + addClass(urlConfigContainer, "qunit-url-config"); + + addEvents(urlConfigContainer.getElementsByTagName("input"), "change", toolbarChanged); + addEvents(urlConfigContainer.getElementsByTagName("select"), "change", toolbarChanged); + + return urlConfigContainer; + } + + function abortTestsButton() { + var button = document$$1.createElement("button"); + button.id = "qunit-abort-tests-button"; + button.innerHTML = "Abort"; + addEvent(button, "click", abortTests); + return button; + } + + function toolbarLooseFilter() { + var filter = document$$1.createElement("form"), + label = document$$1.createElement("label"), + input = document$$1.createElement("input"), + button = document$$1.createElement("button"); + + addClass(filter, "qunit-filter"); + + label.innerHTML = "Filter: "; + + input.type = "text"; + input.value = config.filter || ""; + input.name = "filter"; + input.id = "qunit-filter-input"; + + button.innerHTML = "Go"; + + label.appendChild(input); + + filter.appendChild(label); + filter.appendChild(document$$1.createTextNode(" ")); + filter.appendChild(button); + addEvent(filter, "submit", interceptNavigation); + + return filter; + } + + function moduleListHtml() { + var i, + checked, + html = ""; + + for (i = 0; i < config.modules.length; i++) { + if (config.modules[i].name !== "") { + checked = config.moduleId.indexOf(config.modules[i].moduleId) > -1; + html += "
  • "; + } + } + + return html; + } + + function toolbarModuleFilter() { + var allCheckbox, + commit, + reset, + moduleFilter = document$$1.createElement("form"), + label = document$$1.createElement("label"), + moduleSearch = document$$1.createElement("input"), + dropDown = document$$1.createElement("div"), + actions = document$$1.createElement("span"), + dropDownList = document$$1.createElement("ul"), + dirty = false; + + moduleSearch.id = "qunit-modulefilter-search"; + addEvent(moduleSearch, "input", searchInput); + addEvent(moduleSearch, "input", searchFocus); + addEvent(moduleSearch, "focus", searchFocus); + addEvent(moduleSearch, "click", searchFocus); + + label.id = "qunit-modulefilter-search-container"; + label.innerHTML = "Module: "; + label.appendChild(moduleSearch); + + actions.id = "qunit-modulefilter-actions"; + actions.innerHTML = "" + "" + ""; + allCheckbox = actions.lastChild.firstChild; + commit = actions.firstChild; + reset = commit.nextSibling; + addEvent(commit, "click", applyUrlParams); + + dropDownList.id = "qunit-modulefilter-dropdown-list"; + dropDownList.innerHTML = moduleListHtml(); + + dropDown.id = "qunit-modulefilter-dropdown"; + dropDown.style.display = "none"; + dropDown.appendChild(actions); + dropDown.appendChild(dropDownList); + addEvent(dropDown, "change", selectionChange); + selectionChange(); + + moduleFilter.id = "qunit-modulefilter"; + moduleFilter.appendChild(label); + moduleFilter.appendChild(dropDown); + addEvent(moduleFilter, "submit", interceptNavigation); + addEvent(moduleFilter, "reset", function () { + + // Let the reset happen, then update styles + window.setTimeout(selectionChange); + }); + + // Enables show/hide for the dropdown + function searchFocus() { + if (dropDown.style.display !== "none") { + return; + } + + dropDown.style.display = "block"; + addEvent(document$$1, "click", hideHandler); + addEvent(document$$1, "keydown", hideHandler); + + // Hide on Escape keydown or outside-container click + function hideHandler(e) { + var inContainer = moduleFilter.contains(e.target); + + if (e.keyCode === 27 || !inContainer) { + if (e.keyCode === 27 && inContainer) { + moduleSearch.focus(); + } + dropDown.style.display = "none"; + removeEvent(document$$1, "click", hideHandler); + removeEvent(document$$1, "keydown", hideHandler); + moduleSearch.value = ""; + searchInput(); + } + } + } + + // Processes module search box input + function searchInput() { + var i, + item, + searchText = moduleSearch.value.toLowerCase(), + listItems = dropDownList.children; + + for (i = 0; i < listItems.length; i++) { + item = listItems[i]; + if (!searchText || item.textContent.toLowerCase().indexOf(searchText) > -1) { + item.style.display = ""; + } else { + item.style.display = "none"; + } + } + } + + // Processes selection changes + function selectionChange(evt) { + var i, + item, + checkbox = evt && evt.target || allCheckbox, + modulesList = dropDownList.getElementsByTagName("input"), + selectedNames = []; + + toggleClass(checkbox.parentNode, "checked", checkbox.checked); + + dirty = false; + if (checkbox.checked && checkbox !== allCheckbox) { + allCheckbox.checked = false; + removeClass(allCheckbox.parentNode, "checked"); + } + for (i = 0; i < modulesList.length; i++) { + item = modulesList[i]; + if (!evt) { + toggleClass(item.parentNode, "checked", item.checked); + } else if (checkbox === allCheckbox && checkbox.checked) { + item.checked = false; + removeClass(item.parentNode, "checked"); + } + dirty = dirty || item.checked !== item.defaultChecked; + if (item.checked) { + selectedNames.push(item.parentNode.textContent); + } + } + + commit.style.display = reset.style.display = dirty ? "" : "none"; + moduleSearch.placeholder = selectedNames.join(", ") || allCheckbox.parentNode.textContent; + moduleSearch.title = "Type to filter list. Current selection:\n" + (selectedNames.join("\n") || allCheckbox.parentNode.textContent); + } + + return moduleFilter; + } + + function appendToolbar() { + var toolbar = id("qunit-testrunner-toolbar"); + + if (toolbar) { + toolbar.appendChild(toolbarUrlConfigContainer()); + toolbar.appendChild(toolbarModuleFilter()); + toolbar.appendChild(toolbarLooseFilter()); + toolbar.appendChild(document$$1.createElement("div")).className = "clearfix"; + } + } + + function appendHeader() { + var header = id("qunit-header"); + + if (header) { + header.innerHTML = "" + header.innerHTML + " "; + } + } + + function appendBanner() { + var banner = id("qunit-banner"); + + if (banner) { + banner.className = ""; + } + } + + function appendTestResults() { + var tests = id("qunit-tests"), + result = id("qunit-testresult"), + controls; + + if (result) { + result.parentNode.removeChild(result); + } + + if (tests) { + tests.innerHTML = ""; + result = document$$1.createElement("p"); + result.id = "qunit-testresult"; + result.className = "result"; + tests.parentNode.insertBefore(result, tests); + result.innerHTML = "
    Running...
     
    " + "
    " + "
    "; + controls = id("qunit-testresult-controls"); + } + + if (controls) { + controls.appendChild(abortTestsButton()); + } + } + + function appendFilteredTest() { + var testId = QUnit.config.testId; + if (!testId || testId.length <= 0) { + return ""; + } + return "
    Rerunning selected tests: " + escapeText(testId.join(", ")) + " Run all tests
    "; + } + + function appendUserAgent() { + var userAgent = id("qunit-userAgent"); + + if (userAgent) { + userAgent.innerHTML = ""; + userAgent.appendChild(document$$1.createTextNode("QUnit " + QUnit.version + "; " + navigator.userAgent)); + } + } + + function appendInterface() { + var qunit = id("qunit"); + + if (qunit) { + qunit.innerHTML = "

    " + escapeText(document$$1.title) + "

    " + "

    " + "
    " + appendFilteredTest() + "

    " + "
      "; + } + + appendHeader(); + appendBanner(); + appendTestResults(); + appendUserAgent(); + appendToolbar(); + } + + function appendTestsList(modules) { + var i, l, x, z, test, moduleObj; + + for (i = 0, l = modules.length; i < l; i++) { + moduleObj = modules[i]; + + for (x = 0, z = moduleObj.tests.length; x < z; x++) { + test = moduleObj.tests[x]; + + appendTest(test.name, test.testId, moduleObj.name); + } + } + } + + function appendTest(name, testId, moduleName) { + var title, + rerunTrigger, + testBlock, + assertList, + tests = id("qunit-tests"); + + if (!tests) { + return; + } + + title = document$$1.createElement("strong"); + title.innerHTML = getNameHtml(name, moduleName); + + rerunTrigger = document$$1.createElement("a"); + rerunTrigger.innerHTML = "Rerun"; + rerunTrigger.href = setUrl({ testId: testId }); + + testBlock = document$$1.createElement("li"); + testBlock.appendChild(title); + testBlock.appendChild(rerunTrigger); + testBlock.id = "qunit-test-output-" + testId; + + assertList = document$$1.createElement("ol"); + assertList.className = "qunit-assert-list"; + + testBlock.appendChild(assertList); + + tests.appendChild(testBlock); + } + + // HTML Reporter initialization and load + QUnit.begin(function (details) { + var i, moduleObj, tests; + + // Sort modules by name for the picker + for (i = 0; i < details.modules.length; i++) { + moduleObj = details.modules[i]; + if (moduleObj.name) { + modulesList.push(moduleObj.name); + } + } + modulesList.sort(function (a, b) { + return a.localeCompare(b); + }); + + // Initialize QUnit elements + appendInterface(); + appendTestsList(details.modules); + tests = id("qunit-tests"); + if (tests && config.hidepassed) { + addClass(tests, "hidepass"); + } + }); + + QUnit.done(function (details) { + var banner = id("qunit-banner"), + tests = id("qunit-tests"), + abortButton = id("qunit-abort-tests-button"), + totalTests = stats.passedTests + stats.skippedTests + stats.todoTests + stats.failedTests, + html = [totalTests, " tests completed in ", details.runtime, " milliseconds, with ", stats.failedTests, " failed, ", stats.skippedTests, " skipped, and ", stats.todoTests, " todo.
      ", "", details.passed, " assertions of ", details.total, " passed, ", details.failed, " failed."].join(""), + test, + assertLi, + assertList; + + // Update remaing tests to aborted + if (abortButton && abortButton.disabled) { + html = "Tests aborted after " + details.runtime + " milliseconds."; + + for (var i = 0; i < tests.children.length; i++) { + test = tests.children[i]; + if (test.className === "" || test.className === "running") { + test.className = "aborted"; + assertList = test.getElementsByTagName("ol")[0]; + assertLi = document$$1.createElement("li"); + assertLi.className = "fail"; + assertLi.innerHTML = "Test aborted."; + assertList.appendChild(assertLi); + } + } + } + + if (banner && (!abortButton || abortButton.disabled === false)) { + banner.className = stats.failedTests ? "qunit-fail" : "qunit-pass"; + } + + if (abortButton) { + abortButton.parentNode.removeChild(abortButton); + } + + if (tests) { + id("qunit-testresult-display").innerHTML = html; + } + + if (config.altertitle && document$$1.title) { + + // Show ✖ for good, ✔ for bad suite result in title + // use escape sequences in case file gets loaded with non-utf-8 + // charset + document$$1.title = [stats.failedTests ? "\u2716" : "\u2714", document$$1.title.replace(/^[\u2714\u2716] /i, "")].join(" "); + } + + // Scroll back to top to show results + if (config.scrolltop && window.scrollTo) { + window.scrollTo(0, 0); + } + }); + + function getNameHtml(name, module) { + var nameHtml = ""; + + if (module) { + nameHtml = "" + escapeText(module) + ": "; + } + + nameHtml += "" + escapeText(name) + ""; + + return nameHtml; + } + + QUnit.testStart(function (details) { + var running, testBlock, bad; + + testBlock = id("qunit-test-output-" + details.testId); + if (testBlock) { + testBlock.className = "running"; + } else { + + // Report later registered tests + appendTest(details.name, details.testId, details.module); + } + + running = id("qunit-testresult-display"); + if (running) { + bad = QUnit.config.reorder && details.previousFailure; + + running.innerHTML = [bad ? "Rerunning previously failed test:
      " : "Running:
      ", getNameHtml(details.name, details.module)].join(""); + } + }); + + function stripHtml(string) { + + // Strip tags, html entity and whitespaces + return string.replace(/<\/?[^>]+(>|$)/g, "").replace(/\"/g, "").replace(/\s+/g, ""); + } + + QUnit.log(function (details) { + var assertList, + assertLi, + message, + expected, + actual, + diff, + showDiff = false, + testItem = id("qunit-test-output-" + details.testId); + + if (!testItem) { + return; + } + + message = escapeText(details.message) || (details.result ? "okay" : "failed"); + message = "" + message + ""; + message += "@ " + details.runtime + " ms"; + + // The pushFailure doesn't provide details.expected + // when it calls, it's implicit to also not show expected and diff stuff + // Also, we need to check details.expected existence, as it can exist and be undefined + if (!details.result && hasOwn.call(details, "expected")) { + if (details.negative) { + expected = "NOT " + QUnit.dump.parse(details.expected); + } else { + expected = QUnit.dump.parse(details.expected); + } + + actual = QUnit.dump.parse(details.actual); + message += ""; + + if (actual !== expected) { + + message += ""; + + if (typeof details.actual === "number" && typeof details.expected === "number") { + if (!isNaN(details.actual) && !isNaN(details.expected)) { + showDiff = true; + diff = details.actual - details.expected; + diff = (diff > 0 ? "+" : "") + diff; + } + } else if (typeof details.actual !== "boolean" && typeof details.expected !== "boolean") { + diff = QUnit.diff(expected, actual); + + // don't show diff if there is zero overlap + showDiff = stripHtml(diff).length !== stripHtml(expected).length + stripHtml(actual).length; + } + + if (showDiff) { + message += ""; + } + } else if (expected.indexOf("[object Array]") !== -1 || expected.indexOf("[object Object]") !== -1) { + message += ""; + } else { + message += ""; + } + + if (details.source) { + message += ""; + } + + message += "
      Expected:
      " + escapeText(expected) + "
      Result:
      " + escapeText(actual) + "
      Diff:
      " + diff + "
      Message: " + "Diff suppressed as the depth of object is more than current max depth (" + QUnit.config.maxDepth + ").

      Hint: Use QUnit.dump.maxDepth to " + " run with a higher max depth or " + "Rerun without max depth.

      Message: " + "Diff suppressed as the expected and actual results have an equivalent" + " serialization
      Source:
      " + escapeText(details.source) + "
      "; + + // This occurs when pushFailure is set and we have an extracted stack trace + } else if (!details.result && details.source) { + message += "" + "" + "
      Source:
      " + escapeText(details.source) + "
      "; + } + + assertList = testItem.getElementsByTagName("ol")[0]; + + assertLi = document$$1.createElement("li"); + assertLi.className = details.result ? "pass" : "fail"; + assertLi.innerHTML = message; + assertList.appendChild(assertLi); + }); + + QUnit.testDone(function (details) { + var testTitle, + time, + testItem, + assertList, + good, + bad, + testCounts, + skipped, + sourceName, + tests = id("qunit-tests"); + + if (!tests) { + return; + } + + testItem = id("qunit-test-output-" + details.testId); + + assertList = testItem.getElementsByTagName("ol")[0]; + + good = details.passed; + bad = details.failed; + + // This test passed if it has no unexpected failed assertions + var testPassed = details.failed > 0 ? details.todo : !details.todo; + + if (testPassed) { + + // Collapse the passing tests + addClass(assertList, "qunit-collapsed"); + } else if (config.collapse) { + if (!collapseNext) { + + // Skip collapsing the first failing test + collapseNext = true; + } else { + + // Collapse remaining tests + addClass(assertList, "qunit-collapsed"); + } + } + + // The testItem.firstChild is the test name + testTitle = testItem.firstChild; + + testCounts = bad ? "" + bad + ", " + "" + good + ", " : ""; + + testTitle.innerHTML += " (" + testCounts + details.assertions.length + ")"; + + if (details.skipped) { + stats.skippedTests++; + + testItem.className = "skipped"; + skipped = document$$1.createElement("em"); + skipped.className = "qunit-skipped-label"; + skipped.innerHTML = "skipped"; + testItem.insertBefore(skipped, testTitle); + } else { + addEvent(testTitle, "click", function () { + toggleClass(assertList, "qunit-collapsed"); + }); + + testItem.className = testPassed ? "pass" : "fail"; + + if (details.todo) { + var todoLabel = document$$1.createElement("em"); + todoLabel.className = "qunit-todo-label"; + todoLabel.innerHTML = "todo"; + testItem.className += " todo"; + testItem.insertBefore(todoLabel, testTitle); + } + + time = document$$1.createElement("span"); + time.className = "runtime"; + time.innerHTML = details.runtime + " ms"; + testItem.insertBefore(time, assertList); + + if (!testPassed) { + stats.failedTests++; + } else if (details.todo) { + stats.todoTests++; + } else { + stats.passedTests++; + } + } + + // Show the source of the test when showing assertions + if (details.source) { + sourceName = document$$1.createElement("p"); + sourceName.innerHTML = "Source: " + details.source; + addClass(sourceName, "qunit-source"); + if (testPassed) { + addClass(sourceName, "qunit-collapsed"); + } + addEvent(testTitle, "click", function () { + toggleClass(sourceName, "qunit-collapsed"); + }); + testItem.appendChild(sourceName); + } + }); + + // Avoid readyState issue with phantomjs + // Ref: #818 + var notPhantom = function (p) { + return !(p && p.version && p.version.major > 0); + }(window.phantom); + + if (notPhantom && document$$1.readyState === "complete") { + QUnit.load(); + } else { + addEvent(window, "load", QUnit.load); + } + + // Wrap window.onerror. We will call the original window.onerror to see if + // the existing handler fully handles the error; if not, we will call the + // QUnit.onError function. + var originalWindowOnError = window.onerror; + + // Cover uncaught exceptions + // Returning true will suppress the default browser handler, + // returning false will let it run. + window.onerror = function (message, fileName, lineNumber) { + var ret = false; + if (originalWindowOnError) { + for (var _len = arguments.length, args = Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) { + args[_key - 3] = arguments[_key]; + } + + ret = originalWindowOnError.call.apply(originalWindowOnError, [this, message, fileName, lineNumber].concat(args)); + } + + // Treat return value as window.onerror itself does, + // Only do our handling if not suppressed. + if (ret !== true) { + var error = { + message: message, + fileName: fileName, + lineNumber: lineNumber + }; + + ret = QUnit.onError(error); + } + + return ret; + }; + + // Listen for unhandled rejections, and call QUnit.onUnhandledRejection + window.addEventListener("unhandledrejection", function (event) { + QUnit.onUnhandledRejection(event.reason); + }); + })(); + + /* + * This file is a modified version of google-diff-match-patch's JavaScript implementation + * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js), + * modifications are licensed as more fully set forth in LICENSE.txt. + * + * The original source of google-diff-match-patch is attributable and licensed as follows: + * + * Copyright 2006 Google Inc. + * https://code.google.com/p/google-diff-match-patch/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * More Info: + * https://code.google.com/p/google-diff-match-patch/ + * + * Usage: QUnit.diff(expected, actual) + * + */ + QUnit.diff = function () { + function DiffMatchPatch() {} + + // DIFF FUNCTIONS + + /** + * The data structure representing a diff is an array of tuples: + * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']] + * which means: delete 'Hello', add 'Goodbye' and keep ' world.' + */ + var DIFF_DELETE = -1, + DIFF_INSERT = 1, + DIFF_EQUAL = 0; + + /** + * Find the differences between two texts. Simplifies the problem by stripping + * any common prefix or suffix off the texts before diffing. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {boolean=} optChecklines Optional speedup flag. If present and false, + * then don't run a line-level diff first to identify the changed areas. + * Defaults to true, which does a faster, slightly less optimal diff. + * @return {!Array.} Array of diff tuples. + */ + DiffMatchPatch.prototype.DiffMain = function (text1, text2, optChecklines) { + var deadline, checklines, commonlength, commonprefix, commonsuffix, diffs; + + // The diff must be complete in up to 1 second. + deadline = new Date().getTime() + 1000; + + // Check for null inputs. + if (text1 === null || text2 === null) { + throw new Error("Null input. (DiffMain)"); + } + + // Check for equality (speedup). + if (text1 === text2) { + if (text1) { + return [[DIFF_EQUAL, text1]]; + } + return []; + } + + if (typeof optChecklines === "undefined") { + optChecklines = true; + } + + checklines = optChecklines; + + // Trim off common prefix (speedup). + commonlength = this.diffCommonPrefix(text1, text2); + commonprefix = text1.substring(0, commonlength); + text1 = text1.substring(commonlength); + text2 = text2.substring(commonlength); + + // Trim off common suffix (speedup). + commonlength = this.diffCommonSuffix(text1, text2); + commonsuffix = text1.substring(text1.length - commonlength); + text1 = text1.substring(0, text1.length - commonlength); + text2 = text2.substring(0, text2.length - commonlength); + + // Compute the diff on the middle block. + diffs = this.diffCompute(text1, text2, checklines, deadline); + + // Restore the prefix and suffix. + if (commonprefix) { + diffs.unshift([DIFF_EQUAL, commonprefix]); + } + if (commonsuffix) { + diffs.push([DIFF_EQUAL, commonsuffix]); + } + this.diffCleanupMerge(diffs); + return diffs; + }; + + /** + * Reduce the number of edits by eliminating operationally trivial equalities. + * @param {!Array.} diffs Array of diff tuples. + */ + DiffMatchPatch.prototype.diffCleanupEfficiency = function (diffs) { + var changes, equalities, equalitiesLength, lastequality, pointer, preIns, preDel, postIns, postDel; + changes = false; + equalities = []; // Stack of indices where equalities are found. + equalitiesLength = 0; // Keeping our own length var is faster in JS. + /** @type {?string} */ + lastequality = null; + + // Always equal to diffs[equalities[equalitiesLength - 1]][1] + pointer = 0; // Index of current position. + + // Is there an insertion operation before the last equality. + preIns = false; + + // Is there a deletion operation before the last equality. + preDel = false; + + // Is there an insertion operation after the last equality. + postIns = false; + + // Is there a deletion operation after the last equality. + postDel = false; + while (pointer < diffs.length) { + + // Equality found. + if (diffs[pointer][0] === DIFF_EQUAL) { + if (diffs[pointer][1].length < 4 && (postIns || postDel)) { + + // Candidate found. + equalities[equalitiesLength++] = pointer; + preIns = postIns; + preDel = postDel; + lastequality = diffs[pointer][1]; + } else { + + // Not a candidate, and can never become one. + equalitiesLength = 0; + lastequality = null; + } + postIns = postDel = false; + + // An insertion or deletion. + } else { + + if (diffs[pointer][0] === DIFF_DELETE) { + postDel = true; + } else { + postIns = true; + } + + /* + * Five types to be split: + * ABXYCD + * AXCD + * ABXC + * AXCD + * ABXC + */ + if (lastequality && (preIns && preDel && postIns && postDel || lastequality.length < 2 && preIns + preDel + postIns + postDel === 3)) { + + // Duplicate record. + diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]); + + // Change second copy to insert. + diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT; + equalitiesLength--; // Throw away the equality we just deleted; + lastequality = null; + if (preIns && preDel) { + + // No changes made which could affect previous entry, keep going. + postIns = postDel = true; + equalitiesLength = 0; + } else { + equalitiesLength--; // Throw away the previous equality. + pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1; + postIns = postDel = false; + } + changes = true; + } + } + pointer++; + } + + if (changes) { + this.diffCleanupMerge(diffs); + } + }; + + /** + * Convert a diff array into a pretty HTML report. + * @param {!Array.} diffs Array of diff tuples. + * @param {integer} string to be beautified. + * @return {string} HTML representation. + */ + DiffMatchPatch.prototype.diffPrettyHtml = function (diffs) { + var op, + data, + x, + html = []; + for (x = 0; x < diffs.length; x++) { + op = diffs[x][0]; // Operation (insert, delete, equal) + data = diffs[x][1]; // Text of change. + switch (op) { + case DIFF_INSERT: + html[x] = "" + escapeText(data) + ""; + break; + case DIFF_DELETE: + html[x] = "" + escapeText(data) + ""; + break; + case DIFF_EQUAL: + html[x] = "" + escapeText(data) + ""; + break; + } + } + return html.join(""); + }; + + /** + * Determine the common prefix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the start of each + * string. + */ + DiffMatchPatch.prototype.diffCommonPrefix = function (text1, text2) { + var pointermid, pointermax, pointermin, pointerstart; + + // Quick check for common null cases. + if (!text1 || !text2 || text1.charAt(0) !== text2.charAt(0)) { + return 0; + } + + // Binary search. + // Performance analysis: https://neil.fraser.name/news/2007/10/09/ + pointermin = 0; + pointermax = Math.min(text1.length, text2.length); + pointermid = pointermax; + pointerstart = 0; + while (pointermin < pointermid) { + if (text1.substring(pointerstart, pointermid) === text2.substring(pointerstart, pointermid)) { + pointermin = pointermid; + pointerstart = pointermin; + } else { + pointermax = pointermid; + } + pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); + } + return pointermid; + }; + + /** + * Determine the common suffix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the end of each string. + */ + DiffMatchPatch.prototype.diffCommonSuffix = function (text1, text2) { + var pointermid, pointermax, pointermin, pointerend; + + // Quick check for common null cases. + if (!text1 || !text2 || text1.charAt(text1.length - 1) !== text2.charAt(text2.length - 1)) { + return 0; + } + + // Binary search. + // Performance analysis: https://neil.fraser.name/news/2007/10/09/ + pointermin = 0; + pointermax = Math.min(text1.length, text2.length); + pointermid = pointermax; + pointerend = 0; + while (pointermin < pointermid) { + if (text1.substring(text1.length - pointermid, text1.length - pointerend) === text2.substring(text2.length - pointermid, text2.length - pointerend)) { + pointermin = pointermid; + pointerend = pointermin; + } else { + pointermax = pointermid; + } + pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); + } + return pointermid; + }; + + /** + * Find the differences between two texts. Assumes that the texts do not + * have any common prefix or suffix. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {boolean} checklines Speedup flag. If false, then don't run a + * line-level diff first to identify the changed areas. + * If true, then run a faster, slightly less optimal diff. + * @param {number} deadline Time when the diff should be complete by. + * @return {!Array.} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffCompute = function (text1, text2, checklines, deadline) { + var diffs, longtext, shorttext, i, hm, text1A, text2A, text1B, text2B, midCommon, diffsA, diffsB; + + if (!text1) { + + // Just add some text (speedup). + return [[DIFF_INSERT, text2]]; + } + + if (!text2) { + + // Just delete some text (speedup). + return [[DIFF_DELETE, text1]]; + } + + longtext = text1.length > text2.length ? text1 : text2; + shorttext = text1.length > text2.length ? text2 : text1; + i = longtext.indexOf(shorttext); + if (i !== -1) { + + // Shorter text is inside the longer text (speedup). + diffs = [[DIFF_INSERT, longtext.substring(0, i)], [DIFF_EQUAL, shorttext], [DIFF_INSERT, longtext.substring(i + shorttext.length)]]; + + // Swap insertions for deletions if diff is reversed. + if (text1.length > text2.length) { + diffs[0][0] = diffs[2][0] = DIFF_DELETE; + } + return diffs; + } + + if (shorttext.length === 1) { + + // Single character string. + // After the previous speedup, the character can't be an equality. + return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; + } + + // Check to see if the problem can be split in two. + hm = this.diffHalfMatch(text1, text2); + if (hm) { + + // A half-match was found, sort out the return data. + text1A = hm[0]; + text1B = hm[1]; + text2A = hm[2]; + text2B = hm[3]; + midCommon = hm[4]; + + // Send both pairs off for separate processing. + diffsA = this.DiffMain(text1A, text2A, checklines, deadline); + diffsB = this.DiffMain(text1B, text2B, checklines, deadline); + + // Merge the results. + return diffsA.concat([[DIFF_EQUAL, midCommon]], diffsB); + } + + if (checklines && text1.length > 100 && text2.length > 100) { + return this.diffLineMode(text1, text2, deadline); + } + + return this.diffBisect(text1, text2, deadline); + }; + + /** + * Do the two texts share a substring which is at least half the length of the + * longer text? + * This speedup can produce non-minimal diffs. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {Array.} Five element Array, containing the prefix of + * text1, the suffix of text1, the prefix of text2, the suffix of + * text2 and the common middle. Or null if there was no match. + * @private + */ + DiffMatchPatch.prototype.diffHalfMatch = function (text1, text2) { + var longtext, shorttext, dmp, text1A, text2B, text2A, text1B, midCommon, hm1, hm2, hm; + + longtext = text1.length > text2.length ? text1 : text2; + shorttext = text1.length > text2.length ? text2 : text1; + if (longtext.length < 4 || shorttext.length * 2 < longtext.length) { + return null; // Pointless. + } + dmp = this; // 'this' becomes 'window' in a closure. + + /** + * Does a substring of shorttext exist within longtext such that the substring + * is at least half the length of longtext? + * Closure, but does not reference any external variables. + * @param {string} longtext Longer string. + * @param {string} shorttext Shorter string. + * @param {number} i Start index of quarter length substring within longtext. + * @return {Array.} Five element Array, containing the prefix of + * longtext, the suffix of longtext, the prefix of shorttext, the suffix + * of shorttext and the common middle. Or null if there was no match. + * @private + */ + function diffHalfMatchI(longtext, shorttext, i) { + var seed, j, bestCommon, prefixLength, suffixLength, bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB; + + // Start with a 1/4 length substring at position i as a seed. + seed = longtext.substring(i, i + Math.floor(longtext.length / 4)); + j = -1; + bestCommon = ""; + while ((j = shorttext.indexOf(seed, j + 1)) !== -1) { + prefixLength = dmp.diffCommonPrefix(longtext.substring(i), shorttext.substring(j)); + suffixLength = dmp.diffCommonSuffix(longtext.substring(0, i), shorttext.substring(0, j)); + if (bestCommon.length < suffixLength + prefixLength) { + bestCommon = shorttext.substring(j - suffixLength, j) + shorttext.substring(j, j + prefixLength); + bestLongtextA = longtext.substring(0, i - suffixLength); + bestLongtextB = longtext.substring(i + prefixLength); + bestShorttextA = shorttext.substring(0, j - suffixLength); + bestShorttextB = shorttext.substring(j + prefixLength); + } + } + if (bestCommon.length * 2 >= longtext.length) { + return [bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB, bestCommon]; + } else { + return null; + } + } + + // First check if the second quarter is the seed for a half-match. + hm1 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 4)); + + // Check again based on the third quarter. + hm2 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 2)); + if (!hm1 && !hm2) { + return null; + } else if (!hm2) { + hm = hm1; + } else if (!hm1) { + hm = hm2; + } else { + + // Both matched. Select the longest. + hm = hm1[4].length > hm2[4].length ? hm1 : hm2; + } + + // A half-match was found, sort out the return data. + if (text1.length > text2.length) { + text1A = hm[0]; + text1B = hm[1]; + text2A = hm[2]; + text2B = hm[3]; + } else { + text2A = hm[0]; + text2B = hm[1]; + text1A = hm[2]; + text1B = hm[3]; + } + midCommon = hm[4]; + return [text1A, text1B, text2A, text2B, midCommon]; + }; + + /** + * Do a quick line-level diff on both strings, then rediff the parts for + * greater accuracy. + * This speedup can produce non-minimal diffs. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} deadline Time when the diff should be complete by. + * @return {!Array.} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffLineMode = function (text1, text2, deadline) { + var a, diffs, linearray, pointer, countInsert, countDelete, textInsert, textDelete, j; + + // Scan the text on a line-by-line basis first. + a = this.diffLinesToChars(text1, text2); + text1 = a.chars1; + text2 = a.chars2; + linearray = a.lineArray; + + diffs = this.DiffMain(text1, text2, false, deadline); + + // Convert the diff back to original text. + this.diffCharsToLines(diffs, linearray); + + // Eliminate freak matches (e.g. blank lines) + this.diffCleanupSemantic(diffs); + + // Rediff any replacement blocks, this time character-by-character. + // Add a dummy entry at the end. + diffs.push([DIFF_EQUAL, ""]); + pointer = 0; + countDelete = 0; + countInsert = 0; + textDelete = ""; + textInsert = ""; + while (pointer < diffs.length) { + switch (diffs[pointer][0]) { + case DIFF_INSERT: + countInsert++; + textInsert += diffs[pointer][1]; + break; + case DIFF_DELETE: + countDelete++; + textDelete += diffs[pointer][1]; + break; + case DIFF_EQUAL: + + // Upon reaching an equality, check for prior redundancies. + if (countDelete >= 1 && countInsert >= 1) { + + // Delete the offending records and add the merged ones. + diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert); + pointer = pointer - countDelete - countInsert; + a = this.DiffMain(textDelete, textInsert, false, deadline); + for (j = a.length - 1; j >= 0; j--) { + diffs.splice(pointer, 0, a[j]); + } + pointer = pointer + a.length; + } + countInsert = 0; + countDelete = 0; + textDelete = ""; + textInsert = ""; + break; + } + pointer++; + } + diffs.pop(); // Remove the dummy entry at the end. + + return diffs; + }; + + /** + * Find the 'middle snake' of a diff, split the problem in two + * and return the recursively constructed diff. + * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} deadline Time at which to bail if not yet complete. + * @return {!Array.} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffBisect = function (text1, text2, deadline) { + var text1Length, text2Length, maxD, vOffset, vLength, v1, v2, x, delta, front, k1start, k1end, k2start, k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2; + + // Cache the text lengths to prevent multiple calls. + text1Length = text1.length; + text2Length = text2.length; + maxD = Math.ceil((text1Length + text2Length) / 2); + vOffset = maxD; + vLength = 2 * maxD; + v1 = new Array(vLength); + v2 = new Array(vLength); + + // Setting all elements to -1 is faster in Chrome & Firefox than mixing + // integers and undefined. + for (x = 0; x < vLength; x++) { + v1[x] = -1; + v2[x] = -1; + } + v1[vOffset + 1] = 0; + v2[vOffset + 1] = 0; + delta = text1Length - text2Length; + + // If the total number of characters is odd, then the front path will collide + // with the reverse path. + front = delta % 2 !== 0; + + // Offsets for start and end of k loop. + // Prevents mapping of space beyond the grid. + k1start = 0; + k1end = 0; + k2start = 0; + k2end = 0; + for (d = 0; d < maxD; d++) { + + // Bail out if deadline is reached. + if (new Date().getTime() > deadline) { + break; + } + + // Walk the front path one step. + for (k1 = -d + k1start; k1 <= d - k1end; k1 += 2) { + k1Offset = vOffset + k1; + if (k1 === -d || k1 !== d && v1[k1Offset - 1] < v1[k1Offset + 1]) { + x1 = v1[k1Offset + 1]; + } else { + x1 = v1[k1Offset - 1] + 1; + } + y1 = x1 - k1; + while (x1 < text1Length && y1 < text2Length && text1.charAt(x1) === text2.charAt(y1)) { + x1++; + y1++; + } + v1[k1Offset] = x1; + if (x1 > text1Length) { + + // Ran off the right of the graph. + k1end += 2; + } else if (y1 > text2Length) { + + // Ran off the bottom of the graph. + k1start += 2; + } else if (front) { + k2Offset = vOffset + delta - k1; + if (k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] !== -1) { + + // Mirror x2 onto top-left coordinate system. + x2 = text1Length - v2[k2Offset]; + if (x1 >= x2) { + + // Overlap detected. + return this.diffBisectSplit(text1, text2, x1, y1, deadline); + } + } + } + } + + // Walk the reverse path one step. + for (k2 = -d + k2start; k2 <= d - k2end; k2 += 2) { + k2Offset = vOffset + k2; + if (k2 === -d || k2 !== d && v2[k2Offset - 1] < v2[k2Offset + 1]) { + x2 = v2[k2Offset + 1]; + } else { + x2 = v2[k2Offset - 1] + 1; + } + y2 = x2 - k2; + while (x2 < text1Length && y2 < text2Length && text1.charAt(text1Length - x2 - 1) === text2.charAt(text2Length - y2 - 1)) { + x2++; + y2++; + } + v2[k2Offset] = x2; + if (x2 > text1Length) { + + // Ran off the left of the graph. + k2end += 2; + } else if (y2 > text2Length) { + + // Ran off the top of the graph. + k2start += 2; + } else if (!front) { + k1Offset = vOffset + delta - k2; + if (k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] !== -1) { + x1 = v1[k1Offset]; + y1 = vOffset + x1 - k1Offset; + + // Mirror x2 onto top-left coordinate system. + x2 = text1Length - x2; + if (x1 >= x2) { + + // Overlap detected. + return this.diffBisectSplit(text1, text2, x1, y1, deadline); + } + } + } + } + } + + // Diff took too long and hit the deadline or + // number of diffs equals number of characters, no commonality at all. + return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; + }; + + /** + * Given the location of the 'middle snake', split the diff in two parts + * and recurse. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} x Index of split point in text1. + * @param {number} y Index of split point in text2. + * @param {number} deadline Time at which to bail if not yet complete. + * @return {!Array.} Array of diff tuples. + * @private + */ + DiffMatchPatch.prototype.diffBisectSplit = function (text1, text2, x, y, deadline) { + var text1a, text1b, text2a, text2b, diffs, diffsb; + text1a = text1.substring(0, x); + text2a = text2.substring(0, y); + text1b = text1.substring(x); + text2b = text2.substring(y); + + // Compute both diffs serially. + diffs = this.DiffMain(text1a, text2a, false, deadline); + diffsb = this.DiffMain(text1b, text2b, false, deadline); + + return diffs.concat(diffsb); + }; + + /** + * Reduce the number of edits by eliminating semantically trivial equalities. + * @param {!Array.} diffs Array of diff tuples. + */ + DiffMatchPatch.prototype.diffCleanupSemantic = function (diffs) { + var changes, equalities, equalitiesLength, lastequality, pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1, lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2; + changes = false; + equalities = []; // Stack of indices where equalities are found. + equalitiesLength = 0; // Keeping our own length var is faster in JS. + /** @type {?string} */ + lastequality = null; + + // Always equal to diffs[equalities[equalitiesLength - 1]][1] + pointer = 0; // Index of current position. + + // Number of characters that changed prior to the equality. + lengthInsertions1 = 0; + lengthDeletions1 = 0; + + // Number of characters that changed after the equality. + lengthInsertions2 = 0; + lengthDeletions2 = 0; + while (pointer < diffs.length) { + if (diffs[pointer][0] === DIFF_EQUAL) { + // Equality found. + equalities[equalitiesLength++] = pointer; + lengthInsertions1 = lengthInsertions2; + lengthDeletions1 = lengthDeletions2; + lengthInsertions2 = 0; + lengthDeletions2 = 0; + lastequality = diffs[pointer][1]; + } else { + // An insertion or deletion. + if (diffs[pointer][0] === DIFF_INSERT) { + lengthInsertions2 += diffs[pointer][1].length; + } else { + lengthDeletions2 += diffs[pointer][1].length; + } + + // Eliminate an equality that is smaller or equal to the edits on both + // sides of it. + if (lastequality && lastequality.length <= Math.max(lengthInsertions1, lengthDeletions1) && lastequality.length <= Math.max(lengthInsertions2, lengthDeletions2)) { + + // Duplicate record. + diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]); + + // Change second copy to insert. + diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT; + + // Throw away the equality we just deleted. + equalitiesLength--; + + // Throw away the previous equality (it needs to be reevaluated). + equalitiesLength--; + pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1; + + // Reset the counters. + lengthInsertions1 = 0; + lengthDeletions1 = 0; + lengthInsertions2 = 0; + lengthDeletions2 = 0; + lastequality = null; + changes = true; + } + } + pointer++; + } + + // Normalize the diff. + if (changes) { + this.diffCleanupMerge(diffs); + } + + // Find any overlaps between deletions and insertions. + // e.g: abcxxxxxxdef + // -> abcxxxdef + // e.g: xxxabcdefxxx + // -> defxxxabc + // Only extract an overlap if it is as big as the edit ahead or behind it. + pointer = 1; + while (pointer < diffs.length) { + if (diffs[pointer - 1][0] === DIFF_DELETE && diffs[pointer][0] === DIFF_INSERT) { + deletion = diffs[pointer - 1][1]; + insertion = diffs[pointer][1]; + overlapLength1 = this.diffCommonOverlap(deletion, insertion); + overlapLength2 = this.diffCommonOverlap(insertion, deletion); + if (overlapLength1 >= overlapLength2) { + if (overlapLength1 >= deletion.length / 2 || overlapLength1 >= insertion.length / 2) { + + // Overlap found. Insert an equality and trim the surrounding edits. + diffs.splice(pointer, 0, [DIFF_EQUAL, insertion.substring(0, overlapLength1)]); + diffs[pointer - 1][1] = deletion.substring(0, deletion.length - overlapLength1); + diffs[pointer + 1][1] = insertion.substring(overlapLength1); + pointer++; + } + } else { + if (overlapLength2 >= deletion.length / 2 || overlapLength2 >= insertion.length / 2) { + + // Reverse overlap found. + // Insert an equality and swap and trim the surrounding edits. + diffs.splice(pointer, 0, [DIFF_EQUAL, deletion.substring(0, overlapLength2)]); + + diffs[pointer - 1][0] = DIFF_INSERT; + diffs[pointer - 1][1] = insertion.substring(0, insertion.length - overlapLength2); + diffs[pointer + 1][0] = DIFF_DELETE; + diffs[pointer + 1][1] = deletion.substring(overlapLength2); + pointer++; + } + } + pointer++; + } + pointer++; + } + }; + + /** + * Determine if the suffix of one string is the prefix of another. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the end of the first + * string and the start of the second string. + * @private + */ + DiffMatchPatch.prototype.diffCommonOverlap = function (text1, text2) { + var text1Length, text2Length, textLength, best, length, pattern, found; + + // Cache the text lengths to prevent multiple calls. + text1Length = text1.length; + text2Length = text2.length; + + // Eliminate the null case. + if (text1Length === 0 || text2Length === 0) { + return 0; + } + + // Truncate the longer string. + if (text1Length > text2Length) { + text1 = text1.substring(text1Length - text2Length); + } else if (text1Length < text2Length) { + text2 = text2.substring(0, text1Length); + } + textLength = Math.min(text1Length, text2Length); + + // Quick check for the worst case. + if (text1 === text2) { + return textLength; + } + + // Start by looking for a single character match + // and increase length until no match is found. + // Performance analysis: https://neil.fraser.name/news/2010/11/04/ + best = 0; + length = 1; + while (true) { + pattern = text1.substring(textLength - length); + found = text2.indexOf(pattern); + if (found === -1) { + return best; + } + length += found; + if (found === 0 || text1.substring(textLength - length) === text2.substring(0, length)) { + best = length; + length++; + } + } + }; + + /** + * Split two texts into an array of strings. Reduce the texts to a string of + * hashes where each Unicode character represents one line. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {{chars1: string, chars2: string, lineArray: !Array.}} + * An object containing the encoded text1, the encoded text2 and + * the array of unique strings. + * The zeroth element of the array of unique strings is intentionally blank. + * @private + */ + DiffMatchPatch.prototype.diffLinesToChars = function (text1, text2) { + var lineArray, lineHash, chars1, chars2; + lineArray = []; // E.g. lineArray[4] === 'Hello\n' + lineHash = {}; // E.g. lineHash['Hello\n'] === 4 + + // '\x00' is a valid character, but various debuggers don't like it. + // So we'll insert a junk entry to avoid generating a null character. + lineArray[0] = ""; + + /** + * Split a text into an array of strings. Reduce the texts to a string of + * hashes where each Unicode character represents one line. + * Modifies linearray and linehash through being a closure. + * @param {string} text String to encode. + * @return {string} Encoded string. + * @private + */ + function diffLinesToCharsMunge(text) { + var chars, lineStart, lineEnd, lineArrayLength, line; + chars = ""; + + // Walk the text, pulling out a substring for each line. + // text.split('\n') would would temporarily double our memory footprint. + // Modifying text would create many large strings to garbage collect. + lineStart = 0; + lineEnd = -1; + + // Keeping our own length variable is faster than looking it up. + lineArrayLength = lineArray.length; + while (lineEnd < text.length - 1) { + lineEnd = text.indexOf("\n", lineStart); + if (lineEnd === -1) { + lineEnd = text.length - 1; + } + line = text.substring(lineStart, lineEnd + 1); + lineStart = lineEnd + 1; + + var lineHashExists = lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : lineHash[line] !== undefined; + + if (lineHashExists) { + chars += String.fromCharCode(lineHash[line]); + } else { + chars += String.fromCharCode(lineArrayLength); + lineHash[line] = lineArrayLength; + lineArray[lineArrayLength++] = line; + } + } + return chars; + } + + chars1 = diffLinesToCharsMunge(text1); + chars2 = diffLinesToCharsMunge(text2); + return { + chars1: chars1, + chars2: chars2, + lineArray: lineArray + }; + }; + + /** + * Rehydrate the text in a diff from a string of line hashes to real lines of + * text. + * @param {!Array.} diffs Array of diff tuples. + * @param {!Array.} lineArray Array of unique strings. + * @private + */ + DiffMatchPatch.prototype.diffCharsToLines = function (diffs, lineArray) { + var x, chars, text, y; + for (x = 0; x < diffs.length; x++) { + chars = diffs[x][1]; + text = []; + for (y = 0; y < chars.length; y++) { + text[y] = lineArray[chars.charCodeAt(y)]; + } + diffs[x][1] = text.join(""); + } + }; + + /** + * Reorder and merge like edit sections. Merge equalities. + * Any edit section can move as long as it doesn't cross an equality. + * @param {!Array.} diffs Array of diff tuples. + */ + DiffMatchPatch.prototype.diffCleanupMerge = function (diffs) { + var pointer, countDelete, countInsert, textInsert, textDelete, commonlength, changes, diffPointer, position; + diffs.push([DIFF_EQUAL, ""]); // Add a dummy entry at the end. + pointer = 0; + countDelete = 0; + countInsert = 0; + textDelete = ""; + textInsert = ""; + + while (pointer < diffs.length) { + switch (diffs[pointer][0]) { + case DIFF_INSERT: + countInsert++; + textInsert += diffs[pointer][1]; + pointer++; + break; + case DIFF_DELETE: + countDelete++; + textDelete += diffs[pointer][1]; + pointer++; + break; + case DIFF_EQUAL: + + // Upon reaching an equality, check for prior redundancies. + if (countDelete + countInsert > 1) { + if (countDelete !== 0 && countInsert !== 0) { + + // Factor out any common prefixes. + commonlength = this.diffCommonPrefix(textInsert, textDelete); + if (commonlength !== 0) { + if (pointer - countDelete - countInsert > 0 && diffs[pointer - countDelete - countInsert - 1][0] === DIFF_EQUAL) { + diffs[pointer - countDelete - countInsert - 1][1] += textInsert.substring(0, commonlength); + } else { + diffs.splice(0, 0, [DIFF_EQUAL, textInsert.substring(0, commonlength)]); + pointer++; + } + textInsert = textInsert.substring(commonlength); + textDelete = textDelete.substring(commonlength); + } + + // Factor out any common suffixies. + commonlength = this.diffCommonSuffix(textInsert, textDelete); + if (commonlength !== 0) { + diffs[pointer][1] = textInsert.substring(textInsert.length - commonlength) + diffs[pointer][1]; + textInsert = textInsert.substring(0, textInsert.length - commonlength); + textDelete = textDelete.substring(0, textDelete.length - commonlength); + } + } + + // Delete the offending records and add the merged ones. + if (countDelete === 0) { + diffs.splice(pointer - countInsert, countDelete + countInsert, [DIFF_INSERT, textInsert]); + } else if (countInsert === 0) { + diffs.splice(pointer - countDelete, countDelete + countInsert, [DIFF_DELETE, textDelete]); + } else { + diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert, [DIFF_DELETE, textDelete], [DIFF_INSERT, textInsert]); + } + pointer = pointer - countDelete - countInsert + (countDelete ? 1 : 0) + (countInsert ? 1 : 0) + 1; + } else if (pointer !== 0 && diffs[pointer - 1][0] === DIFF_EQUAL) { + + // Merge this equality with the previous one. + diffs[pointer - 1][1] += diffs[pointer][1]; + diffs.splice(pointer, 1); + } else { + pointer++; + } + countInsert = 0; + countDelete = 0; + textDelete = ""; + textInsert = ""; + break; + } + } + if (diffs[diffs.length - 1][1] === "") { + diffs.pop(); // Remove the dummy entry at the end. + } + + // Second pass: look for single edits surrounded on both sides by equalities + // which can be shifted sideways to eliminate an equality. + // e.g: ABAC -> ABAC + changes = false; + pointer = 1; + + // Intentionally ignore the first and last element (don't need checking). + while (pointer < diffs.length - 1) { + if (diffs[pointer - 1][0] === DIFF_EQUAL && diffs[pointer + 1][0] === DIFF_EQUAL) { + + diffPointer = diffs[pointer][1]; + position = diffPointer.substring(diffPointer.length - diffs[pointer - 1][1].length); + + // This is a single edit surrounded by equalities. + if (position === diffs[pointer - 1][1]) { + + // Shift the edit over the previous equality. + diffs[pointer][1] = diffs[pointer - 1][1] + diffs[pointer][1].substring(0, diffs[pointer][1].length - diffs[pointer - 1][1].length); + diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1]; + diffs.splice(pointer - 1, 1); + changes = true; + } else if (diffPointer.substring(0, diffs[pointer + 1][1].length) === diffs[pointer + 1][1]) { + + // Shift the edit over the next equality. + diffs[pointer - 1][1] += diffs[pointer + 1][1]; + diffs[pointer][1] = diffs[pointer][1].substring(diffs[pointer + 1][1].length) + diffs[pointer + 1][1]; + diffs.splice(pointer + 1, 1); + changes = true; + } + } + pointer++; + } + + // If shifts were made, the diff needs reordering and another shift sweep. + if (changes) { + this.diffCleanupMerge(diffs); + } + }; + + return function (o, n) { + var diff, output, text; + diff = new DiffMatchPatch(); + output = diff.DiffMain(o, n); + diff.diffCleanupEfficiency(output); + text = diff.diffPrettyHtml(output); + + return text; + }; + }(); + + }((function() { return this; }()))); \ No newline at end of file diff --git a/tests/libs/qunit/qunit.d.ts b/tests/libs/qunit/qunit.d.ts new file mode 100644 index 000000000..f348bc4aa --- /dev/null +++ b/tests/libs/qunit/qunit.d.ts @@ -0,0 +1,600 @@ +// Type definitions for QUnit v2.0.1 +// Project: http://qunitjs.com/ +// Definitions by: James Bracy +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +interface Assert +{ + /** + * Instruct QUnit to wait for an asynchronous operation. + * + * The callback returned from `assert.async()` will throw an Error if it is + * invoked more than once (or more often than the accepted call count, if + * provided). + * + * This replaces functionality previously provided by `QUnit.stop()` and + * `QUnit.start()`. + * + * @param {number} [acceptCallCount=1] Number of expected callbacks before the test is done. + */ + async(acceptCallCount?: number): () => void; + + /** + * A deep recursive comparison, working on primitive types, arrays, objects, + * regular expressions, dates and functions. + * + * The `deepEqual()` assertion can be used just like `equal()` when comparing + * the value of objects, such that `{ key: value }` is equal to + * `{ key: value }`. For non-scalar values, identity will be disregarded by + * deepEqual. + * + * `notDeepEqual()` can be used to explicitly test deep, strict inequality. + * + * @param actual Object or Expression being tested + * @param expected Known comparision value + * @param {string} [message] A short description of the assertion + */ + deepEqual(actual: any, expected: any, message?: string): void; + + /** + * A non-strict comparison, roughly equivalent to JUnit's assertEquals. + * + * The `equal` assertion uses the simple comparison operator (`==`) to + * compare the actual and expected arguments. When they are equal, the + * assertion passes; otherwise, it fails. When it fails, both actual and + * expected values are displayed in the test result, in addition to a given + * message. + * + * `notEqual()` can be used to explicitly test inequality. + * + * `strictEqual()` can be used to test strict equality. + * + * @param actual Expression being tested + * @param expected Known comparison value + * @param {string} [message] A short description of the assertion + */ + equal(actual: any, expected: any, message?: string): void; + + /** + * Specify how many assertions are expected to run within a test. + * + * To ensure that an explicit number of assertions are run within any test, + * use `assert.expect( number )` to register an expected count. If the + * number of assertions run does not match the expected count, the test will + * fail. + * + * @param {number} amount Number of assertions in this test. + */ + expect(amount: number): void; + + /** + * An inverted deep recursive comparison, working on primitive types, + * arrays, objects, regular expressions, dates and functions. + * + * @param actual Object or Expression being tested + * @param expected Known comparison value + * @param {string} [message] A short description of the assertion + */ + notDeepEqual(actual: any, expected: any, message?: string): void; + + /** + * A non-strict comparison, checking for inequality. + * + * The `notEqual` assertion uses the simple inverted comparison operator + * (`!=`) to compare the actual and expected arguments. When they aren't + * equal, the assertion passes; otherwise, it fails. When it fails, both + * actual and expected values are displayed in the test result, in addition + * to a given message. + * + * `equal()` can be used to test equality. + * + * `notStrictEqual()` can be used to test strict inequality. + * + * @param actual Object or Expression being tested + * @param expected Known comparison value + * @param {string} [message] A short description of the assertion + */ + notEqual(actual: any, expected: any, message?: string): void; + + /** + * A boolean check, inverse of `ok()` and CommonJS's `assert.ok()`, and + * equivalent to JUnit's `assertFalse()`. Passes if the first argument is + * falsy. + * + * `notOk()` requires just one argument. If the argument evaluates to false, + * the assertion passes; otherwise, it fails. If a second message argument + * is provided, it will be displayed in place of the result. + * + * @param state Expression being tested + * @param {string} [message] A short description of the assertion + */ + notOk(state: any, message?: string): void; + + /** + * A strict comparison of an object's own properties, checking for inequality. + * + * The `notPropEqual` assertion uses the strict inverted comparison operator + * (`!==`) to compare the actual and expected arguments as Objects regarding + * only their properties but not their constructors. + * + * When they aren't equal, the assertion passes; otherwise, it fails. When + * it fails, both actual and expected values are displayed in the test + * result, in addition to a given message. + * + * `equal()` can be used to test equality. + * + * `propEqual()` can be used to test strict equality of an Object properties. + * + * @param actual Object or Expression being tested + * @param expected Known comparison value + * @param {string} [message] A short description of the assertion + */ + notPropEqual(actual: any, expected: any, message?: string): void; + + /** + * A strict comparison, checking for inequality. + * + * The `notStrictEqual` assertion uses the strict inverted comparison + * operator (`!==`) to compare the actual and expected arguments. When they + * aren't equal, the assertion passes; otherwise, it fails. When it fails, + * both actual and expected values are displayed in the test result, in + * addition to a given message. + * + * `equal()` can be used to test equality. + * + * `strictEqual()` can be used to test strict equality. + * + * @param actual Object or Expression being tested + * @param expected Known comparison value + * @param {string} [message] A short description of the assertion + */ + notStrictEqual(actual: any, expected: any, message?: string): void; + + /** + * A boolean check, equivalent to CommonJS's assert.ok() and JUnit's + * assertTrue(). Passes if the first argument is truthy. + * + * The most basic assertion in QUnit, ok() requires just one argument. If + * the argument evaluates to true, the assertion passes; otherwise, it + * fails. If a second message argument is provided, it will be displayed in + * place of the result. + * + * @param state Expression being tested + * @param {string} message A short description of the assertion + */ + ok(state: any, message?: string): void; + + /** + * A strict type and value comparison of an object's own properties. + * + * The `propEqual()` assertion provides strictly (`===`) comparison of + * Object properties. Unlike `deepEqual()`, this assertion can be used to + * compare two objects made with different constructors and prototype. + * + * `strictEqual()` can be used to test strict equality. + * + * `notPropEqual()` can be used to explicitly test strict inequality of + * Object properties. + * + * @param actual Object or Expression being tested + * @param expected Known comparison value + * @param {string} [message] A short description of the assertion + */ + propEqual(actual: any, expected: any, message?: string): void; + + /** + * Report the result of a custom assertion + * + * Some test suites may need to express an expectation that is not defined + * by any of QUnit's built-in assertions. This need may be met by + * encapsulating the expectation in a JavaScript function which returns a + * `Boolean` value representing the result; this value can then be passed + * into QUnit's `ok` assertion. + * + * A more readable solution would involve defining a custom assertion. If + * the expectation function invokes `pushResult`, QUnit will be notified of + * the result and report it accordingly. + * + * @param assertionResult The assertion result + */ + pushResult(assertResult: { + result: boolean; + actual: any; + expected: any; + message: string; + }): void; + + /** + * A strict type and value comparison. + * + * The `strictEqual()` assertion provides the most rigid comparison of type + * and value with the strict equality operator (`===`). + * + * `equal()` can be used to test non-strict equality. + * + * `notStrictEqual()` can be used to explicitly test strict inequality. + * + * @param actual Object or Expression being tested + * @param expected Known comparison value + * @param {string} [message] A short description of the assertion + */ + strictEqual(actual: any, expected: any, message?: string): void; + + /** + * Test if a callback throws an exception, and optionally compare the thrown + * error. + * + * When testing code that is expected to throw an exception based on a + * specific set of circumstances, use assert.throws() to catch the error + * object for testing and comparison. + * + * In very few environments, like Closure Compiler, throws is considered a + * reserved word and will cause an error. For that case, an alias is bundled + * called `raises`. It has the same signature and behaviour, just a + * different name. + */ + throws(block: () => void, expected?: any, message?: any): void; + raises(block: () => void, expected?: any, message?: any): void; + +} + +interface Config +{ + altertitle: boolean; + autostart: boolean; + collapse: boolean; + current: any; + filter: string | RegExp + fixture: string; + hidepassed: boolean; + maxDepth: number; + module: string; + moduleId: string[]; + notrycatch: boolean; + noglobals: boolean; + seed: string; + reorder: boolean; + requireExpects: boolean; + testId: string[]; + testTimeout: number; + scrolltop: boolean; + urlConfig: { + id?: string; + label?: string; + tooltip?: string; + value?: string | string[] | { [key: string]: string } + }[]; +} + +interface Hooks +{ + + /** + * Runs after the last test. If additional tests are defined after the + * module's queue has emptied, it will not run this hook again. + */ + after?: (assert: Assert) => void; + + /** + * Runs after each test. + */ + afterEach?: (assert: Assert) => void; + + /** + * Runs before the first test. + */ + before?: (assert: Assert) => void; + + /** + * Runs before each test. + */ + beforeEach?: (assert: Assert) => void; + +} + +interface NestedHooks +{ + /** + * Runs after the last test. If additional tests are defined after the + * module's queue has emptied, it will not run this hook again. + */ + after: (fn: (assert: Assert) => void) => void; + + /** + * Runs after each test. + */ + afterEach: (fn: (assert: Assert) => void) => void; + + /** + * Runs before the first test. + */ + before: (fn: (assert: Assert) => void) => void; + + /** + * Runs before each test. + */ + beforeEach: (fn: (assert: Assert) => void) => void; + +} + +interface QUnit +{ + + /** + * Namespace for QUnit assertions + * + * QUnit's built-in assertions are defined on the `QUnit.assert` object. An + * instance of this object is passed as the only argument to the `QUnit.test` + * function callback. + * + * This object has properties for each of QUnit's built-in assertion methods. + */ + assert: Assert; + + /** + * Register a callback to fire whenever the test suite begins. + * + * `QUnit.begin()` is called once before running any tests. + * + * @callback callback Callback to execute. + */ + begin(callback: (details: { totalTests: number }) => void): void; + + /** + * Configuration for QUnit + * + * QUnit has a bunch of internal configuration defaults, some of which are + * useful to override. Check the description for each option for details. + */ + config: Config + + /** + * Register a callback to fire whenever the test suite ends. + * + * @param callback Callback to execute + */ + done(callback: (details: { failed: number, passed: number, total: number, runtime: number }) => void): void; + + /** + * Advanced and extensible data dumping for JavaScript. + * + * This method does string serialization by parsing data structures and + * objects. It parses DOM elements to a string representation of their outer + * HTML. By default, nested structures will be displayed up to five levels + * deep. Anything beyond that is replaced by `[object Object]` and + * `[object Array]` placeholders. + * + * If you need more or less output, change the value of `QUnit.dump.maxDepth`, + * representing how deep the elements should be parsed. + * + * Note: This method used to be in QUnit.jsDump, which was changed to + * QUnit.dump. The old property will be removed in QUnit 3.0. + */ + dump: { + maxDepth: number; + parse(data: any): string + }; + + /** + * Copy the properties defined by the `mixin` object into the `target` object. + * + * This method will modify the `target` object to contain the "own" properties + * defined by the `mixin`. If the `mixin` object specifies the value of any + * attribute as undefined, this property will instead be removed from the + * `target` object. + * + * @param target An object whose properties are to be modified + * @param mixin An object describing which properties should be modified + */ + extend(target: any, mixin: any): void; + + /** + * Register a callback to fire whenever an assertion completes. + * + * This is one of several callbacks QUnit provides. Its intended for + * integration scenarios like PhantomJS or Jenkins. The properties of the + * details argument are listed below as options. + * + * @param callback Callback to execute + */ + log(callback: (details: { + result: boolean, + actual: any; + expected: any; + message: string; + source: string; + module: string; + name: string; + runtime: number; + }) => void): void; + + /** + * Group related tests under a single label. + * + * You can use the module name to organize, select, and filter tests to run. + * + * All tests inside a module callback function will be grouped into that + * module. The test names will all be preceded by the module name in the + * test results. Other modules can be nested inside this callback function, + * where their tests' names will be labeled by their names recursively + * prefixed by their parent modules. + * + * If `QUnit.module` is defined without a `nested` callback argument, all + * subsequently defined tests will be grouped into the module until another + * module is defined. + * + * Modules with test group functions allow you to define nested modules, and + * QUnit will run tests on the parent module before going deep on the nested + * ones, even if they're declared first. Additionally, any hook callbacks on + * a parent module will wrap the hooks on a nested module. In other words, + * `before` and `beforeEach` callbacks will form a queue while the + * `afterEach` and `after` callbacks will form a stack. + * + * You can specify code to run before and after tests using the hooks + * argument, and also to create properties that will be shared on the + * testing context. Any additional properties on the `hooks` object will be + * added to that context. The `hooks` argument is still optional if you call + * `QUnit.module` with a callback argument. + * + * The module's callback is invoked with the test environment as its `this` + * context, with the environment's properties copied to the module's tests, + * hooks, and nested modules. Note that changes on tests' `this` are not + * preserved between sibling tests, where `this` will be reset to the initial + * value for each test. + * + * @param {string} name Label for this group of tests + * @param hookds Callbacks to run during test execution + * @param nested A callback with grouped tests and nested modules to run under the current module label + */ + module(name: string, hooks?: Hooks, nested?: (hooks: NestedHooks) => void): void; + module(name: string, nested?: (hooks: NestedHooks) => void): void; + + /** + * Register a callback to fire whenever a module ends. + * + * @param callback Callback to execute + */ + moduleDone(callback: (details: { + name: string; + failed: number; + passed: number; + total: number; + runtime: number; + }) => void): void; + + /** + * Register a callback to fire whenever a module begins. + * + * @param callback Callback to execute + */ + moduleStart(callback: (details: { name: string }) => void): void; + + /** + * Adds a test to exclusively run, preventing all other tests from running. + * + * Use this method to focus your test suite on a specific test. QUnit.only + * will cause any other tests in your suite to be ignored. + * + * Note, that if more than one QUnit.only is present only the first instance + * will run. + * + * This is an alternative to filtering tests to run in the HTML reporter. It + * is especially useful when you use a console reporter or in a codebase + * with a large set of long running tests. + * + * @param {string} name Title of unit being tested + * @param callback Function to close over assertions + */ + only(name: string, callback: (assert: Assert) => void): void; + + /** + * DEPRECATED: Report the result of a custom assertion. + * + * This method is deprecated and it's recommended to use pushResult on its + * direct reference in the assertion context. + * + * QUnit.push reflects to the current running test, and it may leak + * assertions in asynchronous mode. Checkout assert.pushResult() to set a + * proper custom assertion. + * + * Invoking QUnit.push allows to create a readable expectation that is not + * defined by any of QUnit's built-in assertions. + * + * @deprecated + */ + push(result: boolean, actual: any, expected: any, message: string): void; + + /** + * Adds a test like object to be skipped. + * + * Use this method to replace QUnit.test() instead of commenting out entire + * tests. + * + * This test's prototype will be listed on the suite as a skipped test, + * ignoring the callback argument and the respective global and module's + * hooks. + * + * @param {string} Title of unit being tested + */ + skip(name: string, callback?: (assert: Assert) => void): void; + + /** + * Returns a single line string representing the stacktrace (call stack). + * + * This method returns a single line string representing the stacktrace from + * where it was called. According to its offset argument, `QUnit.stack()` will + * return the correspondent line from the call stack. + * + * The default `offset` is `0` and will return the current location where it + * was called. + * + * Not all browsers support retrieving stracktraces. In those, `QUnit.stack()` + * will return undefined. + * + * @param {number} offset Set the stacktrace line offset. + */ + stack(offset?: number): string; + + /** + * `QUnit.start()` must be used to start a test run that has + * `QUnit.config.autostart` set to `false`. + * + * This method was previously used to control async tests on text contexts + * along with QUnit.stop. For asynchronous tests, use assert.async instead. + * + * When your async test has multiple exit points, call `QUnit.start()` for the + * corresponding number of `QUnit.stop()` increments. + */ + start(): void; + + /** + * Add a test to run. + * + * Add a test to run using `QUnit.test()`. + * + * The `assert` argument to the callback contains all of QUnit's assertion + * methods. Use this argument to call your test assertions. + * + * `QUnit.test()` can automatically handle the asynchronous resolution of a + * Promise on your behalf if you return a thenable Promise as the result of + * your callback function. + * + * @param {string} Title of unit being tested + * @param callback Function to close over assertions + */ + test(name: string, callback: (assert: Assert) => void): void; + + /** + * Register a callback to fire whenever a test ends. + * + * @param callback Callback to execute + */ + testDone(callback: (details: { + name: string; + module: string; + failed: number; + passed: number; + total: number; + runtime: number; + }) => void): void; + + /** + * Register a callback to fire whenever a test begins. + * + * @param callback Callback to execute + */ + testStart(callback: (details: { name: string; module: string; }) => void): void; + + /** + * Are the test running from the server or not. + */ + isLocal: boolean; + + /** + * QUnit version + */ + version: string; + +} + +/* QUnit */ +declare const QUnit: QUnit; \ No newline at end of file diff --git a/tests/src/Body.ts b/tests/src/Body.ts new file mode 100644 index 000000000..894f4f81a --- /dev/null +++ b/tests/src/Body.ts @@ -0,0 +1,160 @@ +namespace CANNON +{ + QUnit.module("Body", () => + { + + QUnit.test("computeAABB box", (test) => + { + var body = new Body({ mass: 1 }); + body.addShape(new Box(new Vector3(1, 1, 1))); + body.computeAABB(); + test.equal(body.aabb.min.x, -1); + test.equal(body.aabb.min.y, -1); + test.equal(body.aabb.min.z, -1); + test.equal(body.aabb.max.x, 1); + test.equal(body.aabb.max.y, 1); + test.equal(body.aabb.max.z, 1); + + body.position.x = 1; + body.computeAABB(); + + test.equal(body.aabb.min.x, 0); + test.equal(body.aabb.max.x, 2); + }); + + QUnit.test("computeAABB boxOffset", (test) => + { + var quaternion = new Quaternion(); + quaternion.fromAxisAngle(new Vector3(0, 0, 1), Math.PI / 2); + var body = new Body({ mass: 1 }); + body.addShape(new Box(new Vector3(1, 1, 1)), new Vector3(1, 1, 1)); + body.computeAABB(); + test.equal(body.aabb.min.x, 0); + test.equal(body.aabb.min.y, 0); + test.equal(body.aabb.min.z, 0); + test.equal(body.aabb.max.x, 2); + test.equal(body.aabb.max.y, 2); + test.equal(body.aabb.max.z, 2); + + body.position.x = 1; + body.computeAABB(); + + test.equal(body.aabb.min.x, 1); + test.equal(body.aabb.max.x, 3); + + }); + + QUnit.test("updateInertiaWorld", (test) => + { + var body = new Body({ mass: 1 }); + body.addShape(new Box(new Vector3(1, 1, 1))); + body.quaternion.fromEuler(Math.PI / 2, 0, 0); + body.updateInertiaWorld(); + test.ok(true); + }); + + QUnit.test("pointToLocalFrame", (test) => + { + var body = new Body({ mass: 1 }); + body.addShape(new Sphere(1)); + body.position.set(1, 2, 2); + var localPoint = body.pointToLocalFrame(new Vector3(1, 2, 3)); + test.ok(localPoint.equals(new Vector3(0, 0, 1))); + }); + + QUnit.test("pointToWorldFrame", (test) => + { + var body = new Body({ mass: 1 }); + body.addShape(new Sphere(1)); + body.position.set(1, 2, 2); + var worldPoint = body.pointToWorldFrame(new Vector3(1, 0, 0)); + test.ok(worldPoint.equals(new Vector3(2, 2, 2))); + }); + + QUnit.test("addShape", (test) => + { + var sphereShape = new Sphere(1); + + var bodyA = new Body({ + mass: 1, + shape: sphereShape + }); + var bodyB = new Body({ + mass: 1 + }); + bodyB.addShape(sphereShape); + + test.deepEqual(bodyA.shapes, bodyB.shapes, 'Adding shape via options did not work.'); + test.deepEqual(bodyA.inertia, bodyB.inertia); + }); + + QUnit.test("applyForce", (test) => + { + var sphereShape = new Sphere(1); + var body = new Body({ + mass: 1, + shape: sphereShape + }); + + var worldPoint = new Vector3(1, 0, 0); + var forceVector = new Vector3(0, 1, 0); + body.applyForce(forceVector, worldPoint); + test.deepEqual(body.force, forceVector); + test.deepEqual(body.torque, new Vector3(0, 0, 1)); + + }); + + QUnit.test("applyLocalForce", (test) => + { + var sphereShape = new Sphere(1); + var body = new Body({ + mass: 1, + shape: sphereShape + }); + body.quaternion.fromAxisAngle(new Vector3(1, 0, 0), Math.PI / 2); + + var localPoint = new Vector3(1, 0, 0); + var localForceVector = new Vector3(0, 1, 0); + body.applyLocalForce(localForceVector, localPoint); + test.ok(body.force.equals(new Vector3(0, 0, 1))); // The force is rotated to world space + + }); + + QUnit.test("applyImpulse", (test) => + { + var sphereShape = new Sphere(1); + var body = new Body({ + mass: 1, + shape: sphereShape + }); + + var f = 1000; + var dt = 1 / 60; + var worldPoint = new Vector3(0, 0, 0); + var impulse = new Vector3(f * dt, 0, 0); + body.applyImpulse(impulse, worldPoint); + + test.ok(body.velocity.equals(new Vector3(f * dt, 0, 0))); + + }); + + QUnit.test("applyLocalImpulse", (test) => + { + var sphereShape = new Sphere(1); + var body = new Body({ + mass: 1, + shape: sphereShape + }); + body.quaternion.fromAxisAngle(new Vector3(1, 0, 0), Math.PI / 2); + + var f = 1000; + var dt = 1 / 60; + var localPoint = new Vector3(1, 0, 0); + var localImpulseVector = new Vector3(0, f * dt, 0); + body.applyLocalImpulse(localImpulseVector, localPoint); + test.ok(body.velocity.equals(new Vector3(0, 0, f * dt))); // The force is rotated to world space + + }); + + }); +} \ No newline at end of file diff --git a/tests/src/Box.ts b/tests/src/Box.ts new file mode 100644 index 000000000..6c2a61616 --- /dev/null +++ b/tests/src/Box.ts @@ -0,0 +1,42 @@ +namespace CANNON +{ + QUnit.module("Box", () => + { + + QUnit.test("forEachWOrldCorner", (test) => + { + var box = new Box(new Vector3(1, 1, 1)); + var pos = new Vector3(); + var quat = new Quaternion(); + quat.fromAxisAngle(new Vector3(0, 0, 1), Math.PI * 0.25); + var numCorners = 0; + var unique = []; + box.forEachWorldCorner(pos, quat, function (x, y, z) + { + var corner = new Vector3(x, y, z); + for (var i = 0; i < unique.length; i++) + { + test.ok(!corner.equals(unique[i]), "Corners " + i + " and " + numCorners + " are almost equal: (" + unique[i].toString() + ") == (" + corner.toString() + ")"); + } + unique.push(corner); + numCorners++; + }); + test.equal(numCorners, 8); + }); + + QUnit.test("calculateWorldAABB", (test) => + { + var box = new Box(new Vector3(1, 1, 1)); + var min = new Vector3(); + var max = new Vector3(); + box.calculateWorldAABB(new Vector3(3, 0, 0), + new Quaternion(0, 0, 0, 1), + min, + max); + test.equal(min.x, 2); + test.equal(max.x, 4); + test.equal(min.y, -1); + test.equal(max.y, 1); + }); + }); +} diff --git a/tests/src/Box3.ts b/tests/src/Box3.ts new file mode 100644 index 000000000..a51ee2060 --- /dev/null +++ b/tests/src/Box3.ts @@ -0,0 +1,163 @@ +namespace CANNON +{ + QUnit.module("Box3", () => + { + + QUnit.test("construct", (test) => + { + new Box3(); + test.ok(true); + }); + + QUnit.test("copy", (test) => + { + var a = new Box3(), + b = new Box3(); + a.max.set(1, 2, 3); + b.copy(a); + test.deepEqual(a, b); + }); + + QUnit.test("clone", (test) => + { + var a = new Box3(new Vector3(-1, -2, -3), new Vector3(1, 2, 3)); + var b = a.clone(); + + test.deepEqual(a, b); + + test.equal(a === b, false); + }); + + QUnit.test("extend", (test) => + { + var a = new Box3(new Vector3(-1, -1, -1), new Vector3(1, 1, 1)); + var b = new Box3(new Vector3(-2, -2, -2), new Vector3(2, 2, 2)); + a.union(b); + test.deepEqual(a, b); + + a = new Box3(new Vector3(-1, -1, -1), new Vector3(1, 1, 1)); + b = new Box3(new Vector3(-2, -2, -2), new Vector3(2, 2, 2)); + b.union(a); + test.deepEqual(b.min, new Vector3(-2, -2, -2)); + test.deepEqual(b.max, new Vector3(2, 2, 2)); + + a = new Box3(new Vector3(-2, -1, -1), new Vector3(2, 1, 1)); + b = new Box3(new Vector3(-1, -1, -1), new Vector3(1, 1, 1)); + b.union(a); + test.deepEqual(a.min, new Vector3(-2, -1, -1)); + test.deepEqual(a.max, new Vector3(2, 1, 1)); + }); + + QUnit.test("extend", (test) => + { + var a = new Box3(), + b = new Box3(); + + // Same aabb + a.min.set(-1, -1, 0); + a.max.set(1, 1, 0); + b.min.set(-1, -1, 0); + b.max.set(1, 1, 0); + test.ok(a.overlaps(b), 'should detect overlap'); + + // Corner overlaps + b.min.set(1, 1, 0); + b.max.set(2, 2, 0); + test.ok(a.overlaps(b), 'should detect corner overlap'); + + // Separate + b.min.set(1.1, 1.1, 0); + test.ok(!a.overlaps(b), 'should detect separated'); + + // fully inside + b.min.set(-0.5, -0.5, 0); + b.max.set(0.5, 0.5, 0); + test.ok(a.overlaps(b), 'should detect if aabb is fully inside other aabb'); + b.min.set(-1.5, -1.5, 0); + b.max.set(1.5, 1.5, 0); + test.ok(a.overlaps(b), 'should detect if aabb is fully inside other aabb'); + + // Translated + b.min.set(-3, -0.5, 0); + b.max.set(-2, 0.5, 0); + test.ok(!a.overlaps(b), 'should detect translated'); + + }); + + QUnit.test("contains", (test) => + { + var a = new Box3(), + b = new Box3(); + + a.min.set(-1, -1, -1); + a.max.set(1, 1, 1); + b.min.set(-1, -1, -1); + b.max.set(1, 1, 1); + + test.ok(a.contains(b)); + + a.min.set(-2, -2, -2); + a.max.set(2, 2, 2); + + test.ok(a.contains(b)); + + b.min.set(-3, -3, -3); + b.max.set(3, 3, 3); + + test.equal(a.contains(b), false); + + a.min.set(0, 0, 0); + a.max.set(2, 2, 2); + b.min.set(-1, -1, -1); + b.max.set(1, 1, 1); + + test.equal(a.contains(b), false); + }); + + QUnit.test("toLocalFrame", (test) => + { + var worldAABB = new Box3(); + var localAABB = new Box3(); + var frame = new Transform(); + + worldAABB.min.set(-1, -1, -1); + worldAABB.max.set(1, 1, 1); + + // No transform - should stay the same + frame.toLocalFrameBox3(worldAABB, localAABB); + test.deepEqual(localAABB, worldAABB); + + // Some translation + frame.position.set(-1, 0, 0); + frame.toLocalFrameBox3(worldAABB, localAABB); + test.deepEqual( + localAABB, + new Box3(new Vector3(0, -1, -1), new Vector3(2, 1, 1)) + ); + + }); + + QUnit.test("toWorldFrame", (test) => + { + var localAABB = new Box3(); + var worldAABB = new Box3(); + var frame = new Transform(); + + localAABB.min.set(-1, -1, -1); + localAABB.max.set(1, 1, 1); + + // No transform - should stay the same + frame.toLocalFrameBox3(localAABB, worldAABB); + test.deepEqual(localAABB, worldAABB); + + // Some translation on the frame + frame.position.set(1, 0, 0); + frame.toWorldFrameBox3(localAABB, worldAABB); + test.deepEqual( + worldAABB, + new Box3(new Vector3(0, -1, -1), new Vector3(2, 1, 1)) + ); + + }); + }); +} \ No newline at end of file diff --git a/tests/src/Constraint.ts b/tests/src/Constraint.ts new file mode 100644 index 000000000..1cceb7c09 --- /dev/null +++ b/tests/src/Constraint.ts @@ -0,0 +1,31 @@ +namespace CANNON +{ + QUnit.module("Constraint", () => + { + + QUnit.test("construct", (test) => + { + var bodyA = new Body(); + var bodyB = new Body(); + new Constraint(bodyA, bodyB); + test.ok(true); + }); + + QUnit.test("enable", (test) => + { + var bodyA = new Body(); + var bodyB = new Body(); + var c = new Constraint(bodyA, bodyB); + var eq = new Equation(bodyA, bodyB); + c.equations.push(eq); + + c.enable(); + test.ok(eq.enabled); + + c.disable(); + test.ok(!eq.enabled); + + }); + }); +} + diff --git a/tests/src/ContactEquation.ts b/tests/src/ContactEquation.ts new file mode 100644 index 000000000..23da82bf8 --- /dev/null +++ b/tests/src/ContactEquation.ts @@ -0,0 +1,32 @@ +namespace CANNON +{ + QUnit.module("ContactEquation", () => + { + + QUnit.test("construct", (test) => + { + var bodyA = new Body(); + var bodyB = new Body(); + new ContactEquation(bodyA, bodyB); + test.ok(true); + }); + + QUnit.test("getImpactVelocityAlongNormal", (test) => + { + var bodyA = new Body({ + position: new Vector3(1, 0, 0), + velocity: new Vector3(-10, 0, 0) + }); + var bodyB = new Body({ + position: new Vector3(-1, 0, 0), + velocity: new Vector3(1, 0, 0) + }); + var contact = new ContactEquation(bodyA, bodyB); + contact.ni.set(1, 0, 0); + contact.ri.set(-1, 0, 0); + contact.rj.set(1, 0, 0); + var v = contact.getImpactVelocityAlongNormal(); + test.equal(v, -11); + }); + }); +} \ No newline at end of file diff --git a/tests/src/ConvexPolyhedron.ts b/tests/src/ConvexPolyhedron.ts new file mode 100644 index 000000000..ed3985468 --- /dev/null +++ b/tests/src/ConvexPolyhedron.ts @@ -0,0 +1,199 @@ +namespace CANNON +{ + function createBoxHull(size?) + { + size = (size === undefined ? 0.5 : size); + + var box = new Box(new Vector3(size, size, size)); + return box.convexPolyhedronRepresentation; + } + + function createPolyBox(sx, sy, sz) + { + var v = Vector3; + var box = new Box(new Vector3(sx, sy, sz)); + return box.convexPolyhedronRepresentation; + } + + QUnit.module("ConvexPolyhedron", () => + { + QUnit.test("calculateWorldAABB", (test) => + { + var poly = createPolyBox(1, 1, 1); + var min = new Vector3(); + var max = new Vector3(); + poly.calculateWorldAABB(new Vector3(1, 0, 0), // Translate 2 x in world + new Quaternion(0, 0, 0, 1), + min, + max); + test.equal(min.x, 0); + test.equal(max.x, 2); + test.equal(min.y, -1); + test.equal(max.y, 1); + }); + + QUnit.test("clipFaceAgainstPlane", (test) => + { + var h = createBoxHull(); + + // Four points 1 unit below the plane z=0 - we assume to get back 4 + var inverts = [new Vector3(-0.2, -0.2, -1), + new Vector3(-0.2, 0.2, -1), + new Vector3(0.2, 0.2, -1), + new Vector3(0.2, -0.2, -1)]; + var outverts = []; + h.clipFaceAgainstPlane(inverts, outverts, new Vector3(0, 0, 1), 0.0); + test.equal(outverts.length, 4, "did not get the assumed 4 vertices"); + inverts = []; + outverts = []; + + // Lower the plane to z=-2, we assume no points back + h.clipFaceAgainstPlane(inverts, outverts, new Vector3(0, 0, 1), 2); + test.equal(outverts.length, 0, "got more than zero vertices left after clipping!"); + + // two points below, two over. We get four points back, though 2 of them are clipped to + // the back of the plane + var inverts2 = [new Vector3(-2, -2, 1), + new Vector3(-2, 2, 1), + new Vector3(2, 2, -1), + new Vector3(2, -2, -1)]; + outverts = []; + h.clipFaceAgainstPlane(inverts2, outverts, new Vector3(0, 0, 1), 0.0); + test.equal(outverts.length, 4, "Expected 4 points back from clipping a quad with plane, got " + outverts.length); + }); + + QUnit.test("clipFaceAgainstHull", (test) => + { + // Create box + var hullA = createBoxHull(0.5); + var res = []; + var sepNormal = new Vector3(0, 0, 1); + + // Move the box 0.45 units up - only 0.05 units of the box will be below plane z=0 + var posA = new Vector3(0, 0, 0.45), + quatA = new Quaternion(); + + // All points from B is in the plane z=0 + var worldVertsB = [new Vector3(-1.0, -1.0, 0), + new Vector3(-1.0, 1.0, 0), + new Vector3(1.0, 1.0, 0), + new Vector3(1.0, -1.0, 0)]; + + // We will now clip a face in hullA that is closest to the sepNormal + // against the points in worldVertsB. + // We can expect to get back the 4 corners of the box hullA penetrated 0.05 units + // into the plane worldVertsB we constructed + hullA.clipFaceAgainstHull(sepNormal, posA, quatA, worldVertsB, -100, 100, res); + test.ok(true); + }); + + QUnit.test("clipAgainstHull", (test) => + { + var hullA = createBoxHull(0.6), + posA = new Vector3(-0.5, 0, 0), + quatA = new Quaternion(); + + var hullB = createBoxHull(0.5), + posB = new Vector3(0.5, 0, 0), + quatB = new Quaternion(); + + var sepaxis = new Vector3(); + var found = hullA.findSeparatingAxis(hullB, posA, quatA, posB, quatB, sepaxis); + var result = []; + //hullA.clipAgainstHull(posA,quatA,hullB,posB,quatB,sepaxis,-100,100,result); + quatB.fromAxisAngle(new Vector3(0, 0, 1), Math.PI / 4); + //console.log("clipping...."); + hullA.clipAgainstHull(posA, quatA, hullB, posB, quatB, sepaxis, -100, 100, result); + //console.log("result:",result); + //console.log("done...."); + test.ok(true); + }); + + QUnit.test("testSepAxis", (test) => + { + test.expect(3); + var hullA = createBoxHull(0.5), + posA = new Vector3(-0.2, 0, 0), + quatA = new Quaternion(); + + var hullB = createBoxHull(), + posB = new Vector3(0.2, 0, 0), + quatB = new Quaternion(); + + var sepAxis = new Vector3(1, 0, 0); + var found1 = hullA.testSepAxis(sepAxis, hullB, posA, quatA, posB, quatB); + test.equal(found1, 0.6, "didnt find sep axis depth"); + + // Move away + posA.x = -5; + var found2 = hullA.testSepAxis(sepAxis, hullB, posA, quatA, posB, quatB); + test.equal(found2, false, "found separating axis though there are none"); + + // Inclined 45 degrees, what happens then? + posA.x = 1; + quatB.fromAxisAngle(new Vector3(0, 0, 1), Math.PI / 4); + var found3 = hullA.testSepAxis(sepAxis, hullB, posA, quatA, posB, quatB); + test.ok(typeof (found3), "number" + " Did not fetch"); + }); + + QUnit.test("findSepAxis", (test) => + { + var hullA = createBoxHull(), + posA = new Vector3(-0.2, 0, 0), + quatA = new Quaternion(); + + var hullB = createBoxHull(), + posB = new Vector3(0.2, 0, 0), + quatB = new Quaternion(); + + var sepaxis = new Vector3(); + var found = hullA.findSeparatingAxis(hullB, posA, quatA, posB, quatB, sepaxis); + //console.log("SepAxis found:",found,", the axis:",sepaxis.toString()); + + quatB.fromAxisAngle(new Vector3(0, 0, 1), Math.PI / 4); + var found2 = hullA.findSeparatingAxis(hullB, posA, quatA, posB, quatB, sepaxis); + //console.log("SepAxis found:",found2,", the axis:",sepaxis.toString()); + + test.ok(true); + }); + + QUnit.test("project", (test) => + { + var convex = createBoxHull(0.5), + pos = new Vector3(0, 0, 0), + quat = new Quaternion(); + + var axis = new Vector3(1, 0, 0); + var result = []; + + ConvexPolyhedron.project(convex, axis, pos, quat, result); + test.deepEqual(result, [0.5, -0.5]); + + axis.set(-1, 0, 0); + ConvexPolyhedron.project(convex, axis, pos, quat, result); + test.deepEqual(result, [0.5, -0.5]); + + axis.set(0, 1, 0); + ConvexPolyhedron.project(convex, axis, pos, quat, result); + test.deepEqual(result, [0.5, -0.5]); + + pos.set(0, 1, 0); + axis.set(0, 1, 0); + ConvexPolyhedron.project(convex, axis, pos, quat, result); + test.deepEqual(result, [1.5, 0.5]); + + // Test to rotate + quat.fromAxisAngle(new Vector3(1, 0, 0), Math.PI / 2); + pos.set(0, 1, 0); + axis.set(0, 1, 0); + ConvexPolyhedron.project(convex, axis, pos, quat, result); + test.ok(Math.abs(result[0] - 1.5) < 0.01); + test.ok(Math.abs(result[1] - 0.5) < 0.01); + + }); + + + }); +} + + diff --git a/tests/src/Heightfield.ts b/tests/src/Heightfield.ts new file mode 100644 index 000000000..429f23e39 --- /dev/null +++ b/tests/src/Heightfield.ts @@ -0,0 +1,181 @@ +namespace CANNON +{ + QUnit.module("Heightfield", () => + { + + QUnit.test("calculateWorldAABB", (test) => + { + var hfShape = createHeightfield({ + elementSize: 1, + minValue: 0 + }); + var min = new Vector3(); + var max = new Vector3(); + hfShape.calculateWorldAABB( + new Vector3(), + new Quaternion(), + min, + max + ); + + test.equal(min.x, -Number.MAX_VALUE); + test.equal(max.x, Number.MAX_VALUE); + test.equal(min.y, -Number.MAX_VALUE); + test.equal(max.y, Number.MAX_VALUE); + + }); + + QUnit.test("getConvexTrianglePillar", (test) => + { + var hfShape = createHeightfield({ + elementSize: 1, + minValue: 0, + size: 2 + }); + + hfShape.getConvexTrianglePillar(0, 0, false); + test.equal(hfShape.pillarConvex.vertices.length, 6); + test.deepEqual(hfShape.pillarConvex.vertices.slice(0, 3), [ + new Vector3(-0.25, -0.25, 0.5), + new Vector3(0.75, -0.25, 0.5), + new Vector3(-0.25, 0.75, 0.5) + ]); + test.deepEqual(hfShape.pillarOffset, new Vector3(0.25, 0.25, 0.5)); + + hfShape.getConvexTrianglePillar(0, 0, true); + test.equal(hfShape.pillarConvex.vertices.length, 6); + test.deepEqual(hfShape.pillarConvex.vertices.slice(0, 3), [ + new Vector3(0.25, 0.25, 0.5), + new Vector3(-0.75, 0.25, 0.5), + new Vector3(0.25, -0.75, 0.5) + ]); + test.deepEqual(hfShape.pillarOffset, new Vector3(0.75, 0.75, 0.5)); + + // Out of bounds + test.throws(function () + { + hfShape.getConvexTrianglePillar(1, 1, true); + }, Error); + test.throws(function () + { + hfShape.getConvexTrianglePillar(1, 1, false); + }, Error); + test.throws(function () + { + hfShape.getConvexTrianglePillar(-1, 0, false); + }, Error); + }); + + QUnit.test("getTriangle", (test) => + { + var hfShape = createHeightfield({ + elementSize: 1, + minValue: 0, + size: 2 + }); + var a = new Vector3(); + var b = new Vector3(); + var c = new Vector3(); + + hfShape.getTriangle(0, 0, false, a, b, c); + test.deepEqual(a, new Vector3(0, 0, 1)); + test.deepEqual(b, new Vector3(1, 0, 1)); + test.deepEqual(c, new Vector3(0, 1, 1)); + + hfShape.getTriangle(0, 0, true, a, b, c); + test.deepEqual(a, new Vector3(1, 1, 1)); + test.deepEqual(b, new Vector3(0, 1, 1)); + test.deepEqual(c, new Vector3(1, 0, 1)); + }); + + QUnit.test("getRectMinMax", (test) => + { + var hfShape = createHeightfield(); + var minMax = []; + hfShape.getRectMinMax(0, 0, 1, 1, minMax); + test.deepEqual(minMax, [1, 1]); + }); + + QUnit.test("getHeightAt", (test) => + { + var hfShape = createHeightfield({ + size: 2, + elementSize: 1, + linear: true + }); + var h0 = hfShape.getHeightAt(0, 0); + var h1 = hfShape.getHeightAt(0.25, 0.25); + var h2 = hfShape.getHeightAt(0.75, 0.75); + var h3 = hfShape.getHeightAt(0.99, 0.99); + + test.equal(h0, 0); + test.ok(h0 < h1); + test.ok(h1 < h2); + test.ok(h2 < h3); + + }); + + QUnit.test("update", (test) => + { + var hfShape = createHeightfield(); + hfShape.update(); + + test.ok(true); + }); + + QUnit.test("updateMaxValue", (test) => + { + var hfShape = createHeightfield(); + hfShape.data[0][0] = 10; + hfShape.updateMaxValue(); + test.equal(hfShape.maxValue, 10); + }); + + QUnit.test("updateMinValue", (test) => + { + var hfShape = createHeightfield(); + hfShape.data[0][0] = -10; + hfShape.updateMinValue(); + test.equal(hfShape.minValue, -10); + }); + + QUnit.test("setHeightValueAtIndex", (test) => + { + var hfShape = createHeightfield(); + hfShape.setHeightValueAtIndex(0, 0, 10); + test.equal(hfShape.data[0][0], 10); + }); + + QUnit.test("getIndexOfPosition", (test) => + { + var hfShape = createHeightfield(); + var result = []; + hfShape.getIndexOfPosition(0, 0, result); + test.deepEqual(result, [0, 0]); + }); + }); + + function createHeightfield(options?) + { + options = options || {}; + var matrix = []; + var size = options.size || 20; + for (var i = 0; i < size; i++) + { + matrix.push([]); + for (var j = 0; j < size; j++) + { + if (options.linear) + { + matrix[i].push(i + j); + } else + { + matrix[i].push(1); + } + } + } + var hfShape = new Heightfield(matrix, options); + + return hfShape; + } +} \ No newline at end of file diff --git a/tests/src/HingeConstraint.ts b/tests/src/HingeConstraint.ts new file mode 100644 index 000000000..50cb43dfb --- /dev/null +++ b/tests/src/HingeConstraint.ts @@ -0,0 +1,78 @@ +namespace CANNON +{ + QUnit.module("HingeConstraint", () => + { + + QUnit.test("construct", (test) => + { + var bodyA = new Body({ mass: 1, position: new Vector3(1, 0, 0) }); + var bodyB = new Body({ mass: 1, position: new Vector3(-1, 0, 0) }); + var c = new HingeConstraint(bodyA, bodyB, { maxForce: 123 }); + + test.equal(c.equations.length, 6); // 5 actually, and 1 for the motor + + test.equal(c.equations[0].maxForce, 123); + test.equal(c.equations[1].maxForce, 123); + test.equal(c.equations[2].maxForce, 123); + test.equal(c.equations[3].maxForce, 123); + test.equal(c.equations[4].maxForce, 123); + test.equal(c.equations[5].maxForce, 123); + + test.equal(c.equations[0].minForce, -123); + test.equal(c.equations[1].minForce, -123); + test.equal(c.equations[2].minForce, -123); + test.equal(c.equations[3].minForce, -123); + test.equal(c.equations[4].minForce, -123); + test.equal(c.equations[5].minForce, -123); + + }); + + QUnit.test("update", (test) => + { + var bodyA = new Body({ mass: 1, position: new Vector3(1, 0, 0) }); + var bodyB = new Body({ mass: 1, position: new Vector3(-1, 0, 0) }); + var c = new HingeConstraint(bodyA, bodyB, { maxForce: 123 }); + + c.update(); + test.ok(true); + }); + + QUnit.test("enableDisableMotor", (test) => + { + var bodyA = new Body({ mass: 1, position: new Vector3(1, 0, 0) }); + var bodyB = new Body({ mass: 1, position: new Vector3(-1, 0, 0) }); + var c = new HingeConstraint(bodyA, bodyB); + + c.enableMotor(); + + test.ok(c.motorEquation.enabled); + + c.disableMotor(); + + test.equal(c.motorEquation.enabled, false); + + }); + + QUnit.test("setMotorSpeed", (test) => + { + var bodyA = new Body({ mass: 1, position: new Vector3(1, 0, 0) }); + var bodyB = new Body({ mass: 1, position: new Vector3(-1, 0, 0) }); + var c = new HingeConstraint(bodyA, bodyB); + + c.setMotorSpeed(5); + test.equal(c.motorEquation.targetVelocity, 5); + + }); + + QUnit.test("setMotorMaxForce", (test) => + { + var bodyA = new Body({ mass: 1, position: new Vector3(1, 0, 0) }); + var bodyB = new Body({ mass: 1, position: new Vector3(-1, 0, 0) }); + var c = new HingeConstraint(bodyA, bodyB); + + c.setMotorMaxForce(100); + test.equal(c.motorEquation.maxForce, 100); + + }); + }); +} diff --git a/tests/src/LockConstraint.ts b/tests/src/LockConstraint.ts new file mode 100644 index 000000000..bbad459f5 --- /dev/null +++ b/tests/src/LockConstraint.ts @@ -0,0 +1,40 @@ +namespace CANNON +{ + QUnit.module("LockConstraint", () => + { + + QUnit.test("construct", (test) => + { + var bodyA = new Body({ mass: 1, position: new Vector3(1, 0, 0) }); + var bodyB = new Body({ mass: 1, position: new Vector3(-1, 0, 0) }); + var c = new LockConstraint(bodyA, bodyB, { maxForce: 123 }); + + test.equal(c.equations.length, 6); + + test.equal(c.equations[0].maxForce, 123); + test.equal(c.equations[1].maxForce, 123); + test.equal(c.equations[2].maxForce, 123); + test.equal(c.equations[3].maxForce, 123); + test.equal(c.equations[4].maxForce, 123); + test.equal(c.equations[5].maxForce, 123); + + test.equal(c.equations[0].minForce, -123); + test.equal(c.equations[1].minForce, -123); + test.equal(c.equations[2].minForce, -123); + test.equal(c.equations[3].minForce, -123); + test.equal(c.equations[4].minForce, -123); + test.equal(c.equations[5].minForce, -123); + + }); + + QUnit.test("update", (test) => + { + var bodyA = new Body({ mass: 1, position: new Vector3(1, 0, 0) }); + var bodyB = new Body({ mass: 1, position: new Vector3(-1, 0, 0) }); + var c = new LockConstraint(bodyA, bodyB, { maxForce: 123 }); + + c.update(); + test.ok(true); + }); + }); +} diff --git a/tests/src/Narrowphase.ts b/tests/src/Narrowphase.ts new file mode 100644 index 000000000..400fcabc2 --- /dev/null +++ b/tests/src/Narrowphase.ts @@ -0,0 +1,78 @@ +namespace CANNON +{ + QUnit.module("Narrowphase", () => + { + + QUnit.test("sphereSphere", (test) => + { + var world = new World(); + var cg = new Narrowphase(world); + var result = []; + var sphereShape = new Sphere(1); + + var bodyA = new Body({ mass: 1 }); + bodyA.addShape(sphereShape); + var bodyB = new Body({ mass: 1 }); + bodyB.addShape(sphereShape); + + cg.currentContactMaterial = new ContactMaterial(); + cg.result = result; + cg.sphereSphere( + sphereShape, + sphereShape, + new Vector3(0.5, 0, 0), + new Vector3(-0.5, 0, 0), + new Quaternion(), + new Quaternion(), + bodyA, + bodyB + ); + + test.equal(result.length, 1); + }); + + QUnit.test("sphereHeightfield", (test) => + { + var world = new World(); + var cg = new Narrowphase(world); + var result = []; + var hfShape = createHeightfield(); + var sphereShape = new Sphere(0.1); + cg.currentContactMaterial = new ContactMaterial(); + cg.result = result; + cg.sphereHeightfield( + sphereShape, + hfShape, + new Vector3(0.25, 0.25, 0.05), // hit the first triangle in the field + new Vector3(0, 0, 0), + new Quaternion(), + new Quaternion(), + new Body(), + new Body() + ); + + test.equal(result.length, 1); + + }); + + }); + + function createHeightfield() + { + var matrix = []; + var size = 20; + for (var i = 0; i < size; i++) + { + matrix.push([]); + for (var j = 0; j < size; j++) + { + matrix[i].push(0); + } + } + var hfShape = new Heightfield(matrix, { + elementSize: 1, + }); + + return hfShape; + } +} \ No newline at end of file diff --git a/tests/src/Octree.ts b/tests/src/Octree.ts new file mode 100644 index 000000000..2e4c9cd69 --- /dev/null +++ b/tests/src/Octree.ts @@ -0,0 +1,84 @@ +namespace CANNON +{ + QUnit.module("Octree", () => + { + + QUnit.test("construct", (test) => + { + var tree = new Octree(new Box3()); + test.ok(true); + }) + + QUnit.test("insertRoot", (test) => + { + var aabb = new Box3(new Vector3(-1, -1, -1), new Vector3(1, 1, 1)); + var tree = new Octree(aabb); + + var nodeAABB = new Box3(new Vector3(-1, -1, -1), new Vector3(1, 1, 1)); + var nodeData = 123; + tree.insert(nodeAABB, nodeData); + + // Should end up in root node and not children + test.equal(tree.data.length, 1); + test.equal(tree.children.length, 0); + }); + + QUnit.test("insertDeep", (test) => + { + var aabb = new Box3(new Vector3(-1, -1, -1), new Vector3(1, 1, 1)); + var tree = new Octree(aabb, { + maxDepth: 8 + }); + + var nodeAABB = new Box3(new Vector3(-1, -1, -1), new Vector3(-1, -1, -1)); + var nodeData = 123; + + tree.insert(nodeAABB, nodeData); + + // Should be deep (maxDepth deep) in lower corner + test.ok( + tree // level 0 + .children[0] // 1 + .children[0] // 2 + .children[0] // 3 + .children[0] // 4 + .children[0] // 5 + .children[0] // 6 + .children[0] // 7 + .children[0] // 8 + ); + test.equal(tree.data.length, 0); + + }); + + QUnit.test("aabbQuery", (test) => + { + var aabb = new Box3(new Vector3(-1, -1, -1), new Vector3(1, 1, 1)); + var tree = new Octree(aabb); + + var nodeAABB = new Box3(new Vector3(-1, -1, -1), new Vector3(1, 1, 1)); + var nodeData = 123; + + tree.insert(nodeAABB, nodeData); + + var result: number[] = []; + tree.aabbQuery(aabb, result); + + test.deepEqual(result, [123]); + + + var nodeAABB2 = new Box3(new Vector3(-1, -1, -1), new Vector3(-1, -1, -1)); + var nodeData2 = 456; + tree.insert(nodeAABB2, nodeData2); + + result = []; + tree.aabbQuery(aabb, result); + test.deepEqual(result, [123, 456]); + + result = []; + tree.aabbQuery(new Box3(new Vector3(0, 0, 0), new Vector3(1, 1, 1)), result); + test.deepEqual(result, [123]); + + }); + }); +} diff --git a/tests/src/OverlapKeeper.ts b/tests/src/OverlapKeeper.ts new file mode 100644 index 000000000..c5656c243 --- /dev/null +++ b/tests/src/OverlapKeeper.ts @@ -0,0 +1,86 @@ +namespace CANNON +{ + QUnit.module("OverlapKeeper", () => + { + + QUnit.test("construct", (test) => + { + new OverlapKeeper(); + test.ok(true); + }); + + QUnit.test("set", (test) => + { + var keeper = new OverlapKeeper(); + + keeper.set(1, 2); + test.deepEqual(keeper.current, [keeper.getKey(1, 2)]); + + keeper.set(3, 2); + test.deepEqual(keeper.current, [keeper.getKey(1, 2), keeper.getKey(3, 2)]); + + keeper.set(3, 1); + test.deepEqual(keeper.current, [keeper.getKey(1, 2), keeper.getKey(1, 3), keeper.getKey(3, 2)]); + + }); + + QUnit.test("getDiff", (test) => + { + var keeper = new OverlapKeeper(); + + keeper.set(1, 2); + keeper.set(3, 2); + keeper.set(3, 1); + + keeper.tick(); + + keeper.set(1, 2); + keeper.set(3, 2); + keeper.set(3, 1); + + var additions = []; + var removals = []; + keeper.getDiff(additions, removals); + + test.equal(additions.length, 0); + test.equal(removals.length, 0); + + keeper.tick(); + + keeper.set(1, 2); + keeper.getDiff(additions, removals); + test.equal(additions.length, 0); + test.deepEqual(removals, [1, 3, 2, 3]); + + keeper.tick(); + + keeper.set(1, 2); + keeper.set(1, 2); + + additions = []; + removals = []; + keeper.getDiff(additions, removals); + test.equal(additions.length, 0, 'should handle duplicate entries'); + test.equal(removals.length, 0, 'should handle duplicate entries'); + + keeper.set(3, 2); + keeper.set(3, 1); + additions = []; + removals = []; + keeper.getDiff(additions, removals); + test.deepEqual(additions, [1, 3, 2, 3]); + + keeper.tick(); + + keeper.set(4, 2); + keeper.set(4, 1); + + additions = []; + removals = []; + keeper.getDiff(additions, removals); + test.deepEqual(additions, [1, 4, 2, 4]); + test.deepEqual(removals, [1, 2, 1, 3, 2, 3]); + + }); + }); +} \ No newline at end of file diff --git a/tests/src/Ray.ts b/tests/src/Ray.ts new file mode 100644 index 000000000..c5cbf0694 --- /dev/null +++ b/tests/src/Ray.ts @@ -0,0 +1,292 @@ +namespace CANNON +{ + QUnit.module("Ray", () => + { + + QUnit.test("construct", (test) => + { + var r = new Ray(new Vector3(), new Vector3(1, 0, 0)); + test.ok(true); + }); + + QUnit.test("intersectBody", (test) => + { + var r = new Ray(new Vector3(5, 0, 0), new Vector3(-5, 0, 0)); + r.skipBackfaces = true; + var shape = createPolyhedron(0.5); + var body = new Body({ mass: 1 }); + body.addShape(shape); + + var result = new RaycastResult(); + + r.intersectBody(body, result); + test.ok(result.hasHit); + test.ok(result.hitPointWorld.equals(new Vector3(0.5, 0, 0))); + + // test rotating the body first + result.reset(); + body.quaternion.fromAxisAngle(new Vector3(1, 0, 0), Math.PI); + r.intersectBody(body, result); + test.ok(result.hasHit); + test.ok(result.hitPointWorld.equals(new Vector3(0.5, 0, 0))); + + // test shooting from other direction + result.reset(); + r.to.set(0, 0, -5); + r.from.set(0, 0, 5); + r.intersectBody(body, result); + test.equal(result.hasHit, true); + test.ok(result.hitPointWorld.equals(new Vector3(0, 0, 0.5))); + + // test miss + result.reset(); + var r = new Ray(new Vector3(5, 1, 0), new Vector3(-5, 1, 0)); + r.intersectBody(body, result); + test.equal(result.hasHit, false); + + test.ok(true); + }); + + QUnit.test("intersectBodies", (test) => + { + var r = new Ray(new Vector3(5, 0, 0), new Vector3(-5, 0, 0)); + r.skipBackfaces = true; + var shape = createPolyhedron(0.5); + var body1 = new Body({ mass: 1 }); + body1.addShape(shape); + var body2 = new Body({ mass: 1 }); + body2.addShape(shape); + body2.position.x = -2; + + var result = new RaycastResult(); + r.intersectBodies([body1, body2], result); + test.equal(result.hasHit, true); + test.ok(result.hitPointWorld.equals(new Vector3(0.5, 0, 0))); + }); + + QUnit.test("box", (test) => + { + var r = new Ray(new Vector3(5, 0, 0), new Vector3(-5, 0, 0)); + r.skipBackfaces = true; + var shape = new Box(new Vector3(0.5, 0.5, 0.5)); + var body = new Body({ mass: 1 }); + body.addShape(shape); + var result = new RaycastResult(); + + r.intersectBody(body, result); + test.equal(result.hasHit, true); + test.ok(result.hitPointWorld.equals(new Vector3(0.5, 0, 0))); + + result.reset(); + body.quaternion.fromAxisAngle(new Vector3(1, 0, 0), Math.PI / 2); + r.intersectBody(body, result); + test.equal(result.hasHit, true); + test.ok(result.hitPointWorld.equals(new Vector3(0.5, 0, 0))); + + result.reset(); + body.quaternion.fromAxisAngle(new Vector3(1, 0, 0), Math.PI); + r.intersectBody(body, result); + test.equal(result.hasHit, true); + test.ok(result.hitPointWorld.equals(new Vector3(0.5, 0, 0))); + + result.reset(); + body.quaternion.fromAxisAngle(new Vector3(1, 0, 0), 3 * Math.PI / 2); + r.intersectBody(body, result); + test.equal(result.hasHit, true); + test.ok(result.hitPointWorld.equals(new Vector3(0.5, 0, 0))); + + }); + + QUnit.test("sphere", (test) => + { + var r = new Ray(new Vector3(5, 0, 0), new Vector3(-5, 0, 0)); + r.skipBackfaces = true; + var shape = new Sphere(1); + var body = new Body({ mass: 1 }); + body.addShape(shape); + + var result = new RaycastResult(); + r.intersectBody(body, result); + test.equal(result.hasHit, true); + test.ok(result.hitPointWorld.equals(new Vector3(1, 0, 0))); + + result.reset(); + body.position.set(1, 0, 0); + r.intersectBody(body, result); + test.equal(result.hasHit, true); + test.ok(result.hitPointWorld.equals(new Vector3(2, 0, 0))); + + result.reset(); + r.intersectBody(body, result); + test.equal(result.hasHit, true); + test.ok(result.hitPointWorld.equals(new Vector3(2, 0, 0))); + + result.reset(); + var shape2 = new Sphere(1); + var body2 = new Body({ mass: 1 }); + body2.addShape(shape2, new Vector3(1, 0, 0)); + r.intersectBody(body2, result); + test.equal(result.hasHit, true); + test.ok(result.hitPointWorld.equals(new Vector3(2, 0, 0))); + + }); + + QUnit.test("heightfield", (test) => + { + var r = new Ray(new Vector3(0, 0, 10), new Vector3(0, 0, -10)); + r.skipBackfaces = true; + var data = [ + [1, 1, 1], + [1, 1, 1], + [1, 1, 1] + ]; + var shape = new Heightfield(data, { + elementSize: 1 + }); + var body = new Body({ mass: 1 }); + body.addShape(shape); + + // Hit + var result = new RaycastResult(); + r.intersectBody(body, result); + test.equal(result.hasHit, true); + test.deepEqual(result.hitPointWorld, new Vector3(0, 0, 1)); + + // Miss + var result = new RaycastResult(); + r.from.set(-100, -100, 10); + r.to.set(-100, -100, -10); + r.intersectBody(body, result); + test.equal(result.hasHit, false); + + // Hit all triangles! + var result = new RaycastResult(); + for (var i = 0; i < data.length - 1; i++) + { // 3x3 data points will have 2x2 rectangles in the field + for (var j = 0; j < data[i].length - 1; j++) + { + for (var k = 0; k < 2; k++) + { + result.reset(); + r.from.set(i + 0.25, j + 0.25, 10); + r.to.set(i + 0.25, j + 0.25, -10); + if (k) + { + r.from.x += 0.5; + r.from.y += 0.5; + r.to.x += 0.5; + r.to.y += 0.5; + } + r.intersectBody(body, result); + test.ok(result.hasHit, 'missed triangle ' + [i, j].join(',')); + } + } + } + + test.ok(true); + }); + + QUnit.test("plane", (test) => + { + var r = new Ray(new Vector3(0, 0, 5), new Vector3(0, 0, -5)); + r.skipBackfaces = true; + var shape = new Plane(); + var body = new Body({ mass: 1 }); + body.addShape(shape); + + var result = new RaycastResult(); + r.intersectBody(body, result); + test.equal(result.hasHit, true); + test.ok(result.hitPointWorld.equals(new Vector3(0, 0, 0))); + test.equal(result.distance, 5); + + result.reset(); + var body2 = new Body({ mass: 1 }); + body2.addShape(shape, new Vector3(0, 0, 1), new Quaternion()); + r.intersectBody(body2, result); + test.equal(result.hasHit, true); + test.ok(result.hitPointWorld.equals(new Vector3(0, 0, 1))); + + result.reset(); + var body3 = new Body({ mass: 1 }); + var quat = new Quaternion(); + quat.fromAxisAngle(new Vector3(1, 0, 0), Math.PI / 2); + body3.addShape(shape, new Vector3(), quat); + r.intersectBody(body3, result); + test.equal(result.hasHit, false); + + result.reset(); + var body4 = new Body({ mass: 1 }); + body4.addShape(shape); + var r = new Ray(new Vector3(1, 1, 5), new Vector3(1, 1, -5)); + r.intersectBody(body4, result); + test.equal(result.hasHit, true); + test.deepEqual(result.hitPointWorld, new Vector3(1, 1, 0)); + test.equal(result.distance, 5); + + var result = new RaycastResult(); + r.from.set(0, 1, 1); + r.to.set(0, -1, -1); + body.position.set(0, 0, 0); + r.intersectBody(body, result); + var distance1 = result.distance; + test.equal(result.hasHit, true); + test.ok(result.hitPointWorld.equals(new Vector3(0, 0, 0))); + + var result = new RaycastResult(); + r.from.set(0, 1 - 5, 1); + r.to.set(0, -1 - 5, -1); + body.position.set(0, 0, 0); + r.intersectBody(body, result); + var distance2 = result.distance; + test.equal(result.hasHit, true); + test.ok(result.hitPointWorld.equals(new Vector3(0, -5, 0))); + test.equal(distance1, distance2); + + test.ok(true); + }); + + QUnit.test("trimesh", (test) => + { + var r = new Ray(new Vector3(0.5, 0.5, 10), new Vector3(0.5, 0.5, -10)); + r.skipBackfaces = true; + + var vertices = [ + 0, 0, 0, + 1, 0, 0, + 0, 1, 0 + ]; + var indices = [ + 0, 1, 2 + ]; + + var body = new Body({ + mass: 1, + shape: new Trimesh(vertices, indices) + }); + + // Hit + var result = new RaycastResult(); + r.intersectBody(body, result); + test.equal(result.hasHit, true); + test.deepEqual(result.hitPointWorld, new Vector3(0.5, 0.5, 0)); + + // Miss + result = new RaycastResult(); + r.from.set(-100, -100, 10); + r.to.set(-100, -100, -10); + r.intersectBody(body, result); + test.equal(result.hasHit, false); + + }); + + }); + + function createPolyhedron(size) + { + size = (size === undefined ? 0.5 : size); + var box = new Box(new Vector3(size, size, size)); + box.updateConvexPolyhedronRepresentation(); + return box.convexPolyhedronRepresentation; + } +} \ No newline at end of file diff --git a/tests/src/RaycastVehicle.ts b/tests/src/RaycastVehicle.ts new file mode 100644 index 000000000..c2d60bdf2 --- /dev/null +++ b/tests/src/RaycastVehicle.ts @@ -0,0 +1,145 @@ +namespace CANNON +{ + QUnit.module("RaycastVehicle", () => + { + + QUnit.test("construct", (test) => + { + var vehicle = new RaycastVehicle({ + chassisBody: new Body() + }); + test.ok(true); + }); + + QUnit.test("addWheel", (test) => + { + var vehicle = new RaycastVehicle({ + chassisBody: new Body() + }); + vehicle.addWheel({}); + test.equal(vehicle.wheelInfos.length, 1); + }); + + QUnit.test("addWheel1", (test) => + { + var vehicle = new RaycastVehicle({ + chassisBody: new Body() + }); + vehicle.addWheel({}); + test.equal(vehicle.wheelInfos.length, 1); + vehicle.addWheel({}); + test.equal(vehicle.wheelInfos.length, 2); + }); + + QUnit.test("setSteeringValue", (test) => + { + var vehicle = createVehicle(); + vehicle.setSteeringValue(Math.PI / 4, 0); + test.ok(true); + }); + + QUnit.test("applyEngineForce", (test) => + { + var vehicle = createVehicle(); + vehicle.applyEngineForce(1000, 0); + test.ok(true); + }); + + QUnit.test("setBrake", (test) => + { + var vehicle = createVehicle(); + vehicle.applyEngineForce(1000, 0); + test.ok(true); + }); + + QUnit.test("updateSuspension", (test) => + { + var vehicle = createVehicle(); + vehicle.updateSuspension(1 / 60); + test.ok(true); + }); + + QUnit.test("updateFriction", (test) => + { + var vehicle = createVehicle(); + vehicle.updateFriction(1 / 60); + test.ok(true); + }); + + QUnit.test("updateWheelTransform", (test) => + { + var vehicle = createVehicle(); + vehicle.updateWheelTransform(0); + test.ok(true); + }); + + QUnit.test("updateVehicle", (test) => + { + var vehicle = createVehicle(); + vehicle.updateVehicle(1 / 60); + test.ok(true); + }); + + QUnit.test("getVehicleAxisWorld", (test) => + { + var vehicle = createVehicle(); + var v = new Vector3(); + + vehicle.getVehicleAxisWorld(0, v); + test.deepEqual(v, new Vector3(1, 0, 0)); + + vehicle.getVehicleAxisWorld(1, v); + test.deepEqual(v, new Vector3(0, 1, 0)); + + vehicle.getVehicleAxisWorld(2, v); + test.deepEqual(v, new Vector3(0, 0, 1)); + + test.ok(true); + }); + + QUnit.test("removeFromWorld", (test) => + { + var world = new World(); + var vehicle = new RaycastVehicle({ + chassisBody: new Body({ mass: 1 }) + }); + + vehicle.addToWorld(world); + test.ok(world.bodies.indexOf(vehicle.chassisBody) !== -1); + test.ok(world.has('preStep')); + + vehicle.removeFromWorld(world); + test.ok(world.bodies.indexOf(vehicle.chassisBody) === -1); + test.ok(!world.has('preStep')); + + }); + }); + + function createVehicle() + { + var vehicle = new RaycastVehicle({ + chassisBody: new Body({ + mass: 1 + }) + }); + var down = new Vector3(0, 0, -1); + var info = { + chassisConnectionPointLocal: new Vector3(-5, -1 / 2, 0), + axleLocal: new Vector3(0, -1, 0), + directionLocal: down, + suspensionStiffness: 1000, + suspensionRestLength: 2, + }; + vehicle.addWheel(info); + + var world = new World(); + var planeBody = new Body(); + planeBody.position.z = -1; + planeBody.addShape(new Plane()); + world.addBody(planeBody); + + vehicle.addToWorld(world); + + return vehicle; + } +} \ No newline at end of file diff --git a/tests/src/Sphere.ts b/tests/src/Sphere.ts new file mode 100644 index 000000000..8ca0caf11 --- /dev/null +++ b/tests/src/Sphere.ts @@ -0,0 +1,22 @@ +namespace CANNON +{ + QUnit.module("Sphere", () => + { + + QUnit.test("throwOnWrongRadius", (test) => + { + + // These should be all right + new Sphere(1); + new Sphere(0); + + test.throws(function () + { + new Sphere(-1); + }, Error, 'Should throw on negative radius'); + + test.ok(true); + }); + + }); +} \ No newline at end of file diff --git a/tests/src/Trimesh.ts b/tests/src/Trimesh.ts new file mode 100644 index 000000000..6d7f12878 --- /dev/null +++ b/tests/src/Trimesh.ts @@ -0,0 +1,191 @@ +namespace CANNON +{ + QUnit.module("Trimesh", () => + { + + QUnit.test("updateNormals", (test) => + { + var mesh = Trimesh.createTorus(); + mesh.normals[0] = 1; + mesh.updateNormals(); + test.ok(mesh.normals[0] !== 1); + }); + + QUnit.test("updateAABB", (test) => + { + var mesh = Trimesh.createTorus(); + mesh.aabb.min.set(1, 2, 3); + mesh.updateAABB(); + test.ok(mesh.aabb.min.y !== 2); + }); + + QUnit.test("updateTree scaled", (test) => + { + var mesh = Trimesh.createTorus(); + mesh.updateTree(); + + var bigMesh = Trimesh.createTorus(); + bigMesh.setScale(new Vector3(2, 2, 2)); + + test.equal(bigMesh.aabb.max.x, mesh.aabb.max.x * 2, 'AABB does not scale with the mesh!'); + + test.equal(bigMesh.tree.aabb.max.x, mesh.tree.aabb.max.x, 'Octree AABB scales with the mesh, which is wrong!'); + + }); + + QUnit.test("getTrianglesInAABB unscaled", (test) => + { + var mesh = Trimesh.createTorus(1, 1, 32, 32); + var result: number[] = []; + + // Should get all triangles if we use the full AABB + var aabb = mesh.aabb.clone(); + mesh.getTrianglesInAABB(aabb, result); + test.equal(result.length, mesh.indices.length / 3); + + // Should get less triangles if we use the half AABB + result.length = 0; + aabb.min.scaleNumberTo(0.1, aabb.max); + aabb.max.scaleNumberTo(0.1, aabb.max); + mesh.getTrianglesInAABB(aabb, result); + + console.log(result.length, mesh.indices.length / 3) + + test.ok(result.length < mesh.indices.length / 3); + + }); + + // scaled: function(test){ + // var mesh = Trimesh.createTorus(1,1,16,16); + // var result = []; + + // // Should get all triangles if we use the full AABB + // var aabb = mesh.aabb.clone(); + // mesh.getTrianglesInAABB(aabb, result); + // test.equal(result.length, mesh.indices.length / 3); + + // // Should get less triangles if we use the half AABB + // result.length = 0; + // aabb.min.scaleNumberTo(0.5, aabb.lowerBound); + // aabb.max.scaleNumberTo(0.5, aabb.upperBound); + // mesh.getTrianglesInAABB(aabb, result); + // test.ok(result.length < mesh.indices.length / 3); + + // test.done(); + // } + + QUnit.test("getVertex unscaled", (test) => + { + var mesh = Trimesh.createTorus(); + var vertex = new Vector3(); + mesh.getVertex(0, vertex); + test.deepEqual(vertex, new Vector3(mesh.vertices[0], mesh.vertices[1], mesh.vertices[2])); + }); + + QUnit.test("getVertex scaled", (test) => + { + var mesh = Trimesh.createTorus(); + mesh.setScale(new Vector3(1, 2, 3)); + var vertex = new Vector3(); + mesh.getVertex(0, vertex); + test.deepEqual(vertex, new Vector3(1 * mesh.vertices[0], 2 * mesh.vertices[1], 3 * mesh.vertices[2])); + }); + + QUnit.test("getWorldVertex", (test) => + { + var mesh = Trimesh.createTorus(); + var vertex = new Vector3(); + mesh.getWorldVertex(0, new Vector3(), new Quaternion(), vertex); + test.deepEqual(vertex, new Vector3(mesh.vertices[0], mesh.vertices[1], mesh.vertices[2])); + }); + + QUnit.test("getTriangleVertices", (test) => + { + var mesh = Trimesh.createTorus(); + var va = new Vector3(); + var vb = new Vector3(); + var vc = new Vector3(); + var va1 = new Vector3(); + var vb1 = new Vector3(); + var vc1 = new Vector3(); + mesh.getVertex(mesh.indices[0], va); + mesh.getVertex(mesh.indices[1], vb); + mesh.getVertex(mesh.indices[2], vc); + mesh.getTriangleVertices(0, va1, vb1, vc1); + test.deepEqual(va, va1); + test.deepEqual(vb, vb1); + test.deepEqual(vc, vc1); + }); + + QUnit.test("getNormal", (test) => + { + var mesh = Trimesh.createTorus(); + var normal = new Vector3(); + mesh.getNormal(0, normal); + test.deepEqual(new Vector3(mesh.normals[0], mesh.normals[1], mesh.normals[2]), normal); + }); + + QUnit.test("calculateLocalInertia", (test) => + { + var mesh = Trimesh.createTorus(); + var inertia = new Vector3(); + mesh.calculateLocalInertia(1, inertia); + test.ok(true); + }); + + QUnit.test("computeLocalAABB", (test) => + { + console.log('Trimesh::computeLocalAABB is todo'); + test.ok(true); + }); + + QUnit.test("updateBoundingSphereRadius", (test) => + { + console.log('Trimesh::updateBoundingSphereRadius is todo'); + test.ok(true); + }); + + QUnit.test("calculateWorldAABB", (test) => + { + var poly = Trimesh.createTorus(); + var min = new Vector3(); + var max = new Vector3(); + poly.calculateWorldAABB( + new Vector3(1, 0, 0), // Translate 2 x in world + new Quaternion(0, 0, 0, 1), + min, + max + ); + test.ok(!isNaN(min.x)); + test.ok(!isNaN(max.x)); + }); + + QUnit.test("volume", (test) => + { + var mesh = Trimesh.createTorus(); + test.ok(mesh.volume() > 0); + }); + + QUnit.test("narrowphaseAgainstPlane", (test) => + { + var world = new World(); + + var torusShape = Trimesh.createTorus(); + var torusBody = new Body({ + mass: 1 + }); + torusBody.addShape(torusShape); + + var planeBody = new Body({ + mass: 1 + }); + planeBody.addShape(new Plane()); + + world.addBody(torusBody); + world.addBody(planeBody); + + world.step(1 / 60); + test.ok(true); + }); + }); +} \ No newline at end of file diff --git a/tests/src/World.ts b/tests/src/World.ts new file mode 100644 index 000000000..9e3556475 --- /dev/null +++ b/tests/src/World.ts @@ -0,0 +1,242 @@ +namespace CANNON +{ + QUnit.module("World", () => + { + + QUnit.test("clearForces", (test) => + { + var world = new World(); + var body = new Body(); + world.addBody(body); + body.force.set(1, 2, 3); + body.torque.set(4, 5, 6); + + world.clearForces(); + + test.ok(body.force.equals(new Vector3(0, 0, 0))); + test.ok(body.torque.equals(new Vector3(0, 0, 0))); + + }); + + QUnit.test("rayTestBox", (test) => + { + var world = new World(); + + var body = new Body(); + body.addShape(new Box(new Vector3(1, 1, 1))); + world.addBody(body); + + var from = new Vector3(-10, 0, 0); + var to = new Vector3(10, 0, 0); + + var result = new RaycastResult(); + world.raycastClosest(from, to, {}, result); + + test.equal(result.hasHit, true); + + }); + + QUnit.test("rayTestSphere", (test) => + { + var world = new World(); + + var body = new Body(); + body.addShape(new Sphere(1)); + world.addBody(body); + + var from = new Vector3(-10, 0, 0); + var to = new Vector3(10, 0, 0); + + var result = new RaycastResult(); + world.raycastClosest(from, to, {}, result); + + test.equal(result.hasHit, true); + + }); + + QUnit.test("raycastClosest single", (test) => + { + var world = new World(); + var body = new Body({ + shape: new Sphere(1) + }); + world.addBody(body); + + var from = new Vector3(-10, 0, 0); + var to = new Vector3(10, 0, 0); + + var result = new RaycastResult(); + world.raycastClosest(from, to, {}, result); + + test.equal(result.hasHit, true); + test.equal(result.body, body); + test.equal(result.shape, body.shapes[0]); + }); + + QUnit.test("raycastClosest order", (test) => + { + var world = new World(); + var bodyA = new Body({ shape: new Sphere(1), position: new Vector3(-1, 0, 0) }); + var bodyB = new Body({ shape: new Sphere(1), position: new Vector3(1, 0, 0) }); + world.addBody(bodyA); + world.addBody(bodyB); + + var from = new Vector3(-10, 0, 0); + var to = new Vector3(10, 0, 0); + + var result = new RaycastResult(); + world.raycastClosest(from, to, {}, result); + + test.equal(result.hasHit, true); + test.equal(result.body, bodyA); + test.equal(result.shape, bodyA.shapes[0]); + + from.set(10, 0, 0); + to.set(-10, 0, 0); + + result = new RaycastResult(); + world.raycastClosest(from, to, {}, result); + + test.equal(result.hasHit, true); + test.equal(result.body, bodyB); + test.equal(result.shape, bodyB.shapes[0]); + + }); + + QUnit.test("raycastAll simple", (test) => + { + var world = new World(); + var body = new Body({ shape: new Sphere(1) }); + world.addBody(body); + + var from = new Vector3(-10, 0, 0); + var to = new Vector3(10, 0, 0); + + var hasHit; + var numResults = 0; + var resultBody; + var resultShape; + + var returnVal = world.raycastAll(from, to, {}, function (result) + { + hasHit = result.hasHit; + resultShape = result.shape; + resultBody = result.body; + numResults++; + }); + + test.equal(returnVal, true, 'should return true on hit'); + test.equal(hasHit, true); + test.equal(resultBody, body); + test.equal(numResults, 2); + test.equal(resultShape, resultBody.shapes[0]); + + }); + + QUnit.test("raycastAll twoSpheres", (test) => + { + var world = new World(); + var body = new Body({ shape: new Sphere(1) }); + world.addBody(body); + + var body2 = new Body({ shape: new Sphere(1) }); + world.addBody(body2); + + var from = new Vector3(-10, 0, 0); + var to = new Vector3(10, 0, 0); + + var hasHit = false; + var numResults = 0; + var resultBody; + var resultShape; + + world.raycastAll(from, to, {}, function (result) + { + hasHit = result.hasHit; + resultShape = result.shape; + resultBody = result.body; + numResults++; + }); + + test.equal(hasHit, true); + test.equal(numResults, 4); + + }); + + QUnit.test("raycastAll skipBackFaces", (test) => + { + var world = new World(); + var body = new Body({ shape: new Sphere(1) }); + world.addBody(body); + + var hasHit = false; + var numResults = 0; + var resultBody; + var resultShape; + + world.raycastAll(new Vector3(-10, 0, 0), new Vector3(10, 0, 0), { skipBackfaces: true }, function (result) + { + hasHit = result.hasHit; + resultShape = result.shape; + resultBody = result.body; + numResults++; + }); + + test.equal(hasHit, true); + test.equal(numResults, 1); + + }); + + QUnit.test("raycastAll collisionFilters", (test) => + { + var world = new World(); + var body = new Body({ + shape: new Sphere(1) + }); + world.addBody(body); + body.collisionFilterGroup = 2; + body.collisionFilterMask = 2; + + var numResults = 0; + + world.raycastAll(new Vector3(-10, 0, 0), new Vector3(10, 0, 0), { + collisionFilterGroup: 2, + collisionFilterMask: 2 + }, function (result) + { + numResults++; + }); + + test.equal(numResults, 2); + + numResults = 0; + + world.raycastAll(new Vector3(-10, 0, 0), new Vector3(10, 0, 0), { + collisionFilterGroup: 1, + collisionFilterMask: 1 + }, function (result) + { + numResults++; + }); + + test.equal(numResults, 0, 'should use collision groups!'); + + }); + + QUnit.test("raycastAny", (test) => + { + var world = new World(); + world.addBody(new Body({ shape: new Sphere(1) })); + + var from = new Vector3(-10, 0, 0); + var to = new Vector3(10, 0, 0); + + var result = new RaycastResult(); + world.raycastAny(from, to, {}, result); + + test.ok(result.hasHit); + + }); + + }); +} \ No newline at end of file diff --git a/tests/tsconfig.json b/tests/tsconfig.json new file mode 100644 index 000000000..ff005aec2 --- /dev/null +++ b/tests/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "ES5", + "noImplicitAny": false, + "sourceMap": true, + "composite": true, + // "declaration": true, + // "strict": true, + "experimentalDecorators": true, + "lib": [ + "ES2015", + "dom" + ], + // "outDir": "./debug" + "outFile": "./out/tests.js" + }, + "include": [ + // + "../../feng3d/index.d.ts", + "../dist/index.d.ts", + // + "libs/qunit/qunit.d.ts", + // + "src/**/*.ts" + ] +} \ No newline at end of file diff --git a/tools/threejs/CannonDebugRenderer.js b/tools/threejs/CannonDebugRenderer.js index 4bc79b11b..badc8ca62 100644 --- a/tools/threejs/CannonDebugRenderer.js +++ b/tools/threejs/CannonDebugRenderer.js @@ -7,7 +7,8 @@ * @param {CANNON.World} world * @param {object} [options] */ -THREE.CannonDebugRenderer = function(scene, world, options){ +THREE.CannonDebugRenderer = function (scene, world, options) +{ options = options || {}; this.scene = scene; @@ -18,18 +19,19 @@ THREE.CannonDebugRenderer = function(scene, world, options){ this._material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true }); this._sphereGeometry = new THREE.SphereGeometry(1); this._boxGeometry = new THREE.BoxGeometry(1, 1, 1); - this._planeGeometry = new THREE.PlaneGeometry( 10, 10, 10, 10 ); - this._cylinderGeometry = new THREE.CylinderGeometry( 1, 1, 10, 10 ); + this._planeGeometry = new THREE.PlaneGeometry(10, 10, 10, 10); + this._cylinderGeometry = new THREE.CylinderGeometry(1, 1, 10, 10); }; THREE.CannonDebugRenderer.prototype = { - tmpVec0: new CANNON.Vec3(), - tmpVec1: new CANNON.Vec3(), - tmpVec2: new CANNON.Vec3(), - tmpQuat0: new CANNON.Vec3(), + tmpVec0: new CANNON.Vector3(), + tmpVec1: new CANNON.Vector3(), + tmpVec2: new CANNON.Vector3(), + tmpQuat0: new CANNON.Quaternion(), - update: function(){ + update: function () + { var bodies = this.world.bodies; var meshes = this._meshes; @@ -38,24 +40,27 @@ THREE.CannonDebugRenderer.prototype = { var meshIndex = 0; - for (var i = 0; i !== bodies.length; i++) { + for (var i = 0; i !== bodies.length; i++) + { var body = bodies[i]; - for (var j = 0; j !== body.shapes.length; j++) { + for (var j = 0; j !== body.shapes.length; j++) + { var shape = body.shapes[j]; this._updateMesh(meshIndex, body, shape); var mesh = meshes[meshIndex]; - if(mesh){ + if (mesh) + { // Get world position body.quaternion.vmult(body.shapeOffsets[j], shapeWorldPosition); - body.position.vadd(shapeWorldPosition, shapeWorldPosition); + body.position.addTo(shapeWorldPosition, shapeWorldPosition); // Get world quaternion - body.quaternion.mult(body.shapeOrientations[j], shapeWorldQuaternion); + body.quaternion.multTo(body.shapeOrientations[j], shapeWorldQuaternion); // Copy to meshes mesh.position.copy(shapeWorldPosition); @@ -66,9 +71,11 @@ THREE.CannonDebugRenderer.prototype = { } } - for(var i = meshIndex; i < meshes.length; i++){ + for (var i = meshIndex; i < meshes.length; i++) + { var mesh = meshes[i]; - if(mesh){ + if (mesh) + { this.scene.remove(mesh); } } @@ -76,10 +83,13 @@ THREE.CannonDebugRenderer.prototype = { meshes.length = meshIndex; }, - _updateMesh: function(index, body, shape){ + _updateMesh: function (index, body, shape) + { var mesh = this._meshes[index]; - if(!this._typeMatch(mesh, shape)){ - if(mesh){ + if (!this._typeMatch(mesh, shape)) + { + if (mesh) + { this.scene.remove(mesh); } mesh = this._meshes[index] = this._createMesh(shape); @@ -87,8 +97,10 @@ THREE.CannonDebugRenderer.prototype = { this._scaleMesh(mesh, shape); }, - _typeMatch: function(mesh, shape){ - if(!mesh){ + _typeMatch: function (mesh, shape) + { + if (!mesh) + { return false; } var geo = mesh.geometry; @@ -102,137 +114,149 @@ THREE.CannonDebugRenderer.prototype = { ); }, - _createMesh: function(shape){ + _createMesh: function (shape) + { var mesh; var material = this._material; - switch(shape.type){ + switch (shape.type) + { - case CANNON.Shape.types.SPHERE: - mesh = new THREE.Mesh(this._sphereGeometry, material); - break; + case CANNON.Shape.types.SPHERE: + mesh = new THREE.Mesh(this._sphereGeometry, material); + break; - case CANNON.Shape.types.BOX: - mesh = new THREE.Mesh(this._boxGeometry, material); - break; + case CANNON.Shape.types.BOX: + mesh = new THREE.Mesh(this._boxGeometry, material); + break; - case CANNON.Shape.types.PLANE: - mesh = new THREE.Mesh(this._planeGeometry, material); - break; + case CANNON.Shape.types.PLANE: + mesh = new THREE.Mesh(this._planeGeometry, material); + break; - case CANNON.Shape.types.CONVEXPOLYHEDRON: - // Create mesh - var geo = new THREE.Geometry(); + case CANNON.Shape.types.CONVEXPOLYHEDRON: + // Create mesh + var geo = new THREE.Geometry(); - // Add vertices - for (var i = 0; i < shape.vertices.length; i++) { - var v = shape.vertices[i]; - geo.vertices.push(new THREE.Vector3(v.x, v.y, v.z)); - } - - for(var i=0; i < shape.faces.length; i++){ - var face = shape.faces[i]; + // Add vertices + for (var i = 0; i < shape.vertices.length; i++) + { + var v = shape.vertices[i]; + geo.vertices.push(new THREE.Vector3(v.x, v.y, v.z)); + } - // add triangles - var a = face[0]; - for (var j = 1; j < face.length - 1; j++) { - var b = face[j]; - var c = face[j + 1]; - geo.faces.push(new THREE.Face3(a, b, c)); + for (var i = 0; i < shape.faces.length; i++) + { + var face = shape.faces[i]; + + // add triangles + var a = face[0]; + for (var j = 1; j < face.length - 1; j++) + { + var b = face[j]; + var c = face[j + 1]; + geo.faces.push(new THREE.Face3(a, b, c)); + } } - } - geo.computeBoundingSphere(); - geo.computeFaceNormals(); - - mesh = new THREE.Mesh(geo, material); - shape.geometryId = geo.id; - break; - - case CANNON.Shape.types.TRIMESH: - var geometry = new THREE.Geometry(); - var v0 = this.tmpVec0; - var v1 = this.tmpVec1; - var v2 = this.tmpVec2; - for (var i = 0; i < shape.indices.length / 3; i++) { - shape.getTriangleVertices(i, v0, v1, v2); - geometry.vertices.push( - new THREE.Vector3(v0.x, v0.y, v0.z), - new THREE.Vector3(v1.x, v1.y, v1.z), - new THREE.Vector3(v2.x, v2.y, v2.z) - ); - var j = geometry.vertices.length - 3; - geometry.faces.push(new THREE.Face3(j, j+1, j+2)); - } - geometry.computeBoundingSphere(); - geometry.computeFaceNormals(); - mesh = new THREE.Mesh(geometry, material); - shape.geometryId = geometry.id; - break; - - case CANNON.Shape.types.HEIGHTFIELD: - var geometry = new THREE.Geometry(); - - var v0 = this.tmpVec0; - var v1 = this.tmpVec1; - var v2 = this.tmpVec2; - for (var xi = 0; xi < shape.data.length - 1; xi++) { - for (var yi = 0; yi < shape.data[xi].length - 1; yi++) { - for (var k = 0; k < 2; k++) { - shape.getConvexTrianglePillar(xi, yi, k===0); - v0.copy(shape.pillarConvex.vertices[0]); - v1.copy(shape.pillarConvex.vertices[1]); - v2.copy(shape.pillarConvex.vertices[2]); - v0.vadd(shape.pillarOffset, v0); - v1.vadd(shape.pillarOffset, v1); - v2.vadd(shape.pillarOffset, v2); - geometry.vertices.push( - new THREE.Vector3(v0.x, v0.y, v0.z), - new THREE.Vector3(v1.x, v1.y, v1.z), - new THREE.Vector3(v2.x, v2.y, v2.z) - ); - var i = geometry.vertices.length - 3; - geometry.faces.push(new THREE.Face3(i, i+1, i+2)); + geo.computeBoundingSphere(); + geo.computeFaceNormals(); + + mesh = new THREE.Mesh(geo, material); + shape.geometryId = geo.id; + break; + + case CANNON.Shape.types.TRIMESH: + var geometry = new THREE.Geometry(); + var v0 = this.tmpVec0; + var v1 = this.tmpVec1; + var v2 = this.tmpVec2; + for (var i = 0; i < shape.indices.length / 3; i++) + { + shape.getTriangleVertices(i, v0, v1, v2); + geometry.vertices.push( + new THREE.Vector3(v0.x, v0.y, v0.z), + new THREE.Vector3(v1.x, v1.y, v1.z), + new THREE.Vector3(v2.x, v2.y, v2.z) + ); + var j = geometry.vertices.length - 3; + geometry.faces.push(new THREE.Face3(j, j + 1, j + 2)); + } + geometry.computeBoundingSphere(); + geometry.computeFaceNormals(); + mesh = new THREE.Mesh(geometry, material); + shape.geometryId = geometry.id; + break; + + case CANNON.Shape.types.HEIGHTFIELD: + var geometry = new THREE.Geometry(); + + var v0 = this.tmpVec0; + var v1 = this.tmpVec1; + var v2 = this.tmpVec2; + for (var xi = 0; xi < shape.data.length - 1; xi++) + { + for (var yi = 0; yi < shape.data[xi].length - 1; yi++) + { + for (var k = 0; k < 2; k++) + { + shape.getConvexTrianglePillar(xi, yi, k === 0); + v0.copy(shape.pillarConvex.vertices[0]); + v1.copy(shape.pillarConvex.vertices[1]); + v2.copy(shape.pillarConvex.vertices[2]); + v0.addTo(shape.pillarOffset, v0); + v1.addTo(shape.pillarOffset, v1); + v2.addTo(shape.pillarOffset, v2); + geometry.vertices.push( + new THREE.Vector3(v0.x, v0.y, v0.z), + new THREE.Vector3(v1.x, v1.y, v1.z), + new THREE.Vector3(v2.x, v2.y, v2.z) + ); + var i = geometry.vertices.length - 3; + geometry.faces.push(new THREE.Face3(i, i + 1, i + 2)); + } } } - } - geometry.computeBoundingSphere(); - geometry.computeFaceNormals(); - mesh = new THREE.Mesh(geometry, material); - shape.geometryId = geometry.id; - break; + geometry.computeBoundingSphere(); + geometry.computeFaceNormals(); + mesh = new THREE.Mesh(geometry, material); + shape.geometryId = geometry.id; + break; } - if(mesh){ + if (mesh) + { this.scene.add(mesh); } return mesh; }, - _scaleMesh: function(mesh, shape){ - switch(shape.type){ + _scaleMesh: function (mesh, shape) + { + switch (shape.type) + { - case CANNON.Shape.types.SPHERE: - var radius = shape.radius; - mesh.scale.set(radius, radius, radius); - break; + case CANNON.Shape.types.SPHERE: + var radius = shape.radius; + mesh.scale.set(radius, radius, radius); + break; - case CANNON.Shape.types.BOX: - mesh.scale.copy(shape.halfExtents); - mesh.scale.multiplyScalar(2); - break; + case CANNON.Shape.types.BOX: + mesh.scale.copy(shape.halfExtents); + mesh.scale.multiplyScalar(2); + break; - case CANNON.Shape.types.CONVEXPOLYHEDRON: - mesh.scale.set(1,1,1); - break; + case CANNON.Shape.types.CONVEXPOLYHEDRON: + mesh.scale.set(1, 1, 1); + break; - case CANNON.Shape.types.TRIMESH: - mesh.scale.copy(shape.scale); - break; + case CANNON.Shape.types.TRIMESH: + mesh.scale.copy(shape.scale); + break; - case CANNON.Shape.types.HEIGHTFIELD: - mesh.scale.set(1,1,1); - break; + case CANNON.Shape.types.HEIGHTFIELD: + mesh.scale.set(1, 1, 1); + break; } } diff --git a/tools/threejs/example.html b/tools/threejs/example.html index 7d8abb466..881ee6af4 100644 --- a/tools/threejs/example.html +++ b/tools/threejs/example.html @@ -1,23 +1,27 @@ + THREE.CannonDebugRenderer example + - + + - + + \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..270ef5259 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES5", + "module": "CommonJS", + "noImplicitAny": false, + "sourceMap": true, + "declarationMap": true, + "declaration": true, + "experimentalDecorators": true, + "lib": [ + "ES2015", + "DOM", + ], + "outDir": "lib" + }, + "include": [ + "src/**/*.ts" + ], + "ts-node": { + "files": true, + "compilerOptions": { + "esModuleInterop": true + } + } +} \ No newline at end of file diff --git a/typedoc.json b/typedoc.json new file mode 100644 index 000000000..1c5673f47 --- /dev/null +++ b/typedoc.json @@ -0,0 +1,8 @@ +{ + "name": "@feng3d/cannon", + "entryPoints": [ + "src/index.ts" + ], + "sourcefile-url-prefix": "https://gitlab.com/feng3d/cannon/tree/master/src/", + "out": "public" +} \ No newline at end of file diff --git a/util/cannon.serialize.js b/util/cannon.serialize.js deleted file mode 100644 index 663f09fee..000000000 --- a/util/cannon.serialize.js +++ /dev/null @@ -1,127 +0,0 @@ -/* - -Adds two methods to the CANNON.World class: .toJSON and .fromJSON. These methods are not complete but works for simple cases. - -Usage: - - Include this script after cannon.js or cannon.min.js: - - - - - Now you can do this with your CANNON.World instance: - - var obj = world.toJSON(); // Create a serializable object - var str = JSON.stringify(obj); // Convert to string - - Or, you can load the serialized scene from the JSON: - - var obj = JSON.parse(str); // Parse the string into a JSON object - world.fromJSON(obj); // Load objects into the world - - */ - - -CANNON.World.prototype.toJSON = function(){ - function v2a(v){ - return [v.x, v.y, v.z]; - } - function q2a(q){ - return [q.x, q.y, q.z, q.w]; - } - - var json = { - bodies: [] - }; - for (var i = 0; i < this.bodies.length; i++) { - var body = this.bodies[i]; - - var jsonBody = { - mass: body.mass, - type: body.type, - position: [body.position.x, body.position.y, body.position.z], - quaternion: [body.quaternion.x, body.quaternion.y, body.quaternion.z, body.quaternion.w], - shapes: [], - shapeOffsets: [], - shapeOrientations: [] - }; - - for (var j = 0; j < body.shapes.length; j++) { - var shape = body.shapes[j]; - var jsonShape = { - type: shape.type - }; - switch(shape.type){ - case CANNON.Shape.types.BOX: - jsonShape.halfExtents = v2a(shape.halfExtents); - break; - case CANNON.Shape.types.SPHERE: - jsonShape.radius = shape.radius; - break; - default: - console.log('unhandled shape: ' + shape.type); - continue; - } - jsonBody.shapes.push(jsonShape); - jsonBody.shapeOffsets.push(v2a(body.shapeOffsets[j])); - jsonBody.shapeOrientations.push(q2a(body.shapeOrientations[j])); - } - - json.bodies.push(jsonBody); - } - return json; -}; - -CANNON.World.prototype.fromJSON = function(json){ - function a2v(a,v){ - v = v || new CANNON.Vec3(); - v.x = a[0]; - v.y = a[1]; - v.z = a[2]; - return v; - } - function a2q(a,q){ - q = q || new CANNON.Quaternion(); - q.x = a[0]; - q.y = a[1]; - q.z = a[2]; - q.w = a[3]; - return q; - } - - for (var i = 0; i < json.bodies.length; i++) { - var jsonBody = json.bodies[i]; - var body = new CANNON.Body({ - mass: jsonBody.mass, - type: jsonBody.type, - position: a2v(jsonBody.position), - quaternion: a2q(jsonBody.quaternion) - }); - - for (var j = 0; j < jsonBody.shapes.length; j++) { - var jsonShape = jsonBody.shapes[j]; - var shape; - var offset = a2v(jsonBody.shapeOffsets[j]); - var orientation = a2q(jsonBody.shapeOrientations[j]); - switch(jsonShape.type){ - case CANNON.Shape.types.BOX: - shape = new CANNON.Box(a2v(jsonShape.halfExtents)); - break; - case CANNON.Shape.types.SPHERE: - shape = new CANNON.Sphere(jsonShape.radius); - break; - } - if(shape){ - body.addShape(shape, offset, orientation); - } - } - - this.addBody(body); - } -}; - -CANNON.World.prototype.empty = function(){ - while(this.bodies.length) { - this.remove(this.bodies[0]); - } -}; \ No newline at end of file diff --git a/yuidoc.json b/yuidoc.json deleted file mode 100644 index 4e543674a..000000000 --- a/yuidoc.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "options": { - "outdir" : "docs", - "paths" : ["./src/"] - } -}