// // solarsystem.js // games // // Created by Bridget Went, 5/28/15. // Copyright 2015 High Fidelity, Inc. // // The start to a project to build a virtual physics classroom to simulate the solar system, gravity, and orbital physics. // - A sun with oribiting planets is created in front of the user // - UI elements allow for adjusting the period, gravity, trails, and energy recalculations // - Click "PAUSE" to pause the animation and show planet labels // - In this mode, double-click a planet label to zoom in on that planet // -Double-clicking on earth label initiates satellite orbiter game // -Press "TAB" to toggle back to solar system view // // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // Script.include([ '../utilities/tools/cookies.js', 'games/satellite.js' ]); var DAMPING = 0.0; var LIFETIME = 6000; var BASE_URL = "https://s3.amazonaws.com/hifi-public/marketplace/hificontent/Scripts/planets/planets/"; // Save intiial avatar and camera position var startingPosition = {x: 800, y: 800, z: 800}; MyAvatar.position = startingPosition; var startFrame = Window.location.href; var panelPosition = {x: 300, y: 300}; var panelWidth = 100; var panelHeight = 300; var panelBackground = Overlays.addOverlay("text", { backgroundColor: { red: 200, green: 200, blue: 255 }, x: panelPosition.x, y: panelPosition.y, width: panelWidth, height: panelHeight, alpha: 1.0, backgroundAlpha: 0.5, visible: true }); // Place the sun var MAX_RANGE = 50.0; var SUN_SIZE = 8.0; var center = Vec3.sum(startingPosition, Vec3.multiply(MAX_RANGE, Quat.getFront(Camera.getOrientation()))); var theSun = Entities.addEntity({ type: "Model", modelURL: BASE_URL + "sun.fbx", position: center, dimensions: { x: SUN_SIZE, y: SUN_SIZE, z: SUN_SIZE }, angularVelocity: { x: 0.0, y: 0.1, z: 0.0 }, angularDamping: DAMPING, damping: DAMPING, ignoreCollisions: false, lifetime: LIFETIME, collisionsWillMove: false }); // Reference values for physical constants var referenceRadius = 7.0; var referencePeriod = 1.0; var LARGE_BODY_MASS = 250.0; var SMALL_BODY_MASS = LARGE_BODY_MASS * 0.000000333; var GRAVITY = (Math.pow(referenceRadius, 3.0) / Math.pow((referencePeriod / (2.0 * Math.PI)), 2.0)) / LARGE_BODY_MASS; var REFERENCE_GRAVITY = GRAVITY; var planets = []; var planetCount = 0; var TRAILS_ENABLED = true; var MAX_POINTS_PER_LINE = 50; var LINE_DIM = 200; var LINE_WIDTH = 20; var VELOCITY_OFFSET_Y = -0.3; var VELOCITY_OFFSET_Z = 0.9; var index = 0; var Planet = function(name, trailColor, radius, size) { this.name = name; this.trailColor = trailColor; this.trail = []; this.lineStack = []; this.radius = radius; this.position = Vec3.sum(center, { x: this.radius, y: 0.0, z: 0.0 }); this.period = (2.0 * Math.PI) * Math.sqrt(Math.pow(this.radius, 3.0) / (GRAVITY * LARGE_BODY_MASS)); this.gravity = GRAVITY; this.initialVelocity = Math.sqrt((GRAVITY * LARGE_BODY_MASS) / radius); this.velocity = Vec3.multiply(this.initialVelocity, Vec3.normalize({ x: 0, y: VELOCITY_OFFSET_Y, z: VELOCITY_OFFSET_Z })); this.dimensions = size; this.planet = Entities.addEntity({ type: "Model", modelURL: BASE_URL + name + ".fbx", position: this.position, dimensions: { x: this.dimensions, y: this.dimensions, z: this.dimensions }, velocity: this.velocity, angularDamping: DAMPING, damping: DAMPING, ignoreCollisions: false, lifetime: LIFETIME, collisionsWillMove: true, }); this.computeAcceleration = function() { var acc = -(this.gravity * LARGE_BODY_MASS) * Math.pow(this.radius, (-2.0)); return acc; }; this.update = function(timeStep) { var between = Vec3.subtract(this.position, center); var speed = this.computeAcceleration(this.radius) * timeStep; var vel = Vec3.multiply(speed, Vec3.normalize(between)); // Update velocity and position this.velocity = Vec3.sum(this.velocity, vel); this.position = Vec3.sum(this.position, Vec3.multiply(timeStep, this.velocity)); Entities.editEntity(this.planet, { velocity: this.velocity, position: this.position }); if (TRAILS_ENABLED) { this.updateTrail(); } }; this.resetTrails = function() { elapsed = 0.0; this.trail = []; this.lineStack = []; //add the first line to both the line entity stack and the trail this.trail.push(newLine(this.lineStack, this.position, this.period, this.trailColor)); }; this.updateTrail = function() { var point = this.position; var linePos = Entities.getEntityProperties(this.lineStack[this.lineStack.length - 1]).position; this.trail.push(computeLocalPoint(linePos, point)); Entities.editEntity(this.lineStack[this.lineStack.length - 1], { linePoints: this.trail }); if (this.trail.length == MAX_POINTS_PER_LINE) { this.trail = newLine(this.lineStack, point, this.period, this.trailColor); } }; this.adjustPeriod = function(alpha) { // Update global G constant, period, poke velocity to new value var ratio = this.last_alpha / alpha; this.gravity = Math.pow(ratio, 2.0) * GRAVITY; this.period = ratio * this.period; this.velocity = Vec3.multiply(ratio, this.velocity); this.last_alpha = alpha; } index++; this.resetTrails(); } var MERCURY_LINE_COLOR = {red: 255, green: 255, blue: 255}; var VENUS_LINE_COLOR = {red: 255, green: 160, blue: 110}; var EARTH_LINE_COLOR = {red: 10, green: 150, blue: 160}; var MARS_LINE_COLOR = {red: 180, green: 70, blue: 10}; var JUPITER_LINE_COLOR = {red: 250, green: 140, blue: 0}; var SATURN_LINE_COLOR = {red: 235, green: 215, blue: 0}; var URANUS_LINE_COLOR = {red: 135, green: 205, blue: 240}; var NEPTUNE_LINE_COLOR = {red: 30, green: 140, blue: 255}; var PLUTO_LINE_COLOR = {red: 255, green: 255, blue: 255}; planets.push(new Planet("mercury", MERCURY_LINE_COLOR, 7.0, 1.0)); planets.push(new Planet("venus", VENUS_LINE_COLOR, 8.5, 1.2)); planets.push(new Planet("earth", EARTH_LINE_COLOR, 10.2, 1.6)); planets.push(new Planet("mars", MARS_LINE_COLOR, 16.0, 2.0)); planets.push(new Planet("jupiter", JUPITER_LINE_COLOR, 19.5, 4.3)); planets.push(new Planet("saturn", SATURN_LINE_COLOR, 29.0, 3.7)); planets.push(new Planet("uranus", URANUS_LINE_COLOR, 37.0, 4.0)); planets.push(new Planet("neptune", NEPTUNE_LINE_COLOR, 55.0, 4.2)); planets.push(new Planet("pluto", PLUTO_LINE_COLOR, 80.0, 3.2)); var labels = []; var labelLines = []; var labelsShowing = false; var LABEL_X = 8.0; var LABEL_Y = 3.0; var LABEL_Z = 1.0; var LABEL_DIST = 8.0; var TEXT_HEIGHT = 1.0; var PlanetLabel = function(name, index) { var text = name + " Speed: " + Vec3.length(planets[index].velocity).toFixed(2); var labelPos = Vec3.sum(planets[index].position, { x: 0.0, y: LABEL_DIST, z: LABEL_DIST }); var linePos = planets[i].position; this.line = Entities.addEntity({ type: "Line", position: linePos, dimensions: { x: 20, y: 20, z: 20 }, lineWidth: LINE_WIDTH, color: { red: 255, green: 255, blue: 255 }, linePoints: [{ x: 0, y: 0, z: 0 }, computeLocalPoint(linePos, labelPos)], visible: false }); this.label = Entities.addEntity({ type: "Text", text: text, lineHeight: TEXT_HEIGHT, dimensions: { x: LABEL_X, y: LABEL_Y, z: LABEL_Z }, position: labelPos, backgroundColor: { red: 10, green: 10, blue: 10 }, faceCamera: true, visible: false }); labelLines.push(line); labels.push(label); this.show = function() { this.showing = true; Entities.editEntity(this.line, {visible: true}); Entities.editEntity(this.label, {visible: true}); } this.hide = function() { this.showing = false; Entities.editEntity(this.line, {visible: false}); Entities.editEntity(this.label, {visible: false}); } } var time = 0.0; var elapsed; var TIME_STEP = 60.0; var dt = 1.0 / TIME_STEP; var planetLines = []; var trails = []; function update(deltaTime) { // if (paused) { // return; // } deltaTime = dt; time++; if (time % TIME_STEP == 0) { elapsed++; } for (var i = 0; i < planets.length; ++i) { planets[i].update(deltaTime); } } function computeLocalPoint(linePos, worldPoint) { var localPoint = Vec3.subtract(worldPoint, linePos); return localPoint; } // Create a new line function newLine(lineStack, point, period, color) { if (elapsed < period) { var line = Entities.addEntity({ position: point, type: "Line", color: color, dimensions: { x: LINE_DIM, y: LINE_DIM, z: LINE_DIM }, lifetime: LIFETIME, lineWidth: LINE_WIDTH }); lineStack.push(line); } else { // Begin overwriting first lines after one full revolution (one period) var firstLine = lineStack.shift(); Entities.editEntity(firstLine, { position: point, linePoints: [{ x: 0.0, y: 0.0, z: 0.0 }] }); lineStack.push(firstLine); } var points = []; points.push(computeLocalPoint(point, point)); return points; } // function mousePressEvent(event) { // var clickedOverlay = Overlays.getOverlayAtPoint({ // x: event.x, // y: event.y // }); // if (clickedOverlay == pauseButton) { // paused = !paused; // for (var i = 0; i < NUM_PLANETS; ++i) { // Entities.editEntity(planets[i], { // velocity: { // x: 0.0, // y: 0.0, // z: 0.0 // } // }); // } // if (paused && !labelsShowing) { // Overlays.editOverlay(pauseButton, { // text: "Paused", // backgroundColor: { // red: 255, // green: 50, // blue: 50 // } // }); // showLabels(); // } // if (paused == false && labelsShowing) { // Overlays.editOverlay(pauseButton, { // text: "Pause", // backgroundColor: { // red: 200, // green: 200, // blue: 255 // } // }); // hideLabels(); // } // planetView = false; // } // } // function keyPressEvent(event) { // // Jump back to solar system view // if (event.text == "TAB" && planetView) { // if (earthView) { // satelliteGame.endGame(); // earthView = false; // } // MyAvatar.position = startingPosition; // } // } // //switch to planet view // function mouseDoublePressEvent(event) { // if (earthView) { // return; // } // var pickRay = Camera.computePickRay(event.x, event.y) // var rayPickResult = Entities.findRayIntersection(pickRay, true); // for (var i = 0; i < NUM_PLANETS; ++i) { // if (rayPickResult.entityID === labels[i]) { // planetView = true; // if (i == 2) { // MyAvatar.position = Vec3.sum(center, { // x: 200, // y: 200, // z: 200 // }); // Camera.setPosition(Vec3.sum(center, { // x: 200, // y: 200, // z: 200 // })); // earthView = true; // satelliteGame = new SatelliteGame(); // } else { // MyAvatar.position = Vec3.sum({ // x: 0.0, // y: 0.0, // z: 3.0 // }, planet_properties[i].position); // Camera.lookAt(planet_properties[i].position); // } // break; // } // } // } // // UI ELEMENTS // // USE FLOATING UI PANEL TO IMPROVE UI EXPERIENCE // var paused = false; // // Create UI panel // var panel = new Panel(PANEL_X, PANEL_Y); // var g_multiplier = 1.0; // panel.newSlider("Adjust Gravitational Force: ", 0.1, 5.0, // function(value) { // g_multiplier = value; // G = G_ref * g_multiplier; // }, // function() { // return g_multiplier; // }, // function(value) { // return value.toFixed(1) + "x"; // }); // var period_multiplier = 1.0; // var last_alpha = period_multiplier; // panel.newSubPanel("Adjust Orbital Periods"); // /* // TODO: Loop through all planets, creating new sliders and adjusting their respective orbital periods // */ // for (var i = 0; i < planets.length; ++i) // panel.newSlider("Adjust Orbital Period: ", 0.1, 3.0, // function(value) { // period_multiplier = value; // changePeriod(period_multiplier); // }, // function() { // return period_multiplier; // }, // function(value) { // return (value).toFixed(2) + "x"; // })); // panel.newCheckbox("Leave Trails: ", // function(value) { // trailsEnabled = value; // if (trailsEnabled) { // for (var i = 0; i < NUM_PLANETS; ++i) { // resetTrails(i); // } // //if trails are off and we've already created trails, remove existing trails // } else if (planetLines.length != 0) { // for (var i = 0; i < NUM_PLANETS; ++i) { // for (var j = 0; j < planetLines[i].length; ++j) { // Entities.editEntity(planetLines[i][j], {visible: false}); // } // planetLines[i] = []; // } // } // }, // function() { // return trailsEnabled; // }, // function(value) { // return value; // })); // Clean up models, UI panels, lines, and button overlays function scriptEnding() { satelliteGame.endGame(); Entities.deleteEntity(theSun); for (var i = 0; i < NUM_PLANETS; ++i) { Entities.deleteEntity(planets[i]); } Menu.removeMenu("Developer > Scene"); panel.destroy(); Overlays.deleteOverlay(pauseButton); var e = Entities.findEntities(MyAvatar.position, 16000); for (i = 0; i < e.length; i++) { var props = Entities.getEntityProperties(e[i]); if (props.type === "Line" || props.type === "Text") { Entities.deleteEntity(e[i]); } } }; // Controller.mouseMoveEvent.connect(function panelMouseMoveEvent(event) { // return panel.mouseMoveEvent(event); // }); // Controller.mousePressEvent.connect(function panelMousePressEvent(event) { // return panel.mousePressEvent(event); // }); // Controller.mouseDoublePressEvent.connect(function panelMouseDoublePressEvent(event) { // return panel.mouseDoublePressEvent(event); // }); // Controller.mouseReleaseEvent.connect(function(event) { // return panel.mouseReleaseEvent(event); // }); // Controller.mousePressEvent.connect(mousePressEvent); // Controller.mouseDoublePressEvent.connect(mouseDoublePressEvent); // Controller.keyPressEvent.connect(keyPressEvent); Script.scriptEnding.connect(scriptEnding); Script.update.connect(update);