mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-09 19:12:36 +02:00
merge hand controller things
This commit is contained in:
commit
15e0b40a18
66 changed files with 3132 additions and 1095 deletions
|
@ -82,11 +82,11 @@ void AssetServer::completeSetup() {
|
|||
auto maxBandwidthFloat = maxBandwidthValue.toDouble(-1);
|
||||
|
||||
if (maxBandwidthFloat > 0.0) {
|
||||
const int BYTES_PER_MEGABITS = (1024 * 1024) / 8;
|
||||
int maxBandwidth = maxBandwidthFloat * BYTES_PER_MEGABITS;
|
||||
const int BITS_PER_MEGABITS = 1000 * 1000;
|
||||
int maxBandwidth = maxBandwidthFloat * BITS_PER_MEGABITS;
|
||||
nodeList->setConnectionMaxBandwidth(maxBandwidth);
|
||||
qInfo() << "Set maximum bandwith per connection to" << maxBandwidthFloat << "Mb/s."
|
||||
" (" << maxBandwidth << "bytes/sec)";
|
||||
" (" << maxBandwidth << "bits/s)";
|
||||
}
|
||||
|
||||
// get the path to the asset folder from the domain server settings
|
||||
|
|
|
@ -34,6 +34,8 @@ var TRIGGER_OFF_VALUE = 0.15;
|
|||
|
||||
var BUMPER_ON_VALUE = 0.5;
|
||||
|
||||
var THUMB_ON_VALUE = 0.5;
|
||||
|
||||
var HAND_HEAD_MIX_RATIO = 0.0; // 0 = only use hands for search/move. 1 = only use head for search/move.
|
||||
|
||||
var PICK_WITH_HAND_RAY = true;
|
||||
|
@ -121,7 +123,8 @@ var GRABBABLE_PROPERTIES = [
|
|||
"parentID",
|
||||
"parentJointIndex",
|
||||
"density",
|
||||
"dimensions"
|
||||
"dimensions",
|
||||
"userData"
|
||||
];
|
||||
|
||||
var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with grab.js
|
||||
|
@ -153,20 +156,22 @@ var USE_POINTLIGHT = false;
|
|||
// states for the state machine
|
||||
var STATE_OFF = 0;
|
||||
var STATE_SEARCHING = 1;
|
||||
var STATE_DISTANCE_HOLDING = 2;
|
||||
var STATE_CONTINUE_DISTANCE_HOLDING = 3;
|
||||
var STATE_NEAR_GRABBING = 4;
|
||||
var STATE_CONTINUE_NEAR_GRABBING = 5;
|
||||
var STATE_NEAR_TRIGGER = 6;
|
||||
var STATE_CONTINUE_NEAR_TRIGGER = 7;
|
||||
var STATE_FAR_TRIGGER = 8;
|
||||
var STATE_CONTINUE_FAR_TRIGGER = 9;
|
||||
var STATE_RELEASE = 10;
|
||||
var STATE_EQUIP_SEARCHING = 11;
|
||||
var STATE_EQUIP = 12
|
||||
var STATE_CONTINUE_EQUIP_BD = 13; // equip while bumper is still held down
|
||||
var STATE_CONTINUE_EQUIP = 14;
|
||||
var STATE_WAITING_FOR_BUMPER_RELEASE = 15;
|
||||
var STATE_HOLD_SEARCHING = 2;
|
||||
var STATE_DISTANCE_HOLDING = 3;
|
||||
var STATE_CONTINUE_DISTANCE_HOLDING = 4;
|
||||
var STATE_NEAR_GRABBING = 5;
|
||||
var STATE_CONTINUE_NEAR_GRABBING = 6;
|
||||
var STATE_NEAR_TRIGGER = 7;
|
||||
var STATE_CONTINUE_NEAR_TRIGGER = 8;
|
||||
var STATE_FAR_TRIGGER = 9;
|
||||
var STATE_CONTINUE_FAR_TRIGGER = 10;
|
||||
var STATE_RELEASE = 11;
|
||||
var STATE_EQUIP = 12;
|
||||
var STATE_HOLD = 13;
|
||||
var STATE_CONTINUE_HOLD = 14;
|
||||
var STATE_CONTINUE_EQUIP = 15;
|
||||
var STATE_WAITING_FOR_RELEASE_THUMB_RELEASE = 16;
|
||||
var STATE_WAITING_FOR_EQUIP_THUMB_RELEASE = 17;
|
||||
|
||||
// "collidesWith" is specified by comma-separated list of group names
|
||||
// the possible group names are: static, dynamic, kinematic, myAvatar, otherAvatar
|
||||
|
@ -182,6 +187,8 @@ function stateToName(state) {
|
|||
return "off";
|
||||
case STATE_SEARCHING:
|
||||
return "searching";
|
||||
case STATE_HOLD_SEARCHING:
|
||||
return "hold_searching";
|
||||
case STATE_DISTANCE_HOLDING:
|
||||
return "distance_holding";
|
||||
case STATE_CONTINUE_DISTANCE_HOLDING:
|
||||
|
@ -200,16 +207,18 @@ function stateToName(state) {
|
|||
return "continue_far_trigger";
|
||||
case STATE_RELEASE:
|
||||
return "release";
|
||||
case STATE_EQUIP_SEARCHING:
|
||||
return "equip_searching";
|
||||
case STATE_EQUIP:
|
||||
return "equip";
|
||||
case STATE_CONTINUE_EQUIP_BD:
|
||||
return "continue_equip_bd";
|
||||
case STATE_HOLD:
|
||||
return "hold";
|
||||
case STATE_CONTINUE_HOLD:
|
||||
return "continue_hold";
|
||||
case STATE_CONTINUE_EQUIP:
|
||||
return "continue_equip";
|
||||
case STATE_WAITING_FOR_BUMPER_RELEASE:
|
||||
return "waiting_for_bumper_release";
|
||||
case STATE_WAITING_FOR_EQUIP_THUMB_RELEASE:
|
||||
return "waiting_for_equip_thumb_release";
|
||||
case STATE_WAITING_FOR_RELEASE_THUMB_RELEASE:
|
||||
return "waiting_for_release_thumb_release";
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
|
@ -260,9 +269,13 @@ function MyController(hand) {
|
|||
this.grabbedEntity = null; // on this entity.
|
||||
this.state = STATE_OFF;
|
||||
this.pointer = null; // entity-id of line object
|
||||
this.entityActivated = false;
|
||||
|
||||
this.triggerValue = 0; // rolling average of trigger value
|
||||
this.rawTriggerValue = 0;
|
||||
this.rawBumperValue = 0;
|
||||
this.rawThumbValue = 0;
|
||||
|
||||
//for visualizations
|
||||
this.overlayLine = null;
|
||||
this.particleBeamObject = null;
|
||||
|
@ -296,9 +309,7 @@ function MyController(hand) {
|
|||
this.touchTest();
|
||||
break;
|
||||
case STATE_SEARCHING:
|
||||
this.search();
|
||||
break;
|
||||
case STATE_EQUIP_SEARCHING:
|
||||
case STATE_HOLD_SEARCHING:
|
||||
this.search();
|
||||
break;
|
||||
case STATE_DISTANCE_HOLDING:
|
||||
|
@ -309,13 +320,17 @@ function MyController(hand) {
|
|||
break;
|
||||
case STATE_NEAR_GRABBING:
|
||||
case STATE_EQUIP:
|
||||
case STATE_HOLD:
|
||||
this.nearGrabbing();
|
||||
break;
|
||||
case STATE_WAITING_FOR_BUMPER_RELEASE:
|
||||
this.waitingForBumperRelease();
|
||||
case STATE_WAITING_FOR_EQUIP_THUMB_RELEASE:
|
||||
this.waitingForEquipThumbRelease();
|
||||
break;
|
||||
case STATE_WAITING_FOR_RELEASE_THUMB_RELEASE:
|
||||
this.waitingForReleaseThumbRelease();
|
||||
break;
|
||||
case STATE_CONTINUE_NEAR_GRABBING:
|
||||
case STATE_CONTINUE_EQUIP_BD:
|
||||
case STATE_CONTINUE_HOLD:
|
||||
case STATE_CONTINUE_EQUIP:
|
||||
this.continueNearGrabbing();
|
||||
break;
|
||||
|
@ -787,6 +802,26 @@ function MyController(hand) {
|
|||
return _this.rawBumperValue < BUMPER_ON_VALUE;
|
||||
};
|
||||
|
||||
// this.triggerOrBumperSqueezed = function() {
|
||||
// return triggerSmoothedSqueezed() || bumperSqueezed();
|
||||
// }
|
||||
|
||||
// this.triggerAndBumperReleased = function() {
|
||||
// return triggerSmoothedReleased() && bumperReleased();
|
||||
// }
|
||||
|
||||
this.thumbPress = function(value) {
|
||||
_this.rawThumbValue = value;
|
||||
}
|
||||
|
||||
this.thumbPressed = function() {
|
||||
return _this.rawThumbValue > THUMB_ON_VALUE;
|
||||
};
|
||||
|
||||
this.thumbReleased = function() {
|
||||
return _this.rawThumbValue < THUMB_ON_VALUE;
|
||||
};
|
||||
|
||||
this.off = function() {
|
||||
if (this.triggerSmoothedSqueezed() || this.bumperSqueezed()) {
|
||||
this.lastPickTime = 0;
|
||||
|
@ -794,8 +829,8 @@ function MyController(hand) {
|
|||
this.startingHandRotation = Controller.getPoseValue(controllerHandInput).rotation;
|
||||
if (this.triggerSmoothedSqueezed()) {
|
||||
this.setState(STATE_SEARCHING);
|
||||
} else {
|
||||
this.setState(STATE_EQUIP_SEARCHING);
|
||||
} else if (this.bumperSqueezed()) {
|
||||
this.setState(STATE_HOLD_SEARCHING);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -807,7 +842,11 @@ function MyController(hand) {
|
|||
|
||||
this.checkForStrayChildren();
|
||||
|
||||
if (this.state == STATE_SEARCHING ? this.triggerSmoothedReleased() : this.bumperReleased()) {
|
||||
if (this.state == STATE_SEARCHING && this.triggerSmoothedReleased()) {
|
||||
this.setState(STATE_RELEASE);
|
||||
return;
|
||||
}
|
||||
if (this.state == STATE_HOLD_SEARCHING && this.bumperReleased()) {
|
||||
this.setState(STATE_RELEASE);
|
||||
return;
|
||||
}
|
||||
|
@ -821,18 +860,24 @@ function MyController(hand) {
|
|||
|
||||
var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
|
||||
var currentHandRotation = Controller.getPoseValue(controllerHandInput).rotation;
|
||||
var currentControllerPosition = Controller.getPoseValue(controllerHandInput).position;
|
||||
var handDeltaRotation = Quat.multiply(currentHandRotation, Quat.inverse(this.startingHandRotation));
|
||||
|
||||
var avatarControllerPose = Controller.getPoseValue((this.hand === RIGHT_HAND) ?
|
||||
Controller.Standard.RightHand : Controller.Standard.LeftHand);
|
||||
var controllerRotation = Quat.multiply(MyAvatar.orientation, avatarControllerPose.rotation);
|
||||
|
||||
var distantPickRay = {
|
||||
origin: PICK_WITH_HAND_RAY ? handPosition : Camera.position,
|
||||
direction: PICK_WITH_HAND_RAY ? Quat.getUp(this.getHandRotation()) : Vec3.mix(Quat.getUp(this.getHandRotation()),
|
||||
direction: PICK_WITH_HAND_RAY ? Quat.getUp(controllerRotation) : Vec3.mix(Quat.getUp(controllerRotation),
|
||||
Quat.getFront(Camera.orientation),
|
||||
HAND_HEAD_MIX_RATIO),
|
||||
|
||||
length: PICK_MAX_DISTANCE
|
||||
};
|
||||
|
||||
var searchVisualizationPickRay = {
|
||||
origin: handPosition,
|
||||
origin: currentControllerPosition,
|
||||
direction: Quat.getUp(this.getHandRotation()),
|
||||
length: PICK_MAX_DISTANCE
|
||||
};
|
||||
|
@ -939,7 +984,7 @@ function MyController(hand) {
|
|||
}
|
||||
continue;
|
||||
}
|
||||
if (propsForCandidate.parentID != NULL_UUID && this.state == STATE_EQUIP_SEARCHING) {
|
||||
if (propsForCandidate.parentID != NULL_UUID && this.state == STATE_HOLD_SEARCHING) {
|
||||
// don't allow a double-equip
|
||||
if (WANT_DEBUG_SEARCH_NAME && propsForCandidate.name == WANT_DEBUG_SEARCH_NAME) {
|
||||
print("grab is skipping '" + WANT_DEBUG_SEARCH_NAME + "': it's a child");
|
||||
|
@ -973,15 +1018,19 @@ function MyController(hand) {
|
|||
this.setState(near ? STATE_NEAR_TRIGGER : STATE_FAR_TRIGGER);
|
||||
return;
|
||||
}
|
||||
// near grab or equip with action
|
||||
// near grab with action or equip
|
||||
var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, {});
|
||||
var refCount = ("refCount" in grabData) ? grabData.refCount : 0;
|
||||
if (near && (refCount < 1 || entityHasActions(this.grabbedEntity))) {
|
||||
this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP);
|
||||
if (this.state == STATE_SEARCHING) {
|
||||
this.setState(STATE_NEAR_GRABBING);
|
||||
} else { // (this.state == STATE_HOLD_SEARCHING)
|
||||
this.setState(STATE_HOLD);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// far grab or equip with action
|
||||
if ((isPhysical || this.state == STATE_EQUIP_SEARCHING) && !near) {
|
||||
// far grab
|
||||
if (isPhysical && !near) {
|
||||
if (entityIsGrabbedByOther(this.grabbedEntity)) {
|
||||
// don't distance grab something that is already grabbed.
|
||||
if (WANT_DEBUG_SEARCH_NAME && props.name == WANT_DEBUG_SEARCH_NAME) {
|
||||
|
@ -1003,8 +1052,9 @@ function MyController(hand) {
|
|||
FAR_TO_NEAR_GRAB_PADDING_FACTOR);
|
||||
}
|
||||
|
||||
this.setState(STATE_DISTANCE_HOLDING);
|
||||
|
||||
|
||||
this.setState(this.state == STATE_SEARCHING ? STATE_DISTANCE_HOLDING : STATE_EQUIP);
|
||||
this.searchSphereOff();
|
||||
return;
|
||||
}
|
||||
|
@ -1012,7 +1062,11 @@ function MyController(hand) {
|
|||
// else this thing isn't physical. grab it by reparenting it (but not if we've already
|
||||
// grabbed it).
|
||||
if (refCount < 1) {
|
||||
this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP);
|
||||
if (this.state == STATE_SEARCHING) {
|
||||
this.setState(STATE_NEAR_GRABBING);
|
||||
} else { // this.state == STATE_HOLD_SEARCHING)
|
||||
this.setState(STATE_HOLD);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
// it's not physical and it's already held via parenting. go ahead and grab it, but
|
||||
|
@ -1021,7 +1075,11 @@ function MyController(hand) {
|
|||
this.doubleParentGrab = true;
|
||||
this.previousParentID = props.parentID;
|
||||
this.previousParentJointIndex = props.parentJointIndex;
|
||||
this.setState(this.state == STATE_SEARCHING ? STATE_NEAR_GRABBING : STATE_EQUIP);
|
||||
if (this.state == STATE_SEARCHING) {
|
||||
this.setState(STATE_NEAR_GRABBING);
|
||||
} else { // (this.state == STATE_HOLD_SEARCHING)
|
||||
this.setState(STATE_HOLD);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (WANT_DEBUG_SEARCH_NAME && props.name == WANT_DEBUG_SEARCH_NAME) {
|
||||
|
@ -1056,10 +1114,12 @@ function MyController(hand) {
|
|||
this.distanceHolding = function() {
|
||||
|
||||
// controller pose is in avatar frame
|
||||
var avatarControllerPose = Controller.getPoseValue((this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand);
|
||||
var avatarControllerPose =
|
||||
Controller.getPoseValue((this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand);
|
||||
|
||||
// transform it into world frame
|
||||
var controllerPosition = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, avatarControllerPose.translation));
|
||||
var controllerPosition = Vec3.sum(MyAvatar.position,
|
||||
Vec3.multiplyQbyV(MyAvatar.orientation, avatarControllerPose.translation));
|
||||
var controllerRotation = Quat.multiply(MyAvatar.orientation, avatarControllerPose.rotation);
|
||||
|
||||
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
|
||||
|
@ -1071,8 +1131,11 @@ function MyController(hand) {
|
|||
this.currentObjectTime = now;
|
||||
this.currentCameraOrientation = Camera.orientation;
|
||||
|
||||
this.grabRadius = Vec3.distance(this.currentObjectPosition, controllerPosition);
|
||||
this.grabRadialVelocity = 0.0;
|
||||
|
||||
// compute a constant based on the initial conditions which we use below to exagerate hand motion onto the held object
|
||||
this.radiusScalar = Math.log(Vec3.distance(this.currentObjectPosition, controllerPosition) + 1.0);
|
||||
this.radiusScalar = Math.log(this.grabRadius + 1.0);
|
||||
if (this.radiusScalar < 1.0) {
|
||||
this.radiusScalar = 1.0;
|
||||
}
|
||||
|
@ -1109,7 +1172,7 @@ function MyController(hand) {
|
|||
};
|
||||
|
||||
this.continueDistanceHolding = function() {
|
||||
if (this.triggerSmoothedReleased()) {
|
||||
if (this.triggerSmoothedReleased() && this.bumperReleased()) {
|
||||
this.setState(STATE_RELEASE);
|
||||
this.callEntityMethodOnGrabbed("releaseGrab");
|
||||
return;
|
||||
|
@ -1118,27 +1181,21 @@ function MyController(hand) {
|
|||
this.heartBeat(this.grabbedEntity);
|
||||
|
||||
// controller pose is in avatar frame
|
||||
var avatarControllerPose = Controller.getPoseValue((this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand);
|
||||
var avatarControllerPose = Controller.getPoseValue((this.hand === RIGHT_HAND) ?
|
||||
Controller.Standard.RightHand : Controller.Standard.LeftHand);
|
||||
|
||||
// transform it into world frame
|
||||
var controllerPosition = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, avatarControllerPose.translation));
|
||||
var controllerPosition = Vec3.sum(MyAvatar.position,
|
||||
Vec3.multiplyQbyV(MyAvatar.orientation, avatarControllerPose.translation));
|
||||
var controllerRotation = Quat.multiply(MyAvatar.orientation, avatarControllerPose.rotation);
|
||||
|
||||
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
|
||||
|
||||
if (this.state == STATE_CONTINUE_DISTANCE_HOLDING && this.bumperSqueezed() &&
|
||||
this.hasPresetOffsets()) {
|
||||
var saveGrabbedID = this.grabbedEntity;
|
||||
this.release();
|
||||
this.setState(STATE_EQUIP);
|
||||
this.grabbedEntity = saveGrabbedID;
|
||||
return;
|
||||
}
|
||||
|
||||
var now = Date.now();
|
||||
var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds
|
||||
this.currentObjectTime = now;
|
||||
|
||||
// the action was set up on a previous call. update the targets.
|
||||
// the action was set up when this.distanceHolding was called. update the targets.
|
||||
var radius = Vec3.distance(this.currentObjectPosition, controllerPosition) *
|
||||
this.radiusScalar * DISTANCE_HOLDING_RADIUS_FACTOR;
|
||||
if (radius < 1.0) {
|
||||
|
@ -1156,7 +1213,7 @@ function MyController(hand) {
|
|||
|
||||
// update the currentObject position and rotation.
|
||||
this.currentObjectPosition = Vec3.sum(this.currentObjectPosition, handMoved);
|
||||
this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation);
|
||||
// this.currentObjectRotation = Quat.multiply(handChange, this.currentObjectRotation);
|
||||
|
||||
this.callEntityMethodOnGrabbed("continueDistantGrab");
|
||||
|
||||
|
@ -1166,6 +1223,25 @@ function MyController(hand) {
|
|||
|
||||
var handControllerData = getEntityCustomData('handControllerKey', this.grabbedEntity, defaultMoveWithHeadData);
|
||||
|
||||
// Update radialVelocity
|
||||
var lastVelocity = Vec3.subtract(controllerPosition, this.previousControllerPosition);
|
||||
lastVelocity = Vec3.multiply(lastVelocity, 1.0 / deltaTime);
|
||||
var newRadialVelocity = Vec3.dot(lastVelocity,
|
||||
Vec3.normalize(Vec3.subtract(grabbedProperties.position, controllerPosition)));
|
||||
|
||||
var VELOCITY_AVERAGING_TIME = 0.016;
|
||||
this.grabRadialVelocity = (deltaTime / VELOCITY_AVERAGING_TIME) * newRadialVelocity +
|
||||
(1.0 - (deltaTime / VELOCITY_AVERAGING_TIME)) * this.grabRadialVelocity;
|
||||
|
||||
var RADIAL_GRAB_AMPLIFIER = 10.0;
|
||||
if (Math.abs(this.grabRadialVelocity) > 0.0) {
|
||||
this.grabRadius = this.grabRadius + (this.grabRadialVelocity * deltaTime * this.grabRadius * RADIAL_GRAB_AMPLIFIER);
|
||||
}
|
||||
|
||||
var newTargetPosition = Vec3.multiply(this.grabRadius, Quat.getUp(controllerRotation));
|
||||
newTargetPosition = Vec3.sum(newTargetPosition, controllerPosition);
|
||||
|
||||
|
||||
var objectToAvatar = Vec3.subtract(this.currentObjectPosition, MyAvatar.position);
|
||||
if (handControllerData.disableMoveWithHead !== true) {
|
||||
// mix in head motion
|
||||
|
@ -1227,7 +1303,7 @@ function MyController(hand) {
|
|||
|
||||
var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, this.currentObjectPosition));
|
||||
var success = Entities.updateAction(this.grabbedEntity, this.actionID, {
|
||||
targetPosition: targetPosition,
|
||||
targetPosition: newTargetPosition,
|
||||
linearTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject),
|
||||
targetRotation: this.currentObjectRotation,
|
||||
angularTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject),
|
||||
|
@ -1339,24 +1415,30 @@ function MyController(hand) {
|
|||
this.callEntityMethodOnGrabbed("releaseGrab");
|
||||
return;
|
||||
}
|
||||
if (this.state == STATE_HOLD && this.bumperReleased()) {
|
||||
this.setState(STATE_RELEASE);
|
||||
this.callEntityMethodOnGrabbed("releaseGrab");
|
||||
return;
|
||||
}
|
||||
|
||||
this.lineOff();
|
||||
this.overlayLineOff();
|
||||
|
||||
if (this.entityActivated) {
|
||||
var saveGrabbedID = this.grabbedEntity;
|
||||
this.release();
|
||||
this.grabbedEntity = saveGrabbedID;
|
||||
}
|
||||
|
||||
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
|
||||
this.activateEntity(this.grabbedEntity, grabbedProperties, false);
|
||||
if (grabbedProperties.dynamic && NEAR_GRABBING_KINEMATIC) {
|
||||
Entities.editEntity(this.grabbedEntity, {
|
||||
dynamic: false
|
||||
});
|
||||
}
|
||||
|
||||
// var handRotation = this.getHandRotation();
|
||||
var handRotation = (this.hand === RIGHT_HAND) ? MyAvatar.getRightPalmRotation() : MyAvatar.getLeftPalmRotation();
|
||||
var handPosition = this.getHandPosition();
|
||||
|
||||
var hasPresetPosition = false;
|
||||
if (this.state != STATE_NEAR_GRABBING && this.hasPresetOffsets()) {
|
||||
if ((this.state == STATE_EQUIP || this.state == STATE_HOLD) && this.hasPresetOffsets()) {
|
||||
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
|
||||
// if an object is "equipped" and has a predefined offset, use it.
|
||||
this.ignoreIK = grabbableData.ignoreIK ? grabbableData.ignoreIK : false;
|
||||
|
@ -1372,9 +1454,9 @@ function MyController(hand) {
|
|||
var currentObjectPosition = grabbedProperties.position;
|
||||
var offset = Vec3.subtract(currentObjectPosition, handPosition);
|
||||
this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset);
|
||||
if (this.temporaryPositionOffset && this.state != STATE_NEAR_GRABBING) {
|
||||
if (this.temporaryPositionOffset && (this.state == STATE_EQUIP)) {
|
||||
this.offsetPosition = this.temporaryPositionOffset;
|
||||
hasPresetPosition = true;
|
||||
// hasPresetPosition = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1404,19 +1486,45 @@ function MyController(hand) {
|
|||
}));
|
||||
}
|
||||
|
||||
this.callEntityMethodOnGrabbed(this.state == STATE_NEAR_GRABBING ? "startNearGrab" : "startEquip");
|
||||
Entities.editEntity(this.grabbedEntity, {
|
||||
velocity: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
angularVelocity: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
dynamic: false
|
||||
});
|
||||
|
||||
if (this.state == STATE_NEAR_GRABBING) {
|
||||
this.callEntityMethodOnGrabbed("startNearGrab");
|
||||
} else { // this.state == STATE_EQUIP || this.state == STATE_HOLD
|
||||
this.callEntityMethodOnGrabbed("startEquip");
|
||||
}
|
||||
|
||||
if (this.state == STATE_NEAR_GRABBING) {
|
||||
// near grabbing
|
||||
this.setState(STATE_CONTINUE_NEAR_GRABBING);
|
||||
} else {
|
||||
} else if (this.state == STATE_HOLD) {
|
||||
// holding
|
||||
this.setState(STATE_CONTINUE_HOLD);
|
||||
} else { // (this.state == STATE_EQUIP)
|
||||
// equipping
|
||||
this.setState(STATE_CONTINUE_EQUIP_BD);
|
||||
this.setState(STATE_CONTINUE_EQUIP);
|
||||
}
|
||||
|
||||
this.currentHandControllerTipPosition =
|
||||
(this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition;
|
||||
this.currentObjectTime = Date.now();
|
||||
|
||||
this.currentObjectPosition = grabbedProperties.position;
|
||||
this.currentObjectRotation = grabbedProperties.rotation;
|
||||
this.currentVelocity = ZERO_VEC;
|
||||
this.currentAngularVelocity = ZERO_VEC;
|
||||
};
|
||||
|
||||
this.continueNearGrabbing = function() {
|
||||
|
@ -1426,25 +1534,30 @@ function MyController(hand) {
|
|||
this.callEntityMethodOnGrabbed("releaseGrab");
|
||||
return;
|
||||
}
|
||||
if (this.state == STATE_CONTINUE_EQUIP_BD && this.bumperReleased()) {
|
||||
this.setState(STATE_CONTINUE_EQUIP);
|
||||
return;
|
||||
}
|
||||
if (this.state == STATE_CONTINUE_EQUIP && this.bumperSqueezed()) {
|
||||
this.setState(STATE_WAITING_FOR_BUMPER_RELEASE);
|
||||
if (this.state == STATE_CONTINUE_HOLD && this.bumperReleased()) {
|
||||
this.setState(STATE_RELEASE);
|
||||
this.callEntityMethodOnGrabbed("releaseEquip");
|
||||
return;
|
||||
}
|
||||
if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.bumperSqueezed()) {
|
||||
this.setState(STATE_CONTINUE_EQUIP_BD);
|
||||
if (this.state == STATE_CONTINUE_EQUIP && this.thumbPressed()) {
|
||||
this.setState(STATE_WAITING_FOR_RELEASE_THUMB_RELEASE);
|
||||
this.callEntityMethodOnGrabbed("releaseEquip");
|
||||
return;
|
||||
}
|
||||
if (this.state == STATE_CONTINUE_NEAR_GRABBING && this.thumbPressed()) {
|
||||
this.setState(STATE_WAITING_FOR_EQUIP_THUMB_RELEASE);
|
||||
this.callEntityMethodOnGrabbed("releaseGrab");
|
||||
this.callEntityMethodOnGrabbed("startEquip");
|
||||
return;
|
||||
}
|
||||
if (this.state == STATE_CONTINUE_HOLD && this.thumbPressed()) {
|
||||
this.setState(STATE_WAITING_FOR_EQUIP_THUMB_RELEASE);
|
||||
return;
|
||||
}
|
||||
|
||||
this.heartBeat(this.grabbedEntity);
|
||||
|
||||
var props = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "parentID", "position"]);
|
||||
var props = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "parentID", "position", "rotation"]);
|
||||
if (!props.position) {
|
||||
// server may have reset, taking our equipped entity with it. move back to "off" stte
|
||||
this.setState(STATE_RELEASE);
|
||||
|
@ -1458,7 +1571,11 @@ function MyController(hand) {
|
|||
print("handControllerGrab -- autoreleasing held or equipped item because it is far from hand." +
|
||||
props.parentID + " " + vec3toStr(props.position));
|
||||
this.setState(STATE_RELEASE);
|
||||
this.callEntityMethodOnGrabbed(this.state == STATE_NEAR_GRABBING ? "releaseGrab" : "releaseEquip");
|
||||
if (this.state == STATE_CONTINUE_NEAR_GRABBING) {
|
||||
this.callEntityMethodOnGrabbed("releaseGrab");
|
||||
} else { // (this.state == STATE_CONTINUE_EQUIP || this.state == STATE_CONTINUE_HOLD)
|
||||
this.callEntityMethodOnGrabbed("releaseEquip");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1475,11 +1592,25 @@ function MyController(hand) {
|
|||
var deltaPosition = Vec3.subtract(handControllerPosition, this.currentHandControllerTipPosition); // meters
|
||||
var deltaTime = (now - this.currentObjectTime) / MSEC_PER_SEC; // convert to seconds
|
||||
|
||||
if (deltaTime > 0.0) {
|
||||
var worldDeltaPosition = Vec3.subtract(props.position, this.currentObjectPosition);
|
||||
|
||||
var previousEulers = Quat.safeEulerAngles(this.currentObjectRotation);
|
||||
var newEulers = Quat.safeEulerAngles(props.rotation);
|
||||
var worldDeltaRotation = Vec3.subtract(newEulers, previousEulers);
|
||||
|
||||
this.currentVelocity = Vec3.multiply(worldDeltaPosition, 1.0 / deltaTime);
|
||||
this.currentAngularVelocity = Vec3.multiply(worldDeltaRotation, Math.PI / (deltaTime * 180.0));
|
||||
|
||||
this.currentObjectPosition = props.position;
|
||||
this.currentObjectRotation = props.rotation;
|
||||
}
|
||||
|
||||
this.currentHandControllerTipPosition = handControllerPosition;
|
||||
this.currentObjectTime = now;
|
||||
|
||||
var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, {});
|
||||
if (this.state === STATE_CONTINUE_EQUIP) {
|
||||
if (this.state === STATE_CONTINUE_EQUIP || this.state === STATE_CONTINUE_HOLD) {
|
||||
this.callEntityMethodOnGrabbed("continueEquip");
|
||||
}
|
||||
if (this.state == STATE_CONTINUE_NEAR_GRABBING) {
|
||||
|
@ -1520,14 +1651,19 @@ function MyController(hand) {
|
|||
}
|
||||
};
|
||||
|
||||
this.waitingForBumperRelease = function() {
|
||||
if (this.bumperReleased()) {
|
||||
this.waitingForEquipThumbRelease = function() {
|
||||
if (this.thumbReleased() && this.triggerSmoothedReleased()) {
|
||||
this.setState(STATE_EQUIP);
|
||||
}
|
||||
};
|
||||
this.waitingForReleaseThumbRelease = function() {
|
||||
if (this.thumbReleased() && this.triggerSmoothedReleased()) {
|
||||
this.setState(STATE_RELEASE);
|
||||
}
|
||||
};
|
||||
|
||||
this.nearTrigger = function() {
|
||||
if (this.triggerSmoothedReleased()) {
|
||||
if (this.triggerSmoothedReleased() && this.bumperReleased()) {
|
||||
this.setState(STATE_RELEASE);
|
||||
this.callEntityMethodOnGrabbed("stopNearTrigger");
|
||||
return;
|
||||
|
@ -1540,7 +1676,7 @@ function MyController(hand) {
|
|||
};
|
||||
|
||||
this.farTrigger = function() {
|
||||
if (this.triggerSmoothedReleased()) {
|
||||
if (this.triggerSmoothedReleased() && this.bumperReleased()) {
|
||||
this.setState(STATE_RELEASE);
|
||||
this.callEntityMethodOnGrabbed("stopFarTrigger");
|
||||
return;
|
||||
|
@ -1550,7 +1686,7 @@ function MyController(hand) {
|
|||
};
|
||||
|
||||
this.continueNearTrigger = function() {
|
||||
if (this.triggerSmoothedReleased()) {
|
||||
if (this.triggerSmoothedReleased() && this.bumperReleased()) {
|
||||
this.setState(STATE_RELEASE);
|
||||
this.callEntityMethodOnGrabbed("stopNearTrigger");
|
||||
return;
|
||||
|
@ -1624,7 +1760,7 @@ function MyController(hand) {
|
|||
};
|
||||
|
||||
this.continueFarTrigger = function() {
|
||||
if (this.triggerSmoothedReleased()) {
|
||||
if (this.triggerSmoothedReleased() && this.bumperReleased()) {
|
||||
this.setState(STATE_RELEASE);
|
||||
this.callEntityMethodOnGrabbed("stopFarTrigger");
|
||||
return;
|
||||
|
@ -1722,7 +1858,6 @@ function MyController(hand) {
|
|||
};
|
||||
|
||||
this.release = function() {
|
||||
|
||||
this.turnLightsOff();
|
||||
this.turnOffVisualizations();
|
||||
|
||||
|
@ -1784,6 +1919,11 @@ function MyController(hand) {
|
|||
};
|
||||
|
||||
this.activateEntity = function(entityID, grabbedProperties, wasLoaded) {
|
||||
if (this.entityActivated) {
|
||||
return;
|
||||
}
|
||||
this.entityActivated = true;
|
||||
|
||||
var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {});
|
||||
var now = Date.now();
|
||||
|
||||
|
@ -1858,6 +1998,11 @@ function MyController(hand) {
|
|||
}
|
||||
|
||||
this.deactivateEntity = function(entityID, noVelocity) {
|
||||
if (!this.entityActivated) {
|
||||
return;
|
||||
}
|
||||
this.entityActivated = false;
|
||||
|
||||
var data = getEntityCustomData(GRAB_USER_DATA_KEY, entityID, {});
|
||||
if (data && data["refCount"]) {
|
||||
data["refCount"] = data["refCount"] - 1;
|
||||
|
@ -1873,19 +2018,33 @@ function MyController(hand) {
|
|||
|
||||
// things that are held by parenting and dropped with no velocity will end up as "static" in bullet. If
|
||||
// it looks like the dropped thing should fall, give it a little velocity.
|
||||
var parentID = Entities.getEntityProperties(entityID, ["parentID"]).parentID;
|
||||
var props = Entities.getEntityProperties(entityID, ["parentID", "velocity"])
|
||||
var parentID = props.parentID;
|
||||
var forceVelocity = false;
|
||||
|
||||
var doSetVelocity = false;
|
||||
if (parentID != NULL_UUID && deactiveProps.parentID == NULL_UUID) {
|
||||
// TODO: EntityScriptingInterface::convertLocationToScriptSemantics should be setting up
|
||||
// props.velocity to be a world-frame velocity and localVelocity to be vs parent. Until that
|
||||
// is done, we use a measured velocity here so that things held via a bumper-grab / parenting-grab
|
||||
// can be thrown.
|
||||
doSetVelocity = true;
|
||||
}
|
||||
|
||||
if (!noVelocity &&
|
||||
!doSetVelocity &&
|
||||
parentID == MyAvatar.sessionUUID &&
|
||||
Vec3.length(data["gravity"]) > 0.0 &&
|
||||
data["dynamic"] &&
|
||||
data["parentID"] == NULL_UUID &&
|
||||
!data["collisionless"]) {
|
||||
|
||||
deactiveProps["velocity"] = {
|
||||
x: 0.0,
|
||||
y: 0.1,
|
||||
z: 0.0
|
||||
};
|
||||
doSetVelocity = false;
|
||||
}
|
||||
if (noVelocity) {
|
||||
deactiveProps["velocity"] = {
|
||||
|
@ -1898,9 +2057,23 @@ function MyController(hand) {
|
|||
y: 0.0,
|
||||
z: 0.0
|
||||
};
|
||||
doSetVelocity = false;
|
||||
|
||||
}
|
||||
|
||||
Entities.editEntity(entityID, deactiveProps);
|
||||
|
||||
if (doSetVelocity) {
|
||||
// this is a continuation of the TODO above -- we shouldn't need to set this here.
|
||||
// do this after the parent has been reset. setting this at the same time as
|
||||
// the parent causes it to go off in the wrong direction. This is a bug that should
|
||||
// be fixed.
|
||||
Entities.editEntity(entityID, {
|
||||
velocity: this.currentVelocity,
|
||||
// angularVelocity: this.currentAngularVelocity
|
||||
});
|
||||
}
|
||||
|
||||
data = null;
|
||||
} else if (this.doubleParentGrab) {
|
||||
// we parent-grabbed this from another parent grab. try to put it back where we found it.
|
||||
|
@ -1942,7 +2115,7 @@ function MyController(hand) {
|
|||
this.checkNewlyLoaded = function(loadedEntityID) {
|
||||
if (this.state == STATE_OFF ||
|
||||
this.state == STATE_SEARCHING ||
|
||||
this.state == STATE_EQUIP_SEARCHING) {
|
||||
this.state == STATE_HOLD_SEARCHING) {
|
||||
var loadedProps = Entities.getEntityProperties(loadedEntityID);
|
||||
if (loadedProps.parentID != MyAvatar.sessionUUID) {
|
||||
return;
|
||||
|
@ -1974,6 +2147,9 @@ mapping.from([Controller.Standard.LT]).peek().to(leftController.triggerPress);
|
|||
mapping.from([Controller.Standard.RB]).peek().to(rightController.bumperPress);
|
||||
mapping.from([Controller.Standard.LB]).peek().to(leftController.bumperPress);
|
||||
|
||||
mapping.from([Controller.Standard.LeftPrimaryThumb]).peek().to(leftController.thumbPress);
|
||||
mapping.from([Controller.Standard.RightPrimaryThumb]).peek().to(rightController.thumbPress);
|
||||
|
||||
Controller.enableMapping(MAPPING_NAME);
|
||||
|
||||
//the section below allows the grab script to listen for messages that disable either one or both hands. useful for two handed items
|
||||
|
|
68
examples/cows/cowEntityScript.js
Normal file
68
examples/cows/cowEntityScript.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
|
||||
// cowEntityScript.js
|
||||
// examples/cows
|
||||
//
|
||||
// Created by Eric Levin on 3/25/16
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// This entity script handles the logic for untipping a cow after it collides with something
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
|
||||
(function() {
|
||||
Script.include("../libraries/utils.js");
|
||||
|
||||
var _this = this;
|
||||
_this.COLLISION_COOLDOWN_TIME = 5000;
|
||||
|
||||
|
||||
this.preload = function(entityID) {
|
||||
print("EBL Preload!!");
|
||||
_this.entityID = entityID;
|
||||
_this.mooSound = SoundCache.getSound("https://s3-us-west-1.amazonaws.com/hifi-content/eric/Sounds/moo.wav")
|
||||
_this.mooSoundOptions = {volume: 0.7, loop: false};
|
||||
_this.timeSinceLastCollision = 0;
|
||||
_this.shouldUntipCow = true;
|
||||
}
|
||||
|
||||
this.collisionWithEntity = function(myID, otherID, collisionInfo) {
|
||||
if(_this.shouldUntipCow) {
|
||||
Script.setTimeout(function() {
|
||||
_this.untipCow();
|
||||
_this.shouldUntipCow = true;
|
||||
}, _this.COLLISION_COOLDOWN_TIME);
|
||||
}
|
||||
|
||||
_this.shouldUntipCow = false;
|
||||
|
||||
}
|
||||
|
||||
this.untipCow = function() {
|
||||
// keep yaw but reset pitch and roll
|
||||
var cowProps = Entities.getEntityProperties(_this.entityID, ["rotation", "position"]);
|
||||
var eulerRotation = Quat.safeEulerAngles(cowProps.rotation);
|
||||
eulerRotation.x = 0;
|
||||
eulerRotation.z = 0;
|
||||
var newRotation = Quat.fromVec3Degrees(eulerRotation);
|
||||
Entities.editEntity(_this.entityID, {
|
||||
rotation: newRotation,
|
||||
velocity: {x: 0, y: 0, z: 0},
|
||||
angularVelocity: {x: 0, y: 0, z:0}
|
||||
});
|
||||
|
||||
|
||||
_this.mooSoundOptions.position = cowProps.position;
|
||||
if (!_this.soundInjector) {
|
||||
_this.soundInjector = Audio.playSound(_this.mooSound, _this.mooSoundOptions);
|
||||
} else {
|
||||
_this.soundInjector.setOptions(_this.mooSoundOptions);
|
||||
_this.soundInjector.restart();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
});
|
53
examples/cows/cowSpawner.js
Normal file
53
examples/cows/cowSpawner.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
|
||||
// cowSpawner.js
|
||||
// examples/cows
|
||||
//
|
||||
// Created by Eric Levin on 3/25/16
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// This spawns a cow which will untip itself
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
var orientation = MyAvatar.orientation;
|
||||
orientation = Quat.safeEulerAngles(orientation);
|
||||
orientation.x = 0;
|
||||
orientation = Quat.fromVec3Degrees(orientation);
|
||||
var center = Vec3.sum(MyAvatar.getHeadPosition(), Vec3.multiply(2, Quat.getFront(orientation)));
|
||||
|
||||
|
||||
var SCRIPT_URL = Script.resolvePath("cowEntityScript.js?");
|
||||
var cow = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: "http://hifi-content.s3.amazonaws.com/DomainContent/production/cow/newMooCow.fbx",
|
||||
name: "playa_model_throwinCow",
|
||||
position: center,
|
||||
animation: {
|
||||
currentFrame: 278,
|
||||
running: true,
|
||||
url: "http://hifi-content.s3.amazonaws.com/DomainContent/Junkyard/Playa/newMooCow.fbx"
|
||||
},
|
||||
dimensions: {
|
||||
x: 0.739,
|
||||
y: 1.613,
|
||||
z: 2.529
|
||||
},
|
||||
dynamic: true,
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: -5,
|
||||
z: 0
|
||||
},
|
||||
shapeType: "box",
|
||||
script: SCRIPT_URL,
|
||||
userData: "{\"grabbableKey\":{\"grabbable\":true}}"
|
||||
});
|
||||
|
||||
|
||||
function cleanup() {
|
||||
Entities.deleteEntity(cow);
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
|
@ -20,5 +20,5 @@ Script.load("controllers/squeezeHands.js");
|
|||
Script.load("grab.js");
|
||||
Script.load("directory.js");
|
||||
Script.load("dialTone.js");
|
||||
Script.load("attachedEntitiesManager.js");
|
||||
// Script.load("attachedEntitiesManager.js");
|
||||
Script.load("depthReticle.js");
|
||||
|
|
|
@ -112,7 +112,7 @@ function autoHideReticle() {
|
|||
function checkReticleDepth() {
|
||||
var now = Date.now();
|
||||
var timeSinceLastDepthCheck = now - lastDepthCheckTime;
|
||||
if (timeSinceLastDepthCheck > TIME_BETWEEN_DEPTH_CHECKS) {
|
||||
if (timeSinceLastDepthCheck > TIME_BETWEEN_DEPTH_CHECKS && Reticle.visible) {
|
||||
var newDesiredDepth = desiredDepth;
|
||||
lastDepthCheckTime = now;
|
||||
var reticlePosition = Reticle.position;
|
||||
|
@ -160,7 +160,6 @@ function moveToDesiredDepth() {
|
|||
} else {
|
||||
newDepth = Reticle.depth + distanceToAdjustThisCycle;
|
||||
}
|
||||
|
||||
Reticle.setDepth(newDepth);
|
||||
}
|
||||
}
|
||||
|
|
883
examples/html/edit-style.css
Normal file
883
examples/html/edit-style.css
Normal file
|
@ -0,0 +1,883 @@
|
|||
/*
|
||||
// edit-style.css
|
||||
//
|
||||
// Created by Ryan Huffman on 13 Nov 2014
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
*/
|
||||
|
||||
@font-face {
|
||||
font-family: Raleway-Regular;
|
||||
src: url(../../resources/fonts/Raleway-Regular.ttf), /* Production */
|
||||
url(../../interface/resources/fonts/Raleway-Regular.ttf); /* Development */
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Raleway-Light;
|
||||
src: url(../../resources/fonts/Raleway-Light.ttf),
|
||||
url(../../interface/resources/fonts/Raleway-Light.ttf);
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Raleway-Bold;
|
||||
src: url(../../resources/fonts/Raleway-Bold.ttf),
|
||||
url(../../interface/resources/fonts/Raleway-Bold.ttf);
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Raleway-SemiBold;
|
||||
src: url(../../resources/fonts/Raleway-SemiBold.ttf),
|
||||
url(../../interface/resources/fonts/Raleway-SemiBold.ttf);
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: FiraSans-SemiBold;
|
||||
src: url(../../resources/fonts/FiraSans-SemiBold.ttf),
|
||||
url(../../interface/resources/fonts/FiraSans-SemiBold.ttf);
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: AnonymousPro-Regular;
|
||||
src: url(../../resources/fonts/AnonymousPro-Regular.ttf),
|
||||
url(../../interface/resources/fonts/AnonymousPro-Regular.ttf);
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: HiFi-Glyphs;
|
||||
src: url(../../resources/fonts/hifi-glyphs.ttf),
|
||||
url(../../interface/resources/fonts/hifi-glyphs.ttf);
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 24px 12px 24px 12px;
|
||||
|
||||
color: #afafaf;
|
||||
background-color: #404040;
|
||||
font-family: Raleway-Regular;
|
||||
font-size: 15px;
|
||||
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
|
||||
table {
|
||||
font-family: FiraSans-SemiBold;
|
||||
font-size: 15px;
|
||||
color: #afafaf;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
border: 2px solid #575757;
|
||||
border-radius: 7px;
|
||||
}
|
||||
|
||||
thead {
|
||||
font-family: Raleway-Regular;
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
background-color: #1c1c1c;
|
||||
padding: 1px 0px;
|
||||
border-bottom: 1px solid #575757;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
tbody {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
tfoot {
|
||||
font-family: Raleway-Light;
|
||||
font-size: 13px;
|
||||
background-color: #1c1c1c;
|
||||
border-top: 1px solid #575757;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
tfoot tr {
|
||||
background-color: #1c1cff;
|
||||
}
|
||||
|
||||
thead tr {
|
||||
height: 26px; /* 28px with thead padding */
|
||||
}
|
||||
|
||||
thead th {
|
||||
height: 26px;
|
||||
background-color: #1c1c1c;
|
||||
border-right: 1px solid #575757;
|
||||
}
|
||||
|
||||
thead th:last-child {
|
||||
border: none;
|
||||
}
|
||||
|
||||
tbody td {
|
||||
height: 26px;
|
||||
}
|
||||
|
||||
tfoot td {
|
||||
height: 18px;
|
||||
width: 100%;
|
||||
background-color: #1c1c1c;
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
tr {
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
tr:nth-child(odd) {
|
||||
background-color: #2e2e2e;
|
||||
}
|
||||
|
||||
tr:nth-child(even) {
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
|
||||
tr:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
tr.selected {
|
||||
color: #000000;
|
||||
background-color: #00b4ef;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: center;
|
||||
word-wrap: nowrap;
|
||||
white-space: nowrap;
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
td {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
word-wrap: nowrap;
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
td.url {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
input[type="text"], input[type="number"], textarea {
|
||||
margin: 0;
|
||||
padding: 0 12px;
|
||||
color: #afafaf;
|
||||
background-color: #252525;
|
||||
border: none;
|
||||
font-family: FiraSans-SemiBold;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
textarea {
|
||||
font-family: AnonymousPro-Regular;
|
||||
font-size: 16px;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
min-height: 64px;
|
||||
width: 100%;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
input::-webkit-input-placeholder {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
input:focus, textarea:focus {
|
||||
color: #fff;
|
||||
background-color: #000;
|
||||
outline: 1px solid #00b4ef;
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
input::selection, textarea::selection {
|
||||
color: #000000;
|
||||
background-color: #00b4ef;
|
||||
}
|
||||
|
||||
input.search {
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
input:disabled, textarea:disabled {
|
||||
background-color: #383838;
|
||||
color: #afafaf;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
height: 28px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input[type="number"] {
|
||||
position: relative;
|
||||
height: 28px;
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
input[type=number] {
|
||||
padding-right: 6px;
|
||||
}
|
||||
input[type=number]::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
opacity: 1.0;
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 10px;
|
||||
overflow: hidden;
|
||||
font-family: hifi-glyphs;
|
||||
font-size: 50px;
|
||||
color: #afafaf;
|
||||
cursor: pointer;
|
||||
/*background-color: #000000;*/
|
||||
}
|
||||
input[type=number]::-webkit-inner-spin-button:before,
|
||||
input[type=number]::-webkit-inner-spin-button:after {
|
||||
position:absolute;
|
||||
left: -21px;
|
||||
line-height: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
input[type=number]::-webkit-inner-spin-button:before {
|
||||
content: "6";
|
||||
top: 5px;
|
||||
}
|
||||
input[type=number]::-webkit-inner-spin-button:after {
|
||||
content: "5";
|
||||
bottom: 6px;
|
||||
}
|
||||
input[type="number"]::-webkit-inner-spin-button:hover {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
input.no-spin::-webkit-outer-spin-button,
|
||||
input.no-spin::-webkit-inner-spin-button {
|
||||
display: none;
|
||||
-webkit-appearance: none;
|
||||
margin: 0; /* <-- Apparently some margin are still there even though it's hidden */
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
input[type=button] {
|
||||
font-family: Raleway-Bold;
|
||||
font-size: 13px;
|
||||
text-transform: uppercase;
|
||||
vertical-align: top;
|
||||
height: 28px;
|
||||
min-width: 120px;
|
||||
padding: 0px 12px;
|
||||
margin-right: 8px;
|
||||
border-radius: 5px;
|
||||
border: none;
|
||||
color: #fff;
|
||||
background-color: #000;
|
||||
background: linear-gradient(#343434 20%, #000 100%);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type=button].glyph {
|
||||
font-family: HiFi-Glyphs;
|
||||
font-size: 20px;
|
||||
text-transform: none;
|
||||
min-width: 32px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
input[type=button].red {
|
||||
color: #fff;
|
||||
background-color: #94132e;
|
||||
background: linear-gradient(#d42043 20%, #94132e 100%);
|
||||
}
|
||||
input[type=button].blue {
|
||||
color: #fff;
|
||||
background-color: #94132e;
|
||||
background: linear-gradient(#00b4ef 20%, #1080b8 100%);
|
||||
}
|
||||
|
||||
input[type=button]:enabled:hover {
|
||||
background: linear-gradient(#000, #000);
|
||||
border: none;
|
||||
}
|
||||
input[type=button].red:enabled:hover {
|
||||
background: linear-gradient(#d42043, #d42043);
|
||||
border: none;
|
||||
}
|
||||
input[type=button].blue:enabled:hover {
|
||||
background: linear-gradient(#00b4ef, #00b4ef);
|
||||
border: none;
|
||||
}
|
||||
|
||||
input[type=button]:active {
|
||||
background: linear-gradient(#343434, #343434);
|
||||
}
|
||||
input[type=button].red:active {
|
||||
background: linear-gradient(#94132e, #94132e);
|
||||
}
|
||||
input[type=button].blue:active {
|
||||
background: linear-gradient(#1080b8, #1080b8);
|
||||
}
|
||||
|
||||
input[type=button]:disabled {
|
||||
color: #252525;
|
||||
background: linear-gradient(#575757 20%, #252525 100%);
|
||||
}
|
||||
|
||||
input[type=checkbox] {
|
||||
display: none;
|
||||
}
|
||||
input[type=checkbox] + label {
|
||||
padding-left: 24px;
|
||||
background-position-y: 6px;
|
||||
background-repeat: no-repeat;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACpSURBVDhPY2xoaGD68+dPMSMjY9L////VgTQjAw4AlH8PxLOPHj1azWxjY1MBVNsBFBfBpwkEgNKcQGwtJyfHyATkF0KEiQdAzYlMQEIUyicFyDD9+/ePgRxMvsb///4zkIOZ/v0HmkAGHginYjGNGAzS+BpdkAj8mun/3//92DyPD//993cG88nTJ4+Zm5p/BSZeJYb/DEJADEzNOPF7hn8Mk69cvVIPAHN5pyfo70F5AAAAAElFTkSuQmCC);
|
||||
}
|
||||
input[type=checkbox]:enabled + label:hover {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAClSURBVDhPY2hoaGD6//9/6b9//64D8T8gGycASr/7+/dv5/79+1kYgIxKqDjRAKiniRFIv2JgYBAFYlLAE0aQ66AckgDjjx8/yNP44cMH8jS+fPmSPI0PHz4kT+PNmzfJ03jp0iXyNJ46dYo8jYcPHyYnAbxm+vnzZz8wLhlIwd+/f5/BrKSkdExCQuLrnz9/lIBpUAiIQekXF34PTGmTT548WQ8AokXg+rhVtPYAAAAASUVORK5CYII=);
|
||||
}
|
||||
input[type=checkbox]:checked + label {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFLSURBVDhPjZK9SgNBFIXvbCaQRDQq7mIhQRPBRisJKcwLWOobaCsExEaxcEEQe0trGysfwg0EwWoDsbFIJUaIBJOwus547saNP3FlPzgzzJxzL5edFbZtG77v7wkhtrXWS9gFRQC/DZ07jnOYKJfL+8ie4n7mvyIGdhpay+VyQuK8y5dPZoHuVtbpZcLi4wjJ1x4t316R9dDgBlsSi8mGu7pJjyJFzVaH+r7iqyHSELSQzVADjS0UgjlDKUUsLzVO98+9kSLGV5qaHXhjU0GWNSxk3hCIwnsfeMNCjTArLmHeUBodoLiE+R+jxuHPUZP4elGE3teonx2S/Q7lJzOUlkYQ+A4/xzyegzNhXmJpwTMXry9IFjcoa84O0r+QXpcK1cugCLREZadyoA19Ergxwf96nKjd1KqlYqmLQ540TUNwItUmRWdu3T36AODjwgpY9xqqAAAAAElFTkSuQmCC);
|
||||
}
|
||||
input[type=checkbox]:checked + label:hover {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEySURBVDhPnZLPSsNAEMa/XVPBCE0RhNy0OarP4Av4AD6JB0GwVBA8efBBxHsgh4CQswcRoUIpiIpVAm3zZ5M4szFSbQPBH3xkJvNNZskOer2eLIriKM/ze1JOcS1UHmdZduF5ngEKjr/fN4Z6+oKerwA2gxC4HAFPEWVLsAzgZAvYt3Q6Enw6jg7uBAaTFMNwhpnKdbXCkAJdy8ROu4XrXW2HTJIErHcFDD6nC02Mom8PwymeE2gvS0ZRBBaTlsOXEmdlrfLLOI7Bakrl/zWxCT8T/904f9QW/b06qtrCUdtFCqdjYs2Q2jAPX8c2XQd7Kr/wfV8vwIPs4Ga1ixe5Xrr/YFLTYfKIvWzM6ZtwXZdX7lxXG0L+sxXHcW5t254opRzawQ0S72+dPmjTroIgOP0CQSMt5LDn1T8AAAAASUVORK5CYII=);
|
||||
}
|
||||
|
||||
.selectable {
|
||||
-webkit-touch-callout: text;
|
||||
-webkit-user-select: text;
|
||||
-khtml-user-select: text;
|
||||
-moz-user-select: text;
|
||||
-ms-user-select: text;
|
||||
user-select: text;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.color-box {
|
||||
display: inline-block;
|
||||
width: 15pt;
|
||||
height: 15pt;
|
||||
border: 0.75pt solid black;
|
||||
margin: 1.5pt;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.color-box.highlight {
|
||||
width: 13.5pt;
|
||||
height: 13.5pt;
|
||||
border: 1.5pt solid black;
|
||||
}
|
||||
|
||||
|
||||
.section-header, .sub-section-header {
|
||||
display: table;
|
||||
width: 100%;
|
||||
margin: 22px -12px 0 -12px;
|
||||
padding: 14px 12px 0 12px;
|
||||
font-family: Raleway-Regular;
|
||||
font-size: 12px;
|
||||
color: #afafaf;
|
||||
height: 28px;
|
||||
text-transform: uppercase;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
position: relative;
|
||||
background: #404040 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAqCAIAAAAbNW1vAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAmSURBVChTY1BFAgzhSIDBAQmMcoYHRwIJMCgjAQZ9JMBgBQdWVgBh5XmBV5A2FQAAAABJRU5ErkJggg==) repeat-x top left;
|
||||
}
|
||||
|
||||
.sub-section-header, .no-collapse {
|
||||
background: #404040 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAAjSURBVBhXY1RVVf3PgARYjIyMoEwIYHRwcEBRwQSloYCBAQCwjgPMiI7W2QAAAABJRU5ErkJggg==) repeat-x top left;
|
||||
}
|
||||
|
||||
.section-header:first-child {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
background: none;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.sub-section-header {
|
||||
margin-bottom: -10px;
|
||||
}
|
||||
|
||||
.section-header span {
|
||||
font-family: HiFi-Glyphs;
|
||||
font-size: 30px;
|
||||
float: right;
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 6px;
|
||||
}
|
||||
|
||||
.section-header[collapsed="true"] {
|
||||
margin-bottom: -22px;
|
||||
}
|
||||
|
||||
.text-group[collapsed="true"] ~ .text-group,
|
||||
.zone-group[collapsed="true"] ~ .zone-group,
|
||||
.web-group[collapsed="true"] ~ .web-group,
|
||||
.hyperlink-group[collapsed="true"] ~ .hyperlink-group,
|
||||
.spatial-group[collapsed="true"] ~ .spatial-group,
|
||||
.physical-group[collapsed="true"] ~ .physical-group,
|
||||
.behavior-group[collapsed="true"] ~ .behavior-group,
|
||||
.model-group[collapsed="true"] ~ .model-group,
|
||||
.light-group[collapsed="true"] ~ .light-group {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
||||
.property {
|
||||
display: table;
|
||||
width: 100%;
|
||||
margin-top: 22px;
|
||||
min-height: 29px;
|
||||
}
|
||||
|
||||
.property label {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
font-family: Raleway-SemiBold;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.value {
|
||||
display: block;
|
||||
min-height: 18px;
|
||||
}
|
||||
.value label {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
width: 48px;
|
||||
}
|
||||
.value span {
|
||||
font-family: FiraSans-SemiBold;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.checkbox + .checkbox {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.checkbox-sub-props {
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.property .number {
|
||||
float: left;
|
||||
}
|
||||
.property .number + .number {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.text label, .url label, .number label, .textarea label, .rgb label, .xyz label, .pyr label, .dropdown label, .gen label {
|
||||
float: left;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.number > input {
|
||||
clear: both;
|
||||
float: left;
|
||||
}
|
||||
.number > span {
|
||||
clear: both;
|
||||
float: left;
|
||||
}
|
||||
.xyz > div, .pyr > div, .gen > div {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.unit {
|
||||
padding-left: 4px;
|
||||
vertical-align: top;
|
||||
position: relative;
|
||||
top: 5px;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
position: relative;
|
||||
margin-bottom: -17px;
|
||||
}
|
||||
|
||||
.dropdown select {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.dropdown dl {
|
||||
clear: both;
|
||||
}
|
||||
.dropdown dl {
|
||||
font-family: FiraSans-SemiBold;
|
||||
font-size: 15px;
|
||||
width: 172px;
|
||||
height: 28px;
|
||||
padding: 0 28px 0 12px;
|
||||
|
||||
color: #afafaf;
|
||||
background: linear-gradient(#7d7d7d 20%, #686a68 100%);
|
||||
|
||||
position: relative;
|
||||
}
|
||||
.dropdown dl[dropped="true"] {
|
||||
color: #404040;
|
||||
background: linear-gradient(#afafaf, #afafaf);
|
||||
}
|
||||
|
||||
.dropdown dt {
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
border-right: 1px solid #121212;
|
||||
width: 100%;
|
||||
}
|
||||
.dropdown dt:hover {
|
||||
color: #404040;
|
||||
}
|
||||
.dropdown dt:focus {
|
||||
outline: none;
|
||||
}
|
||||
.dropdown dt span:first-child {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
top: 5px;
|
||||
}
|
||||
.dropdown dt span:last-child {
|
||||
font-family: HiFi-Glyphs;
|
||||
font-size: 42px;
|
||||
float: right;
|
||||
margin-right: -48px;
|
||||
position: relative;
|
||||
left: -12px;
|
||||
top: -11px;
|
||||
}
|
||||
|
||||
.dropdown dd {
|
||||
position: absolute;
|
||||
top: 28px;
|
||||
left: 3px;
|
||||
display: none;
|
||||
}
|
||||
.dropdown dl[dropped="true"] dd {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dropdown li {
|
||||
list-style-type: none;
|
||||
padding: 3px 0 1px 12px;
|
||||
width: 200px;
|
||||
height: auto;
|
||||
font-family: FiraSans-SemiBold;
|
||||
font-size: 15px;
|
||||
color: #404040;
|
||||
background-color: #afafaf
|
||||
}
|
||||
.dropdown li:hover {
|
||||
background-color: #00b4ef;
|
||||
}
|
||||
|
||||
|
||||
div.refresh {
|
||||
box-sizing: border-box;
|
||||
padding-right: 44px;
|
||||
}
|
||||
div.refresh input[type="button"] {
|
||||
float: right;
|
||||
margin-right: -44px;
|
||||
}
|
||||
|
||||
.color-picker {
|
||||
box-sizing: border-box;
|
||||
float: left;
|
||||
margin-bottom: 12px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border: 4px solid #afafaf;
|
||||
border-radius: 4px;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABTSURBVChTjcxBDoAwCETRwTs33sFjwB6uaCE1Ggvav5qQF7CSqu40dllHjYiOT3gh3yV8Ii+Fb+RNMEP9hm3sKENmBhG5P1aImWMH/EMerSAAOAFgTC/R8ZXSXAAAAABJRU5ErkJggg==);
|
||||
background-position: bottom right;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.color-picker:focus {
|
||||
outline: none;
|
||||
}
|
||||
.color-picker[active="true"] {
|
||||
border-color: #000;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABDSURBVChTjcoLCkAhCETRNq0tf97Y5xGZ1gVJ45TH6njThIO+xk2UwhWFcEdH6JCqOuiQiMDi/hcii3crRRb/7ggAPvIMVihQwvSXAAAAAElFTkSuQmCC);
|
||||
}
|
||||
|
||||
.rgb label {
|
||||
float: left;
|
||||
margin-top: 10px;
|
||||
margin-left: 12px;
|
||||
}
|
||||
.rgb label + * {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.tuple {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
.tuple div {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
min-width: 120px;
|
||||
min-height: 1px;
|
||||
}
|
||||
.tuple div:nth-child(1) {
|
||||
float: left;
|
||||
}
|
||||
.tuple div:nth-child(2) {
|
||||
}
|
||||
.tuple div:nth-child(3) {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.rgb .tuple input {
|
||||
padding-left: 65px;
|
||||
}
|
||||
.xyz .tuple input {
|
||||
padding-left: 25px;
|
||||
}
|
||||
.pyr .tuple input {
|
||||
padding-left: 45px;
|
||||
}
|
||||
|
||||
.tuple div > label:first-child {
|
||||
float: left;
|
||||
}
|
||||
.tuple div > label + input {
|
||||
clear: both;
|
||||
float: left;
|
||||
}
|
||||
.tuple div input + label {
|
||||
display: inline !important;
|
||||
float: none !important;
|
||||
position: absolute;
|
||||
margin-top: 8px;
|
||||
margin-left: 6px;
|
||||
left: 0;
|
||||
font-family: FiraSans-SemiBold;
|
||||
font-size: 12px;
|
||||
}
|
||||
.tuple .red + label, .tuple .x + label, .tuple .pitch + label {
|
||||
color: #e2334d;
|
||||
}
|
||||
.tuple .green + label, .tuple .y + label, .tuple .yaw + label {
|
||||
color: #1ac567;
|
||||
}
|
||||
.tuple .blue + label, .tuple .z + label, .tuple .roll + label {
|
||||
color: #1080b8;
|
||||
}
|
||||
|
||||
.tuple .red:focus, .tuple .x:focus, .tuple .pitch:focus {
|
||||
outline-color: #e2334d;
|
||||
}
|
||||
.tuple .green:focus, .tuple .y:focus, .tuple .yaw:focus {
|
||||
outline-color: #1ac567;
|
||||
}
|
||||
tuple, .blue:focus, .tuple .z:focus, .tuple .roll:focus {
|
||||
outline-color: #1080b8;
|
||||
}
|
||||
|
||||
.xyz .buttons input {
|
||||
margin-top: 12px;
|
||||
}
|
||||
.xyz .buttons span {
|
||||
word-wrap: nowrap;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.row input {
|
||||
float: left;
|
||||
}
|
||||
.row input[type=button] {
|
||||
margin-left: 8px;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: #2e2e2e;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #696969;
|
||||
border: 2px solid #2e2e2e;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/* FIXME: Revisit textarea resizer/corner when move to Qt 5.6 or later: see if can get resizer/corner to always be visible and
|
||||
have correct background color with and without scrollbars. */
|
||||
textarea:enabled::-webkit-resizer {
|
||||
background-size: 10px 10px;
|
||||
background: #252525 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAXSURBVChTY1RVVWXADZigNA4wMqUZGACS3gCD5UUtKAAAAABJRU5ErkJggg==) no-repeat bottom right;
|
||||
}
|
||||
textarea:focus::-webkit-resizer {
|
||||
background-size: 10px 10px;
|
||||
background: #000000 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACOSURBVChThdC5DQQhDAXQvyRI5LQxFdABARWQElAPogYkiqEWQhLYGe8xxzJaS5a/8AuQHwDG2n+Lvee0hBDQWlO+hRvy3mNZFjDG5vCDOOeIMaL3/guPKISAWiu9n+AVSSlhraXdF86Qcw6tNdoTvEOlFOScd6iUOv3JGEMopYQx9jNvaawnoHnNr8Z4AuRLPOq2gPgnAAAAAElFTkSuQmCC) no-repeat bottom right;
|
||||
}
|
||||
textarea:enabled[scrolling="true"]::-webkit-resizer {
|
||||
background-size: 10px 10px;
|
||||
background: #2e2e2e url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACKSURBVChTjdAxDsMgDAXQT4UYuQIzCwsSKxsSJ4YDoByDY7AwUOG2aZMQqX+xhd9gzIwxA3/k8a7LCCFgraX+Fk4UY4RSCoyxNfwgzjlyzhhjXOEvSimhtUbvB3hGUkp472m2wxUKIaD3TnOCd6jWim3bvlBrfdjJOUeolEJoZj/4PMH83bl/BXgCWSs2Z09IjgoAAAAASUVORK5CYII=) no-repeat bottom right;
|
||||
}
|
||||
|
||||
|
||||
#entity-list-header {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
#delete {
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
background-color: #ff0000;
|
||||
}
|
||||
|
||||
#entity-list {
|
||||
position: relative; /* New positioning context. */
|
||||
}
|
||||
|
||||
#search-area {
|
||||
padding-right: 148px;
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
|
||||
#filter {
|
||||
width: 98%;
|
||||
}
|
||||
|
||||
#radius-and-unit {
|
||||
float: right;
|
||||
margin-right: -148px;
|
||||
}
|
||||
|
||||
|
||||
#entity-table-scroll {
|
||||
/* Height is set by JavaScript. */
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
padding-top: 28px; /* Space for header and footer outside of scroll region. */
|
||||
margin-top: 28px;
|
||||
border-left: 2px solid #575757;
|
||||
border-right: 2px solid #575757;
|
||||
}
|
||||
|
||||
#entity-table-scroll, #entity-table {
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
|
||||
#entity-table {
|
||||
margin-top: -28px;
|
||||
margin-bottom: -18px;
|
||||
table-layout: fixed;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#entity-table thead {
|
||||
border: 2px solid #575757;
|
||||
border-top-left-radius: 7px;
|
||||
border-top-right-radius: 7px;
|
||||
border-bottom: 1px solid #575757;
|
||||
}
|
||||
|
||||
#entity-table tfoot {
|
||||
border: 2px solid #575757;
|
||||
border-bottom-left-radius: 7px;
|
||||
border-bottom-right-radius: 7px;
|
||||
border-top: 1px solid #575757;
|
||||
}
|
||||
|
||||
#entity-table thead tr, #entity-table thead tr th,
|
||||
#entity-table tfoot tr, #entity-table tfoot tr td {
|
||||
background: none;
|
||||
}
|
||||
|
||||
#entity-table th:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#col-type {
|
||||
width: 16%;
|
||||
}
|
||||
#col-name {
|
||||
width: 42%;
|
||||
}
|
||||
#col-url {
|
||||
width: 42%;
|
||||
}
|
||||
|
||||
#entity-table thead {
|
||||
position: absolute;
|
||||
top: 49px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#entity-table thead th {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#entity-table tfoot {
|
||||
position: absolute;
|
||||
bottom: -21px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#no-entities {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 80px;
|
||||
padding: 12px;
|
||||
font-family: FiraSans-SemiBold;
|
||||
font-size: 15px;
|
||||
font-style: italic;
|
||||
color: #afafaf;
|
||||
}
|
||||
|
||||
|
||||
#properties-list .property:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
#property-id::selection {
|
||||
color: #000000;
|
||||
background-color: #00b4ef;
|
||||
}
|
||||
|
||||
|
||||
input#dimension-rescale-button {
|
||||
min-width: 50px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
|
||||
.color-set label, .color-set span {
|
||||
display: block;
|
||||
}
|
||||
.color-set span {
|
||||
padding-top: 2px;
|
||||
}
|
|
@ -1,6 +1,16 @@
|
|||
<!--
|
||||
// entityList.html
|
||||
//
|
||||
// Created by Ryan Huffman on 19 Nov 2014
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
-->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
<link rel="stylesheet" type="text/css" href="edit-style.css">
|
||||
<script src="list.min.js"></script>
|
||||
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||
<script type="text/javascript" src="eventBridgeLoader.js"></script>
|
||||
|
@ -26,8 +36,10 @@
|
|||
elDelete = document.getElementById("delete");
|
||||
elTeleport = document.getElementById("teleport");
|
||||
elRadius = document.getElementById("radius");
|
||||
elFooter = document.getElementById("footer-text");
|
||||
elNoEntitiesMessage = document.getElementById("no-entities");
|
||||
elNoEntitiesRadius = document.getElementById("no-entities-radius");
|
||||
elEntityTableScroll = document.getElementById("entity-table-scroll");
|
||||
|
||||
document.getElementById("entity-name").onclick = function() {
|
||||
setSortColumn('name');
|
||||
|
@ -168,6 +180,17 @@
|
|||
notFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedEntities.length > 1) {
|
||||
elFooter.firstChild.nodeValue = selectedEntities.length + " entities selected";
|
||||
} else if (selectedEntities.length === 1) {
|
||||
elFooter.firstChild.nodeValue = "1 entity selected";
|
||||
} else if (entityList.visibleItems.length === 1) {
|
||||
elFooter.firstChild.nodeValue = "1 entity found";
|
||||
} else {
|
||||
elFooter.firstChild.nodeValue = entityList.visibleItems.length + " entities found";
|
||||
}
|
||||
|
||||
return notFound;
|
||||
}
|
||||
|
||||
|
@ -214,57 +237,90 @@
|
|||
} else if (data.type == "update") {
|
||||
var newEntities = data.entities;
|
||||
if (newEntities.length == 0) {
|
||||
elEntityTable.style.display = "none";
|
||||
elNoEntitiesMessage.style.display = "block";
|
||||
} else {
|
||||
elEntityTable.style.display = "table";
|
||||
elNoEntitiesMessage.style.display = "none";
|
||||
for (var i = 0; i < newEntities.length; i++) {
|
||||
var id = newEntities[i].id;
|
||||
addEntity(id, newEntities[i].name, newEntities[i].type, newEntities[i].url);
|
||||
}
|
||||
updateSelectedEntities(data.selectedIDs);
|
||||
resize();
|
||||
}
|
||||
}
|
||||
});
|
||||
setTimeout(refreshEntities, 1000);
|
||||
}
|
||||
|
||||
function resize() {
|
||||
// Take up available window space
|
||||
elEntityTableScroll.style.height = window.innerHeight - 232;
|
||||
|
||||
// Update the widths of the header cells to match the body
|
||||
var tds = document.querySelectorAll("#entity-table-body tr:first-child td");
|
||||
var ths = document.querySelectorAll("#entity-table thead th");
|
||||
if (tds.length >= ths.length) {
|
||||
for (var i = 0; i < ths.length; i++) {
|
||||
ths[i].style.width = tds[i].offsetWidth;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.onresize = resize;
|
||||
resize();
|
||||
});
|
||||
}
|
||||
|
||||
// Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked
|
||||
document.addEventListener("contextmenu", function (event) {
|
||||
event.preventDefault();
|
||||
}, false);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload='loaded();'>
|
||||
<div id="entity-list-header">
|
||||
<input type="button" id="refresh" value="Refresh" />
|
||||
<input type="button" id="teleport" value="Teleport" />
|
||||
<input type="button" id="delete" style="background-color: rgb(244, 64, 64); float: right" value="Delete" />
|
||||
<input type="button" class="glyph" id="refresh" value="F" />
|
||||
<input type="button" id="teleport" value="Jump To Selection" />
|
||||
<input type="button" class="red" id="delete" value="Delete" />
|
||||
</div>
|
||||
|
||||
<div id="entity-list">
|
||||
<div id="search-area">
|
||||
<input type="text" class="search" id="filter" placeholder="Filter" />
|
||||
<span id="radius-and-unit"><input type="number" id="radius" value="100" /> m</span>
|
||||
<span id="radius-and-unit"><input type="number" id="radius" value="100" /><span class="unit">m</span></span>
|
||||
</div>
|
||||
<div id="entity-table-scroll">
|
||||
<table id="entity-table">
|
||||
<colgroup>
|
||||
<col span="1" id="col-type" />
|
||||
<col span="1" id="col-name" />
|
||||
<col span="1" id="col-url" />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th id="entity-type" data-sort="type">Type <span class="sort-order" style="display: inline"> ▾</span></th>
|
||||
<th id="entity-name" data-sort="type">Name <span class="sort-order" style="display: none"> ▾</span></th>
|
||||
<th id="entity-url" data-sort="url">File <span class="sort-order" style="display: none"> ▾</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="list" id="entity-table-body">
|
||||
<tr>
|
||||
<td class="type">Type</td>
|
||||
<td class="name">Name</td>
|
||||
<td class="url"><div class='outer'><div class='inner'>URL</div></div></td>
|
||||
<td class="id" style="display: none">Type</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td id="footer-text" colspan="3">Footer text</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
<div id="no-entities">
|
||||
No entities found within a <span id="no-entities-radius">100</span> meter radius. Try moving to a different location and refreshing.
|
||||
</div>
|
||||
</div>
|
||||
<table id="entity-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th id="entity-type" data-sort="type">Type <span class="sort-order" style="display: inline"> ▾</span></th>
|
||||
<th id="entity-name" data-sort="type">Name <span class="sort-order" style="display: inline"> ▾</span></th>
|
||||
<th id="entity-url" data-sort="url">URL <span class="sort-order" style="display: none"> ▾</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="list" id="entity-table-body">
|
||||
<tr>
|
||||
<td class="id" style="display: none">Type</td>
|
||||
<td class="type">Type</td>
|
||||
<td class="name">Name</td>
|
||||
<td class="url"><div class='outer'><div class='inner'>URL</div></div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="no-entities">
|
||||
No entities found within a <span id="no-entities-radius">100</span> meter radius. Try moving to a different location and refreshing.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,16 @@
|
|||
<!--
|
||||
// gridControls.html
|
||||
//
|
||||
// Created by Ryan Huffman on 6 Nov 2014
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
-->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
<link rel="stylesheet" type="text/css" href="edit-style.css">
|
||||
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||
<script type="text/javascript" src="eventBridgeLoader.js"></script>
|
||||
<script>
|
||||
|
@ -109,61 +119,63 @@
|
|||
|
||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'init' }));
|
||||
});
|
||||
|
||||
// Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked
|
||||
document.addEventListener("contextmenu", function (event) {
|
||||
event.preventDefault();
|
||||
}, false);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload='loaded();'>
|
||||
<div class="grid-section">
|
||||
<div id="grid-section">
|
||||
|
||||
<div class="property-section">
|
||||
<label>Visible</label>
|
||||
<div class="section-header">
|
||||
<label>Editing Grid</label>
|
||||
</div>
|
||||
|
||||
<div class="property checkbox">
|
||||
<input type='checkbox' id="horiz-grid-visible">
|
||||
<label for="horiz-grid-visible">Visible</label>
|
||||
</div>
|
||||
|
||||
<div class="property checkbox">
|
||||
<input type="checkbox" id="snap-to-grid">
|
||||
<label for="snap-to-grid">Snap entities to grid</label>
|
||||
</div>
|
||||
|
||||
<div class="property">
|
||||
<div class="number">
|
||||
<label>Major grid size</label>
|
||||
<span>
|
||||
<input type="number" id="major-spacing" min="1" step="1" /><span class="unit">m</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="number">
|
||||
<label>Minor grid size</label>
|
||||
<span>
|
||||
<input type="number" id="minor-spacing" min="0.2" step="0.2" /><span class="unit">m</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="property number">
|
||||
<label>Position (Y axis)</label>
|
||||
<span>
|
||||
<input type='checkbox' id="horiz-grid-visible">
|
||||
<input type="number" id="horiz-y" step="0.1" /><span class="unit">m</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="property-section">
|
||||
<label>Snap to grid</label>
|
||||
<span>
|
||||
<input type='checkbox' id="snap-to-grid">
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div id="horizontal-position" class="property-section">
|
||||
<label>Position (Y Axis)</label>
|
||||
<span>
|
||||
<input type='number' id="horiz-y" class="number" step="0.1"></input>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="property-section">
|
||||
<label>Minor Grid Size</label>
|
||||
<span>
|
||||
<input type='number' id="minor-spacing" min="0.2" step="0.2"></input>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="property-section">
|
||||
<label>Major Grid Every</label>
|
||||
<span>
|
||||
<input type='number' id="major-spacing" min="1" step="1", ></input>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="property-section">
|
||||
<label>Grid Color</label>
|
||||
<div class="property color-set">
|
||||
<label>Grid line color</label>
|
||||
<span id="grid-colors"></span>
|
||||
</div>
|
||||
|
||||
<div class="property-section">
|
||||
<div class="property">
|
||||
<span>
|
||||
<input type="button" id="move-to-selection" value="Move to Selection">
|
||||
</span>
|
||||
</div>
|
||||
<div class="property-section">
|
||||
<span>
|
||||
<input type="button" id="move-to-avatar" value="Move to Avatar">
|
||||
</span>
|
||||
<input type="button" id="move-to-selection" value="Align To Selection">
|
||||
<input type="button" id="move-to-avatar" value="Align To Avatar">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -5,7 +5,7 @@ EntityListTool = function(opts) {
|
|||
|
||||
var url = ENTITY_LIST_HTML_URL;
|
||||
var webView = new OverlayWebWindow({
|
||||
title: 'Entities', source: url, toolWindow: true
|
||||
title: 'Entity List', source: url, toolWindow: true
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -20,6 +20,12 @@
|
|||
<script type="text/javascript" src="../html/eventBridgeLoader.js"></script>
|
||||
<script type="text/javascript" src="particleExplorer.js?v42"></script>
|
||||
<script>
|
||||
function loaded() {
|
||||
// Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked
|
||||
document.addEventListener("contextmenu", function (event) {
|
||||
event.preventDefault();
|
||||
}, false);
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
|
||||
|
@ -60,7 +66,7 @@ body{
|
|||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<body onload="loaded();">
|
||||
<div class="importer">
|
||||
<input type='text' id="importer-input" placeholder="Import: Paste JSON here." onkeypress="handleInputKeyPress(event)">
|
||||
<div class = "exported-props-section">
|
||||
|
|
|
@ -120,27 +120,95 @@ function menuItemEvent(menuItem) {
|
|||
if (menuItem.endsWith(" for Output")) {
|
||||
var selectedDevice = menuItem.trimStartsWith("Use ").trimEndsWith(" for Output");
|
||||
print("output audio selection..." + selectedDevice);
|
||||
Menu.menuItemEvent.disconnect(menuItemEvent);
|
||||
Menu.menuItemEvent.disconnect(menuItemEvent);
|
||||
Menu.setIsOptionChecked(selectedOutputMenu, false);
|
||||
selectedOutputMenu = menuItem;
|
||||
Menu.setIsOptionChecked(selectedOutputMenu, true);
|
||||
if (AudioDevice.setOutputDevice(selectedDevice)) {
|
||||
Settings.setValue(OUTPUT_DEVICE_SETTING, selectedDevice);
|
||||
}
|
||||
Menu.menuItemEvent.connect(menuItemEvent);
|
||||
Menu.menuItemEvent.connect(menuItemEvent);
|
||||
} else if (menuItem.endsWith(" for Input")) {
|
||||
var selectedDevice = menuItem.trimStartsWith("Use ").trimEndsWith(" for Input");
|
||||
print("input audio selection..." + selectedDevice);
|
||||
Menu.menuItemEvent.disconnect(menuItemEvent);
|
||||
Menu.menuItemEvent.disconnect(menuItemEvent);
|
||||
Menu.setIsOptionChecked(selectedInputMenu, false);
|
||||
selectedInputMenu = menuItem;
|
||||
Menu.setIsOptionChecked(selectedInputMenu, true);
|
||||
if (AudioDevice.setInputDevice(selectedDevice)) {
|
||||
Settings.setValue(INPUT_DEVICE_SETTING, selectedDevice);
|
||||
}
|
||||
Menu.menuItemEvent.connect(menuItemEvent);
|
||||
Menu.menuItemEvent.connect(menuItemEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Menu.menuItemEvent.connect(menuItemEvent);
|
||||
|
||||
// Some HMDs (like Oculus CV1) have a built in audio device. If they
|
||||
// do, then this function will handle switching to that device automatically
|
||||
// when you goActive with the HMD active.
|
||||
var wasHmdMounted = false; // assume it's un-mounted to start
|
||||
var switchedAudioInputToHMD = false;
|
||||
var switchedAudioOutputToHMD = false;
|
||||
var previousSelectedInputAudioDevice = "";
|
||||
var previousSelectedOutputAudioDevice = "";
|
||||
|
||||
function restoreAudio() {
|
||||
if (switchedAudioInputToHMD) {
|
||||
print("switching back from HMD preferred audio input to:" + previousSelectedInputAudioDevice);
|
||||
menuItemEvent("Use " + previousSelectedInputAudioDevice + " for Input");
|
||||
}
|
||||
if (switchedAudioOutputToHMD) {
|
||||
print("switching back from HMD preferred audio output to:" + previousSelectedOutputAudioDevice);
|
||||
menuItemEvent("Use " + previousSelectedOutputAudioDevice + " for Output");
|
||||
}
|
||||
}
|
||||
|
||||
function checkHMDAudio() {
|
||||
// Mounted state is changing... handle switching
|
||||
if (HMD.mounted != wasHmdMounted) {
|
||||
print("HMD mounted changed...");
|
||||
|
||||
// We're putting the HMD on... switch to those devices
|
||||
if (HMD.mounted) {
|
||||
print("NOW mounted...");
|
||||
var hmdPreferredAudioInput = HMD.preferredAudioInput();
|
||||
var hmdPreferredAudioOutput = HMD.preferredAudioOutput();
|
||||
print("hmdPreferredAudioInput:" + hmdPreferredAudioInput);
|
||||
print("hmdPreferredAudioOutput:" + hmdPreferredAudioOutput);
|
||||
|
||||
|
||||
var hmdHasPreferredAudio = (hmdPreferredAudioInput !== "") || (hmdPreferredAudioOutput !== "");
|
||||
if (hmdHasPreferredAudio) {
|
||||
print("HMD has preferred audio!");
|
||||
previousSelectedInputAudioDevice = Settings.getValue(INPUT_DEVICE_SETTING);
|
||||
previousSelectedOutputAudioDevice = Settings.getValue(OUTPUT_DEVICE_SETTING);
|
||||
print("previousSelectedInputAudioDevice:" + previousSelectedInputAudioDevice);
|
||||
print("previousSelectedOutputAudioDevice:" + previousSelectedOutputAudioDevice);
|
||||
if (hmdPreferredAudioInput != previousSelectedInputAudioDevice && hmdPreferredAudioInput !== "") {
|
||||
print("switching to HMD preferred audio input to:" + hmdPreferredAudioInput);
|
||||
switchedAudioInputToHMD = true;
|
||||
menuItemEvent("Use " + hmdPreferredAudioInput + " for Input");
|
||||
}
|
||||
if (hmdPreferredAudioOutput != previousSelectedOutputAudioDevice && hmdPreferredAudioOutput !== "") {
|
||||
print("switching to HMD preferred audio output to:" + hmdPreferredAudioOutput);
|
||||
switchedAudioOutputToHMD = true;
|
||||
menuItemEvent("Use " + hmdPreferredAudioOutput + " for Output");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print("HMD NOW un-mounted...");
|
||||
restoreAudio();
|
||||
}
|
||||
}
|
||||
wasHmdMounted = HMD.mounted;
|
||||
}
|
||||
|
||||
Script.update.connect(checkHMDAudio);
|
||||
|
||||
Script.scriptEnding.connect(function () {
|
||||
restoreAudio();
|
||||
Menu.menuItemEvent.disconnect(menuItemEvent);
|
||||
Script.update.disconnect(checkHMDAudio);
|
||||
});
|
||||
|
|
|
@ -106,6 +106,13 @@ elseif(WIN32)
|
|||
|
||||
# add an executable that also has the icon itself and the configured rc file as resources
|
||||
add_executable(${TARGET_NAME} WIN32 ${INTERFACE_SRCS} ${QM} ${CONFIGURE_ICON_RC_OUTPUT})
|
||||
|
||||
add_custom_command(
|
||||
TARGET ${TARGET_NAME}
|
||||
POST_BUILD
|
||||
COMMAND "mt.exe" -manifest "${CMAKE_CURRENT_SOURCE_DIR}/interface.exe.manifest" -inputresource:"$<TARGET_FILE:${TARGET_NAME}>"\;\#1 -outputresource:"$<TARGET_FILE:${TARGET_NAME}>"\;\#1
|
||||
COMMENT "Adding OS version support manifest to exe"
|
||||
)
|
||||
else()
|
||||
add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM})
|
||||
endif()
|
||||
|
|
15
interface/interface.exe.manifest
Normal file
15
interface/interface.exe.manifest
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!--This Id value indicates the application supports Windows 7 functionality-->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
<!--This Id value indicates the application supports Windows 8 functionality-->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
<!--This Id value indicates the application supports Windows 8.1 functionality-->
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||
<!--This Id value indicates the application supports Windows 10 functionality-->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
</assembly>
|
Binary file not shown.
|
@ -21,7 +21,7 @@ import "dialogs"
|
|||
Window {
|
||||
id: root
|
||||
objectName: "AssetServer"
|
||||
title: "My Asset Server"
|
||||
title: "Asset Browser"
|
||||
resizable: true
|
||||
destroyOnInvisible: true
|
||||
x: 40; y: 40
|
||||
|
@ -125,6 +125,10 @@ Window {
|
|||
}, false);
|
||||
}
|
||||
|
||||
function clear() {
|
||||
Assets.mappingModel.clear();
|
||||
}
|
||||
|
||||
function reload() {
|
||||
Assets.mappingModel.refresh();
|
||||
treeView.selection.clear();
|
||||
|
@ -416,7 +420,6 @@ Window {
|
|||
anchors.top: assetDirectory.bottom
|
||||
anchors.bottom: uploadSection.top
|
||||
anchors.margins: 12
|
||||
anchors.bottomMargin: 0
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
|
@ -445,7 +448,7 @@ Window {
|
|||
name: "Upload A File"
|
||||
spacing: hifi.dimensions.contentSpacing.y
|
||||
anchors.bottom: parent.bottom
|
||||
height: 130
|
||||
height: 92
|
||||
|
||||
Item {
|
||||
height: parent.height
|
||||
|
|
|
@ -1,13 +1,25 @@
|
|||
//
|
||||
// ToolWindow.qml
|
||||
//
|
||||
// Created by Bradley Austin Davis on 12 Jan 2016
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import QtWebEngine 1.1
|
||||
import QtWebChannel 1.0
|
||||
import Qt.labs.settings 1.0
|
||||
|
||||
import "windows" as Windows
|
||||
import "controls" as Controls
|
||||
import "windows-uit"
|
||||
import "controls-uit"
|
||||
import "styles-uit"
|
||||
|
||||
Windows.Window {
|
||||
Window {
|
||||
id: toolWindow
|
||||
resizable: true
|
||||
objectName: "ToolWindow"
|
||||
|
@ -15,9 +27,13 @@ Windows.Window {
|
|||
destroyOnInvisible: false
|
||||
closable: true
|
||||
visible: false
|
||||
width: 384; height: 640;
|
||||
title: "Tools"
|
||||
title: "Edit"
|
||||
property alias tabView: tabView
|
||||
implicitWidth: 520; implicitHeight: 695
|
||||
minSize: Qt.vector2d(400, 500)
|
||||
|
||||
HifiConstants { id: hifi }
|
||||
|
||||
onParentChanged: {
|
||||
if (parent) {
|
||||
x = 120;
|
||||
|
@ -32,8 +48,11 @@ Windows.Window {
|
|||
}
|
||||
|
||||
TabView {
|
||||
anchors.fill: parent
|
||||
id: tabView;
|
||||
width: pane.contentWidth
|
||||
height: pane.scrollHeight // Pane height so that don't use Window's scrollbars otherwise tabs may be scrolled out of view.
|
||||
property int tabCount: 0
|
||||
|
||||
Repeater {
|
||||
model: 4
|
||||
Tab {
|
||||
|
@ -43,7 +62,7 @@ Windows.Window {
|
|||
enabled: false
|
||||
property string originalUrl: "";
|
||||
|
||||
Controls.WebView {
|
||||
WebView {
|
||||
id: webView;
|
||||
anchors.fill: parent
|
||||
enabled: false
|
||||
|
@ -60,6 +79,61 @@ Windows.Window {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
style: TabViewStyle {
|
||||
|
||||
frame: Rectangle { // Background shown before content loads.
|
||||
anchors.fill: parent
|
||||
color: hifi.colors.baseGray
|
||||
}
|
||||
|
||||
frameOverlap: 0
|
||||
|
||||
tab: Rectangle {
|
||||
implicitWidth: text.width
|
||||
implicitHeight: 3 * text.height
|
||||
color: styleData.selected ? hifi.colors.black : hifi.colors.tabBackgroundDark
|
||||
|
||||
RalewayRegular {
|
||||
id: text
|
||||
text: styleData.title
|
||||
font.capitalization: Font.AllUppercase
|
||||
size: hifi.fontSizes.tabName
|
||||
width: tabView.tabCount > 1 ? styleData.availableWidth / tabView.tabCount : implicitWidth + 2 * hifi.dimensions.contentSpacing.x
|
||||
elide: Text.ElideRight
|
||||
color: styleData.selected ? hifi.colors.primaryHighlight : hifi.colors.lightGrayText
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
Rectangle { // Separator.
|
||||
width: 1
|
||||
height: parent.height
|
||||
color: hifi.colors.black
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
visible: styleData.index > 0
|
||||
|
||||
Rectangle {
|
||||
width: 1
|
||||
height: 1
|
||||
color: hifi.colors.baseGray
|
||||
anchors.left: parent.left
|
||||
anchors.bottom: parent.bottom
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { // Active underline.
|
||||
width: parent.width - (styleData.index > 0 ? 1 : 0)
|
||||
height: 1
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
color: styleData.selected ? hifi.colors.primaryHighlight : hifi.colors.baseGray
|
||||
}
|
||||
}
|
||||
|
||||
tabOverlap: 0
|
||||
}
|
||||
}
|
||||
|
||||
function updateVisiblity() {
|
||||
|
@ -129,6 +203,7 @@ Windows.Window {
|
|||
tab.originalUrl = "";
|
||||
tab.item.url = "about:blank";
|
||||
tab.item.enabled = false;
|
||||
tabView.tabCount--;
|
||||
}
|
||||
|
||||
function addWebTab(properties) {
|
||||
|
@ -150,6 +225,7 @@ Windows.Window {
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
if (properties.width) {
|
||||
tabView.width = Math.min(Math.max(tabView.width, properties.width), toolWindow.maxSize.x);
|
||||
}
|
||||
|
@ -169,6 +245,7 @@ Windows.Window {
|
|||
|
||||
var result = tab.item;
|
||||
result.enabled = true;
|
||||
tabView.tabCount++;
|
||||
console.log("Setting event bridge: " + eventBridge);
|
||||
result.eventBridgeWrapper.eventBridge = eventBridge;
|
||||
result.url = properties.source;
|
||||
|
|
|
@ -46,7 +46,7 @@ Column {
|
|||
Item {
|
||||
id: leadingSpace
|
||||
width: 1
|
||||
height: isFirst ? hifi.dimensions.contentSpacing.y : hifi.dimensions.controlInterlineHeight
|
||||
height: isFirst ? hifi.dimensions.contentSpacing.y : 0
|
||||
anchors.top: parent.top
|
||||
}
|
||||
|
||||
|
@ -97,11 +97,11 @@ Column {
|
|||
|
||||
HiFiGlyphs {
|
||||
anchors {
|
||||
verticalCenter: title.verticalCenter
|
||||
top: title.top
|
||||
topMargin: -9
|
||||
right: parent.right
|
||||
rightMargin: -4
|
||||
}
|
||||
y: -2
|
||||
size: hifi.fontSizes.disclosureButton
|
||||
text: isCollapsed ? hifi.glyphs.disclosureButtonExpand : hifi.glyphs.disclosureButtonCollapse
|
||||
color: hifi.colors.lightGrayText
|
||||
|
|
74
interface/resources/qml/controls-uit/WebView.qml
Normal file
74
interface/resources/qml/controls-uit/WebView.qml
Normal file
|
@ -0,0 +1,74 @@
|
|||
//
|
||||
// WebView.qml
|
||||
//
|
||||
// Created by Bradley Austin Davis on 12 Jan 2016
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtWebEngine 1.1
|
||||
|
||||
WebEngineView {
|
||||
id: root
|
||||
property var newUrl;
|
||||
|
||||
Component.onCompleted: {
|
||||
console.log("Connecting JS messaging to Hifi Logging")
|
||||
// Ensure the JS from the web-engine makes it to our logging
|
||||
root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
|
||||
console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " + message);
|
||||
});
|
||||
}
|
||||
|
||||
// FIXME hack to get the URL with the auth token included. Remove when we move to Qt 5.6
|
||||
Timer {
|
||||
id: urlReplacementTimer
|
||||
running: false
|
||||
repeat: false
|
||||
interval: 50
|
||||
onTriggered: url = newUrl;
|
||||
}
|
||||
|
||||
onUrlChanged: {
|
||||
console.log("Url changed to " + url);
|
||||
var originalUrl = url.toString();
|
||||
newUrl = urlHandler.fixupUrl(originalUrl).toString();
|
||||
if (newUrl !== originalUrl) {
|
||||
root.stop();
|
||||
if (urlReplacementTimer.running) {
|
||||
console.warn("Replacement timer already running");
|
||||
return;
|
||||
}
|
||||
urlReplacementTimer.start();
|
||||
}
|
||||
}
|
||||
|
||||
onFeaturePermissionRequested: {
|
||||
grantFeaturePermission(securityOrigin, feature, true);
|
||||
}
|
||||
|
||||
onLoadingChanged: {
|
||||
// Required to support clicking on "hifi://" links
|
||||
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
|
||||
var url = loadRequest.url.toString();
|
||||
if (urlHandler.canHandleUrl(url)) {
|
||||
if (urlHandler.handleUrl(url)) {
|
||||
root.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onNewViewRequested:{
|
||||
var component = Qt.createComponent("../Browser.qml");
|
||||
var newWindow = component.createObject(desktop);
|
||||
request.openIn(newWindow.webView)
|
||||
}
|
||||
|
||||
// This breaks the webchannel used for passing messages. Fixed in Qt 5.6
|
||||
// See https://bugreports.qt.io/browse/QTBUG-49521
|
||||
//profile: desktop.browserProfile
|
||||
}
|
|
@ -44,7 +44,7 @@ Preference {
|
|||
|
||||
children: [ contentContainer ]
|
||||
|
||||
height: contentContainer.height + (root.isLast ? 2 * hifi.dimensions.contentSpacing.y : 0)
|
||||
height: contentContainer.height + (contentContainer.isCollapsed ? 0 : hifi.dimensions.controlInterlineHeight)
|
||||
|
||||
Component.onCompleted: d.buildPreferences();
|
||||
|
||||
|
@ -111,7 +111,6 @@ Preference {
|
|||
|
||||
case Preference.Checkbox:
|
||||
checkBoxCount++;
|
||||
console.log("####### checkBoxCount = " + checkBoxCount);
|
||||
builder = checkboxBuilder;
|
||||
break;
|
||||
|
||||
|
|
|
@ -120,7 +120,7 @@ Window {
|
|||
}
|
||||
|
||||
HifiControls.VerticalSpacer {
|
||||
height: 2 // Table view draws a little taller than it's height.
|
||||
height: hifi.dimensions.controlInterlineHeight + 2 // Table view draws a little taller than it's height.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ Item {
|
|||
readonly property color lightGrayText: "#afafaf"
|
||||
readonly property color faintGray: "#e3e3e3"
|
||||
readonly property color primaryHighlight: "#00b4ef"
|
||||
readonly property color blueHighlight: "#00b4ef"
|
||||
readonly property color blueAccent: "#1080b8"
|
||||
readonly property color redHighlight: "#e2334d"
|
||||
readonly property color redAccent: "#b70a37"
|
||||
|
@ -75,12 +76,12 @@ Item {
|
|||
readonly property color lightGrayText80: "#ccafafaf"
|
||||
readonly property color faintGray80: "#cce3e3e3"
|
||||
readonly property color faintGray50: "#80e3e3e3"
|
||||
readonly property color locked: "#252525"
|
||||
|
||||
// Other colors
|
||||
readonly property color white: "#ffffff"
|
||||
readonly property color gray: "#808080"
|
||||
readonly property color black: "#000000"
|
||||
readonly property color locked: "#252525"
|
||||
// Semitransparent
|
||||
readonly property color white50: "#80ffffff"
|
||||
readonly property color white30: "#4dffffff"
|
||||
|
@ -115,6 +116,8 @@ Item {
|
|||
readonly property color dropDownDarkStart: "#7d7d7d"
|
||||
readonly property color dropDownDarkFinish: "#6b6a6b"
|
||||
readonly property color textFieldLightBackground: "#d4d4d4"
|
||||
readonly property color tabBackgroundDark: "#252525"
|
||||
readonly property color tabBackgroundLight: "#d4d4d4"
|
||||
}
|
||||
|
||||
Item {
|
||||
|
@ -152,6 +155,7 @@ Item {
|
|||
readonly property real sectionName: dimensions.largeScreen ? 12 : 10
|
||||
readonly property real inputLabel: dimensions.largeScreen ? 14 : 10
|
||||
readonly property real textFieldInput: dimensions.largeScreen ? 15 : 12
|
||||
readonly property real textFieldInputLabel: dimensions.largeScreen ? 13 : 9
|
||||
readonly property real tableText: dimensions.largeScreen ? 15 : 12
|
||||
readonly property real buttonLabel: dimensions.largeScreen ? 13 : 9
|
||||
readonly property real iconButton: dimensions.largeScreen ? 13 : 9
|
||||
|
@ -164,7 +168,7 @@ Item {
|
|||
readonly property real menuItem: dimensions.largeScreen ? 15 : 11
|
||||
readonly property real shortcutText: dimensions.largeScreen ? 13 : 9
|
||||
readonly property real carat: dimensions.largeScreen ? 38 : 30
|
||||
readonly property real disclosureButton: dimensions.largeScreen ? 20 : 15
|
||||
readonly property real disclosureButton: dimensions.largeScreen ? 30 : 22
|
||||
}
|
||||
|
||||
Item {
|
||||
|
|
|
@ -53,7 +53,7 @@ Fadable {
|
|||
property bool resizable: false
|
||||
|
||||
property vector2d minSize: Qt.vector2d(100, 100)
|
||||
property vector2d maxSize: Qt.vector2d(1280, 720)
|
||||
property vector2d maxSize: Qt.vector2d(1280, 800)
|
||||
|
||||
// The content to place inside the window, determined by the client
|
||||
default property var content
|
||||
|
|
|
@ -680,6 +680,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
|||
|
||||
connect(nodeList.data(), &NodeList::nodeAdded, this, &Application::nodeAdded);
|
||||
connect(nodeList.data(), &NodeList::nodeKilled, this, &Application::nodeKilled);
|
||||
connect(nodeList.data(), &NodeList::nodeActivated, this, &Application::nodeActivated);
|
||||
connect(nodeList.data(), &NodeList::uuidChanged, getMyAvatar(), &MyAvatar::setSessionUUID);
|
||||
connect(nodeList.data(), &NodeList::uuidChanged, this, &Application::setSessionUUID);
|
||||
connect(nodeList.data(), &NodeList::limitOfSilentDomainCheckInsReached, nodeList.data(), &NodeList::reset);
|
||||
|
@ -3215,6 +3216,25 @@ void Application::update(float deltaTime) {
|
|||
|
||||
updateLOD();
|
||||
|
||||
if (!_physicsEnabled && _processOctreeStatsCounter > 0) {
|
||||
|
||||
// process octree stats packets are sent in between full sends of a scene.
|
||||
// We keep physics disabled until we've recieved a full scene and everything near the avatar in that
|
||||
// scene is ready to compute its collision shape.
|
||||
|
||||
if (nearbyEntitiesAreReadyForPhysics()) {
|
||||
_physicsEnabled = true;
|
||||
getMyAvatar()->updateMotionBehaviorFromMenu();
|
||||
} else {
|
||||
auto characterController = getMyAvatar()->getCharacterController();
|
||||
if (characterController) {
|
||||
// if we have a character controller, disable it here so the avatar doesn't get stuck due to
|
||||
// a non-loading collision hull.
|
||||
characterController->setEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
PerformanceTimer perfTimer("devices");
|
||||
DeviceTracker::updateAll();
|
||||
|
@ -4124,6 +4144,27 @@ void Application::nodeAdded(SharedNodePointer node) {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::nodeActivated(SharedNodePointer node) {
|
||||
if (node->getType() == NodeType::AssetServer) {
|
||||
// asset server just connected - check if we have the asset browser showing
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
auto assetDialog = offscreenUi->getRootItem()->findChild<QQuickItem*>("AssetServer");
|
||||
|
||||
if (assetDialog) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
if (nodeList->getThisNodeCanRez()) {
|
||||
// call reload on the shown asset browser dialog to get the mappings (if permissions allow)
|
||||
QMetaObject::invokeMethod(assetDialog, "reload");
|
||||
} else {
|
||||
// we switched to an Asset Server that we can't modify, hide the Asset Browser
|
||||
assetDialog->setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Application::nodeKilled(SharedNodePointer node) {
|
||||
|
||||
// These are here because connecting NodeList::nodeKilled to OctreePacketProcessor::nodeKilled doesn't work:
|
||||
|
@ -4136,10 +4177,7 @@ void Application::nodeKilled(SharedNodePointer node) {
|
|||
|
||||
if (node->getType() == NodeType::AudioMixer) {
|
||||
QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "audioMixerKilled");
|
||||
}
|
||||
|
||||
if (node->getType() == NodeType::EntityServer) {
|
||||
|
||||
} else if (node->getType() == NodeType::EntityServer) {
|
||||
QUuid nodeUUID = node->getUUID();
|
||||
// see if this is the first we've heard of this node...
|
||||
_entityServerJurisdictions.withReadLock([&] {
|
||||
|
@ -4170,6 +4208,16 @@ void Application::nodeKilled(SharedNodePointer node) {
|
|||
} else if (node->getType() == NodeType::AvatarMixer) {
|
||||
// our avatar mixer has gone away - clear the hash of avatars
|
||||
DependencyManager::get<AvatarManager>()->clearOtherAvatars();
|
||||
} else if (node->getType() == NodeType::AssetServer) {
|
||||
// asset server going away - check if we have the asset browser showing
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
auto assetDialog = offscreenUi->getRootItem()->findChild<QQuickItem*>("AssetServer");
|
||||
|
||||
if (assetDialog) {
|
||||
// call reload on the shown asset browser dialog
|
||||
QMetaObject::invokeMethod(assetDialog, "clear");
|
||||
}
|
||||
}
|
||||
}
|
||||
void Application::trackIncomingOctreePacket(ReceivedMessage& message, SharedNodePointer sendingNode, bool wasStatsPacket) {
|
||||
|
@ -4260,22 +4308,7 @@ int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer
|
|||
});
|
||||
});
|
||||
|
||||
if (!_physicsEnabled) {
|
||||
if (nearbyEntitiesAreReadyForPhysics()) {
|
||||
// These stats packets are sent in between full sends of a scene.
|
||||
// We keep physics disabled until we've recieved a full scene and everything near the avatar in that
|
||||
// scene is ready to compute its collision shape.
|
||||
_physicsEnabled = true;
|
||||
getMyAvatar()->updateMotionBehaviorFromMenu();
|
||||
} else {
|
||||
auto characterController = getMyAvatar()->getCharacterController();
|
||||
if (characterController) {
|
||||
// if we have a character controller, disable it here so the avatar doesn't get stuck due to
|
||||
// a non-loading collision hull.
|
||||
characterController->setEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
_processOctreeStatsCounter++;
|
||||
|
||||
return statsMessageLength;
|
||||
}
|
||||
|
|
|
@ -312,6 +312,7 @@ private slots:
|
|||
void domainChanged(const QString& domainHostname);
|
||||
void updateWindowTitle();
|
||||
void nodeAdded(SharedNodePointer node);
|
||||
void nodeActivated(SharedNodePointer node);
|
||||
void nodeKilled(SharedNodePointer node);
|
||||
void packetSent(quint64 length);
|
||||
void updateDisplayMode();
|
||||
|
@ -515,6 +516,8 @@ private:
|
|||
|
||||
std::map<void*, std::function<void()>> _preRenderLambdas;
|
||||
std::mutex _preRenderLambdasLock;
|
||||
|
||||
std::atomic<uint32_t> _processOctreeStatsCounter { 0 };
|
||||
};
|
||||
|
||||
#endif // hifi_Application_h
|
||||
|
|
|
@ -137,16 +137,15 @@ Menu::Menu() {
|
|||
QObject::connect(nodeList.data(), &NodeList::canRezChanged, assetServerAction, &QAction::setEnabled);
|
||||
assetServerAction->setEnabled(nodeList->getThisNodeCanRez());
|
||||
|
||||
// Edit > Reload All Content [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
|
||||
// Edit > Package Model... [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::PackageModel, 0,
|
||||
qApp, SLOT(packageModel()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
// Edit > Reload All Content [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()),
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
|
||||
// Audio menu ----------------------------------
|
||||
MenuWrapper* audioMenu = addMenu("Audio");
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace MenuOption {
|
|||
const QString AnimDebugDrawDefaultPose = "Debug Draw Default Pose";
|
||||
const QString AnimDebugDrawPosition= "Debug Draw Position";
|
||||
const QString AssetMigration = "ATP Asset Migration";
|
||||
const QString AssetServer = "My Asset Server";
|
||||
const QString AssetServer = "Asset Browser";
|
||||
const QString Attachments = "Attachments...";
|
||||
const QString AudioNetworkStats = "Audio Network Stats";
|
||||
const QString AudioNoiseReduction = "Audio Noise Reduction";
|
||||
|
|
|
@ -105,13 +105,18 @@ bool ModelPackager::loadModel() {
|
|||
qWarning() << QString("ModelPackager::loadModel(): Could not open FBX file %1").arg(_fbxInfo.filePath());
|
||||
return false;
|
||||
}
|
||||
qCDebug(interfaceapp) << "Reading FBX file : " << _fbxInfo.filePath();
|
||||
QByteArray fbxContents = fbx.readAll();
|
||||
try {
|
||||
qCDebug(interfaceapp) << "Reading FBX file : " << _fbxInfo.filePath();
|
||||
QByteArray fbxContents = fbx.readAll();
|
||||
|
||||
_geometry.reset(readFBX(fbxContents, QVariantHash(), _fbxInfo.filePath()));
|
||||
_geometry.reset(readFBX(fbxContents, QVariantHash(), _fbxInfo.filePath()));
|
||||
|
||||
// make sure we have some basic mappings
|
||||
populateBasicMapping(_mapping, _fbxInfo.filePath(), *_geometry);
|
||||
// make sure we have some basic mappings
|
||||
populateBasicMapping(_mapping, _fbxInfo.filePath(), *_geometry);
|
||||
} catch (const QString& error) {
|
||||
qCDebug(interfaceapp) << "Error reading " << _fbxInfo.filePath() << ": " << error;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -141,7 +141,7 @@ AABox Avatar::getBounds() const {
|
|||
if (!_skeletonModel->isRenderable()) {
|
||||
return AABox(getPosition(), getUniformScale()); // approximately 2m tall, scaled to user request.
|
||||
}
|
||||
return _skeletonModel->getPartBounds(0, 0, getPosition(), getOrientation());
|
||||
return _skeletonModel->getRenderableMeshBound();
|
||||
}
|
||||
|
||||
void Avatar::animateScaleChanges(float deltaTime) {
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <BugSplat.h>
|
||||
#endif
|
||||
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
disableQtBearerPoll(); // Fixes wifi ping spikes
|
||||
|
||||
|
@ -180,6 +181,8 @@ int main(int argc, const char* argv[]) {
|
|||
mpSender.sendAdditionalFile(qPrintable(logPath));
|
||||
#endif
|
||||
|
||||
printSystemInformation();
|
||||
|
||||
QTranslator translator;
|
||||
translator.load("i18n/interface_en");
|
||||
app.installTranslator(&translator);
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include <QtScript/QScriptEngine>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QThread>
|
||||
|
||||
#include <AssetRequest.h>
|
||||
#include <AssetUpload.h>
|
||||
|
@ -20,6 +21,19 @@
|
|||
#include <NetworkLogging.h>
|
||||
#include <OffscreenUi.h>
|
||||
|
||||
void AssetMappingModel::clear() {
|
||||
// make sure we are on the same thread before we touch the hash
|
||||
if (thread() != QThread::currentThread()) {
|
||||
QMetaObject::invokeMethod(this, "clear");
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "Clearing loaded asset mappings for Asset Browser";
|
||||
|
||||
_pathToItemMap.clear();
|
||||
QStandardItemModel::clear();
|
||||
}
|
||||
|
||||
AssetMappingsScriptingInterface::AssetMappingsScriptingInterface() {
|
||||
_proxyModel.setSourceModel(&_assetMappingModel);
|
||||
_proxyModel.setSortRole(Qt::DisplayRole);
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include <QSortFilterProxyModel>
|
||||
|
||||
|
||||
|
||||
class AssetMappingModel : public QStandardItemModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -30,6 +29,9 @@ public:
|
|||
bool isKnownMapping(QString path) const { return _pathToItemMap.contains(path); }
|
||||
bool isKnownFolder(QString path) const;
|
||||
|
||||
public slots:
|
||||
void clear();
|
||||
|
||||
signals:
|
||||
void errorGettingMappings(QString errorString);
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <QtCore/QDebug>
|
||||
|
||||
#include <udt/PacketHeaders.h>
|
||||
#include <LogHandler.h>
|
||||
|
||||
#include "AudioLogging.h"
|
||||
|
||||
|
@ -129,7 +130,10 @@ int AudioRingBuffer::writeData(const char* data, int maxSize) {
|
|||
int samplesToDelete = samplesToCopy - samplesRoomFor;
|
||||
_nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, samplesToDelete);
|
||||
_overflowCount++;
|
||||
qCDebug(audio) << "Overflowed ring buffer! Overwriting old data";
|
||||
|
||||
const QString RING_BUFFER_OVERFLOW_DEBUG { "AudioRingBuffer::writeData has overflown the buffer. Overwriting old data." };
|
||||
static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex(RING_BUFFER_OVERFLOW_DEBUG);
|
||||
qCDebug(audio) << qPrintable(RING_BUFFER_OVERFLOW_DEBUG);
|
||||
}
|
||||
|
||||
if (_endOfLastWrite + samplesToCopy <= _buffer + _bufferLength) {
|
||||
|
|
|
@ -47,6 +47,9 @@ template<class T> QVariant readBinaryArray(QDataStream& in, int& position) {
|
|||
in.readRawData(compressed.data() + sizeof(quint32), compressedLength);
|
||||
position += compressedLength;
|
||||
QByteArray uncompressed = qUncompress(compressed);
|
||||
if (uncompressed.isEmpty()) { // answers empty byte array if corrupt
|
||||
throw QString("corrupt fbx file");
|
||||
}
|
||||
QDataStream uncompressedIn(uncompressed);
|
||||
uncompressedIn.setByteOrder(QDataStream::LittleEndian);
|
||||
uncompressedIn.setVersion(QDataStream::Qt_4_5); // for single/double precision switch
|
||||
|
|
|
@ -29,6 +29,10 @@ public:
|
|||
const QUrl& textureBaseUrl;
|
||||
};
|
||||
|
||||
QUrl resolveTextureBaseUrl(const QUrl& url, const QUrl& textureBaseUrl) {
|
||||
return textureBaseUrl.isValid() ? textureBaseUrl : url;
|
||||
}
|
||||
|
||||
class GeometryMappingResource : public GeometryResource {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -52,18 +56,17 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) {
|
|||
finishedLoading(false);
|
||||
} else {
|
||||
QUrl url = _url.resolved(filename);
|
||||
QUrl textureBaseUrl;
|
||||
|
||||
QString texdir = mapping.value("texdir").toString();
|
||||
if (!texdir.isNull()) {
|
||||
if (!texdir.endsWith('/')) {
|
||||
texdir += '/';
|
||||
}
|
||||
textureBaseUrl = _url.resolved(texdir);
|
||||
_textureBaseUrl = resolveTextureBaseUrl(url, _url.resolved(texdir));
|
||||
}
|
||||
|
||||
auto modelCache = DependencyManager::get<ModelCache>();
|
||||
GeometryExtra extra{ mapping, textureBaseUrl };
|
||||
GeometryExtra extra{ mapping, _textureBaseUrl };
|
||||
|
||||
// Get the raw GeometryResource, not the wrapped NetworkGeometry
|
||||
_geometryResource = modelCache->getResource(url, QUrl(), false, &extra).staticCast<GeometryResource>();
|
||||
|
@ -160,7 +163,7 @@ class GeometryDefinitionResource : public GeometryResource {
|
|||
Q_OBJECT
|
||||
public:
|
||||
GeometryDefinitionResource(const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl) :
|
||||
GeometryResource(url, textureBaseUrl.isValid() ? textureBaseUrl : url), _mapping(mapping) {}
|
||||
GeometryResource(url, resolveTextureBaseUrl(url, textureBaseUrl)), _mapping(mapping) {}
|
||||
|
||||
virtual void downloadFinished(const QByteArray& data) override;
|
||||
|
||||
|
|
|
@ -95,7 +95,8 @@ class GeometryResource : public Resource, public Geometry {
|
|||
public:
|
||||
using Pointer = QSharedPointer<GeometryResource>;
|
||||
|
||||
GeometryResource(const QUrl& url, const QUrl& textureBaseUrl = QUrl()) : Resource(url) {}
|
||||
GeometryResource(const QUrl& url, const QUrl& textureBaseUrl = QUrl()) :
|
||||
Resource(url), _textureBaseUrl(textureBaseUrl) {}
|
||||
|
||||
virtual bool areTexturesLoaded() const { return isLoaded() && Geometry::areTexturesLoaded(); }
|
||||
|
||||
|
|
|
@ -200,7 +200,7 @@ AssetUpload* AssetClient::createUpload(const QByteArray& data) {
|
|||
return upload;
|
||||
}
|
||||
|
||||
bool AssetClient::getAsset(const QString& hash, DataOffset start, DataOffset end,
|
||||
MessageID AssetClient::getAsset(const QString& hash, DataOffset start, DataOffset end,
|
||||
ReceivedAssetCallback callback, ProgressCallback progressCallback) {
|
||||
if (hash.length() != SHA256_HASH_HEX_LENGTH) {
|
||||
qCWarning(asset_client) << "Invalid hash size";
|
||||
|
@ -230,17 +230,16 @@ bool AssetClient::getAsset(const QString& hash, DataOffset start, DataOffset end
|
|||
|
||||
_pendingRequests[assetServer][messageID] = { QSharedPointer<ReceivedMessage>(), callback, progressCallback };
|
||||
|
||||
|
||||
return true;
|
||||
return messageID;
|
||||
} else {
|
||||
callback(false, AssetServerError::NoError, QByteArray());
|
||||
return false;
|
||||
return INVALID_MESSAGE_ID;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
bool AssetClient::getAssetInfo(const QString& hash, GetInfoCallback callback) {
|
||||
MessageID AssetClient::getAssetInfo(const QString& hash, GetInfoCallback callback) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||
|
||||
|
@ -257,10 +256,10 @@ bool AssetClient::getAssetInfo(const QString& hash, GetInfoCallback callback) {
|
|||
|
||||
_pendingInfoRequests[assetServer][messageID] = callback;
|
||||
|
||||
return true;
|
||||
return messageID;
|
||||
} else {
|
||||
callback(false, AssetServerError::NoError, { "", 0 });
|
||||
return false;
|
||||
return INVALID_MESSAGE_ID;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -352,7 +351,7 @@ void AssetClient::handleAssetGetReply(QSharedPointer<ReceivedMessage> message, S
|
|||
}
|
||||
}
|
||||
|
||||
bool AssetClient::getAssetMapping(const AssetPath& path, MappingOperationCallback callback) {
|
||||
MessageID AssetClient::getAssetMapping(const AssetPath& path, MappingOperationCallback callback) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||
|
||||
|
@ -370,14 +369,14 @@ bool AssetClient::getAssetMapping(const AssetPath& path, MappingOperationCallbac
|
|||
|
||||
_pendingMappingRequests[assetServer][messageID] = callback;
|
||||
|
||||
return true;
|
||||
return messageID;
|
||||
} else {
|
||||
callback(false, AssetServerError::NoError, QSharedPointer<ReceivedMessage>());
|
||||
return false;
|
||||
return INVALID_MESSAGE_ID;
|
||||
}
|
||||
}
|
||||
|
||||
bool AssetClient::getAllAssetMappings(MappingOperationCallback callback) {
|
||||
MessageID AssetClient::getAllAssetMappings(MappingOperationCallback callback) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||
|
||||
|
@ -393,14 +392,14 @@ bool AssetClient::getAllAssetMappings(MappingOperationCallback callback) {
|
|||
|
||||
_pendingMappingRequests[assetServer][messageID] = callback;
|
||||
|
||||
return true;
|
||||
return messageID;
|
||||
} else {
|
||||
callback(false, AssetServerError::NoError, QSharedPointer<ReceivedMessage>());
|
||||
return false;
|
||||
return INVALID_MESSAGE_ID;
|
||||
}
|
||||
}
|
||||
|
||||
bool AssetClient::deleteAssetMappings(const AssetPathList& paths, MappingOperationCallback callback) {
|
||||
MessageID AssetClient::deleteAssetMappings(const AssetPathList& paths, MappingOperationCallback callback) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||
|
||||
|
@ -422,14 +421,14 @@ bool AssetClient::deleteAssetMappings(const AssetPathList& paths, MappingOperati
|
|||
|
||||
_pendingMappingRequests[assetServer][messageID] = callback;
|
||||
|
||||
return true;
|
||||
return messageID;
|
||||
} else {
|
||||
callback(false, AssetServerError::NoError, QSharedPointer<ReceivedMessage>());
|
||||
return false;
|
||||
return INVALID_MESSAGE_ID;
|
||||
}
|
||||
}
|
||||
|
||||
bool AssetClient::setAssetMapping(const QString& path, const AssetHash& hash, MappingOperationCallback callback) {
|
||||
MessageID AssetClient::setAssetMapping(const QString& path, const AssetHash& hash, MappingOperationCallback callback) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||
|
||||
|
@ -448,14 +447,14 @@ bool AssetClient::setAssetMapping(const QString& path, const AssetHash& hash, Ma
|
|||
|
||||
_pendingMappingRequests[assetServer][messageID] = callback;
|
||||
|
||||
return true;
|
||||
return messageID;
|
||||
} else {
|
||||
callback(false, AssetServerError::NoError, QSharedPointer<ReceivedMessage>());
|
||||
return false;
|
||||
return INVALID_MESSAGE_ID;
|
||||
}
|
||||
}
|
||||
|
||||
bool AssetClient::renameAssetMapping(const AssetPath& oldPath, const AssetPath& newPath, MappingOperationCallback callback) {
|
||||
MessageID AssetClient::renameAssetMapping(const AssetPath& oldPath, const AssetPath& newPath, MappingOperationCallback callback) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||
|
||||
|
@ -474,15 +473,53 @@ bool AssetClient::renameAssetMapping(const AssetPath& oldPath, const AssetPath&
|
|||
|
||||
_pendingMappingRequests[assetServer][messageID] = callback;
|
||||
|
||||
return true;
|
||||
return messageID;
|
||||
|
||||
} else {
|
||||
callback(false, AssetServerError::NoError, QSharedPointer<ReceivedMessage>());
|
||||
return false;
|
||||
return INVALID_MESSAGE_ID;
|
||||
}
|
||||
}
|
||||
|
||||
bool AssetClient::uploadAsset(const QByteArray& data, UploadResultCallback callback) {
|
||||
bool AssetClient::cancelMappingRequest(MessageID id) {
|
||||
for (auto& kv : _pendingMappingRequests) {
|
||||
if (kv.second.erase(id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AssetClient::cancelGetAssetInfoRequest(MessageID id) {
|
||||
for (auto& kv : _pendingInfoRequests) {
|
||||
if (kv.second.erase(id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AssetClient::cancelGetAssetRequest(MessageID id) {
|
||||
// Search through each pending mapping request for id `id`
|
||||
for (auto& kv : _pendingRequests) {
|
||||
if (kv.second.erase(id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AssetClient::cancelUploadAssetRequest(MessageID id) {
|
||||
// Search through each pending mapping request for id `id`
|
||||
for (auto& kv : _pendingUploads) {
|
||||
if (kv.second.erase(id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
MessageID AssetClient::uploadAsset(const QByteArray& data, UploadResultCallback callback) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
|
||||
|
||||
|
@ -500,10 +537,10 @@ bool AssetClient::uploadAsset(const QByteArray& data, UploadResultCallback callb
|
|||
|
||||
_pendingUploads[assetServer][messageID] = callback;
|
||||
|
||||
return true;
|
||||
return messageID;
|
||||
} else {
|
||||
callback(false, AssetServerError::NoError, QString());
|
||||
return false;
|
||||
return INVALID_MESSAGE_ID;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -60,6 +60,8 @@ public:
|
|||
Q_INVOKABLE AssetUpload* createUpload(const QString& filename);
|
||||
Q_INVOKABLE AssetUpload* createUpload(const QByteArray& data);
|
||||
|
||||
static const MessageID INVALID_MESSAGE_ID = 0;
|
||||
|
||||
public slots:
|
||||
void init();
|
||||
|
||||
|
@ -75,16 +77,21 @@ private slots:
|
|||
void handleNodeKilled(SharedNodePointer node);
|
||||
|
||||
private:
|
||||
bool getAssetMapping(const AssetHash& hash, MappingOperationCallback callback);
|
||||
bool getAllAssetMappings(MappingOperationCallback callback);
|
||||
bool setAssetMapping(const QString& path, const AssetHash& hash, MappingOperationCallback callback);
|
||||
bool deleteAssetMappings(const AssetPathList& paths, MappingOperationCallback callback);
|
||||
bool renameAssetMapping(const AssetPath& oldPath, const AssetPath& newPath, MappingOperationCallback callback);
|
||||
MessageID getAssetMapping(const AssetHash& hash, MappingOperationCallback callback);
|
||||
MessageID getAllAssetMappings(MappingOperationCallback callback);
|
||||
MessageID setAssetMapping(const QString& path, const AssetHash& hash, MappingOperationCallback callback);
|
||||
MessageID deleteAssetMappings(const AssetPathList& paths, MappingOperationCallback callback);
|
||||
MessageID renameAssetMapping(const AssetPath& oldPath, const AssetPath& newPath, MappingOperationCallback callback);
|
||||
|
||||
bool getAssetInfo(const QString& hash, GetInfoCallback callback);
|
||||
bool getAsset(const QString& hash, DataOffset start, DataOffset end,
|
||||
MessageID getAssetInfo(const QString& hash, GetInfoCallback callback);
|
||||
MessageID getAsset(const QString& hash, DataOffset start, DataOffset end,
|
||||
ReceivedAssetCallback callback, ProgressCallback progressCallback);
|
||||
bool uploadAsset(const QByteArray& data, UploadResultCallback callback);
|
||||
MessageID uploadAsset(const QByteArray& data, UploadResultCallback callback);
|
||||
|
||||
bool cancelMappingRequest(MessageID id);
|
||||
bool cancelGetAssetInfoRequest(MessageID id);
|
||||
bool cancelGetAssetRequest(MessageID id);
|
||||
bool cancelUploadAssetRequest(MessageID id);
|
||||
|
||||
struct GetAssetRequestData {
|
||||
QSharedPointer<ReceivedMessage> message;
|
||||
|
@ -100,6 +107,7 @@ private:
|
|||
|
||||
friend class AssetRequest;
|
||||
friend class AssetUpload;
|
||||
friend class MappingRequest;
|
||||
friend class GetMappingRequest;
|
||||
friend class GetAllMappingsRequest;
|
||||
friend class SetMappingRequest;
|
||||
|
|
|
@ -25,6 +25,16 @@ AssetRequest::AssetRequest(const QString& hash) :
|
|||
{
|
||||
}
|
||||
|
||||
AssetRequest::~AssetRequest() {
|
||||
auto assetClient = DependencyManager::get<AssetClient>();
|
||||
if (_assetRequestID) {
|
||||
assetClient->cancelGetAssetRequest(_assetRequestID);
|
||||
}
|
||||
if (_assetInfoRequestID) {
|
||||
assetClient->cancelGetAssetInfoRequest(_assetInfoRequestID);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetRequest::start() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "start", Qt::AutoConnection);
|
||||
|
@ -60,7 +70,11 @@ void AssetRequest::start() {
|
|||
_state = WaitingForInfo;
|
||||
|
||||
auto assetClient = DependencyManager::get<AssetClient>();
|
||||
assetClient->getAssetInfo(_hash, [this](bool responseReceived, AssetServerError serverError, AssetInfo info) {
|
||||
_assetInfoRequestID = assetClient->getAssetInfo(_hash,
|
||||
[this](bool responseReceived, AssetServerError serverError, AssetInfo info) {
|
||||
|
||||
_assetInfoRequestID = AssetClient::INVALID_MESSAGE_ID;
|
||||
|
||||
_info = info;
|
||||
|
||||
if (!responseReceived) {
|
||||
|
@ -92,8 +106,11 @@ void AssetRequest::start() {
|
|||
int start = 0, end = _info.size;
|
||||
|
||||
auto assetClient = DependencyManager::get<AssetClient>();
|
||||
assetClient->getAsset(_hash, start, end, [this, start, end](bool responseReceived, AssetServerError serverError,
|
||||
const QByteArray& data) {
|
||||
_assetRequestID = assetClient->getAsset(_hash, start, end,
|
||||
[this, start, end](bool responseReceived, AssetServerError serverError, const QByteArray& data) {
|
||||
|
||||
_assetRequestID = AssetClient::INVALID_MESSAGE_ID;
|
||||
|
||||
if (!responseReceived) {
|
||||
_error = NetworkError;
|
||||
} else if (serverError != AssetServerError::NoError) {
|
||||
|
|
|
@ -41,6 +41,7 @@ public:
|
|||
};
|
||||
|
||||
AssetRequest(const QString& hash);
|
||||
virtual ~AssetRequest() override;
|
||||
|
||||
Q_INVOKABLE void start();
|
||||
|
||||
|
@ -61,6 +62,8 @@ private:
|
|||
QString _hash;
|
||||
QByteArray _data;
|
||||
int _numPendingRequests { 0 };
|
||||
MessageID _assetRequestID { AssetClient::INVALID_MESSAGE_ID };
|
||||
MessageID _assetInfoRequestID { AssetClient::INVALID_MESSAGE_ID };
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -21,7 +21,7 @@ class AssetResourceRequest : public ResourceRequest {
|
|||
Q_OBJECT
|
||||
public:
|
||||
AssetResourceRequest(const QUrl& url) : ResourceRequest(url) { }
|
||||
~AssetResourceRequest();
|
||||
virtual ~AssetResourceRequest() override;
|
||||
|
||||
protected:
|
||||
virtual void doSend() override;
|
||||
|
|
|
@ -15,7 +15,12 @@
|
|||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
#include "AssetClient.h"
|
||||
MappingRequest::~MappingRequest() {
|
||||
auto assetClient = DependencyManager::get<AssetClient>();
|
||||
if (_mappingRequestID) {
|
||||
assetClient->cancelMappingRequest(_mappingRequestID);
|
||||
}
|
||||
}
|
||||
|
||||
void MappingRequest::start() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
|
@ -60,7 +65,10 @@ void GetMappingRequest::doStart() {
|
|||
|
||||
auto assetClient = DependencyManager::get<AssetClient>();
|
||||
|
||||
assetClient->getAssetMapping(_path, [this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
|
||||
_mappingRequestID = assetClient->getAssetMapping(_path,
|
||||
[this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
|
||||
|
||||
_mappingRequestID = AssetClient::INVALID_MESSAGE_ID;
|
||||
if (!responseReceived) {
|
||||
_error = NetworkError;
|
||||
} else {
|
||||
|
@ -89,7 +97,11 @@ GetAllMappingsRequest::GetAllMappingsRequest() {
|
|||
|
||||
void GetAllMappingsRequest::doStart() {
|
||||
auto assetClient = DependencyManager::get<AssetClient>();
|
||||
assetClient->getAllAssetMappings([this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
|
||||
_mappingRequestID = assetClient->getAllAssetMappings(
|
||||
[this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
|
||||
|
||||
_mappingRequestID = AssetClient::INVALID_MESSAGE_ID;
|
||||
|
||||
if (!responseReceived) {
|
||||
_error = NetworkError;
|
||||
} else {
|
||||
|
@ -137,7 +149,10 @@ void SetMappingRequest::doStart() {
|
|||
|
||||
auto assetClient = DependencyManager::get<AssetClient>();
|
||||
|
||||
assetClient->setAssetMapping(_path, _hash, [this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
|
||||
_mappingRequestID = assetClient->setAssetMapping(_path, _hash,
|
||||
[this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
|
||||
|
||||
_mappingRequestID = AssetClient::INVALID_MESSAGE_ID;
|
||||
if (!responseReceived) {
|
||||
_error = NetworkError;
|
||||
} else {
|
||||
|
@ -177,7 +192,10 @@ void DeleteMappingsRequest::doStart() {
|
|||
|
||||
auto assetClient = DependencyManager::get<AssetClient>();
|
||||
|
||||
assetClient->deleteAssetMappings(_paths, [this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
|
||||
_mappingRequestID = assetClient->deleteAssetMappings(_paths,
|
||||
[this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
|
||||
|
||||
_mappingRequestID = AssetClient::INVALID_MESSAGE_ID;
|
||||
if (!responseReceived) {
|
||||
_error = NetworkError;
|
||||
} else {
|
||||
|
@ -216,9 +234,10 @@ void RenameMappingRequest::doStart() {
|
|||
|
||||
auto assetClient = DependencyManager::get<AssetClient>();
|
||||
|
||||
assetClient->renameAssetMapping(_oldPath, _newPath, [this, assetClient](bool responseReceived,
|
||||
AssetServerError error,
|
||||
QSharedPointer<ReceivedMessage> message) {
|
||||
_mappingRequestID = assetClient->renameAssetMapping(_oldPath, _newPath,
|
||||
[this, assetClient](bool responseReceived, AssetServerError error, QSharedPointer<ReceivedMessage> message) {
|
||||
|
||||
_mappingRequestID = AssetClient::INVALID_MESSAGE_ID;
|
||||
if (!responseReceived) {
|
||||
_error = NetworkError;
|
||||
} else {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <QtCore/QObject>
|
||||
|
||||
#include "AssetUtils.h"
|
||||
#include "AssetClient.h"
|
||||
|
||||
class MappingRequest : public QObject {
|
||||
Q_OBJECT
|
||||
|
@ -31,12 +32,15 @@ public:
|
|||
UnknownError
|
||||
};
|
||||
|
||||
virtual ~MappingRequest();
|
||||
|
||||
Q_INVOKABLE void start();
|
||||
Error getError() const { return _error; }
|
||||
Q_INVOKABLE QString getErrorString() const;
|
||||
|
||||
protected:
|
||||
Error _error { NoError };
|
||||
MessageID _mappingRequestID { AssetClient::INVALID_MESSAGE_ID };
|
||||
|
||||
private:
|
||||
virtual void doStart() = 0;
|
||||
|
|
|
@ -21,6 +21,7 @@ class ResourceRequest : public QObject {
|
|||
Q_OBJECT
|
||||
public:
|
||||
ResourceRequest(const QUrl& url);
|
||||
virtual ~ResourceRequest() = default;
|
||||
|
||||
enum State {
|
||||
NotStarted = 0,
|
||||
|
|
|
@ -10,10 +10,13 @@
|
|||
//
|
||||
|
||||
#include "SequenceNumberStats.h"
|
||||
#include "NetworkLogging.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include <LogHandler.h>
|
||||
|
||||
#include "NetworkLogging.h"
|
||||
|
||||
float PacketStreamStats::getLostRate() const {
|
||||
return (_expectedReceived == 0) ? 0.0f : (float)_lost / (float)_expectedReceived;
|
||||
}
|
||||
|
@ -63,6 +66,7 @@ SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(qui
|
|||
arrivalInfo._status = OnTime;
|
||||
_lastReceivedSequence = incoming;
|
||||
_stats._expectedReceived++;
|
||||
|
||||
} else { // out of order
|
||||
|
||||
if (wantExtraDebugging) {
|
||||
|
@ -85,6 +89,9 @@ SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(qui
|
|||
} else if (absGap > MAX_REASONABLE_SEQUENCE_GAP) {
|
||||
arrivalInfo._status = Unreasonable;
|
||||
|
||||
static const QString UNREASONABLE_SEQUENCE_REGEX { "unreasonable sequence number: \\d+ previous: \\d+" };
|
||||
static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex(UNREASONABLE_SEQUENCE_REGEX);
|
||||
|
||||
qCDebug(networking) << "unreasonable sequence number:" << incoming << "previous:" << _lastReceivedSequence;
|
||||
|
||||
_stats._unreasonable++;
|
||||
|
@ -147,6 +154,9 @@ SequenceNumberStats::ArrivalInfo SequenceNumberStats::sequenceNumberReceived(qui
|
|||
|
||||
arrivalInfo._status = Unreasonable;
|
||||
|
||||
static const QString UNREASONABLE_SEQUENCE_REGEX { "unreasonable sequence number: \\d+ \\(possible duplicate\\)" };
|
||||
static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex(UNREASONABLE_SEQUENCE_REGEX);
|
||||
|
||||
qCDebug(networking) << "unreasonable sequence number:" << incoming << "(possible duplicate)";
|
||||
|
||||
_stats._unreasonable++;
|
||||
|
|
|
@ -19,6 +19,7 @@ using namespace udt;
|
|||
using namespace std::chrono;
|
||||
|
||||
static const double USECS_PER_SECOND = 1000000.0;
|
||||
static const int BITS_PER_BYTE = 8;
|
||||
|
||||
void CongestionControl::setMaxBandwidth(int maxBandwidth) {
|
||||
_maxBandwidth = maxBandwidth;
|
||||
|
@ -28,11 +29,11 @@ void CongestionControl::setMaxBandwidth(int maxBandwidth) {
|
|||
void CongestionControl::setPacketSendPeriod(double newSendPeriod) {
|
||||
Q_ASSERT_X(newSendPeriod >= 0, "CongestionControl::setPacketPeriod", "Can not set a negative packet send period");
|
||||
|
||||
auto maxBandwidth = _maxBandwidth.load();
|
||||
if (maxBandwidth > 0) {
|
||||
auto packetsPerSecond = (double)_maxBandwidth / (BITS_PER_BYTE * _mss);
|
||||
if (packetsPerSecond > 0.0) {
|
||||
// anytime the packet send period is about to be increased, make sure it stays below the minimum period,
|
||||
// calculated based on the maximum desired bandwidth
|
||||
double minPacketSendPeriod = USECS_PER_SECOND / (((double) maxBandwidth) / _mss);
|
||||
double minPacketSendPeriod = USECS_PER_SECOND / packetsPerSecond;
|
||||
_packetSendPeriod = std::max(newSendPeriod, minPacketSendPeriod);
|
||||
} else {
|
||||
_packetSendPeriod = newSendPeriod;
|
||||
|
@ -145,17 +146,23 @@ void DefaultCC::onLoss(SequenceNumber rangeStart, SequenceNumber rangeEnd) {
|
|||
}
|
||||
|
||||
_loss = true;
|
||||
|
||||
++_nakCount;
|
||||
|
||||
static const double INTER_PACKET_ARRIVAL_INCREASE = 1.125;
|
||||
static const int MAX_DECREASES_PER_CONGESTION_EPOCH = 5;
|
||||
|
||||
// check if this NAK starts a new congestion period - this will be the case if the
|
||||
// NAK received occured for a packet sent after the last decrease
|
||||
if (rangeStart > _lastDecreaseMaxSeq) {
|
||||
_delayedDecrease = (rangeStart == rangeEnd);
|
||||
|
||||
_lastDecreasePeriod = _packetSendPeriod;
|
||||
|
||||
setPacketSendPeriod(ceil(_packetSendPeriod * INTER_PACKET_ARRIVAL_INCREASE));
|
||||
|
||||
if (!_delayedDecrease) {
|
||||
setPacketSendPeriod(ceil(_packetSendPeriod * INTER_PACKET_ARRIVAL_INCREASE));
|
||||
} else {
|
||||
_loss = false;
|
||||
}
|
||||
|
||||
// use EWMA to update the average number of NAKs per congestion
|
||||
static const double NAK_EWMA_ALPHA = 0.125;
|
||||
|
@ -177,10 +184,12 @@ void DefaultCC::onLoss(SequenceNumber rangeStart, SequenceNumber rangeEnd) {
|
|||
|
||||
_randomDecreaseThreshold = distribution(generator);
|
||||
}
|
||||
} else if ((_decreaseCount++ < MAX_DECREASES_PER_CONGESTION_EPOCH) && ((++_nakCount % _randomDecreaseThreshold) == 0)) {
|
||||
} else if (_delayedDecrease && _nakCount == 2) {
|
||||
setPacketSendPeriod(ceil(_packetSendPeriod * INTER_PACKET_ARRIVAL_INCREASE));
|
||||
} else if ((_decreaseCount++ < MAX_DECREASES_PER_CONGESTION_EPOCH) && ((_nakCount % _randomDecreaseThreshold) == 0)) {
|
||||
// there have been fewer than MAX_DECREASES_PER_CONGESTION_EPOCH AND this NAK matches the random count at which we
|
||||
// decided we would decrease the packet send period
|
||||
|
||||
|
||||
setPacketSendPeriod(ceil(_packetSendPeriod * INTER_PACKET_ARRIVAL_INCREASE));
|
||||
_lastDecreaseMaxSeq = _sendCurrSeqNum;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include <PortableHighResolutionClock.h>
|
||||
|
||||
|
@ -61,7 +60,7 @@ protected:
|
|||
double _congestionWindowSize { 16.0 }; // Congestion window size, in packets
|
||||
|
||||
int _bandwidth { 0 }; // estimated bandwidth, packets per second
|
||||
std::atomic<int> _maxBandwidth { -1 }; // Maximum desired bandwidth, bytes per second
|
||||
std::atomic<int> _maxBandwidth { -1 }; // Maximum desired bandwidth, bits per second
|
||||
double _maxCongestionWindowSize { 0.0 }; // maximum cwnd size, in packets
|
||||
|
||||
int _mss { 0 }; // Maximum Packet Size, including all packet headers
|
||||
|
@ -124,6 +123,7 @@ private:
|
|||
int _randomDecreaseThreshold { 1 }; // random threshold on decrease by number of loss events
|
||||
int _avgNAKNum { 0 }; // average number of NAKs per congestion
|
||||
int _decreaseCount { 0 }; // number of decreases in a congestion epoch
|
||||
bool _delayedDecrease { false };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -354,7 +354,7 @@ void Socket::setCongestionControlFactory(std::unique_ptr<CongestionControlVirtua
|
|||
|
||||
|
||||
void Socket::setConnectionMaxBandwidth(int maxBandwidth) {
|
||||
qInfo() << "Setting socket's maximum bandwith to" << maxBandwidth << ". ("
|
||||
qInfo() << "Setting socket's maximum bandwith to" << maxBandwidth << "bps. ("
|
||||
<< _connectionsHash.size() << "live connections)";
|
||||
_maxBandwidth = maxBandwidth;
|
||||
for (auto& pair : _connectionsHash) {
|
||||
|
|
|
@ -392,20 +392,14 @@ void AnimDebugDraw::update() {
|
|||
|
||||
assert(numVerts == (v - verts));
|
||||
|
||||
// The RenderItem culling is not working correctly
|
||||
// Workaround this issue by using the default constructed
|
||||
// item._bound which is a 16 km cube.
|
||||
/*
|
||||
render::Item::Bound theBound;
|
||||
for (int i = 0; i < numVerts; i++) {
|
||||
theBound += verts[i].pos;
|
||||
}
|
||||
data._bound = theBound;
|
||||
*/
|
||||
|
||||
data._isVisible = (numVerts > 0);
|
||||
|
||||
|
||||
data._indexBuffer->resize(sizeof(uint16_t) * numVerts);
|
||||
uint16_t* indices = (uint16_t*)data._indexBuffer->editData();
|
||||
for (int i = 0; i < numVerts; i++) {
|
||||
|
|
|
@ -520,26 +520,6 @@ void ModelMeshPartPayload::render(RenderArgs* args) const {
|
|||
return;
|
||||
}
|
||||
|
||||
// render the part bounding box
|
||||
#ifdef DEBUG_BOUNDING_PARTS
|
||||
{
|
||||
AABox partBounds = getPartBounds(_meshIndex, partIndex);
|
||||
|
||||
glm::vec4 cubeColor(1.0f, 1.0f, 0.0f, 1.0f);
|
||||
if (isSkinned) {
|
||||
cubeColor = glm::vec4(0.0f, 1.0f, 1.0f, 1.0f);
|
||||
} else if (args->_viewFrustum->boxIntersectsFrustum(partBounds)) {
|
||||
cubeColor = glm::vec4(1.0f, 0.0f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
Transform transform;
|
||||
transform.setTranslation(partBounds.calcCenter());
|
||||
transform.setScale(partBounds.getDimensions());
|
||||
batch.setModelTransform(transform);
|
||||
DependencyManager::get<GeometryCache>()->renderWireCube(batch, 1.0f, cubeColor);
|
||||
}
|
||||
#endif //def DEBUG_BOUNDING_PARTS
|
||||
|
||||
auto locations = args->_pipeline->locations;
|
||||
assert(locations);
|
||||
|
||||
|
|
|
@ -89,12 +89,12 @@ bool Model::needsFixupInScene() const {
|
|||
|
||||
void Model::setTranslation(const glm::vec3& translation) {
|
||||
_translation = translation;
|
||||
enqueueLocationChange();
|
||||
updateRenderItems();
|
||||
}
|
||||
|
||||
void Model::setRotation(const glm::quat& rotation) {
|
||||
_rotation = rotation;
|
||||
enqueueLocationChange();
|
||||
updateRenderItems();
|
||||
}
|
||||
|
||||
void Model::setScale(const glm::vec3& scale) {
|
||||
|
@ -124,7 +124,7 @@ void Model::setOffset(const glm::vec3& offset) {
|
|||
_snappedToRegistrationPoint = false;
|
||||
}
|
||||
|
||||
void Model::enqueueLocationChange() {
|
||||
void Model::updateRenderItems() {
|
||||
|
||||
_needsUpdateClusterMatrices = true;
|
||||
|
||||
|
@ -552,50 +552,33 @@ bool Model::addToScene(std::shared_ptr<render::Scene> scene,
|
|||
|
||||
bool somethingAdded = false;
|
||||
|
||||
if (_modelMeshRenderItems.size()) {
|
||||
for (auto item : _modelMeshRenderItems.keys()) {
|
||||
pendingChanges.updateItem<ModelMeshPartPayload>(item, [](ModelMeshPartPayload& data) {
|
||||
data.notifyLocationChanged();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
for (auto renderItem : _modelMeshRenderItemsSet) {
|
||||
if (_modelMeshRenderItems.empty()) {
|
||||
foreach (auto renderItem, _modelMeshRenderItemsSet) {
|
||||
auto item = scene->allocateID();
|
||||
auto renderPayload = std::make_shared<ModelMeshPartPayload::Payload>(renderItem);
|
||||
if (statusGetters.size()) {
|
||||
renderPayload->addStatusGetters(statusGetters);
|
||||
}
|
||||
pendingChanges.resetItem(item, renderPayload);
|
||||
pendingChanges.updateItem<ModelMeshPartPayload>(item, [](ModelMeshPartPayload& data) {
|
||||
data.notifyLocationChanged();
|
||||
});
|
||||
_modelMeshRenderItems.insert(item, renderPayload);
|
||||
somethingAdded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (_collisionRenderItems.size()) {
|
||||
for (auto item : _collisionRenderItems.keys()) {
|
||||
pendingChanges.updateItem<MeshPartPayload>(item, [](MeshPartPayload& data) {
|
||||
data.notifyLocationChanged();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
for (auto renderItem : _collisionRenderItemsSet) {
|
||||
if (_collisionRenderItems.empty()) {
|
||||
foreach (auto renderItem, _collisionRenderItemsSet) {
|
||||
auto item = scene->allocateID();
|
||||
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
|
||||
if (statusGetters.size()) {
|
||||
renderPayload->addStatusGetters(statusGetters);
|
||||
}
|
||||
pendingChanges.resetItem(item, renderPayload);
|
||||
pendingChanges.updateItem<MeshPartPayload>(item, [](MeshPartPayload& data) {
|
||||
data.notifyLocationChanged();
|
||||
});
|
||||
_collisionRenderItems.insert(item, renderPayload);
|
||||
somethingAdded = true;
|
||||
}
|
||||
}
|
||||
|
||||
updateRenderItems();
|
||||
|
||||
_readyWhenAdded = readyToAddToScene();
|
||||
|
||||
return somethingAdded;
|
||||
|
@ -1169,38 +1152,17 @@ void Model::deleteGeometry() {
|
|||
_blendedBlendshapeCoefficients.clear();
|
||||
}
|
||||
|
||||
AABox Model::getPartBounds(int meshIndex, int partIndex, glm::vec3 modelPosition, glm::quat modelOrientation) const {
|
||||
|
||||
AABox Model::getRenderableMeshBound() const {
|
||||
if (!isLoaded()) {
|
||||
return AABox();
|
||||
}
|
||||
|
||||
if (meshIndex < _meshStates.size()) {
|
||||
const MeshState& state = _meshStates.at(meshIndex);
|
||||
bool isSkinned = state.clusterMatrices.size() > 1;
|
||||
if (isSkinned) {
|
||||
// if we're skinned return the entire mesh extents because we can't know for sure our clusters don't move us
|
||||
return calculateScaledOffsetAABox(getFBXGeometry().meshExtents, modelPosition, modelOrientation);
|
||||
} else {
|
||||
// Build a bound using the last known bound from all the renderItems.
|
||||
AABox totalBound;
|
||||
for (auto& renderItem : _modelMeshRenderItemsSet) {
|
||||
totalBound += renderItem->getBound();
|
||||
}
|
||||
return totalBound;
|
||||
}
|
||||
if (getFBXGeometry().meshes.size() > meshIndex) {
|
||||
|
||||
// FIX ME! - This is currently a hack because for some mesh parts our efforts to calculate the bounding
|
||||
// box of the mesh part fails. It seems to create boxes that are not consistent with where the
|
||||
// geometry actually renders. If instead we make all the parts share the bounds of the entire subMesh
|
||||
// things will render properly.
|
||||
//
|
||||
// return calculateScaledOffsetAABox(_calculatedMeshPartBoxes[QPair<int,int>(meshIndex, partIndex)]);
|
||||
//
|
||||
// NOTE: we also don't want to use the _calculatedMeshBoxes[] because they don't handle avatar moving correctly
|
||||
// without recalculating them...
|
||||
// return _calculatedMeshBoxes[meshIndex];
|
||||
//
|
||||
// If we not skinned use the bounds of the subMesh for all it's parts
|
||||
const FBXMesh& mesh = getFBXGeometry().meshes.at(meshIndex);
|
||||
return calculateScaledOffsetExtents(mesh.meshExtents, modelPosition, modelOrientation);
|
||||
}
|
||||
return AABox();
|
||||
}
|
||||
|
||||
void Model::segregateMeshGroups() {
|
||||
|
@ -1292,20 +1254,15 @@ bool Model::initWhenReady(render::ScenePointer scene) {
|
|||
auto renderPayload = std::make_shared<ModelMeshPartPayload::Payload>(renderItem);
|
||||
_modelMeshRenderItems.insert(item, renderPayload);
|
||||
pendingChanges.resetItem(item, renderPayload);
|
||||
pendingChanges.updateItem<ModelMeshPartPayload>(item, [transform, offset](MeshPartPayload& data) {
|
||||
data.notifyLocationChanged();
|
||||
});
|
||||
}
|
||||
foreach (auto renderItem, _collisionRenderItemsSet) {
|
||||
auto item = scene->allocateID();
|
||||
auto renderPayload = std::make_shared<MeshPartPayload::Payload>(renderItem);
|
||||
_collisionRenderItems.insert(item, renderPayload);
|
||||
pendingChanges.resetItem(item, renderPayload);
|
||||
pendingChanges.updateItem<MeshPartPayload>(item, [transform, offset](MeshPartPayload& data) {
|
||||
data.notifyLocationChanged();
|
||||
});
|
||||
}
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
updateRenderItems();
|
||||
|
||||
_readyWhenAdded = true;
|
||||
return true;
|
||||
|
|
|
@ -102,7 +102,7 @@ public:
|
|||
bool isVisible() const { return _isVisible; }
|
||||
|
||||
void updateRenderItems();
|
||||
AABox getPartBounds(int meshIndex, int partIndex, glm::vec3 modelPosition, glm::quat modelOrientation) const;
|
||||
AABox getRenderableMeshBound() const;
|
||||
|
||||
bool maybeStartBlender();
|
||||
|
||||
|
@ -213,8 +213,6 @@ public:
|
|||
void setScale(const glm::vec3& scale);
|
||||
const glm::vec3& getScale() const { return _scale; }
|
||||
|
||||
void enqueueLocationChange();
|
||||
|
||||
/// enables/disables scale to fit behavior, the model will be automatically scaled to the specified largest dimension
|
||||
bool getIsScaledToFit() const { return _scaledToFit; } /// is model scaled to fit
|
||||
glm::vec3 getScaleToFitDimensions() const; /// the dimensions model is scaled to, including inferred y/z
|
||||
|
|
|
@ -220,7 +220,7 @@ void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) {
|
|||
return;
|
||||
}
|
||||
|
||||
QUrl url = expandScriptUrl(normalizeScriptURL(scriptURL));
|
||||
QUrl url = expandScriptUrl(scriptURL);
|
||||
_fileNameString = url.toString();
|
||||
_isReloading = reload;
|
||||
|
||||
|
@ -847,7 +847,7 @@ QUrl ScriptEngine::resolvePath(const QString& include) const {
|
|||
QUrl url(include);
|
||||
// first lets check to see if it's already a full URL
|
||||
if (!url.scheme().isEmpty()) {
|
||||
return expandScriptUrl(normalizeScriptURL(url));
|
||||
return expandScriptUrl(url);
|
||||
}
|
||||
|
||||
// we apparently weren't a fully qualified url, so, let's assume we're relative
|
||||
|
@ -864,7 +864,7 @@ QUrl ScriptEngine::resolvePath(const QString& include) const {
|
|||
}
|
||||
|
||||
// at this point we should have a legitimate fully qualified URL for our parent
|
||||
url = expandScriptUrl(normalizeScriptURL(parentURL.resolved(url)));
|
||||
url = expandScriptUrl(parentURL.resolved(url));
|
||||
return url;
|
||||
}
|
||||
|
||||
|
|
|
@ -47,13 +47,6 @@ QUrl normalizeScriptURL(const QUrl& rawScriptURL) {
|
|||
QUrl fullNormal = rawScriptURL;
|
||||
QUrl defaultScriptLoc = defaultScriptsLocation();
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
#else
|
||||
// Force lowercase on file scripts because of drive letter weirdness.
|
||||
if (rawScriptURL.isLocalFile()) {
|
||||
fullNormal.setPath(fullNormal.path().toLower());
|
||||
}
|
||||
#endif
|
||||
// if this url is something "beneath" the default script url, replace the local path with ~
|
||||
if (fullNormal.scheme() == defaultScriptLoc.scheme() &&
|
||||
fullNormal.host() == defaultScriptLoc.host() &&
|
||||
|
@ -69,7 +62,8 @@ QUrl normalizeScriptURL(const QUrl& rawScriptURL) {
|
|||
}
|
||||
}
|
||||
|
||||
QUrl expandScriptUrl(const QUrl& normalizedScriptURL) {
|
||||
QUrl expandScriptUrl(const QUrl& rawScriptURL) {
|
||||
QUrl normalizedScriptURL = normalizeScriptURL(rawScriptURL);
|
||||
if (normalizedScriptURL.scheme() == "http" ||
|
||||
normalizedScriptURL.scheme() == "https" ||
|
||||
normalizedScriptURL.scheme() == "atp") {
|
||||
|
@ -230,7 +224,7 @@ QVariantList ScriptEngines::getRunning() {
|
|||
}
|
||||
QVariantMap resultNode;
|
||||
resultNode.insert("name", runningScriptURL.fileName());
|
||||
QUrl displayURL = expandScriptUrl(QUrl(runningScriptURL));
|
||||
QUrl displayURL = expandScriptUrl(runningScriptURL);
|
||||
QString displayURLString;
|
||||
if (displayURL.isLocalFile()) {
|
||||
displayURLString = displayURL.toLocalFile();
|
||||
|
@ -251,7 +245,7 @@ static const QString SETTINGS_KEY = "Settings";
|
|||
|
||||
void ScriptEngines::loadDefaultScripts() {
|
||||
QUrl defaultScriptsLoc = defaultScriptsLocation();
|
||||
defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "/scripts/defaultScripts.js");
|
||||
defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "/defaultScripts.js");
|
||||
loadScript(defaultScriptsLoc.toString());
|
||||
}
|
||||
|
||||
|
@ -268,7 +262,7 @@ void ScriptEngines::loadScripts() {
|
|||
loadDefaultScripts();
|
||||
_firstRun.set(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// loads all saved scripts
|
||||
Settings settings;
|
||||
|
@ -310,7 +304,12 @@ void ScriptEngines::saveScripts() {
|
|||
|
||||
QStringList ScriptEngines::getRunningScripts() {
|
||||
QReadLocker lock(&_scriptEnginesHashLock);
|
||||
return _scriptEnginesHash.keys();
|
||||
QList<QUrl> urls = _scriptEnginesHash.keys();
|
||||
QStringList result;
|
||||
for (auto url : urls) {
|
||||
result.append(url.toString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ScriptEngines::stopAllScripts(bool restart) {
|
||||
|
@ -318,7 +317,7 @@ void ScriptEngines::stopAllScripts(bool restart) {
|
|||
if (restart) {
|
||||
// Delete all running scripts from cache so that they are re-downloaded when they are restarted
|
||||
auto scriptCache = DependencyManager::get<ScriptCache>();
|
||||
for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
|
||||
for (QHash<QUrl, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
|
||||
it != _scriptEnginesHash.constEnd(); it++) {
|
||||
if (!it.value()->isFinished()) {
|
||||
scriptCache->deleteScript(it.key());
|
||||
|
@ -327,7 +326,7 @@ void ScriptEngines::stopAllScripts(bool restart) {
|
|||
}
|
||||
|
||||
// Stop and possibly restart all currently running scripts
|
||||
for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
|
||||
for (QHash<QUrl, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
|
||||
it != _scriptEnginesHash.constEnd(); it++) {
|
||||
if (it.value()->isFinished()) {
|
||||
continue;
|
||||
|
@ -350,21 +349,20 @@ bool ScriptEngines::stopScript(const QString& rawScriptURL, bool restart) {
|
|||
if (!scriptURL.isValid()) {
|
||||
scriptURL = normalizeScriptURL(QUrl::fromLocalFile(rawScriptURL));
|
||||
}
|
||||
const QString scriptURLString = scriptURL.toString();
|
||||
|
||||
QReadLocker lock(&_scriptEnginesHashLock);
|
||||
if (_scriptEnginesHash.contains(scriptURLString)) {
|
||||
ScriptEngine* scriptEngine = _scriptEnginesHash[scriptURLString];
|
||||
if (_scriptEnginesHash.contains(scriptURL)) {
|
||||
ScriptEngine* scriptEngine = _scriptEnginesHash[scriptURL];
|
||||
if (restart) {
|
||||
auto scriptCache = DependencyManager::get<ScriptCache>();
|
||||
scriptCache->deleteScript(QUrl(scriptURLString));
|
||||
scriptCache->deleteScript(scriptURL);
|
||||
connect(scriptEngine, &ScriptEngine::finished, this, [this](QString scriptName, ScriptEngine* engine) {
|
||||
reloadScript(scriptName);
|
||||
});
|
||||
}
|
||||
scriptEngine->stop();
|
||||
stoppedScript = true;
|
||||
qCDebug(scriptengine) << "stopping script..." << scriptURLString;
|
||||
qCDebug(scriptengine) << "stopping script..." << scriptURL;
|
||||
}
|
||||
}
|
||||
return stoppedScript;
|
||||
|
@ -379,7 +377,7 @@ void ScriptEngines::setScriptsLocation(const QString& scriptsLocation) {
|
|||
_scriptsModel.updateScriptsLocation(scriptsLocation);
|
||||
}
|
||||
|
||||
void ScriptEngines::reloadAllScripts() {
|
||||
void ScriptEngines::reloadAllScripts() {
|
||||
DependencyManager::get<ScriptCache>()->clearCache();
|
||||
emit scriptsReloading();
|
||||
stopAllScripts(true);
|
||||
|
@ -409,7 +407,7 @@ ScriptEngine* ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserL
|
|||
scriptUrl = normalizeScriptURL(scriptFilename);
|
||||
}
|
||||
|
||||
auto scriptEngine = getScriptEngine(scriptUrl.toString());
|
||||
auto scriptEngine = getScriptEngine(scriptUrl);
|
||||
if (scriptEngine) {
|
||||
return scriptEngine;
|
||||
}
|
||||
|
@ -435,12 +433,12 @@ ScriptEngine* ScriptEngines::loadScript(const QUrl& scriptFilename, bool isUserL
|
|||
return scriptEngine;
|
||||
}
|
||||
|
||||
ScriptEngine* ScriptEngines::getScriptEngine(const QString& rawScriptURL) {
|
||||
ScriptEngine* ScriptEngines::getScriptEngine(const QUrl& rawScriptURL) {
|
||||
ScriptEngine* result = nullptr;
|
||||
{
|
||||
QReadLocker lock(&_scriptEnginesHashLock);
|
||||
const QString scriptURLString = normalizeScriptURL(QUrl(rawScriptURL)).toString();
|
||||
auto it = _scriptEnginesHash.find(scriptURLString);
|
||||
const QUrl scriptURL = normalizeScriptURL(rawScriptURL);
|
||||
auto it = _scriptEnginesHash.find(scriptURL);
|
||||
if (it != _scriptEnginesHash.end()) {
|
||||
result = it.value();
|
||||
}
|
||||
|
@ -459,8 +457,7 @@ void ScriptEngines::onScriptEngineLoaded(const QString& rawScriptURL) {
|
|||
QWriteLocker lock(&_scriptEnginesHashLock);
|
||||
QUrl url = QUrl(rawScriptURL);
|
||||
QUrl normalized = normalizeScriptURL(url);
|
||||
const QString scriptURLString = normalized.toString();
|
||||
_scriptEnginesHash.insertMulti(scriptURLString, scriptEngine);
|
||||
_scriptEnginesHash.insertMulti(normalized, scriptEngine);
|
||||
}
|
||||
emit scriptCountChanged();
|
||||
}
|
||||
|
@ -486,8 +483,8 @@ void ScriptEngines::onScriptFinished(const QString& rawScriptURL, ScriptEngine*
|
|||
bool removed = false;
|
||||
{
|
||||
QWriteLocker lock(&_scriptEnginesHashLock);
|
||||
const QString scriptURLString = normalizeScriptURL(QUrl(rawScriptURL)).toString();
|
||||
for (auto it = _scriptEnginesHash.find(scriptURLString); it != _scriptEnginesHash.end(); ++it) {
|
||||
const QUrl scriptURL = normalizeScriptURL(QUrl(rawScriptURL));
|
||||
for (auto it = _scriptEnginesHash.find(scriptURL); it != _scriptEnginesHash.end(); ++it) {
|
||||
if (it.value() == engine) {
|
||||
_scriptEnginesHash.erase(it);
|
||||
removed = true;
|
||||
|
|
|
@ -45,7 +45,7 @@ public:
|
|||
void loadDefaultScripts();
|
||||
void setScriptsLocation(const QString& scriptsLocation);
|
||||
QStringList getRunningScripts();
|
||||
ScriptEngine* getScriptEngine(const QString& scriptHash);
|
||||
ScriptEngine* getScriptEngine(const QUrl& scriptHash);
|
||||
|
||||
ScriptsModel* scriptsModel() { return &_scriptsModel; };
|
||||
ScriptsModelFilter* scriptsModelFilter() { return &_scriptsModelFilter; };
|
||||
|
@ -65,12 +65,12 @@ public:
|
|||
// Called at shutdown time
|
||||
void shutdownScripting();
|
||||
|
||||
signals:
|
||||
signals:
|
||||
void scriptCountChanged();
|
||||
void scriptsReloading();
|
||||
void scriptLoadError(const QString& filename, const QString& error);
|
||||
|
||||
protected slots:
|
||||
protected slots:
|
||||
void onScriptFinished(const QString& fileNameString, ScriptEngine* engine);
|
||||
|
||||
protected:
|
||||
|
@ -86,7 +86,7 @@ protected:
|
|||
|
||||
Setting::Handle<bool> _firstRun { "firstRun", true };
|
||||
QReadWriteLock _scriptEnginesHashLock;
|
||||
QHash<QString, ScriptEngine*> _scriptEnginesHash;
|
||||
QHash<QUrl, ScriptEngine*> _scriptEnginesHash;
|
||||
QSet<ScriptEngine*> _allKnownScriptEngines;
|
||||
QMutex _allScriptsMutex;
|
||||
std::atomic<bool> _stoppingAllScripts { false };
|
||||
|
@ -97,6 +97,6 @@ protected:
|
|||
};
|
||||
|
||||
QUrl normalizeScriptURL(const QUrl& rawScriptURL);
|
||||
QUrl expandScriptUrl(const QUrl& normalizedScriptURL);
|
||||
QUrl expandScriptUrl(const QUrl& rawScriptURL);
|
||||
|
||||
#endif // hifi_ScriptEngine_h
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#define __STR1__(x) __STR2__(x)
|
||||
#define __LOC__ __FILE__ "(" __STR1__(__LINE__) ") : Warning Msg: "
|
||||
|
||||
static const QString MODELS_LOCATION = "scripts/";
|
||||
static const QString PREFIX_PARAMETER_NAME = "prefix";
|
||||
static const QString MARKER_PARAMETER_NAME = "marker";
|
||||
static const QString IS_TRUNCATED_NAME = "IsTruncated";
|
||||
|
@ -41,7 +40,7 @@ TreeNodeBase::TreeNodeBase(TreeNodeFolder* parent, const QString& name, TreeNode
|
|||
TreeNodeScript::TreeNodeScript(const QString& localPath, const QString& fullPath, ScriptOrigin origin) :
|
||||
TreeNodeBase(NULL, localPath.split("/").last(), TREE_NODE_TYPE_SCRIPT),
|
||||
_localPath(localPath),
|
||||
_fullPath(fullPath),
|
||||
_fullPath(expandScriptUrl(QUrl(fullPath)).toString()),
|
||||
_origin(origin) {
|
||||
};
|
||||
|
||||
|
@ -159,9 +158,11 @@ void ScriptsModel::requestDefaultFiles(QString marker) {
|
|||
|
||||
if (url.isLocalFile()) {
|
||||
// if the url indicates a local directory, use QDirIterator
|
||||
// QString localDir = url.toLocalFile() + "/scripts";
|
||||
QString localDir = expandScriptUrl(url).toLocalFile() + "/scripts";
|
||||
QString localDir = expandScriptUrl(url).toLocalFile();
|
||||
int localDirPartCount = localDir.split("/").size();
|
||||
if (localDir.endsWith("/")) {
|
||||
localDirPartCount--;
|
||||
}
|
||||
#ifdef Q_OS_WIN
|
||||
localDirPartCount++; // one for the drive letter
|
||||
#endif
|
||||
|
@ -176,7 +177,7 @@ void ScriptsModel::requestDefaultFiles(QString marker) {
|
|||
} else {
|
||||
// the url indicates http(s), use QNetworkRequest
|
||||
QUrlQuery query;
|
||||
query.addQueryItem(PREFIX_PARAMETER_NAME, MODELS_LOCATION);
|
||||
query.addQueryItem(PREFIX_PARAMETER_NAME, ".");
|
||||
if (!marker.isEmpty()) {
|
||||
query.addQueryItem(MARKER_PARAMETER_NAME, marker);
|
||||
}
|
||||
|
@ -240,7 +241,7 @@ bool ScriptsModel::parseXML(QByteArray xmlFile) {
|
|||
if (jsRegex.exactMatch(xml.text().toString())) {
|
||||
QString localPath = lastKey.split("/").mid(1).join("/");
|
||||
QUrl fullPath = defaultScriptsLocation();
|
||||
fullPath.setPath(fullPath.path() + "/" + lastKey);
|
||||
fullPath.setPath(fullPath.path() + lastKey);
|
||||
const QString fullPathStr = normalizeScriptURL(fullPath).toString();
|
||||
_treeNodes.append(new TreeNodeScript(localPath, fullPathStr, SCRIPT_ORIGIN_DEFAULT));
|
||||
}
|
||||
|
|
75
libraries/shared/src/CPUID.cpp
Normal file
75
libraries/shared/src/CPUID.cpp
Normal file
|
@ -0,0 +1,75 @@
|
|||
//
|
||||
// CPUID.cpp
|
||||
//
|
||||
// Created by Ryan Huffman on 3/25/16.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "CPUID.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
|
||||
const CPUID::CPUID_Internal CPUID::CPU_Rep;
|
||||
|
||||
std::vector<CPUID::Feature> CPUID::getAllFeatures() {
|
||||
std::vector<CPUID::Feature> features;
|
||||
|
||||
features.push_back({ "3DNOW", CPUID::_3DNOW() });
|
||||
features.push_back({ "3DNOWEXT", CPUID::_3DNOWEXT() });
|
||||
features.push_back({ "ABM", CPUID::ABM() });
|
||||
features.push_back({ "ADX", CPUID::ADX() });
|
||||
features.push_back({ "AES", CPUID::AES() });
|
||||
features.push_back({ "AVX", CPUID::AVX() });
|
||||
features.push_back({ "AVX2", CPUID::AVX2() });
|
||||
features.push_back({ "AVX512CD", CPUID::AVX512CD() });
|
||||
features.push_back({ "AVX512ER", CPUID::AVX512ER() });
|
||||
features.push_back({ "AVX512F", CPUID::AVX512F() });
|
||||
features.push_back({ "AVX512PF", CPUID::AVX512PF() });
|
||||
features.push_back({ "BMI1", CPUID::BMI1() });
|
||||
features.push_back({ "BMI2", CPUID::BMI2() });
|
||||
features.push_back({ "CLFSH", CPUID::CLFSH() });
|
||||
features.push_back({ "CMPXCHG16B", CPUID::CMPXCHG16B() });
|
||||
features.push_back({ "CX8", CPUID::CX8() });
|
||||
features.push_back({ "ERMS", CPUID::ERMS() });
|
||||
features.push_back({ "F16C", CPUID::F16C() });
|
||||
features.push_back({ "FMA", CPUID::FMA() });
|
||||
features.push_back({ "FSGSBASE", CPUID::FSGSBASE() });
|
||||
features.push_back({ "FXSR", CPUID::FXSR() });
|
||||
features.push_back({ "HLE", CPUID::HLE() });
|
||||
features.push_back({ "INVPCID", CPUID::INVPCID() });
|
||||
features.push_back({ "LAHF", CPUID::LAHF() });
|
||||
features.push_back({ "LZCNT", CPUID::LZCNT() });
|
||||
features.push_back({ "MMX", CPUID::MMX() });
|
||||
features.push_back({ "MMXEXT", CPUID::MMXEXT() });
|
||||
features.push_back({ "MONITOR", CPUID::MONITOR() });
|
||||
features.push_back({ "MOVBE", CPUID::MOVBE() });
|
||||
features.push_back({ "MSR", CPUID::MSR() });
|
||||
features.push_back({ "OSXSAVE", CPUID::OSXSAVE() });
|
||||
features.push_back({ "PCLMULQDQ", CPUID::PCLMULQDQ() });
|
||||
features.push_back({ "POPCNT", CPUID::POPCNT() });
|
||||
features.push_back({ "PREFETCHWT1", CPUID::PREFETCHWT1() });
|
||||
features.push_back({ "RDRAND", CPUID::RDRAND() });
|
||||
features.push_back({ "RDSEED", CPUID::RDSEED() });
|
||||
features.push_back({ "RDTSCP", CPUID::RDTSCP() });
|
||||
features.push_back({ "RTM", CPUID::RTM() });
|
||||
features.push_back({ "SEP", CPUID::SEP() });
|
||||
features.push_back({ "SHA", CPUID::SHA() });
|
||||
features.push_back({ "SSE", CPUID::SSE() });
|
||||
features.push_back({ "SSE2", CPUID::SSE2() });
|
||||
features.push_back({ "SSE3", CPUID::SSE3() });
|
||||
features.push_back({ "SSE4.1", CPUID::SSE41() });
|
||||
features.push_back({ "SSE4.2", CPUID::SSE42() });
|
||||
features.push_back({ "SSE4a", CPUID::SSE4a() });
|
||||
features.push_back({ "SSSE3", CPUID::SSSE3() });
|
||||
features.push_back({ "SYSCALL", CPUID::SYSCALL() });
|
||||
features.push_back({ "TBM", CPUID::TBM() });
|
||||
features.push_back({ "XOP", CPUID::XOP() });
|
||||
features.push_back({ "XSAVE", CPUID::XSAVE() });
|
||||
|
||||
return features;
|
||||
};
|
||||
|
||||
#endif
|
212
libraries/shared/src/CPUID.h
Normal file
212
libraries/shared/src/CPUID.h
Normal file
|
@ -0,0 +1,212 @@
|
|||
//
|
||||
// CPUID.h
|
||||
//
|
||||
// Adapted from Microsoft's example for using the cpuid intrinsic,
|
||||
// found at https://msdn.microsoft.com/en-us/library/hskdteyh.aspx
|
||||
//
|
||||
// Provides acccess to information provided by the CPUID opcode
|
||||
//
|
||||
// TODO: Generalize to work outside of Windows.
|
||||
//
|
||||
// Created by Ryan Huffman on 3/25/16.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_CPUID_h
|
||||
#define hifi_CPUID_h
|
||||
|
||||
#include <QtCore/QtGlobal>
|
||||
|
||||
#include <vector>
|
||||
#include <bitset>
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
|
||||
#include <intrin.h>
|
||||
|
||||
class CPUID
|
||||
{
|
||||
// forward declarations
|
||||
class CPUID_Internal;
|
||||
|
||||
public:
|
||||
struct Feature {
|
||||
std::string name;
|
||||
bool supported;
|
||||
};
|
||||
|
||||
static std::vector<Feature> getAllFeatures();
|
||||
|
||||
static std::string Vendor(void) { return CPU_Rep.vendor_; }
|
||||
static std::string Brand(void) { return CPU_Rep.brand_; }
|
||||
|
||||
static bool SSE3(void) { return CPU_Rep.f_1_ECX_[0]; }
|
||||
static bool PCLMULQDQ(void) { return CPU_Rep.f_1_ECX_[1]; }
|
||||
static bool MONITOR(void) { return CPU_Rep.f_1_ECX_[3]; }
|
||||
static bool SSSE3(void) { return CPU_Rep.f_1_ECX_[9]; }
|
||||
static bool FMA(void) { return CPU_Rep.f_1_ECX_[12]; }
|
||||
static bool CMPXCHG16B(void) { return CPU_Rep.f_1_ECX_[13]; }
|
||||
static bool SSE41(void) { return CPU_Rep.f_1_ECX_[19]; }
|
||||
static bool SSE42(void) { return CPU_Rep.f_1_ECX_[20]; }
|
||||
static bool MOVBE(void) { return CPU_Rep.f_1_ECX_[22]; }
|
||||
static bool POPCNT(void) { return CPU_Rep.f_1_ECX_[23]; }
|
||||
static bool AES(void) { return CPU_Rep.f_1_ECX_[25]; }
|
||||
static bool XSAVE(void) { return CPU_Rep.f_1_ECX_[26]; }
|
||||
static bool OSXSAVE(void) { return CPU_Rep.f_1_ECX_[27]; }
|
||||
static bool AVX(void) { return CPU_Rep.f_1_ECX_[28]; }
|
||||
static bool F16C(void) { return CPU_Rep.f_1_ECX_[29]; }
|
||||
static bool RDRAND(void) { return CPU_Rep.f_1_ECX_[30]; }
|
||||
|
||||
static bool MSR(void) { return CPU_Rep.f_1_EDX_[5]; }
|
||||
static bool CX8(void) { return CPU_Rep.f_1_EDX_[8]; }
|
||||
static bool SEP(void) { return CPU_Rep.f_1_EDX_[11]; }
|
||||
static bool CMOV(void) { return CPU_Rep.f_1_EDX_[15]; }
|
||||
static bool CLFSH(void) { return CPU_Rep.f_1_EDX_[19]; }
|
||||
static bool MMX(void) { return CPU_Rep.f_1_EDX_[23]; }
|
||||
static bool FXSR(void) { return CPU_Rep.f_1_EDX_[24]; }
|
||||
static bool SSE(void) { return CPU_Rep.f_1_EDX_[25]; }
|
||||
static bool SSE2(void) { return CPU_Rep.f_1_EDX_[26]; }
|
||||
|
||||
static bool FSGSBASE(void) { return CPU_Rep.f_7_EBX_[0]; }
|
||||
static bool BMI1(void) { return CPU_Rep.f_7_EBX_[3]; }
|
||||
static bool HLE(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_7_EBX_[4]; }
|
||||
static bool AVX2(void) { return CPU_Rep.f_7_EBX_[5]; }
|
||||
static bool BMI2(void) { return CPU_Rep.f_7_EBX_[8]; }
|
||||
static bool ERMS(void) { return CPU_Rep.f_7_EBX_[9]; }
|
||||
static bool INVPCID(void) { return CPU_Rep.f_7_EBX_[10]; }
|
||||
static bool RTM(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_7_EBX_[11]; }
|
||||
static bool AVX512F(void) { return CPU_Rep.f_7_EBX_[16]; }
|
||||
static bool RDSEED(void) { return CPU_Rep.f_7_EBX_[18]; }
|
||||
static bool ADX(void) { return CPU_Rep.f_7_EBX_[19]; }
|
||||
static bool AVX512PF(void) { return CPU_Rep.f_7_EBX_[26]; }
|
||||
static bool AVX512ER(void) { return CPU_Rep.f_7_EBX_[27]; }
|
||||
static bool AVX512CD(void) { return CPU_Rep.f_7_EBX_[28]; }
|
||||
static bool SHA(void) { return CPU_Rep.f_7_EBX_[29]; }
|
||||
|
||||
static bool PREFETCHWT1(void) { return CPU_Rep.f_7_ECX_[0]; }
|
||||
|
||||
static bool LAHF(void) { return CPU_Rep.f_81_ECX_[0]; }
|
||||
static bool LZCNT(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_81_ECX_[5]; }
|
||||
static bool ABM(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[5]; }
|
||||
static bool SSE4a(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[6]; }
|
||||
static bool XOP(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[11]; }
|
||||
static bool TBM(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[21]; }
|
||||
|
||||
static bool SYSCALL(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_81_EDX_[11]; }
|
||||
static bool MMXEXT(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[22]; }
|
||||
static bool RDTSCP(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_81_EDX_[27]; }
|
||||
static bool _3DNOWEXT(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[30]; }
|
||||
static bool _3DNOW(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[31]; }
|
||||
|
||||
private:
|
||||
static const CPUID_Internal CPU_Rep;
|
||||
|
||||
class CPUID_Internal
|
||||
{
|
||||
public:
|
||||
CPUID_Internal()
|
||||
: nIds_ { 0 },
|
||||
nExIds_ { 0 },
|
||||
isIntel_ { false },
|
||||
isAMD_ { false },
|
||||
f_1_ECX_ { 0 },
|
||||
f_1_EDX_ { 0 },
|
||||
f_7_EBX_ { 0 },
|
||||
f_7_ECX_ { 0 },
|
||||
f_81_ECX_ { 0 },
|
||||
f_81_EDX_ { 0 },
|
||||
data_ {},
|
||||
extdata_ {}
|
||||
{
|
||||
//int cpuInfo[4] = {-1};
|
||||
std::array<int, 4> cpui;
|
||||
|
||||
// Calling __cpuid with 0x0 as the function_id argument
|
||||
// gets the number of the highest valid function ID.
|
||||
__cpuid(cpui.data(), 0);
|
||||
nIds_ = cpui[0];
|
||||
|
||||
for (int i = 0; i <= nIds_; ++i) {
|
||||
__cpuidex(cpui.data(), i, 0);
|
||||
data_.push_back(cpui);
|
||||
}
|
||||
|
||||
// Capture vendor string
|
||||
char vendor[0x20];
|
||||
memset(vendor, 0, sizeof(vendor));
|
||||
*reinterpret_cast<int*>(vendor) = data_[0][1];
|
||||
*reinterpret_cast<int*>(vendor + 4) = data_[0][3];
|
||||
*reinterpret_cast<int*>(vendor + 8) = data_[0][2];
|
||||
vendor_ = vendor;
|
||||
if (vendor_ == "GenuineIntel") {
|
||||
isIntel_ = true;
|
||||
} else if (vendor_ == "AuthenticAMD") {
|
||||
isAMD_ = true;
|
||||
}
|
||||
|
||||
// load bitset with flags for function 0x00000001
|
||||
if (nIds_ >= 1) {
|
||||
f_1_ECX_ = data_[1][2];
|
||||
f_1_EDX_ = data_[1][3];
|
||||
}
|
||||
|
||||
// load bitset with flags for function 0x00000007
|
||||
if (nIds_ >= 7) {
|
||||
f_7_EBX_ = data_[7][1];
|
||||
f_7_ECX_ = data_[7][2];
|
||||
}
|
||||
|
||||
// Calling __cpuid with 0x80000000 as the function_id argument
|
||||
// gets the number of the highest valid extended ID.
|
||||
__cpuid(cpui.data(), 0x80000000);
|
||||
nExIds_ = cpui[0];
|
||||
|
||||
char brand[0x40];
|
||||
memset(brand, 0, sizeof(brand));
|
||||
|
||||
for (int i = 0x80000000; i <= nExIds_; ++i) {
|
||||
__cpuidex(cpui.data(), i, 0);
|
||||
extdata_.push_back(cpui);
|
||||
}
|
||||
|
||||
// load bitset with flags for function 0x80000001
|
||||
if (nExIds_ >= 0x80000001) {
|
||||
f_81_ECX_ = extdata_[1][2];
|
||||
f_81_EDX_ = extdata_[1][3];
|
||||
}
|
||||
|
||||
// Interpret CPU brand string if reported
|
||||
if (nExIds_ >= 0x80000004) {
|
||||
memcpy(brand, extdata_[2].data(), sizeof(cpui));
|
||||
memcpy(brand + 16, extdata_[3].data(), sizeof(cpui));
|
||||
memcpy(brand + 32, extdata_[4].data(), sizeof(cpui));
|
||||
brand_ = brand;
|
||||
}
|
||||
};
|
||||
|
||||
int nIds_;
|
||||
int nExIds_;
|
||||
std::string vendor_;
|
||||
std::string brand_;
|
||||
bool isIntel_;
|
||||
bool isAMD_;
|
||||
std::bitset<32> f_1_ECX_;
|
||||
std::bitset<32> f_1_EDX_;
|
||||
std::bitset<32> f_7_EBX_;
|
||||
std::bitset<32> f_7_ECX_;
|
||||
std::bitset<32> f_81_ECX_;
|
||||
std::bitset<32> f_81_EDX_;
|
||||
std::vector<std::array<int, 4>> data_;
|
||||
std::vector<std::array<int, 4>> extdata_;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif // hifi_CPUID_h
|
|
@ -57,11 +57,11 @@ QString findMostRecentFileExtension(const QString& originalFileName, QVector<QSt
|
|||
|
||||
QUrl defaultScriptsLocation() {
|
||||
#ifdef Q_OS_WIN
|
||||
return QUrl(("file:///" + QCoreApplication::applicationDirPath()).toLower());
|
||||
return QUrl(("file:///" + QCoreApplication::applicationDirPath()).toLower() + "/scripts");
|
||||
#elif defined(Q_OS_OSX)
|
||||
return QUrl(("file://" + QCoreApplication::applicationDirPath() + "/../Resources").toLower());
|
||||
return QUrl(("file://" + QCoreApplication::applicationDirPath() + "/../Resources/scripts").toLower());
|
||||
#else
|
||||
// return "http://s3.amazonaws.com/hifi-public";
|
||||
return QUrl("file://" + QCoreApplication::applicationDirPath());
|
||||
return QUrl("file://" + QCoreApplication::applicationDirPath() + "/scripts");
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -23,6 +23,11 @@
|
|||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include "CPUID.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
|
@ -30,6 +35,8 @@
|
|||
#include <QtCore/QDebug>
|
||||
#include <QDateTime>
|
||||
#include <QElapsedTimer>
|
||||
#include <QProcess>
|
||||
#include <QSysInfo>
|
||||
#include <QThread>
|
||||
|
||||
#include "NumericalConstants.h"
|
||||
|
@ -692,3 +699,82 @@ void disableQtBearerPoll() {
|
|||
qputenv("QT_BEARER_POLL_TIMEOUT", EXTREME_BEARER_POLL_TIMEOUT);
|
||||
}
|
||||
|
||||
void printSystemInformation() {
|
||||
// Write system information to log
|
||||
qDebug() << "Build Information";
|
||||
qDebug().noquote() << "\tBuild ABI: " << QSysInfo::buildAbi();
|
||||
qDebug().noquote() << "\tBuild CPU Architecture: " << QSysInfo::buildCpuArchitecture();
|
||||
|
||||
qDebug().noquote() << "System Information";
|
||||
qDebug().noquote() << "\tProduct Name: " << QSysInfo::prettyProductName();
|
||||
qDebug().noquote() << "\tCPU Architecture: " << QSysInfo::currentCpuArchitecture();
|
||||
qDebug().noquote() << "\tKernel Type: " << QSysInfo::kernelType();
|
||||
qDebug().noquote() << "\tKernel Version: " << QSysInfo::kernelVersion();
|
||||
|
||||
auto macVersion = QSysInfo::macVersion();
|
||||
if (macVersion != QSysInfo::MV_None) {
|
||||
qDebug() << "\tMac Version: " << macVersion;
|
||||
}
|
||||
|
||||
auto windowsVersion = QSysInfo::windowsVersion();
|
||||
if (windowsVersion != QSysInfo::WV_None) {
|
||||
qDebug() << "\tWindows Version: " << windowsVersion;
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
SYSTEM_INFO si;
|
||||
GetNativeSystemInfo(&si);
|
||||
|
||||
qDebug() << "SYSTEM_INFO";
|
||||
qDebug().noquote() << "\tOEM ID: " << si.dwOemId;
|
||||
qDebug().noquote() << "\tProcessor Architecture: " << si.wProcessorArchitecture;
|
||||
qDebug().noquote() << "\tProcessor Type: " << si.dwProcessorType;
|
||||
qDebug().noquote() << "\tProcessor Level: " << si.wProcessorLevel;
|
||||
qDebug().noquote() << "\tProcessor Revision: "
|
||||
<< QString("0x%1").arg(si.wProcessorRevision, 4, 16, QChar('0'));
|
||||
qDebug().noquote() << "\tNumber of Processors: " << si.dwNumberOfProcessors;
|
||||
qDebug().noquote() << "\tPage size: " << si.dwPageSize << " Bytes";
|
||||
qDebug().noquote() << "\tMin Application Address: "
|
||||
<< QString("0x%1").arg(qulonglong(si.lpMinimumApplicationAddress), 16, 16, QChar('0'));
|
||||
qDebug().noquote() << "\tMax Application Address: "
|
||||
<< QString("0x%1").arg(qulonglong(si.lpMaximumApplicationAddress), 16, 16, QChar('0'));
|
||||
|
||||
const double BYTES_TO_MEGABYTE = 1.0 / (1024 * 1024);
|
||||
|
||||
qDebug() << "MEMORYSTATUSEX";
|
||||
MEMORYSTATUSEX ms;
|
||||
ms.dwLength = sizeof(ms);
|
||||
if (GlobalMemoryStatusEx(&ms)) {
|
||||
qDebug().noquote() << QString("\tCurrent System Memory Usage: %1%").arg(ms.dwMemoryLoad);
|
||||
qDebug().noquote() << QString("\tAvail Physical Memory: %1 MB").arg(ms.ullAvailPhys * BYTES_TO_MEGABYTE, 20, 'f', 2);
|
||||
qDebug().noquote() << QString("\tTotal Physical Memory: %1 MB").arg(ms.ullTotalPhys * BYTES_TO_MEGABYTE, 20, 'f', 2);
|
||||
qDebug().noquote() << QString("\tAvail in Page File: %1 MB").arg(ms.ullAvailPageFile * BYTES_TO_MEGABYTE, 20, 'f', 2);
|
||||
qDebug().noquote() << QString("\tTotal in Page File: %1 MB").arg(ms.ullTotalPageFile * BYTES_TO_MEGABYTE, 20, 'f', 2);
|
||||
qDebug().noquote() << QString("\tAvail Virtual Memory: %1 MB").arg(ms.ullAvailVirtual * BYTES_TO_MEGABYTE, 20, 'f', 2);
|
||||
qDebug().noquote() << QString("\tTotal Virtual Memory: %1 MB").arg(ms.ullTotalVirtual * BYTES_TO_MEGABYTE, 20, 'f', 2);
|
||||
} else {
|
||||
qDebug() << "\tFailed to retrieve memory status: " << GetLastError();
|
||||
}
|
||||
|
||||
qDebug() << "CPUID";
|
||||
|
||||
qDebug() << "\tCPU Vendor: " << CPUID::Vendor().c_str();
|
||||
qDebug() << "\tCPU Brand: " << CPUID::Brand().c_str();
|
||||
|
||||
for (auto& feature : CPUID::getAllFeatures()) {
|
||||
qDebug().nospace().noquote() << "\t[" << (feature.supported ? "x" : " ") << "] " << feature.name.c_str();
|
||||
}
|
||||
#endif
|
||||
|
||||
qDebug() << "Environment Variables";
|
||||
// List of env variables to include in the log. For privacy reasons we don't send all env variables.
|
||||
const QStringList envWhitelist = {
|
||||
"QTWEBENGINE_REMOTE_DEBUGGING"
|
||||
};
|
||||
auto envVariables = QProcessEnvironment::systemEnvironment();
|
||||
for (auto& env : envWhitelist)
|
||||
{
|
||||
qDebug().noquote().nospace() << "\t" <<
|
||||
(envVariables.contains(env) ? " = " + envVariables.value(env) : " NOT FOUND");
|
||||
}
|
||||
}
|
|
@ -198,4 +198,6 @@ uint qHash(const std::shared_ptr<T>& ptr, uint seed = 0)
|
|||
|
||||
void disableQtBearerPoll();
|
||||
|
||||
void printSystemInformation();
|
||||
|
||||
#endif // hifi_SharedUtil_h
|
||||
|
|
|
@ -130,8 +130,13 @@ void QmlWindowClass::initQml(QVariantMap properties) {
|
|||
}
|
||||
|
||||
void QmlWindowClass::qmlToScript(const QVariant& message) {
|
||||
QJSValue js = qvariant_cast<QJSValue>(message);
|
||||
emit fromQml(js.toVariant());
|
||||
if (message.canConvert<QJSValue>()) {
|
||||
emit fromQml(qvariant_cast<QJSValue>(message).toVariant());
|
||||
} else if (message.canConvert<QString>()) {
|
||||
emit fromQml(message.toString());
|
||||
} else {
|
||||
qWarning() << "Unsupported message type " << message;
|
||||
}
|
||||
}
|
||||
|
||||
void QmlWindowClass::sendToQml(const QVariant& message) {
|
||||
|
|
|
@ -220,6 +220,24 @@ ApplicationWindow {
|
|||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Add Tab 2"
|
||||
onClicked: {
|
||||
console.log(desktop.toolWindow);
|
||||
desktop.toolWindow.addWebTab({ source: "Foo 2" });
|
||||
desktop.toolWindow.showTabForUrl("Foo 2", true);
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Add Tab 3"
|
||||
onClicked: {
|
||||
console.log(desktop.toolWindow);
|
||||
desktop.toolWindow.addWebTab({ source: "Foo 3" });
|
||||
desktop.toolWindow.showTabForUrl("Foo 3", true);
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Destroy Tab"
|
||||
onClicked: {
|
||||
|
|
|
@ -57,13 +57,13 @@ const QCommandLineOption STATS_INTERVAL {
|
|||
};
|
||||
|
||||
const QStringList CLIENT_STATS_TABLE_HEADERS {
|
||||
"Send (P/s)", "Est. Max (P/s)", "RTT (ms)", "CW (P)", "Period (us)",
|
||||
"Send (Mb/s)", "Est. Max (Mb/s)", "RTT (ms)", "CW (P)", "Period (us)",
|
||||
"Recv ACK", "Procd ACK", "Recv LACK", "Recv NAK", "Recv TNAK",
|
||||
"Sent ACK2", "Sent Packets", "Re-sent Packets"
|
||||
};
|
||||
|
||||
const QStringList SERVER_STATS_TABLE_HEADERS {
|
||||
" Mb/s ", "Recv P/s", "Est. Max (P/s)", "RTT (ms)", "CW (P)",
|
||||
" Mb/s ", "Recv Mb/s", "Est. Max (Mb/s)", "RTT (ms)", "CW (P)",
|
||||
"Sent ACK", "Sent LACK", "Sent NAK", "Sent TNAK",
|
||||
"Recv ACK2", "Duplicates (P)"
|
||||
};
|
||||
|
@ -364,7 +364,11 @@ void UDTTest::handleMessage(std::unique_ptr<Message> message) {
|
|||
void UDTTest::sampleStats() {
|
||||
static bool first = true;
|
||||
static const double USECS_PER_MSEC = 1000.0;
|
||||
|
||||
static const double MEGABITS_PER_BYTE = 8.0 / 1000000.0;
|
||||
static const double MS_PER_SECOND = 1000.0;
|
||||
static const double PPS_TO_MBPS = 1500.0 * MEGABITS_PER_BYTE;
|
||||
|
||||
|
||||
if (!_target.isNull()) {
|
||||
if (first) {
|
||||
// output the headers for stats for our table
|
||||
|
@ -378,8 +382,8 @@ void UDTTest::sampleStats() {
|
|||
|
||||
// setup a list of left justified values
|
||||
QStringList values {
|
||||
QString::number(stats.sendRate).rightJustified(CLIENT_STATS_TABLE_HEADERS[++headerIndex].size()),
|
||||
QString::number(stats.estimatedBandwith).rightJustified(CLIENT_STATS_TABLE_HEADERS[++headerIndex].size()),
|
||||
QString::number(stats.sendRate * PPS_TO_MBPS).rightJustified(CLIENT_STATS_TABLE_HEADERS[++headerIndex].size()),
|
||||
QString::number(stats.estimatedBandwith * PPS_TO_MBPS).rightJustified(CLIENT_STATS_TABLE_HEADERS[++headerIndex].size()),
|
||||
QString::number(stats.rtt / USECS_PER_MSEC, 'f', 2).rightJustified(CLIENT_STATS_TABLE_HEADERS[++headerIndex].size()),
|
||||
QString::number(stats.congestionWindowSize).rightJustified(CLIENT_STATS_TABLE_HEADERS[++headerIndex].size()),
|
||||
QString::number(stats.packetSendPeriod).rightJustified(CLIENT_STATS_TABLE_HEADERS[++headerIndex].size()),
|
||||
|
@ -408,16 +412,13 @@ void UDTTest::sampleStats() {
|
|||
|
||||
int headerIndex = -1;
|
||||
|
||||
static const double MEGABITS_PER_BYTE = 8.0 / 1000000.0;
|
||||
static const double MS_PER_SECOND = 1000.0;
|
||||
|
||||
double megabitsPerSecond = (stats.receivedBytes * MEGABITS_PER_BYTE * MS_PER_SECOND) / _statsInterval;
|
||||
|
||||
// setup a list of left justified values
|
||||
QStringList values {
|
||||
QString::number(megabitsPerSecond, 'f', 2).rightJustified(SERVER_STATS_TABLE_HEADERS[++headerIndex].size()),
|
||||
QString::number(stats.receiveRate).rightJustified(SERVER_STATS_TABLE_HEADERS[++headerIndex].size()),
|
||||
QString::number(stats.estimatedBandwith).rightJustified(SERVER_STATS_TABLE_HEADERS[++headerIndex].size()),
|
||||
QString::number(stats.receiveRate * PPS_TO_MBPS).rightJustified(SERVER_STATS_TABLE_HEADERS[++headerIndex].size()),
|
||||
QString::number(stats.estimatedBandwith * PPS_TO_MBPS).rightJustified(SERVER_STATS_TABLE_HEADERS[++headerIndex].size()),
|
||||
QString::number(stats.rtt / USECS_PER_MSEC, 'f', 2).rightJustified(SERVER_STATS_TABLE_HEADERS[++headerIndex].size()),
|
||||
QString::number(stats.congestionWindowSize).rightJustified(SERVER_STATS_TABLE_HEADERS[++headerIndex].size()),
|
||||
QString::number(stats.events[udt::ConnectionStats::Stats::SentACK]).rightJustified(SERVER_STATS_TABLE_HEADERS[++headerIndex].size()),
|
||||
|
|
|
@ -34,20 +34,24 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) {
|
|||
return false;
|
||||
}
|
||||
std::cout << "Reading FBX.....\n";
|
||||
try {
|
||||
QByteArray fbxContents = fbx.readAll();
|
||||
FBXGeometry* geom;
|
||||
if (filename.toLower().endsWith(".obj")) {
|
||||
geom = OBJReader().readOBJ(fbxContents, QVariantHash());
|
||||
} else if (filename.toLower().endsWith(".fbx")) {
|
||||
geom = readFBX(fbxContents, QVariantHash(), filename);
|
||||
} else {
|
||||
qDebug() << "unknown file extension";
|
||||
return false;
|
||||
}
|
||||
result = *geom;
|
||||
|
||||
QByteArray fbxContents = fbx.readAll();
|
||||
FBXGeometry* geom;
|
||||
if (filename.toLower().endsWith(".obj")) {
|
||||
geom = OBJReader().readOBJ(fbxContents, QVariantHash());
|
||||
} else if (filename.toLower().endsWith(".fbx")) {
|
||||
geom = readFBX(fbxContents, QVariantHash(), filename);
|
||||
} else {
|
||||
qDebug() << "unknown file extension";
|
||||
reSortFBXGeometryMeshes(result);
|
||||
} catch (const QString& error) {
|
||||
qDebug() << "Error reading " << filename << ": " << error;
|
||||
return false;
|
||||
}
|
||||
result = *geom;
|
||||
|
||||
reSortFBXGeometryMeshes(result);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue