Skip to content

Commit

Permalink
Merge feature/scene-interface into main
Browse files Browse the repository at this point in the history
  • Loading branch information
LouisHain committed Jul 2, 2024
2 parents 7192440 + 6e1152d commit 4915c4a
Show file tree
Hide file tree
Showing 3 changed files with 266 additions and 5 deletions.
271 changes: 266 additions & 5 deletions app/components/FirstPersonControls.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useRef, useEffect } from 'react';
import { useThree, useFrame } from '@react-three/fiber';
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

export const FirstPersonControls = (speed) => {
const { camera, scene } = useThree();
Expand All @@ -16,16 +17,189 @@ export const FirstPersonControls = (speed) => {
const forward = new THREE.Vector3();

// Desired height above the ground
const cameraHeight = 1.8;
const cameraHeight = 5;


// temporary location for arrow graphs, TODO: move this outside of the controller
type edge = {
arrow1: THREE.Object3D;
arrow2: THREE.Object3D;
weight: number;
}

type arrowgraph = {
arrows: THREE.Object3D[];
edges: edge[];
}

class ArrowGraph {
private graph: arrowgraph;
private arrowsShortestPaths: (THREE.Object3D|undefined)[][];
private scene: THREE.Scene;

constructor(scene: THREE.Scene) {
this.graph = { arrows: [], edges: [] };
this.arrowsShortestPaths = [];
this.scene = scene;
}

addArrow(arrow: THREE.Object3D): void{
this.graph.arrows.push(arrow);
}

addEdge(arrow1name: string, arrow2name: string): void {
const arrow1 = this.scene.getObjectByName(arrow1name);
const arrow2 = this.scene.getObjectByName(arrow2name);
if(arrow1 && arrow2){
const weight: number = Math.sqrt(
Math.pow(arrow1.position.x - arrow2.position.x, 2) +
Math.pow(arrow1.position.y - arrow2.position.y, 2)
);
const newEdge: edge = { arrow1, arrow2, weight };
this.graph.edges.push(newEdge);
}
}

public findShortestPaths(): void {
const arrowCount = this.graph.arrows.length;
this.arrowsShortestPaths = Array.from({ length: arrowCount }, () =>
Array(arrowCount).fill(undefined)
);

this.graph.arrows.forEach((startArrow, startIndex) => {
const distance = new Map<THREE.Object3D, number>();
const previous = new Map<THREE.Object3D, THREE.Object3D[]>();
const remaining = new Set<THREE.Object3D>();

this.graph.arrows.forEach(arrow => {
distance.set(arrow, Infinity);
previous.set(arrow, []);
remaining.add(arrow);
});

distance.set(startArrow, 0);

while (remaining.size > 0) {
let currentArrow: THREE.Object3D | undefined = undefined;
let minDistance = Infinity;

remaining.forEach(arrow => {
for (let [key, value] of distance) {
if (key.name == arrow.name) {
if (value < minDistance) {
minDistance = value;
currentArrow = arrow;
}
}
}
});

if (currentArrow === undefined) break;
remaining.delete(currentArrow);

this.graph.edges.forEach(edge => {
let neighbor: THREE.Object3D | undefined = undefined;
if (edge.arrow1.name == currentArrow.name) {
neighbor = edge.arrow2;
} else if (edge.arrow2.name == currentArrow.name) {
neighbor = edge.arrow1;
}

if (neighbor) {
remaining.forEach(arrow => {
if (arrow.name == neighbor.name) {
let distanceCurrentArrow = Infinity;
for (let [key, value] of distance) {
if (key.name == currentArrow.name){
distanceCurrentArrow = value;
}
}
for (let [key, value] of distance) {
if (key.name == neighbor.name) {
const alt = distanceCurrentArrow + edge.weight;
if (alt < value) {
distance.delete(key);
distance.set(neighbor, alt);
previous.delete(key);
let currentArrowPrevious;
for (let [key, value] of previous) {
if (key.name == currentArrow.name){
currentArrowPrevious = value;
}
}
previous.set(neighbor, [...currentArrowPrevious]);
previous.get(neighbor).push(currentArrow);
}
}
}
}
})
}
});
}

this.graph.arrows.forEach((endArrow, endIndex) => {
if (startArrow !== endArrow) {
for (let [nextArrow, value] of previous){
if (nextArrow.name == endArrow.name){
value.push(endArrow);
nextArrow = value[1];
this.arrowsShortestPaths[startIndex][endIndex] = nextArrow;
}
}
}
});
});
}

updateArrowRotations(destinationArrowName: string): void {
const destinationArrow = this.scene.getObjectByName(destinationArrowName);
if (destinationArrow !== undefined) {
const destinationArrowIndex = this.graph.arrows.findIndex(obj => obj.name == destinationArrow.name);
if (this.arrowsShortestPaths.length === 0) {
this.findShortestPaths();
}
for (let arrowIndex = 0; arrowIndex < this.graph.arrows.length; arrowIndex++) {
let arrow = this.graph.arrows[arrowIndex];
let arrowDestination = this.arrowsShortestPaths[arrowIndex][destinationArrowIndex];
if (arrow != undefined) {
if (arrowDestination != undefined) {
arrow.visible = true;
let vectorToNextArrow = [arrow.position.x - arrowDestination.position.x,
arrow.position.z - arrowDestination.position.z];
arrow.rotation.y = -Math.atan2(vectorToNextArrow[1], vectorToNextArrow[0]);
} else {
arrow.visible = false;
}
}
}
}
}
}

const graph = new ArrowGraph(scene);


// temporary location for rooms, TODO: move this outside of the controller
const rooms = [
{
minX: -50, maxX: 50, minY: 0, maxY: 20, minZ: -50, maxZ: 50,
slopes: [],
slopes: [
{ angle: Math.PI / 3 , position: { x: 0, y: 0, z: 0 }, width: 10 },
{ angle: Math.PI / 3, position: { x: 10, y: 0, z: 0 }, width: 10 }
],
objects: [
{ minX: 10, maxX: 15, minY: 0, maxY: 15, minZ: 10, maxZ: 15 }
]
],
elements: {
arrows: [],
panes: [
{ position: { x: -40, y: -5, z: -40}, verticalRotation: Math.PI / 6, horizontalRotation: Math.PI/4, sizefactor: 10, content: "/images/testbild.png"},
],
windowarcs: [
{ position: { x: 40, y: 0, z: -40}, horizontalRotation: Math.PI / 4, arcRadius: 10, arcHeight: 20, content: "/images/testbild.png"}
]
}
},
{
minX: 50, maxX: 60, minY: 0, maxY: 10, minZ: 0, maxZ: 10,
Expand All @@ -35,12 +209,28 @@ export const FirstPersonControls = (speed) => {
{ angle: Math.PI / 3, position: { x: 10, y: 5, z: 5 }, width: 5, length: 50 }

],
objects: []
objects: [],
elements: {
arrows: [],
panes: [],
windowarcs: []
}
},
{
minX: 60, maxX: 160, minY: 0, maxY: 20, minZ: -50, maxZ: 50,
slopes: [],
objects: []
objects: [],
elements: {
arrows: [
{ position: { x: 0, y: -10, z: 0 }, graphName: "P0" },
{ position: { x: 0, y: -10, z: -20 }, graphName: "P1" },
{ position: { x: 0, y: -10, z: -40 }, graphName: "P2" },
{ position: { x: 15, y: -10, z: -20 }, graphName: "P3" },
{ position: { x: 35, y: -10, z: -40 }, graphName: "P4" },
{ position: { x: 0, y: -10, z: -55 }, graphName: "P5" }],
panes: [],
windowarcs: []
}
},
];

Expand All @@ -65,12 +255,14 @@ export const FirstPersonControls = (speed) => {
break;
case 'KeyE':
case 'Space':
graph.updateArrowRotations("P5");
moveUp.current = true;
break;
case 'KeyQ':
case 'ShiftLeft':
case 'ShiftRight':
moveDown.current = true;
graph.updateArrowRotations("P0");
break;
}
};
Expand Down Expand Up @@ -108,6 +300,9 @@ export const FirstPersonControls = (speed) => {
window.addEventListener('keydown', onKeyDown);
window.addEventListener('keyup', onKeyUp);

const textureLoader = new THREE.TextureLoader();
const gltfloader = new GLTFLoader();

// Add transparent boxes to visualize rooms and slopes
rooms.forEach(room => {
const roomGeometry = new THREE.BoxGeometry(
Expand Down Expand Up @@ -171,8 +366,74 @@ export const FirstPersonControls = (speed) => {
scene.add(slopeMesh);
});

// Add (interactable) elements to the scene
room.elements.panes.forEach((pane, index) => {
textureLoader.load(pane.content, (texture) => {
const aspectRatio = texture.image.width / texture.image.height;
const paneHeight = pane.sizefactor; // or any desired height
const paneWidth = paneHeight * aspectRatio;

const paneGeometry = new THREE.PlaneGeometry(paneWidth, paneHeight);
const paneMaterial = new THREE.MeshBasicMaterial({ map: texture });
const paneMesh = new THREE.Mesh(paneGeometry, paneMaterial);
paneMesh.rotation.set(-pane.verticalRotation, pane.horizontalRotation, 0, 'YXZ');
paneMesh.position.set(
room.minX + (room.maxX - room.minX) / 2 + pane.position.x,
(room.minY + room.maxY) / 2 + pane.position.y,
room.minZ + (room.maxZ - room.minZ) / 2 + pane.position.z
);
paneMesh.name = `pane-${room.minX}-${room.maxX}-${room.minZ}-${room.maxZ}-${index}`; // Naming panes to easily find them later
scene.add(paneMesh);
});
});

room.elements.windowarcs.forEach((arc, index) => {
const texture = textureLoader.load(arc.content);
const arcGeometry = new THREE.CylinderGeometry(arc.arcRadius, arc.arcRadius, arc.arcHeight, 16, 1, true, 0, Math.PI);
const arcMaterial = new THREE.MeshBasicMaterial({ map: texture, side: THREE.BackSide});
const arcMesh = new THREE.Mesh(arcGeometry, arcMaterial);
arcMesh.rotation.y = arc.horizontalRotation;
arcMesh.position.set(
room.minX + (room.maxX - room.minX) / 2 + arc.position.x,
(room.minY + room.maxY) / 2 + arc.position.y,
room.minZ + (room.maxZ - room.minZ) / 2 + arc.position.z
);
arcMesh.name = `arc-${room.minX}-${room.maxX}-${room.minZ}-${room.maxZ}-${index}`; // Naming panes to easily find them later
scene.add(arcMesh);
});


room.elements.arrows.forEach((arrow, index) => {
const mesh = gltfloader.load("/meshes/arrow.glb", function (gltf){
const arrowMesh = gltf.scene;
const whiteTexture = new THREE.MeshBasicMaterial({ color: 0xffffff });
arrowMesh.rotation.y = 0;
arrowMesh.position.set(
room.minX + (room.maxX - room.minX) / 2 + arrow.position.x,
(room.minY + room.maxY) / 2 + arrow.position.y,
room.minZ + (room.maxZ - room.minZ) / 2 + arrow.position.z
);
const modelscale = 2;
arrowMesh.scale.set(modelscale, modelscale, modelscale);
arrowMesh.visible = false;
arrowMesh.name = arrow.graphName; // Naming panes to easily find them later
graph.addArrow(arrowMesh);
scene.add(arrowMesh);
}, undefined, function (error) {
console.error('An error happened', error);
});
})
});

//temporary location of Edges, TODO: move them to a better place when refractoring
graph.addEdge("P0", "P1");
graph.addEdge("P1", "P2");
graph.addEdge("P1", "P3");
graph.addEdge("P2", "P3");
graph.addEdge("P2", "P5");
graph.addEdge("P3", "P4");
graph.addEdge("P4", "P5");

return () => {
window.removeEventListener('keydown', onKeyDown);
window.removeEventListener('keyup', onKeyUp);
Expand Down
Binary file added images/testbild.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added meshes/arrow.glb
Binary file not shown.

0 comments on commit 4915c4a

Please sign in to comment.