├── .babelrc ├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── README.md ├── demo ├── app.js ├── demo.gif ├── demo2.gif ├── index.html ├── style.styl └── test1.jpg ├── dist ├── threejsTextureTool.js └── threejsTextureTool.min.js ├── package.json ├── src ├── CanvasTexture.js ├── ImageTexture.js ├── index.js └── style.styl └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015", 4 | "stage-0" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jeremboo/threejs-texture-tool/f5c4fd894812c3e16eb3c8faad0a8888e5c5bf33/.eslintignore -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | "globals": { 4 | "app": true, 5 | "window": true, 6 | "document": true, 7 | "describe": true, 8 | "it": true 9 | }, 10 | "parser": "babel-eslint", 11 | "rules": { 12 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### OSX ### 2 | .DS_Store 3 | 4 | # Thumbnails 5 | ._* 6 | 7 | 8 | # NPM 9 | 10 | node_modules/ 11 | npm-debug.log 12 | 13 | demo/style.css 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## [ThreejsTextureTool 0.5.1](https://github.com/Jeremboo/threejs-texture-tool) 3 | 4 | A tool to preview and update your canvases or pictures used for your [three.js](https://threejs.org/) textures. 5 | 6 | [demo 0.2.6](http://codepen.io/Jeremboo/full/qqabKY/) 7 | 8 | 9 | ## Getting Started (es6) 10 | 11 | ### Create a texture with a dynamic canvas 12 | 13 | ![Threejs Texture Tool canvas texture demo](https://github.com/Jeremboo/threejs-texture-tool/blob/master/demo/demo.gif?raw=true) 14 | 15 | ```javascript 16 | import { createCanvasTexture } from 'threejs-texture-tool'; 17 | 18 | // Create a canvasTexture 19 | const canvasTexture = createCanvasTexture({ 20 | name: 'myCanvas', 21 | onStart: (props) => { 22 | // Draw once a rectangle and add a mouse move Listener 23 | // To update this canvas 24 | const { width, height, context, canvas, update } = props; 25 | context.rect(0, 0, width, height); 26 | context.fillStyle = '#F6FF49'; 27 | context.fill(); 28 | canvas.onmousemove = e => { 29 | update(e.offsetX, e.offsetY); 30 | }; 31 | }, 32 | onUpdate: (x, y) => { 33 | // Called by `canvasTexture.udpate(...)` 34 | const { context } = canvasTexture; 35 | context.beginPath(); 36 | context.arc(x, y, 10, 0, 2 * Math.PI, false); 37 | context.fillStyle = mainColor; 38 | context.fill(); 39 | context.closePath(); 40 | }, 41 | }); 42 | 43 | // Different accesses 44 | const { texture, material, uniform, canvas } = canvasTexture; 45 | ``` 46 | 47 | ### Create a texture with a picture 48 | 49 | ![Threejs Texture Tool demo with picture](https://github.com/Jeremboo/threejs-texture-tool/blob/master/demo/demo2.gif?raw=true) 50 | 51 | ```javascript 52 | import { createImageTexture } from 'threejs-texture-tool'; 53 | 54 | // Load the picture 55 | const imgTexture = createImageTexture('./test1.jpg', { name: 'test', onLoad: () => { 56 | // Manipulate params 57 | imgTexture.texture.wrapS = 58 | imgTexture.texture.wrapT = 59 | imgTexture.uniform.value.wrapS = 60 | imgTexture.uniform.value.wrapT = 61 | REPEAT_WRAPPING; 62 | } }); 63 | 64 | // Different accesses 65 | const { texture, material, uniform, image } = canvasTexture; 66 | ``` 67 | ## Get material / uniform and other transformations 68 | 69 | For the both textureTools, you can get her material and uniform object compatible with three.js 70 | 71 | ```javascript 72 | 73 | // Use it as material 74 | const mesh = THREE.Mesh( 75 | new BoxGeometry(1, 1, 1), 76 | imageOrCanvasTexture.material, 77 | ); 78 | 79 | // Into shaderMaterial 80 | const shaderMaterial = new ShaderMaterial({ 81 | uniforms: { 82 | imgMap: imageOrCanvasTexture.uniform, 83 | }, 84 | vertexShader: shaderVert, 85 | fragmentShader: shaderFrag, 86 | side: DoubleSide, 87 | }); 88 | 89 | // Get only the picture 90 | const img = document.createElement('img'); 91 | img.src = imageTexture.image; 92 | 93 | // Get only the canvas 94 | const canvas = canvasTexture.canvas; 95 | 96 | ``` 97 | 98 | ## TODO / NEXT STEP 99 | 100 | - remplace dragDrop dependencie from scratch 101 | 102 | - drag and move all openned textures anywhere in the view 103 | 104 | - reset each canvas texture with a button 105 | 106 | - functions to generate specific canvas textures : 107 | - noiseTexture 108 | - perlinNoiseTexture 109 | - gradientTexture 110 | - perlinGradientNoiseTexture 111 | - customTexture 112 | - fusionTexture / superposeTextures 113 | -------------------------------------------------------------------------------- /demo/app.js: -------------------------------------------------------------------------------- 1 | import { Scene, WebGLRenderer, PerspectiveCamera, Object3D, BoxGeometry, MeshFaceMaterial, Mesh } from 'three'; 2 | import { createCanvasTexture, createImageTexture } from '../src/index.js'; 3 | 4 | import './style.styl'; 5 | 6 | 7 | /**/ /* ---- CORE ---- */ 8 | /**/ const mainColor = '#323031'; 9 | /**/ const secondaryColor = '#DB3A34'; 10 | /**/ const bgColor = '#FFC857'; 11 | /**/ let windowWidth = window.innerWidth; 12 | /**/ let windowHeight = window.innerHeight; 13 | /**/ class Webgl { 14 | /**/ constructor(w, h) { 15 | /**/ this.meshCount = 0; 16 | /**/ this.meshListeners = []; 17 | /**/ this.renderer = new WebGLRenderer({ antialias: true, alpha: true }); 18 | /**/ this.renderer.setPixelRatio(window.devicePixelRatio); 19 | /**/ // this.renderer.setClearColor(new Color(bgColor))); 20 | /**/ this.scene = new Scene(); 21 | /**/ this.camera = new PerspectiveCamera(50, w / h, 1, 1000); 22 | /**/ this.camera.position.set(0, 0, 10); 23 | /**/ this.dom = this.renderer.domElement; 24 | /**/ this.update = this.update.bind(this); 25 | /**/ this.resize = this.resize.bind(this); 26 | /**/ this.resize(w, h); // set render size 27 | /**/ } 28 | /**/ add(mesh) { 29 | /**/ this.scene.add(mesh); 30 | /**/ if (!mesh.update) return; 31 | /**/ this.meshListeners.push(mesh.update); 32 | /**/ this.meshCount++; 33 | /**/ } 34 | /**/ update() { 35 | /**/ let i = this.meshCount; 36 | /**/ while (--i >= 0) { 37 | /**/ this.meshListeners[i].apply(this, null); 38 | /**/ } 39 | /**/ this.renderer.render(this.scene, this.camera); 40 | /**/ } 41 | /**/ resize(w, h) { 42 | /**/ this.camera.aspect = w / h; 43 | /**/ this.camera.updateProjectionMatrix(); 44 | /**/ this.renderer.setSize(w, h); 45 | /**/ } 46 | /**/ } 47 | /**/ const webgl = new Webgl(windowWidth, windowHeight); 48 | /**/ document.body.appendChild(webgl.dom); 49 | /**/ 50 | /**/ 51 | /* ---- CREATING ZONE ---- */ 52 | 53 | const CUBE_SIZE = 3; 54 | 55 | // OBJECTS 56 | class Block extends Object3D { 57 | constructor() { 58 | super(); 59 | 60 | this.materials = []; 61 | 62 | let i; 63 | for (i = 0; i < 4; i++) { 64 | const canvasTexture = createCanvasTexture({ 65 | name: 'canvas', 66 | onStart: (canvasTextureProps) => { 67 | const { width, height, context, canvas, update } = canvasTextureProps; 68 | context.rect(0, 0, width, height); 69 | context.fillStyle = bgColor; 70 | context.fill(); 71 | // http://codepen.io/jbpenrath/pen/gLObej 72 | let mouseDown = false; 73 | const paint = e => { 74 | // call the method `onUpdate` defined bellow 75 | if (mouseDown) update(e.offsetX, e.offsetY); 76 | }; 77 | canvas.onmousedown = e => { 78 | mouseDown = true; 79 | paint(e); 80 | }; 81 | canvas.onmouseup = () => { 82 | mouseDown = false; 83 | }; 84 | canvas.onmousemove = paint; 85 | }, 86 | // custom attributes 87 | onUpdate: (x, y) => { 88 | const { context } = canvasTexture; 89 | context.beginPath(); 90 | context.arc(x, y, 10, 0, 2 * Math.PI, false); 91 | context.fillStyle = mainColor; 92 | context.fill(); 93 | context.closePath(); 94 | }, 95 | }); 96 | this.materials.push(canvasTexture.material); 97 | } 98 | 99 | // Add imageTexture 100 | this.materials.push(createImageTexture('./test1.jpg').material); 101 | this.materials.push(createImageTexture('./test1.jpg').material); 102 | 103 | this.geometry = new BoxGeometry(CUBE_SIZE, CUBE_SIZE, CUBE_SIZE, 20, 20); 104 | this.material = new MeshFaceMaterial(this.materials); 105 | this.mesh = new Mesh(this.geometry, this.material); 106 | this.mesh.rotation.set(1.52, 0, 0); 107 | this.add(this.mesh); 108 | 109 | this.rotation.set(0.25, 0.7, 0); 110 | this.update = this.update.bind(this); 111 | } 112 | 113 | update() { 114 | this.mesh.rotation.x += 0.01; 115 | this.mesh.rotation.y += 0.01; 116 | } 117 | } 118 | 119 | 120 | // ADDS 121 | webgl.add(new Block()); 122 | 123 | 124 | /* ---- CREATING ZONE END ---- */ 125 | /**/ /* ---- ON RESIZE ---- */ 126 | /**/ function onResize() { 127 | /**/ windowWidth = window.innerWidth; 128 | /**/ windowHeight = window.innerHeight; 129 | /**/ webgl.resize(windowWidth, windowHeight); 130 | /**/ } 131 | /**/ window.addEventListener('resize', onResize); 132 | /**/ window.addEventListener('orientationchange', onResize); 133 | /**/ /* ---- LOOP ---- */ 134 | /**/ function _loop() { 135 | /**/ webgl.update(); 136 | /**/ requestAnimationFrame(_loop); 137 | /**/ } 138 | /**/ _loop(); 139 | -------------------------------------------------------------------------------- /demo/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jeremboo/threejs-texture-tool/f5c4fd894812c3e16eb3c8faad0a8888e5c5bf33/demo/demo.gif -------------------------------------------------------------------------------- /demo/demo2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jeremboo/threejs-texture-tool/f5c4fd894812c3e16eb3c8faad0a8888e5c5bf33/demo/demo2.gif -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Threejs Texture Tool 6 | 7 | 8 |

9 | @Jeremboo 10 |
┗|`O´|┛ 11 |

12 | 13 | 14 | -------------------------------------------------------------------------------- /demo/style.styl: -------------------------------------------------------------------------------- 1 | // INIT 2 | mainColor = #084C61 3 | secondaryColor = #FFC857 4 | bgColor = #177E89 5 | bgColorDarken = darken(bgColor, 5); 6 | 7 | body, html 8 | width: 100%; 9 | height: 100%; 10 | overflow: hidden 11 | background: bgColor 12 | background: linear-gradient(to bottom, bgColor 70%, bgColorDarken 100%); 13 | margin: 0 14 | font-family: Century Gothic,CenturyGothic,AppleGothic,sans-serif 15 | 16 | .f 17 | position: fixed 18 | bottom: 5px 19 | right: 15px 20 | font-family: 'Arial' 21 | font-size: 0.7rem 22 | color: mainColor 23 | text-align: center; 24 | 25 | a 26 | font-size: 0.8rem 27 | color: secondaryColor 28 | -------------------------------------------------------------------------------- /demo/test1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jeremboo/threejs-texture-tool/f5c4fd894812c3e16eb3c8faad0a8888e5c5bf33/demo/test1.jpg -------------------------------------------------------------------------------- /dist/threejsTextureTool.js: -------------------------------------------------------------------------------- 1 | (function webpackUniversalModuleDefinition(root, factory) { 2 | if(typeof exports === 'object' && typeof module === 'object') 3 | module.exports = factory(require("three")); 4 | else if(typeof define === 'function' && define.amd) 5 | define("threejsTextureTool", ["three"], factory); 6 | else if(typeof exports === 'object') 7 | exports["threejsTextureTool"] = factory(require("three")); 8 | else 9 | root["threejsTextureTool"] = factory(root["three"]); 10 | })(this, function(__WEBPACK_EXTERNAL_MODULE_1__) { 11 | return /******/ (function(modules) { // webpackBootstrap 12 | /******/ // The module cache 13 | /******/ var installedModules = {}; 14 | 15 | /******/ // The require function 16 | /******/ function __webpack_require__(moduleId) { 17 | 18 | /******/ // Check if module is in cache 19 | /******/ if(installedModules[moduleId]) 20 | /******/ return installedModules[moduleId].exports; 21 | 22 | /******/ // Create a new module (and put it into the cache) 23 | /******/ var module = installedModules[moduleId] = { 24 | /******/ exports: {}, 25 | /******/ id: moduleId, 26 | /******/ loaded: false 27 | /******/ }; 28 | 29 | /******/ // Execute the module function 30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 31 | 32 | /******/ // Flag the module as loaded 33 | /******/ module.loaded = true; 34 | 35 | /******/ // Return the exports of the module 36 | /******/ return module.exports; 37 | /******/ } 38 | 39 | 40 | /******/ // expose the modules object (__webpack_modules__) 41 | /******/ __webpack_require__.m = modules; 42 | 43 | /******/ // expose the module cache 44 | /******/ __webpack_require__.c = installedModules; 45 | 46 | /******/ // __webpack_public_path__ 47 | /******/ __webpack_require__.p = ""; 48 | 49 | /******/ // Load entry module and return exports 50 | /******/ return __webpack_require__(0); 51 | /******/ }) 52 | /************************************************************************/ 53 | /******/ ([ 54 | /* 0 */ 55 | /***/ function(module, exports, __webpack_require__) { 56 | 57 | 'use strict'; 58 | 59 | Object.defineProperty(exports, "__esModule", { 60 | value: true 61 | }); 62 | exports.createCanvasTexture = exports.createImageTexture = undefined; 63 | 64 | var _dragDrop = __webpack_require__(6); 65 | 66 | var _dragDrop2 = _interopRequireDefault(_dragDrop); 67 | 68 | var _CanvasTexture = __webpack_require__(2); 69 | 70 | var _CanvasTexture2 = _interopRequireDefault(_CanvasTexture); 71 | 72 | var _ImageTexture = __webpack_require__(3); 73 | 74 | var _ImageTexture2 = _interopRequireDefault(_ImageTexture); 75 | 76 | var _style = __webpack_require__(11); 77 | 78 | var _style2 = _interopRequireDefault(_style); 79 | 80 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 81 | 82 | /** 83 | * ######################### 84 | * INIT 85 | * ######################### 86 | */ 87 | 88 | var textureNameArr = []; 89 | 90 | // Init canvas wrapper into the dom 91 | var textureToolWrapper = document.createElement('ul'); 92 | textureToolWrapper.id = 'texture-tool-wrapper'; 93 | textureToolWrapper.className = 'ThreejsTextureTool-wrapper'; 94 | document.body.appendChild(textureToolWrapper); 95 | 96 | // Add css without style-loader 97 | var style = document.createElement('style'); 98 | style.innerHTML = _style2.default.toString(); 99 | textureToolWrapper.appendChild(style); 100 | 101 | // Listener on keycode to toggle textureToolWrapper when h pressed 102 | document.body.addEventListener('keydown', function (e) { 103 | if (e.keyCode === 72) { 104 | textureToolWrapper.classList.toggle('ThreejsTextureTool_hidden'); 105 | } 106 | }); 107 | 108 | /** 109 | * ######################### 110 | * FUNCTION 111 | * ######################### 112 | */ 113 | 114 | /** 115 | * Add a texture viewer in DOM 116 | * @params {String} name 117 | * @params {Object} texture 118 | */ 119 | function addInDom(name, texture) { 120 | // HTML 121 | var HTML = '\n
  • \n \n
    \n \n
    \n
  • \n '; 122 | textureToolWrapper.insertAdjacentHTML('beforeend', HTML); 123 | 124 | // ACTIONS 125 | var textureWindow = document.getElementById(name + '-window'); 126 | var openBtn = document.getElementById(name + '-open'); 127 | openBtn.addEventListener('click', function () { 128 | openBtn.classList.add('TextureTool-hidden'); 129 | textureWindow.classList.remove('TextureTool-hidden'); 130 | }); 131 | var closeBtn = document.getElementById(name + '-close'); 132 | closeBtn.addEventListener('click', function () { 133 | openBtn.classList.remove('TextureTool-hidden'); 134 | textureWindow.classList.add('TextureTool-hidden'); 135 | }); 136 | textureWindow.appendChild(texture); 137 | return textureWindow; 138 | } 139 | 140 | /** 141 | * Add a texture name into the array 142 | * @params {String} name 143 | */ 144 | function saveTextureName(name) { 145 | var getUniqueName = function getUniqueName(currentName) { 146 | var i = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; 147 | 148 | var n = '' + currentName + (i !== 0 ? '-' + i : ''); 149 | return textureNameArr.indexOf(n) !== -1 ? getUniqueName(currentName, i + 1) : n; 150 | }; 151 | var uniqueName = getUniqueName(name); 152 | textureNameArr.push(uniqueName); 153 | return uniqueName; 154 | } 155 | 156 | /** 157 | * ######################### 158 | * EXPORT 159 | * ######################### 160 | */ 161 | 162 | /** 163 | * Create an texture based on an image 164 | * @params {String} url of the image 165 | * @params {Object} props with : 166 | * - @params {String} name id attribued at the texture 167 | * - @params {Function} onLoad a callback to handle the children 168 | */ 169 | var createImageTexture = exports.createImageTexture = function createImageTexture(url) { 170 | var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, 171 | _ref$name = _ref.name, 172 | name = _ref$name === undefined ? 'imageTexture' : _ref$name, 173 | _ref$onLoad = _ref.onLoad, 174 | onLoad = _ref$onLoad === undefined ? function (f) { 175 | return f; 176 | } : _ref$onLoad; 177 | 178 | var imgTexture = new _ImageTexture2.default(url, function () { 179 | var elm = addInDom(saveTextureName(name), imgTexture.image); 180 | 181 | // Drag Drop Event 182 | (0, _dragDrop2.default)('#' + elm.id, function (files) { 183 | // Read image from file data 184 | var reader = new FileReader(); 185 | reader.addEventListener('load', function (e) { 186 | var bytes = new Uint8Array(e.target.result); 187 | var blob = new Blob([bytes.buffer]); 188 | var URL = window.URL || window.webkitURL; 189 | // remove the old img and update the image 190 | elm.removeChild(imgTexture.image); 191 | // Update the image with a new path 192 | imgTexture.updateImg(URL.createObjectURL(blob), function () { 193 | elm.appendChild(imgTexture.image); 194 | onLoad(imgTexture); 195 | }); 196 | }); 197 | reader.addEventListener('error', function (err) { 198 | // TODO create true error 199 | console.error('FileReader error' + err); 200 | }); 201 | 202 | if (['image/png', 'image/jpg', 'image/jpeg'].indexOf(files[0].type) === -1) { 203 | console.log('FileUpdate error: The file is not at the good format'); 204 | return; 205 | } 206 | reader.readAsArrayBuffer(files[0]); 207 | }); 208 | 209 | onLoad(imgTexture); 210 | }); 211 | return imgTexture; 212 | }; 213 | 214 | /** 215 | * Create an texture based on a canvas 216 | * @params {Object} props with : 217 | * - @params {String} name 218 | * - @params {Number} width 219 | * - @params {Number} height 220 | * - @params {Function} onStart The drawing canvas function 221 | * - @params {Function} onUpdate Method called to update the canvas 222 | */ 223 | var createCanvasTexture = exports.createCanvasTexture = function createCanvasTexture() { 224 | var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, 225 | _ref2$name = _ref2.name, 226 | name = _ref2$name === undefined ? 'canvasTexture' : _ref2$name, 227 | _ref2$width = _ref2.width, 228 | width = _ref2$width === undefined ? 256 : _ref2$width, 229 | _ref2$height = _ref2.height, 230 | height = _ref2$height === undefined ? 256 : _ref2$height, 231 | _ref2$onStart = _ref2.onStart, 232 | onStart = _ref2$onStart === undefined ? function (f) { 233 | return f; 234 | } : _ref2$onStart, 235 | _ref2$onUpdate = _ref2.onUpdate, 236 | onUpdate = _ref2$onUpdate === undefined ? function (f) { 237 | return f; 238 | } : _ref2$onUpdate; 239 | 240 | var canvasTexture = new _CanvasTexture2.default({ width: width, height: height, onStart: onStart, onUpdate: onUpdate }); 241 | addInDom(saveTextureName(name), canvasTexture.canvas); 242 | return canvasTexture; 243 | }; 244 | 245 | /***/ }, 246 | /* 1 */ 247 | /***/ function(module, exports) { 248 | 249 | module.exports = __WEBPACK_EXTERNAL_MODULE_1__; 250 | 251 | /***/ }, 252 | /* 2 */ 253 | /***/ function(module, exports, __webpack_require__) { 254 | 255 | 'use strict'; 256 | 257 | Object.defineProperty(exports, "__esModule", { 258 | value: true 259 | }); 260 | exports.default = CanvasTexture; 261 | 262 | var _three = __webpack_require__(1); 263 | 264 | function CanvasTexture(_ref) { 265 | var _this = this; 266 | 267 | var _ref$width = _ref.width, 268 | width = _ref$width === undefined ? 256 : _ref$width, 269 | _ref$height = _ref.height, 270 | height = _ref$height === undefined ? 256 : _ref$height, 271 | _ref$onStart = _ref.onStart, 272 | onStart = _ref$onStart === undefined ? function (f) { 273 | return f; 274 | } : _ref$onStart, 275 | _ref$onUpdate = _ref.onUpdate, 276 | onUpdate = _ref$onUpdate === undefined ? function (f) { 277 | return f; 278 | } : _ref$onUpdate; 279 | 280 | this.width = width; 281 | this.height = height; 282 | 283 | this.canvas = document.createElement('canvas'); 284 | this.canvas.width = width; 285 | this.canvas.height = height; 286 | 287 | this.context = this.canvas.getContext('2d'); 288 | 289 | this.texture = new _three.Texture(this.canvas); 290 | this.texture.needsUpdate = true; 291 | 292 | this.material = new _three.MeshBasicMaterial({ 293 | map: this.texture, 294 | overdraw: true 295 | }); 296 | 297 | this.uniform = { type: 't', value: this.texture }; 298 | 299 | this.update = function () { 300 | onUpdate.apply(undefined, arguments); 301 | _this.texture.needsUpdate = true; 302 | }; 303 | 304 | onStart(this); 305 | } 306 | 307 | /***/ }, 308 | /* 3 */ 309 | /***/ function(module, exports, __webpack_require__) { 310 | 311 | 'use strict'; 312 | 313 | Object.defineProperty(exports, "__esModule", { 314 | value: true 315 | }); 316 | 317 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 318 | 319 | var _three = __webpack_require__(1); 320 | 321 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 322 | 323 | var ImageTexture = function () { 324 | function ImageTexture(url, callback) { 325 | var _this = this; 326 | 327 | _classCallCheck(this, ImageTexture); 328 | 329 | this.image = null; 330 | this.material = new _three.MeshBasicMaterial({ 331 | overdraw: true 332 | }); 333 | 334 | this.texture = new _three.TextureLoader().load(url, function (texture) { 335 | _this.image = texture.image; 336 | _this.material.needsUpdate = true; 337 | _this.material.map = _this.texture; 338 | callback(_this); 339 | }, function (xhr) { 340 | console.log(xhr.loaded / xhr.total * 100 + '% loaded'); 341 | }, function (xhr) { 342 | console.log('An error happened', xhr); 343 | }); 344 | 345 | this.uniform = { type: 't', value: this.texture }; 346 | } 347 | 348 | _createClass(ImageTexture, [{ 349 | key: 'updateImg', 350 | value: function updateImg(newPath, callback) { 351 | var _this2 = this; 352 | 353 | this.texture = new _three.TextureLoader().load(newPath, function (texture) { 354 | _this2.image = texture.image; 355 | _this2.material.needsUpdate = true; 356 | _this2.material.map = _this2.texture; 357 | _this2.uniform.value = _this2.texture; 358 | callback(_this2); 359 | }, function (xhr) { 360 | console.log(xhr.loaded / xhr.total * 100 + '% loaded'); 361 | }, function (xhr) { 362 | console.log('An error happened', xhr); 363 | }); 364 | } 365 | }]); 366 | 367 | return ImageTexture; 368 | }(); 369 | 370 | exports.default = ImageTexture; 371 | 372 | /***/ }, 373 | /* 4 */ 374 | /***/ function(module, exports, __webpack_require__) { 375 | 376 | exports = module.exports = __webpack_require__(5)(); 377 | // imports 378 | 379 | 380 | // module 381 | exports.push([module.id, ".ThreejsTextureTool-wrapper{position:fixed;top:5vh;bottom:5vh;left:0;max-height:90vh;margin:0;padding:8px;overflow-x:none;overflow-y:auto;list-style:none;font-family:Century Gothic,CenturyGothic,AppleGothic,sans-serif}.ThreejsTextureTool_hidden{display:none}.TextureTool{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;margin-top:8px}.TextureTool-hidden{display:none}.TextureTool-canvas{position:absolute}.TextureTool-window{position:relative;pointer-events:auto;background-color:rgba(0,0,0,.5);-webkit-transition:.2s;transition:.2s}.TextureTool-window:hover{background-color:#000}.TextureTool-window img{max-width:200px;width:100%}.TextureTool-window canvas{cursor:pointer}.TextureTool-window.drag:after{content:'+';display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;position:absolute;width:100%;height:100%;top:0;left:0;z-index:1;background-color:rgba(0,0,0,.5);color:#fff;font-size:3em}.TextureTool-button,.TextureTool-close{white-space:nowrap;text-overflow:ellipsis;border:none;font:inherit;line-height:normal;outline:none}.TextureTool-button{max-width:150px;padding:6px 16px;border-radius:2px;background-color:#084c61;color:#fff;overflow:hidden;font-size:.8em;-webkit-transition:.2s;transition:.2s;cursor:pointer;-webkit-transform:translateX(0);transform:translateX(0)}.TextureTool-button:hover{max-width:999px;background-color:#000;padding:6px 32px}.TextureTool-close{position:absolute;width:20px;height:20px;top:5px;left:5px;background-color:#084c61;color:#fff;border-radius:2px;-webkit-transition:.4s;transition:.4s}.TextureTool-close:after{content:'x';position:absolute;top:46%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.TextureTool-close:hover{width:22px;height:22px;top:4px;right:4px;cursor:pointer;padding:4px 8px;background-color:#000}", ""]); 382 | 383 | // exports 384 | 385 | 386 | /***/ }, 387 | /* 5 */ 388 | /***/ function(module, exports) { 389 | 390 | /* 391 | MIT License http://www.opensource.org/licenses/mit-license.php 392 | Author Tobias Koppers @sokra 393 | */ 394 | // css base code, injected by the css-loader 395 | module.exports = function() { 396 | var list = []; 397 | 398 | // return the list of modules as css string 399 | list.toString = function toString() { 400 | var result = []; 401 | for(var i = 0; i < this.length; i++) { 402 | var item = this[i]; 403 | if(item[2]) { 404 | result.push("@media " + item[2] + "{" + item[1] + "}"); 405 | } else { 406 | result.push(item[1]); 407 | } 408 | } 409 | return result.join(""); 410 | }; 411 | 412 | // import a list of modules into the list 413 | list.i = function(modules, mediaQuery) { 414 | if(typeof modules === "string") 415 | modules = [[null, modules, ""]]; 416 | var alreadyImportedModules = {}; 417 | for(var i = 0; i < this.length; i++) { 418 | var id = this[i][0]; 419 | if(typeof id === "number") 420 | alreadyImportedModules[id] = true; 421 | } 422 | for(i = 0; i < modules.length; i++) { 423 | var item = modules[i]; 424 | // skip already imported module 425 | // this implementation is not 100% perfect for weird media query combinations 426 | // when a module is imported multiple times with different media queries. 427 | // I hope this will never occur (Hey this way we have smaller bundles) 428 | if(typeof item[0] !== "number" || !alreadyImportedModules[item[0]]) { 429 | if(mediaQuery && !item[2]) { 430 | item[2] = mediaQuery; 431 | } else if(mediaQuery) { 432 | item[2] = "(" + item[2] + ") and (" + mediaQuery + ")"; 433 | } 434 | list.push(item); 435 | } 436 | } 437 | }; 438 | return list; 439 | }; 440 | 441 | 442 | /***/ }, 443 | /* 6 */ 444 | /***/ function(module, exports, __webpack_require__) { 445 | 446 | module.exports = dragDrop 447 | 448 | var flatten = __webpack_require__(7) 449 | var parallel = __webpack_require__(9) 450 | 451 | function dragDrop (elem, listeners) { 452 | if (typeof elem === 'string') { 453 | var selector = elem 454 | elem = window.document.querySelector(elem) 455 | if (!elem) { 456 | throw new Error('"' + selector + '" does not match any HTML elements') 457 | } 458 | } 459 | 460 | if (!elem) { 461 | throw new Error('"' + elem + '" is not a valid HTML element') 462 | } 463 | 464 | if (typeof listeners === 'function') { 465 | listeners = { onDrop: listeners } 466 | } 467 | 468 | var timeout 469 | 470 | elem.addEventListener('dragenter', onDragEnter, false) 471 | elem.addEventListener('dragover', onDragOver, false) 472 | elem.addEventListener('dragleave', onDragLeave, false) 473 | elem.addEventListener('drop', onDrop, false) 474 | 475 | // Function to remove drag-drop listeners 476 | return function remove () { 477 | removeDragClass() 478 | elem.removeEventListener('dragenter', onDragEnter, false) 479 | elem.removeEventListener('dragover', onDragOver, false) 480 | elem.removeEventListener('dragleave', onDragLeave, false) 481 | elem.removeEventListener('drop', onDrop, false) 482 | } 483 | 484 | function onDragEnter (e) { 485 | if (listeners.onDragEnter) { 486 | listeners.onDragEnter(e) 487 | } 488 | 489 | // Prevent event 490 | e.stopPropagation() 491 | e.preventDefault() 492 | return false 493 | } 494 | 495 | function onDragOver (e) { 496 | e.stopPropagation() 497 | e.preventDefault() 498 | if (e.dataTransfer.items) { 499 | // Only add "drag" class when `items` contains items that are able to be 500 | // handled by the registered listeners (files vs. text) 501 | var items = toArray(e.dataTransfer.items) 502 | var fileItems = items.filter(function (item) { return item.kind === 'file' }) 503 | var textItems = items.filter(function (item) { return item.kind === 'string' }) 504 | 505 | if (fileItems.length === 0 && !listeners.onDropText) return 506 | if (textItems.length === 0 && !listeners.onDrop) return 507 | if (fileItems.length === 0 && textItems.length === 0) return 508 | } 509 | 510 | elem.classList.add('drag') 511 | clearTimeout(timeout) 512 | 513 | if (listeners.onDragOver) { 514 | listeners.onDragOver(e) 515 | } 516 | 517 | e.dataTransfer.dropEffect = 'copy' 518 | return false 519 | } 520 | 521 | function onDragLeave (e) { 522 | e.stopPropagation() 523 | e.preventDefault() 524 | 525 | if (listeners.onDragLeave) { 526 | listeners.onDragLeave(e) 527 | } 528 | 529 | clearTimeout(timeout) 530 | timeout = setTimeout(removeDragClass, 50) 531 | 532 | return false 533 | } 534 | 535 | function onDrop (e) { 536 | e.stopPropagation() 537 | e.preventDefault() 538 | 539 | if (listeners.onDragLeave) { 540 | listeners.onDragLeave(e) 541 | } 542 | 543 | clearTimeout(timeout) 544 | removeDragClass() 545 | 546 | var pos = { 547 | x: e.clientX, 548 | y: e.clientY 549 | } 550 | 551 | // text drop support 552 | var text = e.dataTransfer.getData('text') 553 | if (text && listeners.onDropText) { 554 | listeners.onDropText(text, pos) 555 | } 556 | 557 | // file drop support 558 | if (e.dataTransfer.items) { 559 | // Handle directories in Chrome using the proprietary FileSystem API 560 | var items = toArray(e.dataTransfer.items).filter(function (item) { 561 | return item.kind === 'file' 562 | }) 563 | 564 | if (items.length === 0) return 565 | 566 | parallel(items.map(function (item) { 567 | return function (cb) { 568 | processEntry(item.webkitGetAsEntry(), cb) 569 | } 570 | }), function (err, results) { 571 | // This catches permission errors with file:// in Chrome. This should never 572 | // throw in production code, so the user does not need to use try-catch. 573 | if (err) throw err 574 | if (listeners.onDrop) { 575 | listeners.onDrop(flatten(results), pos) 576 | } 577 | }) 578 | } else { 579 | var files = toArray(e.dataTransfer.files) 580 | 581 | if (files.length === 0) return 582 | 583 | files.forEach(function (file) { 584 | file.fullPath = '/' + file.name 585 | }) 586 | 587 | if (listeners.onDrop) { 588 | listeners.onDrop(files, pos) 589 | } 590 | } 591 | 592 | return false 593 | } 594 | 595 | function removeDragClass () { 596 | elem.classList.remove('drag') 597 | } 598 | } 599 | 600 | function processEntry (entry, cb) { 601 | var entries = [] 602 | 603 | if (entry.isFile) { 604 | entry.file(function (file) { 605 | file.fullPath = entry.fullPath // preserve pathing for consumer 606 | cb(null, file) 607 | }, function (err) { 608 | cb(err) 609 | }) 610 | } else if (entry.isDirectory) { 611 | var reader = entry.createReader() 612 | readEntries() 613 | } 614 | 615 | function readEntries () { 616 | reader.readEntries(function (entries_) { 617 | if (entries_.length > 0) { 618 | entries = entries.concat(toArray(entries_)) 619 | readEntries() // continue reading entries until `readEntries` returns no more 620 | } else { 621 | doneEntries() 622 | } 623 | }) 624 | } 625 | 626 | function doneEntries () { 627 | parallel(entries.map(function (entry) { 628 | return function (cb) { 629 | processEntry(entry, cb) 630 | } 631 | }), cb) 632 | } 633 | } 634 | 635 | function toArray (list) { 636 | return Array.prototype.slice.call(list || [], 0) 637 | } 638 | 639 | 640 | /***/ }, 641 | /* 7 */ 642 | /***/ function(module, exports) { 643 | 644 | module.exports = function flatten(list, depth) { 645 | depth = (typeof depth == 'number') ? depth : Infinity; 646 | 647 | if (!depth) { 648 | if (Array.isArray(list)) { 649 | return list.map(function(i) { return i; }); 650 | } 651 | return list; 652 | } 653 | 654 | return _flatten(list, 1); 655 | 656 | function _flatten(list, d) { 657 | return list.reduce(function (acc, item) { 658 | if (Array.isArray(item) && d < depth) { 659 | return acc.concat(_flatten(item, d + 1)); 660 | } 661 | else { 662 | return acc.concat(item); 663 | } 664 | }, []); 665 | } 666 | }; 667 | 668 | 669 | /***/ }, 670 | /* 8 */ 671 | /***/ function(module, exports) { 672 | 673 | // shim for using process in browser 674 | var process = module.exports = {}; 675 | 676 | // cached from whatever global is present so that test runners that stub it 677 | // don't break things. But we need to wrap it in a try catch in case it is 678 | // wrapped in strict mode code which doesn't define any globals. It's inside a 679 | // function because try/catches deoptimize in certain engines. 680 | 681 | var cachedSetTimeout; 682 | var cachedClearTimeout; 683 | 684 | function defaultSetTimout() { 685 | throw new Error('setTimeout has not been defined'); 686 | } 687 | function defaultClearTimeout () { 688 | throw new Error('clearTimeout has not been defined'); 689 | } 690 | (function () { 691 | try { 692 | if (typeof setTimeout === 'function') { 693 | cachedSetTimeout = setTimeout; 694 | } else { 695 | cachedSetTimeout = defaultSetTimout; 696 | } 697 | } catch (e) { 698 | cachedSetTimeout = defaultSetTimout; 699 | } 700 | try { 701 | if (typeof clearTimeout === 'function') { 702 | cachedClearTimeout = clearTimeout; 703 | } else { 704 | cachedClearTimeout = defaultClearTimeout; 705 | } 706 | } catch (e) { 707 | cachedClearTimeout = defaultClearTimeout; 708 | } 709 | } ()) 710 | function runTimeout(fun) { 711 | if (cachedSetTimeout === setTimeout) { 712 | //normal enviroments in sane situations 713 | return setTimeout(fun, 0); 714 | } 715 | // if setTimeout wasn't available but was latter defined 716 | if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { 717 | cachedSetTimeout = setTimeout; 718 | return setTimeout(fun, 0); 719 | } 720 | try { 721 | // when when somebody has screwed with setTimeout but no I.E. maddness 722 | return cachedSetTimeout(fun, 0); 723 | } catch(e){ 724 | try { 725 | // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally 726 | return cachedSetTimeout.call(null, fun, 0); 727 | } catch(e){ 728 | // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error 729 | return cachedSetTimeout.call(this, fun, 0); 730 | } 731 | } 732 | 733 | 734 | } 735 | function runClearTimeout(marker) { 736 | if (cachedClearTimeout === clearTimeout) { 737 | //normal enviroments in sane situations 738 | return clearTimeout(marker); 739 | } 740 | // if clearTimeout wasn't available but was latter defined 741 | if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { 742 | cachedClearTimeout = clearTimeout; 743 | return clearTimeout(marker); 744 | } 745 | try { 746 | // when when somebody has screwed with setTimeout but no I.E. maddness 747 | return cachedClearTimeout(marker); 748 | } catch (e){ 749 | try { 750 | // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally 751 | return cachedClearTimeout.call(null, marker); 752 | } catch (e){ 753 | // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. 754 | // Some versions of I.E. have different rules for clearTimeout vs setTimeout 755 | return cachedClearTimeout.call(this, marker); 756 | } 757 | } 758 | 759 | 760 | 761 | } 762 | var queue = []; 763 | var draining = false; 764 | var currentQueue; 765 | var queueIndex = -1; 766 | 767 | function cleanUpNextTick() { 768 | if (!draining || !currentQueue) { 769 | return; 770 | } 771 | draining = false; 772 | if (currentQueue.length) { 773 | queue = currentQueue.concat(queue); 774 | } else { 775 | queueIndex = -1; 776 | } 777 | if (queue.length) { 778 | drainQueue(); 779 | } 780 | } 781 | 782 | function drainQueue() { 783 | if (draining) { 784 | return; 785 | } 786 | var timeout = runTimeout(cleanUpNextTick); 787 | draining = true; 788 | 789 | var len = queue.length; 790 | while(len) { 791 | currentQueue = queue; 792 | queue = []; 793 | while (++queueIndex < len) { 794 | if (currentQueue) { 795 | currentQueue[queueIndex].run(); 796 | } 797 | } 798 | queueIndex = -1; 799 | len = queue.length; 800 | } 801 | currentQueue = null; 802 | draining = false; 803 | runClearTimeout(timeout); 804 | } 805 | 806 | process.nextTick = function (fun) { 807 | var args = new Array(arguments.length - 1); 808 | if (arguments.length > 1) { 809 | for (var i = 1; i < arguments.length; i++) { 810 | args[i - 1] = arguments[i]; 811 | } 812 | } 813 | queue.push(new Item(fun, args)); 814 | if (queue.length === 1 && !draining) { 815 | runTimeout(drainQueue); 816 | } 817 | }; 818 | 819 | // v8 likes predictible objects 820 | function Item(fun, array) { 821 | this.fun = fun; 822 | this.array = array; 823 | } 824 | Item.prototype.run = function () { 825 | this.fun.apply(null, this.array); 826 | }; 827 | process.title = 'browser'; 828 | process.browser = true; 829 | process.env = {}; 830 | process.argv = []; 831 | process.version = ''; // empty string to avoid regexp issues 832 | process.versions = {}; 833 | 834 | function noop() {} 835 | 836 | process.on = noop; 837 | process.addListener = noop; 838 | process.once = noop; 839 | process.off = noop; 840 | process.removeListener = noop; 841 | process.removeAllListeners = noop; 842 | process.emit = noop; 843 | 844 | process.binding = function (name) { 845 | throw new Error('process.binding is not supported'); 846 | }; 847 | 848 | process.cwd = function () { return '/' }; 849 | process.chdir = function (dir) { 850 | throw new Error('process.chdir is not supported'); 851 | }; 852 | process.umask = function() { return 0; }; 853 | 854 | 855 | /***/ }, 856 | /* 9 */ 857 | /***/ function(module, exports, __webpack_require__) { 858 | 859 | /* WEBPACK VAR INJECTION */(function(process) {module.exports = function (tasks, cb) { 860 | var results, pending, keys 861 | var isSync = true 862 | 863 | if (Array.isArray(tasks)) { 864 | results = [] 865 | pending = tasks.length 866 | } else { 867 | keys = Object.keys(tasks) 868 | results = {} 869 | pending = keys.length 870 | } 871 | 872 | function done (err) { 873 | function end () { 874 | if (cb) cb(err, results) 875 | cb = null 876 | } 877 | if (isSync) process.nextTick(end) 878 | else end() 879 | } 880 | 881 | function each (i, err, result) { 882 | results[i] = result 883 | if (--pending === 0 || err) { 884 | done(err) 885 | } 886 | } 887 | 888 | if (!pending) { 889 | // empty 890 | done(null) 891 | } else if (keys) { 892 | // object 893 | keys.forEach(function (key) { 894 | tasks[key](function (err, result) { each(key, err, result) }) 895 | }) 896 | } else { 897 | // array 898 | tasks.forEach(function (task, i) { 899 | task(function (err, result) { each(i, err, result) }) 900 | }) 901 | } 902 | 903 | isSync = false 904 | } 905 | 906 | /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(8))) 907 | 908 | /***/ }, 909 | /* 10 */ 910 | /***/ function(module, exports, __webpack_require__) { 911 | 912 | /* 913 | MIT License http://www.opensource.org/licenses/mit-license.php 914 | Author Tobias Koppers @sokra 915 | */ 916 | var stylesInDom = {}, 917 | memoize = function(fn) { 918 | var memo; 919 | return function () { 920 | if (typeof memo === "undefined") memo = fn.apply(this, arguments); 921 | return memo; 922 | }; 923 | }, 924 | isOldIE = memoize(function() { 925 | return /msie [6-9]\b/.test(window.navigator.userAgent.toLowerCase()); 926 | }), 927 | getHeadElement = memoize(function () { 928 | return document.head || document.getElementsByTagName("head")[0]; 929 | }), 930 | singletonElement = null, 931 | singletonCounter = 0, 932 | styleElementsInsertedAtTop = []; 933 | 934 | module.exports = function(list, options) { 935 | if(false) { 936 | if(typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment"); 937 | } 938 | 939 | options = options || {}; 940 | // Force single-tag solution on IE6-9, which has a hard limit on the # of