diff --git a/packages/client/src/gfx/Map3D.tsx b/packages/client/src/gfx/Map3D.tsx index f6cef23..d29ee29 100644 --- a/packages/client/src/gfx/Map3D.tsx +++ b/packages/client/src/gfx/Map3D.tsx @@ -11,6 +11,8 @@ import * as THREE from 'three'; import { Board, Unit, GameMap, UnitId, Position } from '@bananu7-rts/server/src/types' import { SelectionBox } from './SelectionBox' +import { mapColor, mapHeight, explode } from './map_display' +import { MapBorder } from './MapBorder' type Click = (originalEvent: ThreeEvent, p: Position, button: number, shift: boolean) => void; type RawClick = (e: ThreeEvent) => void; @@ -24,58 +26,6 @@ type Map3DProps = { pointerMove: (p: {x: number, y: number}) => void; } -function tileTypeToColor(tileType: number, vec3Color: THREE.Color) { - const isPassable = tileType === 0; - - switch (tileType) { - case 0: { - const color = 0x11aa11; - vec3Color.set(color); - const f = 0.06; - vec3Color.r += (Math.random() - 0.5) * f; - vec3Color.g += (Math.random() - 0.5) * f; - vec3Color.b += (Math.random() - 0.5) * f; - break; - } - - case 2: { - const color = 0x3377cc; - vec3Color.set(color); - const f = 0.06; - vec3Color.r += (Math.random() - 0.5) * f; - vec3Color.g += (Math.random() - 0.5) * f; - vec3Color.b += (Math.random() - 0.5) * f; - break; - } - - case 1: - default: { - const color = 0x888888; - vec3Color.set(color); - const d = (Math.random() - 0.5) * 0.1; - vec3Color.r += d; - vec3Color.g += d; - vec3Color.b += d; - } - } -} - -function tileTypeToHeight(tileType: number): number { - const correction = 0.01; - switch (tileType) { - case 0: - return 0 - correction; - - case 2: - return -0.5 - Math.random() * 0.7 - correction - - case 1: - default: - return 0.8 + Math.random() * 0.7 - correction; - } -} - - export function Map3D(props: Map3DProps) { // movement const rawClick = (e: ThreeEvent) => { @@ -109,9 +59,6 @@ export function Map3D(props: Map3DProps) { const w = props.map.w; const h = props.map.h; - const xSize = 1; - const ySize = 1; - const ref = useRef(null); useLayoutEffect(() => { if (!ref.current) @@ -122,15 +69,13 @@ export function Map3D(props: Map3DProps) { for (let y = 0; y < h; y++){ for (let x = 0; x < w; x++) { - const ix = y*props.map.w+x; - - const tileType = props.map.tiles[ix]; - const color = tileTypeToColor(tileType, vec3Color); - const height = tileTypeToHeight(tileType); + const color = mapColor(props.map, x, y, vec3Color); + const height = mapHeight(props.map, x, y); // TODO - make sure that everything matches with that corrective offset - mat4Pos.makeTranslation(x * xSize + 0.5, height, y * ySize + 0.5); // TODO -1 to move them down because of their height + mat4Pos.makeTranslation(x + 0.5, height-9, y + 0.5); // TODO -1 to move them down because of their height + const ix = explode(props.map, x, y); ref.current.setMatrixAt(ix, mat4Pos); ref.current.setColorAt(ix, vec3Color); } @@ -150,9 +95,9 @@ export function Map3D(props: Map3DProps) { onPointerDown={pointerDown} onPointerUp={pointerUp} onPointerMove={pointerMove} - position={[0.5*w, 0, ySize*0.5*h]} + position={[0.5*w, 0, 0.5*h]} > - + @@ -163,11 +108,14 @@ export function Map3D(props: Map3DProps) { ref={ref} args={[undefined, undefined, w*h]} receiveShadow + castShadow > - {/**/} - + {/**/} + + + ); -} \ No newline at end of file +} diff --git a/packages/client/src/gfx/MapBorder.tsx b/packages/client/src/gfx/MapBorder.tsx new file mode 100644 index 0000000..68ca5e3 --- /dev/null +++ b/packages/client/src/gfx/MapBorder.tsx @@ -0,0 +1,137 @@ +import { useRef, useLayoutEffect } from 'react' +import * as THREE from 'three'; + +import { tileTypeToColor, tileTypeToHeight } from './map_display' + +export type MapBorderProps = { + w: number, + h: number, +} + +export function MapBorder(props: MapBorderProps) { + const w = props.w; + const h = props.h; + + const borderSize = 30; + + /* + + XAAAAY + CMMMMD + CMMMMD + CMMMMD + CMMMMD + ZBBBBW + + */ + + const borderTilesCount = + borderSize * w * 2 + // horizontal + borderSize * h * 2 + // vertical + borderSize * borderSize * 4; // corners + + const ref = useRef(null); + useLayoutEffect(() => { + if (!ref.current) + return; + + const mat4Pos = new THREE.Matrix4(); + const vec3Color = new THREE.Color(); + const tileType = 0; + + const createRectangle = ( + off: number, + x0: number, + y0: number, + x1: number, + y1: number, + f: (x: number, y:number) => number + ) => { + if (!ref.current) + return 0; + + for (let y = 0; y < y1-y0; y++){ + for (let x = 0; x < x1-x0; x++) { + const ix = y*(x1-x0)+x + off; + const color = tileTypeToColor(tileType, vec3Color); + + // outline darker + vec3Color.g *= Math.abs(f(x,y)); + + const height = tileTypeToHeight(tileType); + + mat4Pos.makeTranslation(x + 0.5 + x0, height-9, y + 0.5 + y0); + + ref.current.setMatrixAt(ix, mat4Pos); + ref.current.setColorAt(ix, vec3Color); + } + } + + return (y1-y0) * (x1-x0); + }; + + // TODO this code is pretty unreadable, but at least it's only called once + + let off = 0; + //top + off += createRectangle(off, 0, -borderSize, w, 0, (x,y) => y/borderSize); + //bottom + off += createRectangle(off, 0, h, w, h+borderSize, (x,y) => (borderSize-y)/borderSize); + // left + off += createRectangle(off, -borderSize, 0, 0, h, (x,y) => x/borderSize); + //right + off += createRectangle(off, w, 0, w+borderSize, h, (x,y) => (borderSize-x)/borderSize); + + // top-left + off += createRectangle(off, -borderSize, -borderSize, 0, 0, + (x,y) => { + const xi = borderSize-x; + const yi = borderSize-y; + return Math.max(1 - Math.sqrt(xi*xi+yi*yi)/Math.sqrt(borderSize*borderSize), 0); + } + ); + + // top-right + off += createRectangle(off, w, -borderSize, w+borderSize, 0, + (x,y) => { + const xi = x; + const yi = borderSize-y; + return Math.max(1 - Math.sqrt(xi*xi+yi*yi)/Math.sqrt(borderSize*borderSize), 0); + } + ); + + // bottom-left + off += createRectangle(off, -borderSize, h, 0, h+borderSize, + (x,y) => { + const xi = borderSize-x; + const yi = y; + return Math.max(1 - Math.sqrt(xi*xi+yi*yi)/Math.sqrt(borderSize*borderSize), 0); + } + ); + + // bottom-right + off += createRectangle(off, w, h, w+borderSize, h+borderSize, + (x,y) => { + const xi = x; + const yi = y; + return Math.max(1 - Math.sqrt(xi*xi+yi*yi)/Math.sqrt(borderSize*borderSize), 0); + } + ); + + + ref.current.instanceMatrix.needsUpdate = true; + if (ref.current.instanceColor) ref.current.instanceColor.needsUpdate = true; + }, [props.w, props.h]) + + return ( + + + + + ); +} \ No newline at end of file diff --git a/packages/client/src/gfx/MapLight.tsx b/packages/client/src/gfx/MapLight.tsx index e22b9b5..7f17dd4 100644 --- a/packages/client/src/gfx/MapLight.tsx +++ b/packages/client/src/gfx/MapLight.tsx @@ -45,10 +45,10 @@ export function MapLight(props: MapLightProps) { shadow-camera-left={-props.target.z} shadow-camera-right={props.target.z} - shadow-bias={-0.002} + shadow-bias={-0.00052} /> - ) + ) } diff --git a/packages/client/src/gfx/Unit3D.tsx b/packages/client/src/gfx/Unit3D.tsx index c772b55..6c7c4d3 100644 --- a/packages/client/src/gfx/Unit3D.tsx +++ b/packages/client/src/gfx/Unit3D.tsx @@ -144,7 +144,7 @@ export function Unit3D(props: Unit3DProps) { })(); const debugPath = props.unit.debug?.pathToNext?.map((a: any) => { - return new THREE.Vector3(a.x, 1, a.y); + return new THREE.Vector3(a.x, 1.2, a.y); }); const action = props.unit.state.action; diff --git a/packages/client/src/gfx/View3D.tsx b/packages/client/src/gfx/View3D.tsx index 2b3d58c..7361a7b 100644 --- a/packages/client/src/gfx/View3D.tsx +++ b/packages/client/src/gfx/View3D.tsx @@ -98,7 +98,7 @@ export function View3D(props: Props) { onPointerMissed={ props.onPointerMissed } dpr={1} > - + diff --git a/packages/client/src/gfx/map_display.ts b/packages/client/src/gfx/map_display.ts new file mode 100644 index 0000000..a0070c2 --- /dev/null +++ b/packages/client/src/gfx/map_display.ts @@ -0,0 +1,101 @@ +import * as THREE from 'three'; +import { GameMap } from '@bananu7-rts/server/src/types' + +export function explode(map: GameMap, x: number, y: number): number { + return y*map.w+x; +} + +export function mapColor(map: GameMap, x: number, y: number, vec3Color: THREE.Color) { + const ix = explode(map, x, y); + const tileType = map.tiles[ix]; + + let factor = 0.0; + + const include = (x: number, y: number, f: number) => { + if (x < 0 || y < 0 || x >= map.w || y >= map.h) + return; + + if (map.tiles[explode(map,x,y)] > 0) + factor += f; + }; + + const kernel: number[][] = [ + [0.10, 0.15, 0.15, 0.15, 0.10], + [0.15, 0.10, 0.10, 0.10, 0.15], + [0.15, 0.10, 0.00, 0.10, 0.15], + [0.15, 0.10, 0.10, 0.10, 0.15], + [0.10, 0.15, 0.15, 0.15, 0.10] + ]; + + for (let dx = 0; dx < 5; dx++) { + for (let dy = 0; dy < 5; dy++) { + include(x + dx-2, y + dy-2, kernel[dy][dx]); + } + } + + + factor = Math.min(factor, 1.0); + + tileTypeToColor(tileType, vec3Color); + if (tileType == 0) { + vec3Color.r = vec3Color.g * factor; + } +} + +export function tileTypeToColor(tileType: number, vec3Color: THREE.Color) { + const isPassable = tileType === 0; + + switch (tileType) { + case 0: { + const color = 0x11aa11; + vec3Color.set(color); + const f = 0.06; + vec3Color.r += (Math.random() - 0.5) * f; + vec3Color.g += (Math.random() - 0.5) * f; + vec3Color.b += (Math.random() - 0.5) * f; + break; + } + + case 2: { + const color = 0x3377cc; + vec3Color.set(color); + const f = 0.06; + vec3Color.r += (Math.random() - 0.5) * f; + vec3Color.g += (Math.random() - 0.5) * f; + vec3Color.b += (Math.random() - 0.5) * f; + break; + } + + case 1: + default: { + const color = 0x888888; + vec3Color.set(color); + const d = (Math.random() - 0.5) * 0.1; + vec3Color.r += d; + vec3Color.g += d; + vec3Color.b += d; + } + } +} + + +export function mapHeight(map: GameMap, x: number, y: number): number { + const ix = explode(map, x, y); + const tileType = map.tiles[ix]; + return tileTypeToHeight(tileType); +} + +export function tileTypeToHeight(tileType: number): number { + const correction = 0.001; + switch (tileType) { + case 0: + return 0 - correction; + + case 2: + return -0.5 - Math.random() * 0.7 - correction + + case 1: + default: + return 0.8 + Math.random() * 4.7 - correction; + } +}