Skip to content

Commit

Permalink
✨ boids: add spawn/destroy buttons, use spawn pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
krispya committed Oct 24, 2024
1 parent 9a88dcd commit df735a5
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 55 deletions.
4 changes: 4 additions & 0 deletions benches/apps/boids/src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ export const useActions = createActions((world) => ({
spawnBoid: (position: THREE.Vector3, velocity: THREE.Vector3) => {
world.spawn(Position(position), Velocity(velocity), Neighbors, Forces);
},
destroyRandomBoid: () => {
const entities = world.query(Position, Velocity, Neighbors, Forces);
if (entities.length) entities[Math.floor(Math.random() * entities.length)].destroy();
},
destroyAllBoids: () => {
world.query(Position, Velocity, Neighbors, Forces).forEach((entity) => {
entity.destroy();
Expand Down
86 changes: 55 additions & 31 deletions benches/apps/boids/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

import { OrbitControls, PerspectiveCamera } from '@react-three/drei';
import { Canvas, useFrame } from '@react-three/fiber';
import { useEntityRef, useWorld } from 'koota/react';
import { StrictMode, useLayoutEffect } from 'react';
import { Entity } from 'koota';
import { useQuery, useWorld } from 'koota/react';
import { memo, StrictMode, useCallback, useLayoutEffect } from 'react';
import * as THREE from 'three';
import { useActions } from './actions';
import { schedule } from './systems/schedule';
import { BoidsConfig, Position, SpatialHashMap } from './traits';
import { InstancedMesh } from './traits/instanced-mesh';
import { BoidsConfig, Position, SpatialHashMap, Velocity } from './traits';
import { Mesh } from './traits';
import { between } from './utils/between';
import { useStats } from './utils/use-stats';

Expand All @@ -17,7 +18,7 @@ export function App() {
const { spawnBoid, destroyAllBoids } = useActions();

useLayoutEffect(() => {
const { count } = world.get(BoidsConfig);
const { initialCount: count } = world.get(BoidsConfig);

for (let i = 0; i < count; i++) {
const position = new THREE.Vector3().randomDirection().multiplyScalar(between(0, 10));
Expand All @@ -35,18 +36,10 @@ export function App() {

return (
<>
<button
style={{ position: 'absolute', top: 0, right: 0, zIndex: 1 }}
onClick={() => {
const position = new THREE.Vector3()
.randomDirection()
.multiplyScalar(between(0, 10));
const velocity = new THREE.Vector3().randomDirection();
spawnBoid(position, velocity);
}}
>
Spawn Boid
</button>
<div style={{ position: 'absolute', top: 0, right: 0, zIndex: 1 }}>
<SpawnButton />
<DestroyButton />
</div>
<Canvas>
<StrictMode>
<ambientLight intensity={Math.PI * 0.2} />
Expand All @@ -65,22 +58,33 @@ export function App() {
}

function Boids() {
const world = useWorld();
const { count } = world.get(BoidsConfig);
const geo = new THREE.IcosahedronGeometry();
const mat = new THREE.MeshStandardMaterial({ color: 'hotpink' });

const entityRef = useEntityRef<THREE.InstancedMesh>((node, entity) => {
// Set initial scale to zero
for (let i = 0; i < node.count; i++) {
node.setMatrixAt(i, new THREE.Matrix4().makeScale(0, 0, 0));
}
entity.add(InstancedMesh({ object: node }));
});

return <instancedMesh ref={entityRef} args={[geo, mat, count * 2]} />;
const boids = useQuery(Position, Velocity);
return (
<>
{boids.map((boid) => (
<Boid key={boid.id()} entity={boid} />
))}
</>
);
}

const Boid = memo(({ entity }: { entity: Entity }) => {
const entityRef = useCallback(
(node: THREE.Mesh) => {
if (node) entity?.add(Mesh(node));
else entity?.remove(Mesh);
},
[entity]
);

return (
<mesh ref={entityRef} scale={0.5}>
<icosahedronGeometry />
<meshStandardMaterial color="hotpink" />
</mesh>
);
});

// Simulation runs a schedule.
function Simulation() {
const world = useWorld();
Expand All @@ -95,3 +99,23 @@ function Simulation() {

return null;
}

function SpawnButton() {
const { spawnBoid } = useActions();
return (
<button
onClick={() => {
const position = new THREE.Vector3().randomDirection().multiplyScalar(between(0, 10));
const velocity = new THREE.Vector3().randomDirection();
spawnBoid(position, velocity);
}}
>
Spawn Boid
</button>
);
}

function DestroyButton() {
const { destroyRandomBoid } = useActions();
return <button onClick={destroyRandomBoid}>Destroy Boid</button>;
}
20 changes: 3 additions & 17 deletions benches/apps/boids/src/systems/sync-three-object.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,8 @@
import * as THREE from 'three';
import { InstancedMesh } from '../traits/instanced-mesh';
import { World } from 'koota';
import { Position } from '../traits';

const dummy = new THREE.Object3D();
import { Mesh, Position } from '../traits';

export const syncThreeObjects = ({ world }: { world: World }) => {
const instanceEntity = world.queryFirst(InstancedMesh);
if (instanceEntity === undefined) return;

const instancedMesh = instanceEntity.get(InstancedMesh)!.object;

world.query(Position).updateEach(([position], entity) => {
dummy.position.copy(position);
dummy.scale.set(0.5, 0.5, 0.5);
dummy.updateMatrix();
instancedMesh.setMatrixAt(entity.id(), dummy.matrix);
world.query(Position, Mesh).updateEach(([position, mesh]) => {
mesh.position.copy(position);
});

instancedMesh.instanceMatrix.needsUpdate = true;
};
2 changes: 1 addition & 1 deletion benches/apps/boids/src/traits/boids-config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { trait } from 'koota';

export const BoidsConfig = trait({
count: 1000,
initialCount: 1000,
maxVelocity: 1,
separationFactor: 1,
alignmentFactor: 1,
Expand Down
2 changes: 1 addition & 1 deletion benches/apps/boids/src/traits/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export * from './position.ts';
export * from './velocity.ts';
export * from './time.ts';
export * from './spatial-hash-map.ts';
export * from './instanced-mesh.ts';
export * from './mesh.ts';
export * from './neighbors.ts';
export * from './forces.ts';
export * from './boids-config.ts';
4 changes: 0 additions & 4 deletions benches/apps/boids/src/traits/instanced-mesh.ts

This file was deleted.

4 changes: 4 additions & 0 deletions benches/apps/boids/src/traits/mesh.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import * as THREE from 'three';
import { trait } from 'koota';

export const Mesh = trait(() => new THREE.Mesh());
2 changes: 1 addition & 1 deletion benches/apps/boids/src/world.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const world = createWorld(
Time,
SpatialHashMap(new SpatialHashMapImpl(5)),
BoidsConfig({
count: 1000,
initialCount: 500,
maxVelocity: 6,
separationFactor: 16,
alignmentFactor: 0.5,
Expand Down

0 comments on commit df735a5

Please sign in to comment.