Skip to content

Commit

Permalink
feat(examples): beziers: add instanceable version of the beziers, and…
Browse files Browse the repository at this point in the history
… add point light option
  • Loading branch information
lojjic committed Mar 26, 2020
1 parent bad5e02 commit 0739f4d
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 13 deletions.
25 changes: 20 additions & 5 deletions packages/troika-examples/bezier-3d/Bezier3DExample.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react'
import { Canvas3D, Group3DFacade } from 'troika-3d'
import Bezier3DFacade from './Bezier3DFacade'
import { Bezier3DFacade, Bezier3DInstanceableFacade } from './Bezier3DFacade'
import ShadowSurface from './ShadowSurfaceFacade'
import DatGui, {DatBoolean, DatNumber} from 'react-dat-gui'

Expand All @@ -18,6 +18,9 @@ class Bezier3DExample extends React.Component {
dashed: 0,
randomize: true,
shadows: true,
instancing: false,
pointLight: false,
directionalLight: true,
p1x: -1,
p1y: 0,
p1z: 0,
Expand All @@ -41,7 +44,7 @@ class Bezier3DExample extends React.Component {
for (let i = 0; i < (state.randomize ? state.count : 1); i++) {
const obj = {
key: 'bezier' + i,
facade: Bezier3DFacade,
facade: state.instancing ? Bezier3DInstanceableFacade : Bezier3DFacade,
radius: state.radius,
color: state.randomize ? (colors[i] || (colors[i] = randomColor())) : 0x66ccff,
castShadow: state.shadows,
Expand All @@ -66,16 +69,24 @@ class Bezier3DExample extends React.Component {
z: vr ? 1 : 3
} }
lights={[
{
state.directionalLight && {
type: 'directional',
z: 2,
y: 1,
castShadow: state.shadows,
shadow: {
//mapSize: {width: 1024, height: 1024},
camera: {far: 10, near: 0.1, left: -2, right: 2, top: 2, bottom: -2}
}
},
state.pointLight && {
type: 'point',
z: 0,
y: 0,
castShadow: state.shadows,
shadow: {
camera: {far: 10, near: 0.001}
}
},
]}
objects={[
{
Expand Down Expand Up @@ -105,8 +116,12 @@ class Bezier3DExample extends React.Component {
<DatNumber path="radius" min={0.001} max={0.1} step={0.001} />
<DatNumber path="dashed" min={0} max={0.2} step={0.01} />
<DatBoolean path="shadows" />
<DatBoolean path="instancing" />
<DatBoolean path="randomize" />
<DatBoolean path="directionalLight" label="Directional Light" />
<DatBoolean path="pointLight" label="Point Light" />
<DatBoolean path="randomize" />
{state.randomize ? <DatNumber path="count" min={1} max={100} step={1} /> : null }
{state.randomize ? <DatNumber path="count" min={1} max={200} step={1} /> : null }

{state.randomize ? null : pointProps.map(prop =>
<DatNumber key={prop} path={prop} min={-1} max={1} step={0.01} />
Expand Down
112 changes: 109 additions & 3 deletions packages/troika-examples/bezier-3d/Bezier3DFacade.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import { Object3DFacade } from 'troika-3d'
import { Object3DFacade, Instanceable3DFacade } from 'troika-3d'
import { BezierMesh } from 'troika-three-utils'
import { Color, MeshStandardMaterial, Vector3 } from 'three'

const noDash = [0, 0]
const tempColor = new Color()
const defaultMaterial = new MeshStandardMaterial({transparent: true})

/**
* Facade wrapper around BezierMesh from three-troika-utils
*/
class Bezier3DFacade extends Object3DFacade {
export class Bezier3DFacade extends Object3DFacade {
constructor(parent) {
super(parent, new BezierMesh())
this.radius = 0.01
this.opacity = 1
this.color = 0xffffff
this.dashArray = [0, 0]
this.dashOffset = 0
this.material = defaultMaterial
}

afterUpdate() {
Expand Down Expand Up @@ -47,4 +51,106 @@ class Bezier3DFacade extends Object3DFacade {
}
}

export default Bezier3DFacade


const instancingMeshes = new WeakMap() //cache of singleton meshes for each material

/**
* Instanceable version
*/
export class Bezier3DInstanceableFacade extends Instanceable3DFacade {
constructor (parent) {
super(parent)
this.radius = 0.01
this.opacity = 1
this.color = this._color = new Color(0xffffff)
this.dashArray = [0, 0]
this.dashOffset = 0
this.material = defaultMaterial
}

afterUpdate () {
let {
p1x, p1y, p1z,
c1x, c1y, c1z,
c2x, c2y, c2z,
p2x, p2y, p2z,
radius,
dashArray,
dashOffset,
opacity,
color
} = this

/*
pointA: {value: new Vector3()},
controlA: {value: new Vector3()},
controlB: {value: new Vector3()},
pointB: {value: new Vector3()},
radius: {value: 0.01},
dashing: {value: new Vector3()} //on, off, offset
*/
if (p1x !== this._p1x || p1y !== this._p1y || p1z !== this._p1z) {
this.setInstanceUniform('pointA', new Vector3(this._p1x = p1x, this._p1y = p1y, this._p1z = p1z))
}
if (c1x !== this._c1x || c1y !== this._c1y || c1z !== this._c1z) {
this.setInstanceUniform('controlA', new Vector3(this._c1x = c1x, this._c1y = c1y, this._c1z = c1z))
}
if (c2x !== this._c2x || c2y !== this._c2y || c2z !== this._c2z) {
this.setInstanceUniform('controlB', new Vector3(this._c2x = c2x, this._c2y = c2y, this._c2z = c2z))
}
if (p2x !== this._p2x || p2y !== this._p2y || p2z !== this._p2z) {
this.setInstanceUniform('pointB', new Vector3(this._p2x = p2x, this._p2y = p2y, this._p2z = p2z))
}
if (radius !== this._radius) {
this.setInstanceUniform('radius', this._radius = radius)
}
dashArray = dashArray || noDash
let lastDashArray = this._dashArray || noDash
if (dashArray[0] !== lastDashArray[0] || dashArray[1] !== lastDashArray[1] || dashOffset !== this._dashOffset) {
this._dashArray = dashArray
this.setInstanceUniform('dashing', new Vector3(dashArray[0], dashArray[1], this._dashOffset = dashOffset))
}

// Material color and opacity:
if (!tempColor.set(color).equals(this._color)) {
this.setInstanceUniform('diffuse', this._color = new Color(color))
}
if (opacity !== this._opacity) {
this.setInstanceUniform('opacity', this._opacity = opacity)
}

super.afterUpdate()
}

set material(material) {
if (material !== this._material) {
this._material = material
let mesh = instancingMeshes.get(material)
if (!mesh) {
mesh = new BezierMesh()
mesh.castShadow = true //TODO - figure out how to control shadow casting
mesh.material = material
mesh.material.instanceUniforms = this.getInstanceUniforms()
instancingMeshes.set(material, mesh)
}
this.instancedThreeObject = mesh
}
}

getInstanceUniforms() {
return [
'pointA',
'controlA',
'controlB',
'pointB',
'radius',
'dashing',

// Set up instancing for material color and opacity by default; users can specify additional
// material-specific uniforms by extending and overriding. TODO - find a nicer way
'diffuse',
'opacity'
]
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Group3DFacade} from 'troika-3d'
import Bezier3DFacade from '../bezier-3d/Bezier3DFacade'
import { Bezier3DFacade, Bezier3DInstanceableFacade } from '../bezier-3d/Bezier3DFacade'
import {Vector3} from 'three'


Expand Down Expand Up @@ -46,7 +46,7 @@ class ConnectionsFacade extends Group3DFacade {
// globeCtrl.applyMatrix4(globe.threeObject.matrixWorld)
cxns.push({
key: cityLabel.$facadeId,
facade: Bezier3DFacade,
facade: Bezier3DInstanceableFacade,
radius: hovering ? 0.0015 : 0.001,
color: hovering ? 0xffffff : colors[i % colors.length],
opacity: hovering ? 1.0 : 0.6,
Expand Down
10 changes: 7 additions & 3 deletions packages/troika-three-utils/src/BezierMesh.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,15 @@ const defaultBaseMaterial = new MeshStandardMaterial({color: 0xffffff, side: Dou
* TODO: allow control of the geometry's segment counts
*/
class BezierMesh extends Mesh {
static getGeometry() {
return geometry || (geometry =
new CylinderBufferGeometry(1, 1, 1, 6, 64).translate(0, 0.5, 0)
)
}

constructor() {
super(
geometry || (geometry =
new CylinderBufferGeometry(1, 1, 1, 6, 64).translate(0, 0.5, 0)
),
BezierMesh.getGeometry(),
defaultBaseMaterial
)

Expand Down

0 comments on commit 0739f4d

Please sign in to comment.