diff options
author | 2025-02-10 16:50:19 -0500 | |
---|---|---|
committer | 2025-02-10 16:50:19 -0500 | |
commit | 89b411863b452fdab9d2b4a0cfd0e9d79d991f72 (patch) | |
tree | 02de0ae38340386c0ac7bb6e8e4b6ab4317878bd | |
parent | 55a0a3de6550f0142b79ab40645c20b465ddded8 (diff) |
Reading messages from DB + clicking to place new messages
-rw-r--r-- | api/.env | 2 | ||||
-rw-r--r-- | api/api.py | 19 | ||||
-rw-r--r-- | src/App.jsx | 72 | ||||
-rw-r--r-- | src/components/chatbubble.jsx | 23 | ||||
-rw-r--r-- | src/components/ground.jsx | 6 | ||||
-rw-r--r-- | src/components/notes.jsx | 12 | ||||
-rw-r--r-- | src/components/player.jsx | 46 | ||||
-rw-r--r-- | src/components/sun.jsx | 18 | ||||
-rw-r--r-- | src/main.jsx | 1 | ||||
-rw-r--r-- | src/style.scss | 13 |
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 @@ -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%; } |