// Cloned by Chris Dobey on 1 Dec 2023 from World "First Person Controls" by Enhanced // Please leave this clone trail here.// Cloned by Enhanced on 21 Jun 2018 from World "First Person Controls" by Mathias Bazin // Please leave this clone trail here.// OpenAi API key (required for project to function and left blank by default):var apikey ="";const openaiURL ="https://api.openai.com/v1/chat/completions";// can POST to this 3rd party URLconst ai_model ="gpt-3.5-turbo";// the OpenAI model we are going to talk to//Image Search API URLvar cors_proxy ='https://corsproxy.io/?'var search_url ='https://www.googleapis.com/customsearch/v1?key=AIzaSyDCtyOoxLZw2GiG-FChBbziU7zHwyAxdH8&cx=a4e0f132c11a1483a&searchType=image&q='const maxTokens =300;// Adjust as neededconst conversation =[{ role:'user', content:'User message 1'},{ role:'assistant', content:'Assistant message 1'},];// Customise AB run parameters (optional).// The following parameters can be customised. (They have default values.)
AB.clockTick =20;// Speed of run: Step every n milliseconds. Default 100.
AB.maxSteps =65545;// Length of run: Maximum length of run in steps. Default 1000.
AB.screenshotStep =50;// For automatic generation of World images.// Take screenshot on this step. (All resources should have finished loading.) Default 50.
AB.drawRunControls =false;
threeworld.drawCameraControls =false;const floorTextureFile ="/uploads/dobeyc3/wooden_floor.png"const wallTextureFile ="/uploads/dobeyc3/brick_wall.jpg"const MOVESPEED =3;//==============================================================================// Defines the THREE.PointerLockControls class, source at https://threejs.org///==============================================================================
THREE.PointerLockControls=function( camera ){var scope =this;
camera.rotation.set(0,0,0);var pitchObject =new THREE.Object3D();
pitchObject.add( camera );var yawObject =new THREE.Object3D();
yawObject.position.y =10;
yawObject.add( pitchObject );var PI_2 =Math.PI /2;var onMouseMove =function( event ){if( scope.enabled ===false)return;var movementX = event.movementX || event.mozMovementX || event.webkitMovementX ||0;var movementY = event.movementY || event.mozMovementY || event.webkitMovementY ||0;
yawObject.rotation.y -= movementX *0.002;
pitchObject.rotation.x -= movementY *0.002;
pitchObject.rotation.x =Math.max(- PI_2,Math.min( PI_2, pitchObject.rotation.x ));};this.dispose =function(){
document.removeEventListener('mousemove', onMouseMove,false);};
document.addEventListener('mousemove', onMouseMove,false);this.enabled =false;this.getObject =function(){return yawObject;};this.getDirection =function(){// assumes the camera itself is not rotatedvar direction =new THREE.Vector3(0,0,-1);var rotation =new THREE.Euler(0,0,0,'YXZ');returnfunction( v ){
rotation.set( pitchObject.rotation.x, yawObject.rotation.y,0);
v.copy( direction ).applyEuler( rotation );return v;};}();};//==============================================================================functionWorld(){var camera, controls;var objects =[];var raycaster;var moveForward =false;var moveBackward =false;var moveLeft =false;var moveRight =false;var canJump =false;var prevTime = performance.now();var velocity =new THREE.Vector3();var direction =new THREE.Vector3();var vertex =new THREE.Vector3();var color =new THREE.Color();var countryInput;var image_url1 =""var image_url2 =""var image_url3 =""var image_url4 =""var parts =[]var name_description1 =[]var name_description2 =[]var name_description3 =[]var name_description4 =[]var previousMesh1 =[];var previousMesh2 =[];var previousMesh3 =[];var previousMesh4 =[];this.newRun =function(){
camera =new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight,1,1000);
threeworld.camera = camera;
threeworld.init3d (0,0,0x7ec0ee);var light =new THREE.HemisphereLight(0xeeeeff,0x777788,0.75);
light.position.set(0.5,1,0.75);
threeworld.scene.add( light );
threeworld.scene.fog =new THREE.Fog(0xffffff,0,1500);
controls =new THREE.PointerLockControls( camera );
threeworld.scene.add( controls.getObject());//This will handle key pressesvar onKeyDown =function( event ){switch( event.keyCode ){case38:// upcase87:// w
moveForward =true;break;case37:// leftcase65:// a
moveLeft =true;break;case40:// downcase83:// s
moveBackward =true;break;case39:// rightcase68:// d
moveRight =true;break;case32:// spaceif( canJump ===true) velocity.y +=350;
canJump =false;break;}};var onKeyUp =function( event ){switch( event.keyCode ){case38:// upcase87:// w
moveForward =false;break;case37:// leftcase65:// a
moveLeft =false;break;case40:// downcase83:// s
moveBackward =false;break;case39:// rightcase68:// d
moveRight =false;break;}};
document.addEventListener('keydown', onKeyDown,false);
document.addEventListener('keyup', onKeyUp,false);
raycaster =new THREE.Raycaster(new THREE.Vector3(),new THREE.Vector3(0,-1,0),0,10);
$("#user_span1").html(" Use WASD or Arrows to move, mouse to look around and space to jump.");var blocker = $("#user_span2");
blocker.html("<p><b>Click screen to enable mouse controls</b></p>");// Assuming you have a variable named 'paragraph' defined elsewherevar description1 ="";// Replace this line with your actual definitionvar description2 ="";var description3 ="";var description4 ="";var landmark1 ="";// Replace this line with your actual definitionvar landmark2 ="";var landmark3 ="";var landmark4 ="";// Initial HTML stringvar thehtml ="";var htmlbutton ="";var generatedText ="";// Create a text box and append it to the HTML stringvar textBox = document.createElement('input');
textBox.type ='text';
textBox.id ='myTextBox';
htmlbutton ="<button id='countrySubmit' class='normbutton'>Submit</button> <br> ";
thehtml +="<label for='myTextBox'>Enter a Country: </label>"+ textBox.outerHTML + htmlbutton;
AB.msg(thehtml,1);// Access the text box using its IDvar myTextBoxElement = document.getElementById('myTextBox');// Define Button functionfunction logText(){
generatedText =""
parts =[]const x = myTextBoxElement.value;// Replace this with dynamic value
fetch(openaiURL,{
method:'POST',
headers:{'Content-Type':'application/json','Authorization':`Bearer ${apikey}`,},
body: JSON.stringify({
model: ai_model,
messages:[{
role:"user",
content:`For the country of ${x}, I want you to return a brief description of each of this country's 4 most famous landmarks.Each description should be aproximately 45 words in length.Give your response in the following format:"Insert name of landmark 1 here: Insert Description 1 here (delimit with "/" symbol) Insert name of landmark 2 here: Insert Description 2 here (delimit with "/" symbol) Insert name of landmark 3 here: Insert Description 3 here (delimit with "/" symbol) Insert name of landmark 4 here: Insert Description 4 here"."It is vitally important that you delimit between each landmark with a / symbol`,}],
max_tokens: maxTokens,}),}).then(response => response.json()).then(data =>{if(data.choices && data.choices.length >0){
generatedText = data.choices[0].message.content;
console.log(generatedText);try{// Split the string based on the slash symbol
parts = generatedText.split('/');// Extract the part before the colon for landmark 1
name_description1 = parts[0].split(':');
landmark1 = name_description1[0].trim();// Use trim to remove leading and trailing whitespaces
description1 = name_description1[1].trim();// Use trim to remove leading and trailing whitespaces// Extract the part before the colon for landmark 2
name_description2 = parts[1].split(':');
landmark2 = name_description2[0].trim();// Use trim to remove leading and trailing whitespaces
description2 = name_description2[1].trim();// Use trim to remove leading and trailing whitespaces// Extract the part before the colon for landmark 3
name_description3 = parts[2].split(':');
landmark3 = name_description3[0].trim();// Use trim to remove leading and trailing whitespaces
description3 = name_description3[1].trim();// Use trim to remove leading and trailing whitespaces// Extract the part before the colon for landmark 4
name_description4 = parts[3].split(':');
landmark4 = name_description4[0].trim();// Use trim to remove leading and trailing whitespaces
description4 = name_description4[1].trim();// Use trim to remove leading and trailing whitespaces
placeText();}catch(error){
console.log("Failed to delimit on /, Trying for a new Response...");
logText();}}else{
console.error('Error: Empty or undefined data.choices array');}}).catch(error => console.error('Error:', error));}function logImage(){// Image 1const fetchImage =(searchUrl, landmark, imageNumber)=>{const imageSearch = searchUrl + landmark;return fetch(imageSearch,{
method:'GET',
headers:{'Content-Type':'application/json',},}).then(response => response.json()).then(data =>{if(data.items && data.items.length >0){return cors_proxy + data.items[0].link;}else{
console.error(`Error:Empty or undefined data.items array for image ${imageNumber}`);returnnull;}}).catch(error =>{
console.error(`Error fetching image ${imageNumber}:`, error);returnnull;});};const imagePromises =[
fetchImage(search_url, landmark1,1),
fetchImage(search_url, landmark2,2),
fetchImage(search_url, landmark3,3),
fetchImage(search_url, landmark4,4),];// MH edit
console.log ( landmark1 );Promise.all(imagePromises).then(imageURLs =>{// imageURLs is an array containing the results of all the fetch requests[image_url1, image_url2, image_url3, image_url4]= imageURLs;// MH edit
console.log ( imageURLs );
placeImage();}).catch(error => console.error('Error:', error));}function placeText(){if(previousMesh1.length >0){for(var i =0; i < previousMesh1.length; i++){
threeworld.scene.remove(previousMesh1[i]);}for(var i =0; i < previousMesh2.length; i++){
threeworld.scene.remove(previousMesh2[i]);}for(var i =0; i < previousMesh3.length; i++){
threeworld.scene.remove(previousMesh3[i]);}for(var i =0; i < previousMesh4.length; i++){
threeworld.scene.remove(previousMesh4[i]);}// Clear the arrays
previousMesh1 =[];
previousMesh2 =[];
previousMesh3 =[];
previousMesh4 =[];}// Load the fontvar loader =new THREE.FontLoader();
loader.load('https://threejs.org/examples/fonts/helvetiker_regular.typeface.json',function(font){// Define the maximum number of characters per linevar maxCharCount =28;// Split the paragraph into lines based on character countvar lines1 =(landmark1 +'\n'+ description1).match(newRegExp('.{1,'+ maxCharCount +'}','g'));var lines2 =(landmark2 +'\n'+ description2).match(newRegExp('.{1,'+ maxCharCount +'}','g'));var lines3 =(landmark3 +'\n'+ description3).match(newRegExp('.{1,'+ maxCharCount +'}','g'));var lines4 =(landmark4 +'\n'+ description4).match(newRegExp('.{1,'+ maxCharCount +'}','g'));// Define the text materialvar textMaterial =new THREE.MeshBasicMaterial({color:0xFFFFFF});// White color// For Paragraph 1for(var i =0; i < lines1.length; i++){// Define the text geometryvar textGeometry =new THREE.TextGeometry(lines1[i],{
font: font,
size:15,
height:0.1,
curveSegments:12,
bevelEnabled:true,
bevelThickness:0.01,
bevelSize:0.01,
bevelOffset:0,
bevelSegments:5});// Create the text meshvar textMesh1 =new THREE.Mesh(textGeometry, textMaterial);// Set the position of the text
textMesh1.position.set(-225,210- i *20,-249);// Front wall, adjust the y position based on the line number// Add the text to the scene
threeworld.scene.add(textMesh1);
previousMesh1.push(textMesh1);}// Paragraph2for(var i =0; i < lines2.length; i++){// Define the text geometryvar textGeometry =new THREE.TextGeometry(lines2[i],{
font: font,
size:15,
height:0.1,
curveSegments:12,
bevelEnabled:true,
bevelThickness:0.01,
bevelSize:0.01,
bevelOffset:0,
bevelSegments:5});// Create the text meshvar textMesh2 =new THREE.Mesh(textGeometry, textMaterial);// Set the position of the text
textMesh2.position.set(249,210- i *20,-225);// Front wall, adjust the y position based on the line number
textMesh2.rotation.set(0,-Math.PI /2,0);// Add the text to the scene
threeworld.scene.add(textMesh2);
previousMesh2.push(textMesh2);}// Paragraph 3for(var i =0; i < lines3.length; i++){// Define the text geometryvar textGeometry =new THREE.TextGeometry(lines3[i],{
font: font,
size:15,
height:0.1,
curveSegments:12,
bevelEnabled:true,
bevelThickness:0.01,
bevelSize:0.01,
bevelOffset:0,
bevelSegments:5});// Create the text meshvar textMesh3 =new THREE.Mesh(textGeometry, textMaterial);// Set the position of the text
textMesh3.position.set(225,210- i *20,249);// Front wall, adjust the y position based on the line number// Rotate the text to face the wall behind the camera
textMesh3.rotation.set(0,Math.PI,0);// Add the text to the scene
threeworld.scene.add(textMesh3);
previousMesh3.push(textMesh3);}// Paragraph 4for(var i =0; i < lines4.length; i++){// Define the text geometryvar textGeometry =new THREE.TextGeometry(lines4[i],{
font: font,
size:15,
height:0.1,
curveSegments:12,
bevelEnabled:true,
bevelThickness:0.01,
bevelSize:0.01,
bevelOffset:0,
bevelSegments:5});// Create the text meshvar textMesh4 =new THREE.Mesh(textGeometry, textMaterial);// Set the position of the text
textMesh4.position.set(-249,210- i *20,225);// Front wall, adjust the y position based on the line number
textMesh4.rotation.set(0,Math.PI /2,0);// Add the text to the scene
threeworld.scene.add(textMesh4);
previousMesh4.push(textMesh4);}});
logImage();}function placeImage(){var image_loader =new THREE.TextureLoader();// Create a geometry for the wallvar geometry =new THREE.PlaneGeometry(150,200);// Create a material using the texture (placeholder texture for now)var material =new THREE.MeshBasicMaterial({ color:0xffffff});// Load the image 1
image_loader.load(image_url1,function(texture){// Create a new material using the loaded texturevar material1 =new THREE.MeshBasicMaterial({ map: texture });// Create a mesh using the geometry and new materialvar mesh1 =new THREE.Mesh(geometry, material1);// Set the position of the mesh
mesh1.position.set(150,120,-249);// Front wall// Add the mesh to the scene
threeworld.scene.add(mesh1);});// Load the image 2
image_loader.load(image_url2,function(texture){// Create a new material using the loaded texturevar material2 =new THREE.MeshBasicMaterial({ map: texture });// Create a mesh using the geometry and new materialvar mesh2 =new THREE.Mesh(geometry, material2);// Set the position and rotation of the mesh
mesh2.position.set(249,120,150);
mesh2.rotation.set(0,-Math.PI /2,0);// Add the mesh to the scene
threeworld.scene.add(mesh2);});// Load the image 3
image_loader.load(image_url3,function(texture){// Create a new material using the loaded texturevar material3 =new THREE.MeshBasicMaterial({ map: texture });// Create a mesh using the geometry and new materialvar mesh3 =new THREE.Mesh(geometry, material3);// Set the position and rotation of the mesh
mesh3.position.set(-150,120,249);
mesh3.rotation.set(0,Math.PI,0);// Add the mesh to the scene
threeworld.scene.add(mesh3);});// Load the image 4
image_loader.load(image_url4,function(texture){// Create a new material using the loaded texturevar material4 =new THREE.MeshBasicMaterial({ map: texture });// Create a mesh using the geometry and new materialvar mesh4 =new THREE.Mesh(geometry, material4);// Set the position and rotation of the mesh
mesh4.position.set(-249,120,-150);
mesh4.rotation.set(0,Math.PI /2,0);// Add the mesh to the scene
threeworld.scene.add(mesh4);});}// Attach event listener using JavaScript
document.getElementById('countrySubmit').addEventListener('click', logText);//The following handles pointer locking when clicking the windowvar havePointerLock ='pointerLockElement' in document ||'mozPointerLockElement' in document ||'webkitPointerLockElement' in document;
console.log(havePointerLock);if( havePointerLock ){var element = document.body;var pointerlockchange =function( event ){if( document.pointerLockElement === element || document.mozPointerLockElement === element || document.webkitPointerLockElement === element ){
controls.enabled =true;
blocker.html(" <p><b>Use WASD or Arrows to move, mouse to look around and space to jump.</b></p> ");}else{
controls.enabled =false;
blocker.html("<p><b>Click screen to enable mouse controls</b></p>");}};var pointerlockerror =function( event ){
console.error("pointerlockerror");};// Hook pointer lock state change events
document.addEventListener('pointerlockchange', pointerlockchange,false);
document.addEventListener('mozpointerlockchange', pointerlockchange,false);
document.addEventListener('webkitpointerlockchange', pointerlockchange,false);
document.addEventListener('pointerlockerror', pointerlockerror,false);
document.addEventListener('mozpointerlockerror', pointerlockerror,false);
document.addEventListener('webkitpointerlockerror', pointerlockerror,false);
document.addEventListener('click',function( event ){// Check if the clicked element has either 'myTextBox' or 'button' IDif(event.target.id ==='myTextBox'|| event.target.id ==='countrySubmit'){return;// Do nothing for the excluded elements}// Ask the browser to lock the pointer
element.requestPointerLock = element.requestPointerLock || element.mozRequestPointerLock || element.webkitRequestPointerLock;
element.requestPointerLock();},false);}else{
$("#user_span1").html('<p>Your browser doesn\'t seem to support Pointer Lock API</p>');}//The following draws a simple scene//floorvar floorGeometry =new THREE.PlaneBufferGeometry(2000,2000,100,100);
floorGeometry.rotateX(-Math.PI /2);var floorTexture =new THREE.ImageUtils.loadTexture ( floorTextureFile );
floorTexture.minFilter = THREE.LinearFilter;
floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping;
floorTexture.offset.set(0,0);
floorTexture.repeat.set(40,40);var floor =new THREE.Mesh(floorGeometry,new THREE.MeshBasicMaterial({map : floorTexture}));
floor.position.set(0,-20,0);
threeworld.scene.add(floor);// Define the geometry of the wallvar wallGeometry =new THREE.BoxGeometry(500,500,1);// Define the material of the wallvar textureLoader =new THREE.TextureLoader();var wallTexture =new THREE.ImageUtils.loadTexture ( wallTextureFile );// Create a material with the loaded texturevar wallMaterial =new THREE.MeshBasicMaterial({ map: wallTexture });// Create four wallsvar wall1 =new THREE.Mesh(wallGeometry, wallMaterial);var wall2 =new THREE.Mesh(wallGeometry, wallMaterial);var wall3 =new THREE.Mesh(wallGeometry, wallMaterial);var wall4 =new THREE.Mesh(wallGeometry, wallMaterial);// Set the position of each wall
wall1.position.set(0,5,-250);// Front wall
wall2.position.set(0,5,250);// Back wall
wall3.position.set(-250,5,0);// Left wall
wall4.position.set(250,5,0);// Right wall// Rotate the left and right walls
wall3.rotation.y =Math.PI /2;
wall4.rotation.y =Math.PI /2;// Add the walls to the scene
threeworld.scene.add(wall1);
threeworld.scene.add(wall2);
threeworld.scene.add(wall3);
threeworld.scene.add(wall4);};this.nextStep =function(){//======================================================================//This will handle moving the player and the camera//======================================================================
raycaster.ray.origin.copy( controls.getObject().position );
raycaster.ray.origin.y -=10;var intersections = raycaster.intersectObjects( objects );var onObject = intersections.length >0;var time = performance.now();var delta =( time - prevTime )/1000;
velocity.x -= velocity.x *10.0* delta;
velocity.z -= velocity.z *10.0* delta;
velocity.y -=9.8*100.0* delta;// 100.0 = mass
direction.z =Number( moveForward )-Number( moveBackward );
direction.x =Number( moveLeft )-Number( moveRight );
direction.normalize();// this ensures consistent movements in all directionsif( moveForward || moveBackward ) velocity.z -= direction.z *400.0* MOVESPEED * delta;if( moveLeft || moveRight ) velocity.x -= direction.x *400.0* MOVESPEED * delta;if( onObject ===true){
velocity.y =Math.max(0, velocity.y );
canJump =true;}
controls.getObject().translateX( velocity.x * delta );
controls.getObject().translateY( velocity.y * delta );
controls.getObject().translateZ( velocity.z * delta );if( controls.getObject().position.y <10){
velocity.y =0;
controls.getObject().position.y =10;
canJump =true;}
prevTime = time;//======================================================================};}