This is the official MarsX scripting documentation. MarsX uses Javascript to create game logic.
You may use CTRL + F
to search on this documentation.
Websites recommended to learn JavaScript at:
https://www.w3schools.com/js/
https://www.freecodecamp.org/
https://www.learn-js.org/
https://codepen.io/ for tons of code examples
https://developer.mozilla.org/ for great documentation and examples
Also recommended to use https://chat.openai.com/ with specific questions, you could refer to the words 'Three.js editor' when asking questions for better answers.
MarsX is using Three.js and our modified version of their editor.
Everything possible with Three.js is possible in the MarsX editor.
Built in to the MarsX editor is also cannon.js.
Credit to the Three.js and cannon.js developers/community.
function update(event) {} // Executed right before a frame is going to be rendered. Its primary purpose is to update the state of the 3D object which owns the script. The method has an event parameter which holds a time and delta property. time represents the elapsed time in milliseconds and delta represents the time between two frames in milliseconds.
function init() {} // Executed once after the application has been loaded.
function start() {} // Executed once when the application is ready to start rendering.
function stop() {} // Executed once when the application is stopped.
It is also possible to implement event listeners for selected browser events. The following events are supported by the editor:
- keydown
- keyup
- pointerdown
- pointerup
- pointermove
Certain application components are accessible in the scope of scripts as variables:
player
A reference to the application player (a wrapper component which executes the editor application).
renderer
A reference to the renderer.
scene
A reference to the scene graph.
camera
A reference to the application's camera.
THREE
A reference to the three.js library.
world
A reference to the cannon.js world.
two
A reference to the two.js canvas.
editor
A reference to the editor, used in the web console (F12).
Code outside of lifecycle and event listeners is immediately executed when the script is loaded.
The this
reference can be used to refer to the 3D object which owns the script.
To refer to the physical body, you may use this.body
.
It is recommended to have the Developer Console open when working on a project.
There are multiple ways to open the console: CTRL + SHIFT + I
, CTRL + SHIFT + J
or by pressing F12
A list of useful Javascript keywords/code:
// Creating variables
globalVariable = null; // create a global variable, can be modified later
let integerVariable = 10; // integer (add decimal to make float)
var boolVariable = true; // boolean
const stringVariable = "string"; // string
integerVariable += 10; // add 10 to the variable
integerVariable -= 10; // remove 10 from the variable
stringVariable += 'c'; // This would have added the character 'c', however, the variable is a constant (use let/var instead).
boolVariable = !boolVariable; // This will switch the bool from true to false, and false to true whenever executed. ! stands for 'not'
// Debugging/logging
console.log(integerVariable); // output: 10
console.log(integerVariable + 10) // output: 20
console.alert("warning");
// Loops (the 'i' stands for index)
for (let i = 0; i < 10; i++) {} // Loop will run 10 times, value 'i' will increase by 1 each time
while (true) {} // While loops tend to freeze the application as they execute as fast and much as they can.
// Statements
if (boolVariable) {console.log("true");}
if (10 > 5) {console.log("10 is greater than 5");}
else if ("word" == "word") {} // == stands for 'equal to'
else {console.log("else");}
if (variable1 != variable2) {} // != stands for 'not equal to'
if (variable1 >= variable2) {} // >= or <= stands for 'greater or equal to' or 'less or equal to'
if (variable1 || variable2) {} // || stands for 'or'
if variable1 && variable2) {} // && stands for 'and'
switch (2) { // Put expression where the '2' is. (could be a variable)
case 1:
console.log(1);
break;
case 2:
console.log("this piece will execute");
break
default:
console.alert("Hello World!");
}
// Datatype conversion
parseInt(stringVariable); // Convert string to int
integerVariable.toString(); // Convert int to string
// Math
Math.floor(Math.random() * 100); // Retrieve random number between 0 and 100 without any decimals
Math.PI // Pi (3.1415..)
Math.sin, Math.tan, Math.cos, Math.asin, Math.atan, Math.acos; // Trigonometry
console.log(1+1); // Addition
console.log(1-1); // Subtraction
console.log(1*1); // Multiplication
console.log(1/1); // Division
console.log(1%1); // Remainder
eval(10+20); // output: 30, eval() can be used to calculate any expression
// Functions
function test() {
console.log("test");
}
test(); // output: "test"
function test(text) {
console.log(text);
}
test("hello"); // output: "hello"
function test(text) {
return text;
}
test("hello"); // output: nothing, instead:
console.log(test("hello")); // output: "hello"
// Stop code
return;
break;
// Try catch (useful when you don't want your program to stop running if it encounters an error)
try {console.log("success");} // Tries to execute the code, if it fails, then code in the 'catch' will be executed.
catch (error) {console.log(error);} // Code inside 'try' somehow failed. (put nothing inside {} if you don't want it to do anything on fail)
// Array
let array = [100, 50, 20];
console.log(array[1]); // output: 50, index starts at 0
for (let i = 0; i < array.length; i++) {console.log(array[i]); // output: 100 50 20
// Comments
// This is a comment
/* This is
a multiline
comment*/
// String
let example = "this is a string";
let example2 = 'this is a string';
let example3 = `this is also a string`;
let age = 30;
console.log(`Age is: ${age}`); // Using the ` to make strings, you can easily pass in variables
// instead of
console.log("Age is: "+age);
console.log("\n"); // The \n makes a 'new line', it can be put anywhere in a string
// Input
let input = prompt("Enter number: ");
let ok = confirm("Do you agree?");
if (ok) {/* code */} // if 'ok' is 'true', then the code inside the curly brackets will execute
It is also recommended that you learn about JSON in Javascript, structure and how to use it.
Scope object:
let cube = scene.getObjectByName('Box'); // Object scoping in the scene, target another object
Sleep functions:
// Sync sleep function
function sleep(ms) {
var start = Date.now(), expire = start + ms;
while (Date.now() < expire) {}
return;
}
sleep(2000); // example
// Async sleep function
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
await sleep(2000); // example
User input:
function init( event ) {
window.addEventListener('keypress', (event) => {
console.log(event.key);
})
}
Moving camera or other objects:
camera.position.x = this.position.x;
camera.position.y = this.position.y;
camera.position.z = this.position.z;
camera.position.y += 20;
// or
camera.position.copy(this.position);
camera.position.y += 20;
// or
camera.position.set(10, 10, 10);
Delete objects:
scene.remove(this);
// or
scene.remove(scene.getObjectByName('Box'));
Log objects:
for (let i = 0; i < scene.children.length; i++) {
console.log(scene.children[i]); // Logs every object created
}
Information about current object:
console.log(this); // Very simple, will output everything about the object that the script created on.
Rotate object:
this.rotation.x += 0.01; // In radians
Clone object:
let objectClone = this.clone(); // Define clone variable as a clone of 'this' object
objectClone.position.copy(this.position); // Set position to 'this' position
scene.add(objectClone); // Add clone to the scene
Change object color:
this.material.color.setRGB(0, 255, 0);
// or
this.material.color.g = 255;
Switching camera:
player.setCamera(this); // 'this' can be replaced with something such as scene.getObjectByName('newCamera');
Create object:
// Create a new box geometry
var geometry = new THREE.BoxGeometry( 1, 1, 1 );
// Create a new material
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
// Create a new mesh with the geometry and material
var cube = new THREE.Mesh( geometry, material );
// Set the position of the cube
cube.position.set( 0, 0, 0 );
// Add the cube to the scene
scene.add( cube );
Smooth movement example, try it on a cube, capsule or similar:
let keysDown={}; // Array with pressed down keys
function keyDown(e){ keysDown[e.key]=true; } // Function to set key to true if it's held down
function keyUp(e){ delete keysDown[e.key]; } // Function to delete key from array if it's released
window.addEventListener( 'keydown', keyDown, false ); // Event listener to detect if a key is down
window.addEventListener( 'keyup', keyUp, false ); // Event listener to detect if a key is released
function update( event ) { // Main loop
if (keysDown['a']) {this.position.x -= 0.1;} // Move left if 'a' is held down
if (keysDown['d']) {this.position.x += 0.1;} // Move right if 'd' is held down
if (keysDown['w']) {this.position.z -= 0.1;} // Move forward if 'w' is held down
if (keysDown['s']) {this.position.z += 0.1;} // Move backward if 's' is held down
camera.position.copy(this.position); // Set camera to 'this' object's position
camera.position.y += 15; // Add offset on 'y' axis
camera.position.z += 15; // Add offset on 'z' axis
camera.rotation.set(-0.75, 0, 0); // Set correct camera rotation
}
Collision detection:
function update( event ) { // Main loop
const box1 = new THREE.Box3().setFromObject(this); // Create collision boxes, required to be in the loop or they won't update
const box2 = new THREE.Box3().setFromObject(scene.getObjectByName('Plane')); // This could be any object, in this case, it's the floor
if (box1.intersectsBox(box2)) { // Check if they collide
console.log("collides"); // Do code
}
this.position.y += 0.01; // Move 'this' object up
}
Adding simple gravity:
const box1 = new THREE.Box3().setFromObject(this); // Create collision boxes
const box2 = new THREE.Box3().setFromObject(scene.getObjectByName('Plane')); // This could be any object, in this case, it's the floor
if (!box1.intersectsBox(box2)) { // Check if they collide
this.position.y -= 0.5;
}
Object velocity and jumping:
let keysDown={}; // Array with pressed down keys
let yVel = 0; // Assign y-axis velocity variable
let jumping = false; // Jumping state boolean
function keyDown(e){ keysDown[e.key]=true; } // Function to set key to true if it's held down
function keyUp(e){ delete keysDown[e.key]; } // Function to delete key from array if it's released
window.addEventListener( 'keydown', keyDown, false ); // Event listener to detect if a key is down
window.addEventListener( 'keyup', keyUp, false ); // Event listener to detect if a key is released
function update( event ) { // Main loop
const box1 = new THREE.Box3().setFromObject(this); // Create collision boxes
const box2 = new THREE.Box3().setFromObject(scene.getObjectByName('Plane')); // This could be any object, in this case, it's the floor
if (box1.intersectsBox(box2) && !jumping) { // If NOT jumping and touching 'Plane'
yVel = 0; // Set velocity to 0
}
else if (!box1.intersectsBox(box2)) { // If not colliding with the ground
yVel -= 0.01; // Add negative value to the velocity, will cause 'this' object to fall
}
this.position.y += yVel; // Set 'this' object's position to the velocity
if (box1.intersectsBox(box2) && keysDown[' ']) {yVel += 0.1; jumping = true;} // Jumping, only works if you're touching the ground
else {jumping = false;} // Change the jumping state boolean to false if you're not jumping anymore
if (keysDown['a']) {this.position.x -= 0.1;} // Move left if 'a' is held down
if (keysDown['d']) {this.position.x += 0.1;} // Move right if 'd' is held down
if (keysDown['w']) {this.position.z -= 0.1;} // Move forward if 'w' is held down
if (keysDown['s']) {this.position.z += 0.1;} // Move backward if 's' is held down
camera.position.copy(this.position); // Set camera to 'this' object's position
camera.position.y += 15; // Add offset on 'y' axis
camera.position.z += 15; // Add offset on 'z' axis
camera.rotation.set(-0.75, 0, 0); // Set correct camera rotation
}
Velocity:
let yVelocity = 0; // Define velocity variable
function update(event) {
yVelocity -= 0.01; // Add negative velocity, cause object to fall
this.position.y += yVelocity; // Apply/update velocity to object's position (y)
2.5D camera angle:
function update( event ) {
camera.position.copy(this.position);
camera.position.x += 80;
camera.rotation.set(0, Math.PI/2, 0);
}
Three.js Github Addons: https://github.com/mrdoob/three.js/tree/dev/examples/jsm
CDN (must be 0.147.0 or below): https://cdn.jsdelivr.net/npm/three@0.147.0/examples/js/
Great websites for additional javascript cdn: https://cdnjs.com/ and https://www.jsdelivr.com/
Import and use a Three.js example addon (PointerLockControls.js in this example):
function loadControls() { // Function
const controls = new THREE.PointerLockControls(camera, document.body); // Addon code inside this function, in this case defining 'controls'
controls.lock(); // Locking cursor
}
const controlsScript = document.createElement('script'); // Make script to import addon
controlsScript.src = 'https://cdn.jsdelivr.net/npm/three@0.147.0/examples/js/controls/PointerLockControls.js'; // Importing addon
controlsScript.onload = loadControls; // Make sure it executes our code after successfully loading
document.head.appendChild(controlsScript); // Add the script element to the document head (finishing initialization)
function stop() {
controlsScript.remove(); // Getting rid of addon after stopped playing
}
Cannon.js physics with three.js:
This example imports the cannon.js library, however, MarsX automatically imports it into the editor now. The importing step of this example is unnecessary.
const boxes = []; // Three.js boxes
const boxBodies = []; // Cannon.js bodies
function loadCannon() { // Function
const world = new CANNON.World(); // Define cannon.js world
world.gravity.set(0, -9.82, 0); // Set world gravity -9.82m/s
const platformGeometry = new THREE.BoxGeometry(10, 0.5, 10);
const platformMaterial = new THREE.MeshPhongMaterial({ color: 0xffffff });
const platform = new THREE.Mesh(platformGeometry, platformMaterial);
scene.add(platform); // Creating the platform and adding it to the scene
const platformShape = new CANNON.Box(new CANNON.Vec3(5, 0.25, 5));
const platformBody = new CANNON.Body({ mass: 0 });
platformBody.addShape(platformShape);
world.addBody(platformBody); // Creating the body of the platform
const boxGeometry = new THREE.BoxGeometry(1, 1, 1);
const boxMaterial = new THREE.MeshPhongMaterial({ color: 0xff0000 });
// Creating box base
// Creating 100 boxes
for (let i = 0; i < 100; i++) {
const box = new THREE.Mesh(boxGeometry, boxMaterial);
box.position.set(Math.random() * 8 - 4, 5 + i * 2, Math.random() * 8 - 4);
scene.add(box);
boxes.push(box);
const boxShape = new CANNON.Box(new CANNON.Vec3(0.5, 0.5, 0.5));
const boxBody = new CANNON.Body({ mass: 1 });
boxBody.addShape(boxShape);
boxBody.position.copy(box.position);
boxBodies.push(boxBody);
world.addBody(boxBody);
}
// Creating light
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
directionalLight.position.set(10, 20, 10);
scene.add(directionalLight);
// Animate function
function animate() {
requestAnimationFrame(animate);
world.step(1 / 60);
// Updating boxes
for (let i = 0; i < boxes.length; i++) {
boxes[i].position.copy(boxBodies[i].position);
boxes[i].quaternion.copy(boxBodies[i].quaternion);
}
renderer.render(scene, camera);
}
animate();
// Clean up function
loadCannon.stop = function() {
// remove boxes from scene
for (let i = 0; i < boxes.length; i++) {
scene.remove(boxes[i]);
}
// remove box bodies from world
for (let i = 0; i < boxBodies.length; i++) {
world.remove(boxBodies[i]);
}
// reset boxes and boxBodies arrays
boxes.length = 0;
boxBodies.length = 0;
}
}
function stop() { // When the program stops, call the clean up function
loadCannon.stop();
}
const cannonScript = document.createElement('script'); // Make script to import addon
cannonScript.src = 'https://cdnjs.cloudflare.com/ajax/libs/cannon.js/0.6.2/cannon.min.js'; // Importing addon
cannonScript.onload = loadCannon; // Make sure it executes our code after successfully loading
document.head.appendChild(cannonScript); // Add the script element to the document head (finishing initialization)
For physics we are using cannon.js. More information about cannon.js and what's possible can be found at: https://github.com/schteppe/cannon.js
Apply physics to project, put in 'scene' script or add the physics directly through the Toolbox:
Important notice: In the User data
of each object, you can enter 2 parameters (not mandatory, default would be anchored set to false and mass set to 1): anchored
and mass
of the object!
Example 1: { "anchored": true, "mass": 10000 }
Example 2: {}
Remember that objects such as platforms want a high mass to prevent objects from clipping through.world
is a global variable to refer to the cannon.js world and this.body
can be used to refer to the current object's physical cannon.js body
// This took a while to make - Cedric
world.broadphase = new CANNON.NaiveBroadphase();
let bodies = [];
scene.children.forEach(object => {
if (!object.geometry) return;
let shape;
if (object.geometry.type === "BoxGeometry") {
const box3 = new THREE.Box3().setFromObject(object);
const size = box3.getSize(new THREE.Vector3());
const halfWidth = size.x / 2;
const halfHeight = size.y / 2;
const halfDepth = size.z / 2;
shape = new CANNON.Box(new CANNON.Vec3(halfWidth, halfHeight, halfDepth));
if (object.rotation) {
const rot = new THREE.Euler().setFromQuaternion(object.quaternion);
const halfSize = new THREE.Vector3(halfWidth, halfHeight, halfDepth);
halfSize.applyEuler(rot);
shape = new CANNON.Box(new CANNON.Vec3(halfSize.x, halfSize.y, halfSize.z));
}
} else if (object.geometry.type === "SphereGeometry") {
const radius = object.geometry.parameters.radius;
shape = new CANNON.Sphere(radius);
if (object.scale) shape.radius *= Math.max(object.scale.x, object.scale.y, object.scale.z);
} else if (object.geometry.type === "CylinderGeometry") {
const radiusTop = object.geometry.parameters.radiusTop;
const radiusBottom = object.geometry.parameters.radiusBottom;
const height = object.geometry.parameters.height;
object.updateMatrixWorld();
const boundingBox = new THREE.Box3().setFromObject(object);
const size = boundingBox.getSize(new THREE.Vector3());
const scaleFactor = Math.max(...object.scale.toArray());
const halfHeight = height / 2 * scaleFactor;
const radius = Math.max(radiusTop, radiusBottom) * scaleFactor;
shape = new CANNON.Cylinder(radius, radius, halfHeight, 16);
const quat = new CANNON.Quaternion();
quat.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2);
shape.transformAllPoints(new CANNON.Vec3(), quat);
if (object.scale) {
shape.radiusTop *= scaleFactor;
shape.radiusBottom *= scaleFactor;
shape.height *= scaleFactor;
}
} else {
console.warn("Unsupported geometry type:", object.geometry.type);
return;
}
let mass = 1;
if (typeof object.userData["mass"] !== "undefined") mass = object.userData["mass"];
const body = new CANNON.Body({mass});
if (shape.type === CANNON.Shape.types.CYLINDER) shape.centerOfMass = new CANNON.Vec3(0, -shape.height / 2, 0);
body.addShape(shape);
body.position.copy(object.position);
body.quaternion.copy(object.quaternion);
world.addBody(body);
bodies.push(body);
object.body = body;
if (object.userData["anchored"] == true) {
body.mass = 1e9;
body.type = CANNON.Body.STATIC;
}
function update() {
if (object.userData !== "anchored") {
object.position.copy(body.position);
object.quaternion.copy(body.quaternion);
}
requestAnimationFrame(update);
}
update();
});
function stop() {
for (let i = 0; i < bodies.length; i++) {
world.remove(bodies[i]);
}
bodies = [];
}
Applying force on an object's physical body:
this.body
can be used to refer to the cannon.js body of the object.
let keysDown = {}; // Array with pressed down keys
// Creating the force and point variables to apply on the body of the object: 'this.body'
var force = new CANNON.Vec3(0, 3000, 0); // The force is in the negative y-direction
var point = new CANNON.Vec3(0, 0, 0); // Apply the force at the center of the body
function keyDown(e) {
keysDown[e.key] = true;
} // Function to set key to true if it's held down
function keyUp(e) {
delete keysDown[e.key];
} // Function to delete key from array if it's released
window.addEventListener("keydown", keyDown, false); // Event listener to detect if a key is down
window.addEventListener("keyup", keyUp, false); // Event listener to detect if a key is released
function update(event) {
if (keysDown[" "]) { // If Spacebar is pressed
this.body.applyForce(force, point); // Applying force upwards
}
camera.position.copy(this.body.position); // Set camera to body's position
camera.position.z += 40; // Add offset on 'z' axis
camera.rotation.set(0, 0, 0); // Set correct camera rotation
}
Rotate physical body:
function init( event ) {
var axis = new CANNON.Vec3(0,0,1);
this.body.quaternion.setFromAxisAngle(axis, Math.PI/2);
}
We have included two.js to easily draw user interface/2 dimensional elements. For more information about two.js and what's possible with it, check here: https://github.com/jonobr1/two.js/
You are not required to use it, you may import another library, use a canvas or use html/css to create buttons and inputboxes. It is all up to you
two
is a global variable to interact with the two.js library.
Below is a few examples on two.js Create a rotating rectangle:
var rect = two.makeRectangle(two.width / 2, two.height / 2, 50 ,50);
function update() {
rect.rotation += 0.01;
}
Change color:
var rect = two.makeRectangle(100, 100, 50, 50);
rect.fill = 'rgb(255, 100, 100)';
rect.noStroke();
Change position:
function update() {
rect.translation.x += 1;
rect.translation.y += 1;
}
Detect when shape is clicked on:
var rect = two.makeRectangle(100, 100, 50, 50);
rect.fill = 'rgb(255, 100, 100)';
rect.noStroke();
two.update(); // Important to update or it won't work
rect._renderer.elem.addEventListener('click', function() {
console.log("Rect was clicked.")
}, false);
Text and counter:
// Create a text object
var counter = two.makeText("0", two.width / 2, two.height / 2);
// Set the font size, alignment and color
counter.size = 80;
counter.alignment = "center";
counter.fill = "lime";
// Initialize the counter value
var count = 0;
function update() {
counter.value = count.toString(); // Set the value to the variable 'count' but string converted
count++; // Add 1 to the value
}
Load image/texture:
const texture = new Two.Texture('https://upload.wikimedia.org/wikipedia/commons/3/3a/Cat03.jpg');
const image = two.makeSprite(texture);
image.translation.set(two.width / 2, two.height / 2);
Shaders can be found at: https://www.shadertoy.com/
Select and object and head over to the Material tab and from there you want to set the type to SHADERMATERIAL.
On the program row, you can now set INFO, VERT and FRAG (information, vertices and color).
Shaders are written in GLSL (OpenGL Shaders Language)
An examples from Three.js on an Icosahedron mesh:
INFO:
{
"defines": {},
"uniforms": {
"time": {
"value": 0
}
}
}
VERT:
uniform float time;
varying vec3 vPosition;
void main() {
vPosition = position;
vPosition.x += sin( time + vPosition.z * 4.0 ) / 4.0;
vPosition.y += cos( time + vPosition.z * 4.0 ) / 4.0;
gl_Position = projectionMatrix * modelViewMatrix * vec4( vPosition, 1.0 );
}
FRAG:
varying vec3 vPosition;
void main() {
gl_FragColor = vec4( vPosition * 2.0, 1.0 );
}
SCRIPT:
function update( event ) {
this.material.uniforms.time.value = event.time / 500.0;
}
Don't forget to set the detail under the Geometry tab to something high such as 24.
// Create an AudioListener and add it to the camera
const listener = new THREE.AudioListener();
camera.add(listener);
// Load an audio file
const audioLoader = new THREE.AudioLoader();
audioLoader.load('https://jonhosting.com/SWC/files/audio/snd_coin.mp3', function(buffer) {
// Create an Audio object and add it to the listener
const sound = new THREE.Audio(listener);
sound.setBuffer(buffer);
sound.setLoop(false); // Set to true if you want the audio to loop
sound.setVolume(1.0); // Set the volume (0.0 to 1.0)
// Play the audio
sound.play();
});
let controller1, controller2;
function init() {
// Create the left and right controllers
controller1 = renderer.xr.getController( 0 );
controller1.addEventListener( 'selectstart', onSelectStart );
controller1.addEventListener( 'selectend', onSelectEnd );
scene.add( controller1 );
controller2 = renderer.xr.getController( 1 );
controller2.addEventListener( 'selectstart', onSelectStart );
controller2.addEventListener( 'selectend', onSelectEnd );
scene.add( controller2 );
// Create the hand meshes and add them to the controllers
const hand1 = new THREE.Mesh( new THREE.SphereBufferGeometry( 0.05, 32, 32 ), new THREE.MeshStandardMaterial() );
//hand1.position.set( -0.1, -0.1, -0.2 );A
controller1.add( hand1 );
const hand2 = new THREE.Mesh( new THREE.SphereBufferGeometry( 0.05, 32, 32 ), new THREE.MeshStandardMaterial() );
//hand2.position.set( 0.1, -0.1, -0.2 );
controller2.add( hand2 );
}
function onSelectStart() {
// Handle select start event
}
function onSelectEnd() {
// Handle select end event
}
function animate() {
renderer.setAnimationLoop( render );
}
function render() {
// Update the hand meshes positions and rotations based on the controllers' positions and rotations
hand1.position.copy( controller1.position );
hand1.quaternion.copy( controller1.quaternion );
hand2.position.copy( controller2.position );
hand2.quaternion.copy( controller2.quaternion );
renderer.render( scene, camera );
}
init()
animate();
Additional Three.js documentation can be found at: https://threejs.org/docs/
Ask questions on MarsF: https://ullblocks.jonhosting.com/forums/ (currently under development)
Or you can join our Discord server: https://discord.gg/s78zWvwV6d
Contact me on Discord with suggestions (or open an issue): Cedric#0591