Skip to content

Commit

Permalink
feat(web): add 3d map viewer
Browse files Browse the repository at this point in the history
  • Loading branch information
fallenoak committed Dec 30, 2023
1 parent 037c29d commit 00ab8f5
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 17 deletions.
59 changes: 44 additions & 15 deletions packages/spelunker-web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion packages/spelunker-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
],
"dependencies": {
"@apollo/client": "^3.8.8",
"@wowserhq/format": "^0.6.0",
"@wowserhq/format": "^0.9.0",
"@wowserhq/scene": "^0.8.1",
"classnames": "^2.3.1",
"crypto-hash": "^2.0.0",
"graphql": "^16.8.1",
Expand Down
104 changes: 104 additions & 0 deletions packages/spelunker-web/src/components/core/MapViewer/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import React, { useEffect, useRef } from 'react';
import { AssetManager, FormatManager, TextureManager, MapManager, OrbitControls } from '@wowserhq/scene';
import * as THREE from 'three';
import styles from './index.styl';

// Maps use z-up coordinates
THREE.Object3D.DEFAULT_UP.set(0, 0, 1);

// We do our own color management!
THREE.ColorManagement.enabled = false;

const assetManager = new AssetManager(process.env.DATA_URI, true);
const formatManager = new FormatManager(assetManager);
const textureManager = new TextureManager(formatManager);

const MapViewer = ({ map: { filename } }) => {
const canvasRef = useRef();
const mapManagerRef = useRef();

useEffect(() => {
if (filename && canvasRef.current) {
const canvasWidth = canvasRef.current.clientWidth;
const canvasHeight = canvasRef.current.clientHeight;

const camera = new THREE.PerspectiveCamera(
60,
canvasWidth / canvasHeight,
0.1,
1277.0,
);
camera.position.set(100.0, 100.0, 100.0);

const clock = new THREE.Clock();

const renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: false,
canvas: canvasRef.current,
});
renderer.setSize(canvasWidth, canvasHeight);

const clearColor = new THREE.Color(0.25, 0.5, 0.8);
renderer.setClearColor(clearColor, 1.0);

const scene = new THREE.Scene();
scene.matrixAutoUpdate = false;

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.minDistance = 10;
controls.maxDistance = 900;
controls.panSpeed = 5.0;
controls.keyPanSpeed = 20.0;
controls.zoomSpeed = 5.0;
controls.screenSpacePanning = false;
controls.update();

const raycaster = new THREE.Raycaster();
const coords = new THREE.Vector2();
const targetOffset = 100.0;

const mapManager = new MapManager(filename, formatManager, textureManager);
mapManagerRef.current = mapManager;

scene.add(mapManager.root);

let rafId;

const animate = () => {
const delta = clock.getDelta();

raycaster.setFromCamera(coords, camera);
raycaster.ray.at(targetOffset, controls.target);

mapManager.setTarget(camera.position.x, camera.position.y);

controls.update(delta);
mapManager.update(delta, camera);

renderer.render(scene, camera);

rafId = requestAnimationFrame(animate);
};

animate();

return () => {
cancelAnimationFrame(rafId);

renderer.dispose();
controls.dispose();
};
}
}, [filename]);

return (
<div className={styles.container}>
<canvas ref={canvasRef} className={styles.viewer} />
</div>
);
};

export default MapViewer;
12 changes: 12 additions & 0 deletions packages/spelunker-web/src/components/core/MapViewer/index.styl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.container {
width: 100%
padding-bottom: 56.25%;
border-radius: 5px;
position: relative;
}

.viewer {
position: absolute;
width: 100%;
height: 100%;
}
1 change: 1 addition & 0 deletions packages/spelunker-web/src/components/core/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export { default as Error, ErrorBoundary } from './Error';
export { default as Form, Button, Input } from './Form';
export { default as GameMap, GameMapPin } from './GameMap';
export { default as List, ListItem } from './List';
export { default as MapViewer } from './MapViewer';
export { default as Pagination } from './Pagination';
export { default as ProjectLink } from './ProjectLink';
export { default as Query } from './Query';
Expand Down
9 changes: 8 additions & 1 deletion packages/spelunker-web/src/components/entities/Map/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import { useParams } from 'react-router-dom';
import { gql } from '@apollo/client';

import { Bounds, Box, GameMap, Query, Tab, TabbedBox, Title } from '../../core';
import { Bounds, Box, GameMap, MapViewer, Query, Tab, TabbedBox, Title } from '../../core';

import AreasTab from './tabs/Areas';
import GameObjectSpawnsTab from './tabs/GameObjectSpawns';
Expand Down Expand Up @@ -81,6 +81,13 @@ const Map = () => {
component={GameObjectSpawnsTab}
path="objects"
/>}

<Tab
label="Viewer"
component={MapViewer}
map={map}
path="viewer"
/>
</TabbedBox>
</Title>
);
Expand Down

0 comments on commit 00ab8f5

Please sign in to comment.