Skip to content

Commit

Permalink
🎛 Use lil-gui
Browse files Browse the repository at this point in the history
  • Loading branch information
marcofugaro committed Jan 18, 2023
1 parent 2fcdf47 commit 2db23b1
Show file tree
Hide file tree
Showing 20 changed files with 368 additions and 577 deletions.
1 change: 1 addition & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module.exports = {
env: {
browser: true,
node: true,
es6: true,
},
extends: [
'eslint:recommended',
Expand Down
6 changes: 2 additions & 4 deletions .github/workflows/deploy-github-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
- master

jobs:
build:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
Expand All @@ -16,9 +16,7 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: 18

- name: Cache node_modules
uses: c-hive/gha-yarn-cache@v2
cache: 'yarn'

- name: Install Dependencies
run: yarn
Expand Down
84 changes: 51 additions & 33 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion esbuild.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const result = await esbuild
.build({
entryPoints: ['src/index.js'],
bundle: true,
format: 'iife',
format: 'esm',
logLevel: 'silent', // sssh...
legalComments: 'none', // don't include licenses txt file
sourcemap: true,
Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,9 @@
"dependencies": {
"cannon-es": "^0.20.0",
"cannon-es-debugger": "^1.0.0",
"controls-gui": "^2.0.0",
"controls-state": "^2.0.0",
"detect-gpu": "^5.0.4",
"image-promise": "^7.0.1",
"lil-gui": "^0.17.0",
"lodash-es": "^4.17.21",
"mp4-wasm": "marcofugaro/mp4-wasm#build-embedded",
"p-map": "^5.5.0",
Expand Down
2 changes: 1 addition & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@
</head>
<body>
<canvas id="app"></canvas>
<script src="app.js"></script>
<script type="module" src="app.js"></script>
</body>
</html>
60 changes: 24 additions & 36 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import WebGLApp from './utils/WebGLApp'
import assets from './utils/AssetManager'
import Suzanne from './scene/Suzanne'
import { addNaturalLight } from './scene/lights'
import { addScreenshotButton, addRecordButton } from './scene/screenshot-record-buttons'
import { addScreenshotButton, addRecordButton } from './screenshot-record-buttons'

// true if the url has the `?debug` parameter, otherwise false
window.DEBUG = window.location.search.includes('debug')
Expand All @@ -23,20 +23,8 @@ const webgl = new WebGLApp({
showFps: window.DEBUG,
// enable OrbitControls
orbitControls: window.DEBUG,
// Add the controls pane inputs
controls: {
roughness: 0.5,
movement: {
speed: {
value: 1.5,
max: 100,
scale: 'exp',
},
frequency: { value: 0.5, max: 5 },
amplitude: { value: 0.7, max: 2 },
},
},
hideControls: !window.DEBUG,
// show the GUI
gui: window.DEBUG,
// enable cannon-es
// world: new CANNON.World(),
})
Expand All @@ -50,29 +38,29 @@ if (window.DEBUG) {
webgl.canvas.style.visibility = 'hidden'

// load any queued assets
assets.load({ renderer: webgl.renderer }).then(() => {
// add any "WebGL components" here...
// append them to the scene so you can
// use them from other components easily
webgl.scene.suzanne = new Suzanne(webgl)
webgl.scene.add(webgl.scene.suzanne)
await assets.load({ renderer: webgl.renderer })

// lights and other scene related stuff
addNaturalLight(webgl)
// add any "WebGL components" here...
// append them to the scene so you can
// use them from other components easily
webgl.scene.suzanne = new Suzanne(webgl)
webgl.scene.add(webgl.scene.suzanne)

// postprocessing
// add an existing effect from the postprocessing library
webgl.composer.addPass(new EffectPass(webgl.camera, new VignetteEffect()))
// lights and other scene related stuff
addNaturalLight(webgl)

// add the save screenshot and save gif buttons
if (window.DEBUG) {
addScreenshotButton(webgl)
addRecordButton(webgl)
}
// postprocessing
// add an existing effect from the postprocessing library
webgl.composer.addPass(new EffectPass(webgl.camera, new VignetteEffect()))

// show canvas
webgl.canvas.style.visibility = ''
// add the save screenshot and save gif buttons
if (window.DEBUG) {
addScreenshotButton(webgl)
addRecordButton(webgl)
}

// start animation loop
webgl.start()
})
// show canvas
webgl.canvas.style.visibility = ''

// start animation loop
webgl.start()
10 changes: 5 additions & 5 deletions src/scene/Box.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as THREE from 'three'
import { BoxGeometry, Group, Mesh, MeshBasicMaterial } from 'three'

// basic three.js component example

export default class Box extends THREE.Group {
export default class Box extends Group {
constructor(webgl, options = {}) {
super(options)
// these can be used also in other methods
Expand All @@ -12,9 +12,9 @@ export default class Box extends THREE.Group {
// destructure and default values like you do in React
const { color = 0x00ff00 } = this.options

const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshBasicMaterial({ color, wireframe: true })
this.box = new THREE.Mesh(geometry, material)
const geometry = new BoxGeometry(1, 1, 1)
const material = new MeshBasicMaterial({ color, wireframe: true })
this.box = new Mesh(geometry, material)

// add it to the group,
// later the group will be added to the scene
Expand Down
16 changes: 8 additions & 8 deletions src/scene/CannonSphere.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as THREE from 'three'
import * as CANNON from 'cannon-es'
import { Group, Mesh, MeshStandardMaterial, SphereGeometry } from 'three'
import { Body, Sphere } from 'cannon-es'

// remember to add the body to the CANNON world and
// the mesh to the three.js scene or to some component
Expand All @@ -8,8 +8,8 @@ import * as CANNON from 'cannon-es'
// webgl.world.addBody(sphere)
// webgl.scene.add(sphere.mesh)

export default class CannonSphere extends CANNON.Body {
mesh = new THREE.Group()
export default class CannonSphere extends Body {
mesh = new Group()

constructor(webgl, options = {}) {
super(options)
Expand All @@ -18,13 +18,13 @@ export default class CannonSphere extends CANNON.Body {

const { radius = 1 } = this.options

this.addShape(new CANNON.Sphere(radius))
this.addShape(new Sphere(radius))

// add corresponding geometry and material
this.mesh.add(
new THREE.Mesh(
new THREE.SphereGeometry(radius, 32, 32),
new THREE.MeshStandardMaterial({ color: Math.random() * 0xffffff })
new Mesh(
new SphereGeometry(radius, 32, 32),
new MeshStandardMaterial({ color: Math.random() * 0xffffff })
)
)

Expand Down
31 changes: 17 additions & 14 deletions src/scene/Suzanne.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as THREE from 'three'
import { Group, MeshStandardMaterial, Raycaster, Vector2 } from 'three'
import glsl from 'glslify'
import assets from '../utils/AssetManager'
import { wireValue, wireUniform } from '../utils/Controls'
import { addUniforms, customizeVertexShader } from '../utils/customizeShader'

// elaborated three.js component example
Expand Down Expand Up @@ -45,7 +44,7 @@ const hdrKey = assets.queue({
type: 'env-map',
})

export default class Suzanne extends THREE.Group {
export default class Suzanne extends Group {
constructor(webgl, options = {}) {
super(options)
this.webgl = webgl
Expand All @@ -55,26 +54,26 @@ export default class Suzanne extends THREE.Group {
const suzanne = suzanneGltf.scene.clone()

const envMap = assets.get(hdrKey)
const material = new THREE.MeshStandardMaterial({
const material = new MeshStandardMaterial({
map: assets.get(albedoKey),
metalnessMap: assets.get(metalnessKey),
roughnessMap: assets.get(roughnessKey),
normalMap: assets.get(normalKey),
normalScale: new THREE.Vector2(2, 2),
normalScale: new Vector2(2, 2),
envMap,
roughness: webgl.controls.roughness,
roughness: 0.5,
metalness: 1,
})
webgl.gui?.addSmart(material, 'roughness')
this.material = material

wireValue(material, () => webgl.controls.roughness)

// add new unifroms and expose current uniforms
addUniforms(material, {
time: { value: 0 },
frequency: wireUniform(material, () => webgl.controls.movement.frequency),
amplitude: wireUniform(material, () => webgl.controls.movement.amplitude),
frequency: { value: 0.5 },
amplitude: { value: 0.7 },
})
webgl.gui?.wireUniforms('movement', material.uniforms, { blacklist: ['time'] })

customizeVertexShader(material, {
head: glsl`
Expand All @@ -83,7 +82,7 @@ export default class Suzanne extends THREE.Group {
uniform float amplitude;
// you could import glsl packages like this
// #pragma glslify: noise = require(glsl-noise/simplex/3d)
// #pragma glslify: noise3d = require(glsl-noise/simplex/3d)
`,
main: glsl`
float theta = sin(position.z * frequency + time) * amplitude;
Expand Down Expand Up @@ -111,6 +110,10 @@ export default class Suzanne extends THREE.Group {
// make it a little bigger
suzanne.scale.multiplyScalar(1.2)

// incremental speed, we can change it through the GUI
this.speed = 1.5
webgl.gui?.folders.find((f) => f._title === 'movement').addSmart(this, 'speed')

this.add(suzanne)

// set the background as the hdr
Expand All @@ -120,11 +123,11 @@ export default class Suzanne extends THREE.Group {
onPointerDown(event, { x, y }) {
// for example, check of we clicked on an
// object with raycasting
const coords = new THREE.Vector2().set(
const coords = new Vector2().set(
(x / this.webgl.width) * 2 - 1,
(-y / this.webgl.height) * 2 + 1
)
const raycaster = new THREE.Raycaster()
const raycaster = new Raycaster()
raycaster.setFromCamera(coords, this.webgl.camera)
const hits = raycaster.intersectObject(this, true)
console.log(hits.length > 0 ? `Hit ${hits[0].object.name}!` : 'No hit')
Expand All @@ -133,6 +136,6 @@ export default class Suzanne extends THREE.Group {
}

update(dt, time) {
this.material.uniforms.time.value += dt * this.webgl.controls.movement.speed
this.material.uniforms.time.value += dt * this.speed
}
}
6 changes: 3 additions & 3 deletions src/scene/lights.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import * as THREE from 'three'
import { DirectionalLight, HemisphereLight } from 'three'

// natural hemisphere light from
// https://threejs.org/examples/#webgl_lights_hemisphere
export function addNaturalLight(webgl) {
const hemiLight = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.6)
const hemiLight = new HemisphereLight(0xffffff, 0xffffff, 0.6)
hemiLight.color.setHSL(0.6, 1, 0.6)
hemiLight.groundColor.setHSL(0.095, 1, 0.75)
hemiLight.position.set(0, 50, 0)
webgl.scene.add(hemiLight)

const dirLight = new THREE.DirectionalLight(0xffffff, 1)
const dirLight = new DirectionalLight(0xffffff, 1)
dirLight.color.setHSL(0.1, 1, 0.95)
dirLight.position.set(3, 5, 1)
dirLight.position.multiplyScalar(50)
Expand Down
File renamed without changes.
46 changes: 46 additions & 0 deletions src/utils/AssetManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import omit from 'lodash/omit'
import loadTexture from './loadTexture'
import loadEnvMap from './loadEnvMap'
import loadGLTF from './loadGLTF'
import { mapValues } from 'lodash-es'

class AssetManager {
#queue = []
Expand Down Expand Up @@ -42,6 +43,46 @@ class AssetManager {
return url
}

// Add a MeshStandardMaterial to be queued,
// input: { map, metalnessMap, roughnessMap, normalMap, ... }
queueStandardMaterial(maps, options = {}) {
const keys = {}

// These textures are non-color and they don't
// need gamma correction
const linearTextures = [
'pbrMap',
'alphaMap',
'aoMap',
'bumpMap',
'displacementMap',
'lightMap',
'metalnessMap',
'normalMap',
'roughnessMap',
'clearcoatMap',
'clearcoatNormalMap',
'clearcoatRoughnessMap',
'sheenRoughnessMap',
'sheenColorMap',
'specularIntensityMap',
'specularColorMap',
'thicknessMap',
'transmissionMap',
]

Object.keys(maps).forEach((map) => {
keys[map] = this.queue({
url: maps[map],
type: 'texture',
...options,
...(linearTextures.includes(map) && { linear: true }),
})
})

return keys
}

_getQueued(url) {
return this.#queue.find((item) => item.url === url)
}
Expand Down Expand Up @@ -74,6 +115,11 @@ class AssetManager {
return this.#loaded[key]
}

// Fetch a loaded MeshStandardMaterial object
getStandardMaterial = (keys) => {
return mapValues(keys, (key) => this.get(key))
}

// Loads a single asset on demand.
async loadSingle({ renderer, ...item }) {
// renderer is used to load textures and env maps,
Expand Down
Loading

0 comments on commit 2db23b1

Please sign in to comment.