diff --git a/package.json b/package.json index eb71943..9d4dfa1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "replay-viewer", - "version": "0.3.0", + "version": "0.3.3", "description": "Rocket League replay viewer React component and tooling", "main": "./lib/index.js", "types": "./lib/index.d.ts", diff --git a/src/builders/field/addCameras.ts b/src/builders/field/addCameras.ts index f62c497..9c247e8 100644 --- a/src/builders/field/addCameras.ts +++ b/src/builders/field/addCameras.ts @@ -1,11 +1,11 @@ -import { OrthographicCamera, PerspectiveCamera, Scene } from "three" +import { OrthographicCamera, PerspectiveCamera, Scene, Vector3 } from "three" import { DEFAULT_CAMERA_OPTIONS } from "../../constants/defaultCameraOptions" import { ABOVE_FIELD_CAMERA, BLUE_GOAL_CAMERA, ORANGE_GOAL_CAMERA, - ORTHOGRAPHIC_CAMERA, + ORTHOGRAPHIC, } from "../../constants/gameObjectNames" export const addCameras = (scene: Scene) => { @@ -24,24 +24,50 @@ export const addCameras = (scene: Scene) => { aboveFieldCamera.position.set(0, 2000, 0) scene.add(aboveFieldCamera) - const orthographicCamera = new OrthographicCamera( - -320, - 320, - 240, - -240, - 0.1, - 20000 - ) - orthographicCamera.name = ORTHOGRAPHIC_CAMERA - orthographicCamera.position.set(3500, 5000, 5000) - orthographicCamera.lookAt(-500, 0, -500) - orthographicCamera.zoom = 0.05 - scene.add(orthographicCamera) + const generateOrthographicCamera = () => { + const camera = new OrthographicCamera(-320, 320, 240, -240, 0.1, 20000) + camera.zoom = 0.05 + scene.add(camera) + return camera + } + + const ORTHOGRAPHIC_X = 3500 + const ORTHOGRAPHIC_Y = 5000 + const ORTHOGRAPHIC_Z = 5000 + + const orthographicCameras = [ + { + name: ORTHOGRAPHIC.BLUE_LEFT, + position: new Vector3(ORTHOGRAPHIC_X, ORTHOGRAPHIC_Y, -ORTHOGRAPHIC_Z), + }, + { + name: ORTHOGRAPHIC.BLUE_RIGHT, + position: new Vector3(-ORTHOGRAPHIC_X, ORTHOGRAPHIC_Y, -ORTHOGRAPHIC_Z), + }, + { + name: ORTHOGRAPHIC.ORANGE_LEFT, + position: new Vector3(-ORTHOGRAPHIC_X, ORTHOGRAPHIC_Y, ORTHOGRAPHIC_Z), + }, + { + name: ORTHOGRAPHIC.ORANGE_RIGHT, + position: new Vector3(ORTHOGRAPHIC_X, ORTHOGRAPHIC_Y, ORTHOGRAPHIC_Z), + }, + { + name: ORTHOGRAPHIC.ABOVE_FIELD, + position: new Vector3(0, 8000, 0), + }, + ].map(({ name, position }) => { + const camera = generateOrthographicCamera() + camera.name = name + camera.position.set(position.x, position.y, position.z) + camera.lookAt(0, 0, 0) + return camera + }) return [ blueGoalCamera, orangeGoalCamera, aboveFieldCamera, - orthographicCamera, + ...orthographicCameras, ] } diff --git a/src/constants/gameObjectNames.ts b/src/constants/gameObjectNames.ts index 6e74403..9554daf 100644 --- a/src/constants/gameObjectNames.ts +++ b/src/constants/gameObjectNames.ts @@ -6,4 +6,10 @@ export const GROUP_SUFFIX = "-group" export const BLUE_GOAL_CAMERA = "Blue Goal Camera" export const ORANGE_GOAL_CAMERA = "Orange Goal Camera" export const ABOVE_FIELD_CAMERA = "Above Field Camera" -export const ORTHOGRAPHIC_CAMERA = "Orthographic Camera" +export const ORTHOGRAPHIC = { + ABOVE_FIELD: "ORTHOGRAPHIC_ABOVE_FIELD", + BLUE_LEFT: "ORTHOGRAPHIC_BLUE_LEFT", + BLUE_RIGHT: "ORTHOGRAPHIC_BLUE_RIGHT", + ORANGE_LEFT: "ORTHOGRAPHIC_ORANGE_LEFT", + ORANGE_RIGHT: "ORTHOGRAPHIC_ORANGE_RIGHT", +} diff --git a/src/managers/CameraManager.ts b/src/managers/CameraManager.ts index 7e95562..9e14520 100644 --- a/src/managers/CameraManager.ts +++ b/src/managers/CameraManager.ts @@ -4,12 +4,18 @@ import { ABOVE_FIELD_CAMERA, BLUE_GOAL_CAMERA, ORANGE_GOAL_CAMERA, - ORTHOGRAPHIC_CAMERA, + ORTHOGRAPHIC, } from "../constants/gameObjectNames" import { dispatchCameraChange } from "../eventbus/events/cameraChange" import { dispatchCameraFrameUpdate } from "../eventbus/events/cameraFrameUpdate" import SceneManager from "./SceneManager" +const ORTHOGRAPHIC_CAMERA_NAMES: string[] = Object.keys(ORTHOGRAPHIC).map( + (key: string) => { + return (ORTHOGRAPHIC as any)[key] as string + } +) + class CameraManager { activeCamera: Camera @@ -42,7 +48,8 @@ class CameraManager { ballCam: true, isUsingBoost: false, }) - if (this.activeCamera.name !== ORTHOGRAPHIC_CAMERA) { + + if (!ORTHOGRAPHIC_CAMERA_NAMES.includes(this.activeCamera.name)) { this.activeCamera.lookAt(position) } } @@ -65,8 +72,22 @@ class CameraManager { case "center": this.setActiveCamera(field.getCamera(ABOVE_FIELD_CAMERA) as any) break - case "orthographic": - this.setActiveCamera(field.getCamera(ORTHOGRAPHIC_CAMERA) as any) + case "orthographic-above-field": + this.setActiveCamera(field.getCamera(ORTHOGRAPHIC.ABOVE_FIELD) as any) + break + case "orthographic-orange-left": + this.setActiveCamera(field.getCamera(ORTHOGRAPHIC.ORANGE_LEFT) as any) + break + case "orthographic-orange-right": + this.setActiveCamera(field.getCamera( + ORTHOGRAPHIC.ORANGE_RIGHT + ) as any) + break + case "orthographic-blue-left": + this.setActiveCamera(field.getCamera(ORTHOGRAPHIC.BLUE_LEFT) as any) + break + case "orthographic-blue-right": + this.setActiveCamera(field.getCamera(ORTHOGRAPHIC.BLUE_RIGHT) as any) break default: this.setActiveCamera(this.defaultCamera) @@ -117,7 +138,15 @@ class CameraManager { export interface CameraLocationOptions { playerName?: string - fieldLocation?: "orange" | "blue" | "center" | "orthographic" + fieldLocation?: + | "orange" + | "blue" + | "center" + | "orthographic-blue-right" + | "orthographic-blue-left" + | "orthographic-orange-right" + | "orthographic-orange-left" + | "orthographic-above-field" } export default CameraManager diff --git a/src/viewer/components/FieldCameraControls.tsx b/src/viewer/components/FieldCameraControls.tsx index 2c523aa..33a5b2f 100644 --- a/src/viewer/components/FieldCameraControls.tsx +++ b/src/viewer/components/FieldCameraControls.tsx @@ -1,4 +1,8 @@ import Button from "@material-ui/core/Button" +import Dialog from "@material-ui/core/Dialog" +import List from "@material-ui/core/List" +import ListItem from "@material-ui/core/ListItem" +import Typography from "@material-ui/core/Typography" import { styled } from "@material-ui/styles" import React, { PureComponent } from "react" @@ -10,25 +14,56 @@ const options: CameraLocationOptions["fieldLocation"][] = [ "blue", "orange", "center", - "orthographic", ] const optionNames = { blue: "Blue Goal", orange: "Orange Goal", center: "Above Field", - orthographic: "Orthographic", +} +const orthographicOptions: CameraLocationOptions["fieldLocation"][] = [ + "orthographic-above-field", + "orthographic-blue-left", + "orthographic-blue-right", + "orthographic-orange-left", + "orthographic-orange-right", +] +const orthographicOptionNames = { + ["orthographic-above-field"]: "Above Field", + ["orthographic-blue-left"]: "Blue Left", + ["orthographic-blue-right"]: "Blue Right", + ["orthographic-orange-left"]: "Orange Left", + ["orthographic-orange-right"]: "Orange Right", } interface Props {} -class FieldCameraControls extends PureComponent { +interface State { + dialogOpen: boolean +} + +class FieldCameraControls extends PureComponent { constructor(props: Props) { super(props) + this.state = { + dialogOpen: false, + } + } + + toggleDialog = () => { + this.setState({ + dialogOpen: !this.state.dialogOpen, + }) } onFieldClick = (fieldLocation: CameraLocationOptions["fieldLocation"]) => { - return () => + return () => { + if (this.state.dialogOpen) { + this.setState({ + dialogOpen: false, + }) + } CameraManager.getInstance().setCameraLocation({ fieldLocation }) + } } renderFieldButtons() { @@ -39,14 +74,48 @@ class FieldCameraControls extends PureComponent { variant="outlined" onClick={this.onFieldClick(option)} > - {optionNames[option || "center"]} + {(optionNames as any)[option || "center"]} ) }) } + renderOrthographicOptions() { + return ( + + + {orthographicOptions.map(option => { + return ( + + + { + (orthographicOptionNames as any)[ + option || "orthographic-orange-right" + ] + } + + + ) + })} + + + ) + } + render() { - return
{this.renderFieldButtons()}
+ return ( +
+ {this.renderFieldButtons()} + + Orthographic + + {this.renderOrthographicOptions()} +
+ ) } } diff --git a/src/viewer/components/ReplayViewer.tsx b/src/viewer/components/ReplayViewer.tsx index c9eceb1..f53edab 100644 --- a/src/viewer/components/ReplayViewer.tsx +++ b/src/viewer/components/ReplayViewer.tsx @@ -4,7 +4,7 @@ import FullscreenIcon from "@material-ui/icons/Fullscreen" import FullscreenExitIcon from "@material-ui/icons/FullscreenExit" import { styled } from "@material-ui/styles" import React, { createRef, PureComponent, RefObject } from "react" -import Fullscreen from "react-full-screen" +import FullScreen from "react-full-screen" import { GameManager } from "../../managers/GameManager" import Scoreboard from "./ScoreBoard" @@ -66,7 +66,10 @@ class ReplayViewer extends PureComponent { const onClick = () => this.toggleFullscreen(!this.state.fullScreen) return ( - +
@@ -78,7 +81,7 @@ class ReplayViewer extends PureComponent { - + ) } @@ -99,6 +102,12 @@ const Viewer = styled("div")({ }, }) +const FullscreenWrapper = styled(FullScreen)({ + width: "100%", + + height: "100%", +}) + const FullscreenToggle = styled("div")({ position: "absolute", bottom: 0,