mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
merge upstream/master into andrew/thermonuclear
This commit is contained in:
commit
53e241fb07
36 changed files with 1265 additions and 307 deletions
252
examples/example/games/airHockey.js
Normal file
252
examples/example/games/airHockey.js
Normal file
|
@ -0,0 +1,252 @@
|
||||||
|
//
|
||||||
|
// AirHockey.js
|
||||||
|
//
|
||||||
|
// Created by Philip Rosedale on January 26, 2015
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// AirHockey table and pucks
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
var debugVisible = false;
|
||||||
|
|
||||||
|
var FIELD_WIDTH = 1.21;
|
||||||
|
var FIELD_LENGTH = 1.92;
|
||||||
|
var FLOOR_THICKNESS = 0.20;
|
||||||
|
var EDGE_THICKESS = 0.10;
|
||||||
|
var EDGE_HEIGHT = 0.10;
|
||||||
|
var DROP_HEIGHT = 0.3;
|
||||||
|
var PUCK_SIZE = 0.15;
|
||||||
|
var PUCK_THICKNESS = 0.03;
|
||||||
|
var PADDLE_SIZE = 0.12;
|
||||||
|
var PADDLE_THICKNESS = 0.03;
|
||||||
|
|
||||||
|
var GOAL_WIDTH = 0.35;
|
||||||
|
|
||||||
|
var GRAVITY = -9.8;
|
||||||
|
var LIFETIME = 6000;
|
||||||
|
var PUCK_DAMPING = 0.03;
|
||||||
|
var PADDLE_DAMPING = 0.35;
|
||||||
|
var ANGULAR_DAMPING = 0.10;
|
||||||
|
var PADDLE_ANGULAR_DAMPING = 0.35;
|
||||||
|
var MODEL_SCALE = 1.52;
|
||||||
|
var MODEL_OFFSET = { x: 0, y: -0.18, z: 0 };
|
||||||
|
|
||||||
|
var scoreSound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/Collisions-hitsandslaps/airhockey_score.wav");
|
||||||
|
|
||||||
|
var polyTable = "https://hifi-public.s3.amazonaws.com/ozan/props/airHockeyTable/airHockeyTableForPolyworld.fbx"
|
||||||
|
var normalTable = "https://hifi-public.s3.amazonaws.com/ozan/props/airHockeyTable/airHockeyTable.fbx"
|
||||||
|
var hitSound1 = "https://s3.amazonaws.com/hifi-public/sounds/Collisions-hitsandslaps/airhockey_hit1.wav"
|
||||||
|
var hitSound2 = "https://s3.amazonaws.com/hifi-public/sounds/Collisions-hitsandslaps/airhockey_hit2.wav"
|
||||||
|
var hitSideSound = "https://s3.amazonaws.com/hifi-public/sounds/Collisions-hitsandslaps/airhockey_hit3.wav"
|
||||||
|
|
||||||
|
var center = Vec3.sum(MyAvatar.position, Vec3.multiply((FIELD_WIDTH + FIELD_LENGTH) * 0.60, Quat.getFront(Camera.getOrientation())));
|
||||||
|
|
||||||
|
var floor = Entities.addEntity(
|
||||||
|
{ type: "Box",
|
||||||
|
position: Vec3.subtract(center, { x: 0, y: 0, z: 0 }),
|
||||||
|
dimensions: { x: FIELD_WIDTH, y: FLOOR_THICKNESS, z: FIELD_LENGTH },
|
||||||
|
color: { red: 128, green: 128, blue: 128 },
|
||||||
|
gravity: { x: 0, y: 0, z: 0 },
|
||||||
|
ignoreCollisions: false,
|
||||||
|
locked: true,
|
||||||
|
visible: debugVisible,
|
||||||
|
lifetime: LIFETIME });
|
||||||
|
|
||||||
|
var edge1 = Entities.addEntity(
|
||||||
|
{ type: "Box",
|
||||||
|
collisionSoundURL: hitSideSound,
|
||||||
|
position: Vec3.sum(center, { x: FIELD_WIDTH / 2.0, y: FLOOR_THICKNESS / 2.0, z: 0 }),
|
||||||
|
dimensions: { x: EDGE_THICKESS, y: EDGE_HEIGHT, z: FIELD_LENGTH + EDGE_THICKESS },
|
||||||
|
color: { red: 100, green: 100, blue: 100 },
|
||||||
|
gravity: { x: 0, y: 0, z: 0 },
|
||||||
|
ignoreCollisions: false,
|
||||||
|
visible: debugVisible,
|
||||||
|
locked: true,
|
||||||
|
lifetime: LIFETIME });
|
||||||
|
|
||||||
|
var edge2 = Entities.addEntity(
|
||||||
|
{ type: "Box",
|
||||||
|
collisionSoundURL: hitSideSound,
|
||||||
|
position: Vec3.sum(center, { x: -FIELD_WIDTH / 2.0, y: FLOOR_THICKNESS / 2.0, z: 0 }),
|
||||||
|
dimensions: { x: EDGE_THICKESS, y: EDGE_HEIGHT, z: FIELD_LENGTH + EDGE_THICKESS },
|
||||||
|
color: { red: 100, green: 100, blue: 100 },
|
||||||
|
gravity: { x: 0, y: 0, z: 0 },
|
||||||
|
ignoreCollisions: false,
|
||||||
|
visible: debugVisible,
|
||||||
|
locked: true,
|
||||||
|
lifetime: LIFETIME });
|
||||||
|
|
||||||
|
var edge3a = Entities.addEntity(
|
||||||
|
{ type: "Box",
|
||||||
|
collisionSoundURL: hitSideSound,
|
||||||
|
position: Vec3.sum(center, { x: FIELD_WIDTH / 4.0 + (GOAL_WIDTH / 4.0), y: FLOOR_THICKNESS / 2.0, z: -FIELD_LENGTH / 2.0 }),
|
||||||
|
dimensions: { x: FIELD_WIDTH / 2.0 - GOAL_WIDTH / 2.0, y: EDGE_HEIGHT, z: EDGE_THICKESS },
|
||||||
|
color: { red: 100, green: 100, blue: 100 },
|
||||||
|
gravity: { x: 0, y: 0, z: 0 },
|
||||||
|
ignoreCollisions: false,
|
||||||
|
visible: debugVisible,
|
||||||
|
locked: true,
|
||||||
|
lifetime: LIFETIME });
|
||||||
|
|
||||||
|
var edge3b = Entities.addEntity(
|
||||||
|
{ type: "Box",
|
||||||
|
collisionSoundURL: hitSideSound,
|
||||||
|
position: Vec3.sum(center, { x: -FIELD_WIDTH / 4.0 - (GOAL_WIDTH / 4.0), y: FLOOR_THICKNESS / 2.0, z: -FIELD_LENGTH / 2.0 }),
|
||||||
|
dimensions: { x: FIELD_WIDTH / 2.0 - GOAL_WIDTH / 2.0, y: EDGE_HEIGHT, z: EDGE_THICKESS },
|
||||||
|
color: { red: 100, green: 100, blue: 100 },
|
||||||
|
gravity: { x: 0, y: 0, z: 0 },
|
||||||
|
ignoreCollisions: false,
|
||||||
|
visible: debugVisible,
|
||||||
|
locked: true,
|
||||||
|
lifetime: LIFETIME });
|
||||||
|
|
||||||
|
var edge4a = Entities.addEntity(
|
||||||
|
{ type: "Box",
|
||||||
|
collisionSoundURL: hitSideSound,
|
||||||
|
position: Vec3.sum(center, { x: FIELD_WIDTH / 4.0 + (GOAL_WIDTH / 4.0), y: FLOOR_THICKNESS / 2.0, z: FIELD_LENGTH / 2.0 }),
|
||||||
|
dimensions: { x: FIELD_WIDTH / 2.0 - GOAL_WIDTH / 2.0, y: EDGE_HEIGHT, z: EDGE_THICKESS },
|
||||||
|
color: { red: 100, green: 100, blue: 100 },
|
||||||
|
gravity: { x: 0, y: 0, z: 0 },
|
||||||
|
ignoreCollisions: false,
|
||||||
|
visible: debugVisible,
|
||||||
|
locked: true,
|
||||||
|
lifetime: LIFETIME });
|
||||||
|
|
||||||
|
var edge4b = Entities.addEntity(
|
||||||
|
{ type: "Box",
|
||||||
|
collisionSoundURL: hitSideSound,
|
||||||
|
position: Vec3.sum(center, { x: -FIELD_WIDTH / 4.0 - (GOAL_WIDTH / 4.0), y: FLOOR_THICKNESS / 2.0, z: FIELD_LENGTH / 2.0 }),
|
||||||
|
dimensions: { x: FIELD_WIDTH / 2.0 - GOAL_WIDTH / 2.0, y: EDGE_HEIGHT, z: EDGE_THICKESS },
|
||||||
|
color: { red: 100, green: 100, blue: 100 },
|
||||||
|
gravity: { x: 0, y: 0, z: 0 },
|
||||||
|
ignoreCollisions: false,
|
||||||
|
visible: debugVisible,
|
||||||
|
locked: true,
|
||||||
|
lifetime: LIFETIME });
|
||||||
|
|
||||||
|
var table = Entities.addEntity(
|
||||||
|
{ type: "Model",
|
||||||
|
modelURL: polyTable,
|
||||||
|
dimensions: Vec3.multiply({ x: 0.8, y: 0.45, z: 1.31 }, MODEL_SCALE),
|
||||||
|
position: Vec3.sum(center, MODEL_OFFSET),
|
||||||
|
ignoreCollisions: false,
|
||||||
|
visible: true,
|
||||||
|
locked: true,
|
||||||
|
lifetime: LIFETIME });
|
||||||
|
|
||||||
|
var puck;
|
||||||
|
var paddle1, paddle2;
|
||||||
|
|
||||||
|
// Create pucks
|
||||||
|
|
||||||
|
function makeNewProp(which) {
|
||||||
|
if (which == "puck") {
|
||||||
|
return Entities.addEntity(
|
||||||
|
{ type: "Model",
|
||||||
|
modelURL: "http://headache.hungry.com/~seth/hifi/puck.obj",
|
||||||
|
compoundShapeURL: "http://headache.hungry.com/~seth/hifi/puck.obj",
|
||||||
|
collisionSoundURL: hitSound1,
|
||||||
|
position: Vec3.sum(center, { x: 0, y: DROP_HEIGHT, z: 0 }),
|
||||||
|
dimensions: { x: PUCK_SIZE, y: PUCK_THICKNESS, z: PUCK_SIZE },
|
||||||
|
gravity: { x: 0, y: GRAVITY, z: 0 },
|
||||||
|
velocity: { x: 0, y: 0.05, z: 0 },
|
||||||
|
ignoreCollisions: false,
|
||||||
|
damping: PUCK_DAMPING,
|
||||||
|
angularDamping: ANGULAR_DAMPING,
|
||||||
|
lifetime: LIFETIME,
|
||||||
|
collisionsWillMove: true });
|
||||||
|
}
|
||||||
|
else if (which == "paddle1") {
|
||||||
|
return Entities.addEntity(
|
||||||
|
{ type: "Model",
|
||||||
|
modelURL: "http://headache.hungry.com/~seth/hifi/puck.obj",
|
||||||
|
compoundShapeURL: "http://headache.hungry.com/~seth/hifi/puck.obj",
|
||||||
|
collisionSoundURL: hitSound2,
|
||||||
|
position: Vec3.sum(center, { x: 0, y: DROP_HEIGHT, z: FIELD_LENGTH * 0.35 }),
|
||||||
|
dimensions: { x: PADDLE_SIZE, y: PADDLE_THICKNESS, z: PADDLE_SIZE },
|
||||||
|
gravity: { x: 0, y: GRAVITY, z: 0 },
|
||||||
|
velocity: { x: 0, y: 0.05, z: 0 },
|
||||||
|
ignoreCollisions: false,
|
||||||
|
damping: PADDLE_DAMPING,
|
||||||
|
angularDamping: PADDLE_ANGULAR_DAMPING,
|
||||||
|
lifetime: LIFETIME,
|
||||||
|
collisionsWillMove: true });
|
||||||
|
}
|
||||||
|
else if (which == "paddle2") {
|
||||||
|
return Entities.addEntity(
|
||||||
|
{ type: "Model",
|
||||||
|
modelURL: "http://headache.hungry.com/~seth/hifi/puck.obj",
|
||||||
|
compoundShapeURL: "http://headache.hungry.com/~seth/hifi/puck.obj",
|
||||||
|
collisionSoundURL: hitSound2,
|
||||||
|
position: Vec3.sum(center, { x: 0, y: DROP_HEIGHT, z: -FIELD_LENGTH * 0.35 }),
|
||||||
|
dimensions: { x: PADDLE_SIZE, y: PADDLE_THICKNESS, z: PADDLE_SIZE },
|
||||||
|
gravity: { x: 0, y: GRAVITY, z: 0 },
|
||||||
|
velocity: { x: 0, y: 0.05, z: 0 },
|
||||||
|
ignoreCollisions: false,
|
||||||
|
damping: PADDLE_DAMPING,
|
||||||
|
angularDamping: PADDLE_ANGULAR_DAMPING,
|
||||||
|
lifetime: LIFETIME,
|
||||||
|
collisionsWillMove: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
puck = makeNewProp("puck");
|
||||||
|
paddle1 = makeNewProp("paddle1");
|
||||||
|
paddle2 = makeNewProp("paddle2");
|
||||||
|
|
||||||
|
function update(deltaTime) {
|
||||||
|
if (Math.random() < 0.1) {
|
||||||
|
puckProps = Entities.getEntityProperties(puck);
|
||||||
|
paddle1Props = Entities.getEntityProperties(paddle1);
|
||||||
|
paddle2Props = Entities.getEntityProperties(paddle2);
|
||||||
|
if (puckProps.position.y < (center.y - DROP_HEIGHT)) {
|
||||||
|
Audio.playSound(scoreSound, {
|
||||||
|
position: center,
|
||||||
|
volume: 1.0
|
||||||
|
});
|
||||||
|
Entities.deleteEntity(puck);
|
||||||
|
puck = makeNewProp("puck");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paddle1Props.position.y < (center.y - DROP_HEIGHT)) {
|
||||||
|
Entities.deleteEntity(paddle1);
|
||||||
|
paddle1 = makeNewProp("paddle1");
|
||||||
|
}
|
||||||
|
if (paddle2Props.position.y < (center.y - DROP_HEIGHT)) {
|
||||||
|
Entities.deleteEntity(paddle2);
|
||||||
|
paddle2 = makeNewProp("paddle2");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function scriptEnding() {
|
||||||
|
|
||||||
|
Entities.editEntity(edge1, { locked: false });
|
||||||
|
Entities.editEntity(edge2, { locked: false });
|
||||||
|
Entities.editEntity(edge3a, { locked: false });
|
||||||
|
Entities.editEntity(edge3b, { locked: false });
|
||||||
|
Entities.editEntity(edge4a, { locked: false });
|
||||||
|
Entities.editEntity(edge4b, { locked: false });
|
||||||
|
Entities.editEntity(floor, { locked: false });
|
||||||
|
Entities.editEntity(table, { locked: false });
|
||||||
|
|
||||||
|
|
||||||
|
Entities.deleteEntity(edge1);
|
||||||
|
Entities.deleteEntity(edge2);
|
||||||
|
Entities.deleteEntity(edge3a);
|
||||||
|
Entities.deleteEntity(edge3b);
|
||||||
|
Entities.deleteEntity(edge4a);
|
||||||
|
Entities.deleteEntity(edge4b);
|
||||||
|
Entities.deleteEntity(floor);
|
||||||
|
Entities.deleteEntity(puck);
|
||||||
|
Entities.deleteEntity(paddle1);
|
||||||
|
Entities.deleteEntity(paddle2);
|
||||||
|
Entities.deleteEntity(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
Script.update.connect(update);
|
||||||
|
Script.scriptEnding.connect(scriptEnding);
|
278
examples/example/games/grabHockey.js
Normal file
278
examples/example/games/grabHockey.js
Normal file
|
@ -0,0 +1,278 @@
|
||||||
|
|
||||||
|
// grab.js
|
||||||
|
// examples
|
||||||
|
//
|
||||||
|
// Created by Eric Levin on May 1, 2015
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Grab's physically moveable entities with the mouse, by applying a spring force.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
var isGrabbing = false;
|
||||||
|
var grabbedEntity = null;
|
||||||
|
var lineEntityID = null;
|
||||||
|
var prevMouse = {};
|
||||||
|
var deltaMouse = {
|
||||||
|
z: 0
|
||||||
|
}
|
||||||
|
var entityProps;
|
||||||
|
var moveUpDown = false;
|
||||||
|
var CLOSE_ENOUGH = 0.001;
|
||||||
|
var FULL_STRENGTH = 1.0;
|
||||||
|
var SPRING_RATE = 1.5;
|
||||||
|
var DAMPING_RATE = 0.80;
|
||||||
|
var ANGULAR_DAMPING_RATE = 0.40;
|
||||||
|
var SCREEN_TO_METERS = 0.001;
|
||||||
|
var currentPosition, currentVelocity, cameraEntityDistance, currentRotation;
|
||||||
|
var velocityTowardTarget, desiredVelocity, addedVelocity, newVelocity, dPosition, camYaw, distanceToTarget, targetPosition;
|
||||||
|
var originalGravity = {x: 0, y: 0, z: 0};
|
||||||
|
var shouldRotate = false;
|
||||||
|
var dQ, theta, axisAngle, dT;
|
||||||
|
var angularVelocity = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
z: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
var grabSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/CloseClamp.wav");
|
||||||
|
var releaseSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/ReleaseClamp.wav");
|
||||||
|
var VOLUME = 0.10;
|
||||||
|
|
||||||
|
var DROP_DISTANCE = 5.0;
|
||||||
|
var DROP_COLOR = {
|
||||||
|
red: 200,
|
||||||
|
green: 200,
|
||||||
|
blue: 200
|
||||||
|
};
|
||||||
|
var DROP_WIDTH = 2;
|
||||||
|
|
||||||
|
|
||||||
|
var dropLine = Overlays.addOverlay("line3d", {
|
||||||
|
color: DROP_COLOR,
|
||||||
|
alpha: 1,
|
||||||
|
visible: false,
|
||||||
|
lineWidth: DROP_WIDTH
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function vectorIsZero(v) {
|
||||||
|
return v.x == 0 && v.y == 0 && v.z == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function nearLinePoint(targetPosition) {
|
||||||
|
// var handPosition = Vec3.sum(MyAvatar.position, {x:0, y:0.2, z:0});
|
||||||
|
var handPosition = MyAvatar.getRightPalmPosition();
|
||||||
|
var along = Vec3.subtract(targetPosition, handPosition);
|
||||||
|
along = Vec3.normalize(along);
|
||||||
|
along = Vec3.multiply(along, 0.4);
|
||||||
|
return Vec3.sum(handPosition, along);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function mousePressEvent(event) {
|
||||||
|
if (!event.isLeftButton) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||||
|
var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking
|
||||||
|
if (intersection.intersects && intersection.properties.collisionsWillMove) {
|
||||||
|
grabbedEntity = intersection.entityID;
|
||||||
|
var props = Entities.getEntityProperties(grabbedEntity)
|
||||||
|
isGrabbing = true;
|
||||||
|
originalGravity = props.gravity;
|
||||||
|
targetPosition = props.position;
|
||||||
|
currentPosition = props.position;
|
||||||
|
currentVelocity = props.velocity;
|
||||||
|
updateDropLine(targetPosition);
|
||||||
|
|
||||||
|
Entities.editEntity(grabbedEntity, {
|
||||||
|
gravity: {x: 0, y: 0, z: 0}
|
||||||
|
});
|
||||||
|
|
||||||
|
lineEntityID = Entities.addEntity({
|
||||||
|
type: "Line",
|
||||||
|
position: nearLinePoint(targetPosition),
|
||||||
|
dimensions: Vec3.subtract(targetPosition, nearLinePoint(targetPosition)),
|
||||||
|
color: { red: 255, green: 255, blue: 255 },
|
||||||
|
lifetime: 300 // if someone crashes while moving something, don't leave the line there forever.
|
||||||
|
});
|
||||||
|
|
||||||
|
Audio.playSound(grabSound, {
|
||||||
|
position: props.position,
|
||||||
|
volume: VOLUME
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateDropLine(position) {
|
||||||
|
Overlays.editOverlay(dropLine, {
|
||||||
|
visible: true,
|
||||||
|
start: {
|
||||||
|
x: position.x,
|
||||||
|
y: position.y + DROP_DISTANCE,
|
||||||
|
z: position.z
|
||||||
|
},
|
||||||
|
end: {
|
||||||
|
x: position.x,
|
||||||
|
y: position.y - DROP_DISTANCE,
|
||||||
|
z: position.z
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function mouseReleaseEvent() {
|
||||||
|
if (isGrabbing) {
|
||||||
|
isGrabbing = false;
|
||||||
|
|
||||||
|
// only restore the original gravity if it's not zero. This is to avoid...
|
||||||
|
// 1. interface A grabs an entity and locally saves off its gravity
|
||||||
|
// 2. interface A sets the entity's gravity to zero
|
||||||
|
// 3. interface B grabs the entity and saves off its gravity (which is zero)
|
||||||
|
// 4. interface A releases the entity and puts the original gravity back
|
||||||
|
// 5. interface B releases the entity and puts the original gravity back (to zero)
|
||||||
|
if (!vectorIsZero(originalGravity)) {
|
||||||
|
Entities.editEntity(grabbedEntity, {
|
||||||
|
gravity: originalGravity
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Overlays.editOverlay(dropLine, {
|
||||||
|
visible: false
|
||||||
|
});
|
||||||
|
targetPosition = null;
|
||||||
|
|
||||||
|
Entities.deleteEntity(lineEntityID);
|
||||||
|
|
||||||
|
Audio.playSound(releaseSound, {
|
||||||
|
position: entityProps.position,
|
||||||
|
volume: VOLUME
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mouseMoveEvent(event) {
|
||||||
|
if (isGrabbing) {
|
||||||
|
// see if something added/restored gravity
|
||||||
|
var props = Entities.getEntityProperties(grabbedEntity);
|
||||||
|
if (!vectorIsZero(props.gravity)) {
|
||||||
|
originalGravity = props.gravity;
|
||||||
|
}
|
||||||
|
|
||||||
|
deltaMouse.x = event.x - prevMouse.x;
|
||||||
|
if (!moveUpDown) {
|
||||||
|
deltaMouse.z = event.y - prevMouse.y;
|
||||||
|
deltaMouse.y = 0;
|
||||||
|
} else {
|
||||||
|
deltaMouse.y = (event.y - prevMouse.y) * -1;
|
||||||
|
deltaMouse.z = 0;
|
||||||
|
}
|
||||||
|
// Update the target position by the amount the mouse moved
|
||||||
|
camYaw = Quat.safeEulerAngles(Camera.getOrientation()).y;
|
||||||
|
dPosition = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, camYaw, 0), deltaMouse);
|
||||||
|
if (!shouldRotate) {
|
||||||
|
// Adjust target position for the object by the mouse move
|
||||||
|
cameraEntityDistance = Vec3.distance(Camera.getPosition(), currentPosition);
|
||||||
|
// Scale distance we want to move by the distance from the camera to the grabbed object
|
||||||
|
// TODO: Correct SCREEN_TO_METERS to be correct for the actual FOV, resolution
|
||||||
|
targetPosition = Vec3.sum(targetPosition, Vec3.multiply(dPosition, cameraEntityDistance * SCREEN_TO_METERS));
|
||||||
|
} else if (shouldRotate) {
|
||||||
|
var transformedDeltaMouse = {
|
||||||
|
x: deltaMouse.z,
|
||||||
|
y: deltaMouse.x,
|
||||||
|
z: deltaMouse.y
|
||||||
|
};
|
||||||
|
transformedDeltaMouse = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, camYaw, 0), transformedDeltaMouse);
|
||||||
|
dQ = Quat.fromVec3Degrees(transformedDeltaMouse);
|
||||||
|
theta = 2 * Math.acos(dQ.w);
|
||||||
|
axisAngle = Quat.axis(dQ);
|
||||||
|
angularVelocity = Vec3.multiply((theta / dT), axisAngle);
|
||||||
|
}
|
||||||
|
|
||||||
|
Entities.editEntity(lineEntityID, {
|
||||||
|
position: nearLinePoint(targetPosition),
|
||||||
|
dimensions: Vec3.subtract(targetPosition, nearLinePoint(targetPosition))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
prevMouse.x = event.x;
|
||||||
|
prevMouse.y = event.y;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function keyReleaseEvent(event) {
|
||||||
|
if (event.text === "SHIFT") {
|
||||||
|
moveUpDown = false;
|
||||||
|
}
|
||||||
|
if (event.text === "SPACE") {
|
||||||
|
shouldRotate = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function keyPressEvent(event) {
|
||||||
|
if (event.text === "SHIFT") {
|
||||||
|
moveUpDown = true;
|
||||||
|
}
|
||||||
|
if (event.text === "SPACE") {
|
||||||
|
shouldRotate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(deltaTime) {
|
||||||
|
dT = deltaTime;
|
||||||
|
if (isGrabbing) {
|
||||||
|
|
||||||
|
entityProps = Entities.getEntityProperties(grabbedEntity);
|
||||||
|
currentPosition = entityProps.position;
|
||||||
|
currentVelocity = entityProps.velocity;
|
||||||
|
currentRotation = entityProps.rotation;
|
||||||
|
|
||||||
|
var dPosition = Vec3.subtract(targetPosition, currentPosition);
|
||||||
|
|
||||||
|
distanceToTarget = Vec3.length(dPosition);
|
||||||
|
if (distanceToTarget > CLOSE_ENOUGH) {
|
||||||
|
// compute current velocity in the direction we want to move
|
||||||
|
velocityTowardTarget = Vec3.dot(currentVelocity, Vec3.normalize(dPosition));
|
||||||
|
velocityTowardTarget = Vec3.multiply(Vec3.normalize(dPosition), velocityTowardTarget);
|
||||||
|
// compute the speed we would like to be going toward the target position
|
||||||
|
|
||||||
|
desiredVelocity = Vec3.multiply(dPosition, (1.0 / deltaTime) * SPRING_RATE);
|
||||||
|
// compute how much we want to add to the existing velocity
|
||||||
|
addedVelocity = Vec3.subtract(desiredVelocity, velocityTowardTarget);
|
||||||
|
// If target is too far, roll off the force as inverse square of distance
|
||||||
|
if (distanceToTarget / cameraEntityDistance > FULL_STRENGTH) {
|
||||||
|
addedVelocity = Vec3.multiply(addedVelocity, Math.pow(FULL_STRENGTH / distanceToTarget, 2.0));
|
||||||
|
}
|
||||||
|
newVelocity = Vec3.sum(currentVelocity, addedVelocity);
|
||||||
|
// Add Damping
|
||||||
|
newVelocity = Vec3.subtract(newVelocity, Vec3.multiply(newVelocity, DAMPING_RATE));
|
||||||
|
// Update entity
|
||||||
|
} else {
|
||||||
|
newVelocity = {x: 0, y: 0, z: 0};
|
||||||
|
}
|
||||||
|
if (shouldRotate) {
|
||||||
|
angularVelocity = Vec3.subtract(angularVelocity, Vec3.multiply(angularVelocity, ANGULAR_DAMPING_RATE));
|
||||||
|
} else {
|
||||||
|
angularVelocity = entityProps.angularVelocity;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entities.editEntity(grabbedEntity, {
|
||||||
|
position: currentPosition,
|
||||||
|
rotation: currentRotation,
|
||||||
|
velocity: newVelocity,
|
||||||
|
angularVelocity: angularVelocity
|
||||||
|
});
|
||||||
|
updateDropLine(targetPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||||
|
Controller.mousePressEvent.connect(mousePressEvent);
|
||||||
|
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
||||||
|
Controller.keyPressEvent.connect(keyPressEvent);
|
||||||
|
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
||||||
|
Script.update.connect(update);
|
|
@ -2456,7 +2456,7 @@ void Application::update(float deltaTime) {
|
||||||
|
|
||||||
if (_physicsEngine.hasOutgoingChanges()) {
|
if (_physicsEngine.hasOutgoingChanges()) {
|
||||||
_entitySimulation.lock();
|
_entitySimulation.lock();
|
||||||
_entitySimulation.handleOutgoingChanges(_physicsEngine.getOutgoingChanges());
|
_entitySimulation.handleOutgoingChanges(_physicsEngine.getOutgoingChanges(), _physicsEngine.getSessionID());
|
||||||
_entitySimulation.handleCollisionEvents(_physicsEngine.getCollisionEvents());
|
_entitySimulation.handleCollisionEvents(_physicsEngine.getCollisionEvents());
|
||||||
_entitySimulation.unlock();
|
_entitySimulation.unlock();
|
||||||
_physicsEngine.dumpStatsIfNecessary();
|
_physicsEngine.dumpStatsIfNecessary();
|
||||||
|
@ -3377,7 +3377,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs
|
||||||
auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
|
auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
|
||||||
DependencyManager::get<DeferredLightingEffect>()->setGlobalLight(skyStage->getSunLight()->getDirection(), skyStage->getSunLight()->getColor(), skyStage->getSunLight()->getIntensity(), skyStage->getSunLight()->getAmbientIntensity());
|
DependencyManager::get<DeferredLightingEffect>()->setGlobalLight(skyStage->getSunLight()->getDirection(), skyStage->getSunLight()->getColor(), skyStage->getSunLight()->getIntensity(), skyStage->getSunLight()->getAmbientIntensity());
|
||||||
DependencyManager::get<DeferredLightingEffect>()->setGlobalAtmosphere(skyStage->getAtmosphere());
|
DependencyManager::get<DeferredLightingEffect>()->setGlobalAtmosphere(skyStage->getAtmosphere());
|
||||||
// NOt yet DependencyManager::get<DeferredLightingEffect>()->setGlobalSkybox(skybox);
|
DependencyManager::get<DeferredLightingEffect>()->setGlobalSkybox(skybox);
|
||||||
|
|
||||||
PROFILE_RANGE("DeferredLighting");
|
PROFILE_RANGE("DeferredLighting");
|
||||||
PerformanceTimer perfTimer("lighting");
|
PerformanceTimer perfTimer("lighting");
|
||||||
|
|
|
@ -655,7 +655,9 @@ float Avatar::calculateDisplayNameScaleFactor(const glm::vec3& textPosition, boo
|
||||||
|
|
||||||
void Avatar::renderDisplayName() {
|
void Avatar::renderDisplayName() {
|
||||||
|
|
||||||
if (_displayName.isEmpty() || _displayNameAlpha == 0.0f) {
|
bool shouldShowReceiveStats = DependencyManager::get<AvatarManager>()->shouldShowReceiveStats();
|
||||||
|
|
||||||
|
if ((_displayName.isEmpty() && !shouldShowReceiveStats) || _displayNameAlpha == 0.0f) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -688,14 +690,40 @@ void Avatar::renderDisplayName() {
|
||||||
float scaleFactor = calculateDisplayNameScaleFactor(textPosition, inHMD);
|
float scaleFactor = calculateDisplayNameScaleFactor(textPosition, inHMD);
|
||||||
glScalef(scaleFactor, -scaleFactor, scaleFactor); // TextRenderer::draw paints the text upside down in y axis
|
glScalef(scaleFactor, -scaleFactor, scaleFactor); // TextRenderer::draw paints the text upside down in y axis
|
||||||
|
|
||||||
int text_x = -_displayNameBoundingRect.width() / 2;
|
// optionally render timing stats for this avatar with the display name
|
||||||
int text_y = -_displayNameBoundingRect.height() / 2;
|
QString renderedDisplayName = _displayName;
|
||||||
|
QRect nameDynamicRect = _displayNameBoundingRect;
|
||||||
|
|
||||||
|
if (shouldShowReceiveStats) {
|
||||||
|
float kilobitsPerSecond = getAverageBytesReceivedPerSecond() / (float) BYTES_PER_KILOBIT;
|
||||||
|
|
||||||
|
QString statsFormat = QString("(%1 Kbps, %2 Hz)");
|
||||||
|
|
||||||
|
if (!renderedDisplayName.isEmpty()) {
|
||||||
|
statsFormat.prepend(" - ");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString statsText = statsFormat.arg(QString::number(kilobitsPerSecond, 'f', 2)).arg(getReceiveRate());
|
||||||
|
glm::vec2 extent = textRenderer(DISPLAYNAME)->computeExtent(statsText);
|
||||||
|
|
||||||
|
// add the extent required for the stats to whatever was calculated for the display name
|
||||||
|
nameDynamicRect.setWidth(nameDynamicRect.width() + extent.x);
|
||||||
|
|
||||||
|
if (extent.y > nameDynamicRect.height()) {
|
||||||
|
nameDynamicRect.setHeight(extent.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderedDisplayName += statsText;
|
||||||
|
}
|
||||||
|
|
||||||
|
int text_x = -nameDynamicRect.width() / 2;
|
||||||
|
int text_y = -nameDynamicRect.height() / 2;
|
||||||
|
|
||||||
// draw a gray background
|
// draw a gray background
|
||||||
int left = text_x + _displayNameBoundingRect.x();
|
int left = text_x + nameDynamicRect.x();
|
||||||
int right = left + _displayNameBoundingRect.width();
|
int right = left + nameDynamicRect.width();
|
||||||
int bottom = text_y + _displayNameBoundingRect.y();
|
int bottom = text_y + nameDynamicRect.y();
|
||||||
int top = bottom + _displayNameBoundingRect.height();
|
int top = bottom + nameDynamicRect.height();
|
||||||
const int border = 8;
|
const int border = 8;
|
||||||
bottom -= border;
|
bottom -= border;
|
||||||
left -= border;
|
left -= border;
|
||||||
|
@ -711,17 +739,6 @@ void Avatar::renderDisplayName() {
|
||||||
|
|
||||||
glm::vec4 color(0.93f, 0.93f, 0.93f, _displayNameAlpha);
|
glm::vec4 color(0.93f, 0.93f, 0.93f, _displayNameAlpha);
|
||||||
|
|
||||||
// optionally render timing stats for this avatar with the display name
|
|
||||||
QString renderedDisplayName = _displayName;
|
|
||||||
|
|
||||||
if (DependencyManager::get<AvatarManager>()->shouldShowReceiveStats()) {
|
|
||||||
float kilobitsPerSecond = getAverageBytesReceivedPerSecond() / (float) BYTES_PER_KILOBIT;
|
|
||||||
|
|
||||||
renderedDisplayName += QString(" - (%1 Kbps, %2 Hz)")
|
|
||||||
.arg(QString::number(kilobitsPerSecond, 'f', 2))
|
|
||||||
.arg(getReceiveRate());
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit();
|
QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit();
|
||||||
|
|
||||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||||
|
@ -932,7 +949,7 @@ void Avatar::setDisplayName(const QString& displayName) {
|
||||||
AvatarData::setDisplayName(displayName);
|
AvatarData::setDisplayName(displayName);
|
||||||
// FIXME is this a sufficient replacement for tightBoundingRect?
|
// FIXME is this a sufficient replacement for tightBoundingRect?
|
||||||
glm::vec2 extent = textRenderer(DISPLAYNAME)->computeExtent(displayName);
|
glm::vec2 extent = textRenderer(DISPLAYNAME)->computeExtent(displayName);
|
||||||
_displayNameBoundingRect = QRect(QPoint(0, 0), QPoint((int)extent.x, (int)extent.y));
|
_displayNameBoundingRect = QRect(0, 0, (int)extent.x, (int)extent.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Avatar::setBillboard(const QByteArray& billboard) {
|
void Avatar::setBillboard(const QByteArray& billboard) {
|
||||||
|
|
|
@ -850,9 +850,18 @@ RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(cons
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityScriptingInterface) {
|
void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityScriptingInterface) {
|
||||||
connect(this, &EntityTreeRenderer::mousePressOnEntity, entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity);
|
connect(this, &EntityTreeRenderer::mousePressOnEntity, entityScriptingInterface,
|
||||||
connect(this, &EntityTreeRenderer::mouseMoveOnEntity, entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity);
|
[=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId){
|
||||||
connect(this, &EntityTreeRenderer::mouseReleaseOnEntity, entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity);
|
entityScriptingInterface->mousePressOnEntity(intersection.entityID, MouseEvent(*event, deviceId));
|
||||||
|
});
|
||||||
|
connect(this, &EntityTreeRenderer::mouseMoveOnEntity, entityScriptingInterface,
|
||||||
|
[=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId) {
|
||||||
|
entityScriptingInterface->mouseMoveOnEntity(intersection.entityID, MouseEvent(*event, deviceId));
|
||||||
|
});
|
||||||
|
connect(this, &EntityTreeRenderer::mouseReleaseOnEntity, entityScriptingInterface,
|
||||||
|
[=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId) {
|
||||||
|
entityScriptingInterface->mouseReleaseOnEntity(intersection.entityID, MouseEvent(*event, deviceId));
|
||||||
|
});
|
||||||
|
|
||||||
connect(this, &EntityTreeRenderer::clickDownOnEntity, entityScriptingInterface, &EntityScriptingInterface::clickDownOnEntity);
|
connect(this, &EntityTreeRenderer::clickDownOnEntity, entityScriptingInterface, &EntityScriptingInterface::clickDownOnEntity);
|
||||||
connect(this, &EntityTreeRenderer::holdingClickOnEntity, entityScriptingInterface, &EntityScriptingInterface::holdingClickOnEntity);
|
connect(this, &EntityTreeRenderer::holdingClickOnEntity, entityScriptingInterface, &EntityScriptingInterface::holdingClickOnEntity);
|
||||||
|
@ -900,7 +909,7 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int device
|
||||||
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking);
|
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking);
|
||||||
if (rayPickResult.intersects) {
|
if (rayPickResult.intersects) {
|
||||||
//qCDebug(entitiesrenderer) << "mousePressEvent over entity:" << rayPickResult.entityID;
|
//qCDebug(entitiesrenderer) << "mousePressEvent over entity:" << rayPickResult.entityID;
|
||||||
emit mousePressOnEntity(rayPickResult.entityID, MouseEvent(*event, deviceID));
|
emit mousePressOnEntity(rayPickResult, event, deviceID);
|
||||||
|
|
||||||
QScriptValueList entityScriptArgs = createMouseEventArgs(rayPickResult.entityID, event, deviceID);
|
QScriptValueList entityScriptArgs = createMouseEventArgs(rayPickResult.entityID, event, deviceID);
|
||||||
QScriptValue entityScript = loadEntityScript(rayPickResult.entity);
|
QScriptValue entityScript = loadEntityScript(rayPickResult.entity);
|
||||||
|
@ -930,7 +939,7 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int devi
|
||||||
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking);
|
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking);
|
||||||
if (rayPickResult.intersects) {
|
if (rayPickResult.intersects) {
|
||||||
//qCDebug(entitiesrenderer) << "mouseReleaseEvent over entity:" << rayPickResult.entityID;
|
//qCDebug(entitiesrenderer) << "mouseReleaseEvent over entity:" << rayPickResult.entityID;
|
||||||
emit mouseReleaseOnEntity(rayPickResult.entityID, MouseEvent(*event, deviceID));
|
emit mouseReleaseOnEntity(rayPickResult, event, deviceID);
|
||||||
|
|
||||||
QScriptValueList entityScriptArgs = createMouseEventArgs(rayPickResult.entityID, event, deviceID);
|
QScriptValueList entityScriptArgs = createMouseEventArgs(rayPickResult.entityID, event, deviceID);
|
||||||
QScriptValue entityScript = loadEntityScript(rayPickResult.entity);
|
QScriptValue entityScript = loadEntityScript(rayPickResult.entity);
|
||||||
|
@ -979,7 +988,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI
|
||||||
}
|
}
|
||||||
|
|
||||||
//qCDebug(entitiesrenderer) << "mouseMoveEvent over entity:" << rayPickResult.entityID;
|
//qCDebug(entitiesrenderer) << "mouseMoveEvent over entity:" << rayPickResult.entityID;
|
||||||
emit mouseMoveOnEntity(rayPickResult.entityID, MouseEvent(*event, deviceID));
|
emit mouseMoveOnEntity(rayPickResult, event, deviceID);
|
||||||
if (entityScript.property("mouseMoveOnEntity").isValid()) {
|
if (entityScript.property("mouseMoveOnEntity").isValid()) {
|
||||||
entityScript.property("mouseMoveOnEntity").call(entityScript, entityScriptArgs);
|
entityScript.property("mouseMoveOnEntity").call(entityScript, entityScriptArgs);
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,9 +93,9 @@ public:
|
||||||
virtual void errorInLoadingScript(const QUrl& url);
|
virtual void errorInLoadingScript(const QUrl& url);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void mousePressOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
void mousePressOnEntity(const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event, unsigned int deviceId);
|
||||||
void mouseMoveOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
void mouseMoveOnEntity(const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event, unsigned int deviceId);
|
||||||
void mouseReleaseOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
void mouseReleaseOnEntity(const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event, unsigned int deviceId);
|
||||||
|
|
||||||
void clickDownOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
void clickDownOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||||
void holdingClickOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
void holdingClickOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
#include <TextureCache.h>
|
#include <TextureCache.h>
|
||||||
#include <gpu/GLBackend.h>
|
#include <gpu/GLBackend.h>
|
||||||
|
|
||||||
|
#include "EntityTreeRenderer.h"
|
||||||
|
|
||||||
const int FIXED_FONT_POINT_SIZE = 40;
|
const int FIXED_FONT_POINT_SIZE = 40;
|
||||||
const float DPI = 30.47;
|
const float DPI = 30.47;
|
||||||
const float METERS_TO_INCHES = 39.3701;
|
const float METERS_TO_INCHES = 39.3701;
|
||||||
|
@ -63,6 +65,7 @@ RenderableWebEntityItem::~RenderableWebEntityItem() {
|
||||||
void RenderableWebEntityItem::render(RenderArgs* args) {
|
void RenderableWebEntityItem::render(RenderArgs* args) {
|
||||||
QOpenGLContext * currentContext = QOpenGLContext::currentContext();
|
QOpenGLContext * currentContext = QOpenGLContext::currentContext();
|
||||||
QSurface * currentSurface = currentContext->surface();
|
QSurface * currentSurface = currentContext->surface();
|
||||||
|
|
||||||
if (!_webSurface) {
|
if (!_webSurface) {
|
||||||
_webSurface = new OffscreenQmlSurface();
|
_webSurface = new OffscreenQmlSurface();
|
||||||
_webSurface->create(currentContext);
|
_webSurface->create(currentContext);
|
||||||
|
@ -89,6 +92,52 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
|
||||||
_webSurface->doneCurrent();
|
_webSurface->doneCurrent();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
auto forwardMouseEvent = [=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event, unsigned int deviceId) {
|
||||||
|
// Ignore mouse interaction if we're locked
|
||||||
|
if (this->getLocked()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intersection.entityID.id == getID()) {
|
||||||
|
if (event->button() == Qt::MouseButton::RightButton) {
|
||||||
|
if (event->type() == QEvent::MouseButtonRelease) {
|
||||||
|
AbstractViewStateInterface::instance()->postLambdaEvent([this] {
|
||||||
|
QMetaObject::invokeMethod(_webSurface->getRootItem(), "goBack");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event->button() == Qt::MouseButton::MiddleButton) {
|
||||||
|
if (event->type() == QEvent::MouseButtonRelease) {
|
||||||
|
AbstractViewStateInterface::instance()->postLambdaEvent([this] {
|
||||||
|
_webSurface->getRootItem()->setProperty("url", _sourceUrl);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map the intersection point to an actual offscreen pixel
|
||||||
|
glm::vec3 point = intersection.intersection;
|
||||||
|
point -= getPosition();
|
||||||
|
point = glm::inverse(getRotation()) * point;
|
||||||
|
point /= _dimensions;
|
||||||
|
point += 0.5f;
|
||||||
|
point.y = 1.0f - point.y;
|
||||||
|
point *= _dimensions * METERS_TO_INCHES * DPI;
|
||||||
|
// Forward the mouse event.
|
||||||
|
QMouseEvent mappedEvent(event->type(),
|
||||||
|
QPoint((int)point.x, (int)point.y),
|
||||||
|
event->screenPos(), event->button(),
|
||||||
|
event->buttons(), event->modifiers());
|
||||||
|
QCoreApplication::sendEvent(_webSurface->getWindow(), &mappedEvent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
EntityTreeRenderer* renderer = static_cast<EntityTreeRenderer*>(args->_renderer);
|
||||||
|
QObject::connect(renderer, &EntityTreeRenderer::mousePressOnEntity, forwardMouseEvent);
|
||||||
|
QObject::connect(renderer, &EntityTreeRenderer::mouseReleaseOnEntity, forwardMouseEvent);
|
||||||
|
QObject::connect(renderer, &EntityTreeRenderer::mouseMoveOnEntity, forwardMouseEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec2 dims = glm::vec2(_dimensions);
|
glm::vec2 dims = glm::vec2(_dimensions);
|
||||||
|
@ -106,8 +155,6 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
|
||||||
glm::vec3 halfDimensions = dimensions / 2.0f;
|
glm::vec3 halfDimensions = dimensions / 2.0f;
|
||||||
glm::quat rotation = getRotation();
|
glm::quat rotation = getRotation();
|
||||||
|
|
||||||
//qCDebug(entitytree) << "RenderableWebEntityItem::render() id:" << getEntityItemID() << "text:" << getText();
|
|
||||||
|
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
{
|
{
|
||||||
glTranslatef(position.x, position.y, position.z);
|
glTranslatef(position.x, position.y, position.z);
|
||||||
|
|
|
@ -577,7 +577,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
||||||
READ_ENTITY_PROPERTY(PROP_USER_DATA, QString, setUserData);
|
READ_ENTITY_PROPERTY(PROP_USER_DATA, QString, setUserData);
|
||||||
|
|
||||||
if (args.bitstreamVersion >= VERSION_ENTITIES_HAVE_ACCELERATION) {
|
if (args.bitstreamVersion >= VERSION_ENTITIES_HAVE_ACCELERATION) {
|
||||||
READ_ENTITY_PROPERTY(PROP_SIMULATOR_ID, QUuid, setSimulatorID);
|
READ_ENTITY_PROPERTY(PROP_SIMULATOR_ID, QUuid, updateSimulatorID);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_MARKETPLACE_ID) {
|
if (args.bitstreamVersion >= VERSION_ENTITIES_HAS_MARKETPLACE_ID) {
|
||||||
|
@ -947,7 +947,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionsWillMove, updateCollisionsWillMove);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionsWillMove, updateCollisionsWillMove);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(locked, setLocked);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(locked, setLocked);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(simulatorID, setSimulatorID);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(simulatorID, updateSimulatorID);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(marketplaceID, setMarketplaceID);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(marketplaceID, setMarketplaceID);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(name, setName);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(name, setName);
|
||||||
|
|
||||||
|
@ -1192,18 +1192,18 @@ void EntityItem::updateVelocityInDomainUnits(const glm::vec3& value) {
|
||||||
void EntityItem::updateVelocity(const glm::vec3& value) {
|
void EntityItem::updateVelocity(const glm::vec3& value) {
|
||||||
auto delta = glm::distance(_velocity, value);
|
auto delta = glm::distance(_velocity, value);
|
||||||
if (delta > IGNORE_LINEAR_VELOCITY_DELTA) {
|
if (delta > IGNORE_LINEAR_VELOCITY_DELTA) {
|
||||||
|
_dirtyFlags |= EntityItem::DIRTY_LINEAR_VELOCITY;
|
||||||
const float MIN_LINEAR_SPEED = 0.001f;
|
const float MIN_LINEAR_SPEED = 0.001f;
|
||||||
if (glm::length(value) < MIN_LINEAR_SPEED) {
|
if (glm::length(value) < MIN_LINEAR_SPEED) {
|
||||||
_velocity = ENTITY_ITEM_ZERO_VEC3;
|
_velocity = ENTITY_ITEM_ZERO_VEC3;
|
||||||
} else {
|
} else {
|
||||||
_velocity = value;
|
_velocity = value;
|
||||||
}
|
// only activate when setting non-zero velocity
|
||||||
_dirtyFlags |= EntityItem::DIRTY_LINEAR_VELOCITY;
|
|
||||||
|
|
||||||
if (delta > ACTIVATION_LINEAR_VELOCITY_DELTA) {
|
if (delta > ACTIVATION_LINEAR_VELOCITY_DELTA) {
|
||||||
_dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
|
_dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::updateDamping(float value) {
|
void EntityItem::updateDamping(float value) {
|
||||||
|
@ -1239,11 +1239,12 @@ void EntityItem::updateAngularVelocity(const glm::vec3& value) {
|
||||||
_angularVelocity = ENTITY_ITEM_ZERO_VEC3;
|
_angularVelocity = ENTITY_ITEM_ZERO_VEC3;
|
||||||
} else {
|
} else {
|
||||||
_angularVelocity = value;
|
_angularVelocity = value;
|
||||||
}
|
// only activate when setting non-zero velocity
|
||||||
if (delta > ACTIVATION_ANGULAR_VELOCITY_DELTA) {
|
if (delta > ACTIVATION_ANGULAR_VELOCITY_DELTA) {
|
||||||
_dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
|
_dirtyFlags |= EntityItem::DIRTY_PHYSICS_ACTIVATION;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::updateAngularDamping(float value) {
|
void EntityItem::updateAngularDamping(float value) {
|
||||||
|
@ -1302,8 +1303,14 @@ void EntityItem::updateLifetime(float value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::setSimulatorID(const QUuid& value) {
|
void EntityItem::setSimulatorID(const QUuid& value) {
|
||||||
|
_simulatorID = value;
|
||||||
|
_simulatorIDChangedTime = usecTimestampNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityItem::updateSimulatorID(const QUuid& value) {
|
||||||
if (_simulatorID != value) {
|
if (_simulatorID != value) {
|
||||||
_simulatorID = value;
|
_simulatorID = value;
|
||||||
_simulatorIDChangedTime = usecTimestampNow();
|
_simulatorIDChangedTime = usecTimestampNow();
|
||||||
|
_dirtyFlags |= EntityItem::DIRTY_SIMULATOR_ID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,6 +82,7 @@ public:
|
||||||
DIRTY_UPDATEABLE = 0x0200,
|
DIRTY_UPDATEABLE = 0x0200,
|
||||||
DIRTY_MATERIAL = 0x00400,
|
DIRTY_MATERIAL = 0x00400,
|
||||||
DIRTY_PHYSICS_ACTIVATION = 0x0800, // we want to activate the object
|
DIRTY_PHYSICS_ACTIVATION = 0x0800, // we want to activate the object
|
||||||
|
DIRTY_SIMULATOR_ID = 0x1000,
|
||||||
DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION,
|
DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION,
|
||||||
DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY
|
DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY
|
||||||
};
|
};
|
||||||
|
@ -289,6 +290,7 @@ public:
|
||||||
|
|
||||||
QUuid getSimulatorID() const { return _simulatorID; }
|
QUuid getSimulatorID() const { return _simulatorID; }
|
||||||
void setSimulatorID(const QUuid& value);
|
void setSimulatorID(const QUuid& value);
|
||||||
|
void updateSimulatorID(const QUuid& value);
|
||||||
quint64 getSimulatorIDChangedTime() const { return _simulatorIDChangedTime; }
|
quint64 getSimulatorIDChangedTime() const { return _simulatorIDChangedTime; }
|
||||||
|
|
||||||
const QString& getMarketplaceID() const { return _marketplaceID; }
|
const QString& getMarketplaceID() const { return _marketplaceID; }
|
||||||
|
|
|
@ -503,7 +503,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) {
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(locked, bool, setLocked);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(locked, bool, setLocked);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(textures, QString, setTextures);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(textures, QString, setTextures);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(userData, QString, setUserData);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(userData, QString, setUserData);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(simulatorID, QUuid, setSimulatorID);
|
//COPY_PROPERTY_FROM_QSCRIPTVALUE(simulatorID, QUuid, setSimulatorID); DO NOT accept this info from QScriptValue
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(text, QString, setText);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(text, QString, setText);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(lineHeight, float, setLineHeight);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(lineHeight, float, setLineHeight);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(textColor, xColor, setTextColor);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(textColor, xColor, setTextColor);
|
||||||
|
|
|
@ -61,13 +61,12 @@ void EntityScriptingInterface::setEntityTree(EntityTree* modelTree) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void bidForSimulationOwnership(EntityItemProperties& properties) {
|
||||||
|
// We make a bid for simulation ownership by declaring our sessionID as simulation owner
|
||||||
void setSimId(EntityItemProperties& propertiesWithSimID, EntityItem* entity) {
|
// in the outgoing properties. The EntityServer may accept the bid or might not.
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
const QUuid myNodeID = nodeList->getSessionUUID();
|
const QUuid myNodeID = nodeList->getSessionUUID();
|
||||||
propertiesWithSimID.setSimulatorID(myNodeID);
|
properties.setSimulatorID(myNodeID);
|
||||||
entity->setSimulatorID(myNodeID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -89,7 +88,7 @@ EntityItemID EntityScriptingInterface::addEntity(const EntityItemProperties& pro
|
||||||
entity->setLastBroadcast(usecTimestampNow());
|
entity->setLastBroadcast(usecTimestampNow());
|
||||||
if (entity) {
|
if (entity) {
|
||||||
// This Node is creating a new object. If it's in motion, set this Node as the simulator.
|
// This Node is creating a new object. If it's in motion, set this Node as the simulator.
|
||||||
setSimId(propertiesWithSimID, entity);
|
bidForSimulationOwnership(propertiesWithSimID);
|
||||||
} else {
|
} else {
|
||||||
qCDebug(entities) << "script failed to add new Entity to local Octree";
|
qCDebug(entities) << "script failed to add new Entity to local Octree";
|
||||||
success = false;
|
success = false;
|
||||||
|
@ -163,29 +162,31 @@ EntityItemID EntityScriptingInterface::editEntity(EntityItemID entityID, const E
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityItemProperties propertiesWithSimID = properties;
|
|
||||||
|
|
||||||
// If we have a local entity tree set, then also update it. We can do this even if we don't know
|
// If we have a local entity tree set, then also update it. We can do this even if we don't know
|
||||||
// the actual id, because we can edit out local entities just with creatorTokenID
|
// the actual id, because we can edit out local entities just with creatorTokenID
|
||||||
if (_entityTree) {
|
if (_entityTree) {
|
||||||
_entityTree->lockForWrite();
|
_entityTree->lockForWrite();
|
||||||
_entityTree->updateEntity(entityID, propertiesWithSimID, canAdjustLocks());
|
_entityTree->updateEntity(entityID, properties);
|
||||||
_entityTree->unlock();
|
_entityTree->unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
// if at this point, we know the id, send the update to the entity server
|
// if at this point, we know the id, send the update to the entity server
|
||||||
if (entityID.isKnownID) {
|
if (entityID.isKnownID) {
|
||||||
// make sure the properties has a type, so that the encode can know which properties to include
|
// make sure the properties has a type, so that the encode can know which properties to include
|
||||||
if (propertiesWithSimID.getType() == EntityTypes::Unknown) {
|
if (properties.getType() == EntityTypes::Unknown) {
|
||||||
EntityItem* entity = _entityTree->findEntityByEntityItemID(entityID);
|
EntityItem* entity = _entityTree->findEntityByEntityItemID(entityID);
|
||||||
if (entity) {
|
if (entity) {
|
||||||
|
// we need to change the outgoing properties, so we make a copy, modify, and send.
|
||||||
|
EntityItemProperties modifiedProperties = properties;
|
||||||
entity->setLastBroadcast(usecTimestampNow());
|
entity->setLastBroadcast(usecTimestampNow());
|
||||||
propertiesWithSimID.setType(entity->getType());
|
modifiedProperties.setType(entity->getType());
|
||||||
setSimId(propertiesWithSimID, entity);
|
bidForSimulationOwnership(modifiedProperties);
|
||||||
|
queueEntityMessage(PacketTypeEntityAddOrEdit, entityID, modifiedProperties);
|
||||||
|
return entityID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
queueEntityMessage(PacketTypeEntityAddOrEdit, entityID, propertiesWithSimID);
|
queueEntityMessage(PacketTypeEntityAddOrEdit, entityID, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
return entityID;
|
return entityID;
|
||||||
|
|
|
@ -37,7 +37,8 @@ const int DIRTY_SIMULATION_FLAGS =
|
||||||
EntityItem::DIRTY_SHAPE |
|
EntityItem::DIRTY_SHAPE |
|
||||||
EntityItem::DIRTY_LIFETIME |
|
EntityItem::DIRTY_LIFETIME |
|
||||||
EntityItem::DIRTY_UPDATEABLE |
|
EntityItem::DIRTY_UPDATEABLE |
|
||||||
EntityItem::DIRTY_MATERIAL;
|
EntityItem::DIRTY_MATERIAL |
|
||||||
|
EntityItem::DIRTY_SIMULATOR_ID;
|
||||||
|
|
||||||
class EntitySimulation : public QObject {
|
class EntitySimulation : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
|
@ -86,7 +86,7 @@ void EntityTree::postAddEntity(EntityItem* entity) {
|
||||||
emit addingEntity(entity->getEntityItemID());
|
emit addingEntity(entity->getEntityItemID());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, bool allowLockChange) {
|
bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, const SharedNodePointer& senderNode) {
|
||||||
EntityTreeElement* containingElement = getContainingElement(entityID);
|
EntityTreeElement* containingElement = getContainingElement(entityID);
|
||||||
if (!containingElement) {
|
if (!containingElement) {
|
||||||
qCDebug(entities) << "UNEXPECTED!!!! EntityTree::updateEntity() entityID doesn't exist!!! entityID=" << entityID;
|
qCDebug(entities) << "UNEXPECTED!!!! EntityTree::updateEntity() entityID doesn't exist!!! entityID=" << entityID;
|
||||||
|
@ -99,22 +99,34 @@ bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProp
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return updateEntityWithElement(existingEntity, properties, containingElement, allowLockChange);
|
return updateEntityWithElement(existingEntity, properties, containingElement, senderNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityTree::updateEntity(EntityItem* entity, const EntityItemProperties& properties, bool allowLockChange) {
|
bool EntityTree::updateEntity(EntityItem* entity, const EntityItemProperties& properties, const SharedNodePointer& senderNode) {
|
||||||
EntityTreeElement* containingElement = getContainingElement(entity->getEntityItemID());
|
EntityTreeElement* containingElement = getContainingElement(entity->getEntityItemID());
|
||||||
if (!containingElement) {
|
if (!containingElement) {
|
||||||
qCDebug(entities) << "UNEXPECTED!!!! EntityTree::updateEntity() entity-->element lookup failed!!! entityID="
|
qCDebug(entities) << "UNEXPECTED!!!! EntityTree::updateEntity() entity-->element lookup failed!!! entityID="
|
||||||
<< entity->getEntityItemID();
|
<< entity->getEntityItemID();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return updateEntityWithElement(entity, properties, containingElement, allowLockChange);
|
return updateEntityWithElement(entity, properties, containingElement, senderNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityTree::updateEntityWithElement(EntityItem* entity, const EntityItemProperties& origProperties,
|
bool EntityTree::updateEntityWithElement(EntityItem* entity, const EntityItemProperties& origProperties,
|
||||||
EntityTreeElement* containingElement, bool allowLockChange) {
|
EntityTreeElement* containingElement, const SharedNodePointer& senderNode) {
|
||||||
EntityItemProperties properties = origProperties;
|
EntityItemProperties properties = origProperties;
|
||||||
|
|
||||||
|
bool allowLockChange;
|
||||||
|
QUuid senderID;
|
||||||
|
if (senderNode.isNull()) {
|
||||||
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
allowLockChange = nodeList->getThisNodeCanAdjustLocks();
|
||||||
|
senderID = nodeList->getSessionUUID();
|
||||||
|
} else {
|
||||||
|
allowLockChange = senderNode->getCanAdjustLocks();
|
||||||
|
senderID = senderNode->getUUID();
|
||||||
|
}
|
||||||
|
|
||||||
if (!allowLockChange && (entity->getLocked() != properties.getLocked())) {
|
if (!allowLockChange && (entity->getLocked() != properties.getLocked())) {
|
||||||
qCDebug(entities) << "Refusing disallowed lock adjustment.";
|
qCDebug(entities) << "Refusing disallowed lock adjustment.";
|
||||||
return false;
|
return false;
|
||||||
|
@ -134,22 +146,41 @@ bool EntityTree::updateEntityWithElement(EntityItem* entity, const EntityItemPro
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (properties.simulatorIDChanged() &&
|
if (getIsServer()) {
|
||||||
!entity->getSimulatorID().isNull() &&
|
bool simulationBlocked = !entity->getSimulatorID().isNull();
|
||||||
properties.getSimulatorID() != entity->getSimulatorID()) {
|
if (properties.simulatorIDChanged()) {
|
||||||
// A Node is trying to take ownership of the simulation of this entity from another Node. Only allow this
|
QUuid submittedID = properties.getSimulatorID();
|
||||||
// if ownership hasn't recently changed.
|
// a legit interface will only submit their own ID or NULL:
|
||||||
if (usecTimestampNow() - entity->getSimulatorIDChangedTime() < SIMULATOR_CHANGE_LOCKOUT_PERIOD) {
|
if (submittedID.isNull()) {
|
||||||
qCDebug(entities) << "simulator_change_lockout_period:"
|
if (entity->getSimulatorID() == senderID) {
|
||||||
<< entity->getSimulatorID() << "to" << properties.getSimulatorID();
|
// We only allow the simulation owner to clear their own simulationID's.
|
||||||
|
simulationBlocked = false;
|
||||||
|
}
|
||||||
|
// else: We assume the sender really did believe it was the simulation owner when it sent
|
||||||
|
} else if (submittedID == senderID) {
|
||||||
|
// the sender is trying to take or continue ownership
|
||||||
|
if (entity->getSimulatorID().isNull() || entity->getSimulatorID() == senderID) {
|
||||||
|
simulationBlocked = false;
|
||||||
|
} else {
|
||||||
|
// the sender is trying to steal ownership from another simulator
|
||||||
|
// so we apply the ownership change filter
|
||||||
|
if (usecTimestampNow() - entity->getSimulatorIDChangedTime() > SIMULATOR_CHANGE_LOCKOUT_PERIOD) {
|
||||||
|
simulationBlocked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// the entire update is suspect --> ignore it
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (simulationBlocked) {
|
||||||
// squash the physics-related changes.
|
// squash the physics-related changes.
|
||||||
properties.setSimulatorIDChanged(false);
|
properties.setSimulatorIDChanged(false);
|
||||||
properties.setPositionChanged(false);
|
properties.setPositionChanged(false);
|
||||||
properties.setRotationChanged(false);
|
properties.setRotationChanged(false);
|
||||||
} else {
|
|
||||||
qCDebug(entities) << "allowing simulatorID change";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// else client accepts what the server says
|
||||||
|
|
||||||
QString entityScriptBefore = entity->getScript();
|
QString entityScriptBefore = entity->getScript();
|
||||||
uint32_t preFlags = entity->getDirtyFlags();
|
uint32_t preFlags = entity->getDirtyFlags();
|
||||||
|
@ -664,7 +695,7 @@ int EntityTree::processEditPacketData(PacketType packetType, const unsigned char
|
||||||
qCDebug(entities) << "User [" << senderNode->getUUID() << "] editing entity. ID:" << entityItemID;
|
qCDebug(entities) << "User [" << senderNode->getUUID() << "] editing entity. ID:" << entityItemID;
|
||||||
qCDebug(entities) << " properties:" << properties;
|
qCDebug(entities) << " properties:" << properties;
|
||||||
}
|
}
|
||||||
updateEntity(entityItemID, properties, senderNode->getCanAdjustLocks());
|
updateEntity(entityItemID, properties, senderNode);
|
||||||
existingEntity->markAsChangedOnServer();
|
existingEntity->markAsChangedOnServer();
|
||||||
} else {
|
} else {
|
||||||
qCDebug(entities) << "User attempted to edit an unknown entity. ID:" << entityItemID;
|
qCDebug(entities) << "User attempted to edit an unknown entity. ID:" << entityItemID;
|
||||||
|
|
|
@ -88,10 +88,10 @@ public:
|
||||||
EntityItem* addEntity(const EntityItemID& entityID, const EntityItemProperties& properties);
|
EntityItem* addEntity(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||||
|
|
||||||
// use this method if you only know the entityID
|
// use this method if you only know the entityID
|
||||||
bool updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, bool allowLockChange);
|
bool updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, const SharedNodePointer& senderNode = SharedNodePointer(nullptr));
|
||||||
|
|
||||||
// use this method if you have a pointer to the entity (avoid an extra entity lookup)
|
// use this method if you have a pointer to the entity (avoid an extra entity lookup)
|
||||||
bool updateEntity(EntityItem* entity, const EntityItemProperties& properties, bool allowLockChange);
|
bool updateEntity(EntityItem* entity, const EntityItemProperties& properties, const SharedNodePointer& senderNode = SharedNodePointer(nullptr));
|
||||||
|
|
||||||
void deleteEntity(const EntityItemID& entityID, bool force = false, bool ignoreWarnings = false);
|
void deleteEntity(const EntityItemID& entityID, bool force = false, bool ignoreWarnings = false);
|
||||||
void deleteEntities(QSet<EntityItemID> entityIDs, bool force = false, bool ignoreWarnings = false);
|
void deleteEntities(QSet<EntityItemID> entityIDs, bool force = false, bool ignoreWarnings = false);
|
||||||
|
@ -180,7 +180,8 @@ private:
|
||||||
|
|
||||||
void processRemovedEntities(const DeleteEntityOperator& theOperator);
|
void processRemovedEntities(const DeleteEntityOperator& theOperator);
|
||||||
bool updateEntityWithElement(EntityItem* entity, const EntityItemProperties& properties,
|
bool updateEntityWithElement(EntityItem* entity, const EntityItemProperties& properties,
|
||||||
EntityTreeElement* containingElement, bool allowLockChange);
|
EntityTreeElement* containingElement,
|
||||||
|
const SharedNodePointer& senderNode = SharedNodePointer(nullptr));
|
||||||
static bool findNearPointOperation(OctreeElement* element, void* extraData);
|
static bool findNearPointOperation(OctreeElement* element, void* extraData);
|
||||||
static bool findInSphereOperation(OctreeElement* element, void* extraData);
|
static bool findInSphereOperation(OctreeElement* element, void* extraData);
|
||||||
static bool findInCubeOperation(OctreeElement* element, void* extraData);
|
static bool findInCubeOperation(OctreeElement* element, void* extraData);
|
||||||
|
|
|
@ -32,6 +32,7 @@ void SimpleEntitySimulation::updateEntitiesInternal(const quint64& now) {
|
||||||
SharedNodePointer ownerNode = nodeList->nodeWithUUID(entity->getSimulatorID());
|
SharedNodePointer ownerNode = nodeList->nodeWithUUID(entity->getSimulatorID());
|
||||||
if (ownerNode.isNull() || !ownerNode->isAlive()) {
|
if (ownerNode.isNull() || !ownerNode->isAlive()) {
|
||||||
qCDebug(entities) << "auto-removing simulation owner" << entity->getSimulatorID();
|
qCDebug(entities) << "auto-removing simulation owner" << entity->getSimulatorID();
|
||||||
|
// TODO: zero velocities when we clear simulatorID?
|
||||||
entity->setSimulatorID(QUuid());
|
entity->setSimulatorID(QUuid());
|
||||||
itemItr = _hasSimulationOwnerEntities.erase(itemItr);
|
itemItr = _hasSimulationOwnerEntities.erase(itemItr);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
#include <OctalCode.h>
|
#include <OctalCode.h>
|
||||||
#include <Shape.h>
|
#include <Shape.h>
|
||||||
#include <gpu/Format.h>
|
#include <gpu/Format.h>
|
||||||
|
#include <LogHandler.h>
|
||||||
|
|
||||||
#include "FBXReader.h"
|
#include "FBXReader.h"
|
||||||
#include "ModelFormatLogging.h"
|
#include "ModelFormatLogging.h"
|
||||||
|
@ -1281,9 +1281,11 @@ FBXLight extractLight(const FBXNode& object) {
|
||||||
|
|
||||||
#if USE_MODEL_MESH
|
#if USE_MODEL_MESH
|
||||||
void buildModelMesh(ExtractedMesh& extracted) {
|
void buildModelMesh(ExtractedMesh& extracted) {
|
||||||
|
static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex("buildModelMesh failed -- .*");
|
||||||
|
|
||||||
if (extracted.mesh.vertices.size() == 0) {
|
if (extracted.mesh.vertices.size() == 0) {
|
||||||
extracted.mesh._mesh = model::Mesh();
|
extracted.mesh._mesh = model::Mesh();
|
||||||
qDebug() << "buildModelMesh failed -- no vertices";
|
qCDebug(modelformat) << "buildModelMesh failed -- no vertices";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
FBXMesh& fbxMesh = extracted.mesh;
|
FBXMesh& fbxMesh = extracted.mesh;
|
||||||
|
@ -1370,7 +1372,7 @@ void buildModelMesh(ExtractedMesh& extracted) {
|
||||||
|
|
||||||
if (! totalIndices) {
|
if (! totalIndices) {
|
||||||
extracted.mesh._mesh = model::Mesh();
|
extracted.mesh._mesh = model::Mesh();
|
||||||
qDebug() << "buildModelMesh failed -- no indices";
|
qCDebug(modelformat) << "buildModelMesh failed -- no indices";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1410,7 +1412,7 @@ void buildModelMesh(ExtractedMesh& extracted) {
|
||||||
mesh.setPartBuffer(pbv);
|
mesh.setPartBuffer(pbv);
|
||||||
} else {
|
} else {
|
||||||
extracted.mesh._mesh = model::Mesh();
|
extracted.mesh._mesh = model::Mesh();
|
||||||
qDebug() << "buildModelMesh failed -- no parts";
|
qCDebug(modelformat) << "buildModelMesh failed -- no parts";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@ const Texture::PixelsPointer Texture::Storage::getMipFace(uint16 level, uint8 fa
|
||||||
|
|
||||||
void Texture::Storage::notifyMipFaceGPULoaded(uint16 level, uint8 face) const {
|
void Texture::Storage::notifyMipFaceGPULoaded(uint16 level, uint8 face) const {
|
||||||
PixelsPointer mipFace = getMipFace(level, face);
|
PixelsPointer mipFace = getMipFace(level, face);
|
||||||
if (mipFace) {
|
if (mipFace && (_type != TEX_CUBE)) {
|
||||||
mipFace->_isGPULoaded = true;
|
mipFace->_isGPULoaded = true;
|
||||||
mipFace->_sysmem.resize(0);
|
mipFace->_sysmem.resize(0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,7 +127,7 @@ public:
|
||||||
CUBE_FACE_LEFT_NEG_X,
|
CUBE_FACE_LEFT_NEG_X,
|
||||||
CUBE_FACE_TOP_POS_Y,
|
CUBE_FACE_TOP_POS_Y,
|
||||||
CUBE_FACE_BOTTOM_NEG_Y,
|
CUBE_FACE_BOTTOM_NEG_Y,
|
||||||
CUBE_FACE_BACK_POS_X,
|
CUBE_FACE_BACK_POS_Z,
|
||||||
CUBE_FACE_FRONT_NEG_Z,
|
CUBE_FACE_FRONT_NEG_Z,
|
||||||
|
|
||||||
NUM_CUBE_FACES, // Not a valid vace index
|
NUM_CUBE_FACES, // Not a valid vace index
|
||||||
|
|
|
@ -99,3 +99,6 @@ void Light::setShowContour(float show) {
|
||||||
}
|
}
|
||||||
editSchema()._control.w = show;
|
editSchema()._control.w = show;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -180,6 +180,7 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
typedef std::shared_ptr< SphericalHarmonics > SHPointer;
|
||||||
|
|
||||||
class Light {
|
class Light {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -39,6 +39,9 @@ void Skybox::setColor(const Color& color) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Skybox::setCubemap(const gpu::TexturePointer& cubemap) {
|
void Skybox::setCubemap(const gpu::TexturePointer& cubemap) {
|
||||||
|
if (_isSHValid && (cubemap != _cubemap)) {
|
||||||
|
_isSHValid = false;
|
||||||
|
}
|
||||||
_cubemap = cubemap;
|
_cubemap = cubemap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,6 +52,9 @@ void Skybox::clearCubemap() {
|
||||||
void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Skybox& skybox) {
|
void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Skybox& skybox) {
|
||||||
|
|
||||||
if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) {
|
if (skybox.getCubemap() && skybox.getCubemap()->isDefined()) {
|
||||||
|
|
||||||
|
skybox.getIrradianceSH();
|
||||||
|
|
||||||
static gpu::PipelinePointer thePipeline;
|
static gpu::PipelinePointer thePipeline;
|
||||||
static gpu::BufferPointer theBuffer;
|
static gpu::BufferPointer theBuffer;
|
||||||
static gpu::Stream::FormatPointer theFormat;
|
static gpu::Stream::FormatPointer theFormat;
|
||||||
|
@ -113,3 +119,223 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky
|
||||||
batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(skybox.getColor(),1.0f), 0.f, 0);
|
batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(skybox.getColor(),1.0f), 0.f, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
glm::vec3 sRGBToLinear(glm::vec3& color) {
|
||||||
|
const float GAMMA_CORRECTION = 2.2f;
|
||||||
|
return glm::pow(color, glm::vec3(GAMMA_CORRECTION));
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec3 linearTosRGB(glm::vec3& color) {
|
||||||
|
const float GAMMA_CORRECTION_INV = 1.0f / 2.2f;
|
||||||
|
return glm::pow(color, glm::vec3(GAMMA_CORRECTION_INV));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Originial code for the Spherical Harmonics taken from "Sun and Black Cat- Igor Dykhta (igor dykhta email) © 2007-2014 "
|
||||||
|
void sphericalHarmonicsAdd(float * result, int order, const float * inputA, const float * inputB) {
|
||||||
|
const int numCoeff = order * order;
|
||||||
|
for(int i=0; i < numCoeff; i++) {
|
||||||
|
result[i] = inputA[i] + inputB[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sphericalHarmonicsScale(float * result, int order, const float * input, float scale) {
|
||||||
|
const int numCoeff = order * order;
|
||||||
|
for(int i=0; i < numCoeff; i++) {
|
||||||
|
result[i] = input[i] * scale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sphericalHarmonicsEvaluateDirection(float * result, int order, const glm::vec3 & dir) {
|
||||||
|
// calculate coefficients for first 3 bands of spherical harmonics
|
||||||
|
double P_0_0 = 0.282094791773878140;
|
||||||
|
double P_1_0 = 0.488602511902919920 * dir.z;
|
||||||
|
double P_1_1 = -0.488602511902919920;
|
||||||
|
double P_2_0 = 0.946174695757560080 * dir.z * dir.z - 0.315391565252520050;
|
||||||
|
double P_2_1 = -1.092548430592079200 * dir.z;
|
||||||
|
double P_2_2 = 0.546274215296039590;
|
||||||
|
result[0] = P_0_0;
|
||||||
|
result[1] = P_1_1 * dir.y;
|
||||||
|
result[2] = P_1_0;
|
||||||
|
result[3] = P_1_1 * dir.x;
|
||||||
|
result[4] = P_2_2 * (dir.x * dir.y + dir.y * dir.x);
|
||||||
|
result[5] = P_2_1 * dir.y;
|
||||||
|
result[6] = P_2_0;
|
||||||
|
result[7] = P_2_1 * dir.x;
|
||||||
|
result[8] = P_2_2 * (dir.x * dir.x - dir.y * dir.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<glm::vec3> & output, const uint order) {
|
||||||
|
const uint sqOrder = order*order;
|
||||||
|
|
||||||
|
// allocate memory for calculations
|
||||||
|
output.resize(sqOrder);
|
||||||
|
std::vector<float> resultR(sqOrder);
|
||||||
|
std::vector<float> resultG(sqOrder);
|
||||||
|
std::vector<float> resultB(sqOrder);
|
||||||
|
|
||||||
|
int width, height;
|
||||||
|
|
||||||
|
// initialize values
|
||||||
|
float fWt = 0.0f;
|
||||||
|
for(uint i=0; i < sqOrder; i++) {
|
||||||
|
output[i] = glm::vec3(0.0f);
|
||||||
|
resultR[i] = 0.0f;
|
||||||
|
resultG[i] = 0;
|
||||||
|
resultB[i] = 0;
|
||||||
|
}
|
||||||
|
std::vector<float> shBuff(sqOrder);
|
||||||
|
std::vector<float> shBuffB(sqOrder);
|
||||||
|
// get width and height
|
||||||
|
width = height = cubeTexture.getWidth();
|
||||||
|
if(width != height) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float UCHAR_TO_FLOAT = 1.0f / float(std::numeric_limits<unsigned char>::max());
|
||||||
|
|
||||||
|
// for each face of cube texture
|
||||||
|
for(int face=0; face < gpu::Texture::NUM_CUBE_FACES; face++) {
|
||||||
|
|
||||||
|
auto numComponents = cubeTexture.accessStoredMipFace(0,face)->_format.getDimensionCount();
|
||||||
|
auto data = cubeTexture.accessStoredMipFace(0,face)->_sysmem.readData();
|
||||||
|
if (data == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// step between two texels for range [0, 1]
|
||||||
|
float invWidth = 1.0f / float(width);
|
||||||
|
// initial negative bound for range [-1, 1]
|
||||||
|
float negativeBound = -1.0f + invWidth;
|
||||||
|
// step between two texels for range [-1, 1]
|
||||||
|
float invWidthBy2 = 2.0f / float(width);
|
||||||
|
|
||||||
|
for(int y=0; y < width; y++) {
|
||||||
|
// texture coordinate V in range [-1 to 1]
|
||||||
|
const float fV = negativeBound + float(y) * invWidthBy2;
|
||||||
|
|
||||||
|
for(int x=0; x < width; x++) {
|
||||||
|
// texture coordinate U in range [-1 to 1]
|
||||||
|
const float fU = negativeBound + float(x) * invWidthBy2;
|
||||||
|
|
||||||
|
// determine direction from center of cube texture to current texel
|
||||||
|
glm::vec3 dir;
|
||||||
|
switch(face) {
|
||||||
|
case gpu::Texture::CUBE_FACE_RIGHT_POS_X: {
|
||||||
|
dir.x = 1.0f;
|
||||||
|
dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth);
|
||||||
|
dir.z = 1.0f - (invWidthBy2 * float(x) + invWidth);
|
||||||
|
dir = -dir;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gpu::Texture::CUBE_FACE_LEFT_NEG_X: {
|
||||||
|
dir.x = -1.0f;
|
||||||
|
dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth);
|
||||||
|
dir.z = -1.0f + (invWidthBy2 * float(x) + invWidth);
|
||||||
|
dir = -dir;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gpu::Texture::CUBE_FACE_TOP_POS_Y: {
|
||||||
|
dir.x = - 1.0f + (invWidthBy2 * float(x) + invWidth);
|
||||||
|
dir.y = 1.0f;
|
||||||
|
dir.z = - 1.0f + (invWidthBy2 * float(y) + invWidth);
|
||||||
|
dir = -dir;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gpu::Texture::CUBE_FACE_BOTTOM_NEG_Y: {
|
||||||
|
dir.x = - 1.0f + (invWidthBy2 * float(x) + invWidth);
|
||||||
|
dir.y = - 1.0f;
|
||||||
|
dir.z = 1.0f - (invWidthBy2 * float(y) + invWidth);
|
||||||
|
dir = -dir;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gpu::Texture::CUBE_FACE_BACK_POS_Z: {
|
||||||
|
dir.x = - 1.0f + (invWidthBy2 * float(x) + invWidth);
|
||||||
|
dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth);
|
||||||
|
dir.z = 1.0f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case gpu::Texture::CUBE_FACE_FRONT_NEG_Z: {
|
||||||
|
dir.x = 1.0f - (invWidthBy2 * float(x) + invWidth);
|
||||||
|
dir.y = 1.0f - (invWidthBy2 * float(y) + invWidth);
|
||||||
|
dir.z = - 1.0f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize direction
|
||||||
|
dir = glm::normalize(dir);
|
||||||
|
|
||||||
|
// scale factor depending on distance from center of the face
|
||||||
|
const float fDiffSolid = 4.0f / ((1.0f + fU*fU + fV*fV) *
|
||||||
|
sqrtf(1.0f + fU*fU + fV*fV));
|
||||||
|
fWt += fDiffSolid;
|
||||||
|
|
||||||
|
// calculate coefficients of spherical harmonics for current direction
|
||||||
|
sphericalHarmonicsEvaluateDirection(shBuff.data(), order, dir);
|
||||||
|
|
||||||
|
// index of texel in texture
|
||||||
|
uint pixOffsetIndex = (x + y * width) * numComponents;
|
||||||
|
|
||||||
|
// get color from texture and map to range [0, 1]
|
||||||
|
glm::vec3 clr(float(data[pixOffsetIndex]) * UCHAR_TO_FLOAT,
|
||||||
|
float(data[pixOffsetIndex+1]) * UCHAR_TO_FLOAT,
|
||||||
|
float(data[pixOffsetIndex+2]) * UCHAR_TO_FLOAT);
|
||||||
|
|
||||||
|
// Gamma correct
|
||||||
|
clr = sRGBToLinear(clr);
|
||||||
|
|
||||||
|
// scale color and add to previously accumulated coefficients
|
||||||
|
sphericalHarmonicsScale(shBuffB.data(), order,
|
||||||
|
shBuff.data(), clr.r * fDiffSolid);
|
||||||
|
sphericalHarmonicsAdd(resultR.data(), order,
|
||||||
|
resultR.data(), shBuffB.data());
|
||||||
|
sphericalHarmonicsScale(shBuffB.data(), order,
|
||||||
|
shBuff.data(), clr.g * fDiffSolid);
|
||||||
|
sphericalHarmonicsAdd(resultG.data(), order,
|
||||||
|
resultG.data(), shBuffB.data());
|
||||||
|
sphericalHarmonicsScale(shBuffB.data(), order,
|
||||||
|
shBuff.data(), clr.b * fDiffSolid);
|
||||||
|
sphericalHarmonicsAdd(resultB.data(), order,
|
||||||
|
resultB.data(), shBuffB.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// final scale for coefficients
|
||||||
|
const float fNormProj = (4.0f * glm::pi<float>()) / fWt;
|
||||||
|
sphericalHarmonicsScale(resultR.data(), order, resultR.data(), fNormProj);
|
||||||
|
sphericalHarmonicsScale(resultG.data(), order, resultG.data(), fNormProj);
|
||||||
|
sphericalHarmonicsScale(resultB.data(), order, resultB.data(), fNormProj);
|
||||||
|
|
||||||
|
// save result
|
||||||
|
for(uint i=0; i < sqOrder; i++) {
|
||||||
|
// gamma Correct
|
||||||
|
// output[i] = linearTosRGB(glm::vec3(resultR[i], resultG[i], resultB[i]));
|
||||||
|
output[i] = glm::vec3(resultR[i], resultG[i], resultB[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const SphericalHarmonics& Skybox::getIrradianceSH() const {
|
||||||
|
if (!_isSHValid) {
|
||||||
|
if (_cubemap && _cubemap->isDefined()) {
|
||||||
|
std::vector< glm::vec3 > coefs;
|
||||||
|
sphericalHarmonicsFromTexture(*_cubemap, coefs, 3);
|
||||||
|
|
||||||
|
_irradianceSH.L00 = coefs[0];
|
||||||
|
_irradianceSH.L1m1 = coefs[1];
|
||||||
|
_irradianceSH.L10 = coefs[2];
|
||||||
|
_irradianceSH.L11 = coefs[3];
|
||||||
|
_irradianceSH.L2m2 = coefs[4];
|
||||||
|
_irradianceSH.L2m1 = coefs[5];
|
||||||
|
_irradianceSH.L20 = coefs[6];
|
||||||
|
_irradianceSH.L21 = coefs[7];
|
||||||
|
_irradianceSH.L22 = coefs[8];
|
||||||
|
|
||||||
|
_isSHValid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _irradianceSH;
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
|
|
||||||
#include "gpu/Texture.h"
|
#include "gpu/Texture.h"
|
||||||
|
|
||||||
|
#include "Light.h"
|
||||||
|
|
||||||
class ViewFrustum;
|
class ViewFrustum;
|
||||||
//class Transform;
|
//class Transform;
|
||||||
namespace gpu { class Batch; }
|
namespace gpu { class Batch; }
|
||||||
|
@ -34,11 +36,16 @@ public:
|
||||||
const gpu::TexturePointer& getCubemap() const { return _cubemap; }
|
const gpu::TexturePointer& getCubemap() const { return _cubemap; }
|
||||||
void clearCubemap();
|
void clearCubemap();
|
||||||
|
|
||||||
|
const SphericalHarmonics& getIrradianceSH() const;
|
||||||
|
|
||||||
static void render(gpu::Batch& batch, const ViewFrustum& frustum, const Skybox& skybox);
|
static void render(gpu::Batch& batch, const ViewFrustum& frustum, const Skybox& skybox);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
gpu::TexturePointer _cubemap;
|
gpu::TexturePointer _cubemap;
|
||||||
|
|
||||||
|
mutable SphericalHarmonics _irradianceSH;
|
||||||
|
mutable bool _isSHValid = false;
|
||||||
|
|
||||||
Color _color{1.0f, 1.0f, 1.0f};
|
Color _color{1.0f, 1.0f, 1.0f};
|
||||||
};
|
};
|
||||||
typedef std::shared_ptr< Skybox > SkyboxPointer;
|
typedef std::shared_ptr< Skybox > SkyboxPointer;
|
||||||
|
|
|
@ -20,5 +20,6 @@ varying vec3 color;
|
||||||
void main(void) {
|
void main(void) {
|
||||||
vec3 coord = normalize(normal);
|
vec3 coord = normalize(normal);
|
||||||
vec4 texel = textureCube(cubeMap, coord);
|
vec4 texel = textureCube(cubeMap, coord);
|
||||||
gl_FragData[0] = vec4(texel.xyz * color, 0.0);
|
vec3 pixel = pow(texel.xyz * color, vec3(1.0/2.2)); // manual Gamma correction
|
||||||
|
gl_FragData[0] = vec4(pixel, 0.0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ static const quint8 STEPS_TO_DECIDE_BALLISTIC = 4;
|
||||||
EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItem* entity) :
|
EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItem* entity) :
|
||||||
ObjectMotionState(shape),
|
ObjectMotionState(shape),
|
||||||
_entity(entity),
|
_entity(entity),
|
||||||
_sentMoving(false),
|
_sentActive(false),
|
||||||
_numNonMovingUpdates(0),
|
_numNonMovingUpdates(0),
|
||||||
_lastStep(0),
|
_lastStep(0),
|
||||||
_serverPosition(0.0f),
|
_serverPosition(0.0f),
|
||||||
|
@ -35,8 +35,9 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItem* entity
|
||||||
_serverGravity(0.0f),
|
_serverGravity(0.0f),
|
||||||
_serverAcceleration(0.0f),
|
_serverAcceleration(0.0f),
|
||||||
_accelerationNearlyGravityCount(0),
|
_accelerationNearlyGravityCount(0),
|
||||||
_shouldClaimSimulationOwnership(false),
|
_candidateForOwnership(false),
|
||||||
_movingStepsWithoutSimulationOwner(0)
|
_loopsSinceOwnershipBid(0),
|
||||||
|
_loopsWithoutOwner(0)
|
||||||
{
|
{
|
||||||
_type = MOTION_STATE_TYPE_ENTITY;
|
_type = MOTION_STATE_TYPE_ENTITY;
|
||||||
assert(entity != nullptr);
|
assert(entity != nullptr);
|
||||||
|
@ -66,6 +67,28 @@ void EntityMotionState::updateServerPhysicsVariables(uint32_t flags) {
|
||||||
void EntityMotionState::handleEasyChanges(uint32_t flags) {
|
void EntityMotionState::handleEasyChanges(uint32_t flags) {
|
||||||
updateServerPhysicsVariables(flags);
|
updateServerPhysicsVariables(flags);
|
||||||
ObjectMotionState::handleEasyChanges(flags);
|
ObjectMotionState::handleEasyChanges(flags);
|
||||||
|
if (flags & EntityItem::DIRTY_SIMULATOR_ID) {
|
||||||
|
_loopsWithoutOwner = 0;
|
||||||
|
_candidateForOwnership = 0;
|
||||||
|
if (_entity->getSimulatorID().isNull()
|
||||||
|
&& !_entity->isMoving()
|
||||||
|
&& _body->isActive()) {
|
||||||
|
// remove the ACTIVATION flag because this object is coming to rest
|
||||||
|
// according to a remote simulation and we don't want to wake it up again
|
||||||
|
flags &= ~EntityItem::DIRTY_PHYSICS_ACTIVATION;
|
||||||
|
_body->setActivationState(WANTS_DEACTIVATION);
|
||||||
|
} else {
|
||||||
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
const QUuid& sessionID = nodeList->getSessionUUID();
|
||||||
|
if (_entity->getSimulatorID() != sessionID) {
|
||||||
|
_loopsSinceOwnershipBid = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((flags & EntityItem::DIRTY_PHYSICS_ACTIVATION) && !_body->isActive()) {
|
||||||
|
_body->activate();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -95,15 +118,6 @@ bool EntityMotionState::isMoving() const {
|
||||||
return _entity && _entity->isMoving();
|
return _entity && _entity->isMoving();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityMotionState::isMovingVsServer() const {
|
|
||||||
auto alignmentDot = glm::abs(glm::dot(_serverRotation, _entity->getRotation()));
|
|
||||||
if (glm::distance(_serverPosition, _entity->getPosition()) > IGNORE_POSITION_DELTA ||
|
|
||||||
alignmentDot < IGNORE_ALIGNMENT_DOT) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This callback is invoked by the physics simulation in two cases:
|
// This callback is invoked by the physics simulation in two cases:
|
||||||
// (1) when the RigidBody is first added to the world
|
// (1) when the RigidBody is first added to the world
|
||||||
// (irregardless of MotionType: STATIC, DYNAMIC, or KINEMATIC)
|
// (irregardless of MotionType: STATIC, DYNAMIC, or KINEMATIC)
|
||||||
|
@ -143,19 +157,16 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
|
||||||
|
|
||||||
_entity->setLastSimulated(usecTimestampNow());
|
_entity->setLastSimulated(usecTimestampNow());
|
||||||
|
|
||||||
// if (_entity->getSimulatorID().isNull() && isMoving()) {
|
if (_entity->getSimulatorID().isNull()) {
|
||||||
if (_entity->getSimulatorID().isNull() && isMovingVsServer()) {
|
_loopsWithoutOwner++;
|
||||||
// if object is moving and has no owner, attempt to claim simulation ownership.
|
|
||||||
_movingStepsWithoutSimulationOwner++;
|
|
||||||
} else {
|
|
||||||
_movingStepsWithoutSimulationOwner = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t ownershipClaimDelay = 50; // TODO -- how to pick this? based on meters from our characterController?
|
const uint32_t OWNERSHIP_BID_DELAY = 50;
|
||||||
|
if (_loopsWithoutOwner > OWNERSHIP_BID_DELAY) {
|
||||||
if (_movingStepsWithoutSimulationOwner > ownershipClaimDelay) {
|
|
||||||
//qDebug() << "Warning -- claiming something I saw moving." << getName();
|
//qDebug() << "Warning -- claiming something I saw moving." << getName();
|
||||||
setShouldClaimSimulationOwnership(true);
|
_candidateForOwnership = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_loopsWithoutOwner = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WANT_DEBUG
|
#ifdef WANT_DEBUG
|
||||||
|
@ -177,8 +188,11 @@ void EntityMotionState::computeObjectShapeInfo(ShapeInfo& shapeInfo) {
|
||||||
// we alwasy resend packets for objects that have stopped moving up to some max limit.
|
// we alwasy resend packets for objects that have stopped moving up to some max limit.
|
||||||
const int MAX_NUM_NON_MOVING_UPDATES = 5;
|
const int MAX_NUM_NON_MOVING_UPDATES = 5;
|
||||||
|
|
||||||
bool EntityMotionState::doesNotNeedToSendUpdate() const {
|
bool EntityMotionState::isCandidateForOwnership(const QUuid& sessionID) const {
|
||||||
return !_body || (_body->isActive() && _numNonMovingUpdates > MAX_NUM_NON_MOVING_UPDATES);
|
if (!_body || !_entity) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return _candidateForOwnership || sessionID == _entity->getSimulatorID();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||||
|
@ -191,6 +205,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||||
_serverVelocity = bulletToGLM(_body->getLinearVelocity());
|
_serverVelocity = bulletToGLM(_body->getLinearVelocity());
|
||||||
_serverAngularVelocity = bulletToGLM(_body->getAngularVelocity());
|
_serverAngularVelocity = bulletToGLM(_body->getAngularVelocity());
|
||||||
_lastStep = simulationStep;
|
_lastStep = simulationStep;
|
||||||
|
_sentActive = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,21 +217,26 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||||
|
|
||||||
int numSteps = simulationStep - _lastStep;
|
int numSteps = simulationStep - _lastStep;
|
||||||
float dt = (float)(numSteps) * PHYSICS_ENGINE_FIXED_SUBSTEP;
|
float dt = (float)(numSteps) * PHYSICS_ENGINE_FIXED_SUBSTEP;
|
||||||
_lastStep = simulationStep;
|
|
||||||
bool isActive = _body->isActive();
|
|
||||||
|
|
||||||
|
const float INACTIVE_UPDATE_PERIOD = 0.5f;
|
||||||
|
if (!_sentActive) {
|
||||||
|
// we resend the inactive update every INACTIVE_UPDATE_PERIOD
|
||||||
|
// until it is removed from the outgoing updates
|
||||||
|
// (which happens when we don't own the simulation and it isn't touching our simulation)
|
||||||
|
return (dt > INACTIVE_UPDATE_PERIOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isActive = _body->isActive();
|
||||||
if (!isActive) {
|
if (!isActive) {
|
||||||
if (_sentMoving) {
|
// object has gone inactive but our last send was moving --> send non-moving update immediately
|
||||||
// this object just went inactive so send an update immediately
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
const float NON_MOVING_UPDATE_PERIOD = 1.0f;
|
|
||||||
if (dt > NON_MOVING_UPDATE_PERIOD && _numNonMovingUpdates < MAX_NUM_NON_MOVING_UPDATES) {
|
|
||||||
// RELIABLE_SEND_HACK: since we're not yet using a reliable method for non-moving update packets we repeat these
|
|
||||||
// at a faster rate than the MAX period above, and only send a limited number of them.
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
_lastStep = simulationStep;
|
||||||
|
if (glm::length2(_serverVelocity) > 0.0f) {
|
||||||
|
_serverVelocity += _serverAcceleration * dt;
|
||||||
|
_serverVelocity *= powf(1.0f - _body->getLinearDamping(), dt);
|
||||||
|
_serverPosition += dt * _serverVelocity;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Else we measure the error between current and extrapolated transform (according to expected behavior
|
// Else we measure the error between current and extrapolated transform (according to expected behavior
|
||||||
|
@ -224,15 +244,10 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||||
|
|
||||||
// NOTE: math is done in the simulation-frame, which is NOT necessarily the same as the world-frame
|
// NOTE: math is done in the simulation-frame, which is NOT necessarily the same as the world-frame
|
||||||
// due to _worldOffset.
|
// due to _worldOffset.
|
||||||
|
// TODO: compensate for _worldOffset offset here
|
||||||
|
|
||||||
// compute position error
|
// compute position error
|
||||||
if (glm::length2(_serverVelocity) > 0.0f) {
|
|
||||||
_serverVelocity += _serverAcceleration * dt;
|
|
||||||
_serverVelocity *= powf(1.0f - _body->getLinearDamping(), dt);
|
|
||||||
_serverPosition += dt * _serverVelocity;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: compensate for simulation offset here
|
|
||||||
btTransform worldTrans = _body->getWorldTransform();
|
btTransform worldTrans = _body->getWorldTransform();
|
||||||
glm::vec3 position = bulletToGLM(worldTrans.getOrigin());
|
glm::vec3 position = bulletToGLM(worldTrans.getOrigin());
|
||||||
|
|
||||||
|
@ -285,42 +300,50 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||||
return (fabsf(glm::dot(actualRotation, _serverRotation)) < MIN_ROTATION_DOT);
|
return (fabsf(glm::dot(actualRotation, _serverRotation)) < MIN_ROTATION_DOT);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityMotionState::shouldSendUpdate(uint32_t simulationFrame) {
|
bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep, const QUuid& sessionID) {
|
||||||
if (!_entity || !remoteSimulationOutOfSync(simulationFrame)) {
|
// NOTE: we expect _entity and _body to be valid in this context, since shouldSendUpdate() is only called
|
||||||
|
// after doesNotNeedToSendUpdate() returns false and that call should return 'true' if _entity or _body are NULL.
|
||||||
|
assert(_entity);
|
||||||
|
assert(_body);
|
||||||
|
|
||||||
|
if (!remoteSimulationOutOfSync(simulationStep)) {
|
||||||
|
_candidateForOwnership = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getShouldClaimSimulationOwnership()) {
|
if (_entity->getSimulatorID() == sessionID) {
|
||||||
|
// we own the simulation
|
||||||
|
_candidateForOwnership = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
const uint32_t FRAMES_BETWEEN_OWNERSHIP_CLAIMS = 30;
|
||||||
const QUuid& myNodeID = nodeList->getSessionUUID();
|
if (_candidateForOwnership) {
|
||||||
const QUuid& simulatorID = _entity->getSimulatorID();
|
_candidateForOwnership = false;
|
||||||
|
++_loopsSinceOwnershipBid;
|
||||||
|
if (_loopsSinceOwnershipBid > FRAMES_BETWEEN_OWNERSHIP_CLAIMS) {
|
||||||
|
// we don't own the simulation, but it's time to bid for it
|
||||||
|
_loopsSinceOwnershipBid = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (simulatorID != myNodeID) {
|
_candidateForOwnership = false;
|
||||||
// some other Node owns the simulating of this, so don't broadcast the results of local simulation.
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step) {
|
void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const QUuid& sessionID, uint32_t step) {
|
||||||
if (!_entity || !_entity->isKnownID()) {
|
assert(_entity);
|
||||||
return; // never update entities that are unknown
|
assert(_entity->isKnownID());
|
||||||
}
|
|
||||||
|
|
||||||
bool active = _body->isActive();
|
bool active = _body->isActive();
|
||||||
if (!active) {
|
if (!active) {
|
||||||
if (_sentMoving) {
|
|
||||||
// make sure all derivatives are zero
|
// make sure all derivatives are zero
|
||||||
glm::vec3 zero(0.0f);
|
glm::vec3 zero(0.0f);
|
||||||
_entity->setVelocity(zero);
|
_entity->setVelocity(zero);
|
||||||
_entity->setAngularVelocity(zero);
|
_entity->setAngularVelocity(zero);
|
||||||
_entity->setAcceleration(zero);
|
_entity->setAcceleration(zero);
|
||||||
}
|
_sentActive = false;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
float gravityLength = glm::length(_entity->getGravity());
|
float gravityLength = glm::length(_entity->getGravity());
|
||||||
float accVsGravity = glm::abs(glm::length(_measuredAcceleration) - gravityLength);
|
float accVsGravity = glm::abs(glm::length(_measuredAcceleration) - gravityLength);
|
||||||
|
@ -343,6 +366,21 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
||||||
} else {
|
} else {
|
||||||
_entity->setAcceleration(glm::vec3(0.0f));
|
_entity->setAcceleration(glm::vec3(0.0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const float DYNAMIC_LINEAR_VELOCITY_THRESHOLD = 0.05f; // 5 cm/sec
|
||||||
|
const float DYNAMIC_ANGULAR_VELOCITY_THRESHOLD = 0.087266f; // ~5 deg/sec
|
||||||
|
bool movingSlowly = glm::length2(_entity->getVelocity()) < (DYNAMIC_LINEAR_VELOCITY_THRESHOLD * DYNAMIC_LINEAR_VELOCITY_THRESHOLD)
|
||||||
|
&& glm::length2(_entity->getAngularVelocity()) < (DYNAMIC_ANGULAR_VELOCITY_THRESHOLD * DYNAMIC_ANGULAR_VELOCITY_THRESHOLD)
|
||||||
|
&& _entity->getAcceleration() == glm::vec3(0.0f);
|
||||||
|
|
||||||
|
if (movingSlowly) {
|
||||||
|
// velocities might not be zero, but we'll fake them as such, which will hopefully help convince
|
||||||
|
// other simulating observers to deactivate their own copies
|
||||||
|
glm::vec3 zero(0.0f);
|
||||||
|
_entity->setVelocity(zero);
|
||||||
|
_entity->setAngularVelocity(zero);
|
||||||
|
}
|
||||||
|
_sentActive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// remember properties for local server prediction
|
// remember properties for local server prediction
|
||||||
|
@ -352,25 +390,15 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
||||||
_serverAcceleration = _entity->getAcceleration();
|
_serverAcceleration = _entity->getAcceleration();
|
||||||
_serverAngularVelocity = _entity->getAngularVelocity();
|
_serverAngularVelocity = _entity->getAngularVelocity();
|
||||||
|
|
||||||
_sentMoving = _serverVelocity != glm::vec3(0.0f) || _serverAngularVelocity != glm::vec3(0.0f);
|
|
||||||
|
|
||||||
EntityItemProperties properties = _entity->getProperties();
|
EntityItemProperties properties = _entity->getProperties();
|
||||||
|
|
||||||
// explicitly set the properties that changed
|
// explicitly set the properties that changed so that they will be packed
|
||||||
properties.setPosition(_serverPosition);
|
properties.setPosition(_serverPosition);
|
||||||
properties.setRotation(_serverRotation);
|
properties.setRotation(_serverRotation);
|
||||||
properties.setVelocity(_serverVelocity);
|
properties.setVelocity(_serverVelocity);
|
||||||
properties.setAcceleration(_serverAcceleration);
|
properties.setAcceleration(_serverAcceleration);
|
||||||
properties.setAngularVelocity(_serverAngularVelocity);
|
properties.setAngularVelocity(_serverAngularVelocity);
|
||||||
|
|
||||||
// RELIABLE_SEND_HACK: count number of updates for entities at rest
|
|
||||||
// so we can stop sending them after some limit.
|
|
||||||
if (_sentMoving) {
|
|
||||||
_numNonMovingUpdates = 0;
|
|
||||||
} else {
|
|
||||||
_numNonMovingUpdates++;
|
|
||||||
}
|
|
||||||
if (_numNonMovingUpdates <= 1) {
|
|
||||||
// we only update lastEdited when we're sending new physics data
|
// we only update lastEdited when we're sending new physics data
|
||||||
quint64 lastSimulated = _entity->getLastSimulated();
|
quint64 lastSimulated = _entity->getLastSimulated();
|
||||||
_entity->setLastEdited(lastSimulated);
|
_entity->setLastEdited(lastSimulated);
|
||||||
|
@ -384,27 +412,19 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
||||||
qCDebug(physics) << " lastSimulated:" << debugTime(lastSimulated, now);
|
qCDebug(physics) << " lastSimulated:" << debugTime(lastSimulated, now);
|
||||||
#endif //def WANT_DEBUG
|
#endif //def WANT_DEBUG
|
||||||
|
|
||||||
} else {
|
if (sessionID == _entity->getSimulatorID()) {
|
||||||
properties.setLastEdited(_entity->getLastEdited());
|
// we think we own the simulation
|
||||||
}
|
if (!active) {
|
||||||
|
// we own the simulation but the entity has stopped, so we tell the server that we're clearing simulatorID
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
// but we remember that we do still own it... and rely on the server to tell us that we don't
|
||||||
QUuid myNodeID = nodeList->getSessionUUID();
|
|
||||||
QUuid simulatorID = _entity->getSimulatorID();
|
|
||||||
|
|
||||||
if (getShouldClaimSimulationOwnership()) {
|
|
||||||
// we think we should own it, so we tell the server that we do,
|
|
||||||
// but we don't remember that we own it...
|
|
||||||
// instead we expect the sever to tell us later whose ownership it has accepted
|
|
||||||
properties.setSimulatorID(myNodeID);
|
|
||||||
setShouldClaimSimulationOwnership(false);
|
|
||||||
} else if (simulatorID == myNodeID
|
|
||||||
&& !_sentMoving
|
|
||||||
&& _numNonMovingUpdates == MAX_NUM_NON_MOVING_UPDATES) {
|
|
||||||
// we own it, the entity has stopped, and we're sending the last non-moving update
|
|
||||||
// --> give up ownership
|
|
||||||
_entity->setSimulatorID(QUuid());
|
|
||||||
properties.setSimulatorID(QUuid());
|
properties.setSimulatorID(QUuid());
|
||||||
|
} else {
|
||||||
|
// explicitly set the property's simulatorID so that it is flagged as changed and will be packed
|
||||||
|
properties.setSimulatorID(sessionID);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// we don't own the simulation for this entity yet, but we're sending a bid for it
|
||||||
|
properties.setSimulatorID(sessionID);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (EntityItem::getSendPhysicsUpdates()) {
|
if (EntityItem::getSendPhysicsUpdates()) {
|
||||||
|
@ -453,7 +473,7 @@ QUuid EntityMotionState::getSimulatorID() const {
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
void EntityMotionState::bump() {
|
void EntityMotionState::bump() {
|
||||||
setShouldClaimSimulationOwnership(true);
|
_candidateForOwnership = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityMotionState::resetMeasuredBodyAcceleration() {
|
void EntityMotionState::resetMeasuredBodyAcceleration() {
|
||||||
|
|
|
@ -36,7 +36,6 @@ public:
|
||||||
virtual MotionType computeObjectMotionType() const;
|
virtual MotionType computeObjectMotionType() const;
|
||||||
|
|
||||||
virtual bool isMoving() const;
|
virtual bool isMoving() const;
|
||||||
virtual bool isMovingVsServer() const;
|
|
||||||
|
|
||||||
// this relays incoming position/rotation to the RigidBody
|
// this relays incoming position/rotation to the RigidBody
|
||||||
virtual void getWorldTransform(btTransform& worldTrans) const;
|
virtual void getWorldTransform(btTransform& worldTrans) const;
|
||||||
|
@ -46,13 +45,10 @@ public:
|
||||||
|
|
||||||
virtual void computeObjectShapeInfo(ShapeInfo& shapeInfo);
|
virtual void computeObjectShapeInfo(ShapeInfo& shapeInfo);
|
||||||
|
|
||||||
bool doesNotNeedToSendUpdate() const;
|
bool isCandidateForOwnership(const QUuid& sessionID) const;
|
||||||
bool remoteSimulationOutOfSync(uint32_t simulationStep);
|
bool remoteSimulationOutOfSync(uint32_t simulationStep);
|
||||||
bool shouldSendUpdate(uint32_t simulationFrame);
|
bool shouldSendUpdate(uint32_t simulationStep, const QUuid& sessionID);
|
||||||
void sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step);
|
void sendUpdate(OctreeEditPacketSender* packetSender, const QUuid& sessionID, uint32_t step);
|
||||||
|
|
||||||
void setShouldClaimSimulationOwnership(bool value) { _shouldClaimSimulationOwnership = value; }
|
|
||||||
bool getShouldClaimSimulationOwnership() { return _shouldClaimSimulationOwnership; }
|
|
||||||
|
|
||||||
virtual uint32_t getAndClearIncomingDirtyFlags() const;
|
virtual uint32_t getAndClearIncomingDirtyFlags() const;
|
||||||
|
|
||||||
|
@ -92,7 +88,7 @@ protected:
|
||||||
|
|
||||||
EntityItem* _entity;
|
EntityItem* _entity;
|
||||||
|
|
||||||
bool _sentMoving; // true if last update was moving
|
bool _sentActive; // true if body was active when we sent last update
|
||||||
int _numNonMovingUpdates; // RELIABLE_SEND_HACK for "not so reliable" resends of packets for non-moving objects
|
int _numNonMovingUpdates; // RELIABLE_SEND_HACK for "not so reliable" resends of packets for non-moving objects
|
||||||
|
|
||||||
// these are for the prediction of the remote server's simple extrapolation
|
// these are for the prediction of the remote server's simple extrapolation
|
||||||
|
@ -109,8 +105,9 @@ protected:
|
||||||
glm::vec3 _measuredAcceleration;
|
glm::vec3 _measuredAcceleration;
|
||||||
|
|
||||||
quint8 _accelerationNearlyGravityCount;
|
quint8 _accelerationNearlyGravityCount;
|
||||||
bool _shouldClaimSimulationOwnership;
|
bool _candidateForOwnership;
|
||||||
quint32 _movingStepsWithoutSimulationOwner;
|
uint32_t _loopsSinceOwnershipBid;
|
||||||
|
uint32_t _loopsWithoutOwner;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_EntityMotionState_h
|
#endif // hifi_EntityMotionState_h
|
||||||
|
|
|
@ -146,10 +146,6 @@ void ObjectMotionState::handleEasyChanges(uint32_t flags) {
|
||||||
_body->setMassProps(mass, inertia);
|
_body->setMassProps(mass, inertia);
|
||||||
_body->updateInertiaTensor();
|
_body->updateInertiaTensor();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & EntityItem::DIRTY_PHYSICS_ACTIVATION) {
|
|
||||||
_body->activate();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine) {
|
void ObjectMotionState::handleHardAndEasyChanges(uint32_t flags, PhysicsEngine* engine) {
|
||||||
|
|
|
@ -36,11 +36,11 @@ enum MotionStateType {
|
||||||
// and re-added to the physics engine and "easy" which just updates the body properties.
|
// and re-added to the physics engine and "easy" which just updates the body properties.
|
||||||
const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_MOTION_TYPE | EntityItem::DIRTY_SHAPE);
|
const uint32_t HARD_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_MOTION_TYPE | EntityItem::DIRTY_SHAPE);
|
||||||
const uint32_t EASY_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES |
|
const uint32_t EASY_DIRTY_PHYSICS_FLAGS = (uint32_t)(EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES |
|
||||||
EntityItem::DIRTY_MASS | EntityItem::DIRTY_COLLISION_GROUP |
|
EntityItem::DIRTY_MASS | EntityItem::DIRTY_COLLISION_GROUP);
|
||||||
EntityItem::DIRTY_MATERIAL | EntityItem::DIRTY_PHYSICS_ACTIVATION);
|
|
||||||
|
|
||||||
// These are the set of incoming flags that the PhysicsEngine needs to hear about:
|
// These are the set of incoming flags that the PhysicsEngine needs to hear about:
|
||||||
const uint32_t DIRTY_PHYSICS_FLAGS = HARD_DIRTY_PHYSICS_FLAGS | EASY_DIRTY_PHYSICS_FLAGS;
|
const uint32_t DIRTY_PHYSICS_FLAGS = HARD_DIRTY_PHYSICS_FLAGS | EASY_DIRTY_PHYSICS_FLAGS |
|
||||||
|
EntityItem::DIRTY_MATERIAL | (uint32_t)EntityItem::DIRTY_PHYSICS_ACTIVATION;
|
||||||
|
|
||||||
// These are the outgoing flags that the PhysicsEngine can affect:
|
// These are the outgoing flags that the PhysicsEngine can affect:
|
||||||
const uint32_t OUTGOING_DIRTY_PHYSICS_FLAGS = EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES;
|
const uint32_t OUTGOING_DIRTY_PHYSICS_FLAGS = EntityItem::DIRTY_TRANSFORM | EntityItem::DIRTY_VELOCITIES;
|
||||||
|
|
|
@ -188,7 +188,7 @@ VectorOfMotionStates& PhysicalEntitySimulation::getObjectsToChange() {
|
||||||
return _tempVector;
|
return _tempVector;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicalEntitySimulation::handleOutgoingChanges(VectorOfMotionStates& motionStates) {
|
void PhysicalEntitySimulation::handleOutgoingChanges(VectorOfMotionStates& motionStates, const QUuid& sessionID) {
|
||||||
// walk the motionStates looking for those that correspond to entities
|
// walk the motionStates looking for those that correspond to entities
|
||||||
for (auto stateItr : motionStates) {
|
for (auto stateItr : motionStates) {
|
||||||
ObjectMotionState* state = &(*stateItr);
|
ObjectMotionState* state = &(*stateItr);
|
||||||
|
@ -196,24 +196,32 @@ void PhysicalEntitySimulation::handleOutgoingChanges(VectorOfMotionStates& motio
|
||||||
EntityMotionState* entityState = static_cast<EntityMotionState*>(state);
|
EntityMotionState* entityState = static_cast<EntityMotionState*>(state);
|
||||||
EntityItem* entity = entityState->getEntity();
|
EntityItem* entity = entityState->getEntity();
|
||||||
if (entity) {
|
if (entity) {
|
||||||
|
if (entity->isKnownID() && entityState->isCandidateForOwnership(sessionID)) {
|
||||||
_outgoingChanges.insert(entityState);
|
_outgoingChanges.insert(entityState);
|
||||||
|
}
|
||||||
_entitiesToSort.insert(entityState->getEntity());
|
_entitiesToSort.insert(entityState->getEntity());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// send outgoing packets
|
|
||||||
uint32_t numSubsteps = _physicsEngine->getNumSubsteps();
|
uint32_t numSubsteps = _physicsEngine->getNumSubsteps();
|
||||||
if (_lastStepSendPackets != numSubsteps) {
|
if (_lastStepSendPackets != numSubsteps) {
|
||||||
_lastStepSendPackets = numSubsteps;
|
_lastStepSendPackets = numSubsteps;
|
||||||
|
|
||||||
|
if (sessionID.isNull()) {
|
||||||
|
// usually don't get here, but if so --> nothing to do
|
||||||
|
_outgoingChanges.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// send outgoing packets
|
||||||
QSet<EntityMotionState*>::iterator stateItr = _outgoingChanges.begin();
|
QSet<EntityMotionState*>::iterator stateItr = _outgoingChanges.begin();
|
||||||
while (stateItr != _outgoingChanges.end()) {
|
while (stateItr != _outgoingChanges.end()) {
|
||||||
EntityMotionState* state = *stateItr;
|
EntityMotionState* state = *stateItr;
|
||||||
if (state->doesNotNeedToSendUpdate()) {
|
if (!state->isCandidateForOwnership(sessionID)) {
|
||||||
stateItr = _outgoingChanges.erase(stateItr);
|
stateItr = _outgoingChanges.erase(stateItr);
|
||||||
} else if (state->shouldSendUpdate(numSubsteps)) {
|
} else if (state->shouldSendUpdate(numSubsteps, sessionID)) {
|
||||||
state->sendUpdate(_entityPacketSender, numSubsteps);
|
state->sendUpdate(_entityPacketSender, sessionID, numSubsteps);
|
||||||
++stateItr;
|
++stateItr;
|
||||||
} else {
|
} else {
|
||||||
++stateItr;
|
++stateItr;
|
||||||
|
|
|
@ -48,7 +48,7 @@ public:
|
||||||
VectorOfMotionStates& getObjectsToAdd();
|
VectorOfMotionStates& getObjectsToAdd();
|
||||||
VectorOfMotionStates& getObjectsToChange();
|
VectorOfMotionStates& getObjectsToChange();
|
||||||
|
|
||||||
void handleOutgoingChanges(VectorOfMotionStates& motionStates);
|
void handleOutgoingChanges(VectorOfMotionStates& motionStates, const QUuid& sessionID);
|
||||||
void handleCollisionEvents(CollisionEvents& collisionEvents);
|
void handleCollisionEvents(CollisionEvents& collisionEvents);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -53,6 +53,7 @@ public:
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
void setSessionUUID(const QUuid& sessionID) { _sessionID = sessionID; }
|
void setSessionUUID(const QUuid& sessionID) { _sessionID = sessionID; }
|
||||||
|
const QUuid& getSessionID() const { return _sessionID; }
|
||||||
|
|
||||||
void addObject(ObjectMotionState* motionState);
|
void addObject(ObjectMotionState* motionState);
|
||||||
void removeObject(ObjectMotionState* motionState);
|
void removeObject(ObjectMotionState* motionState);
|
||||||
|
|
|
@ -111,7 +111,7 @@ vec3 evalSkyboxGlobalColor(float shadowAttenuation, vec3 position, vec3 normal,
|
||||||
vec4 fragEyeVector = invViewMat * vec4(-position, 0.0);
|
vec4 fragEyeVector = invViewMat * vec4(-position, 0.0);
|
||||||
vec3 fragEyeDir = normalize(fragEyeVector.xyz);
|
vec3 fragEyeDir = normalize(fragEyeVector.xyz);
|
||||||
|
|
||||||
vec3 color = diffuse.rgb * evalSkyboxLight(fragNormal, 0.75).xyz * getLightAmbientIntensity(light);
|
vec3 color = diffuse.rgb * evalSphericalLight(ambientSphere, fragNormal).xyz * getLightAmbientIntensity(light);
|
||||||
|
|
||||||
vec4 shading = evalFragShading(fragNormal, -getLightDirection(light), fragEyeDir, specular, gloss);
|
vec4 shading = evalFragShading(fragNormal, -getLightDirection(light), fragEyeDir, specular, gloss);
|
||||||
|
|
||||||
|
|
|
@ -291,7 +291,12 @@ void DeferredLightingEffect::render() {
|
||||||
auto globalLight = _allocatedLights[_globalLights.front()];
|
auto globalLight = _allocatedLights[_globalLights.front()];
|
||||||
|
|
||||||
if (locations->ambientSphere >= 0) {
|
if (locations->ambientSphere >= 0) {
|
||||||
auto sh = globalLight->getAmbientSphere();
|
model::SphericalHarmonics sh;
|
||||||
|
if (useSkyboxCubemap) {
|
||||||
|
sh = _skybox->getIrradianceSH();
|
||||||
|
} else {
|
||||||
|
sh = globalLight->getAmbientSphere();
|
||||||
|
}
|
||||||
for (int i =0; i <model::SphericalHarmonics::NUM_COEFFICIENTS; i++) {
|
for (int i =0; i <model::SphericalHarmonics::NUM_COEFFICIENTS; i++) {
|
||||||
program->setUniformValue(locations->ambientSphere + i, *(((QVector4D*) &sh) + i));
|
program->setUniformValue(locations->ambientSphere + i, *(((QVector4D*) &sh) + i));
|
||||||
}
|
}
|
||||||
|
|
|
@ -374,3 +374,6 @@ void OffscreenQmlSurface::setProxyWindow(QWindow* window) {
|
||||||
_renderControl->_renderWindow = window;
|
_renderControl->_renderWindow = window;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QQuickWindow* OffscreenQmlSurface::getWindow() {
|
||||||
|
return _quickWindow;
|
||||||
|
}
|
|
@ -71,6 +71,7 @@ public:
|
||||||
|
|
||||||
void setBaseUrl(const QUrl& baseUrl);
|
void setBaseUrl(const QUrl& baseUrl);
|
||||||
QQuickItem* getRootItem();
|
QQuickItem* getRootItem();
|
||||||
|
QQuickWindow* getWindow();
|
||||||
|
|
||||||
virtual bool eventFilter(QObject* originalDestination, QEvent* event);
|
virtual bool eventFilter(QObject* originalDestination, QEvent* event);
|
||||||
|
|
||||||
|
|
|
@ -514,7 +514,8 @@ void NetworkTexture::setImage(const QImage& image, bool translucent, const QColo
|
||||||
|
|
||||||
if ((_width > 0) && (_height > 0)) {
|
if ((_width > 0) && (_height > 0)) {
|
||||||
|
|
||||||
bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE);
|
// bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE);
|
||||||
|
bool isLinearRGB = !(_type == CUBE_TEXTURE); //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE);
|
||||||
|
|
||||||
gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB));
|
gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB));
|
||||||
gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB));
|
gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB));
|
||||||
|
@ -602,6 +603,45 @@ void NetworkTexture::setImage(const QImage& image, bool translucent, const QColo
|
||||||
faces.push_back(image.copy(QRect(3 * faceWidth, faceWidth, faceWidth, faceWidth)).mirrored(true, false));
|
faces.push_back(image.copy(QRect(3 * faceWidth, faceWidth, faceWidth, faceWidth)).mirrored(true, false));
|
||||||
// Front = -Z
|
// Front = -Z
|
||||||
faces.push_back(image.copy(QRect(1 * faceWidth, faceWidth, faceWidth, faceWidth)).mirrored(true, false));
|
faces.push_back(image.copy(QRect(1 * faceWidth, faceWidth, faceWidth, faceWidth)).mirrored(true, false));
|
||||||
|
|
||||||
|
} else if ((_height / 4) == (_width / 3)) {
|
||||||
|
int faceWidth = _height / 4;
|
||||||
|
|
||||||
|
// Here is the expected layout for the faces in an image with the 4/3 aspect ratio:
|
||||||
|
//
|
||||||
|
// <-------WIDTH-------->
|
||||||
|
// ^ +------+------+------+
|
||||||
|
// | | | | |
|
||||||
|
// | | | +Y | |
|
||||||
|
// | | | | |
|
||||||
|
// H +------+------+------+
|
||||||
|
// E | | | |
|
||||||
|
// I | -X | -Z | +X |
|
||||||
|
// G | | | |
|
||||||
|
// H +------+------+------+
|
||||||
|
// T | | | |
|
||||||
|
// | | | -Y | |
|
||||||
|
// | | | | |
|
||||||
|
// | +------+------+------+
|
||||||
|
// | | | | |
|
||||||
|
// | | | +Z! | | <+Z is upside down!
|
||||||
|
// | | | | |
|
||||||
|
// V +------+------+------+
|
||||||
|
//
|
||||||
|
// FaceWidth = width / 3 = height / 4
|
||||||
|
|
||||||
|
// Right = +X
|
||||||
|
faces.push_back(image.copy(QRect(2 * faceWidth, faceWidth, faceWidth, faceWidth)).mirrored(true, false));
|
||||||
|
// Left = -X
|
||||||
|
faces.push_back(image.copy(QRect(0 * faceWidth, faceWidth, faceWidth, faceWidth)).mirrored(true, false));
|
||||||
|
// Top = +Y
|
||||||
|
faces.push_back(image.copy(QRect(1 * faceWidth, 0, faceWidth, faceWidth)).mirrored(false, true));
|
||||||
|
// Bottom = -Y
|
||||||
|
faces.push_back(image.copy(QRect(1 * faceWidth, 2 * faceWidth, faceWidth, faceWidth)).mirrored(false, true));
|
||||||
|
// Back = +Z
|
||||||
|
faces.push_back(image.copy(QRect(1 * faceWidth, 3 * faceWidth, faceWidth, faceWidth)).mirrored(false, true));
|
||||||
|
// Front = -Z
|
||||||
|
faces.push_back(image.copy(QRect(1 * faceWidth, faceWidth, faceWidth, faceWidth)).mirrored(true, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (faces.size() == gpu::Texture::NUM_FACES_PER_TYPE[gpu::Texture::TEX_CUBE]) {
|
if (faces.size() == gpu::Texture::NUM_FACES_PER_TYPE[gpu::Texture::TEX_CUBE]) {
|
||||||
|
|
|
@ -103,7 +103,7 @@ void EntityTests::entityTreeTests(bool verbose) {
|
||||||
|
|
||||||
properties.setPosition(newPosition);
|
properties.setPosition(newPosition);
|
||||||
|
|
||||||
tree.updateEntity(entityID, properties, true);
|
tree.updateEntity(entityID, properties);
|
||||||
|
|
||||||
float targetRadius = oneMeter * 2.0f;
|
float targetRadius = oneMeter * 2.0f;
|
||||||
const EntityItem* foundEntityByRadius = tree.findClosestEntity(positionNearOrigin, targetRadius);
|
const EntityItem* foundEntityByRadius = tree.findClosestEntity(positionNearOrigin, targetRadius);
|
||||||
|
@ -143,7 +143,7 @@ void EntityTests::entityTreeTests(bool verbose) {
|
||||||
|
|
||||||
properties.setPosition(newPosition);
|
properties.setPosition(newPosition);
|
||||||
|
|
||||||
tree.updateEntity(entityID, properties, true);
|
tree.updateEntity(entityID, properties);
|
||||||
|
|
||||||
float targetRadius = oneMeter * 2.0f;
|
float targetRadius = oneMeter * 2.0f;
|
||||||
const EntityItem* foundEntityByRadius = tree.findClosestEntity(positionAtCenter, targetRadius);
|
const EntityItem* foundEntityByRadius = tree.findClosestEntity(positionAtCenter, targetRadius);
|
||||||
|
|
Loading…
Reference in a new issue