Skip to content

Commit

Permalink
Merge pull request #29 from bananu7/performance-2
Browse files Browse the repository at this point in the history
Performance 2
  • Loading branch information
bananu7 authored Oct 18, 2022
2 parents 941c99d + 47c4d92 commit 5ce66ac
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 67 deletions.
1 change: 1 addition & 0 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"copy": "rm -rf ../server/client && cp -R dist ../server/client",
"preview": "vite preview"
},
"dependencies": {
Expand Down
24 changes: 8 additions & 16 deletions packages/client/src/gfx/Board3D.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useState, useRef, Suspense, useLayoutEffect, useMemo } from 'react'
import { useCallback, useEffect, useState, useRef, Suspense, useLayoutEffect, useMemo, memo, Ref } from 'react'

import {
useLoader, Canvas, useFrame,
Expand Down Expand Up @@ -47,7 +47,11 @@ export interface Props {
}

export function Board3D(props: Props) {
const [pointer, setPointer] = useState<{x:number, y:number}>({x: 0, y: 0});
const pointer = useRef({ x: 0, y: 0 });
const setPointer = useCallback((p: Position) => {
pointer.current.x = p.x;
pointer.current.y = p.y;
}, [pointer]);

const units = props.unitStates.map(u =>
(<Unit3D
Expand Down Expand Up @@ -81,20 +85,8 @@ export function Board3D(props: Props) {
props.select(new Set(selection), shift);
};

useEffect(() => {
if (!groupRef.current)
return;

/*
const box = new THREE.Box3().setFromObject(groupRef.current);
const size = box.getSize(new THREE.Vector3()).length();
const center = box.getCenter(new THREE.Vector3());
groupRef.current.position.sub(center);
*/
});

return (
<group ref={groupRef} dispose={null} name="ship">
<group name="board">
<Map3D
map={props.board.map}
click={props.mapClick}
Expand All @@ -105,7 +97,7 @@ export function Board3D(props: Props) {
{
props.selectedAction &&
props.selectedAction.action === 'Build' &&
<BuildPreview building={props.selectedAction.building} position={pointer}/>
<BuildPreview building={props.selectedAction.building} position={pointer.current}/>
}
</group>
);
Expand Down
65 changes: 41 additions & 24 deletions packages/client/src/gfx/Map3D.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@

import { useEffect, useState, useRef, useLayoutEffect, useCallback } from 'react'
import { useEffect, useState, useRef, useLayoutEffect, useCallback, memo } from 'react'

import {
ThreeEvent
ThreeEvent,
useFrame
} from '@react-three/fiber'

import * as THREE from 'three';
Expand Down Expand Up @@ -30,30 +31,25 @@ export function Map3D(props: Map3DProps) {
};

// selection box
const [drag, setDrag] = useState<{x:number, y:number}|undefined>(undefined);
const [pointer, setPointer] = useState<{x:number, y:number}|undefined>(undefined);
const drag = useRef<{x: number, y: number} | undefined>(undefined);
const pointer = useRef({ x: 0, y: 0 });
const pointerDown = useCallback((e: ThreeEvent<PointerEvent>) => {
if (e.nativeEvent.button === 0)
setDrag({x: e.point.x, y: e.point.z});
drag.current = {x: e.point.x, y: e.point.z};
}, []);
const pointerMove = useCallback((e: ThreeEvent<PointerEvent>) => {
setPointer({x: e.point.x, y: e.point.z});
pointer.current.x = e.point.x;
pointer.current.y = e.point.z;;
props.pointerMove({x: e.point.x, y: e.point.z});
}, [props.pointerMove]);
const pointerUp = useCallback((e: ThreeEvent<PointerEvent>) => {
// TODO - only do select if no action?
// maybe send drag up instead of handling it here
if (drag && e.nativeEvent.button === 0) {
props.selectInBox({x1: drag.x, y1: drag.y, x2: e.point.x, y2: e.point.z}, e.nativeEvent.shiftKey);
if (drag.current && e.nativeEvent.button === 0) {
props.selectInBox({x1: drag.current.x, y1: drag.current.y, x2: e.point.x, y2: e.point.z}, e.nativeEvent.shiftKey);
}
setDrag(undefined);
setPointer(undefined);
}, [drag, props.selectInBox]);

const selectionBoxSize = (drag && pointer) ? {
x: Math.abs(drag.x - pointer.x),
y: Math.abs(drag.y - pointer.y)
} : undefined;
drag.current = undefined;
}, [drag.current, props.selectInBox]);

// actual map
const w = props.map.w;
Expand Down Expand Up @@ -90,6 +86,30 @@ export function Map3D(props: Map3DProps) {
if (ref.current.instanceColor) ref.current.instanceColor.needsUpdate = true;
}, [props.map])

const sbxRef = useRef<THREE.Mesh>(null);
useFrame(() => {
if (!sbxRef.current)
return;

if (!drag.current) {
sbxRef.current.visible = false;
return;
} else {
sbxRef.current.visible = true;
}

const selectionBoxSize = {
x: Math.abs(drag.current.x - pointer.current.x),
y: Math.abs(drag.current.y - pointer.current.y)
};

const sbx = pointer.current.x - selectionBoxSize.x / 2 * (pointer.current.x > drag.current.x ? 1 : -1);
const sby = pointer.current.y - selectionBoxSize.y / 2 * (pointer.current.y > drag.current.y ? 1 : -1);

sbxRef.current.position.set(sbx, 2, sby);
sbxRef.current.scale.set(selectionBoxSize.x, selectionBoxSize.y, 1);
})

return (
<group name="Game Map">
<mesh
Expand All @@ -105,18 +125,15 @@ export function Map3D(props: Map3DProps) {
<meshBasicMaterial opacity={0} transparent={true} />
</mesh>

{drag && pointer && selectionBoxSize && <mesh
<mesh
name="SelectionBox"
position={[
pointer.x - selectionBoxSize.x / 2 * (pointer.x > drag.x ? 1 : -1),
2,
pointer.y - selectionBoxSize.y / 2 * (pointer.y > drag.y ? 1 : -1)
]}
position={[0,2,0]}
rotation={[-Math.PI/2, 0, 0]}
ref={sbxRef}
>
<planeGeometry args={[selectionBoxSize.x, selectionBoxSize.y]}/>
<planeGeometry args={[1, 1]}/>
<meshBasicMaterial wireframe color={0x00ff00} />
</mesh>}
</mesh>

<instancedMesh
ref={ref}
Expand Down
24 changes: 14 additions & 10 deletions packages/client/src/gfx/SelectionCircle.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import * as THREE from 'three';
import { useRef, useEffect } from 'react'

const enemyMaterial = new THREE.MeshBasicMaterial({
color: 0xff0000
});

const friendlyMaterial = new THREE.MeshBasicMaterial({
color: 0x00ff00
});

const ringGeometry = new THREE.RingGeometry(1, 1.1, 32);

export function SelectionCircle(props: { size: number, enemy?: boolean }) {
const innerRadius = props.size;
const outerRadius = props.size * 1.1;
const segments = 32;

const ref = useRef<THREE.RingGeometry>(null);
const ref = useRef<THREE.Mesh>(null);

useEffect(() => {
if (!ref.current)
Expand All @@ -16,18 +26,12 @@ export function SelectionCircle(props: { size: number, enemy?: boolean }) {

return (
<mesh
ref={ref}
name="SelectionRing"
position={[0, 0, 0]}
material={props.enemy ? enemyMaterial : friendlyMaterial}
geometry={ringGeometry}
>
<ringGeometry
ref={ref}
args={[innerRadius, outerRadius, segments]}
/>
<meshBasicMaterial
color={props.enemy ? 0xff0000 : 0x00ff00}
opacity={1.0}
transparent={false}
/>
</mesh>
);
}
56 changes: 56 additions & 0 deletions packages/client/src/gfx/ThreeCache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import * as THREE from 'three';

// TODO - should this be usable from a hook?

// This class is used to avoid recreating the three geometry with the same parameters
export class ThreeCache {
constructor() { }

boxes: Map<number, THREE.BoxGeometry> = new Map();
getBoxGeometry(size: number) {
const cached = this.boxes.get(size);
if (cached) {
return cached;
} else {
const geometry = new THREE.BoxGeometry(size, 2, size);
this.boxes.set(size, geometry);
return geometry;
}
}

cylinders: Map<number, THREE.CylinderGeometry> = new Map();
getCylinderGeometry(size: number) {
const cached = this.cylinders.get(size);
if (cached) {
return cached;
} else {
const geometry = new THREE.CylinderGeometry(size, size, 2, 12);
this.cylinders.set(size, geometry);
return geometry;
}
}

standardMaterials: Map<number, THREE.MeshStandardMaterial> = new Map();
getStandardMaterial(color: number) {
const cached = this.standardMaterials.get(color);
if (cached) {
return cached;
} else {
const material = new THREE.MeshStandardMaterial({color});
this.standardMaterials.set(color, material);
return material;
}
}

basicMaterials: Map<number, THREE.MeshBasicMaterial> = new Map();
getBasicMaterial(color: number) {
const cached = this.basicMaterials.get(color);
if (cached) {
return cached;
} else {
const material = new THREE.MeshBasicMaterial({color});
this.basicMaterials.set(color, material);
return material;
}
}
}
45 changes: 28 additions & 17 deletions packages/client/src/gfx/Unit3D.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,18 @@ import { Board, Unit, GameMap, UnitId, Position, UnitState } from 'server/types'
import { SelectionCircle } from './SelectionCircle'
import { Line3D } from './Line3D'
import { Map3D, Box } from './Map3D'
import { ThreeCache } from './ThreeCache'

const cache = new ThreeCache();

const invisibleMaterial = new THREE.MeshBasicMaterial({
colorWrite: false,
depthWrite: false,
transparent: true,
opacity:0,
});

const coneGeometry = new THREE.ConeGeometry(0.5, 2, 8);
function ConeIndicator(props: {unit: UnitState, smoothing: boolean}) {
// TODO - this will be replaced with animations etc
let indicatorColor = 0xeeeeee;
Expand All @@ -31,10 +42,13 @@ function ConeIndicator(props: {unit: UnitState, smoothing: boolean}) {
indicatorColor = 0xffff55;

return (
<mesh position={[0, 5, 0]} rotation={[0, -props.unit.direction, -1.57]}>
<coneGeometry args={[0.5, 2, 8]} />
<meshBasicMaterial color={indicatorColor} />
</mesh>
<mesh
position={[0, 5, 0]}
// TODO unit should rotate
rotation={[0, 0, -1.57]}
geometry={coneGeometry}
material={cache.getBasicMaterial(indicatorColor)}
/>
);
}

Expand All @@ -56,11 +70,12 @@ export function Unit3D(props: Unit3DProps) {
}

// TODO better color choices
const ownerToColor = (owner: number) => {
const ownerToColor = (owner: number): number => {
switch(owner) {
case 0: return 0xdddddd;
case 1: return 0x1111ee;
case 2: return 0xee1111;
default: return 0xff00ff;
}
};

Expand Down Expand Up @@ -108,6 +123,8 @@ export function Unit3D(props: Unit3DProps) {
if(!unitGroupRef.current)
return;

unitGroupRef.current.rotation.y = -props.unit.direction;

// TODO - temporary fix to bring units where they're needed quickly
if (softSnapVelocity.x > 5 || softSnapVelocity.y > 5) {
unitGroupRef.current.position.x = props.unit.position.x;
Expand All @@ -121,7 +138,6 @@ export function Unit3D(props: Unit3DProps) {

return (
<group>
{/*<Line3D points={path} />*/}
<group
ref={unitGroupRef}
position={[0, 1, 0]}
Expand All @@ -133,13 +149,9 @@ export function Unit3D(props: Unit3DProps) {
<mesh
onContextMenu={ onClick }
onClick={ onClick }
>
<cylinderGeometry args={[selectorSize, selectorSize, 2, 12]} />
<meshBasicMaterial
colorWrite={false}
depthWrite={false}
/>
</mesh>
geometry={cache.getCylinderGeometry(selectorSize)}
material={invisibleMaterial}
/>

{ props.selected &&
<SelectionCircle size={selectorSize} enemy={props.enemy} />
Expand All @@ -148,10 +160,9 @@ export function Unit3D(props: Unit3DProps) {
<mesh
castShadow
receiveShadow
>
<boxGeometry args={[unitSize, 2, unitSize]} />
<meshStandardMaterial color={color} />
</mesh>
geometry={cache.getBoxGeometry(unitSize)}
material={cache.getStandardMaterial(color)}
/>
</group>
</group>
);
Expand Down
1 change: 1 addition & 0 deletions packages/server/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
client/

0 comments on commit 5ce66ac

Please sign in to comment.