Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🔎⚗️ ↝ Modifying a 3js package for generating worlds on the client #16

Closed
Binary file modified .DS_Store
Binary file not shown.
3 changes: 3 additions & 0 deletions components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ export default function Header () {
<Link href={'https://github.com/signal-k'}>
Github
</Link>
<Link href={'/generator'}>
Explore worlds {/* Make submenu items */}
</Link>
<Link href={'/publications/create'}>
Create
</Link>
Expand Down
48 changes: 48 additions & 0 deletions generator/components/Controls.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useState, useEffect } from 'react';

import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import Form from 'react-bootstrap/Form';
import Tabs from 'react-bootstrap/Tabs';
import Tab from 'react-bootstrap/Tabs';

import LayerPanel from './panels/LayerPanel';
import InfoPanel from './panels/InfoPanel';

import { useStatePersisted } from '../hooks/use-state-persisted';
import { PlanetEditorState } from '../hooks/use-planet-editor-state';
import GraphicsPanel from './panels/GraphicsPanel';

const tabClasses = 'border-left border-right border-bottom';
const tabStyles = {
paddingTop: '10px',
paddingLeft: '6px',
paddingRight: '6px'
};

export default function Controls ({ planetState }: { planetState: PlanetEditorState }) {
const [tab, setTab] = useStatePersisted('world-gen:active-tab', 'planet-info-tab');
console.log(tab);

return (
<>
<Row>
<Col>
<Form autoComplete='off' data-lpignore="true">
<Tabs id='control-tabs' activeKey={tab} onSelect={setTab} className='nav-fill' transition={false}>
<Tab id='planet-info-tab' eventKey='planet-info-tab' title='Info' className={tabClasses} style={tabStyles} tabIndex="-1" >
<InfoPanel {...{ planetState }} />
</Tab>
<Tab id='layers-tab' eventKey='layers-tab' title='Layers' className={tabClasses} style={{ ...tabStyles, paddingTop: 0, paddingLeft: 0, paddingRight: 0 }} tabIndex="-1">
<LayerPanel {...{ planetState }} />
</Tab>
<Tab id='graphics-tab' eventKey='graphics-tab' title='Graphics' className={tabClasses} style={tabStyles} tabIndex="-1">
<GraphicsPanel {...{ planetState }} />
</Tab>
</Tabs>
</Form>
</Col>
</Row>
</>
);
}
175 changes: 175 additions & 0 deletions generator/components/FieldEditors.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';
import Octicon, { Sync } from '@primer/octicons-react';
import InputGroup from 'react-bootstrap/InputGroup';
import Tooltip from 'rc-tooltip';
import Slider from 'rc-slider';
import { Vector2, Vector3 } from 'three';


import { randomSeed } from '../services/helpers';
import { SliderPicker as ColorSlider } from 'react-color';

const sliderStyle = {
height: '24px'
};

export function TextBox(props: { label: string, value: string, onChange: (value: string) => void }) {
return (
<Form.Group as={Col}>
<Form.Label><strong>{props.label}:</strong> {props.value + ''}</Form.Label>
<Form.Control type="text" value={props.value + ''} onChange={handleChange} />
</Form.Group>
);

function handleChange(e: any) {
props.onChange && props.onChange(e.target.value);
}
}

export function SeedInput(props: { label?: string, value: string, onChange: (value: string) => void }) {
return (
<Form.Group as={Col}>
<Form.Label><strong>{props.label || 'Seed'}:</strong></Form.Label>
<InputGroup>
<Form.Control type="input" value={props.value + ''} onChange={handleChange} />
<InputGroup.Append>
<Button variant="outline-secondary" title='Randomize' onClick={handleRandomization}>
<Octicon icon={Sync} />
</Button>
</InputGroup.Append>
</InputGroup>
</Form.Group>
);

function handleRandomization() {
props.onChange && props.onChange(randomSeed());
}

function handleChange(e: any) {
props.onChange && props.onChange(e.target.value);
}
}

export function ColorPicker(props: { label: string, value: string, onChange: (value: string) => void }) {

return (
<Form.Group as={Col}>
<Form.Label>{props.label}: {props.value}</Form.Label>
<ColorSlider color={props.value} onChangeComplete={handleChange} />
</Form.Group>
);

function handleChange(e: any) {
props.onChange && props.onChange(e.hex.toUpperCase());
}
}

export function NumberSlider(props: { label: string, min: number, max: number, step: number, value: number, onChange: (value: number) => void }) {
return (
<Form.Group as={Col}>
<Form.Label><strong>{props.label}:</strong> {props.value}</Form.Label>
<Slider min={props.min} max={props.max} defaultValue={props.value} step={props.step} onChange={props.onChange} />
</Form.Group>
);
}

const VECTOR_LABEL_WIDTH = 3;
export function Vector2Slider({ label, min, max, step, value, onChange }: { label: string, min: Vector2 | number, max: Vector2 | number, step?: Vector2 | number, value: Vector2, onChange: (value: Vector2) => void }) {
step = typeof step === 'undefined' ? 1 : step;

let vectorMin = typeof min === 'number' ? new Vector2(min, min) : min;
let vectorMax = typeof max === 'number' ? new Vector2(max, max) : max;
let vectorStep = typeof step === 'number' ? new Vector2(step, step) : step;

return (<Form.Group as={Col}>
<Form.Label className='font-weight-bold mb-0'>{label}:</Form.Label>
<Row>
<Col xs={VECTOR_LABEL_WIDTH}><strong>X:</strong> {value.x}</Col>
<Col className='pl-0'>
<Slider min={vectorMin.x} max={vectorMax.x} defaultValue={value.x} step={vectorStep.x} onChange={handleChange('x')} />
</Col>
</Row>
<Row>
<Col xs={VECTOR_LABEL_WIDTH}><strong>Y:</strong> {value.y}</Col>
<Col className='pl-0'>
<Slider min={vectorMin.y} max={vectorMax.y} defaultValue={value.y} step={vectorStep.y} onChange={handleChange('y')} />
</Col>
</Row>
</Form.Group>);

function handleChange(part: 'x' | 'y') {
return (newValue: number) => {
if (onChange) {
if (part === 'x') {
onChange(new Vector2(newValue, value.y));
} else {
onChange(new Vector2(value.x, newValue));
}
}
}
}
}

export function Vector3Slider({ label, min, max, step, value, onChange }: { label: string, min: Vector3 | number, max: Vector3 | number, step?: Vector3 | number, value: Vector3, onChange: (value: Vector3) => void }) {
step = typeof step === 'undefined' ? 1 : step;

let vectorMin = typeof min === 'number' ? new Vector3(min, min, min) : min;
let vectorMax = typeof max === 'number' ? new Vector3(max, max, max) : max;
let vectorStep = typeof step === 'number' ? new Vector3(step, step, step) : step;

return (<Form.Group as={Col}>
<Form.Label className='font-weight-bold mb-0'>{label}:</Form.Label>
<Row>
<Col xs={VECTOR_LABEL_WIDTH}><strong>X:</strong> {value.x}</Col>
<Col className='pl-0'>
<Slider min={vectorMin.x} max={vectorMax.x} defaultValue={value.x} step={vectorStep.x} onChange={handleChange('x')} />
</Col>
</Row>
<Row>
<Col xs={VECTOR_LABEL_WIDTH}><strong>Y:</strong> {value.y}</Col>
<Col className='pl-0'>
<Slider min={vectorMin.y} max={vectorMax.y} defaultValue={value.y} step={vectorStep.y} onChange={handleChange('y')} />
</Col>
</Row>
<Row>
<Col xs={VECTOR_LABEL_WIDTH}><strong>Z:</strong> {value.z}</Col>
<Col className='pl-0'>
<Slider min={vectorMin.z} max={vectorMax.z} defaultValue={value.z} step={vectorStep.z} onChange={handleChange('z')} />
</Col>
</Row>
</Form.Group>);

function handleChange(part: 'x' | 'y' | 'z') {
return (newValue: number) => {
if (onChange) {
switch (part) {
case 'x':
onChange(new Vector3(newValue, value.y, value.z));
break;
case 'y':
onChange(new Vector3(value.x, newValue, value.z));
break;
case 'z':
onChange(new Vector3(value.x, value.y, newValue));
break;
}
}
}
}
}

export function CheckboxInput(props: { label: string, value: boolean, onChange: (value: boolean) => void }) {

return (
<Form.Group as={Col}>
<Form.Check type='checkbox' label={props.label} checked={props.value} onChange={handleChange} />
</Form.Group>
);

function handleChange(e: any) {
props.onChange && props.onChange(e.target.checked);
}
}
52 changes: 52 additions & 0 deletions generator/components/GithubCorner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@

export default function GitHubCorner() {
return (
<>
<a href="https://github.com/signal-k" target="_blank" className="github-corner" aria-label="View source on GitHub">
<svg width="80" height="80" viewBox="0 0 250 250"
style={{position: 'absolute',top: 0, border: 0, right: 0, fill: '#151513', color: '#fff', zIndex: 999}} aria-hidden="true">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
<path
d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
fill="currentColor" style={{transformOrigin: '130px 106px'}} className="octo-arm"></path>
<path
d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
fill="currentColor" className="octo-body"></path>
</svg>
</a>
<style jsx>{`
.github-corner:hover .octo-arm {
animation: octocat-wave 560ms ease-in-out
}

@keyframes octocat-wave {

0%,
100% {
transform: rotate(0)
}

20%,
60% {
transform: rotate(-25deg)
}

40%,
80% {
transform: rotate(10deg)
}
}

@media (max-width:500px) {
.github-corner:hover .octo-arm {
animation: none
}

.github-corner .octo-arm {
animation: octocat-wave 560ms ease-in-out
}
}
`}</style>
</>
)
}
13 changes: 13 additions & 0 deletions generator/components/Layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Head from "next/head";
import Container from 'react-bootstrap/Container';
import GithubCorner from './GithubCorner';

export default function Layout(props: {children: any[]}) {
return <>
<Head><title>World Generation</title></Head>
<GithubCorner />
<Container fluid>
{props.children}
</Container>
</>;
}
22 changes: 22 additions & 0 deletions generator/components/SceneDisplay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

import { useLayoutEffect, useRef } from 'react';
import SceneManager from '../services/base-scene-manager';

export default function SceneDisplay ({sceneManager}: {sceneManager: SceneManager}) {
const canvasRef = useRef<HTMLCanvasElement>(null);

if (process.browser) {
useLayoutEffect(() => {
console.log('Starting scene...');
sceneManager.init(canvasRef.current);
sceneManager.start();

return () => {
console.log('Stopping scene...');
sceneManager.stop();
};
}, []);
}

return <canvas ref={canvasRef} style={{ width: '100%' }} />
}
15 changes: 15 additions & 0 deletions generator/components/SubPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Link from "next/link";
import Row from "react-bootstrap/Row";
import Col from "react-bootstrap/Col";
import Layout from "./Layout";

export default function SubPage ({ header, children }: {header: string, children: any }) {
return (
<Layout>
<Row>
<Col><h1 className="display-4">Hello {header}</h1></Col>
</Row>
{children}
</Layout>
)
}
12 changes: 12 additions & 0 deletions generator/components/panels/GraphicsPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { CheckboxInput, NumberSlider } from "../FieldEditors";
import { PlanetEditorState } from "../../hooks/use-planet-editor-state";

export default function GraphicsPanel({ planetState }: { planetState: PlanetEditorState }) {
return (
<>
<NumberSlider label='Resolution' min={2} max={128} step={1} value={planetState.resolution.current} onChange={planetState.resolution.set} />
<NumberSlider label='Rotate' min={0} max={2} step={0.01} value={planetState.rotate.current} onChange={planetState.rotate.set} />
<CheckboxInput label='Wireframes' value={planetState.wireframes.current} onChange={planetState.wireframes.set} />
</>
);
}
33 changes: 33 additions & 0 deletions generator/components/panels/InfoPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { PlanetEditorState } from "../../hooks/use-planet-editor-state";
import { NumberSlider, TextBox, SeedInput, ColorPicker } from "../FieldEditors";
import Form from "react-bootstrap/Form";
import Col from 'react-bootstrap/Col';

export default function InfoPanel({ planetState }: { planetState: PlanetEditorState }) {
return (
<>
<Form.Row>
<Form.Group as={Col}>
<TextBox label='Name' value={planetState.name.current} onChange={planetState.name.set} />
</Form.Group>
<Form.Group as={Col}>
<SeedInput label='Seed' value={planetState.seed.current} onChange={planetState.seed.set} />
</Form.Group>
</Form.Row>
<Form.Row>
<Form.Group as={Col}>
<NumberSlider label='Radius' min={0.25} max={16} step={0.05} value={planetState.radius.current} onChange={planetState.radius.set} />
</Form.Group>
</Form.Row>
<Form.Row>
<Form.Group as={Col}>
<NumberSlider label='Sea Level' min={0} max={2} step={0.05} value={planetState.seaLevel.current} onChange={planetState.seaLevel.set} />
</Form.Group>
</Form.Row>



{/* <ColorPicker label='Color' value={planetState.colors.current} onChange={planetState.colors.set} /> */}
</>
);
}
Loading