mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 04:04:13 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi
This commit is contained in:
commit
c1c9f698df
12 changed files with 321 additions and 150 deletions
|
@ -1,6 +1,7 @@
|
|||
// Pool Table
|
||||
var tableParts = [];
|
||||
var balls = [];
|
||||
var cueBall;
|
||||
|
||||
var LENGTH = 2.84;
|
||||
var WIDTH = 1.42;
|
||||
|
@ -13,6 +14,8 @@ var HOLE_SIZE = BALL_SIZE;
|
|||
var DROP_HEIGHT = BALL_SIZE * 3.0;
|
||||
var GRAVITY = -9.8;
|
||||
var BALL_GAP = 0.001;
|
||||
var tableCenter;
|
||||
var cuePosition;
|
||||
|
||||
var startStroke = 0;
|
||||
|
||||
|
@ -23,7 +26,7 @@ var reticle = Overlays.addOverlay("image", {
|
|||
y: screenSize.y / 2 - 16,
|
||||
width: 32,
|
||||
height: 32,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/reticle.png",
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/billiardsReticle.png",
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
alpha: 1
|
||||
});
|
||||
|
@ -102,7 +105,7 @@ function makeBalls(pos) {
|
|||
{ red: 128, green: 128, blue: 128}]; // Gray
|
||||
|
||||
// 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++) {
|
||||
ballPosition.z = pos.z - ((row - 1.0) / 2.0 * (BALL_SIZE + BALL_GAP) * SCALE);
|
||||
for (var spot = 0; spot < row; spot++) {
|
||||
|
@ -113,23 +116,26 @@ function makeBalls(pos) {
|
|||
color: colors[balls.length],
|
||||
gravity: { x: 0, y: GRAVITY, z: 0 },
|
||||
ignoreCollisions: false,
|
||||
damping: 0.40,
|
||||
damping: 0.50,
|
||||
collisionsWillMove: true }));
|
||||
ballPosition.z += (BALL_SIZE + BALL_GAP) * SCALE;
|
||||
}
|
||||
ballPosition.x += (BALL_GAP + Math.sqrt(3.0) / 2.0 * BALL_SIZE) * SCALE;
|
||||
}
|
||||
// Cue Ball
|
||||
ballPosition = { x: pos.x - (LENGTH / 4.0) * SCALE, y: pos.y + DROP_HEIGHT, z: pos.z };
|
||||
balls.push(Entities.addEntity(
|
||||
cuePosition = { x: pos.x - (LENGTH / 4.0) * SCALE, y: pos.y + HEIGHT / 2.0 + DROP_HEIGHT, z: pos.z };
|
||||
cueBall = Entities.addEntity(
|
||||
{ type: "Sphere",
|
||||
position: ballPosition,
|
||||
position: cuePosition,
|
||||
dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE },
|
||||
color: { red: 255, green: 255, blue: 255 },
|
||||
gravity: { x: 0, y: GRAVITY, z: 0 },
|
||||
angularVelocity: { x: 0, y: 0, z: 0 },
|
||||
velocity: {x: 0, y: 0, z: 0 },
|
||||
ignoreCollisions: false,
|
||||
damping: 0.40,
|
||||
collisionsWillMove: true }));
|
||||
damping: 0.50,
|
||||
collisionsWillMove: true });
|
||||
|
||||
}
|
||||
|
||||
function shootCue(velocity) {
|
||||
|
@ -140,19 +146,23 @@ function shootCue(velocity) {
|
|||
var velocity = Vec3.multiply(forwardVector, velocity);
|
||||
var BULLET_LIFETIME = 3.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(
|
||||
{ type: "Sphere",
|
||||
position: cuePosition,
|
||||
dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE },
|
||||
color: { red: 255, green: 255, blue: 255 },
|
||||
dimensions: { x: SHOOTER_SIZE, y: SHOOTER_SIZE, z: SHOOTER_SIZE },
|
||||
color: SHOOTER_COLOR,
|
||||
velocity: velocity,
|
||||
lifetime: BULLET_LIFETIME,
|
||||
gravity: { x: 0, y: BULLET_GRAVITY, z: 0 },
|
||||
damping: 0.10,
|
||||
density: 1000,
|
||||
density: 8000,
|
||||
ignoreCollisions: false,
|
||||
collisionsWillMove: true
|
||||
});
|
||||
print("Shot, velocity = " + velocity);
|
||||
}
|
||||
|
||||
function keyReleaseEvent(event) {
|
||||
|
@ -185,9 +195,25 @@ function cleanup() {
|
|||
Entities.deleteEntity(balls[i]);
|
||||
}
|
||||
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);
|
||||
makeBalls(tableCenter);
|
||||
|
@ -195,3 +221,4 @@ makeBalls(tableCenter);
|
|||
Script.scriptEnding.connect(cleanup);
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
||||
Script.update.connect(update);
|
||||
|
|
|
@ -47,7 +47,6 @@ var yawFromMouse = 0;
|
|||
var pitchFromMouse = 0;
|
||||
var isMouseDown = false;
|
||||
|
||||
var BULLET_VELOCITY = 5.0;
|
||||
var MIN_THROWER_DELAY = 1000;
|
||||
var MAX_THROWER_DELAY = 1000;
|
||||
var LEFT_BUTTON_3 = 3;
|
||||
|
@ -98,7 +97,7 @@ var reticle = Overlays.addOverlay("image", {
|
|||
y: screenSize.y / 2 - 16,
|
||||
width: 32,
|
||||
height: 32,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/reticle.png",
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/billiardsReticle.png",
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
alpha: 1
|
||||
});
|
||||
|
@ -113,6 +112,25 @@ var offButton = Overlays.addOverlay("image", {
|
|||
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) {
|
||||
var text = Overlays.addOverlay("text", {
|
||||
x: screenSize.x / 2 - 100,
|
||||
|
@ -127,24 +145,30 @@ if (showScore) {
|
|||
});
|
||||
}
|
||||
|
||||
function printVector(string, vector) {
|
||||
print(string + " " + vector.x + ", " + vector.y + ", " + vector.z);
|
||||
}
|
||||
var BULLET_VELOCITY = 10.0;
|
||||
|
||||
function shootBullet(position, velocity) {
|
||||
var BULLET_SIZE = 0.07;
|
||||
function shootBullet(position, velocity, grenade) {
|
||||
var BULLET_SIZE = 0.10;
|
||||
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(
|
||||
{ type: "Sphere",
|
||||
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 },
|
||||
velocity: velocity,
|
||||
velocity: bVelocity,
|
||||
lifetime: BULLET_LIFETIME,
|
||||
gravity: { x: 0, y: BULLET_GRAVITY, z: 0 },
|
||||
gravity: { x: 0, y: bGravity, z: 0 },
|
||||
damping: 0.01,
|
||||
density: 5000,
|
||||
density: 8000,
|
||||
ignoreCollisions: false,
|
||||
collisionsWillMove: true
|
||||
});
|
||||
|
@ -158,13 +182,15 @@ function shootBullet(position, velocity) {
|
|||
}
|
||||
|
||||
// Kickback the arm
|
||||
if (elbowKickAngle > 0.0) {
|
||||
MyAvatar.setJointData("LeftForeArm", rotationBeforeKickback);
|
||||
}
|
||||
rotationBeforeKickback = MyAvatar.getJointRotation("LeftForeArm");
|
||||
var armRotation = MyAvatar.getJointRotation("LeftForeArm");
|
||||
armRotation = Quat.multiply(armRotation, Quat.fromPitchYawRollDegrees(0.0, 0.0, KICKBACK_ANGLE));
|
||||
MyAvatar.setJointData("LeftForeArm", armRotation);
|
||||
elbowKickAngle = KICKBACK_ANGLE;
|
||||
}
|
||||
|
||||
function shootTarget() {
|
||||
var TARGET_SIZE = 0.50;
|
||||
var TARGET_GRAVITY = 0.0;
|
||||
|
@ -174,7 +200,7 @@ function shootTarget() {
|
|||
var DISTANCE_TO_LAUNCH_FROM = 5.0;
|
||||
var ANGLE_RANGE_FOR_LAUNCH = 20.0;
|
||||
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 });
|
||||
targetDirection = Quat.multiply(Camera.getOrientation(), targetDirection);
|
||||
var forwardVector = Quat.getFront(targetDirection);
|
||||
|
@ -205,6 +231,78 @@ function shootTarget() {
|
|||
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) {
|
||||
|
||||
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;
|
||||
Script.setTimeout(shootTarget, time);
|
||||
} else if ((event.text == ".") || (event.text == "SPACE")) {
|
||||
shootFromMouse();
|
||||
shootFromMouse(false);
|
||||
} else if (event.text == ",") {
|
||||
shootFromMouse(true);
|
||||
} else if (event.text == "r") {
|
||||
playLoadSound();
|
||||
} else if (event.text == "s") {
|
||||
|
@ -241,7 +341,6 @@ function keyPressEvent(event) {
|
|||
Quat.print("arm = ", MyAvatar.getJointRotation("LeftArm"));
|
||||
Quat.print("forearm = ", MyAvatar.getJointRotation("LeftForeArm"));
|
||||
Quat.print("hand = ", MyAvatar.getJointRotation("LeftHand"));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -287,18 +386,7 @@ function update(deltaTime) {
|
|||
if (targetID && !targetID.isKnownID) {
|
||||
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 (Controller.getNumberOfSpatialControls() > 0) {
|
||||
activeControllers = Controller.getNumberOfSpatialControls();
|
||||
|
@ -372,19 +460,19 @@ function update(deltaTime) {
|
|||
|
||||
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 camera = Camera.getPosition();
|
||||
var forwardVector = Quat.getFront(Camera.getOrientation());
|
||||
var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_FROM_CAMERA));
|
||||
var velocity = Vec3.multiply(forwardVector, BULLET_VELOCITY);
|
||||
shootBullet(newPosition, velocity);
|
||||
shootBullet(newPosition, velocity, grenade);
|
||||
}
|
||||
|
||||
function mouseReleaseEvent(event) {
|
||||
|
@ -392,21 +480,23 @@ function mouseReleaseEvent(event) {
|
|||
isMouseDown = false;
|
||||
}
|
||||
|
||||
function mouseMoveEvent(event) {
|
||||
if (isMouseDown) {
|
||||
var MOUSE_YAW_SCALE = -0.25;
|
||||
var MOUSE_PITCH_SCALE = -12.5;
|
||||
var FIXED_MOUSE_TIMESTEP = 0.016;
|
||||
yawFromMouse += ((event.x - lastX) * MOUSE_YAW_SCALE * FIXED_MOUSE_TIMESTEP);
|
||||
pitchFromMouse += ((event.y - lastY) * MOUSE_PITCH_SCALE * FIXED_MOUSE_TIMESTEP);
|
||||
lastX = event.x;
|
||||
lastY = event.y;
|
||||
}
|
||||
function mousePressEvent(event) {
|
||||
var clickedText = false;
|
||||
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
|
||||
if (clickedOverlay == offButton) {
|
||||
Script.stop();
|
||||
} else if (clickedOverlay == platformButton) {
|
||||
makePlatform(-9.8, 1.0, 5);
|
||||
} else if (clickedOverlay == gridButton) {
|
||||
makeGrid("Box", 1.0, 3);
|
||||
}
|
||||
}
|
||||
|
||||
function scriptEnding() {
|
||||
Overlays.deleteOverlay(reticle);
|
||||
Overlays.deleteOverlay(offButton);
|
||||
Overlays.deleteOverlay(platformButton);
|
||||
Overlays.deleteOverlay(gridButton);
|
||||
Overlays.deleteOverlay(pointer[0]);
|
||||
Overlays.deleteOverlay(pointer[1]);
|
||||
Overlays.deleteOverlay(text);
|
||||
|
@ -418,7 +508,7 @@ Entities.entityCollisionWithEntity.connect(entityCollisionWithEntity);
|
|||
Script.scriptEnding.connect(scriptEnding);
|
||||
Script.update.connect(update);
|
||||
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
|
||||
|
||||
|
|
|
@ -4,6 +4,6 @@ set(TARGET_NAME ice-server)
|
|||
setup_hifi_project(Network)
|
||||
|
||||
# link the shared hifi libraries
|
||||
link_hifi_libraries(networking shared)
|
||||
link_hifi_libraries(embedded-webserver networking shared)
|
||||
|
||||
include_dependency_includes()
|
|
@ -20,14 +20,18 @@
|
|||
const int CLEAR_INACTIVE_PEERS_INTERVAL_MSECS = 1 * 1000;
|
||||
const int PEER_SILENCE_THRESHOLD_MSECS = 5 * 1000;
|
||||
|
||||
const quint16 ICE_SERVER_MONITORING_PORT = 40110;
|
||||
|
||||
IceServer::IceServer(int argc, char* argv[]) :
|
||||
QCoreApplication(argc, argv),
|
||||
_id(QUuid::createUuid()),
|
||||
_serverSocket(),
|
||||
_activePeers()
|
||||
_activePeers(),
|
||||
_httpManager(ICE_SERVER_MONITORING_PORT, QString("%1/web/").arg(QCoreApplication::applicationDirPath()), this)
|
||||
{
|
||||
// start the ice-server socket
|
||||
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);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
|
|
@ -12,17 +12,21 @@
|
|||
#ifndef hifi_IceServer_h
|
||||
#define hifi_IceServer_h
|
||||
|
||||
#include <qcoreapplication.h>
|
||||
#include <qsharedpointer.h>
|
||||
#include <qudpsocket.h>
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QSharedPointer>
|
||||
#include <QUdpSocket>
|
||||
|
||||
#include <NetworkPeer.h>
|
||||
#include <HTTPConnection.h>
|
||||
#include <HTTPManager.h>
|
||||
|
||||
typedef QHash<QUuid, SharedNetworkPeer> NetworkPeerHash;
|
||||
|
||||
class IceServer : public QCoreApplication {
|
||||
class IceServer : public QCoreApplication, public HTTPRequestHandler {
|
||||
Q_OBJECT
|
||||
public:
|
||||
IceServer(int argc, char* argv[]);
|
||||
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false);
|
||||
private slots:
|
||||
void processDatagrams();
|
||||
void clearInactivePeers();
|
||||
|
@ -34,6 +38,7 @@ private:
|
|||
QUdpSocket _serverSocket;
|
||||
NetworkPeerHash _activePeers;
|
||||
QHash<QUuid, QSet<QUuid> > _currentConnections;
|
||||
HTTPManager _httpManager;
|
||||
};
|
||||
|
||||
#endif // hifi_IceServer_h
|
|
@ -22,88 +22,62 @@
|
|||
#include "ImageOverlay.h"
|
||||
|
||||
ImageOverlay::ImageOverlay() :
|
||||
_textureID(0),
|
||||
_imageURL(),
|
||||
_renderImage(false),
|
||||
_textureBound(false),
|
||||
_wantClipFromImage(false)
|
||||
{
|
||||
_isLoaded = false;
|
||||
}
|
||||
|
||||
ImageOverlay::ImageOverlay(const ImageOverlay* imageOverlay) :
|
||||
Overlay2D(imageOverlay),
|
||||
_texture(imageOverlay->_texture),
|
||||
_imageURL(imageOverlay->_imageURL),
|
||||
_textureImage(imageOverlay->_textureImage),
|
||||
_textureID(0),
|
||||
_fromImage(),
|
||||
_fromImage(imageOverlay->_fromImage),
|
||||
_renderImage(imageOverlay->_renderImage),
|
||||
_textureBound(false),
|
||||
_wantClipFromImage(false)
|
||||
_wantClipFromImage(imageOverlay->_wantClipFromImage)
|
||||
{
|
||||
}
|
||||
|
||||
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?
|
||||
void ImageOverlay::setImageURL(const QUrl& 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() {
|
||||
QNetworkReply* reply = static_cast<QNetworkReply*>(sender());
|
||||
|
||||
// replace our byte array with the downloaded data
|
||||
QByteArray rawData = reply->readAll();
|
||||
_textureImage.loadFromData(rawData);
|
||||
_renderImage = true;
|
||||
_isLoaded = true;
|
||||
reply->deleteLater();
|
||||
if (url.isEmpty()) {
|
||||
_isLoaded = true;
|
||||
_renderImage = false;
|
||||
_texture.clear();
|
||||
} else {
|
||||
_isLoaded = false;
|
||||
_renderImage = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ImageOverlay::render(RenderArgs* args) {
|
||||
if (!_visible || !_isLoaded) {
|
||||
return; // do nothing if we're not visible
|
||||
if (!_isLoaded && _renderImage) {
|
||||
_isLoaded = true;
|
||||
_texture = DependencyManager::get<TextureCache>()->getTexture(_imageURL);
|
||||
}
|
||||
if (_renderImage && !_textureBound) {
|
||||
_textureID = _parent->bindTexture(_textureImage);
|
||||
_textureBound = true;
|
||||
|
||||
// If we are not visible or loaded, return. If we are trying to render an
|
||||
// image but the texture hasn't loaded, return.
|
||||
if (!_visible || !_isLoaded || (_renderImage && !_texture->isLoaded())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_renderImage) {
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D, _textureID);
|
||||
glBindTexture(GL_TEXTURE_2D, _texture->getID());
|
||||
}
|
||||
|
||||
const float MAX_COLOR = 255.0f;
|
||||
xColor color = getColor();
|
||||
float alpha = getAlpha();
|
||||
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 right = _bounds.right() + 1;
|
||||
int top = _bounds.top();
|
||||
|
@ -111,10 +85,29 @@ void ImageOverlay::render(RenderArgs* args) {
|
|||
|
||||
glm::vec2 topLeft(left, top);
|
||||
glm::vec2 bottomRight(right, bottom);
|
||||
glm::vec2 texCoordTopLeft(x, 1.0f - y);
|
||||
glm::vec2 texCoordBottomRight(x + w, 1.0f - (y + h));
|
||||
|
||||
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);
|
||||
} else {
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight);
|
||||
|
@ -153,7 +146,7 @@ void ImageOverlay::setProperties(const QScriptValue& properties) {
|
|||
subImageRect.setHeight(oldSubImageRect.height());
|
||||
}
|
||||
setClipFromSource(subImageRect);
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue imageURL = properties.property("imageURL");
|
||||
if (imageURL.isValid()) {
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <TextureCache.h>
|
||||
|
||||
#include "Overlay.h"
|
||||
#include "Overlay2D.h"
|
||||
|
@ -49,18 +50,14 @@ public:
|
|||
|
||||
virtual ImageOverlay* createClone() const;
|
||||
|
||||
private slots:
|
||||
void replyFinished(); // we actually want to hide this...
|
||||
|
||||
private:
|
||||
|
||||
QUrl _imageURL;
|
||||
QImage _textureImage;
|
||||
|
||||
GLuint _textureID;
|
||||
NetworkTexturePointer _texture;
|
||||
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 _textureBound; // has the texture been bound
|
||||
bool _wantClipFromImage;
|
||||
};
|
||||
|
||||
|
|
|
@ -108,6 +108,16 @@ bool ObjectMotionState::doesNotNeedToSendUpdate() const {
|
|||
|
||||
bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) {
|
||||
assert(_body);
|
||||
|
||||
// if we've never checked before, our _sentFrame will be 0, and we need to initialize our state
|
||||
if (_sentFrame == 0) {
|
||||
_sentPosition = bulletToGLM(_body->getWorldTransform().getOrigin());
|
||||
_sentVelocity = bulletToGLM(_body->getLinearVelocity());
|
||||
_sentAngularVelocity = bulletToGLM(_body->getAngularVelocity());
|
||||
_sentFrame = simulationFrame;
|
||||
return false;
|
||||
}
|
||||
|
||||
float dt = (float)(simulationFrame - _sentFrame) * PHYSICS_ENGINE_FIXED_SUBSTEP;
|
||||
_sentFrame = simulationFrame;
|
||||
bool isActive = _body->isActive();
|
||||
|
|
|
@ -186,22 +186,6 @@ void PhysicsEngine::init(EntityEditPacketSender* packetSender) {
|
|||
// default gravity of the world is zero, so each object must specify its own gravity
|
||||
// TODO: set up gravity zones
|
||||
_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);
|
||||
|
@ -231,41 +215,67 @@ void PhysicsEngine::stepSimulation() {
|
|||
_numSubsteps += (uint32_t)numSubsteps;
|
||||
unlock();
|
||||
|
||||
// This is step (3) which is done outside of stepSimulation() so we can lock _entityTree.
|
||||
//
|
||||
// Unfortunately we have to unlock the simulation (above) before we try to lock the _entityTree
|
||||
// to avoid deadlock -- the _entityTree may try to lock its EntitySimulation (from which this
|
||||
// PhysicsEngine derives) when updating/adding/deleting entities so we need to wait for our own
|
||||
// lock on the tree before we re-lock ourselves.
|
||||
//
|
||||
// TODO: untangle these lock sequences.
|
||||
_entityTree->lockForWrite();
|
||||
lock();
|
||||
_dynamicsWorld->synchronizeMotionStates();
|
||||
unlock();
|
||||
_entityTree->unlock();
|
||||
|
||||
computeCollisionEvents();
|
||||
if (numSubsteps > 0) {
|
||||
// This is step (3) which is done outside of stepSimulation() so we can lock _entityTree.
|
||||
//
|
||||
// Unfortunately we have to unlock the simulation (above) before we try to lock the _entityTree
|
||||
// to avoid deadlock -- the _entityTree may try to lock its EntitySimulation (from which this
|
||||
// PhysicsEngine derives) when updating/adding/deleting entities so we need to wait for our own
|
||||
// lock on the tree before we re-lock ourselves.
|
||||
//
|
||||
// TODO: untangle these lock sequences.
|
||||
_entityTree->lockForWrite();
|
||||
lock();
|
||||
_dynamicsWorld->synchronizeMotionStates();
|
||||
unlock();
|
||||
_entityTree->unlock();
|
||||
|
||||
computeCollisionEvents();
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicsEngine::computeCollisionEvents() {
|
||||
// update all contacts
|
||||
// update all contacts every frame
|
||||
int numManifolds = _collisionDispatcher->getNumManifolds();
|
||||
for (int i = 0; i < numManifolds; ++i) {
|
||||
btPersistentManifold* contactManifold = _collisionDispatcher->getManifoldByIndexInternal(i);
|
||||
if (contactManifold->getNumContacts() > 0) {
|
||||
// TODO: require scripts to register interest in callbacks for specific objects
|
||||
// so we can filter out most collision events right here.
|
||||
const btCollisionObject* objectA = static_cast<const btCollisionObject*>(contactManifold->getBody0());
|
||||
const btCollisionObject* objectB = static_cast<const btCollisionObject*>(contactManifold->getBody1());
|
||||
|
||||
if (!(objectA->isActive() || objectB->isActive())) {
|
||||
// both objects are inactive so stop tracking this contact,
|
||||
// which will eventually trigger a CONTACT_EVENT_TYPE_END
|
||||
continue;
|
||||
}
|
||||
|
||||
void* a = objectA->getUserPointer();
|
||||
void* b = objectB->getUserPointer();
|
||||
if (a || b) {
|
||||
// the manifold has up to 4 distinct points, but only extract info from the first
|
||||
_contactMap[ContactKey(a, b)].update(_numSubsteps, contactManifold->getContactPoint(0), _originOffset);
|
||||
_contactMap[ContactKey(a, b)].update(_numContactFrames, contactManifold->getContactPoint(0), _originOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We harvest collision callbacks every few frames, which contributes the following effects:
|
||||
//
|
||||
// (1) There is a maximum collision callback rate per pair: substep_rate / SUBSTEPS_PER_COLLIION_FRAME
|
||||
// (2) END/START cycles shorter than SUBSTEPS_PER_COLLIION_FRAME will be filtered out
|
||||
// (3) There is variable lag between when the contact actually starts and when it is reported,
|
||||
// up to SUBSTEPS_PER_COLLIION_FRAME * time_per_substep
|
||||
//
|
||||
const uint32_t SUBSTEPS_PER_COLLISION_FRAME = 2;
|
||||
if (_numSubsteps - _numContactFrames * SUBSTEPS_PER_COLLISION_FRAME < SUBSTEPS_PER_COLLISION_FRAME) {
|
||||
// we don't harvest collision callbacks every frame
|
||||
// this sets a maximum callback-per-contact rate
|
||||
// and also filters out END/START events that happen on shorter timescales
|
||||
return;
|
||||
}
|
||||
|
||||
++_numContactFrames;
|
||||
// scan known contacts and trigger events
|
||||
ContactMap::iterator contactItr = _contactMap.begin();
|
||||
while (contactItr != _contactMap.end()) {
|
||||
|
@ -289,7 +299,7 @@ void PhysicsEngine::computeCollisionEvents() {
|
|||
}
|
||||
|
||||
// TODO: enable scripts to filter based on contact event type
|
||||
ContactEventType type = contactItr->second.computeType(_numSubsteps);
|
||||
ContactEventType type = contactItr->second.computeType(_numContactFrames);
|
||||
if (type == CONTACT_EVENT_TYPE_END) {
|
||||
ContactMap::iterator iterToDelete = contactItr;
|
||||
++contactItr;
|
||||
|
|
|
@ -110,6 +110,7 @@ private:
|
|||
EntityEditPacketSender* _entityPacketSender = NULL;
|
||||
|
||||
ContactMap _contactMap;
|
||||
uint32_t _numContactFrames = 0;
|
||||
};
|
||||
|
||||
#endif // hifi_PhysicsEngine_h
|
||||
|
|
|
@ -385,7 +385,9 @@ Texture::~Texture() {
|
|||
NetworkTexture::NetworkTexture(const QUrl& url, TextureType type, const QByteArray& content) :
|
||||
Resource(url, !content.isEmpty()),
|
||||
_type(type),
|
||||
_translucent(false) {
|
||||
_translucent(false),
|
||||
_width(0),
|
||||
_height(0) {
|
||||
|
||||
if (!url.isValid()) {
|
||||
_loaded = true;
|
||||
|
@ -532,6 +534,8 @@ void NetworkTexture::loadContent(const QByteArray& content) {
|
|||
void NetworkTexture::setImage(const QImage& image, bool translucent, const QColor& averageColor) {
|
||||
_translucent = translucent;
|
||||
_averageColor = averageColor;
|
||||
_width = image.width();
|
||||
_height = image.height();
|
||||
|
||||
finishedLoading(true);
|
||||
imageLoaded(image);
|
||||
|
|
|
@ -150,6 +150,9 @@ public:
|
|||
/// Returns the lazily-computed average texture color.
|
||||
const QColor& getAverageColor() const { return _averageColor; }
|
||||
|
||||
int getWidth() const { return _width; }
|
||||
int getHeight() const { return _height; }
|
||||
|
||||
protected:
|
||||
|
||||
virtual void downloadFinished(QNetworkReply* reply);
|
||||
|
@ -163,6 +166,8 @@ private:
|
|||
TextureType _type;
|
||||
bool _translucent;
|
||||
QColor _averageColor;
|
||||
int _width;
|
||||
int _height;
|
||||
};
|
||||
|
||||
/// Caches derived, dilated textures.
|
||||
|
|
Loading…
Reference in a new issue