aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/.env2
-rw-r--r--api/api.py19
-rw-r--r--src/App.jsx72
-rw-r--r--src/components/chatbubble.jsx23
-rw-r--r--src/components/ground.jsx6
-rw-r--r--src/components/notes.jsx12
-rw-r--r--src/components/player.jsx46
-rw-r--r--src/components/sun.jsx18
-rw-r--r--src/main.jsx1
-rw-r--r--src/style.scss13
10 files changed, 143 insertions, 69 deletions
diff --git a/api/.env b/api/.env
new file mode 100644
index 0000000..3c25e5e
--- /dev/null
+++ b/api/.env
@@ -0,0 +1,2 @@
+DB_URI=mongodb://127.0.0.1
+DB_NAME=forum
diff --git a/api/api.py b/api/api.py
index 64d9fe5..1006a2a 100644
--- a/api/api.py
+++ b/api/api.py
@@ -1,8 +1,17 @@
-import time
-from flask import Flask
+from flask import Flask, config
+from dotenv import dotenv_values
+from pymongo import MongoClient
+from bson.json_util import dumps
+
+config = dotenv_values(".env")
app = Flask(__name__)
-@app.route('/api/time')
-def get_current_time():
- return {'time': time.time()}
+app.mongoclient = MongoClient(config["DB_URI"])
+app.db = app.mongoclient[config["DB_NAME"]]
+print("Connected to MongoDB database")
+
+@app.route('/api/message')
+def get_messages():
+ messages = dumps(list(app.db["message"].find(limit=100)))
+ return messages
diff --git a/src/App.jsx b/src/App.jsx
index 25fe834..81436a8 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -3,30 +3,32 @@ import { Canvas, useFrame } from '@react-three/fiber';
import Notes from './components/notes';
import Player from './components/player';
import Ground from './components/ground';
-import React, { Suspense, useContext, useEffect, useState } from 'react';
+import React, { Suspense, useContext, useEffect, useRef, useState } from 'react';
import { Physics } from '@react-three/rapier';
+import Sun from './components/sun';
+import { Fog } from 'three';
+import * as everforest from './_everforest.module.scss'
export const AppContext = React.createContext(null);
function KeyPressedClearer() {
- const { setKeysPressed } = useContext(AppContext);
- useFrame((_s, _d) => setKeysPressed([]));
+ const { keysPressed } = useContext(AppContext);
+ useFrame((_s, _d) => keysPressed.current = [], -1);
return <></>
}
function App() {
- const [keys, setKeys] = useState([]);
- const [keysPressed, setKeysPressed] = useState([]);
+ const keys = useRef([]);
+ const keysPressed = useRef([]);
+ const [messages, setMessages] = useState();
const playerKeyDown = (event) => {
- setKeys(keys => {
- if(!keys.includes(event.code)) {
- setKeysPressed(keysPressed => [...keysPressed, event.code]);
- }
- return [...keys, event.code]
- });
+ if (!keys.current.includes(event.code)) {
+ keysPressed.current.push(event.code);
+ }
+ keys.current.push(event.code);
}
const playerKeyUp = (event) => {
- setKeys(keys => keys.filter(k => k != event.code));
+ keys.current = keys.current.filter(k => k != event.code);
}
useEffect(() => {
window.addEventListener('keydown', playerKeyDown);
@@ -36,28 +38,34 @@ function App() {
window.removeEventListener('keyup', playerKeyUp);
};
}, []);
+ useEffect(() => {
+ fetch('/api/message').then((res) => res.json()).then((data) => {
+ console.log(data);
+ setMessages(data);
+ });
+ }, []);
return (
- <AppContext.Provider value={{ keys: keys, keysPressed: keysPressed, setKeysPressed: setKeysPressed }}>
- <Canvas shadows>
- <KeyPressedClearer />
- <Suspense>
- <Physics timeStep={1/60}>
- <directionalLight
- castShadow
- position={[100, 100, 100]}
- lookAt={[0, 0, 0]}
- intensity={Math.PI / 2}
- shadow-mapSize-height={2048}
- shadow-mapSize-width={2048}
- />
- <ambientLight intensity={Math.PI / 4} />
+ <>
+ <div className='dot' />
+ <p className='unselectable hint'>
+ E - read<br/>
+ LMB - write
+ </p>
+ <AppContext.Provider value={{ keys: keys, keysPressed: keysPressed, messages: messages, setMessages: setMessages }}>
+ <Canvas shadows>
+ <KeyPressedClearer />
+ <Suspense>
+ <fog color={everforest.bg0} attach="fog" far={500}/>
+ <Sun />
<Notes />
- <Player />
- <Ground />
- </Physics>
- </Suspense>
- </Canvas>
- </AppContext.Provider>
+ <Physics timeStep={1 / 60}>
+ <Player />
+ <Ground />
+ </Physics>
+ </Suspense>
+ </Canvas>
+ </AppContext.Provider>
+ </>
)
}
diff --git a/src/components/chatbubble.jsx b/src/components/chatbubble.jsx
index a1ef114..c85f026 100644
--- a/src/components/chatbubble.jsx
+++ b/src/components/chatbubble.jsx
@@ -1,7 +1,7 @@
import { useContext, useRef, useState } from 'react'
import * as everforest from '../_everforest.module.scss'
import { useGLTF } from '@react-three/drei';
-import { useFrame, useThree } from '@react-three/fiber';
+import { useFrame } from '@react-three/fiber';
import { Html } from '@react-three/drei';
import { AppContext } from '../App';
import Color from 'color';
@@ -13,25 +13,29 @@ export default function ChatBubble({ position, text }) {
const [activatable, setActivatable] = useState(false);
const [active, setActive] = useState(false);
const { keysPressed } = useContext(AppContext);
- const { camera } = useThree();
- useFrame((_, delta) => {
+ useFrame((state, delta) => {
if (active) {
meshRef.current.rotation.y += delta;
+ let cameraPos = new Vector3();
+ state.camera.getWorldPosition(cameraPos);
+ if (cameraPos.distanceToSquared(meshRef.current.position) > 900) {
+ setActive(false);
+ }
}
if(hovered) {
let cameraPos = new Vector3();
- camera.getWorldPosition(cameraPos);
- setActivatable(cameraPos.distanceToSquared(meshRef.current.position) < 9);
+ state.camera.getWorldPosition(cameraPos);
+ setActivatable(cameraPos.distanceToSquared(meshRef.current.position) < 25);
} else {
setActivatable(false);
}
- if (keysPressed.includes('KeyE') && activatable) {
+ if (keysPressed.current.includes('KeyE') && activatable) {
setActive(!active);
}
- });
+ }, -2);
const { nodes } = useGLTF('../assets/message-bubble.glb');
- let color = Color(active ? everforest.blue : everforest.orange);
+ let color = Color(active ? everforest.blue : everforest.purple);
if (activatable) {
color = color.lighten(.1);
}
@@ -41,10 +45,9 @@ export default function ChatBubble({ position, text }) {
ref={meshRef}
scale={active ? 3 : 2}
geometry={nodes.Curve.geometry}
- castShadow
onPointerOver={(_) => setHovered(true)}
onPointerOut={(_) => setHovered(false)}>
- <meshStandardMaterial color={color.toString()} />
+ <meshStandardMaterial color={color.toString()}/>
{active && <Html center position={[0, .5, 0]} className='unselectable textPopup'><span>{text}</span></Html>}
</mesh>
)
diff --git a/src/components/ground.jsx b/src/components/ground.jsx
index 4042538..cc128de 100644
--- a/src/components/ground.jsx
+++ b/src/components/ground.jsx
@@ -1,16 +1,16 @@
import { useRef } from 'react';
import * as everforest from '../_everforest.module.scss'
import { RigidBody } from '@react-three/rapier';
-import { DoubleSide } from 'three';
import { useGLTF } from '@react-three/drei';
export default function Ground() {
const meshRef = useRef();
const { nodes } = useGLTF('../assets/terrain.glb');
+
return (
<RigidBody type='fixed' colliders="trimesh">
- <mesh ref={meshRef} position={[0, 0, 0]} rotation={[-Math.PI / 2, 0, 0]} receiveShadow geometry={nodes.Plane.geometry}>
- <meshStandardMaterial color={everforest.yellow} side={DoubleSide}/>
+ <mesh ref={meshRef} position={[0, 0, 0]} rotation={[-Math.PI / 2, 0, 0]} geometry={nodes.Plane.geometry} name='ground'>
+ <meshStandardMaterial color={everforest.yellow}/>
</mesh>
</RigidBody>
);
diff --git a/src/components/notes.jsx b/src/components/notes.jsx
index 0dd267a..027cd10 100644
--- a/src/components/notes.jsx
+++ b/src/components/notes.jsx
@@ -1,14 +1,12 @@
+import { useContext } from "react";
import ChatBubble from "./chatbubble";
-
-const chatbubbles = [
- {position: [0,0,-3], text: "ugh. really struggling with double bleeds in my ankles. makes it hard to do very much of anything, let alone focus for my hobbies"},
- {position: [-1,0,-5], text: "reddit is everywhere on google and i am sick of it... why can't there be a good forum site?"},
-];
+import { AppContext } from "../App";
export default function Notes() {
+ const { messages } = useContext(AppContext);
return (<>
- {chatbubbles.map((chatbubble, index) =>
- <ChatBubble key={index} position={chatbubble.position} text={chatbubble.text}/>
+ {messages.map((chatbubble, index) =>
+ <ChatBubble key={index} position={chatbubble.position} text={chatbubble.message}/>
)}
</>);
}
diff --git a/src/components/player.jsx b/src/components/player.jsx
index bd8e392..a9ddad0 100644
--- a/src/components/player.jsx
+++ b/src/components/player.jsx
@@ -1,29 +1,47 @@
-import { Capsule, PerspectiveCamera, PointerLockControls } from "@react-three/drei";
-import { useFrame } from "@react-three/fiber";
+import { PerspectiveCamera, PointerLockControls } from "@react-three/drei";
+import { useFrame, useThree } from "@react-three/fiber";
import { useContext, useEffect, useRef } from "react";
import { AppContext } from "../App";
-import { CapsuleCollider, RapierCollider, RapierRigidBody, RigidBody, useRapier, vec3 } from "@react-three/rapier";
-import { quat } from "@react-three/rapier";
-import { Euler, Object3D, Vector3 } from "three";
+import { CapsuleCollider, RigidBody, useRapier, vec3 } from "@react-three/rapier";
+import { Vector3 } from "three";
import { useBeforePhysicsStep } from "@react-three/rapier";
+import { Raycaster } from "three";
const _movespeed = 3.0;
export default function Player() {
const controlsRef = useRef();
- const { keys } = useContext(AppContext);
+ const { keys, setMessages } = useContext(AppContext);
const rapier = useRapier();
const controller = useRef();
const collider = useRef();
const rigidbody = useRef();
const camera = useRef();
+ const raycaster = useRef(new Raycaster());
+ const { scene, pointer } = useThree();
const refState = useRef({
grounded: false,
- jumping: false,
velocity: vec3(),
});
+ const onClick = (_) => {
+ raycaster.current.setFromCamera(pointer, camera.current);
+ const intersect = raycaster.current.intersectObjects(scene.children).at(0);
+ if (intersect && intersect.object.name === 'ground' && intersect.distance < 10 && controlsRef.current.isLocked) {
+ const message = prompt();
+ if (message) {
+ setMessages(messages => [...messages, { position: intersect.point, message: message}]);
+ }
+ controlsRef.current.lock();
+ }
+ };
+
+ useEffect(() => {
+ window.addEventListener("click", onClick);
+ return () => window.removeEventListener("click", onClick);
+ }, []);
+
useEffect(() => {
const c = rapier.world.createCharacterController(0.1);
c.setApplyImpulsesToDynamicBodies(true);
@@ -31,10 +49,18 @@ export default function Player() {
controller.current = c;
}, [rapier]);
+ useFrame((_, delta) => {
+ const fov_axis = +(keys.current.includes('Minus')) - +(keys.current.includes('Equal'));
+ if (fov_axis != 0) {
+ camera.current.fov += 45 * fov_axis * delta;
+ camera.current.updateProjectionMatrix();
+ }
+ });
+
useBeforePhysicsStep((world) => {
if (controller.current && rigidbody.current && collider.current) {
- const move_axis_x = +(keys.includes('KeyD')) - +(keys.includes('KeyA'));
- const move_axis_z = +(keys.includes('KeyW')) - +(keys.includes('KeyS'));
+ const move_axis_x = +(keys.current.includes('KeyD')) - +(keys.current.includes('KeyA'));
+ const move_axis_z = +(keys.current.includes('KeyW')) - +(keys.current.includes('KeyS'));
const { velocity } = refState.current;
const position = vec3(rigidbody.current.translation());
@@ -69,7 +95,7 @@ export default function Player() {
return (
<RigidBody type="kinematicPosition" colliders={false} ref={rigidbody} position={[0, 2, 0]}>
- <PerspectiveCamera makeDefault position={[0, .9, 0]} fov={90} ref={camera} />
+ <PerspectiveCamera makeDefault position={[0, .9, 0]} ref={camera} fov={80}/>
<PointerLockControls ref={controlsRef} />
<CapsuleCollider ref={collider} args={[1, 0.5]} />
</RigidBody>
diff --git a/src/components/sun.jsx b/src/components/sun.jsx
new file mode 100644
index 0000000..455a065
--- /dev/null
+++ b/src/components/sun.jsx
@@ -0,0 +1,18 @@
+import { Matrix4 } from 'three';
+import * as everforest from '../_everforest.module.scss'
+
+export default function Sun() {
+ return (<>
+ <directionalLight
+ position={[1000, 1000, 1000]}
+ lookAt={[0, 0, 0]}
+ intensity={Math.PI / 2}
+ color={everforest.red}
+ />
+ <ambientLight intensity={Math.PI / 4} />
+ <mesh position={[1000,1000,1000]}>
+ <icosahedronGeometry args={[100,100]}/>
+ <meshStandardMaterial color={everforest.red} emissive={everforest.red} emissiveIntensity={1} fog={false}/>
+ </mesh>
+ </>);
+}
diff --git a/src/main.jsx b/src/main.jsx
index 97bc548..3d9da8a 100644
--- a/src/main.jsx
+++ b/src/main.jsx
@@ -5,6 +5,5 @@ import App from './App.jsx'
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
- <div className='dot'/>
</StrictMode>,
)
diff --git a/src/style.scss b/src/style.scss
index 23c3679..aaa420d 100644
--- a/src/style.scss
+++ b/src/style.scss
@@ -31,7 +31,17 @@
height: 5px;
border-radius: 50%;
transform: translate3d(-50%, -50%, 0);
- border: 2px solid white;
+ border: 2px solid everforest.$fg;
+ z-index: 999999;
+}
+
+.hint {
+ position: fixed;
+ bottom: 0%;
+ left: 0%;
+ color: everforest.$bg2;
+ z-index: 999999;
+ margin: 5px;
}
html,
@@ -47,6 +57,7 @@ h1 {
html {
background-color: everforest.$bg0;
+ color: everforest.$fg;
height: 100%;
}