Repository: toxicFork/react-three-renderer-example Branch: master Commit: 0398b735bdc1 Files: 51 Total size: 157.6 KB Directory structure: gitextract_5jlez6np/ ├── .eslintrc ├── .gitignore ├── .gitmodules ├── CONTRIBUTORS.md ├── README.md ├── assets/ │ ├── advanced.html │ └── index.html ├── config/ │ ├── webpackCommonsChunkPluginConfig.js │ └── webpackPluginsWithoutUglify.js ├── gulpfile.babel.js ├── package.json ├── src/ │ ├── examples/ │ │ ├── AdvancedExample/ │ │ │ ├── AdvancedComponent.js │ │ │ └── index.js │ │ ├── AnimationCloth/ │ │ │ ├── Cloth.js │ │ │ ├── ClothGeometry.jsx │ │ │ ├── Info.js │ │ │ ├── Poles.js │ │ │ ├── Sphere.js │ │ │ ├── StaticWorld.js │ │ │ ├── index.js │ │ │ ├── index.jsx │ │ │ └── shaders/ │ │ │ ├── depth.frag │ │ │ └── depth.vert │ │ ├── Benchmark/ │ │ │ ├── RotatingCube.js │ │ │ ├── RotatingCubes.js │ │ │ └── RotatingCubesDirectUpdates.js │ │ ├── DraggableCubes/ │ │ │ ├── AllCubes.js │ │ │ ├── DraggableCube.js │ │ │ └── index.js │ │ ├── ExampleBase.js │ │ ├── ExampleBrowser.js │ │ ├── ExampleViewer.js │ │ ├── Geometries/ │ │ │ └── index.js │ │ ├── GeometryShapes/ │ │ │ ├── Rect.js │ │ │ ├── Resources.js │ │ │ ├── Shape.js │ │ │ ├── Shapes.js │ │ │ └── index.js │ │ ├── ManualRendering/ │ │ │ ├── Info.js │ │ │ └── index.js │ │ ├── Physics/ │ │ │ ├── index.js │ │ │ ├── mousePick/ │ │ │ │ └── PickableMesh.js │ │ │ └── mousePick.js │ │ ├── Simple/ │ │ │ └── index.js │ │ ├── WebGLCameraExample/ │ │ │ ├── Info.js │ │ │ ├── PointCloud.js │ │ │ └── index.js │ │ └── inputs/ │ │ └── MouseInput.js │ ├── index.jsx │ └── ref/ │ └── trackball.js └── webpack.config.babel.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .eslintrc ================================================ // Use this file as a starting point for your project's .eslintrc. // Copy this file, and add rule overrides as needed. { "extends": "airbnb", "parser": "babel-eslint", "rules": { "id-length": 0, "no-bitwise": "off", "no-underscore-dangle": 0, "linebreak-style": "off", "no-mixed-operators": "off", "no-plusplus": [ 0 ], "no-param-reassign": [ 2, { "props": false } ] } } ================================================ FILE: .gitignore ================================================ /node_modules .idea/dictionaries/ .idea/uiDesigner.xml .idea/ ================================================ FILE: .gitmodules ================================================ [submodule "pages"] path = pages url = git@github.com:toxicFork/react-three-renderer-example.git branch = gh-pages ================================================ FILE: CONTRIBUTORS.md ================================================ * [@toxicFork](https://github.com/toxicFork) * Original Author * [@vkammerer](https://github.com/vkammerer) * Added installation instructions ================================================ FILE: README.md ================================================ react-three-renderer-example ============================ Examples for [react-three-renderer](https://github.com/toxicFork/react-three-renderer). https://toxicfork.github.com/react-three-renderer-example/ #### Installation `` npm install `` #### Local server `` npm start `` Then visit [http://localhost:8080](http://localhost:8080) on your favorite webgl-enabled browser :) ================================================ FILE: assets/advanced.html ================================================


View Source
================================================ FILE: assets/index.html ================================================
================================================ FILE: config/webpackCommonsChunkPluginConfig.js ================================================ /* eslint-disable import/no-extraneous-dependencies */ import webpack from 'webpack'; import path from 'path'; // noinspection WebpackConfigHighlighting module.exports = new webpack.optimize.CommonsChunkPlugin( { name: 'common', filename: path.join('js', 'bundle-commons.js'), }); ================================================ FILE: config/webpackPluginsWithoutUglify.js ================================================ /* eslint-disable import/no-extraneous-dependencies */ import webpack from 'webpack'; import commonsChunkPluginConfig from './webpackCommonsChunkPluginConfig'; // noinspection WebpackConfigHighlighting module.exports = [ new webpack.DefinePlugin({ 'process.env': { NODE_ENV: '"production"', }, }), commonsChunkPluginConfig, ]; ================================================ FILE: gulpfile.babel.js ================================================ /* eslint-disable import/no-extraneous-dependencies */ import gulp from 'gulp'; import webpack from 'webpack'; import WebpackDevServer from 'webpack-dev-server'; import gutil from 'gulp-util'; import path from 'path'; import runSequence from 'run-sequence'; import del from 'del'; import webpackConfig from './webpack.config.babel'; import pluginsWithoutUglify from './config/webpackPluginsWithoutUglify'; import webpackCommonsChunkPluginConfig from './config/webpackCommonsChunkPluginConfig'; const cache = {}; const config = { prod: false, addon: false, noEval: false, }; webpackConfig.output.devtoolModuleFilenameTemplate = info => `wp:///${path.relative(__dirname, info.resourcePath)}`; webpackConfig.output.devtoolFallbackModuleFilenameTemplate = info => `wp:///${path.relative(__dirname, info.resourcePath)}?${info.hash}`; require('webpack/lib/ModuleFilenameHelpers').createFooter = () => ''; // pretend it's prod ( still has sourcemaps ) // slowest compilation gulp.task('webpack-dev-server-prod', () => { config.prod = true; config.addon = true; runSequence('webpack-dev-server'); }); // no eval, faster runtime, slower compilation gulp.task('webpack-dev-server-no-eval', () => { config.noEval = true; runSequence('webpack-dev-server'); }); // fast compilation, low runtime performance gulp.task('webpack-dev-server', () => { const host = '0.0.0.0'; const port = 8080; webpackConfig.cache = cache; webpackConfig.entry.app = [ `webpack-dev-server/client?http://${host}:${port}`, // WebpackDevServer host and port 'webpack/hot/only-dev-server', // "only" prevents reload on syntax errors ].concat(webpackConfig.entry.app); webpackConfig.entry.advanced = [ `webpack-dev-server/client?http://${host}:${port}`, // WebpackDevServer host and port 'webpack/hot/only-dev-server', // "only" prevents reload on syntax errors ].concat(webpackConfig.entry.advanced); webpackConfig.module.loaders.forEach((loader) => { if (loader.loader === 'babel-loader') { loader.query.plugins.push('react-hot-loader/babel'); } }); if (config.prod) { webpackConfig.devtool = 'source-map'; } else if (config.noEval) { webpackConfig.devtool = 'cheap-module-source-map'; } else { webpackConfig.devtool = 'eval-cheap-module-source-map'; } webpackConfig.plugins = [ new webpack.HotModuleReplacementPlugin(), webpackCommonsChunkPluginConfig, ]; if (config.prod) { webpackConfig.plugins.unshift( new webpack.DefinePlugin({ 'process.env': { NODE_ENV: '"production"', ENABLE_REACT_ADDON_HOOKS: config.addon ? '"true"' : '"false"', }, })); webpackConfig.plugins.push( new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false, }, mangle: true, })); } // Start a webpack-dev-server const compiler = webpack(webpackConfig); new WebpackDevServer(compiler, webpackConfig.devServer).listen(port, host, (err) => { if (err) { throw new gutil.PluginError('webpack-dev-server', err); } // Server listening gutil.log('[webpack-dev-server]', 'http://localhost:8080/webpack-dev-server/index.html'); // keep the server alive or continue? // callback(); }); }); // only enable addon integration, everything else in prod settings gulp.task('build-prod-with-addon', (callback) => { webpackConfig.plugins.unshift(new webpack.DefinePlugin({ 'process.env': { ENABLE_REACT_ADDON_HOOKS: '"true"', }, })); runSequence('build', callback); }); // also adds sourceMaps too! gulp.task('build-prod-with-addon-no-mangle', (callback) => { webpackConfig.devtool = 'source-map'; webpackConfig.plugins = pluginsWithoutUglify.concat([ new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false, }, mangle: false, }), ]); runSequence('build-prod-with-addon', callback); }); // build without production node env gulp.task('build-dev', (callback) => { webpackConfig.plugins = [ new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false, }, mangle: true, }), ]; runSequence('build', callback); }); gulp.task('clean-pages', () => del('pages/**/!(.git|README.md)')); gulp.task('copy-assets', () => gulp .src('assets/**/*') .pipe(gulp.dest('pages/'))); // just run webpack with default config (prod) gulp.task('build', ['clean-pages'], (callback) => { webpack(webpackConfig, (err, stats) => { if (err) { throw new gutil.PluginError('webpack', err); } gutil.log('[webpack]', stats.toString({ // output options })); runSequence('copy-assets', callback); }); }); gulp.task('default', ['webpack-dev-server']); ================================================ FILE: package.json ================================================ { "name": "react-three-renderer-example", "version": "1.0.0", "description": "An example showing how to use the react-three-renderer package", "main": "index.js", "scripts": { "start": "gulp webpack-dev-server", "eslint-internal": "eslint ./src/", "eslint": "npm run eslint-internal -loglevel silent || true", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [ "reactjs", "threejs", "renderer" ], "author": "Firtina \"toxicFork\" Ozbalikci", "license": "MIT", "dependencies": { "cannon": "^0.6.2", "history": "^4.6.3", "prop-types": "^15.5.10", "react": "^15.6.1", "react-dom": "^15.6.1", "react-router": "^4.1.2", "react-router-dom": "^4.1.2", "react-sizeme": "^2.3.4", "react-three-renderer": "^3.2.1", "stats.js": "^1.0.0", "three": "^0.86.0", "react-addons-perf": "^15.4.2" }, "devDependencies": { "babel-eslint": "^7.1.1", "babel-cli": "^6.24.1", "babel-core": "^6.25.0", "babel-loader": "^7.1.1", "babel-plugin-transform-decorators-legacy": "^1.3.4", "eslint": "^3.11.1", "eslint-config-airbnb": "^13.0.0", "eslint-plugin-import": "^2.2.0", "eslint-plugin-jsx-a11y": "2.2.3", "eslint-plugin-react": "^6.7.1", "babel-plugin-transform-runtime": "^6.23.0", "babel-polyfill": "^6.23.0", "babel-preset-es2015": "^6.24.1", "babel-preset-react": "^6.24.1", "babel-preset-stage-0": "^6.24.1", "babel-runtime": "^6.25.0", "gulp": "^3.9.1", "gulp-babel": "^6.1.2", "gulp-util": "^3.0.8", "json-loader": "^0.5.7", "raw-loader": "^0.5.1", "react-hot-loader": "^3.0.0-beta.6", "run-sequence": "^2.1.0", "webpack": "^3.4.1", "webpack-dev-server": "^2.6.1", "del": "latest" }, "repository": { "type": "git", "url": "git+https://github.com/toxicFork/react-three-renderer-example.git" }, "bugs": { "url": "https://github.com/toxicFork/react-three-renderer-example/issues" }, "babel": { "presets": [ "es2015", "stage-0", "react" ], "plugins": [ "transform-runtime", "transform-decorators-legacy" ] }, "homepage": "https://github.com/toxicFork/react-three-renderer-example#readme" } ================================================ FILE: src/examples/AdvancedExample/AdvancedComponent.js ================================================ ================================================ FILE: src/examples/AdvancedExample/index.js ================================================ // see advanced.html :) import React from 'react'; import React3Renderer from 'react-three-renderer/lib/React3Renderer'; import * as THREE from 'three'; // ^ Look mom, no react-dom! const canvas = document.getElementById('canvas'); const react3Renderer = new React3Renderer(); const width = 800; const height = 600; const cameraPosition = new THREE.Vector3(0, 0, 5); let cubeRotation = new THREE.Euler(); function onRecreateCanvas() { // no need to deal with this now, but here we'd need to create a new canvas and // re-render the scene there. } function animate() { cubeRotation = new THREE.Euler( cubeRotation.x + 0.1, cubeRotation.y + 0.1, 0 ); react3Renderer.render( , canvas); requestAnimationFrame(animate); } animate(); ================================================ FILE: src/examples/AnimationCloth/Cloth.js ================================================ /* * Cloth Simulation using a relaxed constrains solver */ // Suggested Readings // Advanced Character Physics by Thomas Jakobsen Character // http://freespace.virgin.net/hugo.elias/models/m_cloth.htm // http://en.wikipedia.org/wiki/Cloth_modeling // http://cg.alexandra.dk/tag/spring-mass-system/ // Real-time Cloth Animation http://www.darwin3d.com/gamedev/articles/col0599.pdf import * as THREE from 'three'; function plane(width, height) { return (u, v) => { const x = (u - 0.5) * width; const y = (v + 0.5) * height; const z = 0; return new THREE.Vector3(x, y, z); }; } const DAMPING = 0.03; const DRAG = 1 - DAMPING; const MASS = 0.1; const restDistance = 25; const xSegs = 10; // const ySegs = 10; // const clothFunction = plane(restDistance * xSegs, restDistance * ySegs); class Particle { constructor(x, y, z, mass) { this.position = clothFunction(x, y); // position this.previous = clothFunction(x, y); // previous this.original = clothFunction(x, y); this.a = new THREE.Vector3(0, 0, 0); // acceleration this.mass = mass; this.invMass = 1 / mass; this.tmp = new THREE.Vector3(); this.tmp2 = new THREE.Vector3(); } // Force -> Acceleration addForce(force) { this.a.add( this.tmp2.copy(force).multiplyScalar(this.invMass), ); } // Performs verlet integration integrate(timesQ) { const newPos = this.tmp.subVectors(this.position, this.previous); newPos.multiplyScalar(DRAG).add(this.position); newPos.add(this.a.multiplyScalar(timesQ)); this.tmp = this.previous; this.previous = this.position; this.position = newPos; this.a.set(0, 0, 0); } } class Cloth { static clothFunction = clothFunction; static MASS = MASS; constructor(w = 10, h = 10) { this.w = w; this.h = h; const particles = []; const constrains = []; let u; let v; // Create particles for (v = 0; v <= h; ++v) { for (u = 0; u <= w; ++u) { particles.push( new Particle(u / w, v / h, 0, MASS), ); } } function index(indexU, indexV) { return indexU + indexV * (w + 1); } // Structural for (v = 0; v < h; v++) { for (u = 0; u < w; u++) { constrains.push([ particles[index(u, v)], particles[index(u, v + 1)], restDistance, ]); constrains.push([ particles[index(u, v)], particles[index(u + 1, v)], restDistance, ]); } } for (u = w, v = 0; v < h; v++) { constrains.push([ particles[index(u, v)], particles[index(u, v + 1)], restDistance, ]); } for (v = h, u = 0; u < w; u++) { constrains.push([ particles[index(u, v)], particles[index(u + 1, v)], restDistance, ]); } this.particles = particles; this.constrains = constrains; this.index = index; } } export default Cloth; ================================================ FILE: src/examples/AnimationCloth/ClothGeometry.jsx ================================================ import React from 'react'; import PropTypes from 'prop-types'; import PureRenderMixin from 'react/lib/ReactComponentWithPureRenderMixin'; import Cloth from './Cloth'; class ClothGeometry extends React.Component { static propTypes = { cloth: PropTypes.instanceOf(Cloth).isRequired, }; componentDidMount() { const geometry = this.geometry; geometry.computeFaceNormals(); } _geometryRef = (geometry) => { this.geometry = geometry; }; shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate; render() { const { cloth, } = this.props; return (); } } export default ClothGeometry; ================================================ FILE: src/examples/AnimationCloth/Info.js ================================================ import React from 'react'; import PropTypes from 'prop-types'; class Info extends React.Component { static propTypes = { toggleWind: PropTypes.func.isRequired, toggleSphere: PropTypes.func.isRequired, togglePins: PropTypes.func.isRequired, toggleRotate: PropTypes.func.isRequired, onFrameChange: PropTypes.func.isRequired, minTimePerFrame: PropTypes.number.isRequired, rotating: PropTypes.bool.isRequired, winding: PropTypes.bool.isRequired, balling: PropTypes.bool.isRequired, }; render() { const linkStyle = { textDecoration: 'underline', cursor: 'pointer', }; const { toggleRotate, toggleWind, toggleSphere, togglePins, minTimePerFrame, onFrameChange, rotating, winding, balling, } = this.props; return (
three.js - Simple Cloth Simulation
Verlet integration with Constrains relaxation
Toggle: Camera{rotating ? '*' : null} | Wind{winding ? '*' : null} | Ball{balling ? '*' : null} | Pins | Time between frames (ms):
Note: add some time between frames (e.g. 60ms) if you would like to inspect the scene through react devtools, because updating every frame kills the addon.
); } } export default Info; ================================================ FILE: src/examples/AnimationCloth/Poles.js ================================================ import React from 'react'; import * as THREE from 'three'; import PureRenderMixin from 'react/lib/ReactComponentWithPureRenderMixin'; class Poles extends React.Component { constructor(props, context) { super(props, context); this.state = { poleMaterialColor: Number(0xffffff).toString(16), poleMaterialSpecular: Number(0x111111).toString(16), poleMaterialShininess: 100, sidePolePositions: [ new THREE.Vector3(-125, -62, 0), new THREE.Vector3(125, -62, 0), ], boxPositions: [ new THREE.Vector3(125, -250, 0), new THREE.Vector3(-125, -250, 0), ], topPolePosition: new THREE.Vector3(0, -250 + 750 / 2, 0), subResource: false, }; } shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate; render() { return ( {this.state.sidePolePositions.map((position, i) => ( ))} { this.state.subResource ? {this.state.subResource ? : null} { } : null } {this.state.boxPositions.map((position, i) => ( ))} ); } } export default Poles; ================================================ FILE: src/examples/AnimationCloth/Sphere.js ================================================ import React from 'react'; import * as THREE from 'three'; import PureRenderMixin from 'react/lib/ReactComponentWithPureRenderMixin'; import PropTypes from 'prop-types'; const ballSize = 60; // 40 class Sphere extends React.Component { static propTypes = { visible: PropTypes.bool.isRequired, position: PropTypes.instanceOf(THREE.Vector3).isRequired, }; constructor(props, context) { super(props, context); this.state = { color: '0xaaaaaa', }; } shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate; render() { const { visible, position, } = this.props; return ( ); } } export default Sphere; ================================================ FILE: src/examples/AnimationCloth/StaticWorld.js ================================================ import React from 'react'; import * as THREE from 'three'; import PropTypes from 'prop-types'; import PureRenderMixin from 'react/lib/ReactComponentWithPureRenderMixin'; import fragmentShaderDepth from 'raw-loader!./shaders/depth.frag'; import vertexShaderDepth from 'raw-loader!./shaders/depth.vert'; import ClothGeometry from './ClothGeometry'; import Poles from './Poles'; import Cloth from './Cloth'; class StaticWorld extends React.Component { static propTypes = { clothRef: PropTypes.func.isRequired, cloth: PropTypes.instanceOf(Cloth).isRequired, }; constructor(props, context) { super(props, context); this.directionalLightPosition = new THREE.Vector3(50, 200, 100).multiplyScalar(1.3); this.lightTarget = new THREE.Vector3(0, 0, 0); this.groundPosition = new THREE.Vector3(0, -250, 0); this.groundRotation = new THREE.Euler(-Math.PI / 2, 0, 0); this.groundRepeat = new THREE.Vector2(25, 25); this.state = { ambientLightColor: '666666', directionalLightColor: 'dfebff', fragmentShaderDepth, vertexShaderDepth, }; // check if HMR is enabled if (module.hot) { // accept update of dependency module.hot.accept('raw-loader!./shaders/depth.frag', () => { this.setState({ fragmentShaderDepth: require('raw-loader!./shaders/depth.frag'), }); }); module.hot.accept('raw-loader!./shaders/depth.vert', () => { this.setState({ vertexShaderDepth: require('raw-loader!./shaders/depth.vert'), }); }); } } shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate; render() { const shadowCameraSize = 300; const { ambientLightColor, directionalLightColor, fragmentShaderDepth: frag, vertexShaderDepth: vert, } = this.state; return ( { /* */ } ); } } export default StaticWorld; ================================================ FILE: src/examples/AnimationCloth/index.js ================================================ import jsx from './index.jsx'; module.exports = jsx; ================================================ FILE: src/examples/AnimationCloth/index.jsx ================================================ import React from 'react'; import * as THREE from 'three'; import Stats from 'stats.js'; import React3 from 'react-three-renderer'; import ExampleBase from '../ExampleBase'; import Info from './Info'; import Cloth from './Cloth'; import StaticWorld from './StaticWorld'; import Sphere from './Sphere'; import TrackballControls from '../../ref/trackball'; const ballSize = 60; // 40 const GRAVITY = 981 * 1.4; // const gravity = new THREE.Vector3(0, -GRAVITY, 0).multiplyScalar(Cloth.MASS); const TIMESTEP = 18 / 1000; const TIMESTEP_SQ = TIMESTEP * TIMESTEP; const diff = new THREE.Vector3(); function satisfyConstrains(p1, p2, distance) { diff.subVectors(p2.position, p1.position); const currentDist = diff.length(); if (currentDist === 0) { return; } // prevents division by 0 const correction = diff.multiplyScalar(1 - distance / currentDist); const correctionHalf = correction.multiplyScalar(0.5); p1.position.add(correctionHalf); p2.position.sub(correctionHalf); } const tmpForce = new THREE.Vector3(); class AnimationCloth extends ExampleBase { constructor(props, context) { super(props, context); this.state = { ...this.state, minTimePerFrame: 0, rotate: true, wind: true, sphere: false, }; const xSegs = 10; // const ySegs = 10; // this.cloth = new Cloth(xSegs, ySegs); const pinsFormation = []; let pins = [6]; pinsFormation.push(pins); pins = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; pinsFormation.push(pins); pins = [0]; pinsFormation.push(pins); pins = []; // cut the rope ;) pinsFormation.push(pins); pins = [0, this.cloth.w]; // classic 2 pins pinsFormation.push(pins); pins = pinsFormation[1]; this.pins = pins; this.pinsFormation = pinsFormation; this.fog = new THREE.Fog(0xcce0ff, 500, 10000); this.windForce = new THREE.Vector3(0, 0, 0); this.state = { ...this.state, ballPosition: new THREE.Vector3(0, -45, 0), cameraPosition: new THREE.Vector3(0, 50, 1500), }; this.scenePosition = new THREE.Vector3(0, 0, 0); } componentDidMount() { const controls = new TrackballControls( this.mainCamera, this.react3); controls.rotateSpeed = 1.0; controls.zoomSpeed = 1.2; controls.panSpeed = 0.8; controls.noZoom = false; controls.noPan = false; controls.staticMoving = true; controls.dynamicDampingFactor = 0.3; controls.addEventListener('change', () => { this.setState({ cameraPosition: this.mainCamera.position, }); }); this.controls = controls; this.stats = new Stats(); this.stats.domElement.style.position = 'absolute'; this.stats.domElement.style.top = '0px'; this.container.appendChild(this.stats.domElement); } componentWillUnmount() { delete this.stats; this.controls.dispose(); delete this.controls; } _toggleRotate = () => { this.setState({ rotate: !this.state.rotate }); }; _toggleWind = () => { this.setState({ wind: !this.state.wind }); }; _toggleSphere = () => { this.setState({ sphere: !this.state.sphere }); }; _togglePins = () => { this.pins = this.pinsFormation[~~(Math.random() * this.pinsFormation.length)]; }; _simulate(time) { if (!this.lastTime) { this.lastTime = time; return; } let i; let il; let particles; let particle; let constrain; const clothGeometry = React3.findTHREEObject(this._clothGeometry); const sphere = React3.findTHREEObject(this.sphere); // Aerodynamics forces if (this.state.wind) { let face; const faces = clothGeometry.faces; let normal; particles = this.cloth.particles; for (i = 0, il = faces.length; i < il; i++) { face = faces[i]; normal = face.normal; tmpForce.copy(normal).normalize().multiplyScalar(normal.dot(this.windForce)); particles[face.a].addForce(tmpForce); particles[face.b].addForce(tmpForce); particles[face.c].addForce(tmpForce); } } for (particles = this.cloth.particles, i = 0, il = particles.length; i < il; i++) { particle = particles[i]; particle.addForce(gravity); particle.integrate(TIMESTEP_SQ); } // Start Constrains const constrains = this.cloth.constrains; il = constrains.length; for (i = 0; i < il; i++) { constrain = constrains[i]; satisfyConstrains(constrain[0], constrain[1], constrain[2]); } const ballPosition = this.state.ballPosition.clone(); // Ball Constrains ballPosition.z = -Math.sin(Date.now() / 600) * 90; // + 40; ballPosition.x = Math.cos(Date.now() / 400) * 70; if (sphere.visible) { for (particles = this.cloth.particles, i = 0, il = particles.length; i < il; i++) { particle = particles[i]; const pos = particle.position; diff.subVectors(pos, ballPosition); if (diff.length() < ballSize) { // collided diff.normalize().multiplyScalar(ballSize); pos.copy(ballPosition).add(diff); } } } // Floor Constraints for (particles = this.cloth.particles, i = 0, il = particles.length ; i < il; i++) { particle = particles[i]; const pos = particle.position; if (pos.y < -250) { pos.y = -250; } } // Pin Constrains for (i = 0, il = this.pins.length; i < il; i++) { const xy = this.pins[i]; const p = particles[xy]; p.position.copy(p.original); p.previous.copy(p.original); } this.setState({ ballPosition, }); } _onAnimate = () => { this.controls.update(); const { minTimePerFrame, } = this.state; let time; if (minTimePerFrame > 0) { time = Math.round(Date.now() / minTimePerFrame) * minTimePerFrame; } else { time = Date.now(); } if (time === this.state.time) { return; } const windStrength = Math.cos(time / 7000) * 20 + 40; this.windForce.set( Math.sin(time / 2000), Math.cos(time / 3000), Math.sin(time / 1000)).normalize().multiplyScalar(windStrength); this._simulate(time); const clothGeometry = React3.findTHREEObject(this._clothGeometry); // render const timer = time * 0.0002; const p = this.cloth.particles; let il; let i; for (i = 0, il = p.length; i < il; ++i) { clothGeometry.vertices[i].copy(p[i].position); } clothGeometry.computeFaceNormals(); clothGeometry.computeVertexNormals(); clothGeometry.normalsNeedUpdate = true; clothGeometry.verticesNeedUpdate = true; const newState = { time, spherePosition: this.ballPosition, }; if (this.state.rotate) { newState.cameraPosition = new THREE.Vector3( Math.cos(timer) * 1500, this.state.cameraPosition.y, Math.sin(timer) * 1500); } this.setState(newState); this.stats.update(); }; _clothRef = (ref) => { this._clothGeometry = ref; }; _onFrameChange = (event) => { this.setState({ minTimePerFrame: +event.target.value, }); }; _containerRef = (container) => { this.container = container; }; _sphereRef = (sphere) => { this.sphere = sphere; }; _react3Ref = (react3) => { this.react3 = react3; }; _mainCameraRef = (mainCamera) => { this.mainCamera = mainCamera; }; render() { const { width, height, } = this.props; const { minTimePerFrame, } = this.state; return (
); } } export default AnimationCloth; ================================================ FILE: src/examples/AnimationCloth/shaders/depth.frag ================================================ uniform sampler2D texture; varying vec2 vUV; 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 = fract( depth * bit_shift ); res -= res.xxyz * bit_mask; return res; } void main() { vec4 pixel = texture2D( texture, vUV ); if ( pixel.a < 0.5 ) discard; gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z ); } ================================================ FILE: src/examples/AnimationCloth/shaders/depth.vert ================================================ varying vec2 vUV; void main() { vUV = 0.75 * uv; vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); gl_Position = projectionMatrix * mvPosition; } ================================================ FILE: src/examples/Benchmark/RotatingCube.js ================================================ import React from 'react'; import * as THREE from 'three'; import PropTypes from 'prop-types'; const meshScale = new THREE.Vector3(1, 1, 1).multiplyScalar(0.5); class RotatingCube extends React.Component { static propTypes = { position: PropTypes.instanceOf(THREE.Vector3).isRequired, quaternion: PropTypes.instanceOf(THREE.Quaternion).isRequired, }; render() { const { position, quaternion, } = this.props; return ( ); } } export default RotatingCube; ================================================ FILE: src/examples/Benchmark/RotatingCubes.js ================================================ import React from 'react'; import React3 from 'react-three-renderer'; import * as THREE from 'three'; import ExampleBase from '../ExampleBase'; import Stats from 'stats.js'; import RotatingCube from './RotatingCube'; class RotatingCubes extends ExampleBase { constructor(props, context) { super(props, context); const N = 200; this.fog = new THREE.Fog(0x001525, 10, 40); const d = 20; this.lightPosition = new THREE.Vector3(d, d, d); this.lightTarget = new THREE.Vector3(0, 0, 0); this.groundQuaternion = new THREE.Quaternion() .setFromAxisAngle(new THREE.Vector3(1, 0, 0), -Math.PI / 2); this.cameraPosition = new THREE.Vector3(10, 2, 0); this.cameraQuaternion = new THREE.Quaternion() .setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI / 2); const bodies = []; bodies.length = N; this.bodies = bodies; this._createBodies(); this.state = { numBodies: N, meshStates: this._getMeshStates(), }; } _getMeshStates() { return this.bodies.map(({ position, quaternion }) => ({ position: new THREE.Vector3().copy(position), quaternion: new THREE.Quaternion().copy(quaternion), })); } _onAnimate = () => { this._updatePhysics(); this._updateGraphics(); this.stats.update(); }; _updateGraphics() { this.setState({ meshStates: this._getMeshStates(), }); } _updatePhysics() { const time = new Date().getTime(); const bodies = this.bodies; for (let i = 0; i < bodies.length; ++i) { const body = bodies[i]; const sinTime = Math.sin(time * body.timeScale); body.quaternion.multiply(body.rotationDeltaPerFrame); const { movementPerFrame } = body; body.position.copy(body.startPosition.clone() .add(movementPerFrame.clone() .multiplyScalar(sinTime))); } } componentDidMount() { const { container, } = this.refs; this.stats = new Stats(); this.stats.domElement.style.position = 'absolute'; this.stats.domElement.style.top = '0px'; container.appendChild(this.stats.domElement); } componentWillUnmount() { delete this.stats; } _createBodies() { const { bodies } = this; const N = bodies.length; for (let i = 0; i < N; ++i) { bodies[i] = this._createBody(i); } } _createBody() { const position = new THREE.Vector3( -2.5 + Math.random() * 5, 0.5 + Math.random() * 5, -2.5 + Math.random() * 5); return { position, timeScale: Math.random() * 0.005, startPosition: position.clone(), movementPerFrame: new THREE.Vector3(Math.random(), Math.random(), Math.random()), rotationDeltaPerFrame: new THREE.Quaternion() .setFromEuler(new THREE.Euler( Math.random() * 0.05, Math.random() * 0.05, Math.random() * 0.05)), quaternion: new THREE.Quaternion(), }; } _onBodiesSelectChange = (event) => { const numBodies = event.target.value; this.bodies.length = numBodies; this._createBodies(); this.setState({ numBodies, meshStates: this._getMeshStates(), }); this._updateGraphics(); }; _getInputBox(title) { const { numBodies } = this.state; return (
{title}
); } render() { const { width, height, } = this.props; const { meshStates, } = this.state; const d = 20; const cubeMeshes = meshStates.map(({ position, quaternion }, i) => ()); return (
{this._getInputBox('Rotating Cubes - Through React')} {cubeMeshes}
); } } export default RotatingCubes; ================================================ FILE: src/examples/Benchmark/RotatingCubesDirectUpdates.js ================================================ import React from 'react'; import React3 from 'react-three-renderer'; import * as THREE from 'three'; import RotatingCube from './RotatingCube'; import RotatingCubes from './RotatingCubes'; class RotatingCubesDirectUpdates extends RotatingCubes { _getMeshStates() { const { bodies } = this; return bodies.map(({ position, quaternion, ref }) => ({ position: new THREE.Vector3().copy(position), quaternion: new THREE.Quaternion().copy(quaternion), ref, })); } _bodyRef(index, body) { if (body === null) { // dismounted return; } this.bodies[index].body = React3.findTHREEObject(body); } _updateGraphics() { const { bodies } = this; for (let i = 0; i < bodies.length; ++i) { const body = bodies[i]; if (body.body) { body.body.position.copy(body.position); body.body.quaternion.copy(body.quaternion); } } } _createBody(i) { return { ...super._createBody(), ref: this._bodyRef.bind(this, i), }; } render() { const { width, height, } = this.props; const { meshStates, } = this.state; const d = 20; const cubeMeshes = meshStates.map(({ position, quaternion, ref }, i) => ()); return (
{this._getInputBox('Rotating Cubes - Direct Updates')} {cubeMeshes}
); } } export default RotatingCubesDirectUpdates; ================================================ FILE: src/examples/DraggableCubes/AllCubes.js ================================================ import React from 'react'; import DraggableCube from './DraggableCube'; import * as THREE from 'three'; import PureRenderMixin from 'react/lib/ReactComponentWithPureRenderMixin'; import PropTypes from 'prop-types'; import MouseInput from '../inputs/MouseInput'; class AllCubes extends React.Component { static propTypes = { mouseInput: PropTypes.instanceOf(MouseInput), camera: PropTypes.instanceOf(THREE.PerspectiveCamera), onCubesMounted: PropTypes.func.isRequired, onHoverStart: PropTypes.func.isRequired, onHoverEnd: PropTypes.func.isRequired, onDragStart: PropTypes.func.isRequired, onDragEnd: PropTypes.func.isRequired, cursor: PropTypes.any, }; constructor(props, context) { super(props, context); const cubePositions = []; cubePositions.length = 200; for (let i = 0; i < 200; ++i) { cubePositions[i] = new THREE.Vector3( Math.random() * 1000 - 500, Math.random() * 600 - 300, Math.random() * 800 - 400 ); } const cubes = []; cubes.length = cubePositions.length; this.cubes = cubes; this.cubePositions = cubePositions; this._hoveredCubes = 0; this._draggingCubes = 0; } componentDidMount() { const { onCubesMounted, } = this.props; onCubesMounted(this.cubes); } shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate; _onCubeCreate = (index, cube) => { this.cubes[index] = cube; }; _onCubeMouseEnter = () => { if (this._hoveredCubes === 0) { const { onHoverStart, } = this.props; onHoverStart(); } this._hoveredCubes++; }; _onCubeMouseLeave = () => { this._hoveredCubes--; if (this._hoveredCubes === 0) { const { onHoverEnd, } = this.props; onHoverEnd(); } }; _onCubeDragStart = () => { if (this._draggingCubes === 0) { const { onDragStart, } = this.props; onDragStart(); } this._draggingCubes++; }; _onCubeDragEnd = () => { this._draggingCubes--; if (this._draggingCubes === 0) { const { onDragEnd, } = this.props; onDragEnd(); } }; render() { const { mouseInput, camera, cursor, } = this.props; return ( {this.cubePositions.map((cubePosition, index) => { const onCreate = this._onCubeCreate.bind(this, index); return (); })} ); } } export default AllCubes; ================================================ FILE: src/examples/DraggableCubes/DraggableCube.js ================================================ import React from 'react'; import PropTypes from 'prop-types'; import * as THREE from 'three'; import PureRenderMixin from 'react/lib/ReactComponentWithPureRenderMixin'; import MouseInput from '../inputs/MouseInput'; // shared plane for dragging purposes // it's good to share because you can drag only one cube at a time const dragPlane = new THREE.Plane(); const backVector = new THREE.Vector3(0, 0, -1); class DraggableCube extends React.Component { static propTypes = { initialPosition: PropTypes.instanceOf(THREE.Vector3).isRequired, mouseInput: PropTypes.instanceOf(MouseInput), camera: PropTypes.instanceOf(THREE.PerspectiveCamera), onCreate: PropTypes.func.isRequired, onMouseEnter: PropTypes.func.isRequired, onMouseLeave: PropTypes.func.isRequired, onDragStart: PropTypes.func.isRequired, onDragEnd: PropTypes.func.isRequired, cursor: PropTypes.any, }; constructor(props, context) { super(props, context); this.rotation = new THREE.Euler( Math.random() * 2 * Math.PI, Math.random() * 2 * Math.PI, Math.random() * 2 * Math.PI ); this.scale = new THREE.Vector3( Math.random() * 2 + 1, Math.random() * 2 + 1, Math.random() * 2 + 1 ); this.color = new THREE.Color(Math.random() * 0xffffff); const hsl = this.color.getHSL(); hsl.s = Math.min(1, hsl.s * 1.1); hsl.l = Math.min(1, hsl.l * 1.1); const { h, s, l } = hsl; this.hoverColor = new THREE.Color().setHSL(h, s, l); this.pressedColor = 0xff0000; const { initialPosition, } = props; this.state = { hovered: false, pressed: false, position: initialPosition, }; } shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate; componentWillUnmount() { document.removeEventListener('mouseup', this._onDocumentMouseUp); } _onMouseEnter = () => { this.setState({ hovered: true, }); const { onMouseEnter } = this.props; onMouseEnter(); }; _onMouseDown = (event, intersection) => { event.preventDefault(); event.stopPropagation(); const { position, } = this.state; const { onDragStart, camera, } = this.props; dragPlane.setFromNormalAndCoplanarPoint(backVector.clone() .applyQuaternion(camera.quaternion), intersection.point); this._offset = intersection.point.clone().sub(position); document.addEventListener('mouseup', this._onDocumentMouseUp); document.addEventListener('mousemove', this._onDocumentMouseMove); this.setState({ pressed: true, }); onDragStart(); }; _onDocumentMouseMove = (event) => { event.preventDefault(); const { mouseInput, } = this.props; const ray:THREE.Ray = mouseInput.getCameraRay(new THREE .Vector2(event.clientX, event.clientY)); const intersection = dragPlane.intersectLine(new THREE.Line3( ray.origin, ray.origin.clone() .add(ray.direction.clone().multiplyScalar(10000)) )); if (intersection) { this.setState({ position: intersection.sub(this._offset), }); } }; _onDocumentMouseUp = (event) => { event.preventDefault(); document.removeEventListener('mouseup', this._onDocumentMouseUp); document.removeEventListener('mousemove', this._onDocumentMouseMove); const { onDragEnd, } = this.props; onDragEnd(); this.setState({ pressed: false, }); }; _onMouseLeave = () => { if (this.state.hovered) { this.setState({ hovered: false, }); } const { onMouseLeave, } = this.props; onMouseLeave(); }; _ref = (mesh) => { const { onCreate, } = this.props; onCreate(mesh); }; render() { const { rotation, scale, } = this; const { cursor: { dragging, }, } = this.props; const { hovered, pressed, position, } = this.state; let color; const hoverHighlight = (hovered && !dragging); if (pressed) { color = this.pressedColor; } else if (hoverHighlight) { color = this.hoverColor; } else { color = this.color; } return ( {hoverHighlight ? : null} ); } } export default DraggableCube; ================================================ FILE: src/examples/DraggableCubes/index.js ================================================ import React from 'react'; import PureRenderMixin from 'react/lib/ReactComponentWithPureRenderMixin'; import * as THREE from 'three'; import Stats from 'stats.js'; import React3 from 'react-three-renderer'; import ExampleBase from '../ExampleBase'; import TrackballControls from '../../ref/trackball'; import MouseInput from '../inputs/MouseInput'; import AllCubes from './AllCubes'; class DraggableCubes extends ExampleBase { constructor(props, context) { super(props, context); this.state = { cameraPosition: new THREE.Vector3(0, 0, 1000), cameraRotation: new THREE.Euler(), mouseInput: null, hovering: false, dragging: false, }; this._cursor = { hovering: false, dragging: false, }; this.lightPosition = new THREE.Vector3(0, 500, 2000); this.lightTarget = new THREE.Vector3(0, 0, 0); } shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate; _onAnimate = () => { this._onAnimateInternal(); }; componentDidMount() { this.stats = new Stats(); this.stats.domElement.style.position = 'absolute'; this.stats.domElement.style.top = '0px'; const { container, camera, } = this.refs; container.appendChild(this.stats.domElement); const controls = new TrackballControls(camera); controls.rotateSpeed = 1.0; controls.zoomSpeed = 1.2; controls.panSpeed = 0.8; controls.noZoom = false; controls.noPan = false; controls.staticMoving = true; controls.dynamicDampingFactor = 0.3; this.controls = controls; this.controls.addEventListener('change', this._onTrackballChange); } _onCubesMounted = (cubes) => { this.cubes = cubes; }; _onHoverStart = () => { this.setState({ hovering: true, }); }; _onHoverEnd = () => { this.setState({ hovering: false, }); }; _onDragStart = () => { this.setState({ dragging: true, }); }; _onDragEnd = () => { this.setState({ dragging: false, }); }; componentDidUpdate(newProps) { const { mouseInput, } = this.refs; const { width, height, } = this.props; if (width !== newProps.width || height !== newProps.height) { mouseInput.containerResized(); } } _onTrackballChange = () => { this.setState({ cameraPosition: this.refs.camera.position.clone(), cameraRotation: this.refs.camera.rotation.clone(), }); }; componentWillUnmount() { this.controls.removeEventListener('change', this._onTrackballChange); this.controls.dispose(); delete this.controls; delete this.stats; } _onAnimateInternal() { const { mouseInput, camera, } = this.refs; if (!mouseInput.isReady()) { const { scene, container, } = this.refs; mouseInput.ready(scene, container, camera); mouseInput.restrictIntersections(this.cubes); mouseInput.setActive(false); } if (this.state.mouseInput !== mouseInput) { this.setState({ mouseInput, }); } if (this.state.camera !== camera) { this.setState({ camera, }); } this.stats.update(); this.controls.update(); } render() { const { width, height, } = this.props; const { cameraPosition, cameraRotation, mouseInput, camera, hovering, dragging, } = this.state; const style = {}; if (dragging) { style.cursor = 'move'; } else if (hovering) { style.cursor = 'pointer'; } this._cursor.hovering = hovering; this._cursor.dragging = dragging; return (
); } } export default DraggableCubes; ================================================ FILE: src/examples/ExampleBase.js ================================================ import React from 'react'; import PropTypes from 'prop-types'; class ExampleBase extends React.Component { static propTypes = { width: PropTypes.number.isRequired, height: PropTypes.number.isRequired, }; } export default ExampleBase; ================================================ FILE: src/examples/ExampleBrowser.js ================================================ import React from 'react'; import { NavLink } from 'react-router-dom'; import ExampleViewer from './ExampleViewer'; import SimpleExample from './Simple/index'; import ManualRenderingExample from './ManualRendering/index'; import ClothExample from './AnimationCloth/index'; import GeometriesExample from './Geometries/index'; import CameraExample from './WebGLCameraExample/index'; import GeometryShapesExample from './GeometryShapes/index'; import DraggableCubes from './DraggableCubes/index'; import Physics from './Physics/index'; import PhysicsMousePick from './Physics/mousePick'; import BenchmarkRotatingCubes from './Benchmark/RotatingCubes'; import RotatingCubesDirectUpdates from './Benchmark/RotatingCubesDirectUpdates'; const examples = [ { name: 'Simple', component: SimpleExample, url: 'Simple/index', slug: 'webgl_simple', }, { name: 'Cloth', component: ClothExample, url: 'AnimationCloth/index', slug: 'webgl_cloth', }, { name: 'Camera', component: CameraExample, url: 'WebGLCameraExample/index', slug: 'webgl_camera', }, { name: 'Geometries', component: GeometriesExample, url: 'Geometries/index', slug: 'webgl_geometries', }, { name: 'Geometry Shapes', component: GeometryShapesExample, url: 'GeometryShapes/index', slug: 'webgl_geometry_shapes', }, { name: 'Draggable Cubes', component: DraggableCubes, url: 'DraggableCubes/index', slug: 'webgl_draggable_cubes', }, { name: 'Physics', component: Physics, url: 'Physics/index', slug: 'webgl_physics', }, { name: 'Physics - MousePick', component: PhysicsMousePick, url: 'Physics/mousePick', slug: 'webgl_physics_mousepick', }, { separator: true, name: 'Advanced', }, { name: 'Without react-dom', advanced: true, page: 'advanced.html', }, { name: 'Manual rendering', component: ManualRenderingExample, url: 'ManualRendering/index', slug: 'advanced_manual_rendering', }, { separator: true, name: 'Benchmarks', }, { name: 'RotatingCubes - Through React', component: BenchmarkRotatingCubes, url: 'Benchmark/RotatingCubes', slug: 'benchmarks_rotating_cubes_react', }, { name: 'RotatingCubes - Direct Updates', component: RotatingCubesDirectUpdates, url: 'Benchmark/RotatingCubesDirectUpdates', slug: 'benchmarks_rotating_cubes_direct', }, ]; const ExampleBrowser = ({ match }) => { const { params } = match; const activeExample = params.slug && examples.find(example => example.slug === params.slug); return (

react-three-renderer / examples

webgl

{examples.map((example, index) => { if (example.separator) { return (

{example.name}

); } if (example.advanced) { return (
{example.name} (new tab)
); } return ( {example.name} ); })}
); }; ExampleBrowser.propTypes = { match: React.PropTypes.object.isRequired, }; export default ExampleBrowser; ================================================ FILE: src/examples/ExampleViewer.js ================================================ import React from 'react'; import sizeMe from 'react-sizeme'; const ExampleViewer = ({ example, size }) => { let sourceButton = null; let exampleContent = null; if (example) { const { component: ExampleComponent, url, } = example; exampleContent = (); sourceButton = (); } return (
{exampleContent} {sourceButton}
); }; ExampleViewer.propTypes = { example: React.PropTypes.object, size: React.PropTypes.object, }; export default sizeMe({ monitorHeight: true, refreshRate: 200 })(ExampleViewer); ================================================ FILE: src/examples/Geometries/index.js ================================================ import React from 'react'; import * as THREE from 'three'; import Stats from 'stats.js'; import React3 from 'react-three-renderer'; import ExampleBase from '../ExampleBase'; class Geometries extends ExampleBase { constructor(props, context) { super(props, context); this.directionalLightPosition = new THREE.Vector3(0, 1, 0); this.objectPositions = [ new THREE.Vector3(-400, 0, 200), new THREE.Vector3(-200, 0, 200), new THREE.Vector3(0, 0, 200), new THREE.Vector3(200, 0, 200), new THREE.Vector3(-400, 0, 0), new THREE.Vector3(-200, 0, 0), new THREE.Vector3(0, 0, 0), new THREE.Vector3(200, 0, 0), new THREE.Vector3(400, 0, 0), new THREE.Vector3(-400, 0, -200), new THREE.Vector3(-200, 0, -200), new THREE.Vector3(0, 0, -200), new THREE.Vector3(200, 0, -200), new THREE.Vector3(400, 0, -200), ]; this.lathePoints = []; for (let i = 0; i < 50; i++) { this.lathePoints.push(new THREE .Vector2(Math.sin(i * 0.2) * Math.sin(i * 0.1) * 15 + 50, (i - 5) * 2)); } this.arrowDir = new THREE.Vector3(0, 1, 0); this.arrowOrigin = new THREE.Vector3(0, 0, 0); this.scenePosition = new THREE.Vector3(0, 0, 0); this.state = { ...this.state, timer: Date.now() * 0.0001, }; } _onAnimate = () => { this._onAnimateInternal(); }; componentDidMount() { this.stats = new Stats(); this.stats.domElement.style.position = 'absolute'; this.stats.domElement.style.top = '0px'; this.refs.container.appendChild(this.stats.domElement); } componentWillUnmount() { delete this.stats; } _onAnimateInternal() { const timer = Date.now() * 0.0001; this.setState({ timer, }); this.stats.update(); } render() { const { width, height, } = this.props; const { timer, } = this.state; const objectRotation = new THREE.Euler( timer * 5, timer * 2.5, 0 ); return (
); } } export default Geometries; ================================================ FILE: src/examples/GeometryShapes/Rect.js ================================================ import React from 'react'; import PropTypes from 'react/lib/ReactPropTypes'; function Rect(props) { const { width, length, resourceId, } = props; return ( ); } Rect.propTypes = { width: PropTypes.number.isRequired, length: PropTypes.number.isRequired, resourceId: PropTypes.string.isRequired, }; export default Rect; ================================================ FILE: src/examples/GeometryShapes/Resources.js ================================================ import React from 'react'; import * as THREE from 'three'; import Rect from './Rect'; class Resources extends React.Component { shouldComponentUpdate() { return false; } render() { this.textureRepeat = new THREE.Vector2(0.008, 0.008); const x = 0; const y = 0; const sqLength = 80; const rectLength = 120; const rectWidth = 40; const californiaPts = []; californiaPts.push(new THREE.Vector2(610, 320)); californiaPts.push(new THREE.Vector2(450, 300)); californiaPts.push(new THREE.Vector2(392, 392)); californiaPts.push(new THREE.Vector2(266, 438)); californiaPts.push(new THREE.Vector2(190, 570)); californiaPts.push(new THREE.Vector2(190, 600)); californiaPts.push(new THREE.Vector2(160, 620)); californiaPts.push(new THREE.Vector2(160, 650)); californiaPts.push(new THREE.Vector2(180, 640)); californiaPts.push(new THREE.Vector2(165, 680)); californiaPts.push(new THREE.Vector2(150, 670)); californiaPts.push(new THREE.Vector2(90, 737)); californiaPts.push(new THREE.Vector2(80, 795)); californiaPts.push(new THREE.Vector2(50, 835)); californiaPts.push(new THREE.Vector2(64, 870)); californiaPts.push(new THREE.Vector2(60, 945)); californiaPts.push(new THREE.Vector2(300, 945)); californiaPts.push(new THREE.Vector2(300, 743)); californiaPts.push(new THREE.Vector2(600, 473)); californiaPts.push(new THREE.Vector2(626, 425)); californiaPts.push(new THREE.Vector2(600, 370)); californiaPts.push(new THREE.Vector2(610, 320)); for (let i = 0; i < californiaPts.length; i++) californiaPts[i].multiplyScalar(0.25); this.californiaPts = californiaPts; return ( {((function roundedRect(rectX, rectY, roundedRectWidth, roundedRectHeight, radius) { return ( ); })(0, 0, 50, 50, 20))} {((function circleShape() { const circleRadius = 40; return ( ); })())} {((function splineShape() { const splinePoints = []; splinePoints.push(new THREE.Vector2(70, 20)); splinePoints.push(new THREE.Vector2(80, 90)); splinePoints.push(new THREE.Vector2(-30, 70)); splinePoints.push(new THREE.Vector2(0, 0)); return ( ); })())} ); } } export default Resources; ================================================ FILE: src/examples/GeometryShapes/Shape.js ================================================ import React from 'react'; import * as THREE from 'three'; import PropTypes from 'react/lib/ReactPropTypes'; import PureRenderMixin from 'react/lib/ReactComponentWithPureRenderMixin'; const extrudeSettings = { amount: 8, bevelEnabled: true, bevelSegments: 2, steps: 2, bevelSize: 1, bevelThickness: 1, }; class Shape extends React.Component { static propTypes = { resourceId: PropTypes.string.isRequired, color: PropTypes.any.isRequired, x: PropTypes.number.isRequired, y: PropTypes.number.isRequired, z: PropTypes.number.isRequired, rx: PropTypes.number.isRequired, ry: PropTypes.number.isRequired, rz: PropTypes.number.isRequired, s: PropTypes.number.isRequired, }; shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate; render() { const { rx, ry, rz, s, resourceId, color, x, y, z, } = this.props; const rotation = new THREE.Euler(rx, ry, rz); const scale = new THREE.Vector3(s, s, s); return ( ); } } export default Shape; ================================================ FILE: src/examples/GeometryShapes/Shapes.js ================================================ import React from 'react'; import Shape from './Shape'; class Shapes extends React.Component { shouldComponentUpdate() { return false; } render() { return ( ); } } export default Shapes; ================================================ FILE: src/examples/GeometryShapes/index.js ================================================ import React from 'react'; import * as THREE from 'three'; import Stats from 'stats.js'; import React3 from 'react-three-renderer'; import ExampleBase from '../ExampleBase'; import Resources from './Resources'; import Shapes from './Shapes'; class GeometryShapes extends ExampleBase { constructor(props, context) { super(props, context); this.cameraPosition = new THREE.Vector3(0, 150, 500); this.groupPosition = new THREE.Vector3(0, 50, 0); this.targetRotationOnMouseDown = 0; this.mouseX = 0; this.mouseXOnMouseDown = 0; this.targetRotation = 0; this.state = { ...this.state, groupRotation: new THREE.Euler(0, 0, 0), }; } componentDidMount() { this.stats = new Stats(); const container = this.refs.container; this.stats.domElement.style.position = 'absolute'; this.stats.domElement.style.top = '0px'; container.appendChild(this.stats.domElement); container.addEventListener('mousedown', this._onDocumentMouseDown, false); container.addEventListener('touchstart', this._onDocumentTouchStart, false); document.addEventListener('touchmove', this._onDocumentTouchMove, false); } componentWillUnmount() { const container = this.refs.container; container.removeEventListener('mousedown', this._onDocumentMouseDown, false); container.removeEventListener('touchstart', this._onDocumentTouchStart, false); document.removeEventListener('touchmove', this._onDocumentTouchMove, false); document.removeEventListener('mousemove', this._onDocumentMouseMove, false); document.removeEventListener('mouseup', this._onDocumentMouseUp, false); document.removeEventListener('mouseout', this._onDocumentMouseOut, false); delete this.stats; } _onDocumentMouseDown = (event) => { event.preventDefault(); document.addEventListener('mousemove', this._onDocumentMouseMove, false); document.addEventListener('mouseup', this._onDocumentMouseUp, false); document.addEventListener('mouseout', this._onDocumentMouseOut, false); const { width, } = this.props; const windowHalfX = width / 2; this.mouseXOnMouseDown = event.clientX - windowHalfX; this.targetRotationOnMouseDown = this.targetRotation; }; _onDocumentMouseMove = (event) => { const { width, } = this.props; const windowHalfX = width / 2; this.mouseX = event.clientX - windowHalfX; this.targetRotation = this.targetRotationOnMouseDown + (this.mouseX - this.mouseXOnMouseDown) * 0.02; }; _onDocumentMouseUp = () => { document.removeEventListener('mousemove', this._onDocumentMouseMove, false); document.removeEventListener('mouseup', this._onDocumentMouseUp, false); document.removeEventListener('mouseout', this._onDocumentMouseOut, false); }; _onDocumentMouseOut = () => { document.removeEventListener('mousemove', this._onDocumentMouseMove, false); document.removeEventListener('mouseup', this._onDocumentMouseUp, false); document.removeEventListener('mouseout', this._onDocumentMouseOut, false); }; _onDocumentTouchStart = (event) => { if (event.touches.length === 1) { event.preventDefault(); const { width, } = this.props; const windowHalfX = width / 2; this.mouseXOnMouseDown = event.touches[0].pageX - windowHalfX; this.targetRotationOnMouseDown = this.targetRotation; } }; _onDocumentTouchMove = (event) => { if (event.touches.length === 1) { event.preventDefault(); const { width, } = this.props; const windowHalfX = width / 2; this.mouseX = event.touches[0].pageX - windowHalfX; this.targetRotation = this.targetRotationOnMouseDown + (this.mouseX - this.mouseXOnMouseDown) * 0.05; } }; _onAnimate = () => { this._onAnimateInternal(); }; _onAnimateInternal() { const groupRotationY = this.state.groupRotation.y; if (Math.abs(groupRotationY - this.targetRotation) > 0.0001) { this.setState({ groupRotation: new THREE.Euler(0, groupRotationY + (this.targetRotation - groupRotationY) * 0.05, 0), }); } this.stats.update(); } render() { const { width, height, } = this.props; const { groupRotation, } = this.state; return (
Simple procedurally generated 3D shapes
Drag to spin
); } } export default GeometryShapes; ================================================ FILE: src/examples/ManualRendering/Info.js ================================================ import React from 'react'; import PropTypes from 'prop-types'; class Info extends React.Component { static propTypes = { onUpdateColorButtonPress: PropTypes.func.isRequired, onRenderButtonPress: PropTypes.func.isRequired, onManualButtonPress: PropTypes.func.isRequired, forceManual: PropTypes.bool, cubeColor: PropTypes.number.isRequired, }; constructor(props, context) { super(props, context); this.state = { colorButtonPressed: false, }; } _onColorButtonPress = () => { this.props.onUpdateColorButtonPress(); this.setState({ colorButtonPressed: true, }); }; _onTriggerPress = () => { this.setState({ colorButtonPressed: false, }); this.props.onRenderButtonPress(); }; _manualButtonPress = () => { this.setState({ colorButtonPressed: false, }); this.props.onManualButtonPress(); }; render() { const { forceManual, cubeColor, } = this.props; const { colorButtonPressed, } = this.state; const triggerButtonStyle = {}; if (colorButtonPressed && forceManual) { triggerButtonStyle.fontWeight = 'bold'; } return (

Manual rendering

If automatic rendering is off, the canvas will re-render only when you press the "Trigger render" button.
This way you can save battery life or have finer controls for rendering.
Check your CPU profiler with automatic rendering on/off :)

> 16 & 255)}, ${(cubeColor >> 8 & 255)}, ${(cubeColor & 255)})`, }} > 

); } } export default Info; ================================================ FILE: src/examples/ManualRendering/index.js ================================================ import React from 'react'; import React3 from 'react-three-renderer'; import * as THREE from 'three'; import ExampleBase from '../ExampleBase'; import Info from './Info'; class Manual extends ExampleBase { constructor(props, context) { super(props, context); this.cameraPosition = new THREE.Vector3(0, 0, 5); this._onManualRenderTriggerCreated = (renderTrigger) => { // assign to variable to be able to reuse the trigger this._renderTrigger = renderTrigger; }; this.state = { cubeColor: 0x00ff00, forceManual: true, }; } componentDidMount() { // render one frame to show initial scene this._renderTrigger(); setTimeout(() => { // this should not be visible! this.setState({ cubeColor: 0x0000ff, }); }, 20); setTimeout(() => { // render again after updating color this.setState({ cubeColor: 0xff0000, }, () => { this._renderTrigger(); }); }, 1000); } _onUpdateColorButtonPress = () => { this.setState({ cubeColor: Math.floor(Math.random() * 0xffffff), }); }; _onRenderButtonPress = () => { this._renderTrigger(); }; _onManualButtonPress = () => { this.setState({ forceManual: !this.state.forceManual, }); }; render() { const { width, height, } = this.props; const { forceManual, cubeColor, } = this.state; return (
); } } export default Manual; ================================================ FILE: src/examples/Physics/index.js ================================================ import React from 'react'; import React3 from 'react-three-renderer'; import * as THREE from 'three'; import CANNON from 'cannon/src/Cannon'; import Stats from 'stats.js'; import ExampleBase from '../ExampleBase'; class Physics extends ExampleBase { constructor(props, context) { super(props, context); const world = new CANNON.World(); this.world = world; world.gravity.set(0, 0, 0); world.broadphase = new CANNON.NaiveBroadphase(); world.solver.iterations = 10; const shape = new CANNON.Box(new CANNON.Vec3(1, 1, 1)); const mass = 1; const body = new CANNON.Body({ mass, }); body.addShape(shape); body.angularVelocity.set(0, 10, 0); body.angularDamping = 0.5; world.addBody(body); this._onMouseDown = () => { body.angularVelocity.y += 5; }; this.cameraPosition = new THREE.Vector3(0, 0, 5); const timeStep = 1 / 60; const updatePhysics = () => { // Step the physics world world.step(timeStep); // Copy coordinates from Cannon.js to Three.js this.setState({ // need to call new THREE.* in order to ensure an update goes through meshPosition: new THREE.Vector3().copy(body.position), meshQuaternion: new THREE.Quaternion().copy(body.quaternion), }); }; this._onAnimate = () => { updatePhysics(); this.stats.update(); }; this.state = { meshPosition: new THREE.Vector3(), meshQuaternion: new THREE.Quaternion(), }; } componentWillUnmount() { delete this.world; delete this.stats; } componentDidMount() { const { container, } = this.refs; this.stats = new Stats(); this.stats.domElement.style.position = 'absolute'; this.stats.domElement.style.top = '0px'; container.appendChild(this.stats.domElement); } render() { const { width, height, } = this.props; const { meshPosition, meshQuaternion, } = this.state; return (
); } } export default Physics; ================================================ FILE: src/examples/Physics/mousePick/PickableMesh.js ================================================ import React from 'react'; import * as THREE from 'three'; import PropTypes from 'prop-types'; class PickableMesh extends React.Component { static propTypes = { position: PropTypes.instanceOf(THREE.Vector3).isRequired, quaternion: PropTypes.instanceOf(THREE.Quaternion).isRequired, meshes: PropTypes.arrayOf(PropTypes.instanceOf(THREE.Mesh)).isRequired, bodyIndex: PropTypes.number.isRequired, onMouseDown: PropTypes.func.isRequired, }; componentDidMount() { const { mesh, } = this.refs; const { bodyIndex, meshes, } = this.props; mesh.userData._bodyIndex = bodyIndex; meshes.push(mesh); } componentWillUnmount() { const { mesh, } = this.refs; const { meshes, } = this.props; meshes.splice(meshes.indexOf(mesh), 1); } _onMouseDown = (event, intersection) => { event.preventDefault(); this.props.onMouseDown(this.refs.mesh.userData._bodyIndex, intersection); }; render() { const { position, quaternion, } = this.props; return ( ); } } export default PickableMesh; ================================================ FILE: src/examples/Physics/mousePick.js ================================================ import React from 'react'; import React3 from 'react-three-renderer'; import * as THREE from 'three'; import CANNON from 'cannon/src/Cannon'; import MouseInput from '../inputs/MouseInput'; import ExampleBase from '../ExampleBase'; import Stats from 'stats.js'; import PickableMesh from './mousePick/PickableMesh'; const backVector = new THREE.Vector3(0, 0, -1); const dragPlane = new THREE.Plane(); class PhysicsMousePick extends ExampleBase { constructor(props, context) { super(props, context); const N = 100; this._raycaster = new THREE.Raycaster(); this.fog = new THREE.Fog(0x001525, 10, 40); const d = 20; this.lightPosition = new THREE.Vector3(d, d, d); this.lightTarget = new THREE.Vector3(0, 0, 0); this.groundQuaternion = new THREE.Quaternion() .setFromAxisAngle(new THREE.Vector3(1, 0, 0), -Math.PI / 2); this.cameraPosition = new THREE.Vector3(10, 2, 0); this.cameraQuaternion = new THREE.Quaternion() .setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI / 2); const world = new CANNON.World(); const bodies = []; const meshRefs = []; let constrainedBody; let pivot; const initCannon = () => { world.quatNormalizeSkip = 0; world.quatNormalizeFast = false; world.gravity.set(0, -10, 0); world.broadphase = new CANNON.NaiveBroadphase(); const mass = 5; const boxShape = new CANNON.Box(new CANNON.Vec3(0.25, 0.25, 0.25)); for (let i = 0; i < N; ++i) { const boxBody = new CANNON.Body({ mass, }); boxBody.addShape(boxShape); boxBody.position.set( -2.5 + Math.random() * 5, 2.5 + Math.random() * 5, -2.5 + Math.random() * 5); world.addBody(boxBody); bodies.push(boxBody); meshRefs.push((mesh) => { if (mesh) { mesh.userData._bodyIndex = i; this.meshes.push(mesh); } }); } const groundShape = new CANNON.Plane(); const groundBody = new CANNON.Body({ mass: 0 }); groundBody.addShape(groundShape); groundBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2); // WAIT A MINUTE I CAN CREATE A REACT RENDERER FOR CANNON // patience is a virtue // breathe in breathe out breathe in breathe out // let's finish this one first world.addBody(groundBody); const shape = new CANNON.Sphere(0.1); const jointBody = new CANNON.Body({ mass: 0 }); jointBody.addShape(shape); jointBody.collisionFilterGroup = 0; jointBody.collisionFilterMask = 0; world.addBody(jointBody); this.jointBody = jointBody; }; initCannon(); const timeStep = 1 / 60; const updatePhysics = () => { // Step the physics world world.step(timeStep); }; const _getMeshStates = () => bodies .map(({ position, quaternion }, bodyIndex) => ({ position: new THREE.Vector3().copy(position), quaternion: new THREE.Quaternion().copy(quaternion), ref: meshRefs[bodyIndex], })); this._onAnimate = () => { updatePhysics(); this.setState({ meshStates: _getMeshStates(), }); this.stats.update(); }; this._addMouseConstraint = ({ x, y, z }, bodyIndex) => { // The cannon body constrained by the mouse joint constrainedBody = bodies[bodyIndex]; // Vector to the clicked point, relative to the body const v1 = new CANNON.Vec3(x, y, z).vsub(constrainedBody.position); // Apply anti-quaternion to vector to transform it into the local body coordinate system const antiRot = constrainedBody.quaternion.inverse(); pivot = antiRot.vmult(v1); // pivot is not in local body coordinates // Move the cannon click marker particle to the click position this.jointBody.position.set(x, y, z); // Create a new constraint // The pivot for the jointBody is zero this.mouseConstraint = new CANNON .PointToPointConstraint( constrainedBody, pivot, this.jointBody, new CANNON.Vec3(0, 0, 0) ); // Add the t to world world.addConstraint(this.mouseConstraint); this.world = world; }; this.state = { clickMarkerVisible: false, clickMarkerPosition: new THREE.Vector3(), meshStates: _getMeshStates(), }; this.meshes = []; } _setClickMarker(x, y, z) { return { clickMarkerPosition: new THREE.Vector3(x, y, z), clickMarkerVisible: true, }; } componentDidMount() { const { mouseInput, container, } = this.refs; this.stats = new Stats(); this.stats.domElement.style.position = 'absolute'; this.stats.domElement.style.top = '0px'; container.appendChild(this.stats.domElement); if (!mouseInput.isReady()) { const { scene, camera, } = this.refs; mouseInput.ready(scene, container, camera); mouseInput.restrictIntersections(this.meshes); mouseInput.setActive(false); } } componentDidUpdate(newProps) { const { mouseInput, } = this.refs; const { width, height, } = this.props; if (width !== newProps.width || height !== newProps.height) { mouseInput.containerResized(); } } componentWillUnmount() { delete this.world; delete this.stats; } _onMeshMouseDown = (bodyIndex, intersection) => { const { camera, } = this.refs; const pos = intersection.point; this.setState({ // Set marker on contact point ...this._setClickMarker(pos.x, pos.y, pos.z), }); dragPlane.setFromNormalAndCoplanarPoint(backVector.clone() .applyQuaternion(camera.quaternion), pos); this._addMouseConstraint(pos, bodyIndex); window.addEventListener('mousemove', this._onMouseMove, false); window.addEventListener('mouseup', this._onMouseUp, false); }; _onMouseUp = () => { window.removeEventListener('mousemove', this._onMouseMove, false); window.removeEventListener('mouseup', this._onMouseUp, false); this.setState({ clickMarkerVisible: false, }); this.world.removeConstraint(this.mouseConstraint); this.mouseConstraint = false; }; _onMouseMove = (event) => { const { mouseInput, } = this.refs; const ray:THREE.Ray = mouseInput.getCameraRay(new THREE.Vector2(event.clientX, event.clientY)); const pos = dragPlane.intersectLine( new THREE.Line3(ray.origin, ray.origin .clone() .add(ray.direction .clone() .multiplyScalar(10000)))); if (pos) { this.setState({ ... this._setClickMarker(pos.x, pos.y, pos.z), }); // Move the joint body to a new position this.jointBody.position.set(pos.x, pos.y, pos.z); this.mouseConstraint.update(); } }; render() { const { width, height, } = this.props; const { clickMarkerVisible, clickMarkerPosition, meshStates, } = this.state; const d = 20; const cubeMeshes = meshStates.map(({ position, quaternion }, i) => ()); return (
{cubeMeshes}
); } } export default PhysicsMousePick; ================================================ FILE: src/examples/Simple/index.js ================================================ import React from 'react'; import React3 from 'react-three-renderer'; import * as THREE from 'three'; class Simple extends React.Component { static propTypes = { width: React.PropTypes.number.isRequired, height: React.PropTypes.number.isRequired, }; constructor(props, context) { super(props, context); this.cameraPosition = new THREE.Vector3(0, 0, 5); // construct the position vector here, because if we use 'new' within render, // React will think that things have changed when they have not. this.state = { cubeRotation: new THREE.Euler(), }; this._onAnimate = () => { // we will get this callback every frame // pretend cubeRotation is immutable. // this helps with updates and pure rendering. // React will be sure that the rotation has now updated. this.setState({ cubeRotation: new THREE.Euler( this.state.cubeRotation.x + 0.1, this.state.cubeRotation.y + 0.1, 0 ), }); }; } render() { const { width, height, } = this.props; // or you can use: // width = window.innerWidth // height = window.innerHeight return ( ); } } export default Simple; ================================================ FILE: src/examples/WebGLCameraExample/Info.js ================================================ import React from 'react'; import PropTypes from 'prop-types'; class Info extends React.Component { static propTypes = { pause: PropTypes.func.isRequired, frame: PropTypes.func.isRequired, }; render() { return (
three.js - cameras
O orthographic   P perspective
); } } export default Info; ================================================ FILE: src/examples/WebGLCameraExample/PointCloud.js ================================================ import React from 'react'; import * as THREE from 'three'; class PointCloud extends React.Component { constructor(props, context) { super(props, context); this.pointCloudVertices = []; for (let i = 0; i < 10000; i++) { const vertex = new THREE.Vector3(); vertex.x = THREE.Math.randFloatSpread(2000); vertex.y = THREE.Math.randFloatSpread(2000); vertex.z = THREE.Math.randFloatSpread(2000); this.pointCloudVertices.push(vertex); } } shouldComponentUpdate() { return false; } render() { return ( ); } } export default PointCloud; ================================================ FILE: src/examples/WebGLCameraExample/index.js ================================================ import React from 'react'; import ReactDOM from 'react-dom'; import * as THREE from 'three'; import ExampleBase from './../ExampleBase'; import React3 from 'react-three-renderer'; import Info from './Info'; import PointCloud from './PointCloud'; import TrackballControls from '../../ref/trackball'; const perspectiveCameraName = 'perspectiveCamera'; const orthographicCameraName = 'orthographicCamera'; const mainCameraName = 'mainCamera'; const perspectiveCameraRotation = new THREE.Euler(0, Math.PI, 0); const orthographicCameraRotation = new THREE.Euler(0, Math.PI, 0); const spherePosition = new THREE.Vector3(0, 0, 150); class WebGLCameraExample extends ExampleBase { constructor(props, context) { super(props, context); const r = Date.now() * 0.0005; this.state = { ... this.state, meshPosition: new THREE.Vector3(Math.cos(r), Math.sin(r), Math.sin(r)).multiplyScalar(700), childPosition: new THREE.Vector3(70 * Math.cos(2 * r), 150, 70 * Math.sin(r)), activeCameraName: perspectiveCameraName, paused: false, mainCameraPosition: new THREE.Vector3(0, 0, 2500), }; } componentDidMount() { document.addEventListener('keydown', this._onKeyDown, false); const controls = new TrackballControls(this.refs.mainCamera, ReactDOM.findDOMNode(this.refs.react3)); controls.rotateSpeed = 1.0; controls.zoomSpeed = 1.2; controls.panSpeed = 0.8; controls.noZoom = false; controls.noPan = false; controls.staticMoving = true; controls.dynamicDampingFactor = 0.3; controls.addEventListener('change', () => { this.setState({ mainCameraPosition: this.refs.mainCamera.position, }); }); this.controls = controls; } componentWillUnmount() { document.removeEventListener('keydown', this._onKeyDown, false); this.controls.dispose(); delete this.controls; } _onKeyDown = (event) => { switch (event.keyCode) { default: break; case 79: // O this.setState({ activeCameraName: orthographicCameraName, }); break; case 80: // P this.setState({ activeCameraName: perspectiveCameraName, }); break; } }; _onAnimate = () => { this.controls.update(); if (this.state.paused) { return; } const r = Date.now() * 0.0005; this.setState({ r, meshPosition: new THREE.Vector3(Math.cos(r), Math.sin(r), Math.sin(r)).multiplyScalar(700), childPosition: new THREE.Vector3(70 * Math.cos(2 * r), 150, 70 * Math.sin(r)), }); }; _pause = () => { this.setState({ paused: !this.state.paused, }); }; _frame = () => { this.setState({ paused: false, }, () => { this._onAnimate(); this.setState({ paused: true, }); }); }; render() { const { width, height, } = this.props; const { meshPosition, childPosition, r, } = this.state; const aspectRatio = 0.5 * width / height; return (
{ }
); } } export default WebGLCameraExample; ================================================ FILE: src/examples/inputs/MouseInput.js ================================================ import React3 from 'react-three-renderer'; import * as THREE from 'three'; import ReactUpdates from 'react-dom/lib/ReactUpdates'; import SyntheticMouseEvent from 'react-dom/lib/SyntheticMouseEvent'; import Module from 'react-three-renderer/lib/Module'; import PropTypes from 'react/lib/ReactPropTypes'; const tempVector2 = new THREE.Vector2(); const listenerCallbackNames = { mousedown: 'onMouseDown', mouseup: 'onMouseUp', }; const mouseEvents = [ 'onMouseEnter', 'onMouseLeave', 'onMouseDown', 'onMouseUp', 'onClick', ]; const boolProps = { ignorePointerEvents: false, }; class MouseInput extends Module { constructor() { super(); this._isReady = false; this._active = true; this._restrictIntersections = false; this._objectsToIntersect = null; this._restrictedIntersectionRecursive = false; this._patchedDescriptors = []; } // noinspection JSUnusedGlobalSymbols setup(react3RendererInstance) { super.setup(react3RendererInstance); const Object3DDescriptor = react3RendererInstance.threeElementDescriptors.object3D.constructor; Object.values(react3RendererInstance.threeElementDescriptors).forEach(elementDescriptor => { if (elementDescriptor instanceof Object3DDescriptor) { mouseEvents.forEach(eventName => { elementDescriptor.hasEvent(eventName); }); Object.keys(boolProps).forEach(propName => { elementDescriptor.hasProp(propName, { type: PropTypes.bool, update(threeObject, value, hasProp) { if (hasProp) { threeObject.userData[propName] = value; } else { threeObject.userData[propName] = boolProps[propName]; } }, default: boolProps[propName], }); }); this._patchedDescriptors.push(elementDescriptor); } }); } isReady() { return this._isReady; } setActive(active) { this._active = active; } restrictIntersections(objects, recursive = false) { this._restrictIntersections = true; this._objectsToIntersect = objects; this._restrictedIntersectionRecursive = recursive; } ready(scene, container, camera) { this._isReady = true; this._scene = scene; this._container = container; this._camera = camera; this._raycaster = new THREE.Raycaster(); this._mouse = new THREE.Vector2(); this._onMouseMove = (event) => { this._mouse.set(event.clientX, event.clientY); if (!this._active) { this._updateEnterLeave(); } }; this._containerRect = this._container.getBoundingClientRect(); this._hoverObjectMap = {}; document.addEventListener('mousemove', this._onMouseMove, false); this._intersectionsForClick = null; this._caughtListenersCleanupFunctions = []; Object.keys(listenerCallbackNames).forEach(eventName => { let boundListener; const listenerCallbackName = listenerCallbackNames[eventName]; switch (eventName) { case 'mousedown': boundListener = this._onMouseDown.bind(this, listenerCallbackName); break; case 'mouseup': boundListener = this._onMouseUp.bind(this, listenerCallbackName); break; default: break; } if (boundListener) { container.addEventListener(eventName, boundListener, true); this._caughtListenersCleanupFunctions.push(() => { container.removeEventListener(eventName, boundListener, true); }); } }); } _onMouseDown = (callbackName, mouseEvent) => { ReactUpdates.batchedUpdates(() => { const { event, intersections, } = this._intersectAndDispatch(callbackName, mouseEvent); if (event.isDefaultPrevented() || event.isPropagationStopped()) { this._intersectionsForClick = null; } else { this._intersectionsForClick = intersections; } }); }; _onMouseUp = (callbackName, mouseEvent) => { ReactUpdates.batchedUpdates(() => { const { event, intersections, } = this._intersectAndDispatch(callbackName, mouseEvent); if (!(event.isDefaultPrevented() || event.isPropagationStopped())) { if (this._intersectionsForClick === null) { return; } // intersect current intersections with the intersections for click // call xzibit ASAP we have a good one son // it wasn't that good const intersectionUUIDMap = this._intersectionsForClick.reduce((map, intersection) => { map[intersection.object.uuid] = intersection; return map; }, {}); for (let i = 0; i < intersections.length; ++i) { if (event.isDefaultPrevented() || event.isPropagationStopped()) { return; } const intersection = intersections[i]; const object = intersection.object; const uuid = object.uuid; if (intersectionUUIDMap[uuid]) { // oh boy oh boy here we go, we got a clicker React3.eventDispatcher .dispatchEvent(object, 'onClick', this._createSyntheticMouseEvent('click', event), intersection); } } } }); this._intersectionsForClick = null; }; _createSyntheticMouseEvent(eventType, prototype) { return SyntheticMouseEvent.getPooled(null, null, new MouseEvent(eventType, prototype), prototype.target); } _intersectAndDispatch(callbackName, mouseEvent) { const event = SyntheticMouseEvent.getPooled(null, null, mouseEvent, mouseEvent.target); const intersections = this._getIntersections(tempVector2.set(event.clientX, event.clientY)); ReactUpdates.batchedUpdates(() => { for (let i = 0; i < intersections.length; ++i) { const intersection = intersections[i]; if (event.isDefaultPrevented() || event.isPropagationStopped()) { return; } const object = intersection.object; React3.eventDispatcher.dispatchEvent(object, callbackName, event, intersection); } }); return { event, intersections, }; } _getIntersections(mouseCoords) { const relativeMouseCoords = this._getRelativeMouseCoords(mouseCoords); this._raycaster.setFromCamera(relativeMouseCoords, this._camera); if (this._restrictIntersections) { return this._raycaster.intersectObjects(this._objectsToIntersect, this._restrictedIntersectionRecursive); } return this._raycaster.intersectObject(this._scene, true); } // noinspection JSUnusedGlobalSymbols /** * * @param {THREE.Vector2} mouseCoords usually an event's clientX and clientY * @returns {THREE.Ray} */ getCameraRay(mouseCoords) { const relativeMouseCoords = this._getRelativeMouseCoords(mouseCoords); const originalRay = this._raycaster.ray.clone(); this._raycaster.setFromCamera(relativeMouseCoords, this._camera); const resultRay = this._raycaster.ray.clone(); this._raycaster.ray.copy(originalRay); return resultRay; } // noinspection JSUnusedGlobalSymbols intersectObject(mouseCoords, object, recursive = false) { const relativeMouseCoords = this._getRelativeMouseCoords(mouseCoords); const originalRay = this._raycaster.ray.clone(); this._raycaster.setFromCamera(relativeMouseCoords, this._camera); const intersections = this._raycaster.intersectObject(object, recursive); this._raycaster.ray.copy(originalRay); return intersections; } containerResized() { this._containerRect = this._container.getBoundingClientRect(); } update() { if (!this._isReady) { return; } if (this._active) { this._updateEnterLeave(); } } _updateEnterLeave() { const intersections = this._getIntersections(this._mouse); const hoverMapToUpdate = { ...this._hoverObjectMap, }; const mouseEnterEvent = this._createSyntheticMouseEvent('mouseEnter', { target: this._container, clientX: this._mouse.x, clientY: this._mouse.y, }); // find first intersection that does not ignore pointer events for (let depth = 0; depth < intersections.length; ++depth) { const intersection = intersections[depth]; const object = intersection.object; if (object.userData && object.userData.ignorePointerEvents) { continue; } const uuid = object.uuid; if (this._hoverObjectMap[uuid]) { delete hoverMapToUpdate[uuid]; // just update that intersection this._hoverObjectMap[uuid].intersection = intersection; } else { this._hoverObjectMap[uuid] = { object, intersection, }; if (!(mouseEnterEvent.isDefaultPrevented() || mouseEnterEvent.isPropagationStopped())) { React3.eventDispatcher.dispatchEvent(object, 'onMouseEnter', mouseEnterEvent, intersection, depth); } } // we have found the first solid intersection, don't go further break; } const mouseLeaveEvent = this._createSyntheticMouseEvent('mouseLeave', { target: this._container, clientX: this._mouse.x, clientY: this._mouse.y, }); // delete all unseen uuids in hover map const unseenUUIDs = Object.keys(hoverMapToUpdate); for (let i = 0; i < unseenUUIDs.length; ++i) { const uuid = unseenUUIDs[i]; if (!(mouseLeaveEvent.isDefaultPrevented() || mouseLeaveEvent.isPropagationStopped())) { React3.eventDispatcher.dispatchEvent(this._hoverObjectMap[uuid].object, 'onMouseLeave', mouseLeaveEvent); } delete this._hoverObjectMap[uuid]; } } _getRelativeMouseCoords(screenMouseCoords) { const containerRect = this._containerRect; const relativeMouseCoords = screenMouseCoords.clone() .sub(tempVector2.set(containerRect.left, containerRect.top)) .divide(tempVector2.set(containerRect.width, containerRect.height)); // mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1; // mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1; relativeMouseCoords.x = relativeMouseCoords.x * 2 - 1; relativeMouseCoords.y = -relativeMouseCoords.y * 2 + 1; return relativeMouseCoords; } // noinspection JSUnusedGlobalSymbols dispose() { document.removeEventListener('mousemove', this._onMouseMove, false); this._caughtListenersCleanupFunctions.forEach(cleanupFunction => cleanupFunction()); delete this._caughtListenersCleanupFunctions; delete this._onMouseMove; this._patchedDescriptors.forEach(elementDescriptor => { const allProps = Object.keys(boolProps) .concat(mouseEvents); allProps.forEach(propName => { elementDescriptor.removeProp(propName); }); }); } } export default MouseInput; ================================================ FILE: src/index.jsx ================================================ /* eslint-disable no-undef */ import React from 'react'; import ReactDOM from 'react-dom'; import { HashRouter as Router, Route, } from 'react-router-dom'; import Perf from 'react-addons-perf'; import createHashHistory from 'history/createHashHistory'; import ExampleBrowser from './examples/ExampleBrowser'; const customHistory = createHashHistory(); window.Perf = Perf; ReactDOM.render(
, document.getElementById('content'), ); ================================================ FILE: src/ref/trackball.js ================================================ /* eslint-disable */ import * as THREE from 'three'; /** * @author Eberhard Graether / http://egraether.com/ * @author Mark Lundin / http://mark-lundin.com * @author Simone Manini / http://daron1337.github.io * @author Luca Antiga / http://lantiga.github.io */ class TrackballControls extends THREE.EventDispatcher { constructor(object, domElement) { super(); const _this = this; const STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 }; this.object = object; this.domElement = ( domElement !== undefined ) ? domElement : document; // API this.enabled = true; this.screen = { left: 0, top: 0, width: 0, height: 0 }; this.rotateSpeed = 1.0; this.zoomSpeed = 1.2; this.panSpeed = 0.3; this.noRotate = false; this.noZoom = false; this.noPan = false; this.staticMoving = false; this.dynamicDampingFactor = 0.2; this.minDistance = 0; this.maxDistance = Infinity; this.keys = [ 65/* A */, 83/* S */, 68/* D */, ]; // internals this.target = new THREE.Vector3(); const EPS = 0.000001; const lastPosition = new THREE.Vector3(); let _state = STATE.NONE; let _prevState = STATE.NONE; const _eye = new THREE.Vector3(); const _movePrev = new THREE.Vector2(); const _moveCurr = new THREE.Vector2(); const _lastAxis = new THREE.Vector3(); let _lastAngle = 0; const _zoomStart = new THREE.Vector2(); const _zoomEnd = new THREE.Vector2(); let _touchZoomDistanceStart = 0; let _touchZoomDistanceEnd = 0; const _panStart = new THREE.Vector2(); const _panEnd = new THREE.Vector2(); // for reset this.target0 = this.target.clone(); this.position0 = this.object.position.clone(); this.up0 = this.object.up.clone(); // events const changeEvent = { type: 'change' }; const startEvent = { type: 'start' }; const endEvent = { type: 'end' }; // methods this.handleResize = () => { if (this.domElement === document) { this.screen.left = 0; this.screen.top = 0; this.screen.width = window.innerWidth; this.screen.height = window.innerHeight; } else { const box = this.domElement.getBoundingClientRect(); // adjustments come from similar code in the jquery offset() function const d = this.domElement.ownerDocument.documentElement; this.screen.left = box.left + window.pageXOffset - d.clientLeft; this.screen.top = box.top + window.pageYOffset - d.clientTop; this.screen.width = box.width; this.screen.height = box.height; } }; this.handleEvent = (event) => { if (typeof this[event.type] === 'function') { this[event.type](event); } }; const getMouseOnScreen = ( function wrapper() { const vector = new THREE.Vector2(); return (pageX, pageY) => { vector.set( ( pageX - _this.screen.left ) / _this.screen.width, ( pageY - _this.screen.top ) / _this.screen.height ); return vector; }; }() ); const getMouseOnCircle = ( function wrapper() { const vector = new THREE.Vector2(); return (pageX, pageY) => { vector.set( ( ( pageX - _this.screen.width * 0.5 - _this.screen.left ) / ( _this.screen.width * 0.5 ) ), ( ( _this.screen.height + 2 * ( _this.screen.top - pageY ) ) / _this.screen.width ) // screen.width intentional ); return vector; }; }() ); this.rotateCamera = ( function wrapper() { const axis = new THREE.Vector3(); const quaternion = new THREE.Quaternion(); const eyeDirection = new THREE.Vector3(); const objectUpDirection = new THREE.Vector3(); const objectSidewaysDirection = new THREE.Vector3(); const moveDirection = new THREE.Vector3(); let angle; return function rotateCamera() { moveDirection.set(_moveCurr.x - _movePrev.x, _moveCurr.y - _movePrev.y, 0); angle = moveDirection.length(); if (angle) { _eye.copy(_this.object.position).sub(_this.target); eyeDirection.copy(_eye).normalize(); objectUpDirection.copy(_this.object.up).normalize(); objectSidewaysDirection.crossVectors(objectUpDirection, eyeDirection).normalize(); objectUpDirection.setLength(_moveCurr.y - _movePrev.y); objectSidewaysDirection.setLength(_moveCurr.x - _movePrev.x); moveDirection.copy(objectUpDirection.add(objectSidewaysDirection)); axis.crossVectors(moveDirection, _eye).normalize(); angle *= _this.rotateSpeed; quaternion.setFromAxisAngle(axis, angle); _eye.applyQuaternion(quaternion); _this.object.up.applyQuaternion(quaternion); _lastAxis.copy(axis); _lastAngle = angle; } else if (!_this.staticMoving && _lastAngle) { _lastAngle *= Math.sqrt(1.0 - _this.dynamicDampingFactor); _eye.copy(_this.object.position).sub(_this.target); quaternion.setFromAxisAngle(_lastAxis, _lastAngle); _eye.applyQuaternion(quaternion); _this.object.up.applyQuaternion(quaternion); } _movePrev.copy(_moveCurr); }; }() ); this.zoomCamera = () => { let factor; if (_state === STATE.TOUCH_ZOOM_PAN) { factor = _touchZoomDistanceStart / _touchZoomDistanceEnd; _touchZoomDistanceStart = _touchZoomDistanceEnd; _eye.multiplyScalar(factor); } else { factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed; if (factor !== 1.0 && factor > 0.0) { _eye.multiplyScalar(factor); if (_this.staticMoving) { _zoomStart.copy(_zoomEnd); } else { _zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor; } } } }; this.panCamera = ( function wrapper() { const mouseChange = new THREE.Vector2(); const objectUp = new THREE.Vector3(); const pan = new THREE.Vector3(); return function panCamera() { mouseChange.copy(_panEnd).sub(_panStart); if (mouseChange.lengthSq()) { mouseChange.multiplyScalar(_eye.length() * _this.panSpeed); pan.copy(_eye).cross(_this.object.up).setLength(mouseChange.x); pan.add(objectUp.copy(_this.object.up).setLength(mouseChange.y)); _this.object.position.add(pan); _this.target.add(pan); if (_this.staticMoving) { _panStart.copy(_panEnd); } else { _panStart.add(mouseChange.subVectors(_panEnd, _panStart).multiplyScalar(_this.dynamicDampingFactor)); } } }; }() ); this.checkDistances = () => { if (!_this.noZoom || !_this.noPan) { if (_eye.lengthSq() > _this.maxDistance * _this.maxDistance) { _this.object.position.addVectors(_this.target, _eye.setLength(_this.maxDistance)); _zoomStart.copy(_zoomEnd); } if (_eye.lengthSq() < _this.minDistance * _this.minDistance) { _this.object.position.addVectors(_this.target, _eye.setLength(_this.minDistance)); _zoomStart.copy(_zoomEnd); } } }; this.update = () => { _eye.subVectors(_this.object.position, _this.target); if (!_this.noRotate) { _this.rotateCamera(); } if (!_this.noZoom) { _this.zoomCamera(); } if (!_this.noPan) { _this.panCamera(); } _this.object.position.addVectors(_this.target, _eye); _this.checkDistances(); _this.object.lookAt(_this.target); if (lastPosition.distanceToSquared(_this.object.position) > EPS) { _this.dispatchEvent(changeEvent); lastPosition.copy(_this.object.position); } }; this.reset = () => { _state = STATE.NONE; _prevState = STATE.NONE; _this.target.copy(_this.target0); _this.object.position.copy(_this.position0); _this.object.up.copy(_this.up0); _eye.subVectors(_this.object.position, _this.target); _this.object.lookAt(_this.target); _this.dispatchEvent(changeEvent); lastPosition.copy(_this.object.position); }; // listeners function keydown(event) { if (_this.enabled === false) return; window.removeEventListener('keydown', keydown); _prevState = _state; if (_state !== STATE.NONE) { return; } if (event.keyCode === _this.keys[STATE.ROTATE] && !_this.noRotate) { _state = STATE.ROTATE; } else if (event.keyCode === _this.keys[STATE.ZOOM] && !_this.noZoom) { _state = STATE.ZOOM; } else if (event.keyCode === _this.keys[STATE.PAN] && !_this.noPan) { _state = STATE.PAN; } } function keyup() { if (_this.enabled === false) return; _state = _prevState; window.addEventListener('keydown', keydown, false); } function mousemove(event) { if (_this.enabled === false) return; event.preventDefault(); event.stopPropagation(); if (_state === STATE.ROTATE && !_this.noRotate) { _movePrev.copy(_moveCurr); _moveCurr.copy(getMouseOnCircle(event.pageX, event.pageY)); } else if (_state === STATE.ZOOM && !_this.noZoom) { _zoomEnd.copy(getMouseOnScreen(event.pageX, event.pageY)); } else if (_state === STATE.PAN && !_this.noPan) { _panEnd.copy(getMouseOnScreen(event.pageX, event.pageY)); } } function mouseup(event) { if (_this.enabled === false) return; event.preventDefault(); event.stopPropagation(); _state = STATE.NONE; document.removeEventListener('mousemove', mousemove); document.removeEventListener('mouseup', mouseup); _this.dispatchEvent(endEvent); } function mousedown(event) { if (_this.enabled === false) return; event.preventDefault(); event.stopPropagation(); if (_state === STATE.NONE) { _state = event.button; } if (_state === STATE.ROTATE && !_this.noRotate) { _moveCurr.copy(getMouseOnCircle(event.pageX, event.pageY)); _movePrev.copy(_moveCurr); } else if (_state === STATE.ZOOM && !_this.noZoom) { _zoomStart.copy(getMouseOnScreen(event.pageX, event.pageY)); _zoomEnd.copy(_zoomStart); } else if (_state === STATE.PAN && !_this.noPan) { _panStart.copy(getMouseOnScreen(event.pageX, event.pageY)); _panEnd.copy(_panStart); } document.addEventListener('mousemove', mousemove, false); document.addEventListener('mouseup', mouseup, false); _this.dispatchEvent(startEvent); } function mousewheel(event) { if (_this.enabled === false) return; event.preventDefault(); event.stopPropagation(); let delta = 0; if (event.wheelDelta) { // WebKit / Opera / Explorer 9 delta = event.wheelDelta / 40; } else if (event.detail) { // Firefox delta = -event.detail / 3; } _zoomStart.y += delta * 0.01; _this.dispatchEvent(startEvent); _this.dispatchEvent(endEvent); } function touchstart(event) { if (_this.enabled === false) return; switch (event.touches.length) { case 1: _state = STATE.TOUCH_ROTATE; _moveCurr.copy(getMouseOnCircle(event.touches[0].pageX, event.touches[0].pageY)); _movePrev.copy(_moveCurr); break; case 2: _state = STATE.TOUCH_ZOOM_PAN; const dx = event.touches[0].pageX - event.touches[1].pageX; const dy = event.touches[0].pageY - event.touches[1].pageY; _touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt(dx * dx + dy * dy); const x = ( event.touches[0].pageX + event.touches[1].pageX ) / 2; const y = ( event.touches[0].pageY + event.touches[1].pageY ) / 2; _panStart.copy(getMouseOnScreen(x, y)); _panEnd.copy(_panStart); break; default: _state = STATE.NONE; } _this.dispatchEvent(startEvent); } function touchmove(event) { if (_this.enabled === false) return; event.preventDefault(); event.stopPropagation(); switch (event.touches.length) { case 1: _movePrev.copy(_moveCurr); _moveCurr.copy(getMouseOnCircle(event.touches[0].pageX, event.touches[0].pageY)); break; case 2: const dx = event.touches[0].pageX - event.touches[1].pageX; const dy = event.touches[0].pageY - event.touches[1].pageY; _touchZoomDistanceEnd = Math.sqrt(dx * dx + dy * dy); const x = ( event.touches[0].pageX + event.touches[1].pageX ) / 2; const y = ( event.touches[0].pageY + event.touches[1].pageY ) / 2; _panEnd.copy(getMouseOnScreen(x, y)); break; default: _state = STATE.NONE; } } function touchend(event) { if (_this.enabled === false) return; switch (event.touches.length) { default: // no touches break; case 1: _moveCurr.copy(getMouseOnCircle(event.touches[0].pageX, event.touches[0].pageY)); _movePrev.copy(_moveCurr); break; case 2: _touchZoomDistanceStart = _touchZoomDistanceEnd = 0; const x = ( event.touches[0].pageX + event.touches[1].pageX ) / 2; const y = ( event.touches[0].pageY + event.touches[1].pageY ) / 2; _panEnd.copy(getMouseOnScreen(x, y)); _panStart.copy(_panEnd); break; } _state = STATE.NONE; _this.dispatchEvent(endEvent); } function contextmenu(event) { event.preventDefault(); } this.dispose = () => { this.domElement.removeEventListener('contextmenu', contextmenu, false); this.domElement.removeEventListener('mousedown', mousedown, false); this.domElement.removeEventListener('mousewheel', mousewheel, false); this.domElement.removeEventListener('DOMMouseScroll', mousewheel, false); // firefox this.domElement.removeEventListener('touchstart', touchstart, false); this.domElement.removeEventListener('touchend', touchend, false); this.domElement.removeEventListener('touchmove', touchmove, false); document.removeEventListener('mousemove', mousemove, false); document.removeEventListener('mouseup', mouseup, false); window.removeEventListener('keydown', keydown, false); window.removeEventListener('keyup', keyup, false); }; this.domElement.addEventListener('contextmenu', contextmenu, false); this.domElement.addEventListener('mousedown', mousedown, false); this.domElement.addEventListener('mousewheel', mousewheel, false); this.domElement.addEventListener('DOMMouseScroll', mousewheel, false); // firefox this.domElement.addEventListener('touchstart', touchstart, false); this.domElement.addEventListener('touchend', touchend, false); this.domElement.addEventListener('touchmove', touchmove, false); window.addEventListener('keydown', keydown, false); window.addEventListener('keyup', keyup, false); this.handleResize(); // force an update at start this.update(); } } export default TrackballControls; /* eslint-enable */ ================================================ FILE: webpack.config.babel.js ================================================ /* eslint-disable import/no-extraneous-dependencies */ import path from 'path'; import webpack from 'webpack'; import pluginsWithoutUglify from './config/webpackPluginsWithoutUglify'; import packageJson from './package.json'; const outPath = path.join(__dirname, 'pages'); const plugins = pluginsWithoutUglify.concat([ new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false, }, mangle: true, }), ]); const babelLoaderConfigShared = { test: /\.jsx?$/, loader: 'babel-loader', query: { ...packageJson.babel, cacheDirectory: true, }, }; export default { entry: { app: [ './src/index.jsx', ], advanced: [ './src/examples/AdvancedExample/index.js', ], }, output: { path: outPath, filename: path.join('js', 'bundle-[name].js'), }, module: { loaders: [ { loader: 'json-loader', test: /\.json$/, }, { exclude: /node_modules/, ...babelLoaderConfigShared, }, { include: /react-three-renderer[\\/]src/, ...babelLoaderConfigShared, }, ], }, resolve: { extensions: ['.js', '.jsx'], alias: { // use the source files 'react-three-renderer': path.join( __dirname, 'node_modules', 'react-three-renderer', 'src'), }, }, devServer: { contentBase: path.join(__dirname, 'assets'), // noInfo: true, // --no-info option hot: true, inline: true, stats: { colors: true }, }, plugins, };