Skip to content

Commit bf2fea7

Browse files
committed
* expose more internal utilities from the library
* improve cubes example with reusable useFlatShading hook
1 parent ef5a22b commit bf2fea7

File tree

11 files changed

+373
-172
lines changed

11 files changed

+373
-172
lines changed

examples/3d-cubes/src/components/Cube.tsx

Lines changed: 37 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
Transform3dProps,
77
useFactoryRef,
88
} from 'react-css-transform';
9-
import { FlatMaterial } from './FlatMaterial';
9+
import { Face } from './Face';
1010
import { halfPi, yAxis, xAxis } from '../constants';
1111

1212
const propTypes = {
@@ -46,36 +46,42 @@ export const Cube = ({ faceSize, color, ...otherProps }: CubeProps) => {
4646

4747
return (
4848
<Transform3d {...otherProps}>
49-
<Transform3d translate={pos1.current} rotate={-halfPi} rotateAxis={yAxis}>
50-
<FlatMaterial color={color}>
51-
<div style={faceStyle} className="face" />
52-
</FlatMaterial>
53-
</Transform3d>
54-
<Transform3d translate={pos2.current} rotate={halfPi} rotateAxis={yAxis}>
55-
<FlatMaterial color={color}>
56-
<div style={faceStyle} className="face" />
57-
</FlatMaterial>
58-
</Transform3d>
59-
<Transform3d translate={pos3.current} rotate={halfPi} rotateAxis={xAxis}>
60-
<FlatMaterial color={color}>
61-
<div style={faceStyle} className="face" />
62-
</FlatMaterial>
63-
</Transform3d>
64-
<Transform3d translate={pos4.current} rotate={-halfPi} rotateAxis={xAxis}>
65-
<FlatMaterial color={color}>
66-
<div style={faceStyle} className="face" />
67-
</FlatMaterial>
68-
</Transform3d>
69-
<Transform3d translate={pos5.current} rotate={Math.PI} rotateAxis={yAxis}>
70-
<FlatMaterial color={color}>
71-
<div style={faceStyle} className="face" />
72-
</FlatMaterial>
73-
</Transform3d>
74-
<Transform3d translate={pos6.current}>
75-
<FlatMaterial color={color}>
76-
<div style={faceStyle} className="face" />
77-
</FlatMaterial>
78-
</Transform3d>
49+
<Face
50+
translate={pos1.current}
51+
rotate={-halfPi}
52+
rotateAxis={yAxis}
53+
color={color}
54+
style={faceStyle}
55+
/>
56+
<Face
57+
translate={pos2.current}
58+
rotate={halfPi}
59+
rotateAxis={yAxis}
60+
color={color}
61+
style={faceStyle}
62+
/>
63+
<Face
64+
translate={pos3.current}
65+
rotate={halfPi}
66+
rotateAxis={xAxis}
67+
color={color}
68+
style={faceStyle}
69+
/>
70+
<Face
71+
translate={pos4.current}
72+
rotate={-halfPi}
73+
rotateAxis={xAxis}
74+
color={color}
75+
style={faceStyle}
76+
/>
77+
<Face
78+
translate={pos5.current}
79+
rotate={Math.PI}
80+
rotateAxis={yAxis}
81+
color={color}
82+
style={faceStyle}
83+
/>
84+
<Face translate={pos6.current} color={color} style={faceStyle} />
7985
</Transform3d>
8086
);
8187
};
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { use3dTransformations } from 'react-css-transform';
2+
import { useGlobalContext } from './GlobalContext';
3+
import { useFlatShading } from '../useFlatShading';
4+
import { vec3 } from 'gl-matrix';
5+
import { useMemo } from 'react';
6+
import { blendHex, mergeMatrixWithStyles } from '../utils';
7+
8+
import type { CSSProperties } from 'react';
9+
import type { Transform3dProps } from 'react-css-transform';
10+
11+
type FaceProps = Transform3dProps & {
12+
color: string;
13+
style: CSSProperties;
14+
};
15+
16+
// local position is always 0, outer transform will apply positioning
17+
const objectLocalPosition = vec3.fromValues(0, 0, 0);
18+
// local normal is always z: 1, outer transform will apply positioning
19+
const objectLocalNormal = vec3.fromValues(0, 0, 1);
20+
21+
export const Face = ({ color, style, ...transform3dProps }: FaceProps) => {
22+
const { matrixWorld } = use3dTransformations(transform3dProps);
23+
24+
const { lightPosition, lightColor } = useGlobalContext();
25+
26+
const { diffuseColor, emissiveColor } = useMemo(() => {
27+
return {
28+
diffuseColor: blendHex(color, '#ffffff', 0.1),
29+
emissiveColor: blendHex(color, '#000000', 0.333),
30+
};
31+
}, [color]);
32+
33+
const { outputColorString } = useFlatShading({
34+
diffuseColor,
35+
emissiveColor,
36+
lightColor,
37+
objectLocalPosition,
38+
objectLocalNormal,
39+
lightWorldPosition: lightPosition,
40+
parentMatrixWorld: matrixWorld.current,
41+
});
42+
43+
const faceStyle = useMemo(() => {
44+
return mergeMatrixWithStyles(matrixWorld.current, {
45+
...style,
46+
backgroundColor: outputColorString,
47+
});
48+
}, [matrixWorld, style, outputColorString]);
49+
50+
return <div style={faceStyle} className="face" />;
51+
};

examples/3d-cubes/src/components/FlatMaterial.tsx

Lines changed: 0 additions & 128 deletions
This file was deleted.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { createContext, useContext, useEffect, useMemo, useState } from 'react';
2+
import { vec3 } from 'gl-matrix';
3+
import type { ReactNode } from 'react';
4+
5+
type GlobalState = {
6+
width: number;
7+
height: number;
8+
perspective: number;
9+
lightPosition: vec3;
10+
lightColor: string;
11+
};
12+
13+
const getDefaultState = (width: number, height: number): GlobalState => ({
14+
width,
15+
height,
16+
perspective: (500 / 750) * height,
17+
lightPosition: vec3.fromValues(width / 3, height / 3, 0),
18+
lightColor: '#ffffff',
19+
});
20+
21+
const updateGlobalStateDimensions = (
22+
width: number,
23+
height: number,
24+
state: GlobalState,
25+
): GlobalState => {
26+
return {
27+
...state,
28+
width,
29+
height,
30+
perspective: (500 / 750) * height,
31+
lightPosition: vec3.set(state.lightPosition, width / 3, height / 3, 0),
32+
};
33+
};
34+
35+
const GlobalContext = createContext<GlobalState>(
36+
getDefaultState(window.innerWidth, window.innerHeight),
37+
);
38+
39+
export const useGlobalContext = () => useContext<GlobalState>(GlobalContext);
40+
41+
type DimensionsProviderProps = {
42+
children: ReactNode;
43+
};
44+
export const GlobalStateProvider = ({ children }: DimensionsProviderProps) => {
45+
const [globalState, setGlobalState] = useState(() =>
46+
getDefaultState(window.innerWidth, window.innerHeight),
47+
);
48+
49+
useEffect(() => {
50+
const handleResize = () => {
51+
setGlobalState((state) =>
52+
updateGlobalStateDimensions(
53+
window.innerWidth,
54+
window.innerHeight,
55+
state,
56+
),
57+
);
58+
};
59+
60+
window.addEventListener('resize', handleResize);
61+
return () => window.removeEventListener('resize', handleResize);
62+
}, []);
63+
64+
return (
65+
<GlobalContext.Provider value={globalState}>
66+
{children}
67+
</GlobalContext.Provider>
68+
);
69+
};

examples/3d-cubes/src/index.tsx

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,20 @@
1-
import {
2-
useCallback,
3-
useEffect,
4-
useMemo,
5-
useState,
6-
} from 'react';
1+
import { useCallback, useEffect, useMemo, useState } from 'react';
72
import { createRoot } from 'react-dom/client';
83
import { Transform3d, useFactoryRef } from 'react-css-transform';
94
import { vec3 } from 'gl-matrix';
105
import { CubeGroup } from './components/CubeGroup';
116
import {
12-
DimensionsProvider,
13-
useDimensionsContext,
14-
} from './components/DimensionsContext';
7+
GlobalStateProvider,
8+
useGlobalContext,
9+
} from './components/GlobalContext';
1510
import { yAxis, zAxis } from './constants';
1611

1712
import './styles.css';
1813

1914
const frameTime = 1000 / 60;
2015

2116
const BaseApp = () => {
22-
const { width, height, perspective } = useDimensionsContext();
17+
const { width, height, perspective } = useGlobalContext();
2318
const appStyle = useMemo(
2419
() => ({
2520
perspective,
@@ -94,9 +89,9 @@ const BaseApp = () => {
9489

9590
const App = () => {
9691
return (
97-
<DimensionsProvider>
92+
<GlobalStateProvider>
9893
<BaseApp />
99-
</DimensionsProvider>
94+
</GlobalStateProvider>
10095
);
10196
};
10297

0 commit comments

Comments
 (0)