Skip to content

Commit

Permalink
add: more basic examples with mdx
Browse files Browse the repository at this point in the history
  • Loading branch information
brianzinn committed Oct 28, 2022
1 parent bd3f94c commit daa6605
Show file tree
Hide file tree
Showing 7 changed files with 351 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Color3, Vector3 } from '@babylonjs/core/Maths/math'
import React from 'react'
import { Engine, Scene } from 'react-babylonjs'

const ThemeContext = React.createContext({
color: Color3.Red(),
position: Vector3.Zero(),
name: 'default context',
})

const ThemedBox = () => {
const ctx = React.useContext(ThemeContext)
return (
<box name={ctx.name} position={ctx.position}>
<standardMaterial name="mat" diffuseColor={ctx.color} specularColor={Color3.Black()} />
</box>
)
}

const EngineScene = () => (
<div style={{ flex: 1, display: 'flex', maxWidth: '80%' }}>
<ThemeContext.Consumer>
{(value) => (
<Engine antialias adaptToDeviceRatio canvasId="babylon-js">
<Scene>
<ThemeContext.Provider value={value}>
<arcRotateCamera
name="arc"
target={Vector3.Zero()}
minZ={0.001}
alpha={-Math.PI / 4}
beta={Math.PI / 4}
radius={5}
/>
<hemisphericLight name="light1" intensity={0.7} direction={Vector3.Up()} />
<ground name="ground1" width={6} height={6} subdivisions={2} />
<ThemedBox />
</ThemeContext.Provider>
</Scene>
</Engine>
)}
</ThemeContext.Consumer>
</div>
)

const BridgedContext = () => (
<ThemeContext.Provider
value={{
color: Color3.FromHexString('#FFAF00'),
position: new Vector3(0, 1, 0),
name: 'testing',
}}
>
<EngineScene />
</ThemeContext.Provider>
)

export default BridgedContext
15 changes: 15 additions & 0 deletions packages/static/content/examples/basic/context-bridge/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
title: 'Context Bridge'
---

These can be used to bring context across renderer boundaries.

In `react-babylonjs` that will be on the Engine and Scene components. So,
capture (Consumer) the context outside and "Provide" inside the Scene.

Example is given here - you would need these for Themes or libraries like Redux
that depend on context. If you are using Redux and your project is not too far
along consider switching to a state management library that doesn't suffer from
this issue such as Zustand.

[devtool:ContextBridge.tsx]
86 changes: 86 additions & 0 deletions packages/static/content/examples/basic/custom-mesh/CustomMesh.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { Vector3 } from '@babylonjs/core/Maths/math.vector'
import { VertexData } from '@babylonjs/core/Meshes/mesh.vertexData'
// this import is just so it works with debug builds. you should import from '@babylonjs/*` directly as above
import { Mesh } from 'react-babylonjs/node_modules/@babylonjs/core/Meshes/mesh'

import React, { FC, useState } from 'react'
import { Engine, Scene, useScene } from 'react-babylonjs'

type CustomMeshType = {
name: string
position: Vector3
useWireframe: boolean
}

const CustomMesh: FC<CustomMeshType> = (props) => {
const scene = useScene()

const [customMesh] = useState(() => {
const meshInstance = new Mesh(props.name, scene!)

//Set arrays for positions and indices
var positions = [-5, 2, -3, -7, -2, -3, -3, -2, -3, 5, 2, 3, 7, -2, 3, 3, -2, 3]
var indices = [0, 1, 2, 3, 4, 5]
var colors = [1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1]

//Empty array to contain calculated values
var normals: number[] = []

var vertexData = new VertexData()
VertexData.ComputeNormals(positions, indices, normals)

//Assign positions, indices and normals to vertexData
vertexData.positions = positions
vertexData.indices = indices
vertexData.normals = normals
vertexData.colors = colors

//Apply vertexData to custom mesh
vertexData.applyToMesh(meshInstance)

return meshInstance
})

return (
<mesh name="mesh" fromInstance={customMesh} disposeInstanceOnUnmount position={props.position}>
<standardMaterial name={`${props.name}-mat`} wireframe={props.useWireframe} />
</mesh>
)
}

const CustomMeshes = () => {
const [displayLast, setDisplayLast] = useState(true)
const toggleDisplay = () => {
setDisplayLast((cur) => !cur)
}
return (
<>
<div>
<button className="btn btn-primary m-2" onClick={toggleDisplay}>
Toggle Top Triangle Visibility
</button>
</div>
<div style={{ flex: 1, display: 'flex' }}>
<Engine antialias adaptToDeviceRatio canvasId="babylon-js">
<Scene>
<arcRotateCamera
name="camera1"
target={Vector3.Zero()}
alpha={Math.PI / 2}
beta={Math.PI / 2}
radius={20}
/>
<hemisphericLight name="light1" intensity={0.7} direction={Vector3.Up()} />
<CustomMesh name="custom-0" position={new Vector3(0, 0, 0)} useWireframe={false} />
<CustomMesh name="custom-2" position={new Vector3(0, 2, 0)} useWireframe={true} />
{displayLast && (
<CustomMesh name="custom-4" position={new Vector3(0, 4, 0)} useWireframe={false} />
)}
</Scene>
</Engine>
</div>
</>
)
}

export default CustomMeshes
27 changes: 27 additions & 0 deletions packages/static/content/examples/basic/custom-mesh/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
title: 'Custom Mesh'
---

We're not just limited to basic shapes here, but the real purpose of this
example is to highlight 2 custom properties:

```tsx
<mesh name="mesh" fromInstance={customMesh} disposeInstanceOnUnmount>
...
</mesh>
```

When you supply a `fromInstance` prop then the renderer will substibute what you
provide instead of "newing up" that object for you. You can do this with
Cameras, Textures, etc. Property changes will flow to that instance as usual.

The renderer will not dispose of this instance when it unmounts the Component,
but you can opt-in by specifying that you would like that done on your behalf.
In our case it's a bit simple - we could have in a useEffect disposed the mesh,
but in some cases you may have something like a texture map that you want
re-used and whenever you use `fromInstance` the renderer will assume you are
managing the lifecycle of that object unless you request automatic disposal with
`disposeInstanceOnUnmount` (which is the same as
`disposeInstanceOnUnmount={true}`)

[devtool:CustomMesh.tsx]
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { Color3, Color4, Vector3 } from '@babylonjs/core'
import React, { useCallback, useEffect, useState } from 'react'
import { Engine, Scene } from 'react-babylonjs'

type GridItemType = [
number,
{
toPoint: () => { x: number; y: number }
width: () => number
height: () => number
color: Color4
isHovered: boolean
}
]

type GridType = {
entries: () => Iterable<GridItemType>
}

const importUMD = async (url: string, module = { exports: {} }) =>
(Function('module', 'exports', await (await fetch(url)).text()).call(
module,
module,
module.exports
),
module).exports

const getGrid = async (): Promise<GridType> => {
// const cdn = 'https://cdn.jsdelivr.net/npm/honeycomb-grid'; await import (cdn)
const importResults = await importUMD(
'https://cdn.jsdelivr.net/npm/[email protected]/dist/honeycomb.min.js'
)
console.log('import results:', importResults)
const { defineGrid, extendHex } = importResults as any
const Hex = extendHex({
size: 1, // default: 1
orientation: 'flat', // default: 'pointy'
color: new Color4(0, 0.68, 1),
hovered: false,
})

// create a Grid factory that uses the Hex factory:
const Grid = defineGrid(Hex)

// create a rectangle grid with each tile assigned a random color:
const grid = Grid.rectangle({
width: GRID_WIDTH,
height: GRID_HEIGHT,
onCreate: (hex: any) => {
hex.color = new Color4(Math.random(), Math.random(), Math.random(), 1)
hex.isHovered = Math.random() < 0.1
},
})

return grid
}

const GRID_WIDTH = 60
const GRID_HEIGHT = 40
const HOVER_COLOR = new Color4(0.8, 0.8, 0.8, 1)

const Instances = () => {
// const hexRef = useRef<CreatedInstance<Mesh>>(null);
const [hexMesh, setHexMesh] = useState(null)
const hexRef = useCallback((node) => {
if (node) {
const mesh = node
mesh.registerInstancedBuffer('color', 4)
setHexMesh(mesh)
}
}, [])

const [grid, setGrid] = useState<null | GridType>(null)

useEffect(() => {
getGrid().then((grid) => setGrid(grid))
}, [])

const createUpdate = () => {
getGrid().then((grid) => setGrid(grid))
}

return (
<div className="App">
<button className="btn btn-primary m-2" onClick={createUpdate}>
Change hex colours
</button>
<Engine antialias adaptToDeviceRatio canvasId="babylonJS">
<Scene clearColor={Color4.FromColor3(Color3.White())}>
<arcRotateCamera
name="arc"
target={Vector3.Zero()}
minZ={0.001}
alpha={-Math.PI / 2}
beta={Math.PI / 1.2}
radius={Math.max(GRID_WIDTH, GRID_HEIGHT) * 1.5}
/>
<hemisphericLight name="light1" intensity={0.9} direction={Vector3.Down()} />
<disc ref={hexRef} name="hex" radius={1} tessellation={6} isVisible={false} />
{hexMesh &&
grid &&
Array.from(grid.entries()).map((entry) => {
const [i, tile] = entry
const { x, y } = tile.toPoint()
console.log(`${i}->{${x},${y}} (${tile.width()})`)
return (
<instancedMesh
source={hexMesh}
key={i}
name={`hex-${i}`}
position={
new Vector3(
x + tile.width() / 2 - GRID_WIDTH * 0.75,
y + tile.height() / 2 - (Math.sqrt(3) * GRID_HEIGHT) / 2,
0
)
}
instancedBuffers={{
color: tile.isHovered ? HOVER_COLOR : tile.color,
}}
/>
)
})}
</Scene>
</Engine>
</div>
)
}

export default Instances
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
title: 'InstancedMesh Hex'
---

An alternative to 'fromInstance' prop for meshes is to use
[instanced meshes](https://doc.babylonjs.com/typedoc/classes/BABYLON.InstancedMesh).
You can read up more on "InstancedMesh" in babylon.js, but they have less
overhead of separate instancing hundreds of meshes.

Essentially in this example we create a single "hex" mesh. Then we add hundreds
of instances using that mesh as a source. Each instance is set with it's own
position and color.

```tsx
<instancedMesh
source={hexMesh}
key={i}
name={`hex-${i}`}
position={
new Vector3(
x + tile.width() / 2 - GRID_WIDTH * 0.75,
y + tile.height() / 2 - (Math.sqrt(3) * GRID_HEIGHT) / 2,
0
)
}
instancedBuffers={{
color: tile.isHovered ? HOVER_COLOR : tile.color,
}}
/>
```

[devtool:InstancedMeshHex.tsx]
3 changes: 3 additions & 0 deletions packages/static/content/nav.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ const toc = [
'/examples/basic',
'/examples/basic/animations',
'/examples/basic/moving-boxes',
'/examples/basic/context-bridge',
'/examples/basic/custom-mesh',
'/examples/basic/instanced-mesh-hex',
]

const navSortMap = toc.reduce((c, slug, i) => {
Expand Down

0 comments on commit daa6605

Please sign in to comment.