Repository: ektogamat/threejs-andy-bolierplate Branch: main Commit: 6ce1c237e63b Files: 15 Total size: 17.0 KB Directory structure: gitextract_t8_s_4ws/ ├── .gitignore ├── 3d files/ │ └── starterscene.blend ├── LICENSE ├── bundler/ │ ├── webpack.common.js │ ├── webpack.dev.js │ └── webpack.prod.js ├── package.json ├── readme.md ├── src/ │ ├── debug.js │ ├── index.html │ ├── main.css │ └── script.js └── static/ ├── .gitkeep ├── favicon/ │ └── site.webmanifest └── models/ └── gltf/ └── starter-scene.glb ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ node_modules ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2022 Anderson Mancini Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: bundler/webpack.common.js ================================================ const CopyWebpackPlugin = require('copy-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCSSExtractPlugin = require('mini-css-extract-plugin') const path = require('path') module.exports = { entry: path.resolve(__dirname, '../src/script.js'), output: { hashFunction: 'xxhash64', filename: 'bundle.[contenthash].js', path: path.resolve(__dirname, '../dist') }, devtool: 'source-map', plugins: [ new CopyWebpackPlugin({ patterns: [ { from: path.resolve(__dirname, '../static') } ] }), new HtmlWebpackPlugin({ template: path.resolve(__dirname, '../src/index.html'), minify: true }), new MiniCSSExtractPlugin() ], module: { rules: [ // HTML { test: /\.(html)$/, use: [ 'html-loader' ] }, // JS { test: /\.js$/, exclude: /node_modules/, use: [ 'babel-loader' ] }, // CSS { test: /\.css$/, use: [ MiniCSSExtractPlugin.loader, 'css-loader' ] }, // Images { test: /\.(jpg|png|gif|svg)$/, type: 'asset/resource', generator: { filename: 'assets/images/[hash][ext]' } }, // Fonts { test: /\.(ttf|eot|woff|woff2)$/, type: 'asset/resource', generator: { filename: 'assets/fonts/[hash][ext]' } }, // Shaders { test: /\.(glsl|vs|fs|vert|frag)$/, type: 'asset/source', generator: { filename: 'assets/images/[hash][ext]' } } ] } } ================================================ FILE: bundler/webpack.dev.js ================================================ const path = require('path') const { merge } = require('webpack-merge') const commonConfiguration = require('./webpack.common.js') const ip = require('internal-ip') const portFinderSync = require('portfinder-sync') const infoColor = (_message) => { return `\u001b[1m\u001b[34m${_message}\u001b[39m\u001b[22m` } module.exports = merge( commonConfiguration, { stats: 'errors-warnings', mode: 'development', devServer: { host: 'local-ip', port: portFinderSync.getPort(8080), open: true, https: false, allowedHosts: 'all', hot: false, watchFiles: ['src/**', 'static/**'], static: { watch: true, directory: path.join(__dirname, '../static') }, client: { logging: 'none', overlay: true, progress: false }, onAfterSetupMiddleware: function(devServer) { const port = devServer.options.port const https = devServer.options.https ? 's' : '' const localIp = ip.v4.sync() const domain1 = `http${https}://${localIp}:${port}` const domain2 = `http${https}://localhost:${port}` console.log(`Project running at:\n - ${infoColor(domain1)}\n - ${infoColor(domain2)}`) } } } ) ================================================ FILE: bundler/webpack.prod.js ================================================ const { merge } = require('webpack-merge') const commonConfiguration = require('./webpack.common.js') const { CleanWebpackPlugin } = require('clean-webpack-plugin') module.exports = merge( commonConfiguration, { mode: 'production', devtool: false, plugins: [ new CleanWebpackPlugin() ] } ) ================================================ FILE: package.json ================================================ { "repository": "#", "license": "UNLICENSED", "author": "Anderson Mancini (https://twitter.com/Andersonmancini)", "scripts": { "build": "webpack --config ./bundler/webpack.prod.js", "dev": "webpack serve --config ./bundler/webpack.dev.js", "deploy": "vercel --prod" }, "dependencies": { "@babel/core": "^7.15.8", "@babel/preset-env": "^7.15.8", "babel-loader": "^8.2.3", "clean-webpack-plugin": "^4.0.0", "copy-webpack-plugin": "^9.0.1", "css-loader": "^6.5.0", "file-loader": "^6.2.0", "html-loader": "^3.0.0", "html-webpack-plugin": "^5.5.0", "lil-gui": "^0.11.0", "mini-css-extract-plugin": "^2.4.3", "portfinder-sync": "0.0.2", "raw-loader": "^4.0.2", "style-loader": "^3.3.1", "three": "^0.134.0", "webpack": "^5.60.0", "webpack-cli": "^4.9.1", "webpack-dev-server": "^4.4.0", "webpack-merge": "^5.8.0" } } ================================================ FILE: readme.md ================================================ # Ektogamat ThreeJS Boilerplate

by Anderson Mancini

# Introduction This is a three.js starter project with only 120 lines. But why create another threejs boilerplate when there are already so many out there? When I started, I had a hard time finding something simple and written in VanillaJS that would allow, with just a few lines of code, to import a model and have something functional on the screen. Also, most of the examples that exist in the threejs documentation need to be in this framework for them to work just by copying and pasting the code. So I decided to share this starter project that tries to make things a little easier. This uses a very basic setup of Node.js to make it easier to install project dependencies and also webpack to make development easier. I think this would be very useful for web developers, who are trying to get started with threejs. In the source file, which is all documented, you can understand the basic structure of a project in threejs. Resources: [Threejs](https://threejs.org/), [WebGL](https://github.com/KhronosGroup/WebGL), [webpack](https://webpack.js.org/) , [Babel](https://babeljs.io/ ), [ESLint](https://eslint.org/) # Show, don't tell Here you can see a video on how to use this # Getting Started Download and install Node.js on your computer (https://nodejs.org/en/download/). Then, open VSCODE, drag the project folder to it. Open VSCODE terminal and install dependencies (you need to do this only in the first time) ``` npm install ``` Run this command in your terminal to open a local server at localhost:8080 ``` npm run dev ``` # Debug interface You can enable a debug interface by getting the contents of "debug.js" file and place it in the end of the main file. This will give you some interface to change things like colors and light position, which can be very useful when you change the model to something else. # Some projects developed using this - https://neotix.com.br/vitrine-virtual/ - https://rava-cycle-neotix.glitch.me/ - https://vitamine-se.glitch.me/ - https://neotix.com.br/promo_havaianas/ - https://realestate-neotix.vercel.app/ - https://windland-neotix.vercel.app/ - https://dna-chain.vercel.app/ - http://houses-neotix.vercel.app/ - https://fgr-implantacao.vercel.app/ - https://metaverse-event.vercel.app/ # Notes Would be really appreciated if you are willing to give me a star here on GitHub 🎉 or buy me a coffee ☕ https://www.buymeacoffee.com/andersonmancini. The money will be used to produce more content about threejs or to buy new courses. I plan to add more functionality to this over time, like the ability to use advanced controls, particle systems, etc. If you have any questions or somewhere I can do better, welcome to send an e-mail to me 🙏 ================================================ FILE: src/debug.js ================================================ import { GUI } from 'three/examples/jsm/libs/dat.gui.module.js' const gui = new GUI() // create parameters for GUI var params = {color: sunLight.color.getHex(), color2: ambient.color.getHex(), color3: scene.background.getHex()} // create a function to be called by GUI const update = function () { var colorObj = new THREE.Color( params.color ) var colorObj2 = new THREE.Color( params.color2 ) var colorObj3 = new THREE.Color( params.color3 ) sunLight.color.set(colorObj) ambient.color.set(colorObj2) scene.background.set(colorObj3) } ////////////////////////////////////////////////// //// GUI CONFIG gui.add(sunLight, 'intensity').min(0).max(10).step(0.0001).name('Dir intensity') gui.add(sunLight.position, 'x').min(-100).max(100).step(0.00001).name('Dir X pos') gui.add(sunLight.position, 'y').min(0).max(100).step(0.00001).name('Dir Y pos') gui.add(sunLight.position, 'z').min(-100).max(100).step(0.00001).name('Dir Z pos') gui.addColor(params,'color').name('Dir color').onChange(update) gui.addColor(params,'color2').name('Amb color').onChange(update) gui.add(ambient, 'intensity').min(0).max(10).step(0.001).name('Amb intensity') gui.addColor(params,'color3').name('BG color').onChange(update) ////////////////////////////////////////////////// //// ON MOUSE MOVE TO GET CAMERA POSITION document.addEventListener('mousemove', (event) => { event.preventDefault() console.log(camera.position) }, false) ================================================ FILE: src/index.html ================================================ Ektogamat Threejs Starter Boilerplate ================================================ FILE: src/main.css ================================================ body, html { margin: 0; padding: 0; overscroll-behavior: none; overflow: hidden; } ================================================ FILE: src/script.js ================================================ ///////////////////////////////////////////////////////////////////////// ///// IMPORT import './main.css' import * as THREE from 'three' import { TWEEN } from 'three/examples/jsm/libs/tween.module.min.js' import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js' import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js' import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js' ///////////////////////////////////////////////////////////////////////// //// DRACO LOADER TO LOAD DRACO COMPRESSED MODELS FROM BLENDER const dracoLoader = new DRACOLoader() const loader = new GLTFLoader() dracoLoader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/') dracoLoader.setDecoderConfig({ type: 'js' }) loader.setDRACOLoader(dracoLoader) ///////////////////////////////////////////////////////////////////////// ///// DIV CONTAINER CREATION TO HOLD THREEJS EXPERIENCE const container = document.createElement('div') document.body.appendChild(container) ///////////////////////////////////////////////////////////////////////// ///// SCENE CREATION const scene = new THREE.Scene() scene.background = new THREE.Color('#c8f0f9') ///////////////////////////////////////////////////////////////////////// ///// RENDERER CONFIG const renderer = new THREE.WebGLRenderer({ antialias: true}) // turn on antialias renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)) //set pixel ratio renderer.setSize(window.innerWidth, window.innerHeight) // make it full screen renderer.outputEncoding = THREE.sRGBEncoding // set color encoding container.appendChild(renderer.domElement) // add the renderer to html div ///////////////////////////////////////////////////////////////////////// ///// CAMERAS CONFIG const camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 100) camera.position.set(34,16,-20) scene.add(camera) ///////////////////////////////////////////////////////////////////////// ///// MAKE EXPERIENCE FULL SCREEN window.addEventListener('resize', () => { const width = window.innerWidth const height = window.innerHeight camera.aspect = width / height camera.updateProjectionMatrix() renderer.setSize(width, height) renderer.setPixelRatio(2) }) ///////////////////////////////////////////////////////////////////////// ///// CREATE ORBIT CONTROLS const controls = new OrbitControls(camera, renderer.domElement) ///////////////////////////////////////////////////////////////////////// ///// SCENE LIGHTS const ambient = new THREE.AmbientLight(0xa0a0fc, 0.82) scene.add(ambient) const sunLight = new THREE.DirectionalLight(0xe8c37b, 1.96) sunLight.position.set(-69,44,14) scene.add(sunLight) ///////////////////////////////////////////////////////////////////////// ///// LOADING GLB/GLTF MODEL FROM BLENDER loader.load('models/gltf/starter-scene.glb', function (gltf) { scene.add(gltf.scene) }) ///////////////////////////////////////////////////////////////////////// //// INTRO CAMERA ANIMATION USING TWEEN function introAnimation() { controls.enabled = false //disable orbit controls to animate the camera new TWEEN.Tween(camera.position.set(26,4,-35 )).to({ // from camera position x: 16, //desired x position to go y: 50, //desired y position to go z: -0.1 //desired z position to go }, 6500) // time take to animate .delay(1000).easing(TWEEN.Easing.Quartic.InOut).start() // define delay, easing .onComplete(function () { //on finish animation controls.enabled = true //enable orbit controls setOrbitControlsLimits() //enable controls limits TWEEN.remove(this) // remove the animation from memory }) } introAnimation() // call intro animation on start ///////////////////////////////////////////////////////////////////////// //// DEFINE ORBIT CONTROLS LIMITS function setOrbitControlsLimits(){ controls.enableDamping = true controls.dampingFactor = 0.04 controls.minDistance = 35 controls.maxDistance = 60 controls.enableRotate = true controls.enableZoom = true controls.maxPolarAngle = Math.PI /2.5 } ///////////////////////////////////////////////////////////////////////// //// RENDER LOOP FUNCTION function rendeLoop() { TWEEN.update() // update animations controls.update() // update orbit controls renderer.render(scene, camera) // render the scene using the camera requestAnimationFrame(rendeLoop) //loop the render function } rendeLoop() //start rendering ================================================ FILE: static/.gitkeep ================================================ ================================================ FILE: static/favicon/site.webmanifest ================================================ {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}