mirror of
https://github.com/overte-org/overte.git
synced 2025-08-10 03:19:24 +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
|
// 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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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()
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
|
@ -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()) {
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -108,6 +108,16 @@ bool ObjectMotionState::doesNotNeedToSendUpdate() const {
|
||||||
|
|
||||||
bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) {
|
bool ObjectMotionState::shouldSendUpdate(uint32_t simulationFrame) {
|
||||||
assert(_body);
|
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;
|
float dt = (float)(simulationFrame - _sentFrame) * PHYSICS_ENGINE_FIXED_SUBSTEP;
|
||||||
_sentFrame = simulationFrame;
|
_sentFrame = simulationFrame;
|
||||||
bool isActive = _body->isActive();
|
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
|
// 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);
|
||||||
|
@ -231,41 +215,67 @@ void PhysicsEngine::stepSimulation() {
|
||||||
_numSubsteps += (uint32_t)numSubsteps;
|
_numSubsteps += (uint32_t)numSubsteps;
|
||||||
unlock();
|
unlock();
|
||||||
|
|
||||||
// This is step (3) which is done outside of stepSimulation() so we can lock _entityTree.
|
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
|
// Unfortunately we have to unlock the simulation (above) before we try to lock the _entityTree
|
||||||
// PhysicsEngine derives) when updating/adding/deleting entities so we need to wait for our own
|
// to avoid deadlock -- the _entityTree may try to lock its EntitySimulation (from which this
|
||||||
// lock on the tree before we re-lock ourselves.
|
// 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();
|
// TODO: untangle these lock sequences.
|
||||||
lock();
|
_entityTree->lockForWrite();
|
||||||
_dynamicsWorld->synchronizeMotionStates();
|
lock();
|
||||||
unlock();
|
_dynamicsWorld->synchronizeMotionStates();
|
||||||
_entityTree->unlock();
|
unlock();
|
||||||
|
_entityTree->unlock();
|
||||||
computeCollisionEvents();
|
|
||||||
|
computeCollisionEvents();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsEngine::computeCollisionEvents() {
|
void PhysicsEngine::computeCollisionEvents() {
|
||||||
// update all contacts
|
// update all contacts every frame
|
||||||
int numManifolds = _collisionDispatcher->getNumManifolds();
|
int numManifolds = _collisionDispatcher->getNumManifolds();
|
||||||
for (int i = 0; i < numManifolds; ++i) {
|
for (int i = 0; i < numManifolds; ++i) {
|
||||||
btPersistentManifold* contactManifold = _collisionDispatcher->getManifoldByIndexInternal(i);
|
btPersistentManifold* contactManifold = _collisionDispatcher->getManifoldByIndexInternal(i);
|
||||||
if (contactManifold->getNumContacts() > 0) {
|
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* objectA = static_cast<const btCollisionObject*>(contactManifold->getBody0());
|
||||||
const btCollisionObject* objectB = static_cast<const btCollisionObject*>(contactManifold->getBody1());
|
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* a = objectA->getUserPointer();
|
||||||
void* b = objectB->getUserPointer();
|
void* b = objectB->getUserPointer();
|
||||||
if (a || b) {
|
if (a || b) {
|
||||||
// the manifold has up to 4 distinct points, but only extract info from the first
|
// 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
|
// scan known contacts and trigger events
|
||||||
ContactMap::iterator contactItr = _contactMap.begin();
|
ContactMap::iterator contactItr = _contactMap.begin();
|
||||||
while (contactItr != _contactMap.end()) {
|
while (contactItr != _contactMap.end()) {
|
||||||
|
@ -289,7 +299,7 @@ void PhysicsEngine::computeCollisionEvents() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: enable scripts to filter based on contact event type
|
// 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) {
|
if (type == CONTACT_EVENT_TYPE_END) {
|
||||||
ContactMap::iterator iterToDelete = contactItr;
|
ContactMap::iterator iterToDelete = contactItr;
|
||||||
++contactItr;
|
++contactItr;
|
||||||
|
|
|
@ -110,6 +110,7 @@ private:
|
||||||
EntityEditPacketSender* _entityPacketSender = NULL;
|
EntityEditPacketSender* _entityPacketSender = NULL;
|
||||||
|
|
||||||
ContactMap _contactMap;
|
ContactMap _contactMap;
|
||||||
|
uint32_t _numContactFrames = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_PhysicsEngine_h
|
#endif // hifi_PhysicsEngine_h
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in a new issue