// 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 URL
const ai_model = "gpt-3.5-turbo"; // the OpenAI model we are going to talk to
//Image Search API URL
var 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 needed
const 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 rotated
var direction = new THREE.Vector3( 0, 0, - 1 );
var rotation = new THREE.Euler( 0, 0, 0, 'YXZ' );
return function ( v ) {
rotation.set( pitchObject.rotation.x, yawObject.rotation.y, 0 );
v.copy( direction ).applyEuler( rotation );
return v;
};
}();
};
//==============================================================================
function World() {
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 presses
var onKeyDown = function ( event ) {
switch ( event.keyCode ) {
case 38: // up
case 87: // w
moveForward = true;
break;
case 37: // left
case 65: // a
moveLeft = true; break;
case 40: // down
case 83: // s
moveBackward = true;
break;
case 39: // right
case 68: // d
moveRight = true;
break;
case 32: // space
if ( canJump === true ) velocity.y += 350;
canJump = false;
break;
}
};
var onKeyUp = function ( event ) {
switch( event.keyCode ) {
case 38: // up
case 87: // w
moveForward = false;
break;
case 37: // left
case 65: // a
moveLeft = false;
break;
case 40: // down
case 83: // s
moveBackward = false;
break;
case 39: // right
case 68: // 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 elsewhere
var description1 = ""; // Replace this line with your actual definition
var description2 = "";
var description3 = "";
var description4 = "";
var landmark1 = ""; // Replace this line with your actual definition
var landmark2 = "";
var landmark3 = "";
var landmark4 = "";
// Initial HTML string
var thehtml = "";
var htmlbutton = "";
var generatedText = "";
// Create a text box and append it to the HTML string
var 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 ID
var myTextBoxElement = document.getElementById('myTextBox');
// Define Button function
function 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 1
const 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}`);
return null;
}
})
.catch(error => {
console.error(`Error fetching image ${imageNumber}:`, error);
return null;
});
};
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 font
var loader = new THREE.FontLoader();
loader.load('https://threejs.org/examples/fonts/helvetiker_regular.typeface.json', function(font) {
// Define the maximum number of characters per line
var maxCharCount = 28;
// Split the paragraph into lines based on character count
var lines1 = (landmark1 + '\n' + description1).match(new RegExp('.{1,' + maxCharCount + '}', 'g'));
var lines2 = (landmark2 + '\n' + description2).match(new RegExp('.{1,' + maxCharCount + '}', 'g'));
var lines3 = (landmark3 + '\n' + description3).match(new RegExp('.{1,' + maxCharCount + '}', 'g'));
var lines4 = (landmark4 + '\n' + description4).match(new RegExp('.{1,' + maxCharCount + '}', 'g'));
// Define the text material
var textMaterial = new THREE.MeshBasicMaterial({color: 0xFFFFFF}); // White color
// For Paragraph 1
for (var i = 0; i < lines1.length; i++) {
// Define the text geometry
var 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 mesh
var 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);
}
// Paragraph2
for (var i = 0; i < lines2.length; i++) {
// Define the text geometry
var 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 mesh
var 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 3
for (var i = 0; i < lines3.length; i++) {
// Define the text geometry
var 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 mesh
var 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 4
for (var i = 0; i < lines4.length; i++) {
// Define the text geometry
var 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 mesh
var 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 wall
var 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 texture
var material1 = new THREE.MeshBasicMaterial({ map: texture });
// Create a mesh using the geometry and new material
var 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 texture
var material2 = new THREE.MeshBasicMaterial({ map: texture });
// Create a mesh using the geometry and new material
var 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 texture
var material3 = new THREE.MeshBasicMaterial({ map: texture });
// Create a mesh using the geometry and new material
var 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 texture
var material4 = new THREE.MeshBasicMaterial({ map: texture });
// Create a mesh using the geometry and new material
var 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 window
var 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' ID
if (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
//floor
var 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 wall
var wallGeometry = new THREE.BoxGeometry(500, 500, 1);
// Define the material of the wall
var textureLoader = new THREE.TextureLoader();
var wallTexture = new THREE.ImageUtils.loadTexture ( wallTextureFile );
// Create a material with the loaded texture
var wallMaterial = new THREE.MeshBasicMaterial({ map: wallTexture });
// Create four walls
var 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 directions
if ( 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;
//======================================================================
};
}