merge upstream/master into andrew/isentropic

Conflicts:
	libraries/physics/src/ObjectMotionState.cpp
This commit is contained in:
Andrew Meadows 2015-01-23 14:04:26 -08:00
commit 1ad5f9c7db
13 changed files with 587 additions and 197 deletions

View file

@ -1,6 +1,7 @@
// Pool Table // Pool Table
var tableParts = []; var tableParts = [];
var balls = []; var balls = [];
var cueBall;
var LENGTH = 2.84; var LENGTH = 2.84;
var WIDTH = 1.42; var WIDTH = 1.42;
@ -13,6 +14,8 @@ var HOLE_SIZE = BALL_SIZE;
var DROP_HEIGHT = BALL_SIZE * 3.0; var DROP_HEIGHT = BALL_SIZE * 3.0;
var GRAVITY = -9.8; var GRAVITY = -9.8;
var BALL_GAP = 0.001; var BALL_GAP = 0.001;
var tableCenter;
var cuePosition;
var startStroke = 0; var startStroke = 0;
@ -23,7 +26,7 @@ var reticle = Overlays.addOverlay("image", {
y: screenSize.y / 2 - 16, y: screenSize.y / 2 - 16,
width: 32, width: 32,
height: 32, height: 32,
imageURL: HIFI_PUBLIC_BUCKET + "images/reticle.png", imageURL: HIFI_PUBLIC_BUCKET + "images/billiardsReticle.png",
color: { red: 255, green: 255, blue: 255}, color: { red: 255, green: 255, blue: 255},
alpha: 1 alpha: 1
}); });
@ -102,7 +105,7 @@ function makeBalls(pos) {
{ red: 128, green: 128, blue: 128}]; // Gray { red: 128, green: 128, blue: 128}]; // Gray
// Object balls // Object balls
var ballPosition = { x: pos.x + (LENGTH / 4.0) * SCALE, y: pos.y + DROP_HEIGHT, z: pos.z }; var ballPosition = { x: pos.x + (LENGTH / 4.0) * SCALE, y: pos.y + HEIGHT / 2.0 + DROP_HEIGHT, z: pos.z };
for (var row = 1; row <= 5; row++) { for (var row = 1; row <= 5; row++) {
ballPosition.z = pos.z - ((row - 1.0) / 2.0 * (BALL_SIZE + BALL_GAP) * SCALE); ballPosition.z = pos.z - ((row - 1.0) / 2.0 * (BALL_SIZE + BALL_GAP) * SCALE);
for (var spot = 0; spot < row; spot++) { for (var spot = 0; spot < row; spot++) {
@ -113,23 +116,26 @@ function makeBalls(pos) {
color: colors[balls.length], color: colors[balls.length],
gravity: { x: 0, y: GRAVITY, z: 0 }, gravity: { x: 0, y: GRAVITY, z: 0 },
ignoreCollisions: false, ignoreCollisions: false,
damping: 0.40, damping: 0.50,
collisionsWillMove: true })); collisionsWillMove: true }));
ballPosition.z += (BALL_SIZE + BALL_GAP) * SCALE; ballPosition.z += (BALL_SIZE + BALL_GAP) * SCALE;
} }
ballPosition.x += (BALL_GAP + Math.sqrt(3.0) / 2.0 * BALL_SIZE) * SCALE; ballPosition.x += (BALL_GAP + Math.sqrt(3.0) / 2.0 * BALL_SIZE) * SCALE;
} }
// Cue Ball // Cue Ball
ballPosition = { x: pos.x - (LENGTH / 4.0) * SCALE, y: pos.y + DROP_HEIGHT, z: pos.z }; cuePosition = { x: pos.x - (LENGTH / 4.0) * SCALE, y: pos.y + HEIGHT / 2.0 + DROP_HEIGHT, z: pos.z };
balls.push(Entities.addEntity( cueBall = Entities.addEntity(
{ type: "Sphere", { type: "Sphere",
position: ballPosition, position: cuePosition,
dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE }, dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE },
color: { red: 255, green: 255, blue: 255 }, color: { red: 255, green: 255, blue: 255 },
gravity: { x: 0, y: GRAVITY, z: 0 }, gravity: { x: 0, y: GRAVITY, z: 0 },
angularVelocity: { x: 0, y: 0, z: 0 },
velocity: {x: 0, y: 0, z: 0 },
ignoreCollisions: false, ignoreCollisions: false,
damping: 0.40, damping: 0.50,
collisionsWillMove: true })); collisionsWillMove: true });
} }
function shootCue(velocity) { function shootCue(velocity) {
@ -140,19 +146,23 @@ function shootCue(velocity) {
var velocity = Vec3.multiply(forwardVector, velocity); var velocity = Vec3.multiply(forwardVector, velocity);
var BULLET_LIFETIME = 3.0; var BULLET_LIFETIME = 3.0;
var BULLET_GRAVITY = 0.0; var BULLET_GRAVITY = 0.0;
var SHOOTER_COLOR = { red: 255, green: 0, blue: 0 };
var SHOOTER_SIZE = BALL_SIZE / 1.5 * SCALE;
bulletID = Entities.addEntity( bulletID = Entities.addEntity(
{ type: "Sphere", { type: "Sphere",
position: cuePosition, position: cuePosition,
dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE }, dimensions: { x: SHOOTER_SIZE, y: SHOOTER_SIZE, z: SHOOTER_SIZE },
color: { red: 255, green: 255, blue: 255 }, color: SHOOTER_COLOR,
velocity: velocity, velocity: velocity,
lifetime: BULLET_LIFETIME, lifetime: BULLET_LIFETIME,
gravity: { x: 0, y: BULLET_GRAVITY, z: 0 }, gravity: { x: 0, y: BULLET_GRAVITY, z: 0 },
damping: 0.10, damping: 0.10,
density: 1000, density: 8000,
ignoreCollisions: false, ignoreCollisions: false,
collisionsWillMove: true collisionsWillMove: true
}); });
print("Shot, velocity = " + velocity);
} }
function keyReleaseEvent(event) { function keyReleaseEvent(event) {
@ -185,9 +195,25 @@ function cleanup() {
Entities.deleteEntity(balls[i]); Entities.deleteEntity(balls[i]);
} }
Overlays.deleteOverlay(reticle); Overlays.deleteOverlay(reticle);
Entities.deleteEntity(cueBall);
} }
var tableCenter = Vec3.sum(MyAvatar.position, Vec3.multiply(4.0, Quat.getFront(Camera.getOrientation()))); function update(deltaTime) {
if (!cueBall.isKnownID) {
cueBall = Entities.identifyEntity(cueBall);
} else {
// Check if cue ball has fallen off table, re-drop if so
var cueProperties = Entities.getEntityProperties(cueBall);
if (cueProperties.position.y < tableCenter.y) {
// Replace the cueball
Entities.editEntity(cueBall, { position: cuePosition } );
}
}
}
tableCenter = Vec3.sum(MyAvatar.position, Vec3.multiply(4.0, Quat.getFront(Camera.getOrientation())));
makeTable(tableCenter); makeTable(tableCenter);
makeBalls(tableCenter); makeBalls(tableCenter);
@ -195,3 +221,4 @@ makeBalls(tableCenter);
Script.scriptEnding.connect(cleanup); Script.scriptEnding.connect(cleanup);
Controller.keyPressEvent.connect(keyPressEvent); Controller.keyPressEvent.connect(keyPressEvent);
Controller.keyReleaseEvent.connect(keyReleaseEvent); Controller.keyReleaseEvent.connect(keyReleaseEvent);
Script.update.connect(update);

View file

@ -47,7 +47,6 @@ var yawFromMouse = 0;
var pitchFromMouse = 0; var pitchFromMouse = 0;
var isMouseDown = false; var isMouseDown = false;
var BULLET_VELOCITY = 5.0;
var MIN_THROWER_DELAY = 1000; var MIN_THROWER_DELAY = 1000;
var MAX_THROWER_DELAY = 1000; var MAX_THROWER_DELAY = 1000;
var LEFT_BUTTON_3 = 3; var LEFT_BUTTON_3 = 3;
@ -98,7 +97,7 @@ var reticle = Overlays.addOverlay("image", {
y: screenSize.y / 2 - 16, y: screenSize.y / 2 - 16,
width: 32, width: 32,
height: 32, height: 32,
imageURL: HIFI_PUBLIC_BUCKET + "images/reticle.png", imageURL: HIFI_PUBLIC_BUCKET + "images/billiardsReticle.png",
color: { red: 255, green: 255, blue: 255}, color: { red: 255, green: 255, blue: 255},
alpha: 1 alpha: 1
}); });
@ -113,6 +112,25 @@ var offButton = Overlays.addOverlay("image", {
alpha: 1 alpha: 1
}); });
var platformButton = Overlays.addOverlay("image", {
x: screenSize.x - 48,
y: 130,
width: 32,
height: 32,
imageURL: HIFI_PUBLIC_BUCKET + "images/city.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
var gridButton = Overlays.addOverlay("image", {
x: screenSize.x - 48,
y: 164,
width: 32,
height: 32,
imageURL: HIFI_PUBLIC_BUCKET + "images/blocks.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
if (showScore) { if (showScore) {
var text = Overlays.addOverlay("text", { var text = Overlays.addOverlay("text", {
x: screenSize.x / 2 - 100, x: screenSize.x / 2 - 100,
@ -127,24 +145,30 @@ if (showScore) {
}); });
} }
function printVector(string, vector) { var BULLET_VELOCITY = 10.0;
print(string + " " + vector.x + ", " + vector.y + ", " + vector.z);
}
function shootBullet(position, velocity) { function shootBullet(position, velocity, grenade) {
var BULLET_SIZE = 0.07; var BULLET_SIZE = 0.10;
var BULLET_LIFETIME = 10.0; var BULLET_LIFETIME = 10.0;
var BULLET_GRAVITY = 0.0; var BULLET_GRAVITY = -0.25;
var GRENADE_VELOCITY = 15.0;
var GRENADE_SIZE = 0.35;
var GRENADE_GRAVITY = -9.8;
var bVelocity = grenade ? Vec3.multiply(GRENADE_VELOCITY, Vec3.normalize(velocity)) : velocity;
var bSize = grenade ? GRENADE_SIZE : BULLET_SIZE;
var bGravity = grenade ? GRENADE_GRAVITY : BULLET_GRAVITY;
bulletID = Entities.addEntity( bulletID = Entities.addEntity(
{ type: "Sphere", { type: "Sphere",
position: position, position: position,
dimensions: { x: BULLET_SIZE, y: BULLET_SIZE, z: BULLET_SIZE }, dimensions: { x: bSize, y: bSize, z: bSize },
color: { red: 255, green: 0, blue: 0 }, color: { red: 255, green: 0, blue: 0 },
velocity: velocity, velocity: bVelocity,
lifetime: BULLET_LIFETIME, lifetime: BULLET_LIFETIME,
gravity: { x: 0, y: BULLET_GRAVITY, z: 0 }, gravity: { x: 0, y: bGravity, z: 0 },
damping: 0.01, damping: 0.01,
density: 5000, density: 8000,
ignoreCollisions: false, ignoreCollisions: false,
collisionsWillMove: true collisionsWillMove: true
}); });
@ -158,13 +182,15 @@ function shootBullet(position, velocity) {
} }
// Kickback the arm // Kickback the arm
if (elbowKickAngle > 0.0) {
MyAvatar.setJointData("LeftForeArm", rotationBeforeKickback);
}
rotationBeforeKickback = MyAvatar.getJointRotation("LeftForeArm"); rotationBeforeKickback = MyAvatar.getJointRotation("LeftForeArm");
var armRotation = MyAvatar.getJointRotation("LeftForeArm"); var armRotation = MyAvatar.getJointRotation("LeftForeArm");
armRotation = Quat.multiply(armRotation, Quat.fromPitchYawRollDegrees(0.0, 0.0, KICKBACK_ANGLE)); armRotation = Quat.multiply(armRotation, Quat.fromPitchYawRollDegrees(0.0, 0.0, KICKBACK_ANGLE));
MyAvatar.setJointData("LeftForeArm", armRotation); MyAvatar.setJointData("LeftForeArm", armRotation);
elbowKickAngle = KICKBACK_ANGLE; elbowKickAngle = KICKBACK_ANGLE;
} }
function shootTarget() { function shootTarget() {
var TARGET_SIZE = 0.50; var TARGET_SIZE = 0.50;
var TARGET_GRAVITY = 0.0; var TARGET_GRAVITY = 0.0;
@ -174,7 +200,7 @@ function shootTarget() {
var DISTANCE_TO_LAUNCH_FROM = 5.0; var DISTANCE_TO_LAUNCH_FROM = 5.0;
var ANGLE_RANGE_FOR_LAUNCH = 20.0; var ANGLE_RANGE_FOR_LAUNCH = 20.0;
var camera = Camera.getPosition(); var camera = Camera.getPosition();
//printVector("camera", camera);
var targetDirection = Quat.angleAxis(getRandomFloat(-ANGLE_RANGE_FOR_LAUNCH, ANGLE_RANGE_FOR_LAUNCH), { x:0, y:1, z:0 }); var targetDirection = Quat.angleAxis(getRandomFloat(-ANGLE_RANGE_FOR_LAUNCH, ANGLE_RANGE_FOR_LAUNCH), { x:0, y:1, z:0 });
targetDirection = Quat.multiply(Camera.getOrientation(), targetDirection); targetDirection = Quat.multiply(Camera.getOrientation(), targetDirection);
var forwardVector = Quat.getFront(targetDirection); var forwardVector = Quat.getFront(targetDirection);
@ -205,6 +231,78 @@ function shootTarget() {
Audio.playSound(targetLaunchSound, audioOptions); Audio.playSound(targetLaunchSound, audioOptions);
} }
function makeGrid(type, scale, size) {
var separation = scale * 2;
var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation())));
var x, y, z;
var GRID_LIFE = 60.0;
var dimensions;
for (x = 0; x < size; x++) {
for (y = 0; y < size; y++) {
for (z = 0; z < size; z++) {
dimensions = { x: separation/2.0 * (0.5 + Math.random()), y: separation/2.0 * (0.5 + Math.random()), z: separation/2.0 * (0.5 + Math.random()) / 4.0 };
Entities.addEntity(
{ type: type,
position: { x: pos.x + x * separation, y: pos.y + y * separation, z: pos.z + z * separation },
dimensions: dimensions,
color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 },
velocity: { x: 0, y: 0, z: 0 },
gravity: { x: 0, y: 0, z: 0 },
lifetime: GRID_LIFE,
rotation: Camera.getOrientation(),
damping: 0.1,
density: 100.0,
collisionsWillMove: true });
}
}
}
}
function makePlatform(gravity, scale, size) {
var separation = scale * 2;
var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation())));
pos.y -= separation * size;
var x, y, z;
var TARGET_LIFE = 60.0;
var INITIAL_GAP = 0.5;
var dimensions;
for (x = 0; x < size; x++) {
for (y = 0; y < size; y++) {
for (z = 0; z < size; z++) {
dimensions = { x: separation/2.0, y: separation, z: separation/2.0 };
Entities.addEntity(
{ type: "Box",
position: { x: pos.x - (separation * size / 2.0) + x * separation,
y: pos.y + y * (separation + INITIAL_GAP),
z: pos.z - (separation * size / 2.0) + z * separation },
dimensions: dimensions,
color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 },
velocity: { x: 0, y: 0, z: 0 },
gravity: { x: 0, y: gravity, z: 0 },
lifetime: TARGET_LIFE,
damping: 0.1,
density: 100.0,
collisionsWillMove: true });
}
}
}
// Make a floor for this stuff to fall onto
Entities.addEntity({
type: "Box",
position: { x: pos.x, y: pos.y - separation / 2.0, z: pos.z },
dimensions: { x: 2.0 * separation * size, y: separation / 2.0, z: 2.0 * separation * size },
color: { red: 128, green: 128, blue: 128 },
lifetime: TARGET_LIFE
});
}
function entityCollisionWithEntity(entity1, entity2, collision) { function entityCollisionWithEntity(entity1, entity2, collision) {
if (((entity1.id == bulletID.id) || (entity1.id == targetID.id)) && if (((entity1.id == bulletID.id) || (entity1.id == targetID.id)) &&
@ -233,7 +331,9 @@ function keyPressEvent(event) {
var time = MIN_THROWER_DELAY + Math.random() * MAX_THROWER_DELAY; var time = MIN_THROWER_DELAY + Math.random() * MAX_THROWER_DELAY;
Script.setTimeout(shootTarget, time); Script.setTimeout(shootTarget, time);
} else if ((event.text == ".") || (event.text == "SPACE")) { } else if ((event.text == ".") || (event.text == "SPACE")) {
shootFromMouse(); shootFromMouse(false);
} else if (event.text == ",") {
shootFromMouse(true);
} else if (event.text == "r") { } else if (event.text == "r") {
playLoadSound(); playLoadSound();
} else if (event.text == "s") { } else if (event.text == "s") {
@ -241,7 +341,6 @@ function keyPressEvent(event) {
Quat.print("arm = ", MyAvatar.getJointRotation("LeftArm")); Quat.print("arm = ", MyAvatar.getJointRotation("LeftArm"));
Quat.print("forearm = ", MyAvatar.getJointRotation("LeftForeArm")); Quat.print("forearm = ", MyAvatar.getJointRotation("LeftForeArm"));
Quat.print("hand = ", MyAvatar.getJointRotation("LeftHand")); Quat.print("hand = ", MyAvatar.getJointRotation("LeftHand"));
} }
} }
@ -287,18 +386,7 @@ function update(deltaTime) {
if (targetID && !targetID.isKnownID) { if (targetID && !targetID.isKnownID) {
targetID = Entities.identifyEntity(targetID); targetID = Entities.identifyEntity(targetID);
} }
// Check for mouseLook movement, update rotation
// rotate body yaw for yaw received from mouse
var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3Radians( { x: 0, y: yawFromMouse, z: 0 } ));
//MyAvatar.orientation = newOrientation;
yawFromMouse = 0;
// apply pitch from mouse
var newPitch = MyAvatar.headPitch + pitchFromMouse;
//MyAvatar.headPitch = newPitch;
pitchFromMouse = 0;
if (activeControllers == 0) { if (activeControllers == 0) {
if (Controller.getNumberOfSpatialControls() > 0) { if (Controller.getNumberOfSpatialControls() > 0) {
activeControllers = Controller.getNumberOfSpatialControls(); activeControllers = Controller.getNumberOfSpatialControls();
@ -372,19 +460,19 @@ function update(deltaTime) {
var velocity = Vec3.multiply(BULLET_VELOCITY, Vec3.normalize(palmToFingerTipVector)); var velocity = Vec3.multiply(BULLET_VELOCITY, Vec3.normalize(palmToFingerTipVector));
shootBullet(position, velocity); shootBullet(position, velocity, false);
} }
} }
} }
} }
function shootFromMouse() { function shootFromMouse(grenade) {
var DISTANCE_FROM_CAMERA = 1.0; var DISTANCE_FROM_CAMERA = 1.0;
var camera = Camera.getPosition(); var camera = Camera.getPosition();
var forwardVector = Quat.getFront(Camera.getOrientation()); var forwardVector = Quat.getFront(Camera.getOrientation());
var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_FROM_CAMERA)); var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_FROM_CAMERA));
var velocity = Vec3.multiply(forwardVector, BULLET_VELOCITY); var velocity = Vec3.multiply(forwardVector, BULLET_VELOCITY);
shootBullet(newPosition, velocity); shootBullet(newPosition, velocity, grenade);
} }
function mouseReleaseEvent(event) { function mouseReleaseEvent(event) {
@ -392,21 +480,23 @@ function mouseReleaseEvent(event) {
isMouseDown = false; isMouseDown = false;
} }
function mouseMoveEvent(event) { function mousePressEvent(event) {
if (isMouseDown) { var clickedText = false;
var MOUSE_YAW_SCALE = -0.25; var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
var MOUSE_PITCH_SCALE = -12.5; if (clickedOverlay == offButton) {
var FIXED_MOUSE_TIMESTEP = 0.016; Script.stop();
yawFromMouse += ((event.x - lastX) * MOUSE_YAW_SCALE * FIXED_MOUSE_TIMESTEP); } else if (clickedOverlay == platformButton) {
pitchFromMouse += ((event.y - lastY) * MOUSE_PITCH_SCALE * FIXED_MOUSE_TIMESTEP); makePlatform(-9.8, 1.0, 5);
lastX = event.x; } else if (clickedOverlay == gridButton) {
lastY = event.y; makeGrid("Box", 1.0, 3);
} }
} }
function scriptEnding() { function scriptEnding() {
Overlays.deleteOverlay(reticle); Overlays.deleteOverlay(reticle);
Overlays.deleteOverlay(offButton); Overlays.deleteOverlay(offButton);
Overlays.deleteOverlay(platformButton);
Overlays.deleteOverlay(gridButton);
Overlays.deleteOverlay(pointer[0]); Overlays.deleteOverlay(pointer[0]);
Overlays.deleteOverlay(pointer[1]); Overlays.deleteOverlay(pointer[1]);
Overlays.deleteOverlay(text); Overlays.deleteOverlay(text);
@ -418,7 +508,7 @@ Entities.entityCollisionWithEntity.connect(entityCollisionWithEntity);
Script.scriptEnding.connect(scriptEnding); Script.scriptEnding.connect(scriptEnding);
Script.update.connect(update); Script.update.connect(update);
Controller.mouseReleaseEvent.connect(mouseReleaseEvent); Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
Controller.mouseMoveEvent.connect(mouseMoveEvent); Controller.mousePressEvent.connect(mousePressEvent);
Controller.keyPressEvent.connect(keyPressEvent); Controller.keyPressEvent.connect(keyPressEvent);

View file

@ -0,0 +1,172 @@
// PaddleBall.js
//
// Created by Philip Rosedale on January 21, 2015
// Copyright 2014 High Fidelity, Inc.
//
// Move your hand with the hydra controller, and hit the ball with the paddle.
// Click 'X' button to turn off this script.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var BALL_SIZE = 0.08;
var PADDLE_SIZE = 0.20;
var PADDLE_THICKNESS = 0.06;
var PADDLE_COLOR = { red: 184, green: 134, blue: 11 };
var BALL_COLOR = { red: 255, green: 0, blue: 0 };
var LINE_COLOR = { red: 255, green: 255, blue: 0 };
var PADDLE_OFFSET = { x: 0.05, y: 0.0, z: 0.0 };
var GRAVITY = 0.0;
var SPRING_FORCE = 15.0;
var lastSoundTime = 0;
var gameOn = false;
var leftHanded = false;
var controllerID;
if (leftHanded) {
controllerID = 1;
} else {
controllerID = 3;
}
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
hitSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Collisions-ballhitsandcatches/billiards/collision1.wav");
var screenSize = Controller.getViewportDimensions();
var offButton = Overlays.addOverlay("image", {
x: screenSize.x - 48,
y: 96,
width: 32,
height: 32,
imageURL: HIFI_PUBLIC_BUCKET + "images/close.png",
color: { red: 255, green: 255, blue: 255},
alpha: 1
});
var ball, paddle, paddleModel, line;
function createEntities() {
ball = Entities.addEntity(
{ type: "Sphere",
position: Controller.getSpatialControlPosition(controllerID),
dimensions: { x: BALL_SIZE, y: BALL_SIZE, z: BALL_SIZE },
color: BALL_COLOR,
gravity: { x: 0, y: GRAVITY, z: 0 },
ignoreCollisions: false,
damping: 0.50,
collisionsWillMove: true });
paddle = Entities.addEntity(
{ type: "Box",
position: Controller.getSpatialControlPosition(controllerID),
dimensions: { x: PADDLE_SIZE, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 0.80 },
color: PADDLE_COLOR,
gravity: { x: 0, y: 0, z: 0 },
ignoreCollisions: false,
damping: 0.10,
visible: false,
rotation: Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)),
collisionsWillMove: false });
modelURL = "http://public.highfidelity.io/models/attachments/pong_paddle.fbx";
paddleModel = Entities.addEntity(
{ type: "Model",
position: Vec3.sum(Controller.getSpatialControlPosition(controllerID), PADDLE_OFFSET),
dimensions: { x: PADDLE_SIZE * 1.5, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 1.25 },
color: PADDLE_COLOR,
gravity: { x: 0, y: 0, z: 0 },
ignoreCollisions: true,
modelURL: modelURL,
damping: 0.10,
rotation: Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)),
collisionsWillMove: false });
line = Overlays.addOverlay("line3d", {
start: { x: 0, y: 0, z: 0 },
end: { x: 0, y: 0, z: 0 },
color: LINE_COLOR,
alpha: 1,
visible: true,
lineWidth: 2 });
}
function deleteEntities() {
Entities.deleteEntity(ball);
Entities.deleteEntity(paddle);
Entities.deleteEntity(paddleModel);
Overlays.deleteOverlay(line);
}
function update(deltaTime) {
var palmPosition = Controller.getSpatialControlPosition(controllerID);
var controllerActive = (Vec3.length(palmPosition) > 0);
if (!gameOn && controllerActive) {
createEntities();
gameOn = true;
} else if (gameOn && !controllerActive) {
deleteEntities();
gameOn = false;
}
if (!gameOn || !controllerActive) {
return;
}
if (!paddle.isKnownID) {
paddle = Entities.identifyEntity(paddle);
}
if (!ball.isKnownID) {
ball = Entities.identifyEntity(ball);
} else {
var props = Entities.getEntityProperties(ball);
var spring = Vec3.subtract(palmPosition, props.position);
var paddleWorldOrientation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID));
var springLength = Vec3.length(spring);
spring = Vec3.normalize(spring);
var ballVelocity = Vec3.sum(props.velocity, Vec3.multiply(springLength * SPRING_FORCE * deltaTime, spring));
Entities.editEntity(ball, { velocity: ballVelocity });
Overlays.editOverlay(line, { start: props.position, end: palmPosition });
Entities.editEntity(paddle, { position: palmPosition,
velocity: Controller.getSpatialControlVelocity(controllerID),
rotation: paddleWorldOrientation });
Entities.editEntity(paddleModel, { position: Vec3.sum(palmPosition, Vec3.multiplyQbyV(paddleWorldOrientation, PADDLE_OFFSET)),
velocity: Controller.getSpatialControlVelocity(controllerID),
rotation: paddleWorldOrientation });
}
}
function entityCollisionWithEntity(entity1, entity2, collision) {
if ((entity1.id == ball.id) || (entity2.id ==ball.id)) {
var props1 = Entities.getEntityProperties(entity1);
var props2 = Entities.getEntityProperties(entity2);
var dVel = Vec3.length(Vec3.subtract(props1.velocity, props2.velocity));
var currentTime = new Date().getTime();
var MIN_MSECS_BETWEEN_BOUNCE_SOUNDS = 100;
var MIN_VELOCITY_FOR_SOUND_IMPACT = 0.25;
if ((dVel > MIN_VELOCITY_FOR_SOUND_IMPACT) && (currentTime - lastSoundTime) > MIN_MSECS_BETWEEN_BOUNCE_SOUNDS) {
Audio.playSound(hitSound, { position: props1.position, volume: Math.min(dVel, 1.0) });
lastSoundTime = new Date().getTime();
}
}
}
function mousePressEvent(event) {
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
if (clickedOverlay == offButton) {
Script.stop();
}
}
function scriptEnding() {
if (gameOn) {
deleteEntities();
}
Overlays.deleteOverlay(offButton);
}
Entities.entityCollisionWithEntity.connect(entityCollisionWithEntity);
Controller.mousePressEvent.connect(mousePressEvent);
Script.scriptEnding.connect(scriptEnding);
Script.update.connect(update);

View file

@ -4,6 +4,6 @@ set(TARGET_NAME ice-server)
setup_hifi_project(Network) setup_hifi_project(Network)
# link the shared hifi libraries # link the shared hifi libraries
link_hifi_libraries(networking shared) link_hifi_libraries(embedded-webserver networking shared)
include_dependency_includes() include_dependency_includes()

View file

@ -20,14 +20,18 @@
const int CLEAR_INACTIVE_PEERS_INTERVAL_MSECS = 1 * 1000; const int CLEAR_INACTIVE_PEERS_INTERVAL_MSECS = 1 * 1000;
const int PEER_SILENCE_THRESHOLD_MSECS = 5 * 1000; const int PEER_SILENCE_THRESHOLD_MSECS = 5 * 1000;
const quint16 ICE_SERVER_MONITORING_PORT = 40110;
IceServer::IceServer(int argc, char* argv[]) : IceServer::IceServer(int argc, char* argv[]) :
QCoreApplication(argc, argv), QCoreApplication(argc, argv),
_id(QUuid::createUuid()), _id(QUuid::createUuid()),
_serverSocket(), _serverSocket(),
_activePeers() _activePeers(),
_httpManager(ICE_SERVER_MONITORING_PORT, QString("%1/web/").arg(QCoreApplication::applicationDirPath()), this)
{ {
// start the ice-server socket // start the ice-server socket
qDebug() << "ice-server socket is listening on" << ICE_SERVER_DEFAULT_PORT; qDebug() << "ice-server socket is listening on" << ICE_SERVER_DEFAULT_PORT;
qDebug() << "monitoring http endpoint is listening on " << ICE_SERVER_MONITORING_PORT;
_serverSocket.bind(QHostAddress::AnyIPv4, ICE_SERVER_DEFAULT_PORT); _serverSocket.bind(QHostAddress::AnyIPv4, ICE_SERVER_DEFAULT_PORT);
// call our process datagrams slot when the UDP socket has packets ready // call our process datagrams slot when the UDP socket has packets ready
@ -165,3 +169,28 @@ void IceServer::clearInactivePeers() {
} }
} }
} }
bool IceServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler) {
//
// We need an HTTP handler in order to monitor the health of the ice server
// The correct functioning of the ICE server will first be determined by its HTTP availability,
// and then by the existence of a minimum number of peers in the list, matching the minimum number of
// domains in production by High Fidelity.
//
int MINIMUM_PEERS = 3;
bool IS_HEALTHY = false;
IS_HEALTHY = _activePeers.size() >= MINIMUM_PEERS ? true : false;
if (connection->requestOperation() == QNetworkAccessManager::GetOperation) {
if (url.path() == "/status") {
if (IS_HEALTHY) {
connection->respond(HTTPConnection::StatusCode200, QByteArray::number(_activePeers.size()));
} else {
connection->respond(HTTPConnection::StatusCode404, QByteArray::number(_activePeers.size()));
}
}
}
return true;
}

View file

@ -12,17 +12,21 @@
#ifndef hifi_IceServer_h #ifndef hifi_IceServer_h
#define hifi_IceServer_h #define hifi_IceServer_h
#include <qcoreapplication.h> #include <QtCore/QCoreApplication>
#include <qsharedpointer.h> #include <QtCore/QSharedPointer>
#include <qudpsocket.h> #include <QUdpSocket>
#include <NetworkPeer.h> #include <NetworkPeer.h>
#include <HTTPConnection.h>
#include <HTTPManager.h>
typedef QHash<QUuid, SharedNetworkPeer> NetworkPeerHash; typedef QHash<QUuid, SharedNetworkPeer> NetworkPeerHash;
class IceServer : public QCoreApplication { class IceServer : public QCoreApplication, public HTTPRequestHandler {
Q_OBJECT
public: public:
IceServer(int argc, char* argv[]); IceServer(int argc, char* argv[]);
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false);
private slots: private slots:
void processDatagrams(); void processDatagrams();
void clearInactivePeers(); void clearInactivePeers();
@ -34,6 +38,7 @@ private:
QUdpSocket _serverSocket; QUdpSocket _serverSocket;
NetworkPeerHash _activePeers; NetworkPeerHash _activePeers;
QHash<QUuid, QSet<QUuid> > _currentConnections; QHash<QUuid, QSet<QUuid> > _currentConnections;
HTTPManager _httpManager;
}; };
#endif // hifi_IceServer_h #endif // hifi_IceServer_h

View file

@ -1427,6 +1427,8 @@ public:
void setColorMaterial(const StackArray::Entry& entry) { color = entry.color; material = entry.material; } void setColorMaterial(const StackArray::Entry& entry) { color = entry.color; material = entry.material; }
void mix(const EdgeCrossing& first, const EdgeCrossing& second, float t); void mix(const EdgeCrossing& first, const EdgeCrossing& second, float t);
VoxelPoint createPoint(int clampedX, int clampedZ, float step) const;
}; };
void EdgeCrossing::mix(const EdgeCrossing& first, const EdgeCrossing& second, float t) { void EdgeCrossing::mix(const EdgeCrossing& first, const EdgeCrossing& second, float t) {
@ -1437,6 +1439,16 @@ void EdgeCrossing::mix(const EdgeCrossing& first, const EdgeCrossing& second, fl
material = (t < 0.5f) ? first.material : second.material; material = (t < 0.5f) ? first.material : second.material;
} }
VoxelPoint EdgeCrossing::createPoint(int clampedX, int clampedZ, float step) const {
VoxelPoint voxelPoint = { glm::vec3(clampedX + point.x, point.y, clampedZ + point.z) * step,
{ (quint8)qRed(color), (quint8)qGreen(color), (quint8)qBlue(color) },
{ (char)(normal.x * numeric_limits<qint8>::max()), (char)(normal.y * numeric_limits<qint8>::max()),
(char)(normal.z * numeric_limits<qint8>::max()) },
{ (quint8)material, 0, 0, 0 },
{ numeric_limits<quint8>::max(), 0, 0, 0 } };
return voxelPoint;
}
const int MAX_NORMALS_PER_VERTEX = 4; const int MAX_NORMALS_PER_VERTEX = 4;
class NormalIndex { class NormalIndex {
@ -1498,6 +1510,84 @@ const NormalIndex& IndexVector::get(int y) const {
return (relative >= 0 && relative < size()) ? at(relative) : invalidIndex; return (relative >= 0 && relative < size()) ? at(relative) : invalidIndex;
} }
static inline glm::vec3 getNormal(const QVector<VoxelPoint>& vertices, const NormalIndex& i0,
const NormalIndex& i1, const NormalIndex& i2, const NormalIndex& i3) {
// check both triangles in case one is degenerate
const glm::vec3& v0 = vertices.at(i0.indices[0]).vertex;
glm::vec3 normal = glm::cross(vertices.at(i1.indices[0]).vertex - v0, vertices.at(i2.indices[0]).vertex - v0);
if (glm::length(normal) > EPSILON) {
return normal;
}
return glm::cross(vertices.at(i2.indices[0]).vertex - v0, vertices.at(i3.indices[0]).vertex - v0);
}
static inline void appendTriangle(const EdgeCrossing& e0, const EdgeCrossing& e1, const EdgeCrossing& e2,
int clampedX, int clampedZ, float step, QVector<VoxelPoint>& vertices, QVector<int>& indices,
QMultiHash<VoxelCoord, int>& quadIndices) {
int firstIndex = vertices.size();
vertices.append(e0.createPoint(clampedX, clampedZ, step));
vertices.append(e1.createPoint(clampedX, clampedZ, step));
vertices.append(e2.createPoint(clampedX, clampedZ, step));
indices.append(firstIndex);
indices.append(firstIndex + 1);
indices.append(firstIndex + 2);
indices.append(firstIndex + 2);
int minimumY = qMin((int)e0.point.y, qMin((int)e1.point.y, (int)e2.point.y));
int maximumY = qMax((int)e0.point.y, qMax((int)e1.point.y, (int)e2.point.y));
for (int y = minimumY; y <= maximumY; y++) {
quadIndices.insert(qRgb(clampedX, y, clampedZ), firstIndex);
}
}
const int CORNER_COUNT = 4;
static StackArray::Entry getEntry(const StackArray* lineSrc, int stackWidth, int y, float heightfieldHeight,
EdgeCrossing cornerCrossings[CORNER_COUNT], int cornerIndex) {
int offsetX = (cornerIndex & X_MAXIMUM_FLAG) ? 1 : 0;
int offsetZ = (cornerIndex & Y_MAXIMUM_FLAG) ? 1 : 0;
const StackArray& src = lineSrc[offsetZ * stackWidth + offsetX];
int count = src.getEntryCount();
if (count > 0) {
int relative = y - src.getPosition();
if (relative < count && (relative >= 0 || heightfieldHeight == 0.0f)) {
return src.getEntry(y, heightfieldHeight);
}
}
const EdgeCrossing& cornerCrossing = cornerCrossings[cornerIndex];
if (cornerCrossing.point.y == 0.0f) {
return src.getEntry(y, heightfieldHeight);
}
StackArray::Entry entry;
bool set = false;
if (cornerCrossing.point.y >= y) {
entry.color = cornerCrossing.color;
entry.material = cornerCrossing.material;
set = true;
entry.setHermiteY(cornerCrossing.normal, glm::clamp(cornerCrossing.point.y - y, 0.0f, 1.0f));
} else {
entry.material = entry.color = 0;
}
if (!(cornerIndex & X_MAXIMUM_FLAG)) {
const EdgeCrossing& nextCornerCrossingX = cornerCrossings[cornerIndex | X_MAXIMUM_FLAG];
if (nextCornerCrossingX.point.y != 0.0f && (nextCornerCrossingX.point.y >= y) != set) {
float t = glm::clamp((y - cornerCrossing.point.y) /
(nextCornerCrossingX.point.y - cornerCrossing.point.y), 0.0f, 1.0f);
entry.setHermiteX(glm::normalize(glm::mix(cornerCrossing.normal, nextCornerCrossingX.normal, t)), t);
}
}
if (!(cornerIndex & Y_MAXIMUM_FLAG)) {
const EdgeCrossing& nextCornerCrossingZ = cornerCrossings[cornerIndex | Y_MAXIMUM_FLAG];
if (nextCornerCrossingZ.point.y != 0.0f && (nextCornerCrossingZ.point.y >= y) != set) {
float t = glm::clamp((y - cornerCrossing.point.y) /
(nextCornerCrossingZ.point.y - cornerCrossing.point.y), 0.0f, 1.0f);
entry.setHermiteZ(glm::normalize(glm::mix(cornerCrossing.normal, nextCornerCrossingZ.normal, t)), t);
}
}
return entry;
}
void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const glm::vec3& translation, void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const glm::vec3& translation,
const glm::quat& rotation, const glm::vec3& scale, bool cursor) { const glm::quat& rotation, const glm::vec3& scale, bool cursor) {
if (!node->getHeight()) { if (!node->getHeight()) {
@ -1706,7 +1796,6 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
const int LOWER_RIGHT_CORNER = 8; const int LOWER_RIGHT_CORNER = 8;
const int NO_CORNERS = 0; const int NO_CORNERS = 0;
const int ALL_CORNERS = UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_LEFT_CORNER | LOWER_RIGHT_CORNER; const int ALL_CORNERS = UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_LEFT_CORNER | LOWER_RIGHT_CORNER;
const int CORNER_COUNT = 4;
const int NEXT_CORNERS[] = { 1, 3, 0, 2 }; const int NEXT_CORNERS[] = { 1, 3, 0, 2 };
int corners = NO_CORNERS; int corners = NO_CORNERS;
if (heightfieldHeight != 0.0f) { if (heightfieldHeight != 0.0f) {
@ -1772,6 +1861,15 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
} }
minimumY = qMin(minimumY, cornerMinimumY); minimumY = qMin(minimumY, cornerMinimumY);
maximumY = qMax(maximumY, cornerMaximumY); maximumY = qMax(maximumY, cornerMaximumY);
if (corners == (LOWER_LEFT_CORNER | UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER)) {
appendTriangle(cornerCrossings[1], cornerCrossings[0], cornerCrossings[2],
clampedX, clampedZ, step, vertices, indices, quadIndices);
} else if (corners == (UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER)) {
appendTriangle(cornerCrossings[2], cornerCrossings[3], cornerCrossings[1],
clampedX, clampedZ, step, vertices, indices, quadIndices);
}
} }
int position = minimumY; int position = minimumY;
int count = maximumY - minimumY + 1; int count = maximumY - minimumY + 1;
@ -1781,7 +1879,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
indicesZ[x].position = position; indicesZ[x].position = position;
indicesZ[x].resize(count); indicesZ[x].resize(count);
for (int y = position, end = position + count; y < end; y++) { for (int y = position, end = position + count; y < end; y++) {
const StackArray::Entry& entry = lineSrc->getEntry(y, heightfieldHeight); StackArray::Entry entry = getEntry(lineSrc, stackWidth, y, heightfieldHeight, cornerCrossings, 0);
if (displayHermite && x != 0 && z != 0 && !lineSrc->isEmpty() && y >= lineSrc->getPosition()) { if (displayHermite && x != 0 && z != 0 && !lineSrc->isEmpty() && y >= lineSrc->getPosition()) {
glm::vec3 normal; glm::vec3 normal;
if (entry.hermiteX != 0) { if (entry.hermiteX != 0) {
@ -1846,41 +1944,38 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
if (!(corners & (1 << i))) { if (!(corners & (1 << i))) {
continue; continue;
} }
int offsetX = (i & X_MAXIMUM_FLAG) ? 1 : 0; const EdgeCrossing& cornerCrossing = cornerCrossings[i];
int offsetZ = (i & Y_MAXIMUM_FLAG) ? 1 : 0; if (cornerCrossing.point.y >= y && cornerCrossing.point.y < y + 1) {
const quint16* height = heightLineSrc + offsetZ * width + offsetX;
float heightValue = *height * voxelScale;
if (heightValue >= y && heightValue < y + 1) {
crossedCorners |= (1 << i); crossedCorners |= (1 << i);
} }
} }
switch (crossedCorners) { switch (crossedCorners) {
case UPPER_LEFT_CORNER: case UPPER_LEFT_CORNER:
case LOWER_LEFT_CORNER | UPPER_LEFT_CORNER: case LOWER_LEFT_CORNER | UPPER_LEFT_CORNER:
case LOWER_LEFT_CORNER | UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER: case LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER | UPPER_LEFT_CORNER:
case UPPER_LEFT_CORNER | LOWER_RIGHT_CORNER: case UPPER_LEFT_CORNER | LOWER_RIGHT_CORNER:
crossings[crossingCount++] = cornerCrossings[0]; crossings[crossingCount++] = cornerCrossings[0];
crossings[crossingCount - 1].point.y -= y; crossings[crossingCount - 1].point.y -= y;
break; break;
case UPPER_RIGHT_CORNER: case UPPER_RIGHT_CORNER:
case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER: case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER:
case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER:
case UPPER_RIGHT_CORNER | LOWER_LEFT_CORNER: case UPPER_RIGHT_CORNER | LOWER_LEFT_CORNER:
case LOWER_LEFT_CORNER | UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER:
crossings[crossingCount++] = cornerCrossings[1]; crossings[crossingCount++] = cornerCrossings[1];
crossings[crossingCount - 1].point.y -= y; crossings[crossingCount - 1].point.y -= y;
break; break;
case LOWER_LEFT_CORNER: case LOWER_LEFT_CORNER:
case LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER: case LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER:
case LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER | UPPER_LEFT_CORNER: case UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER:
crossings[crossingCount++] = cornerCrossings[2]; crossings[crossingCount++] = cornerCrossings[2];
crossings[crossingCount - 1].point.y -= y; crossings[crossingCount - 1].point.y -= y;
break; break;
case LOWER_RIGHT_CORNER: case LOWER_RIGHT_CORNER:
case UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER: case UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER:
case UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER: case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER:
crossings[crossingCount++] = cornerCrossings[3]; crossings[crossingCount++] = cornerCrossings[3];
crossings[crossingCount - 1].point.y -= y; crossings[crossingCount - 1].point.y -= y;
break; break;
@ -1890,30 +1985,24 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
if (!(corners & (1 << i))) { if (!(corners & (1 << i))) {
continue; continue;
} }
int offsetX = (i & X_MAXIMUM_FLAG) ? 1 : 0;
int offsetZ = (i & Y_MAXIMUM_FLAG) ? 1 : 0;
const quint16* height = heightLineSrc + offsetZ * width + offsetX;
float heightValue = *height * voxelScale;
int nextIndex = NEXT_CORNERS[i]; int nextIndex = NEXT_CORNERS[i];
if (!(corners & (1 << nextIndex))) { if (!(corners & (1 << nextIndex))) {
continue; continue;
} }
int nextOffsetX = (nextIndex & X_MAXIMUM_FLAG) ? 1 : 0; const EdgeCrossing& cornerCrossing = cornerCrossings[i];
int nextOffsetZ = (nextIndex & Y_MAXIMUM_FLAG) ? 1 : 0; const EdgeCrossing& nextCornerCrossing = cornerCrossings[nextIndex];
const quint16* nextHeight = heightLineSrc + nextOffsetZ * width + nextOffsetX; float divisor = (nextCornerCrossing.point.y - cornerCrossing.point.y);
float nextHeightValue = *nextHeight * voxelScale;
float divisor = (nextHeightValue - heightValue);
if (divisor == 0.0f) { if (divisor == 0.0f) {
continue; continue;
} }
float t1 = (y - heightValue) / divisor; float t1 = (y - cornerCrossing.point.y) / divisor;
float t2 = (y + 1 - heightValue) / divisor; float t2 = (y + 1 - cornerCrossing.point.y) / divisor;
if (t1 >= 0.0f && t1 <= 1.0f) { if (t1 >= 0.0f && t1 <= 1.0f) {
crossings[crossingCount++].mix(cornerCrossings[i], cornerCrossings[nextIndex], t1); crossings[crossingCount++].mix(cornerCrossing, nextCornerCrossing, t1);
crossings[crossingCount - 1].point.y -= y; crossings[crossingCount - 1].point.y -= y;
} }
if (t2 >= 0.0f && t2 <= 1.0f) { if (t2 >= 0.0f && t2 <= 1.0f) {
crossings[crossingCount++].mix(cornerCrossings[i], cornerCrossings[nextIndex], t2); crossings[crossingCount++].mix(cornerCrossing, nextCornerCrossing, t2);
crossings[crossingCount - 1].point.y -= y; crossings[crossingCount - 1].point.y -= y;
} }
} }
@ -1924,10 +2013,13 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
// the terrifying conditional code that follows checks each cube edge for a crossing, gathering // the terrifying conditional code that follows checks each cube edge for a crossing, gathering
// its properties (color, material, normal) if one is present; as before, boundary edges are excluded // its properties (color, material, normal) if one is present; as before, boundary edges are excluded
if (crossingCount == 0) { if (crossingCount == 0) {
const StackArray::Entry& nextEntryY = lineSrc->getEntry(y + 1, heightfieldHeight); StackArray::Entry nextEntryY = getEntry(lineSrc, stackWidth, y + 1,
heightfieldHeight, cornerCrossings, 0);
if (middleX) { if (middleX) {
const StackArray::Entry& nextEntryX = lineSrc[1].getEntry(y, nextHeightfieldHeightX); StackArray::Entry nextEntryX = getEntry(lineSrc, stackWidth, y, nextHeightfieldHeightX,
const StackArray::Entry& nextEntryXY = lineSrc[1].getEntry(y + 1, nextHeightfieldHeightX); cornerCrossings, 1);
StackArray::Entry nextEntryXY = getEntry(lineSrc, stackWidth, y + 1, nextHeightfieldHeightX,
cornerCrossings, 1);
if (alpha0 != alpha1) { if (alpha0 != alpha1) {
EdgeCrossing& crossing = crossings[crossingCount++]; EdgeCrossing& crossing = crossings[crossingCount++];
crossing.point = glm::vec3(entry.getHermiteX(crossing.normal), 0.0f, 0.0f); crossing.point = glm::vec3(entry.getHermiteX(crossing.normal), 0.0f, 0.0f);
@ -1944,12 +2036,12 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
crossing.setColorMaterial(alpha2 == 0 ? nextEntryXY : nextEntryY); crossing.setColorMaterial(alpha2 == 0 ? nextEntryXY : nextEntryY);
} }
if (middleZ) { if (middleZ) {
const StackArray::Entry& nextEntryZ = lineSrc[stackWidth].getEntry(y, StackArray::Entry nextEntryZ = getEntry(lineSrc, stackWidth, y, nextHeightfieldHeightZ,
nextHeightfieldHeightZ); cornerCrossings, 2);
const StackArray::Entry& nextEntryXZ = lineSrc[stackWidth + 1].getEntry( StackArray::Entry nextEntryXZ = getEntry(lineSrc, stackWidth, y, nextHeightfieldHeightXZ,
y, nextHeightfieldHeightXZ); cornerCrossings, 3);
const StackArray::Entry& nextEntryXYZ = lineSrc[stackWidth + 1].getEntry( StackArray::Entry nextEntryXYZ = getEntry(lineSrc, stackWidth, y + 1,
y + 1, nextHeightfieldHeightXZ); nextHeightfieldHeightXZ, cornerCrossings, 3);
if (alpha1 != alpha5) { if (alpha1 != alpha5) {
EdgeCrossing& crossing = crossings[crossingCount++]; EdgeCrossing& crossing = crossings[crossingCount++];
crossing.point = glm::vec3(1.0f, 0.0f, nextEntryX.getHermiteZ(crossing.normal)); crossing.point = glm::vec3(1.0f, 0.0f, nextEntryX.getHermiteZ(crossing.normal));
@ -1957,8 +2049,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
} }
if (alpha3 != alpha7) { if (alpha3 != alpha7) {
EdgeCrossing& crossing = crossings[crossingCount++]; EdgeCrossing& crossing = crossings[crossingCount++];
const StackArray::Entry& nextEntryXY = lineSrc[1].getEntry(y + 1, StackArray::Entry nextEntryXY = getEntry(lineSrc, stackWidth, y + 1,
nextHeightfieldHeightX); nextHeightfieldHeightX, cornerCrossings, 1);
crossing.point = glm::vec3(1.0f, 1.0f, nextEntryXY.getHermiteZ(crossing.normal)); crossing.point = glm::vec3(1.0f, 1.0f, nextEntryXY.getHermiteZ(crossing.normal));
crossing.setColorMaterial(alpha3 == 0 ? nextEntryXYZ : nextEntryXY); crossing.setColorMaterial(alpha3 == 0 ? nextEntryXYZ : nextEntryXY);
} }
@ -1969,15 +2061,15 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
} }
if (alpha5 != alpha7) { if (alpha5 != alpha7) {
EdgeCrossing& crossing = crossings[crossingCount++]; EdgeCrossing& crossing = crossings[crossingCount++];
const StackArray::Entry& nextEntryXZ = lineSrc[stackWidth + 1].getEntry( StackArray::Entry nextEntryXZ = getEntry(lineSrc, stackWidth, y,
y, nextHeightfieldHeightXZ); nextHeightfieldHeightXZ, cornerCrossings, 3);
crossing.point = glm::vec3(1.0f, nextEntryXZ.getHermiteY(crossing.normal), 1.0f); crossing.point = glm::vec3(1.0f, nextEntryXZ.getHermiteY(crossing.normal), 1.0f);
crossing.setColorMaterial(alpha5 == 0 ? nextEntryXYZ : nextEntryXZ); crossing.setColorMaterial(alpha5 == 0 ? nextEntryXYZ : nextEntryXZ);
} }
if (alpha6 != alpha7) { if (alpha6 != alpha7) {
EdgeCrossing& crossing = crossings[crossingCount++]; EdgeCrossing& crossing = crossings[crossingCount++];
const StackArray::Entry& nextEntryYZ = lineSrc[stackWidth].getEntry( StackArray::Entry nextEntryYZ = getEntry(lineSrc, stackWidth, y + 1,
y + 1, nextHeightfieldHeightZ); nextHeightfieldHeightZ, cornerCrossings, 2);
crossing.point = glm::vec3(nextEntryYZ.getHermiteX(crossing.normal), 1.0f, 1.0f); crossing.point = glm::vec3(nextEntryYZ.getHermiteX(crossing.normal), 1.0f, 1.0f);
crossing.setColorMaterial(alpha6 == 0 ? nextEntryXYZ : nextEntryYZ); crossing.setColorMaterial(alpha6 == 0 ? nextEntryXYZ : nextEntryYZ);
} }
@ -1989,9 +2081,10 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
crossing.setColorMaterial(alpha0 == 0 ? nextEntryY : entry); crossing.setColorMaterial(alpha0 == 0 ? nextEntryY : entry);
} }
if (middleZ) { if (middleZ) {
const StackArray::Entry& nextEntryZ = lineSrc[stackWidth].getEntry(y, nextHeightfieldHeightZ); StackArray::Entry nextEntryZ = getEntry(lineSrc, stackWidth, y,
const StackArray::Entry& nextEntryYZ = lineSrc[stackWidth].getEntry(y + 1, nextHeightfieldHeightZ, cornerCrossings, 2);
nextHeightfieldHeightZ); StackArray::Entry nextEntryYZ = getEntry(lineSrc, stackWidth, y + 1,
nextHeightfieldHeightZ, cornerCrossings, 2);
if (alpha0 != alpha4) { if (alpha0 != alpha4) {
EdgeCrossing& crossing = crossings[crossingCount++]; EdgeCrossing& crossing = crossings[crossingCount++];
crossing.point = glm::vec3(0.0f, 0.0f, entry.getHermiteZ(crossing.normal)); crossing.point = glm::vec3(0.0f, 0.0f, entry.getHermiteZ(crossing.normal));
@ -2174,10 +2267,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ - 1), indices.size()); quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ - 1), indices.size());
quadIndices.insert(qRgb(reclampedX, y, reclampedZ - 1), indices.size()); quadIndices.insert(qRgb(reclampedX, y, reclampedZ - 1), indices.size());
} }
const glm::vec3& first = vertices.at(index.indices[0]).vertex; glm::vec3 normal = getNormal(vertices, index, index1, index2, index3);
glm::vec3 normal = glm::cross(vertices.at(index1.indices[0]).vertex - first,
vertices.at(index3.indices[0]).vertex - first);
if (alpha0 == 0) { // quad faces negative x if (alpha0 == 0) { // quad faces negative x
indices.append(index3.getClosestIndex(normal = -normal, vertices)); indices.append(index3.getClosestIndex(normal = -normal, vertices));
indices.append(index2.getClosestIndex(normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices));
@ -2206,10 +2296,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
if (reclampedZ > 0) { if (reclampedZ > 0) {
quadIndices.insert(qRgb(reclampedX, y, reclampedZ - 1), indices.size()); quadIndices.insert(qRgb(reclampedX, y, reclampedZ - 1), indices.size());
} }
const glm::vec3& first = vertices.at(index.indices[0]).vertex; glm::vec3 normal = getNormal(vertices, index, index3, index2, index1);
glm::vec3 normal = glm::cross(vertices.at(index3.indices[0]).vertex - first,
vertices.at(index1.indices[0]).vertex - first);
if (alpha0 == 0) { // quad faces negative y if (alpha0 == 0) { // quad faces negative y
indices.append(index3.getClosestIndex(normal, vertices)); indices.append(index3.getClosestIndex(normal, vertices));
indices.append(index2.getClosestIndex(normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices));
@ -2235,10 +2322,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
} }
quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ), indices.size()); quadIndices.insert(qRgb(reclampedX, y - 1, reclampedZ), indices.size());
const glm::vec3& first = vertices.at(index.indices[0]).vertex; glm::vec3 normal = getNormal(vertices, index, index1, index2, index3);
glm::vec3 normal = glm::cross(vertices.at(index1.indices[0]).vertex - first,
vertices.at(index3.indices[0]).vertex - first);
if (alpha0 == 0) { // quad faces negative z if (alpha0 == 0) { // quad faces negative z
indices.append(index1.getClosestIndex(normal, vertices)); indices.append(index1.getClosestIndex(normal, vertices));
indices.append(index2.getClosestIndex(normal, vertices)); indices.append(index2.getClosestIndex(normal, vertices));

View file

@ -22,88 +22,62 @@
#include "ImageOverlay.h" #include "ImageOverlay.h"
ImageOverlay::ImageOverlay() : ImageOverlay::ImageOverlay() :
_textureID(0), _imageURL(),
_renderImage(false), _renderImage(false),
_textureBound(false),
_wantClipFromImage(false) _wantClipFromImage(false)
{ {
_isLoaded = false;
} }
ImageOverlay::ImageOverlay(const ImageOverlay* imageOverlay) : ImageOverlay::ImageOverlay(const ImageOverlay* imageOverlay) :
Overlay2D(imageOverlay), Overlay2D(imageOverlay),
_texture(imageOverlay->_texture),
_imageURL(imageOverlay->_imageURL), _imageURL(imageOverlay->_imageURL),
_textureImage(imageOverlay->_textureImage), _textureImage(imageOverlay->_textureImage),
_textureID(0), _fromImage(imageOverlay->_fromImage),
_fromImage(),
_renderImage(imageOverlay->_renderImage), _renderImage(imageOverlay->_renderImage),
_textureBound(false), _wantClipFromImage(imageOverlay->_wantClipFromImage)
_wantClipFromImage(false)
{ {
} }
ImageOverlay::~ImageOverlay() { ImageOverlay::~ImageOverlay() {
if (_parent && _textureID) {
// do we need to call this?
//_parent->deleteTexture(_textureID);
}
} }
// TODO: handle setting image multiple times, how do we manage releasing the bound texture? // TODO: handle setting image multiple times, how do we manage releasing the bound texture?
void ImageOverlay::setImageURL(const QUrl& url) { void ImageOverlay::setImageURL(const QUrl& url) {
_imageURL = url; _imageURL = url;
_isLoaded = false;
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkReply* reply = networkAccessManager.get(QNetworkRequest(url));
connect(reply, &QNetworkReply::finished, this, &ImageOverlay::replyFinished);
}
void ImageOverlay::replyFinished() { if (url.isEmpty()) {
QNetworkReply* reply = static_cast<QNetworkReply*>(sender()); _isLoaded = true;
_renderImage = false;
// replace our byte array with the downloaded data _texture.clear();
QByteArray rawData = reply->readAll(); } else {
_textureImage.loadFromData(rawData); _isLoaded = false;
_renderImage = true; _renderImage = true;
_isLoaded = true; }
reply->deleteLater();
} }
void ImageOverlay::render(RenderArgs* args) { void ImageOverlay::render(RenderArgs* args) {
if (!_visible || !_isLoaded) { if (!_isLoaded && _renderImage) {
return; // do nothing if we're not visible _isLoaded = true;
_texture = DependencyManager::get<TextureCache>()->getTexture(_imageURL);
} }
if (_renderImage && !_textureBound) {
_textureID = _parent->bindTexture(_textureImage); // If we are not visible or loaded, return. If we are trying to render an
_textureBound = true; // image but the texture hasn't loaded, return.
if (!_visible || !_isLoaded || (_renderImage && !_texture->isLoaded())) {
return;
} }
if (_renderImage) { if (_renderImage) {
glEnable(GL_TEXTURE_2D); glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, _textureID); glBindTexture(GL_TEXTURE_2D, _texture->getID());
} }
const float MAX_COLOR = 255.0f; const float MAX_COLOR = 255.0f;
xColor color = getColor(); xColor color = getColor();
float alpha = getAlpha(); float alpha = getAlpha();
glColor4f(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); glColor4f(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha);
float imageWidth = _textureImage.width();
float imageHeight = _textureImage.height();
QRect fromImage;
if (_wantClipFromImage) {
fromImage = _fromImage;
} else {
fromImage.setX(0);
fromImage.setY(0);
fromImage.setWidth(imageWidth);
fromImage.setHeight(imageHeight);
}
float x = fromImage.x() / imageWidth;
float y = fromImage.y() / imageHeight;
float w = fromImage.width() / imageWidth; // ?? is this what we want? not sure
float h = fromImage.height() / imageHeight;
int left = _bounds.left(); int left = _bounds.left();
int right = _bounds.right() + 1; int right = _bounds.right() + 1;
int top = _bounds.top(); int top = _bounds.top();
@ -111,10 +85,29 @@ void ImageOverlay::render(RenderArgs* args) {
glm::vec2 topLeft(left, top); glm::vec2 topLeft(left, top);
glm::vec2 bottomRight(right, bottom); glm::vec2 bottomRight(right, bottom);
glm::vec2 texCoordTopLeft(x, 1.0f - y);
glm::vec2 texCoordBottomRight(x + w, 1.0f - (y + h));
if (_renderImage) { if (_renderImage) {
float imageWidth = _texture->getWidth();
float imageHeight = _texture->getHeight();
QRect fromImage;
if (_wantClipFromImage) {
fromImage = _fromImage;
} else {
fromImage.setX(0);
fromImage.setY(0);
fromImage.setWidth(imageWidth);
fromImage.setHeight(imageHeight);
}
float x = fromImage.x() / imageWidth;
float y = fromImage.y() / imageHeight;
float w = fromImage.width() / imageWidth; // ?? is this what we want? not sure
float h = fromImage.height() / imageHeight;
glm::vec2 texCoordTopLeft(x, y);
glm::vec2 texCoordBottomRight(x + w, y + h);
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight); DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight);
} else { } else {
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight); DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight);
@ -153,7 +146,7 @@ void ImageOverlay::setProperties(const QScriptValue& properties) {
subImageRect.setHeight(oldSubImageRect.height()); subImageRect.setHeight(oldSubImageRect.height());
} }
setClipFromSource(subImageRect); setClipFromSource(subImageRect);
} }
QScriptValue imageURL = properties.property("imageURL"); QScriptValue imageURL = properties.property("imageURL");
if (imageURL.isValid()) { if (imageURL.isValid()) {

View file

@ -24,6 +24,7 @@
#include <NetworkAccessManager.h> #include <NetworkAccessManager.h>
#include <SharedUtil.h> #include <SharedUtil.h>
#include <TextureCache.h>
#include "Overlay.h" #include "Overlay.h"
#include "Overlay2D.h" #include "Overlay2D.h"
@ -49,18 +50,14 @@ public:
virtual ImageOverlay* createClone() const; virtual ImageOverlay* createClone() const;
private slots:
void replyFinished(); // we actually want to hide this...
private: private:
QUrl _imageURL; QUrl _imageURL;
QImage _textureImage; QImage _textureImage;
GLuint _textureID; NetworkTexturePointer _texture;
QRect _fromImage; // where from in the image to sample QRect _fromImage; // where from in the image to sample
bool _renderImage; // is there an image associated with this overlay, or is it just a colored rectangle bool _renderImage; // is there an image associated with this overlay, or is it just a colored rectangle
bool _textureBound; // has the texture been bound
bool _wantClipFromImage; bool _wantClipFromImage;
}; };

View file

@ -70,7 +70,7 @@
<property name="font"> <property name="font">
<font> <font>
<family>Helvetica,Arial,sans-serif</family> <family>Helvetica,Arial,sans-serif</family>
<pointsize>-1</pointsize> <pointsize>16</pointsize>
<weight>75</weight> <weight>75</weight>
<italic>false</italic> <italic>false</italic>
<bold>true</bold> <bold>true</bold>
@ -78,7 +78,7 @@
</property> </property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">color: #0e7077; <string notr="true">color: #0e7077;
font: bold 16px; font: bold 16pt;
</string> </string>
</property> </property>
<property name="text"> <property name="text">
@ -192,7 +192,7 @@ font: bold 16px;
</sizepolicy> </sizepolicy>
</property> </property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">font: 14px; color: #5f5f5f; margin: 2px;</string> <string notr="true">font: 14pt; color: #5f5f5f; margin: 2px;</string>
</property> </property>
<property name="text"> <property name="text">
<string>There are no scripts running.</string> <string>There are no scripts running.</string>
@ -245,8 +245,8 @@ font: bold 16px;
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>334</width> <width>328</width>
<height>20</height> <height>18</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -259,7 +259,7 @@ font: bold 16px;
<enum>Qt::LeftToRight</enum> <enum>Qt::LeftToRight</enum>
</property> </property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">font-size: 14px;</string> <string notr="true">font-size: 14pt;</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing"> <property name="spacing">
@ -301,7 +301,7 @@ font: bold 16px;
<item> <item>
<widget class="QLabel" name="tipLabel"> <widget class="QLabel" name="tipLabel">
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">font: 14px; color: #5f5f5f; margin: 2px;</string> <string notr="true">font: 14pt; color: #5f5f5f; margin: 2px;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Tip</string> <string>Tip</string>
@ -370,7 +370,7 @@ font: bold 16px;
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">color: #0e7077; <string notr="true">color: #0e7077;
font: bold 16px;</string> font: bold 16pt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Load Scripts</string> <string>Load Scripts</string>

View file

@ -254,22 +254,6 @@ void PhysicsEngine::init(EntityEditPacketSender* packetSender) {
// default gravity of the world is zero, so each object must specify its own gravity // default gravity of the world is zero, so each object must specify its own gravity
// TODO: set up gravity zones // TODO: set up gravity zones
_dynamicsWorld->setGravity(btVector3(0.0f, 0.0f, 0.0f)); _dynamicsWorld->setGravity(btVector3(0.0f, 0.0f, 0.0f));
// GROUND HACK: add a big planar floor (and walls for testing) to catch falling objects
btTransform groundTransform;
groundTransform.setIdentity();
for (int i = 0; i < 3; ++i) {
btVector3 normal(0.0f, 0.0f, 0.0f);
normal[i] = 1.0f;
btCollisionShape* plane = new btStaticPlaneShape(normal, 0.0f);
btCollisionObject* groundObject = new btCollisionObject();
groundObject->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT);
groundObject->setCollisionShape(plane);
groundObject->setWorldTransform(groundTransform);
_dynamicsWorld->addCollisionObject(groundObject);
}
} }
assert(packetSender); assert(packetSender);

View file

@ -385,7 +385,9 @@ Texture::~Texture() {
NetworkTexture::NetworkTexture(const QUrl& url, TextureType type, const QByteArray& content) : NetworkTexture::NetworkTexture(const QUrl& url, TextureType type, const QByteArray& content) :
Resource(url, !content.isEmpty()), Resource(url, !content.isEmpty()),
_type(type), _type(type),
_translucent(false) { _translucent(false),
_width(0),
_height(0) {
if (!url.isValid()) { if (!url.isValid()) {
_loaded = true; _loaded = true;
@ -532,6 +534,8 @@ void NetworkTexture::loadContent(const QByteArray& content) {
void NetworkTexture::setImage(const QImage& image, bool translucent, const QColor& averageColor) { void NetworkTexture::setImage(const QImage& image, bool translucent, const QColor& averageColor) {
_translucent = translucent; _translucent = translucent;
_averageColor = averageColor; _averageColor = averageColor;
_width = image.width();
_height = image.height();
finishedLoading(true); finishedLoading(true);
imageLoaded(image); imageLoaded(image);

View file

@ -150,6 +150,9 @@ public:
/// Returns the lazily-computed average texture color. /// Returns the lazily-computed average texture color.
const QColor& getAverageColor() const { return _averageColor; } const QColor& getAverageColor() const { return _averageColor; }
int getWidth() const { return _width; }
int getHeight() const { return _height; }
protected: protected:
virtual void downloadFinished(QNetworkReply* reply); virtual void downloadFinished(QNetworkReply* reply);
@ -163,6 +166,8 @@ private:
TextureType _type; TextureType _type;
bool _translucent; bool _translucent;
QColor _averageColor; QColor _averageColor;
int _width;
int _height;
}; };
/// Caches derived, dilated textures. /// Caches derived, dilated textures.