├── .gitignore ├── README.md ├── package.json ├── public ├── favicon.ico ├── index.html └── manifest.json └── src ├── App.css ├── App.js ├── App.test.js ├── ThreeMap.css ├── ThreeMap.js ├── assets └── imgs │ └── planets │ ├── Earth.png │ ├── EarthNormal.png │ ├── EarthSpec.png │ ├── Mars-normalmap_2k.png │ ├── Mars_2k-050104.png │ ├── earth-specular.gif │ ├── earth.jpg │ ├── land_ocean.jpg │ ├── mars_1k_color.jpg │ └── mars_1k_normal.jpg ├── common └── threejslibs │ ├── Projector.js │ └── stats.min.js ├── index.css ├── index.js ├── logo.svg └── registerServiceWorker.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 详细博客地址: 2 | > [使用React+Three.js 封装一个三维地球](https://zrysmt.github.io/2017/09/23/%E4%BD%BF%E7%94%A8React+Three.js%E5%B0%81%E8%A3%85%E4%B8%80%E4%B8%AA%E4%B8%89%E7%BB%B4%E5%9C%B0%E7%90%83/) 3 | 4 | ## 1.环境 5 | 使用facebook给出的脚手架工具[create-react-app](https://github.com/facebookincubator/create-react-app). 6 | 7 | ```bash 8 | npm install -g create-react-app 9 | 10 | create-react-app react-threejs-app 11 | cd react-threejs-app/ 12 | ``` 13 | 执行 14 | ```bash 15 | npm start 16 | ``` 17 | 浏览器会自动打开`localhost:3000`。 18 | 19 | ## 2.一步一个脚印 20 | ### 2.1 准备一张高清的世界地图 21 | 这里在github仓库中已经给出。 22 | ### 2.2 定义一个组件`ThreeMap` 23 | 在`ThreeMap.js`定义组件`ThreeMap`,并且创建改组件的样式`ThreeMap.css`。css定义三维地球的容器的宽度和高度。 24 | ```css 25 | #WebGL-output{ 26 | width: 100%; 27 | height: 700px; 28 | } 29 | ``` 30 | 并且该组件在`App.js`引用。 31 | ### 2.2 引入库和样式 32 | ```js 33 | import './ThreeMap.css'; 34 | import React, { Component } from 'react'; 35 | import * as THREE from 'three'; 36 | import Orbitcontrols from 'three-orbitcontrols'; 37 | import Stats from './common/threejslibs/stats.min.js'; 38 | ``` 39 | ### 2.3 初始化方法入口和要渲染的虚拟DOM 40 | ```js 41 | componentDidMount(){ 42 | this.initThree(); 43 | } 44 | ``` 45 | 要渲染的虚拟DOM设定好 46 | ```js 47 | render(){ 48 | return( 49 |
50 | ) 51 | } 52 | ``` 53 | ### 2.4 initThree方法 54 | - 创建场景 55 | 56 | ```js 57 | let scene; 58 | scene = new THREE.Scene(); 59 | ``` 60 | - 创建Group 61 | 62 | ```js 63 | let group; 64 | group = new THREE.Group(); 65 | scene.add( group ); 66 | ``` 67 | - 创建相机 68 | 69 | ```js 70 | camera = new THREE.PerspectiveCamera( 60, width / height, 1, 2000 ); 71 | camera.position.x = -10; 72 | camera.position.y = 15; 73 | camera.position.z = 500; 74 | camera.lookAt( scene.position ); 75 | ``` 76 | - 相机作为`Orbitcontrols`的参数,支持鼠标交互 77 | 78 | ```js 79 | let orbitControls = new Orbitcontrols(camera); 80 | orbitControls.autoRotate = false; 81 | ``` 82 | - 添加光源:环境光和点光源 83 | 84 | ```js 85 | let ambi = new THREE.AmbientLight(0x686868); //环境光 86 | scene.add(ambi); 87 | let spotLight = new THREE.DirectionalLight(0xffffff); //点光源 88 | spotLight.position.set(550, 100, 550); 89 | spotLight.intensity = 0.6; 90 | scene.add(spotLight); 91 | ``` 92 | - 创建模型和材质 93 | 94 | ```js 95 | let loader = new THREE.TextureLoader(); 96 | let planetTexture = require("./assets/imgs/planets/Earth.png"); 97 | 98 | loader.load( planetTexture, function ( texture ) { 99 | let geometry = new THREE.SphereGeometry( 200, 20, 20 ); 100 | let material = new THREE.MeshBasicMaterial( { map: texture, overdraw: 0.5 } ); 101 | let mesh = new THREE.Mesh( geometry, material ); 102 | group.add( mesh ); 103 | } ); 104 | ``` 105 | - 渲染 106 | 107 | ```js 108 | let renderer; 109 | renderer = new THREE.WebGLRenderer(); 110 | renderer.setClearColor( 0xffffff ); 111 | renderer.setPixelRatio( window.devicePixelRatio ); 112 | renderer.setSize( width, height ); 113 | container.appendChild( renderer.domElement ); 114 | ``` 115 | - 增加监控的信息状态 116 | 117 | ```js 118 | stats = new Stats(); 119 | container.appendChild( stats.dom ); 120 | ``` 121 | **将以上封装到`init`函数中** 122 | - 动态渲染,地球自转 123 | 124 | ```js 125 | function animate() { 126 | requestAnimationFrame( animate ); 127 | render(); 128 | stats.update(); 129 | } 130 | function render() { 131 | group.rotation.y -= 0.005; //这行可以控制地球自转 132 | renderer.render( scene, camera ); 133 | } 134 | ``` 135 | 调用的顺序是: 136 | ```js 137 | init(); 138 | animate(); 139 | 140 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-threejs-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^15.6.1", 7 | "react-dom": "^15.6.1", 8 | "react-scripts": "1.0.13", 9 | "three": "^0.87.1", 10 | "three-orbitcontrols": "^1.2.1" 11 | }, 12 | "scripts": { 13 | "start": "react-scripts start", 14 | "build": "react-scripts build", 15 | "test": "react-scripts test --env=jsdom", 16 | "eject": "react-scripts eject" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zrysmt/react-threejs-app/6bcafa341a0b3ffbfc223b2f124397f558a236f0/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | React App 23 | 24 | 25 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 30px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 30px; 13 | padding: 20px; 14 | color: white; 15 | } 16 | 17 | .App-intro { 18 | font-size: large; 19 | } 20 | 21 | @keyframes App-logo-spin { 22 | from { transform: rotate(0deg); } 23 | to { transform: rotate(360deg); } 24 | } 25 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import logo from './logo.svg'; 3 | import './App.css'; 4 | import ThreeMap from './ThreeMap'; 5 | 6 | class App extends Component { 7 | render() { 8 | return ( 9 |
10 |
11 | logo 12 |
13 |
14 | 15 |
16 |
17 | ); 18 | } 19 | } 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | }); 9 | -------------------------------------------------------------------------------- /src/ThreeMap.css: -------------------------------------------------------------------------------- 1 | #WebGL-output{ 2 | width: 100%; 3 | height: 700px; 4 | } -------------------------------------------------------------------------------- /src/ThreeMap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * React+Three.js 三维地球 3 | */ 4 | import './ThreeMap.css'; 5 | import React, { Component } from 'react'; 6 | import * as THREE from 'three'; 7 | import Orbitcontrols from 'three-orbitcontrols'; 8 | import Stats from './common/threejslibs/stats.min.js'; 9 | 10 | class ThreeMap extends Component{ 11 | componentDidMount(){ 12 | this.initThree(); 13 | } 14 | initThree(){ 15 | let stats; 16 | let camera, scene, renderer; 17 | let group; 18 | let container = document.getElementById('WebGL-output'); 19 | let width = container.clientWidth,height = container.clientHeight; 20 | 21 | init(); 22 | animate(); 23 | 24 | function init() { 25 | scene = new THREE.Scene(); 26 | group = new THREE.Group(); 27 | scene.add( group ); 28 | 29 | camera = new THREE.PerspectiveCamera( 60, width / height, 1, 2000 ); 30 | camera.position.x = -10; 31 | camera.position.y = 15; 32 | camera.position.z = 500; 33 | camera.lookAt( scene.position ); 34 | 35 | //控制地球 36 | let orbitControls = new /*THREE.OrbitControls*/Orbitcontrols(camera); 37 | orbitControls.autoRotate = false; 38 | // let clock = new THREE.Clock(); 39 | //光源 40 | let ambi = new THREE.AmbientLight(0x686868); 41 | scene.add(ambi); 42 | 43 | let spotLight = new THREE.DirectionalLight(0xffffff); 44 | spotLight.position.set(550, 100, 550); 45 | spotLight.intensity = 0.6; 46 | 47 | scene.add(spotLight); 48 | // Texture 49 | let loader = new THREE.TextureLoader(); 50 | let planetTexture = require("./assets/imgs/planets/Earth.png"); 51 | 52 | loader.load( planetTexture, function ( texture ) { 53 | let geometry = new THREE.SphereGeometry( 200, 20, 20 ); 54 | let material = new THREE.MeshBasicMaterial( { map: texture, overdraw: 0.5 } ); 55 | let mesh = new THREE.Mesh( geometry, material ); 56 | group.add( mesh ); 57 | } ); 58 | 59 | renderer = new THREE.WebGLRenderer(); 60 | renderer.setClearColor( 0xffffff ); 61 | renderer.setPixelRatio( window.devicePixelRatio ); 62 | renderer.setSize( width, height ); 63 | container.appendChild( renderer.domElement ); 64 | stats = new Stats(); 65 | container.appendChild( stats.dom ); //增加状态信息 66 | 67 | } 68 | 69 | function animate() { 70 | requestAnimationFrame( animate ); 71 | render(); 72 | stats.update(); 73 | } 74 | function render() { 75 | group.rotation.y -= 0.005; //这行可以控制地球自转 76 | renderer.render( scene, camera ); 77 | } 78 | } 79 | render(){ 80 | return( 81 |
82 | ) 83 | } 84 | } 85 | 86 | export default ThreeMap; -------------------------------------------------------------------------------- /src/assets/imgs/planets/Earth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zrysmt/react-threejs-app/6bcafa341a0b3ffbfc223b2f124397f558a236f0/src/assets/imgs/planets/Earth.png -------------------------------------------------------------------------------- /src/assets/imgs/planets/EarthNormal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zrysmt/react-threejs-app/6bcafa341a0b3ffbfc223b2f124397f558a236f0/src/assets/imgs/planets/EarthNormal.png -------------------------------------------------------------------------------- /src/assets/imgs/planets/EarthSpec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zrysmt/react-threejs-app/6bcafa341a0b3ffbfc223b2f124397f558a236f0/src/assets/imgs/planets/EarthSpec.png -------------------------------------------------------------------------------- /src/assets/imgs/planets/Mars-normalmap_2k.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zrysmt/react-threejs-app/6bcafa341a0b3ffbfc223b2f124397f558a236f0/src/assets/imgs/planets/Mars-normalmap_2k.png -------------------------------------------------------------------------------- /src/assets/imgs/planets/Mars_2k-050104.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zrysmt/react-threejs-app/6bcafa341a0b3ffbfc223b2f124397f558a236f0/src/assets/imgs/planets/Mars_2k-050104.png -------------------------------------------------------------------------------- /src/assets/imgs/planets/earth-specular.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zrysmt/react-threejs-app/6bcafa341a0b3ffbfc223b2f124397f558a236f0/src/assets/imgs/planets/earth-specular.gif -------------------------------------------------------------------------------- /src/assets/imgs/planets/earth.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zrysmt/react-threejs-app/6bcafa341a0b3ffbfc223b2f124397f558a236f0/src/assets/imgs/planets/earth.jpg -------------------------------------------------------------------------------- /src/assets/imgs/planets/land_ocean.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zrysmt/react-threejs-app/6bcafa341a0b3ffbfc223b2f124397f558a236f0/src/assets/imgs/planets/land_ocean.jpg -------------------------------------------------------------------------------- /src/assets/imgs/planets/mars_1k_color.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zrysmt/react-threejs-app/6bcafa341a0b3ffbfc223b2f124397f558a236f0/src/assets/imgs/planets/mars_1k_color.jpg -------------------------------------------------------------------------------- /src/assets/imgs/planets/mars_1k_normal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zrysmt/react-threejs-app/6bcafa341a0b3ffbfc223b2f124397f558a236f0/src/assets/imgs/planets/mars_1k_normal.jpg -------------------------------------------------------------------------------- /src/common/threejslibs/Projector.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | * @author supereggbert / http://www.paulbrunt.co.uk/ 4 | * @author julianwa / https://github.com/julianwa 5 | */ 6 | import * as THREE from 'three'; 7 | 8 | THREE.RenderableObject = function () { 9 | 10 | this.id = 0; 11 | 12 | this.object = null; 13 | this.z = 0; 14 | this.renderOrder = 0; 15 | 16 | }; 17 | 18 | // 19 | 20 | THREE.RenderableFace = function () { 21 | 22 | this.id = 0; 23 | 24 | this.v1 = new THREE.RenderableVertex(); 25 | this.v2 = new THREE.RenderableVertex(); 26 | this.v3 = new THREE.RenderableVertex(); 27 | 28 | this.normalModel = new THREE.Vector3(); 29 | 30 | this.vertexNormalsModel = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ]; 31 | this.vertexNormalsLength = 0; 32 | 33 | this.color = new THREE.Color(); 34 | this.material = null; 35 | this.uvs = [ new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() ]; 36 | 37 | this.z = 0; 38 | this.renderOrder = 0; 39 | 40 | }; 41 | 42 | // 43 | 44 | THREE.RenderableVertex = function () { 45 | 46 | this.position = new THREE.Vector3(); 47 | this.positionWorld = new THREE.Vector3(); 48 | this.positionScreen = new THREE.Vector4(); 49 | 50 | this.visible = true; 51 | 52 | }; 53 | 54 | THREE.RenderableVertex.prototype.copy = function ( vertex ) { 55 | 56 | this.positionWorld.copy( vertex.positionWorld ); 57 | this.positionScreen.copy( vertex.positionScreen ); 58 | 59 | }; 60 | 61 | // 62 | 63 | THREE.RenderableLine = function () { 64 | 65 | this.id = 0; 66 | 67 | this.v1 = new THREE.RenderableVertex(); 68 | this.v2 = new THREE.RenderableVertex(); 69 | 70 | this.vertexColors = [ new THREE.Color(), new THREE.Color() ]; 71 | this.material = null; 72 | 73 | this.z = 0; 74 | this.renderOrder = 0; 75 | 76 | }; 77 | 78 | // 79 | 80 | THREE.RenderableSprite = function () { 81 | 82 | this.id = 0; 83 | 84 | this.object = null; 85 | 86 | this.x = 0; 87 | this.y = 0; 88 | this.z = 0; 89 | 90 | this.rotation = 0; 91 | this.scale = new THREE.Vector2(); 92 | 93 | this.material = null; 94 | this.renderOrder = 0; 95 | 96 | }; 97 | 98 | // 99 | 100 | THREE.Projector = function () { 101 | 102 | var _object, _objectCount, _objectPool = [], _objectPoolLength = 0, 103 | _vertex, _vertexCount, _vertexPool = [], _vertexPoolLength = 0, 104 | _face, _faceCount, _facePool = [], _facePoolLength = 0, 105 | _line, _lineCount, _linePool = [], _linePoolLength = 0, 106 | _sprite, _spriteCount, _spritePool = [], _spritePoolLength = 0, 107 | 108 | _renderData = { objects: [], lights: [], elements: [] }, 109 | 110 | _vector3 = new THREE.Vector3(), 111 | _vector4 = new THREE.Vector4(), 112 | 113 | _clipBox = new THREE.Box3( new THREE.Vector3( - 1, - 1, - 1 ), new THREE.Vector3( 1, 1, 1 ) ), 114 | _boundingBox = new THREE.Box3(), 115 | _points3 = new Array( 3 ), 116 | 117 | _viewMatrix = new THREE.Matrix4(), 118 | _viewProjectionMatrix = new THREE.Matrix4(), 119 | 120 | _modelMatrix, 121 | _modelViewProjectionMatrix = new THREE.Matrix4(), 122 | 123 | _normalMatrix = new THREE.Matrix3(), 124 | 125 | _frustum = new THREE.Frustum(), 126 | 127 | _clippedVertex1PositionScreen = new THREE.Vector4(), 128 | _clippedVertex2PositionScreen = new THREE.Vector4(); 129 | 130 | // 131 | 132 | this.projectVector = function ( vector, camera ) { 133 | 134 | console.warn( 'THREE.Projector: .projectVector() is now vector.project().' ); 135 | vector.project( camera ); 136 | 137 | }; 138 | 139 | this.unprojectVector = function ( vector, camera ) { 140 | 141 | console.warn( 'THREE.Projector: .unprojectVector() is now vector.unproject().' ); 142 | vector.unproject( camera ); 143 | 144 | }; 145 | 146 | this.pickingRay = function () { 147 | 148 | console.error( 'THREE.Projector: .pickingRay() is now raycaster.setFromCamera().' ); 149 | 150 | }; 151 | 152 | // 153 | 154 | var RenderList = function () { 155 | 156 | var normals = []; 157 | var colors = []; 158 | var uvs = []; 159 | 160 | var object = null; 161 | var material = null; 162 | 163 | var normalMatrix = new THREE.Matrix3(); 164 | 165 | function setObject( value ) { 166 | 167 | object = value; 168 | material = object.material; 169 | 170 | normalMatrix.getNormalMatrix( object.matrixWorld ); 171 | 172 | normals.length = 0; 173 | colors.length = 0; 174 | uvs.length = 0; 175 | 176 | } 177 | 178 | function projectVertex( vertex ) { 179 | 180 | var position = vertex.position; 181 | var positionWorld = vertex.positionWorld; 182 | var positionScreen = vertex.positionScreen; 183 | 184 | positionWorld.copy( position ).applyMatrix4( _modelMatrix ); 185 | positionScreen.copy( positionWorld ).applyMatrix4( _viewProjectionMatrix ); 186 | 187 | var invW = 1 / positionScreen.w; 188 | 189 | positionScreen.x *= invW; 190 | positionScreen.y *= invW; 191 | positionScreen.z *= invW; 192 | 193 | vertex.visible = positionScreen.x >= - 1 && positionScreen.x <= 1 && 194 | positionScreen.y >= - 1 && positionScreen.y <= 1 && 195 | positionScreen.z >= - 1 && positionScreen.z <= 1; 196 | 197 | } 198 | 199 | function pushVertex( x, y, z ) { 200 | 201 | _vertex = getNextVertexInPool(); 202 | _vertex.position.set( x, y, z ); 203 | 204 | projectVertex( _vertex ); 205 | 206 | } 207 | 208 | function pushNormal( x, y, z ) { 209 | 210 | normals.push( x, y, z ); 211 | 212 | } 213 | 214 | function pushColor( r, g, b ) { 215 | 216 | colors.push( r, g, b ); 217 | 218 | } 219 | 220 | function pushUv( x, y ) { 221 | 222 | uvs.push( x, y ); 223 | 224 | } 225 | 226 | function checkTriangleVisibility( v1, v2, v3 ) { 227 | 228 | if ( v1.visible === true || v2.visible === true || v3.visible === true ) return true; 229 | 230 | _points3[ 0 ] = v1.positionScreen; 231 | _points3[ 1 ] = v2.positionScreen; 232 | _points3[ 2 ] = v3.positionScreen; 233 | 234 | return _clipBox.intersectsBox( _boundingBox.setFromPoints( _points3 ) ); 235 | 236 | } 237 | 238 | function checkBackfaceCulling( v1, v2, v3 ) { 239 | 240 | return ( ( v3.positionScreen.x - v1.positionScreen.x ) * 241 | ( v2.positionScreen.y - v1.positionScreen.y ) - 242 | ( v3.positionScreen.y - v1.positionScreen.y ) * 243 | ( v2.positionScreen.x - v1.positionScreen.x ) ) < 0; 244 | 245 | } 246 | 247 | function pushLine( a, b ) { 248 | 249 | var v1 = _vertexPool[ a ]; 250 | var v2 = _vertexPool[ b ]; 251 | 252 | // Clip 253 | 254 | v1.positionScreen.copy( v1.position ).applyMatrix4( _modelViewProjectionMatrix ); 255 | v2.positionScreen.copy( v2.position ).applyMatrix4( _modelViewProjectionMatrix ); 256 | 257 | if ( clipLine( v1.positionScreen, v2.positionScreen ) === true ) { 258 | 259 | // Perform the perspective divide 260 | v1.positionScreen.multiplyScalar( 1 / v1.positionScreen.w ); 261 | v2.positionScreen.multiplyScalar( 1 / v2.positionScreen.w ); 262 | 263 | _line = getNextLineInPool(); 264 | _line.id = object.id; 265 | _line.v1.copy( v1 ); 266 | _line.v2.copy( v2 ); 267 | _line.z = Math.max( v1.positionScreen.z, v2.positionScreen.z ); 268 | _line.renderOrder = object.renderOrder; 269 | 270 | _line.material = object.material; 271 | 272 | if ( object.material.vertexColors === THREE.VertexColors ) { 273 | 274 | _line.vertexColors[ 0 ].fromArray( colors, a * 3 ); 275 | _line.vertexColors[ 1 ].fromArray( colors, b * 3 ); 276 | 277 | } 278 | 279 | _renderData.elements.push( _line ); 280 | 281 | } 282 | 283 | } 284 | 285 | function pushTriangle( a, b, c ) { 286 | 287 | var v1 = _vertexPool[ a ]; 288 | var v2 = _vertexPool[ b ]; 289 | var v3 = _vertexPool[ c ]; 290 | 291 | if ( checkTriangleVisibility( v1, v2, v3 ) === false ) return; 292 | 293 | if ( material.side === THREE.DoubleSide || checkBackfaceCulling( v1, v2, v3 ) === true ) { 294 | 295 | _face = getNextFaceInPool(); 296 | 297 | _face.id = object.id; 298 | _face.v1.copy( v1 ); 299 | _face.v2.copy( v2 ); 300 | _face.v3.copy( v3 ); 301 | _face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3; 302 | _face.renderOrder = object.renderOrder; 303 | 304 | // use first vertex normal as face normal 305 | 306 | _face.normalModel.fromArray( normals, a * 3 ); 307 | _face.normalModel.applyMatrix3( normalMatrix ).normalize(); 308 | 309 | for ( var i = 0; i < 3; i ++ ) { 310 | 311 | var normal = _face.vertexNormalsModel[ i ]; 312 | normal.fromArray( normals, arguments[ i ] * 3 ); 313 | normal.applyMatrix3( normalMatrix ).normalize(); 314 | 315 | var uv = _face.uvs[ i ]; 316 | uv.fromArray( uvs, arguments[ i ] * 2 ); 317 | 318 | } 319 | 320 | _face.vertexNormalsLength = 3; 321 | 322 | _face.material = object.material; 323 | 324 | _renderData.elements.push( _face ); 325 | 326 | } 327 | 328 | } 329 | 330 | return { 331 | setObject: setObject, 332 | projectVertex: projectVertex, 333 | checkTriangleVisibility: checkTriangleVisibility, 334 | checkBackfaceCulling: checkBackfaceCulling, 335 | pushVertex: pushVertex, 336 | pushNormal: pushNormal, 337 | pushColor: pushColor, 338 | pushUv: pushUv, 339 | pushLine: pushLine, 340 | pushTriangle: pushTriangle 341 | }; 342 | 343 | }; 344 | 345 | var renderList = new RenderList(); 346 | 347 | function projectObject( object ) { 348 | 349 | if ( object.visible === false ) return; 350 | 351 | if ( object instanceof THREE.Light ) { 352 | 353 | _renderData.lights.push( object ); 354 | 355 | } else if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Points ) { 356 | 357 | if ( object.material.visible === false ) return; 358 | if ( object.frustumCulled === true && _frustum.intersectsObject( object ) === false ) return; 359 | 360 | addObject( object ); 361 | 362 | } else if ( object instanceof THREE.Sprite ) { 363 | 364 | if ( object.material.visible === false ) return; 365 | if ( object.frustumCulled === true && _frustum.intersectsSprite( object ) === false ) return; 366 | 367 | addObject( object ); 368 | 369 | } 370 | 371 | var children = object.children; 372 | 373 | for ( var i = 0, l = children.length; i < l; i ++ ) { 374 | 375 | projectObject( children[ i ] ); 376 | 377 | } 378 | 379 | } 380 | 381 | function addObject( object ) { 382 | 383 | _object = getNextObjectInPool(); 384 | _object.id = object.id; 385 | _object.object = object; 386 | 387 | _vector3.setFromMatrixPosition( object.matrixWorld ); 388 | _vector3.applyMatrix4( _viewProjectionMatrix ); 389 | _object.z = _vector3.z; 390 | _object.renderOrder = object.renderOrder; 391 | 392 | _renderData.objects.push( _object ); 393 | 394 | } 395 | 396 | this.projectScene = function ( scene, camera, sortObjects, sortElements ) { 397 | 398 | _faceCount = 0; 399 | _lineCount = 0; 400 | _spriteCount = 0; 401 | 402 | _renderData.elements.length = 0; 403 | 404 | if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); 405 | if ( camera.parent === null ) camera.updateMatrixWorld(); 406 | 407 | _viewMatrix.copy( camera.matrixWorldInverse ); 408 | _viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix ); 409 | 410 | _frustum.setFromMatrix( _viewProjectionMatrix ); 411 | 412 | // 413 | 414 | _objectCount = 0; 415 | 416 | _renderData.objects.length = 0; 417 | _renderData.lights.length = 0; 418 | 419 | projectObject( scene ); 420 | 421 | if ( sortObjects === true ) { 422 | 423 | _renderData.objects.sort( painterSort ); 424 | 425 | } 426 | 427 | // 428 | 429 | var objects = _renderData.objects; 430 | 431 | for ( var o = 0, ol = objects.length; o < ol; o ++ ) { 432 | 433 | var object = objects[ o ].object; 434 | var geometry = object.geometry; 435 | 436 | renderList.setObject( object ); 437 | 438 | _modelMatrix = object.matrixWorld; 439 | 440 | _vertexCount = 0; 441 | 442 | if ( object instanceof THREE.Mesh ) { 443 | 444 | if ( geometry instanceof THREE.BufferGeometry ) { 445 | 446 | var attributes = geometry.attributes; 447 | var groups = geometry.groups; 448 | 449 | if ( attributes.position === undefined ) continue; 450 | 451 | var positions = attributes.position.array; 452 | 453 | for ( var i = 0, l = positions.length; i < l; i += 3 ) { 454 | 455 | renderList.pushVertex( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); 456 | 457 | } 458 | 459 | if ( attributes.normal !== undefined ) { 460 | 461 | var normals = attributes.normal.array; 462 | 463 | for ( var i = 0, l = normals.length; i < l; i += 3 ) { 464 | 465 | renderList.pushNormal( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ); 466 | 467 | } 468 | 469 | } 470 | 471 | if ( attributes.uv !== undefined ) { 472 | 473 | var uvs = attributes.uv.array; 474 | 475 | for ( var i = 0, l = uvs.length; i < l; i += 2 ) { 476 | 477 | renderList.pushUv( uvs[ i ], uvs[ i + 1 ] ); 478 | 479 | } 480 | 481 | } 482 | 483 | if ( geometry.index !== null ) { 484 | 485 | var indices = geometry.index.array; 486 | 487 | if ( groups.length > 0 ) { 488 | 489 | for ( var g = 0; g < groups.length; g ++ ) { 490 | 491 | var group = groups[ g ]; 492 | 493 | for ( var i = group.start, l = group.start + group.count; i < l; i += 3 ) { 494 | 495 | renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] ); 496 | 497 | } 498 | 499 | } 500 | 501 | } else { 502 | 503 | for ( var i = 0, l = indices.length; i < l; i += 3 ) { 504 | 505 | renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] ); 506 | 507 | } 508 | 509 | } 510 | 511 | } else { 512 | 513 | for ( var i = 0, l = positions.length / 3; i < l; i += 3 ) { 514 | 515 | renderList.pushTriangle( i, i + 1, i + 2 ); 516 | 517 | } 518 | 519 | } 520 | 521 | } else if ( geometry instanceof THREE.Geometry ) { 522 | 523 | var vertices = geometry.vertices; 524 | var faces = geometry.faces; 525 | var faceVertexUvs = geometry.faceVertexUvs[ 0 ]; 526 | 527 | _normalMatrix.getNormalMatrix( _modelMatrix ); 528 | 529 | var material = object.material; 530 | 531 | var isMultiMaterial = Array.isArray( material ); 532 | 533 | for ( var v = 0, vl = vertices.length; v < vl; v ++ ) { 534 | 535 | var vertex = vertices[ v ]; 536 | 537 | _vector3.copy( vertex ); 538 | 539 | if ( material.morphTargets === true ) { 540 | 541 | var morphTargets = geometry.morphTargets; 542 | var morphInfluences = object.morphTargetInfluences; 543 | 544 | for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { 545 | 546 | var influence = morphInfluences[ t ]; 547 | 548 | if ( influence === 0 ) continue; 549 | 550 | var target = morphTargets[ t ]; 551 | var targetVertex = target.vertices[ v ]; 552 | 553 | _vector3.x += ( targetVertex.x - vertex.x ) * influence; 554 | _vector3.y += ( targetVertex.y - vertex.y ) * influence; 555 | _vector3.z += ( targetVertex.z - vertex.z ) * influence; 556 | 557 | } 558 | 559 | } 560 | 561 | renderList.pushVertex( _vector3.x, _vector3.y, _vector3.z ); 562 | 563 | } 564 | 565 | for ( var f = 0, fl = faces.length; f < fl; f ++ ) { 566 | 567 | var face = faces[ f ]; 568 | 569 | material = isMultiMaterial === true 570 | ? object.material[ face.materialIndex ] 571 | : object.material; 572 | 573 | if ( material === undefined ) continue; 574 | 575 | var side = material.side; 576 | 577 | var v1 = _vertexPool[ face.a ]; 578 | var v2 = _vertexPool[ face.b ]; 579 | var v3 = _vertexPool[ face.c ]; 580 | 581 | if ( renderList.checkTriangleVisibility( v1, v2, v3 ) === false ) continue; 582 | 583 | var visible = renderList.checkBackfaceCulling( v1, v2, v3 ); 584 | 585 | if ( side !== THREE.DoubleSide ) { 586 | 587 | if ( side === THREE.FrontSide && visible === false ) continue; 588 | if ( side === THREE.BackSide && visible === true ) continue; 589 | 590 | } 591 | 592 | _face = getNextFaceInPool(); 593 | 594 | _face.id = object.id; 595 | _face.v1.copy( v1 ); 596 | _face.v2.copy( v2 ); 597 | _face.v3.copy( v3 ); 598 | 599 | _face.normalModel.copy( face.normal ); 600 | 601 | if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) { 602 | 603 | _face.normalModel.negate(); 604 | 605 | } 606 | 607 | _face.normalModel.applyMatrix3( _normalMatrix ).normalize(); 608 | 609 | var faceVertexNormals = face.vertexNormals; 610 | 611 | for ( var n = 0, nl = Math.min( faceVertexNormals.length, 3 ); n < nl; n ++ ) { 612 | 613 | var normalModel = _face.vertexNormalsModel[ n ]; 614 | normalModel.copy( faceVertexNormals[ n ] ); 615 | 616 | if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) { 617 | 618 | normalModel.negate(); 619 | 620 | } 621 | 622 | normalModel.applyMatrix3( _normalMatrix ).normalize(); 623 | 624 | } 625 | 626 | _face.vertexNormalsLength = faceVertexNormals.length; 627 | 628 | var vertexUvs = faceVertexUvs[ f ]; 629 | 630 | if ( vertexUvs !== undefined ) { 631 | 632 | for ( var u = 0; u < 3; u ++ ) { 633 | 634 | _face.uvs[ u ].copy( vertexUvs[ u ] ); 635 | 636 | } 637 | 638 | } 639 | 640 | _face.color = face.color; 641 | _face.material = material; 642 | 643 | _face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3; 644 | _face.renderOrder = object.renderOrder; 645 | 646 | _renderData.elements.push( _face ); 647 | 648 | } 649 | 650 | } 651 | 652 | } else if ( object instanceof THREE.Line ) { 653 | 654 | _modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix ); 655 | 656 | if ( geometry instanceof THREE.BufferGeometry ) { 657 | 658 | var attributes = geometry.attributes; 659 | 660 | if ( attributes.position !== undefined ) { 661 | 662 | var positions = attributes.position.array; 663 | 664 | for ( var i = 0, l = positions.length; i < l; i += 3 ) { 665 | 666 | renderList.pushVertex( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); 667 | 668 | } 669 | 670 | if ( attributes.color !== undefined ) { 671 | 672 | var colors = attributes.color.array; 673 | 674 | for ( var i = 0, l = colors.length; i < l; i += 3 ) { 675 | 676 | renderList.pushColor( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ); 677 | 678 | } 679 | 680 | } 681 | 682 | if ( geometry.index !== null ) { 683 | 684 | var indices = geometry.index.array; 685 | 686 | for ( var i = 0, l = indices.length; i < l; i += 2 ) { 687 | 688 | renderList.pushLine( indices[ i ], indices[ i + 1 ] ); 689 | 690 | } 691 | 692 | } else { 693 | 694 | var step = object instanceof THREE.LineSegments ? 2 : 1; 695 | 696 | for ( var i = 0, l = ( positions.length / 3 ) - 1; i < l; i += step ) { 697 | 698 | renderList.pushLine( i, i + 1 ); 699 | 700 | } 701 | 702 | } 703 | 704 | } 705 | 706 | } else if ( geometry instanceof THREE.Geometry ) { 707 | 708 | var vertices = object.geometry.vertices; 709 | 710 | if ( vertices.length === 0 ) continue; 711 | 712 | v1 = getNextVertexInPool(); 713 | v1.positionScreen.copy( vertices[ 0 ] ).applyMatrix4( _modelViewProjectionMatrix ); 714 | 715 | var step = object instanceof THREE.LineSegments ? 2 : 1; 716 | 717 | for ( var v = 1, vl = vertices.length; v < vl; v ++ ) { 718 | 719 | v1 = getNextVertexInPool(); 720 | v1.positionScreen.copy( vertices[ v ] ).applyMatrix4( _modelViewProjectionMatrix ); 721 | 722 | if ( ( v + 1 ) % step > 0 ) continue; 723 | 724 | v2 = _vertexPool[ _vertexCount - 2 ]; 725 | 726 | _clippedVertex1PositionScreen.copy( v1.positionScreen ); 727 | _clippedVertex2PositionScreen.copy( v2.positionScreen ); 728 | 729 | if ( clipLine( _clippedVertex1PositionScreen, _clippedVertex2PositionScreen ) === true ) { 730 | 731 | // Perform the perspective divide 732 | _clippedVertex1PositionScreen.multiplyScalar( 1 / _clippedVertex1PositionScreen.w ); 733 | _clippedVertex2PositionScreen.multiplyScalar( 1 / _clippedVertex2PositionScreen.w ); 734 | 735 | _line = getNextLineInPool(); 736 | 737 | _line.id = object.id; 738 | _line.v1.positionScreen.copy( _clippedVertex1PositionScreen ); 739 | _line.v2.positionScreen.copy( _clippedVertex2PositionScreen ); 740 | 741 | _line.z = Math.max( _clippedVertex1PositionScreen.z, _clippedVertex2PositionScreen.z ); 742 | _line.renderOrder = object.renderOrder; 743 | 744 | _line.material = object.material; 745 | 746 | if ( object.material.vertexColors === THREE.VertexColors ) { 747 | 748 | _line.vertexColors[ 0 ].copy( object.geometry.colors[ v ] ); 749 | _line.vertexColors[ 1 ].copy( object.geometry.colors[ v - 1 ] ); 750 | 751 | } 752 | 753 | _renderData.elements.push( _line ); 754 | 755 | } 756 | 757 | } 758 | 759 | } 760 | 761 | } else if ( object instanceof THREE.Points ) { 762 | 763 | _modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix ); 764 | 765 | if ( geometry instanceof THREE.Geometry ) { 766 | 767 | var vertices = object.geometry.vertices; 768 | 769 | for ( var v = 0, vl = vertices.length; v < vl; v ++ ) { 770 | 771 | var vertex = vertices[ v ]; 772 | 773 | _vector4.set( vertex.x, vertex.y, vertex.z, 1 ); 774 | _vector4.applyMatrix4( _modelViewProjectionMatrix ); 775 | 776 | pushPoint( _vector4, object, camera ); 777 | 778 | } 779 | 780 | } else if ( geometry instanceof THREE.BufferGeometry ) { 781 | 782 | var attributes = geometry.attributes; 783 | 784 | if ( attributes.position !== undefined ) { 785 | 786 | var positions = attributes.position.array; 787 | 788 | for ( var i = 0, l = positions.length; i < l; i += 3 ) { 789 | 790 | _vector4.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ], 1 ); 791 | _vector4.applyMatrix4( _modelViewProjectionMatrix ); 792 | 793 | pushPoint( _vector4, object, camera ); 794 | 795 | } 796 | 797 | } 798 | 799 | } 800 | 801 | } else if ( object instanceof THREE.Sprite ) { 802 | 803 | _vector4.set( _modelMatrix.elements[ 12 ], _modelMatrix.elements[ 13 ], _modelMatrix.elements[ 14 ], 1 ); 804 | _vector4.applyMatrix4( _viewProjectionMatrix ); 805 | 806 | pushPoint( _vector4, object, camera ); 807 | 808 | } 809 | 810 | } 811 | 812 | if ( sortElements === true ) { 813 | 814 | _renderData.elements.sort( painterSort ); 815 | 816 | } 817 | 818 | return _renderData; 819 | 820 | }; 821 | 822 | function pushPoint( _vector4, object, camera ) { 823 | 824 | var invW = 1 / _vector4.w; 825 | 826 | _vector4.z *= invW; 827 | 828 | if ( _vector4.z >= - 1 && _vector4.z <= 1 ) { 829 | 830 | _sprite = getNextSpriteInPool(); 831 | _sprite.id = object.id; 832 | _sprite.x = _vector4.x * invW; 833 | _sprite.y = _vector4.y * invW; 834 | _sprite.z = _vector4.z; 835 | _sprite.renderOrder = object.renderOrder; 836 | _sprite.object = object; 837 | 838 | _sprite.rotation = object.rotation; 839 | 840 | _sprite.scale.x = object.scale.x * Math.abs( _sprite.x - ( _vector4.x + camera.projectionMatrix.elements[ 0 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 12 ] ) ); 841 | _sprite.scale.y = object.scale.y * Math.abs( _sprite.y - ( _vector4.y + camera.projectionMatrix.elements[ 5 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 13 ] ) ); 842 | 843 | _sprite.material = object.material; 844 | 845 | _renderData.elements.push( _sprite ); 846 | 847 | } 848 | 849 | } 850 | 851 | // Pools 852 | 853 | function getNextObjectInPool() { 854 | 855 | if ( _objectCount === _objectPoolLength ) { 856 | 857 | var object = new THREE.RenderableObject(); 858 | _objectPool.push( object ); 859 | _objectPoolLength ++; 860 | _objectCount ++; 861 | return object; 862 | 863 | } 864 | 865 | return _objectPool[ _objectCount ++ ]; 866 | 867 | } 868 | 869 | function getNextVertexInPool() { 870 | 871 | if ( _vertexCount === _vertexPoolLength ) { 872 | 873 | var vertex = new THREE.RenderableVertex(); 874 | _vertexPool.push( vertex ); 875 | _vertexPoolLength ++; 876 | _vertexCount ++; 877 | return vertex; 878 | 879 | } 880 | 881 | return _vertexPool[ _vertexCount ++ ]; 882 | 883 | } 884 | 885 | function getNextFaceInPool() { 886 | 887 | if ( _faceCount === _facePoolLength ) { 888 | 889 | var face = new THREE.RenderableFace(); 890 | _facePool.push( face ); 891 | _facePoolLength ++; 892 | _faceCount ++; 893 | return face; 894 | 895 | } 896 | 897 | return _facePool[ _faceCount ++ ]; 898 | 899 | 900 | } 901 | 902 | function getNextLineInPool() { 903 | 904 | if ( _lineCount === _linePoolLength ) { 905 | 906 | var line = new THREE.RenderableLine(); 907 | _linePool.push( line ); 908 | _linePoolLength ++; 909 | _lineCount ++; 910 | return line; 911 | 912 | } 913 | 914 | return _linePool[ _lineCount ++ ]; 915 | 916 | } 917 | 918 | function getNextSpriteInPool() { 919 | 920 | if ( _spriteCount === _spritePoolLength ) { 921 | 922 | var sprite = new THREE.RenderableSprite(); 923 | _spritePool.push( sprite ); 924 | _spritePoolLength ++; 925 | _spriteCount ++; 926 | return sprite; 927 | 928 | } 929 | 930 | return _spritePool[ _spriteCount ++ ]; 931 | 932 | } 933 | 934 | // 935 | 936 | function painterSort( a, b ) { 937 | 938 | if ( a.renderOrder !== b.renderOrder ) { 939 | 940 | return a.renderOrder - b.renderOrder; 941 | 942 | } else if ( a.z !== b.z ) { 943 | 944 | return b.z - a.z; 945 | 946 | } else if ( a.id !== b.id ) { 947 | 948 | return a.id - b.id; 949 | 950 | } else { 951 | 952 | return 0; 953 | 954 | } 955 | 956 | } 957 | 958 | function clipLine( s1, s2 ) { 959 | 960 | var alpha1 = 0, alpha2 = 1, 961 | 962 | // Calculate the boundary coordinate of each vertex for the near and far clip planes, 963 | // Z = -1 and Z = +1, respectively. 964 | 965 | bc1near = s1.z + s1.w, 966 | bc2near = s2.z + s2.w, 967 | bc1far = - s1.z + s1.w, 968 | bc2far = - s2.z + s2.w; 969 | 970 | if ( bc1near >= 0 && bc2near >= 0 && bc1far >= 0 && bc2far >= 0 ) { 971 | 972 | // Both vertices lie entirely within all clip planes. 973 | return true; 974 | 975 | } else if ( ( bc1near < 0 && bc2near < 0 ) || ( bc1far < 0 && bc2far < 0 ) ) { 976 | 977 | // Both vertices lie entirely outside one of the clip planes. 978 | return false; 979 | 980 | } else { 981 | 982 | // The line segment spans at least one clip plane. 983 | 984 | if ( bc1near < 0 ) { 985 | 986 | // v1 lies outside the near plane, v2 inside 987 | alpha1 = Math.max( alpha1, bc1near / ( bc1near - bc2near ) ); 988 | 989 | } else if ( bc2near < 0 ) { 990 | 991 | // v2 lies outside the near plane, v1 inside 992 | alpha2 = Math.min( alpha2, bc1near / ( bc1near - bc2near ) ); 993 | 994 | } 995 | 996 | if ( bc1far < 0 ) { 997 | 998 | // v1 lies outside the far plane, v2 inside 999 | alpha1 = Math.max( alpha1, bc1far / ( bc1far - bc2far ) ); 1000 | 1001 | } else if ( bc2far < 0 ) { 1002 | 1003 | // v2 lies outside the far plane, v2 inside 1004 | alpha2 = Math.min( alpha2, bc1far / ( bc1far - bc2far ) ); 1005 | 1006 | } 1007 | 1008 | if ( alpha2 < alpha1 ) { 1009 | 1010 | // The line segment spans two boundaries, but is outside both of them. 1011 | // (This can't happen when we're only clipping against just near/far but good 1012 | // to leave the check here for future usage if other clip planes are added.) 1013 | return false; 1014 | 1015 | } else { 1016 | 1017 | // Update the s1 and s2 vertices to match the clipped line segment. 1018 | s1.lerp( s2, alpha1 ); 1019 | s2.lerp( s1, 1 - alpha2 ); 1020 | 1021 | return true; 1022 | 1023 | } 1024 | 1025 | } 1026 | 1027 | } 1028 | 1029 | }; 1030 | -------------------------------------------------------------------------------- /src/common/threejslibs/stats.min.js: -------------------------------------------------------------------------------- 1 | // stats.js - http://github.com/mrdoob/stats.js 2 | var Stats = function() { 3 | function h(a) { c.appendChild(a.dom); return a } 4 | 5 | function k(a) { for (var d = 0; d < c.children.length; d++) c.children[d].style.display = d === a ? "block" : "none"; 6 | l = a } 7 | var l = 0, 8 | c = document.createElement("div"); 9 | c.style.cssText = "position:fixed;top:0;left:0;cursor:pointer;opacity:0.9;z-index:10000"; 10 | c.addEventListener("click", function(a) { a.preventDefault(); 11 | k(++l % c.children.length) }, !1); 12 | var g = (performance || Date).now(), 13 | e = g, 14 | a = 0, 15 | r = h(new Stats.Panel("FPS", "#0ff", "#002")), 16 | f = h(new Stats.Panel("MS", "#0f0", "#020")); 17 | if (this.performance && this.performance.memory) var t = h(new Stats.Panel("MB", "#f08", "#201")); 18 | k(0); 19 | return { REVISION: 16, dom: c, addPanel: h, showPanel: k, begin: function() { g = (performance || Date).now() }, end: function() { a++; var c = (performance || Date).now(); 20 | f.update(c - g, 200); if (c > e + 1E3 && (r.update(1E3 * a / (c - e), 100), e = c, a = 0, t)) { var d = performance.memory; 21 | t.update(d.usedJSHeapSize / 1048576, d.jsHeapSizeLimit / 1048576) } return c }, update: function() { g = this.end() }, domElement: c, setMode: k } 22 | }; 23 | Stats.Panel = function(h, k, l) { 24 | var c = Infinity, 25 | g = 0, 26 | e = Math.round, 27 | a = e(window.devicePixelRatio || 1), 28 | r = 80 * a, 29 | f = 48 * a, 30 | t = 3 * a, 31 | u = 2 * a, 32 | d = 3 * a, 33 | m = 15 * a, 34 | n = 74 * a, 35 | p = 30 * a, 36 | q = document.createElement("canvas"); 37 | q.width = r; 38 | q.height = f; 39 | q.style.cssText = "width:80px;height:48px"; 40 | var b = q.getContext("2d"); 41 | b.font = "bold " + 9 * a + "px Helvetica,Arial,sans-serif"; 42 | b.textBaseline = "top"; 43 | b.fillStyle = l; 44 | b.fillRect(0, 0, r, f); 45 | b.fillStyle = k; 46 | b.fillText(h, t, u); 47 | b.fillRect(d, m, n, p); 48 | b.fillStyle = l; 49 | b.globalAlpha = .9; 50 | b.fillRect(d, m, n, p); 51 | return { 52 | dom: q, 53 | update: function(f, 54 | v) { c = Math.min(c, f); 55 | g = Math.max(g, f); 56 | b.fillStyle = l; 57 | b.globalAlpha = 1; 58 | b.fillRect(0, 0, r, m); 59 | b.fillStyle = k; 60 | b.fillText(e(f) + " " + h + " (" + e(c) + "-" + e(g) + ")", t, u); 61 | b.drawImage(q, d + a, m, n - a, p, d, m, n - a, p); 62 | b.fillRect(d + n - a, m, a, p); 63 | b.fillStyle = l; 64 | b.globalAlpha = .9; 65 | b.fillRect(d + n - a, m, a, e((1 - f / v) * p)) } 66 | } 67 | }; 68 | export default Stats; 69 | // "object" === typeof module && (module.exports = Stats); -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import registerServiceWorker from './registerServiceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | registerServiceWorker(); 9 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | // In production, we register a service worker to serve assets from local cache. 2 | 3 | // This lets the app load faster on subsequent visits in production, and gives 4 | // it offline capabilities. However, it also means that developers (and users) 5 | // will only see deployed updates on the "N+1" visit to a page, since previously 6 | // cached resources are updated in the background. 7 | 8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 9 | // This link also includes instructions on opting out of this behavior. 10 | 11 | const isLocalhost = Boolean( 12 | window.location.hostname === 'localhost' || 13 | // [::1] is the IPv6 localhost address. 14 | window.location.hostname === '[::1]' || 15 | // 127.0.0.1/8 is considered localhost for IPv4. 16 | window.location.hostname.match( 17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 18 | ) 19 | ); 20 | 21 | export default function register() { 22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 23 | // The URL constructor is available in all browsers that support SW. 24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location); 25 | if (publicUrl.origin !== window.location.origin) { 26 | // Our service worker won't work if PUBLIC_URL is on a different origin 27 | // from what our page is served on. This might happen if a CDN is used to 28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 29 | return; 30 | } 31 | 32 | window.addEventListener('load', () => { 33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 34 | 35 | if (!isLocalhost) { 36 | // Is not local host. Just register service worker 37 | registerValidSW(swUrl); 38 | } else { 39 | // This is running on localhost. Lets check if a service worker still exists or not. 40 | checkValidServiceWorker(swUrl); 41 | } 42 | }); 43 | } 44 | } 45 | 46 | function registerValidSW(swUrl) { 47 | navigator.serviceWorker 48 | .register(swUrl) 49 | .then(registration => { 50 | registration.onupdatefound = () => { 51 | const installingWorker = registration.installing; 52 | installingWorker.onstatechange = () => { 53 | if (installingWorker.state === 'installed') { 54 | if (navigator.serviceWorker.controller) { 55 | // At this point, the old content will have been purged and 56 | // the fresh content will have been added to the cache. 57 | // It's the perfect time to display a "New content is 58 | // available; please refresh." message in your web app. 59 | console.log('New content is available; please refresh.'); 60 | } else { 61 | // At this point, everything has been precached. 62 | // It's the perfect time to display a 63 | // "Content is cached for offline use." message. 64 | console.log('Content is cached for offline use.'); 65 | } 66 | } 67 | }; 68 | }; 69 | }) 70 | .catch(error => { 71 | console.error('Error during service worker registration:', error); 72 | }); 73 | } 74 | 75 | function checkValidServiceWorker(swUrl) { 76 | // Check if the service worker can be found. If it can't reload the page. 77 | fetch(swUrl) 78 | .then(response => { 79 | // Ensure service worker exists, and that we really are getting a JS file. 80 | if ( 81 | response.status === 404 || 82 | response.headers.get('content-type').indexOf('javascript') === -1 83 | ) { 84 | // No service worker found. Probably a different app. Reload the page. 85 | navigator.serviceWorker.ready.then(registration => { 86 | registration.unregister().then(() => { 87 | window.location.reload(); 88 | }); 89 | }); 90 | } else { 91 | // Service worker found. Proceed as normal. 92 | registerValidSW(swUrl); 93 | } 94 | }) 95 | .catch(() => { 96 | console.log( 97 | 'No internet connection found. App is running in offline mode.' 98 | ); 99 | }); 100 | } 101 | 102 | export function unregister() { 103 | if ('serviceWorker' in navigator) { 104 | navigator.serviceWorker.ready.then(registration => { 105 | registration.unregister(); 106 | }); 107 | } 108 | } 109 | --------------------------------------------------------------------------------