Skip to content

Commit

Permalink
Support positions, colors, and animateCustom of particles
Browse files Browse the repository at this point in the history
  • Loading branch information
flyskywhy committed Oct 9, 2020
1 parent a57665c commit 6345e94
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 22 deletions.
21 changes: 17 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,19 +86,23 @@ const config = {
visible: true
},
particles: {
// 'rainbow' or 'solid' color of particles
// 'rainbow' or 'solid' or 'multi' color of particles
colorMode: 'rainbow',
// Color of lines if colorMode: 'solid', must be hex color
// Color of particles if colorMode: 'solid', must be hex color
color: '#3FB568',
// Colors of particles if colorMode: 'multi', must be hex color
colors: undefined, // ['#771112']
// Positions of particles
positions: undefined, // [{x: 20, y: 5, z: 3}],
// Transparency of particles
count: 500, // or positions.length above
// The minimum particle size
transparency: 0.9,
// 'square' or 'circle' shape of particles
shape: 'square',
// 'canvas' or 'cube' boundary of particles
boundingBox: 'canvas',
// The exact number of particles to render
count: 500,
// The minimum particle size
minSize: 10,
// The maximum particle size
maxSize: 75,
Expand Down Expand Up @@ -132,6 +136,15 @@ const config = {
export default () => <ParticleField config={config} />;
```

If you want to animateCustom instead of src/lib/animates.js :
```
let particleState = {};
...
<ParticleField config={config} state={particleState} />;
...
animateCustom(particleState.current);
```

## Local Development

Clone the repo
Expand Down
17 changes: 14 additions & 3 deletions src/ParticleField.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const r = 400;
* Creates a particle cloud with various config options
*/
const ParticleField = ({
state,
particles,
lines,
direction,
Expand Down Expand Up @@ -90,6 +91,7 @@ const ParticleField = ({
pointMaterial,
particlesData,
particlePositions,
particleColors,
bounds
] = useMemo(
() =>
Expand Down Expand Up @@ -117,20 +119,29 @@ const ParticleField = ({
pointCloudGeometry,
particlesData,
particlePositions,
particleColors,
linePositions,
lineColors,
showLines: lines.visible,
boundaryType
};

if (state) {
state.current = animation.current;
}

// Direct access to render loop, executes on each frame
// State changes must be passed into hook via refs
// useFrame() contents are called in a requestAnimationFrame()
useFrame(() => {
// Enables damping of OrbitControls
controlsRef.current.update();
// Animate current state of particles + lines
animate(animation.current);

if (!state) {
// If no state to be used by animateCustom,
// then animate current state of particles + lines
animate(animation.current);
}
});

return (
Expand Down Expand Up @@ -196,7 +207,7 @@ ParticleField.propTypes = {
maxSize: PropTypes.number,
boundingBox: PropTypes.oneOf(['canvas', 'cube']),
shape: PropTypes.oneOf(['circle', 'square']),
colorMode: PropTypes.oneOf(['rainbow', 'solid']),
colorMode: PropTypes.oneOf(['rainbow', 'solid', 'multi']),
color: PropTypes.string,
transparency: PropTypes.number,
visible: PropTypes.bool
Expand Down
4 changes: 3 additions & 1 deletion src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@ export default {
particles: {
colorMode: 'rainbow',
color: '#3FB568',
colors: undefined,
positions: undefined,
count: 500,
transparency: 0.9,
shape: 'square',
boundingBox: 'canvas',
count: 500,
minSize: 10,
maxSize: 75,
visible: true
Expand Down
4 changes: 2 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import initialConfig from './config';
* For a real-time configuration generator and various demos
* @see https://timellenberger.com/particles
*/
const ParticleCanvas = ({ config }) => {
const ParticleCanvas = ({ config, state }) => {
const [clientSide, setClientSide] = useState(false);
useEffect(() => {
setClientSide(true);
Expand All @@ -30,7 +30,7 @@ const ParticleCanvas = ({ config }) => {
: initialConfig.antialias
}}
>
<ParticleField {...merge({}, initialConfig, config)} />
<ParticleField {...merge({}, initialConfig, config)} state={state} />
</Canvas>
);
};
Expand Down
55 changes: 45 additions & 10 deletions src/lib/computeParticles.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
ShaderMaterial,
Vector3
} from 'three';
import hexRgb from 'hex-rgb';
import isHex from 'is-hexcolor';
import {
getParticleVertexShader,
getParticleFragmentShader
Expand All @@ -23,6 +25,8 @@ export default ({
const {
boundingBox,
count,
positions,
colors,
colorMode,
color,
shape,
Expand All @@ -33,11 +37,12 @@ export default ({
} = particles;
// Add particles to geometry
// Maintain two arrays
// particlePositions contains random x,y,z coords for each particle
// particlePositions contains x,y,z coords for each particle
// particlesData contains a random x,y,z velocity vector for each particle
const pointCloudGeometry = new BufferGeometry();
const particlePositions = new Float32Array(count * 3);
const particleSizes = new Float32Array(count);
const particleColors = new Float32Array(count * 3);
const particlesData = [];

let xBounds;
Expand All @@ -56,16 +61,26 @@ export default ({
zBounds = dimension === '2D' ? 0 : r;
}

for (let i = 0; i < count; i += 1) {
// Calculate possible (x, y, z) location of particle
// within the size of the canvas or cube size
const x = Math.random() * xBounds - xBounds / 2;
const y = Math.random() * yBounds - yBounds / 2;
const z = Math.random() * zBounds - zBounds / 2;
particlePositions[i * 3] = x;
particlePositions[i * 3 + 1] = y;
particlePositions[i * 3 + 2] = z;
if (positions) {
for (let i = 0; i < count; i += 1) {
particlePositions[i * 3] = positions[i].x;
particlePositions[i * 3 + 1] = positions[i].y;
particlePositions[i * 3 + 2] = positions[i].z;
}
} else {
for (let i = 0; i < count; i += 1) {
// Calculate possible (x, y, z) location of particle
// within the size of the canvas or cube size
const x = Math.random() * xBounds - xBounds / 2;
const y = Math.random() * yBounds - yBounds / 2;
const z = Math.random() * zBounds - zBounds / 2;
particlePositions[i * 3] = x;
particlePositions[i * 3 + 1] = y;
particlePositions[i * 3 + 2] = z;
}
}

for (let i = 0; i < count; i += 1) {
// Choose size of each particle
particleSizes[i] = Math.random() * (maxSize - minSize) + minSize;

Expand All @@ -85,6 +100,21 @@ export default ({
});
}

if (colors) {
for (let i = 0; i < count; i += 1) {
if (isHex(colors[i])) {
const { red, green, blue } = hexRgb(colors[i]);
particleColors[i * 3] = (red / 255).toFixed(2);
particleColors[i * 3 + 1] = (green / 255).toFixed(2);
particleColors[i * 3 + 2] = (blue / 255).toFixed(2);
} else {
particleColors[i * 3] = 1;
particleColors[i * 3 + 1] = 1;
particleColors[i * 3 + 2] = 1;
}
}
}

pointCloudGeometry.setDrawRange(0, count);
pointCloudGeometry.setAttribute(
'position',
Expand All @@ -94,6 +124,10 @@ export default ({
'size',
new BufferAttribute(particleSizes, 1).setUsage(DynamicDrawUsage)
);
pointCloudGeometry.setAttribute(
'color',
new BufferAttribute(particleColors, 3).setUsage(DynamicDrawUsage)
);

// Material for particle, use shaders to morph shape and color
const pointMaterial = new ShaderMaterial({
Expand Down Expand Up @@ -124,6 +158,7 @@ export default ({
pointMaterial,
particlesData,
particlePositions,
particleColors,
bounds
];
};
5 changes: 3 additions & 2 deletions src/shaders/ParticleShaders.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@ export const getParticleVertexShader = ({
}) => `
// Size attribute for particle geometry
attribute float size;
attribute vec3 color;
// Calculate color based on particle position
// Calculate color based on particle position or attribute
varying vec3 vColor;
void main() {
Expand All @@ -56,6 +57,7 @@ void main() {
${colorMode === 'rainbow' ? rainbowVertextColors : ''}
${colorMode === 'solid' ? solidVertexColors({ color }) : ''}
${colorMode === 'multi' ? 'vColor = color;' : ''}
}
`;

Expand All @@ -78,7 +80,6 @@ if (r > 1.0) {
*/
export const getParticleFragmentShader = ({ particleShape, transparency }) => `
// Color from uniforms arg
uniform vec3 color;
// Color calculated from vertex shader, based on particle position
varying vec3 vColor;
Expand Down

0 comments on commit 6345e94

Please sign in to comment.