├── .gitignore
├── .prettierignore
├── static
├── favicon.ico
├── textures
│ └── square.png
└── styles
│ └── main.css
├── .prettierrc
├── webpack.config.js
├── src
├── modal.js
├── webgl.js
└── index.js
├── package.json
├── README.md
└── index.html
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | public/*
2 |
--------------------------------------------------------------------------------
/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aakatev/three-js-webpack/HEAD/static/favicon.ico
--------------------------------------------------------------------------------
/static/textures/square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aakatev/three-js-webpack/HEAD/static/textures/square.png
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "es5",
3 | "tabWidth": 2,
4 | "semi": false,
5 | "singleQuote": true
6 | }
7 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | module.exports = {
4 | mode: 'production',
5 | entry: './src/index.js',
6 | output: {
7 | path: path.resolve(__dirname, 'public'),
8 | filename: 'bundle.js',
9 | },
10 | performance: {
11 | maxEntrypointSize: 1024000,
12 | maxAssetSize: 1024000
13 | },
14 | devServer: {
15 | publicPath: '/public/',
16 | compress: true,
17 | port: 9000,
18 | hot: true,
19 | },
20 | }
21 |
--------------------------------------------------------------------------------
/src/modal.js:
--------------------------------------------------------------------------------
1 | var modal = document.getElementById('info-modal')
2 | var btn = document.getElementById('open-modal-btn')
3 | var span = document.getElementsByClassName('close-modal-btn')[0]
4 |
5 | btn.onclick = function () {
6 | modal.style.display = 'block'
7 | }
8 |
9 | span.onclick = function () {
10 | modal.style.display = 'none'
11 | }
12 |
13 | window.onclick = function (event) {
14 | if (event.target == modal) {
15 | modal.style.display = 'none'
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "three-npm",
3 | "version": "1.0.1",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "build": "webpack --config webpack.config.js",
8 | "start": "webpack-dev-server --mode development",
9 | "format": "prettier --write '**/*.js'"
10 | },
11 | "keywords": [],
12 | "author": "",
13 | "license": "ISC",
14 | "dependencies": {
15 | "three": "^0.115.0"
16 | },
17 | "devDependencies": {
18 | "prettier": "^2.0.4",
19 | "webpack": "^4.42.1",
20 | "webpack-cli": "^3.3.11",
21 | "webpack-dev-server": "^3.11.0"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # three-js-webpack
2 |
3 | Starter project for Three.JS. Configured with Webpack 4 as a bundler.
4 |
5 | Great and easy way to bootstrap your Three.JS project.
6 |
7 | ## Development
8 |
9 | Clone the project and install dependencies:
10 |
11 | ```bash
12 | git clone https://github.com/aakatev/three-js-webpack.git
13 | npm i
14 | ```
15 |
16 | Start webpack development server:
17 |
18 | ```bash
19 | npm run start
20 | ```
21 |
22 | Webpack configuration is located in [`webpack.config.js`](webpack.config.js).
23 |
24 | ## Deployment on GitHub Pages
25 |
26 | **Works with any other static website hosting too.**
27 |
28 | Bundle your code, and push it in your repo:
29 |
30 | ```bash
31 | npm run build
32 | git add
33 | git commit -m"Deploying on GitHub Pages"
34 | git push
35 | ```
36 |
37 | ## Extra
38 |
39 | The code can be formated with prettier:
40 |
41 | ```bash
42 | npm run format
43 | ```
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Three.JS-Webpack Boilerplate
9 |
10 |
11 |
12 |
13 | menu
14 |
15 |
16 |
17 |
×
18 |
Welcome to my Three.JS-Webpack boilerplate!
19 |
It is a great and easy way to bootstrap Three.Js project.
20 |
To get started clone the repo git clone https://github.com/aakatev/three-js-webpack.git.
21 |
For more information, navigate to the project GitHub page!
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/webgl.js:
--------------------------------------------------------------------------------
1 | var WEBGL = {
2 | isWebGLAvailable: function () {
3 | try {
4 | var canvas = document.createElement('canvas')
5 | return !!(
6 | window.WebGLRenderingContext &&
7 | (canvas.getContext('webgl') || canvas.getContext('experimental-webgl'))
8 | )
9 | } catch (e) {
10 | return false
11 | }
12 | },
13 |
14 | isWebGL2Available: function () {
15 | try {
16 | var canvas = document.createElement('canvas')
17 | return !!(window.WebGL2RenderingContext && canvas.getContext('webgl2'))
18 | } catch (e) {
19 | return false
20 | }
21 | },
22 |
23 | getWebGLErrorMessage: function () {
24 | return this.getErrorMessage(1)
25 | },
26 |
27 | getWebGL2ErrorMessage: function () {
28 | return this.getErrorMessage(2)
29 | },
30 |
31 | getErrorMessage: function (version) {
32 | var names = {
33 | 1: 'WebGL',
34 | 2: 'WebGL 2',
35 | }
36 |
37 | var contexts = {
38 | 1: window.WebGLRenderingContext,
39 | 2: window.WebGL2RenderingContext,
40 | }
41 |
42 | var message =
43 | 'Your $0 does not seem to support $1'
44 |
45 | var element = document.createElement('div')
46 | element.id = 'webgl-error-message'
47 |
48 | if (contexts[version]) {
49 | message = message.replace('$0', 'graphics card')
50 | } else {
51 | message = message.replace('$0', 'browser')
52 | }
53 |
54 | message = message.replace('$1', names[version])
55 |
56 | element.innerHTML = message
57 |
58 | return element
59 | },
60 | }
61 |
62 | module.exports = { WEBGL }
63 |
--------------------------------------------------------------------------------
/static/styles/main.css:
--------------------------------------------------------------------------------
1 | @import "https://fonts.googleapis.com/icon?family=Material+Icons";
2 |
3 | body {
4 | font-family: monospace;
5 | margin: 0;
6 | font-size: 1.3rem;
7 | font-weight: normal;
8 | }
9 |
10 | canvas {
11 | display: block;
12 | }
13 |
14 | code {
15 | font-family: Consolas,"courier new";
16 | color: crimson;
17 | background-color: #f1f1f1;
18 | padding: 2px;
19 | }
20 |
21 | p {
22 | overflow: scroll;
23 | }
24 |
25 | .float {
26 | position:fixed;
27 | width:60px;
28 | height:60px;
29 | bottom:40px;
30 | right:40px;
31 | background-color:#0C9;
32 | color:#FFF;
33 | border-radius:50px;
34 | text-align:center;
35 | box-shadow: 2px 2px 3px #999;
36 | }
37 |
38 | .float:hover {
39 | cursor: pointer;
40 | }
41 |
42 | .icon-float{
43 | margin-top:18px;
44 | }
45 |
46 | .modal {
47 | display: none; /* Hidden by default */
48 | position: fixed; /* Stay in place */
49 | z-index: 1; /* Sit on top */
50 | left: 0;
51 | top: 0;
52 | width: 100%; /* Full width */
53 | height: 100%; /* Full height */
54 | overflow: auto; /* Enable scroll if needed */
55 | background-color: rgb(0,0,0); /* Fallback color */
56 | background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
57 | }
58 |
59 | /* Modal Content/Box */
60 | .modal-content {
61 | background-color: #fefefe;
62 | margin: 15% auto; /* 15% from the top and centered */
63 | padding: 20px;
64 | border: 1px solid #888;
65 | width: 80%; /* Could be more or less, depending on screen size */
66 | }
67 |
68 | .close-modal-btn {
69 | color: #aaa;
70 | float: right;
71 | font-size: 28px;
72 | font-weight: bold;
73 | }
74 |
75 | .close-modal-btn:hover,
76 | .close-modal-btn:focus {
77 | color: black;
78 | text-decoration: none;
79 | cursor: pointer;
80 | }
81 |
82 | #webgl-error-message {
83 | text-align: center;
84 | background: #fff;
85 | color: #000;
86 | padding: 1.5em;
87 | width: 400px;
88 | margin: 5em auto 0;
89 | }
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import * as THREE from 'three'
2 | import { WEBGL } from './webgl'
3 | import './modal'
4 |
5 | if (WEBGL.isWebGLAvailable()) {
6 | var camera, scene, renderer
7 | var plane
8 | var mouse,
9 | raycaster,
10 | isShiftDown = false
11 |
12 | var rollOverMesh, rollOverMaterial
13 | var cubeGeo, cubeMaterial
14 |
15 | var objects = []
16 |
17 | init()
18 | render()
19 |
20 | function init() {
21 | camera = new THREE.PerspectiveCamera(
22 | 45,
23 | window.innerWidth / window.innerHeight,
24 | 1,
25 | 10000
26 | )
27 | camera.position.set(500, 800, 1300)
28 | camera.lookAt(0, 0, 0)
29 |
30 | scene = new THREE.Scene()
31 | scene.background = new THREE.Color(0xf0f0f0)
32 |
33 | var rollOverGeo = new THREE.BoxBufferGeometry(50, 50, 50)
34 | rollOverMaterial = new THREE.MeshBasicMaterial({
35 | color: 0xff0000,
36 | opacity: 0.5,
37 | transparent: true,
38 | })
39 | rollOverMesh = new THREE.Mesh(rollOverGeo, rollOverMaterial)
40 | scene.add(rollOverMesh)
41 |
42 | cubeGeo = new THREE.BoxBufferGeometry(50, 50, 50)
43 | cubeMaterial = new THREE.MeshLambertMaterial({
44 | color: 0xfeb74c,
45 | map: new THREE.TextureLoader().load('static/textures/square.png'),
46 | })
47 |
48 | var gridHelper = new THREE.GridHelper(1000, 20)
49 | scene.add(gridHelper)
50 |
51 | raycaster = new THREE.Raycaster()
52 | mouse = new THREE.Vector2()
53 |
54 | var geometry = new THREE.PlaneBufferGeometry(1000, 1000)
55 | geometry.rotateX(-Math.PI / 2)
56 |
57 | plane = new THREE.Mesh(
58 | geometry,
59 | new THREE.MeshBasicMaterial({ visible: false })
60 | )
61 | scene.add(plane)
62 |
63 | objects.push(plane)
64 |
65 | var ambientLight = new THREE.AmbientLight(0x606060)
66 | scene.add(ambientLight)
67 |
68 | var directionalLight = new THREE.DirectionalLight(0xffffff)
69 | directionalLight.position.set(1, 0.75, 0.5).normalize()
70 | scene.add(directionalLight)
71 |
72 | renderer = new THREE.WebGLRenderer({ antialias: true })
73 | renderer.setPixelRatio(window.devicePixelRatio)
74 | renderer.setSize(window.innerWidth, window.innerHeight)
75 | document.body.appendChild(renderer.domElement)
76 |
77 | document.addEventListener('mousemove', onDocumentMouseMove, false)
78 | document.addEventListener('mousedown', onDocumentMouseDown, false)
79 | document.addEventListener('keydown', onDocumentKeyDown, false)
80 | document.addEventListener('keyup', onDocumentKeyUp, false)
81 | window.addEventListener('resize', onWindowResize, false)
82 | }
83 |
84 | function onWindowResize() {
85 | camera.aspect = window.innerWidth / window.innerHeight
86 | camera.updateProjectionMatrix()
87 |
88 | renderer.setSize(window.innerWidth, window.innerHeight)
89 | }
90 |
91 | function onDocumentMouseMove(event) {
92 | event.preventDefault()
93 |
94 | mouse.set(
95 | (event.clientX / window.innerWidth) * 2 - 1,
96 | -(event.clientY / window.innerHeight) * 2 + 1
97 | )
98 |
99 | raycaster.setFromCamera(mouse, camera)
100 |
101 | var intersects = raycaster.intersectObjects(objects)
102 |
103 | if (intersects.length > 0) {
104 | var intersect = intersects[0]
105 |
106 | rollOverMesh.position.copy(intersect.point).add(intersect.face.normal)
107 | rollOverMesh.position
108 | .divideScalar(50)
109 | .floor()
110 | .multiplyScalar(50)
111 | .addScalar(25)
112 | }
113 |
114 | render()
115 | }
116 |
117 | function onDocumentMouseDown(event) {
118 | event.preventDefault()
119 |
120 | mouse.set(
121 | (event.clientX / window.innerWidth) * 2 - 1,
122 | -(event.clientY / window.innerHeight) * 2 + 1
123 | )
124 |
125 | raycaster.setFromCamera(mouse, camera)
126 |
127 | var intersects = raycaster.intersectObjects(objects)
128 |
129 | if (intersects.length > 0) {
130 | var intersect = intersects[0]
131 |
132 | if (isShiftDown) {
133 | if (intersect.object !== plane) {
134 | scene.remove(intersect.object)
135 |
136 | objects.splice(objects.indexOf(intersect.object), 1)
137 | }
138 |
139 | } else {
140 | var voxel = new THREE.Mesh(cubeGeo, cubeMaterial)
141 | voxel.position.copy(intersect.point).add(intersect.face.normal)
142 | voxel.position.divideScalar(50).floor().multiplyScalar(50).addScalar(25)
143 | scene.add(voxel)
144 |
145 | objects.push(voxel)
146 | }
147 |
148 | render()
149 | }
150 | }
151 |
152 | function onDocumentKeyDown(event) {
153 | switch (event.keyCode) {
154 | case 16:
155 | isShiftDown = true
156 | break
157 | }
158 | }
159 |
160 | function onDocumentKeyUp(event) {
161 | switch (event.keyCode) {
162 | case 16:
163 | isShiftDown = false
164 | break
165 | }
166 | }
167 |
168 | function render() {
169 | renderer.render(scene, camera)
170 | }
171 | } else {
172 | var warning = WEBGL.getWebGLErrorMessage()
173 | document.body.appendChild(warning)
174 | }
175 |
--------------------------------------------------------------------------------