Skip to content

Commit daa6605

Browse files
committed
add: more basic examples with mdx
1 parent bd3f94c commit daa6605

File tree

7 files changed

+351
-0
lines changed

7 files changed

+351
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { Color3, Vector3 } from '@babylonjs/core/Maths/math'
2+
import React from 'react'
3+
import { Engine, Scene } from 'react-babylonjs'
4+
5+
const ThemeContext = React.createContext({
6+
color: Color3.Red(),
7+
position: Vector3.Zero(),
8+
name: 'default context',
9+
})
10+
11+
const ThemedBox = () => {
12+
const ctx = React.useContext(ThemeContext)
13+
return (
14+
<box name={ctx.name} position={ctx.position}>
15+
<standardMaterial name="mat" diffuseColor={ctx.color} specularColor={Color3.Black()} />
16+
</box>
17+
)
18+
}
19+
20+
const EngineScene = () => (
21+
<div style={{ flex: 1, display: 'flex', maxWidth: '80%' }}>
22+
<ThemeContext.Consumer>
23+
{(value) => (
24+
<Engine antialias adaptToDeviceRatio canvasId="babylon-js">
25+
<Scene>
26+
<ThemeContext.Provider value={value}>
27+
<arcRotateCamera
28+
name="arc"
29+
target={Vector3.Zero()}
30+
minZ={0.001}
31+
alpha={-Math.PI / 4}
32+
beta={Math.PI / 4}
33+
radius={5}
34+
/>
35+
<hemisphericLight name="light1" intensity={0.7} direction={Vector3.Up()} />
36+
<ground name="ground1" width={6} height={6} subdivisions={2} />
37+
<ThemedBox />
38+
</ThemeContext.Provider>
39+
</Scene>
40+
</Engine>
41+
)}
42+
</ThemeContext.Consumer>
43+
</div>
44+
)
45+
46+
const BridgedContext = () => (
47+
<ThemeContext.Provider
48+
value={{
49+
color: Color3.FromHexString('#FFAF00'),
50+
position: new Vector3(0, 1, 0),
51+
name: 'testing',
52+
}}
53+
>
54+
<EngineScene />
55+
</ThemeContext.Provider>
56+
)
57+
58+
export default BridgedContext
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
title: 'Context Bridge'
3+
---
4+
5+
These can be used to bring context across renderer boundaries.
6+
7+
In `react-babylonjs` that will be on the Engine and Scene components. So,
8+
capture (Consumer) the context outside and "Provide" inside the Scene.
9+
10+
Example is given here - you would need these for Themes or libraries like Redux
11+
that depend on context. If you are using Redux and your project is not too far
12+
along consider switching to a state management library that doesn't suffer from
13+
this issue such as Zustand.
14+
15+
[devtool:ContextBridge.tsx]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { Vector3 } from '@babylonjs/core/Maths/math.vector'
2+
import { VertexData } from '@babylonjs/core/Meshes/mesh.vertexData'
3+
// this import is just so it works with debug builds. you should import from '@babylonjs/*` directly as above
4+
import { Mesh } from 'react-babylonjs/node_modules/@babylonjs/core/Meshes/mesh'
5+
6+
import React, { FC, useState } from 'react'
7+
import { Engine, Scene, useScene } from 'react-babylonjs'
8+
9+
type CustomMeshType = {
10+
name: string
11+
position: Vector3
12+
useWireframe: boolean
13+
}
14+
15+
const CustomMesh: FC<CustomMeshType> = (props) => {
16+
const scene = useScene()
17+
18+
const [customMesh] = useState(() => {
19+
const meshInstance = new Mesh(props.name, scene!)
20+
21+
//Set arrays for positions and indices
22+
var positions = [-5, 2, -3, -7, -2, -3, -3, -2, -3, 5, 2, 3, 7, -2, 3, 3, -2, 3]
23+
var indices = [0, 1, 2, 3, 4, 5]
24+
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]
25+
26+
//Empty array to contain calculated values
27+
var normals: number[] = []
28+
29+
var vertexData = new VertexData()
30+
VertexData.ComputeNormals(positions, indices, normals)
31+
32+
//Assign positions, indices and normals to vertexData
33+
vertexData.positions = positions
34+
vertexData.indices = indices
35+
vertexData.normals = normals
36+
vertexData.colors = colors
37+
38+
//Apply vertexData to custom mesh
39+
vertexData.applyToMesh(meshInstance)
40+
41+
return meshInstance
42+
})
43+
44+
return (
45+
<mesh name="mesh" fromInstance={customMesh} disposeInstanceOnUnmount position={props.position}>
46+
<standardMaterial name={`${props.name}-mat`} wireframe={props.useWireframe} />
47+
</mesh>
48+
)
49+
}
50+
51+
const CustomMeshes = () => {
52+
const [displayLast, setDisplayLast] = useState(true)
53+
const toggleDisplay = () => {
54+
setDisplayLast((cur) => !cur)
55+
}
56+
return (
57+
<>
58+
<div>
59+
<button className="btn btn-primary m-2" onClick={toggleDisplay}>
60+
Toggle Top Triangle Visibility
61+
</button>
62+
</div>
63+
<div style={{ flex: 1, display: 'flex' }}>
64+
<Engine antialias adaptToDeviceRatio canvasId="babylon-js">
65+
<Scene>
66+
<arcRotateCamera
67+
name="camera1"
68+
target={Vector3.Zero()}
69+
alpha={Math.PI / 2}
70+
beta={Math.PI / 2}
71+
radius={20}
72+
/>
73+
<hemisphericLight name="light1" intensity={0.7} direction={Vector3.Up()} />
74+
<CustomMesh name="custom-0" position={new Vector3(0, 0, 0)} useWireframe={false} />
75+
<CustomMesh name="custom-2" position={new Vector3(0, 2, 0)} useWireframe={true} />
76+
{displayLast && (
77+
<CustomMesh name="custom-4" position={new Vector3(0, 4, 0)} useWireframe={false} />
78+
)}
79+
</Scene>
80+
</Engine>
81+
</div>
82+
</>
83+
)
84+
}
85+
86+
export default CustomMeshes
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
title: 'Custom Mesh'
3+
---
4+
5+
We're not just limited to basic shapes here, but the real purpose of this
6+
example is to highlight 2 custom properties:
7+
8+
```tsx
9+
<mesh name="mesh" fromInstance={customMesh} disposeInstanceOnUnmount>
10+
...
11+
</mesh>
12+
```
13+
14+
When you supply a `fromInstance` prop then the renderer will substibute what you
15+
provide instead of "newing up" that object for you. You can do this with
16+
Cameras, Textures, etc. Property changes will flow to that instance as usual.
17+
18+
The renderer will not dispose of this instance when it unmounts the Component,
19+
but you can opt-in by specifying that you would like that done on your behalf.
20+
In our case it's a bit simple - we could have in a useEffect disposed the mesh,
21+
but in some cases you may have something like a texture map that you want
22+
re-used and whenever you use `fromInstance` the renderer will assume you are
23+
managing the lifecycle of that object unless you request automatic disposal with
24+
`disposeInstanceOnUnmount` (which is the same as
25+
`disposeInstanceOnUnmount={true}`)
26+
27+
[devtool:CustomMesh.tsx]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import { Color3, Color4, Vector3 } from '@babylonjs/core'
2+
import React, { useCallback, useEffect, useState } from 'react'
3+
import { Engine, Scene } from 'react-babylonjs'
4+
5+
type GridItemType = [
6+
number,
7+
{
8+
toPoint: () => { x: number; y: number }
9+
width: () => number
10+
height: () => number
11+
color: Color4
12+
isHovered: boolean
13+
}
14+
]
15+
16+
type GridType = {
17+
entries: () => Iterable<GridItemType>
18+
}
19+
20+
const importUMD = async (url: string, module = { exports: {} }) =>
21+
(Function('module', 'exports', await (await fetch(url)).text()).call(
22+
module,
23+
module,
24+
module.exports
25+
),
26+
module).exports
27+
28+
const getGrid = async (): Promise<GridType> => {
29+
// const cdn = 'https://cdn.jsdelivr.net/npm/honeycomb-grid'; await import (cdn)
30+
const importResults = await importUMD(
31+
'https://cdn.jsdelivr.net/npm/[email protected]/dist/honeycomb.min.js'
32+
)
33+
console.log('import results:', importResults)
34+
const { defineGrid, extendHex } = importResults as any
35+
const Hex = extendHex({
36+
size: 1, // default: 1
37+
orientation: 'flat', // default: 'pointy'
38+
color: new Color4(0, 0.68, 1),
39+
hovered: false,
40+
})
41+
42+
// create a Grid factory that uses the Hex factory:
43+
const Grid = defineGrid(Hex)
44+
45+
// create a rectangle grid with each tile assigned a random color:
46+
const grid = Grid.rectangle({
47+
width: GRID_WIDTH,
48+
height: GRID_HEIGHT,
49+
onCreate: (hex: any) => {
50+
hex.color = new Color4(Math.random(), Math.random(), Math.random(), 1)
51+
hex.isHovered = Math.random() < 0.1
52+
},
53+
})
54+
55+
return grid
56+
}
57+
58+
const GRID_WIDTH = 60
59+
const GRID_HEIGHT = 40
60+
const HOVER_COLOR = new Color4(0.8, 0.8, 0.8, 1)
61+
62+
const Instances = () => {
63+
// const hexRef = useRef<CreatedInstance<Mesh>>(null);
64+
const [hexMesh, setHexMesh] = useState(null)
65+
const hexRef = useCallback((node) => {
66+
if (node) {
67+
const mesh = node
68+
mesh.registerInstancedBuffer('color', 4)
69+
setHexMesh(mesh)
70+
}
71+
}, [])
72+
73+
const [grid, setGrid] = useState<null | GridType>(null)
74+
75+
useEffect(() => {
76+
getGrid().then((grid) => setGrid(grid))
77+
}, [])
78+
79+
const createUpdate = () => {
80+
getGrid().then((grid) => setGrid(grid))
81+
}
82+
83+
return (
84+
<div className="App">
85+
<button className="btn btn-primary m-2" onClick={createUpdate}>
86+
Change hex colours
87+
</button>
88+
<Engine antialias adaptToDeviceRatio canvasId="babylonJS">
89+
<Scene clearColor={Color4.FromColor3(Color3.White())}>
90+
<arcRotateCamera
91+
name="arc"
92+
target={Vector3.Zero()}
93+
minZ={0.001}
94+
alpha={-Math.PI / 2}
95+
beta={Math.PI / 1.2}
96+
radius={Math.max(GRID_WIDTH, GRID_HEIGHT) * 1.5}
97+
/>
98+
<hemisphericLight name="light1" intensity={0.9} direction={Vector3.Down()} />
99+
<disc ref={hexRef} name="hex" radius={1} tessellation={6} isVisible={false} />
100+
{hexMesh &&
101+
grid &&
102+
Array.from(grid.entries()).map((entry) => {
103+
const [i, tile] = entry
104+
const { x, y } = tile.toPoint()
105+
console.log(`${i}->{${x},${y}} (${tile.width()})`)
106+
return (
107+
<instancedMesh
108+
source={hexMesh}
109+
key={i}
110+
name={`hex-${i}`}
111+
position={
112+
new Vector3(
113+
x + tile.width() / 2 - GRID_WIDTH * 0.75,
114+
y + tile.height() / 2 - (Math.sqrt(3) * GRID_HEIGHT) / 2,
115+
0
116+
)
117+
}
118+
instancedBuffers={{
119+
color: tile.isHovered ? HOVER_COLOR : tile.color,
120+
}}
121+
/>
122+
)
123+
})}
124+
</Scene>
125+
</Engine>
126+
</div>
127+
)
128+
}
129+
130+
export default Instances
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
title: 'InstancedMesh Hex'
3+
---
4+
5+
An alternative to 'fromInstance' prop for meshes is to use
6+
[instanced meshes](https://doc.babylonjs.com/typedoc/classes/BABYLON.InstancedMesh).
7+
You can read up more on "InstancedMesh" in babylon.js, but they have less
8+
overhead of separate instancing hundreds of meshes.
9+
10+
Essentially in this example we create a single "hex" mesh. Then we add hundreds
11+
of instances using that mesh as a source. Each instance is set with it's own
12+
position and color.
13+
14+
```tsx
15+
<instancedMesh
16+
source={hexMesh}
17+
key={i}
18+
name={`hex-${i}`}
19+
position={
20+
new Vector3(
21+
x + tile.width() / 2 - GRID_WIDTH * 0.75,
22+
y + tile.height() / 2 - (Math.sqrt(3) * GRID_HEIGHT) / 2,
23+
0
24+
)
25+
}
26+
instancedBuffers={{
27+
color: tile.isHovered ? HOVER_COLOR : tile.color,
28+
}}
29+
/>
30+
```
31+
32+
[devtool:InstancedMeshHex.tsx]

packages/static/content/nav.js

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ const toc = [
1212
'/examples/basic',
1313
'/examples/basic/animations',
1414
'/examples/basic/moving-boxes',
15+
'/examples/basic/context-bridge',
16+
'/examples/basic/custom-mesh',
17+
'/examples/basic/instanced-mesh-hex',
1518
]
1619

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

0 commit comments

Comments
 (0)