Merge branch 'master' of github.com:highfidelity/hifi into fix-update-entity-operator

This commit is contained in:
Seth Alves 2016-05-13 07:12:11 -07:00
commit 9bd4b4f8d8
218 changed files with 3641 additions and 1925 deletions

View file

@ -386,8 +386,7 @@ void Agent::sendAvatarBillboardPacket() {
void Agent::processAgentAvatarAndAudio(float deltaTime) {
if (!_scriptEngine->isFinished() && _isAvatar) {
auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>();
const int SCRIPT_AUDIO_BUFFER_SAMPLES = floor(((SCRIPT_DATA_CALLBACK_USECS * AudioConstants::SAMPLE_RATE)
/ (1000 * 1000)) + 0.5);
const int SCRIPT_AUDIO_BUFFER_SAMPLES = AudioConstants::SAMPLE_RATE / SCRIPT_FPS + 0.5;
const int SCRIPT_AUDIO_BUFFER_BYTES = SCRIPT_AUDIO_BUFFER_SAMPLES * sizeof(int16_t);
QByteArray avatarByteArray = scriptedAvatar->toByteArray(true, randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO);

View file

@ -23,7 +23,7 @@ AssignmentAction::AssignmentAction(EntityActionType type, const QUuid& id, Entit
AssignmentAction::~AssignmentAction() {
}
void AssignmentAction::removeFromSimulation(EntitySimulation* simulation) const {
void AssignmentAction::removeFromSimulation(EntitySimulationPointer simulation) const {
withReadLock([&]{
simulation->removeAction(_id);
simulation->applyActionChanges();

View file

@ -24,7 +24,7 @@ public:
AssignmentAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity);
virtual ~AssignmentAction();
virtual void removeFromSimulation(EntitySimulation* simulation) const;
virtual void removeFromSimulation(EntitySimulationPointer simulation) const;
virtual EntityItemWeakPointer getOwnerEntity() const { return _ownerEntity; }
virtual void setOwnerEntity(const EntityItemPointer ownerEntity) { _ownerEntity = ownerEntity; }
virtual bool updateArguments(QVariantMap arguments);

View file

@ -56,7 +56,7 @@ OctreePointer EntityServer::createTree() {
tree->createRootElement();
tree->addNewlyCreatedHook(this);
if (!_entitySimulation) {
SimpleEntitySimulation* simpleSimulation = new SimpleEntitySimulation();
SimpleEntitySimulationPointer simpleSimulation { new SimpleEntitySimulation() };
simpleSimulation->setEntityTree(tree);
tree->setSimulation(simpleSimulation);
_entitySimulation = simpleSimulation;

View file

@ -28,6 +28,8 @@ struct ViewerSendingStats {
};
class SimpleEntitySimulation;
using SimpleEntitySimulationPointer = std::shared_ptr<SimpleEntitySimulation>;
class EntityServer : public OctreeServer, public NewlyCreatedEntityHook {
Q_OBJECT
@ -69,7 +71,7 @@ private slots:
void handleEntityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
private:
SimpleEntitySimulation* _entitySimulation;
SimpleEntitySimulationPointer _entitySimulation;
QTimer* _pruneDeletedEntitiesTimer = nullptr;
QReadWriteLock _viewerSendingStatsLock;

View file

@ -141,6 +141,16 @@ void OctreeQueryNode::writeToPacket(const unsigned char* buffer, unsigned int by
}
}
void OctreeQueryNode::copyCurrentViewFrustum(ViewFrustum& viewOut) const {
QMutexLocker viewLocker(&_viewMutex);
viewOut = _currentViewFrustum;
}
void OctreeQueryNode::copyLastKnownViewFrustum(ViewFrustum& viewOut) const {
QMutexLocker viewLocker(&_viewMutex);
viewOut = _lastKnownViewFrustum;
}
bool OctreeQueryNode::updateCurrentViewFrustum() {
// if shutting down, return immediately
if (_isShuttingDown) {
@ -171,11 +181,13 @@ bool OctreeQueryNode::updateCurrentViewFrustum() {
}
// if there has been a change, then recalculate
if (!newestViewFrustum.isVerySimilar(_currentViewFrustum)) {
_currentViewFrustum = newestViewFrustum;
_currentViewFrustum.calculate();
currentViewFrustumChanged = true;
{ // if there has been a change, then recalculate
QMutexLocker viewLocker(&_viewMutex);
if (!newestViewFrustum.isVerySimilar(_currentViewFrustum)) {
_currentViewFrustum = newestViewFrustum;
_currentViewFrustum.calculate();
currentViewFrustumChanged = true;
}
}
// Also check for LOD changes from the client
@ -219,11 +231,14 @@ void OctreeQueryNode::updateLastKnownViewFrustum() {
return;
}
bool frustumChanges = !_lastKnownViewFrustum.isVerySimilar(_currentViewFrustum);
{
QMutexLocker viewLocker(&_viewMutex);
bool frustumChanges = !_lastKnownViewFrustum.isVerySimilar(_currentViewFrustum);
if (frustumChanges) {
// save our currentViewFrustum into our lastKnownViewFrustum
_lastKnownViewFrustum = _currentViewFrustum;
if (frustumChanges) {
// save our currentViewFrustum into our lastKnownViewFrustum
_lastKnownViewFrustum = _currentViewFrustum;
}
}
// save that we know the view has been sent.
@ -237,15 +252,13 @@ bool OctreeQueryNode::moveShouldDump() const {
return false;
}
QMutexLocker viewLocker(&_viewMutex);
glm::vec3 oldPosition = _lastKnownViewFrustum.getPosition();
glm::vec3 newPosition = _currentViewFrustum.getPosition();
// theoretically we could make this slightly larger but relative to avatar scale.
const float MAXIMUM_MOVE_WITHOUT_DUMP = 0.0f;
if (glm::distance(newPosition, oldPosition) > MAXIMUM_MOVE_WITHOUT_DUMP) {
return true;
}
return false;
return glm::distance(newPosition, oldPosition) > MAXIMUM_MOVE_WITHOUT_DUMP;
}
void OctreeQueryNode::dumpOutOfView() {
@ -257,8 +270,10 @@ void OctreeQueryNode::dumpOutOfView() {
int stillInView = 0;
int outOfView = 0;
OctreeElementBag tempBag;
ViewFrustum viewCopy;
copyCurrentViewFrustum(viewCopy);
while (OctreeElementPointer elementToCheck = elementBag.extract()) {
if (elementToCheck->isInView(_currentViewFrustum)) {
if (elementToCheck->isInView(viewCopy)) {
tempBag.insert(elementToCheck);
stillInView++;
} else {
@ -267,7 +282,7 @@ void OctreeQueryNode::dumpOutOfView() {
}
if (stillInView > 0) {
while (OctreeElementPointer elementToKeepInBag = tempBag.extract()) {
if (elementToKeepInBag->isInView(_currentViewFrustum)) {
if (elementToKeepInBag->isInView(viewCopy)) {
elementBag.insert(elementToKeepInBag);
}
}

View file

@ -44,7 +44,7 @@ public:
bool packetIsDuplicate() const;
bool shouldSuppressDuplicatePacket();
unsigned int getAvailable() const { return _octreePacket->bytesAvailableForWrite(); }
int getMaxSearchLevel() const { return _maxSearchLevel; }
void resetMaxSearchLevel() { _maxSearchLevel = 1; }
@ -56,8 +56,8 @@ public:
OctreeElementBag elementBag;
OctreeElementExtraEncodeData extraEncodeData;
ViewFrustum& getCurrentViewFrustum() { return _currentViewFrustum; }
ViewFrustum& getLastKnownViewFrustum() { return _lastKnownViewFrustum; }
void copyCurrentViewFrustum(ViewFrustum& viewOut) const;
void copyLastKnownViewFrustum(ViewFrustum& viewOut) const;
// These are not classic setters because they are calculating and maintaining state
// which is set asynchronously through the network receive
@ -114,6 +114,8 @@ private:
int _maxSearchLevel { 1 };
int _maxLevelReachedInLastSearch { 1 };
mutable QMutex _viewMutex { QMutex::Recursive };
ViewFrustum _currentViewFrustum;
ViewFrustum _lastKnownViewFrustum;
quint64 _lastTimeBagEmpty { 0 };
@ -139,7 +141,7 @@ private:
QQueue<OCTREE_PACKET_SEQUENCE> _nackedSequenceNumbers;
quint64 _sceneSendStartTime = 0;
std::array<char, udt::MAX_PACKET_SIZE> _lastOctreePayload;
};

View file

@ -9,6 +9,9 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <chrono>
#include <thread>
#include <NodeList.h>
#include <NumericalConstants.h>
#include <udt/PacketHeaders.h>
@ -101,13 +104,16 @@ bool OctreeSendThread::process() {
int elapsed = (usecTimestampNow() - start);
int usecToSleep = OCTREE_SEND_INTERVAL_USECS - elapsed;
if (usecToSleep > 0) {
PerformanceWarning warn(false,"OctreeSendThread... usleep()",false,&_usleepTime,&_usleepCalls);
usleep(usecToSleep);
} else {
if (usecToSleep <= 0) {
const int MIN_USEC_TO_SLEEP = 1;
usleep(MIN_USEC_TO_SLEEP);
usecToSleep = MIN_USEC_TO_SLEEP;
}
{
PerformanceWarning warn(false,"OctreeSendThread... usleep()",false,&_usleepTime,&_usleepCalls);
std::this_thread::sleep_for(std::chrono::microseconds(usecToSleep));
}
}
return isStillRunning(); // keep running till they terminate us
@ -332,8 +338,6 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
_packetData.changeSettings(true, targetSize); // FIXME - eventually support only compressed packets
const ViewFrustum* lastViewFrustum = viewFrustumChanged ? &nodeData->getLastKnownViewFrustum() : NULL;
// If the current view frustum has changed OR we have nothing to send, then search against
// the current view frustum for things to send.
if (viewFrustumChanged || nodeData->elementBag.isEmpty()) {
@ -411,7 +415,7 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
quint64 lockWaitEnd = usecTimestampNow();
lockWaitElapsedUsec = (float)(lockWaitEnd - lockWaitStart);
quint64 encodeStart = usecTimestampNow();
OctreeElementPointer subTree = nodeData->elementBag.extract();
if (!subTree) {
return;
@ -420,18 +424,22 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
float octreeSizeScale = nodeData->getOctreeSizeScale();
int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust();
int boundaryLevelAdjust = boundaryLevelAdjustClient +
int boundaryLevelAdjust = boundaryLevelAdjustClient +
(viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(),
WANT_EXISTS_BITS, DONT_CHOP, viewFrustumChanged, lastViewFrustum,
EncodeBitstreamParams params(INT_MAX, WANT_EXISTS_BITS, DONT_CHOP,
viewFrustumChanged,
boundaryLevelAdjust, octreeSizeScale,
nodeData->getLastTimeBagEmpty(),
isFullScene, &nodeData->stats, _myServer->getJurisdiction(),
&nodeData->extraEncodeData);
nodeData->copyCurrentViewFrustum(params.viewFrustum);
if (viewFrustumChanged) {
nodeData->copyLastKnownViewFrustum(params.lastViewFrustum);
}
// Our trackSend() function is implemented by the server subclass, and will be called back
// during the encodeTreeBitstream() as new entities/data elements are sent
// during the encodeTreeBitstream() as new entities/data elements are sent
params.trackSend = [this, node](const QUuid& dataID, quint64 dataEdited) {
_myServer->trackSend(dataID, dataEdited, node->getUUID());
};

View file

@ -44,7 +44,7 @@ else ()
endif ()
find_package(Qt5 COMPONENTS
Gui Multimedia Network OpenGL Qml Quick Script Svg
Gui Multimedia Network OpenGL Qml Quick Script ScriptTools Svg
WebChannel WebEngine WebEngineWidgets WebKitWidgets WebSockets)
# grab the ui files in resources/ui
@ -201,7 +201,7 @@ include_directories("${PROJECT_SOURCE_DIR}/src")
target_link_libraries(
${TARGET_NAME}
Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL
Qt5::Qml Qt5::Quick Qt5::Script Qt5::Svg
Qt5::Qml Qt5::Quick Qt5::Script Qt5::ScriptTools Qt5::Svg
Qt5::WebChannel Qt5::WebEngine Qt5::WebEngineWidgets Qt5::WebKitWidgets
)

View file

@ -274,7 +274,7 @@
},
{
"id": "walkFwd",
"interpTarget": 15,
"interpTarget": 16,
"interpDuration": 6,
"transitions": [
{ "var": "isNotMoving", "state": "idle" },
@ -497,7 +497,7 @@
"data": {
"url": "animations/idle.fbx",
"startFrame": 0.0,
"endFrame": 90.0,
"endFrame": 300.0,
"timeScale": 1.0,
"loopFlag": true
},
@ -509,7 +509,7 @@
"data": {
"url": "animations/talk.fbx",
"startFrame": 0.0,
"endFrame": 801.0,
"endFrame": 800.0,
"timeScale": 1.0,
"loopFlag": true
},
@ -572,7 +572,7 @@
"data": {
"url": "animations/idle_to_walk.fbx",
"startFrame": 1.0,
"endFrame": 19.0,
"endFrame": 13.0,
"timeScale": 1.0,
"loopFlag": false
},
@ -631,11 +631,12 @@
"id": "turnRight",
"type": "clip",
"data": {
"url": "animations/turn_right.fbx",
"url": "animations/turn_left.fbx",
"startFrame": 0.0,
"endFrame": 30.0,
"timeScale": 1.0,
"loopFlag": true
"loopFlag": true,
"mirrorFlag": true
},
"children": []
},

View file

@ -1,19 +1,21 @@
{
"name": "Vive to Standard",
"channels": [
{ "from": "Vive.LY", "when": "Vive.LS", "filters": "invert", "to": "Standard.LY" },
{ "from": "Vive.LX", "when": "Vive.LS", "to": "Standard.LX" },
{ "from": "Vive.LY", "when": "Vive.LS", "filters": ["invert" ,{ "type": "deadZone", "min": 0.6 }], "to": "Standard.LY" },
{ "from": "Vive.LX", "when": "Vive.LS", "filters": [{ "type": "deadZone", "min": 0.6 }], "to": "Standard.LX" },
{ "from": "Vive.LT", "to": "Standard.LT" },
{ "from": "Vive.LB", "to": "Standard.LB" },
{ "from": "Vive.LeftGrip", "to": "Standard.LB" },
{ "from": "Vive.LS", "to": "Standard.LS" },
{ "from": "Vive.LSTouch", "to": "Standard.LSTouch" },
{ "from": "Vive.RY", "when": "Vive.RS", "filters": "invert", "to": "Standard.RY" },
{ "from": "Vive.RX", "when": "Vive.RS", "to": "Standard.RX" },
{ "from": "Vive.RY", "when": "Vive.RS", "filters": ["invert", { "type": "deadZone", "min": 0.6 }], "to": "Standard.RY" },
{ "from": "Vive.RX", "when": "Vive.RS", "filters": [{ "type": "deadZone", "min": 0.6 }], "to": "Standard.RX" },
{ "from": "Vive.RT", "to": "Standard.RT" },
{ "from": "Vive.RB", "to": "Standard.RB" },
{ "from": "Vive.RightGrip", "to": "Standard.RB" },
{ "from": "Vive.RS", "to": "Standard.RS" },
{ "from": "Vive.RSTouch", "to": "Standard.RSTouch" },
{ "from": "Vive.LeftApplicationMenu", "to": "Standard.Back" },
{ "from": "Vive.RightApplicationMenu", "to": "Standard.Start" },

View file

@ -1,31 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px" y="0px" width="1600px" height="100px" viewBox="0 0 1600 100" xml:space="preserve">
<path fill="#FFFFFF" d="M92.8,69.3c-0.8-1.5-1.9-2.7-3.1-3.8c4.4-11.4,3.7-24.6-2.6-35.8c-9.2-16.3-28.3-24.4-46.3-20l-3.1,0.7
l4.9,8.7l1.7-0.3c13.7-2.7,27.6,3.6,34.5,15.7c4.9,8.7,5.5,18.9,1.9,27.9c-2.1,0.1-4.3,0.6-6.2,1.8c-6.5,3.7-8.8,12-5.1,18.5
c3.7,6.5,12,8.8,18.5,5.1C94.2,84.1,96.5,75.8,92.8,69.3z"/>
<path fill="#FFFFFF" d="M54.2,82.6l-1.5,0.1c-12.3,0.8-24.2-5.6-30.2-16.3c-3.8-6.6-4.9-14.2-3.7-21.3c2.8,0.4,5.9-0.1,8.6-1.6
c6.5-3.7,8.8-12,5.1-18.5s-12-8.8-18.5-5.1C7.7,23.7,5.4,32,9,38.5c0.3,0.6,0.7,1.2,1.2,1.7c-2.6,10.3-1.4,21.5,4.1,31.1
c7.5,13.2,21.5,21.2,36.5,21.2c1.8,0,3.5-0.1,5.2-0.3l3.6-0.4L54.2,82.6z"/>
<path fill="#FFFFFF" d="M67.2,63.4H33.8c-1,0-2.1-0.5-2.6-1.5c-0.5-0.9-0.5-2.1,0-3L47.8,30c0.5-0.9,1.6-1.5,2.6-1.5
s2.1,0.5,2.6,1.5l16.7,28.9c0.5,0.9,0.5,2.1,0,3C69.3,62.9,68.3,63.4,67.2,63.4z M39,57.4h23L50.4,37.5L39,57.4z"/>
<polygon fill="#FFFFFF" points="175.4,30.6 149.9,8 123.9,30.7 139,30.7 139.2,59.6 161,59.3 160.8,30.7 "/>
<polygon fill="#FFFFFF" points="225.6,39.8 251.1,62.5 277.1,39.8 261.9,39.8 261.7,8.9 240,9.2 240.2,39.8 "/>
<path fill="#FFFFFF" d="M174.3,42.8c1.8,3.7,2.8,7.8,2.8,12.1c0,15.2-12.3,27.5-27.5,27.5c-15.2,0-27.5-12.3-27.5-27.5
c0-4.4,1-8.5,2.9-12.1h-7.9c-1.4,3.8-2.2,7.8-2.2,12.1c0,19.2,15.6,34.7,34.7,34.7c19.2,0,34.7-15.6,34.7-34.7
c0-4.3-0.8-8.3-2.2-12.1H174.3z"/>
<path fill="#FFFFFF" d="M278.8,53c0.1,0.7,0.1,1.5,0.1,2.2c0,15.2-12.4,27.6-27.6,27.6c-15.2,0-27.6-12.4-27.6-27.6
c0-1.1,0.1-2.1,0.2-3.1c-2.1-2.1-4.1-4.1-6.2-6.2c-0.8,3-1.3,6.1-1.3,9.3c0,19.2,15.6,34.9,34.9,34.9s34.9-15.6,34.9-34.9
c0-2.9-0.4-5.8-1.1-8.5L278.8,53z"/>
<circle fill="none" stroke="#000000" stroke-width="7" stroke-miterlimit="10" stroke-dasharray="7.7202,7.7202" cx="-174" cy="-5.8" r="14.7"/>
<path d="M-174-10.6c2.6,0,4.7,2.1,4.7,4.7s-2.1,4.7-4.7,4.7s-4.7-2.1-4.7-4.7S-176.6-10.6-174-10.6 M-174-17.6
c-6.5,0-11.7,5.3-11.7,11.7s5.3,11.7,11.7,11.7s11.7-5.3,11.7-11.7S-167.5-17.6-174-17.6L-174-17.6z"/>
<path fill="#FFFFFF" d="M353.3,91.2c-0.3,0-0.7,0-1,0c-1.8-0.2-3.5-0.4-5.3-0.7c-21.3-3.6-35.2-22.8-32-44.2
c2.7-18.2,17.7-31.4,36.8-32.5c17.2-0.9,33.8,11.4,38.2,28.5c0.8,3.1,1.1,6.3,1.6,9.5c0,0.3,0,0.7,0,1c-0.2,0.9-0.4,1.8-0.5,2.7
c-1.3,16.3-12.9,30.1-28.8,34C359.3,90.4,356.3,90.7,353.3,91.2z M353.7,83.9c8.3,0,16.1-3.4,22.6-9.9c2.2-2.2,2-3.1-0.7-4.5
c-3.9-1.9-7.8-3.7-11.7-5.6c-4-2-4.6-8.1-1.1-10.8c2-1.5,2.4-3.7,2.1-5.9c-0.2-1.8-1-3.5-1.2-5.3c-0.6-6-5.2-10.2-11.1-10.1
c-5.9,0.1-10.4,4.8-10.6,10.9c-0.1,1.4-0.4,2.8-0.9,4.1c-0.6,1.9,0.1,4.9,1.7,6.3c3.8,3.1,3.1,9-1.4,11.2c-3.6,1.7-7.2,3.4-10.8,5.2
c-3.4,1.6-3.6,2.5-0.8,5.1C336.2,80.6,343.8,83.9,353.7,83.9z"/>
<polygon fill="#FFFFFF" points="445.3,14.1 484.6,14.1 461.5,38.2 485.6,41.4 422.2,86.9 441.2,53.4 425.6,49.3 "/>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1600 100" style="enable-background:new 0 0 1600 100;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M92.8,69.3c-0.8-1.5-1.9-2.7-3.1-3.8c4.4-11.4,3.7-24.6-2.6-35.8c-9.2-16.3-28.3-24.4-46.3-20l-3.1,0.7l4.9,8.7
l1.7-0.3c13.7-2.7,27.6,3.6,34.5,15.7c4.9,8.7,5.5,18.9,1.9,27.9c-2.1,0.1-4.3,0.6-6.2,1.8c-6.5,3.7-8.8,12-5.1,18.5
c3.7,6.5,12,8.8,18.5,5.1C94.2,84.1,96.5,75.8,92.8,69.3z"/>
<path class="st0" d="M54.2,82.6l-1.5,0.1c-12.3,0.8-24.2-5.6-30.2-16.3c-3.8-6.6-4.9-14.2-3.7-21.3c2.8,0.4,5.9-0.1,8.6-1.6
c6.5-3.7,8.8-12,5.1-18.5s-12-8.8-18.5-5.1C7.7,23.7,5.4,32,9,38.5c0.3,0.6,0.7,1.2,1.2,1.7c-2.6,10.3-1.4,21.5,4.1,31.1
c7.5,13.2,21.5,21.2,36.5,21.2c1.8,0,3.5-0.1,5.2-0.3l3.6-0.4L54.2,82.6z"/>
<path class="st0" d="M67.2,63.4H33.8c-1,0-2.1-0.5-2.6-1.5c-0.5-0.9-0.5-2.1,0-3L47.8,30c0.5-0.9,1.6-1.5,2.6-1.5S52.5,29,53,30
l16.7,28.9c0.5,0.9,0.5,2.1,0,3C69.3,62.9,68.3,63.4,67.2,63.4z M39,57.4h23L50.4,37.5L39,57.4z"/>
<polygon class="st0" points="175.4,30.6 149.9,8 123.9,30.7 139,30.7 139.2,59.6 161,59.3 160.8,30.7 "/>
<polygon class="st0" points="225.6,39.8 251.1,62.5 277.1,39.8 261.9,39.8 261.7,8.9 240,9.2 240.2,39.8 "/>
<path class="st0" d="M174.3,42.8c1.8,3.7,2.8,7.8,2.8,12.1c0,15.2-12.3,27.5-27.5,27.5s-27.5-12.3-27.5-27.5c0-4.4,1-8.5,2.9-12.1
h-7.9c-1.4,3.8-2.2,7.8-2.2,12.1c0,19.2,15.6,34.7,34.7,34.7c19.2,0,34.7-15.6,34.7-34.7c0-4.3-0.8-8.3-2.2-12.1
C182.1,42.8,174.3,42.8,174.3,42.8z"/>
<path class="st0" d="M278.8,53c0.1,0.7,0.1,1.5,0.1,2.2c0,15.2-12.4,27.6-27.6,27.6c-15.2,0-27.6-12.4-27.6-27.6
c0-1.1,0.1-2.1,0.2-3.1c-2.1-2.1-4.1-4.1-6.2-6.2c-0.8,3-1.3,6.1-1.3,9.3c0,19.2,15.6,34.9,34.9,34.9s34.9-15.6,34.9-34.9
c0-2.9-0.4-5.8-1.1-8.5L278.8,53z"/>
<path class="st0" d="M353.3,91.2c-0.3,0-0.7,0-1,0c-1.8-0.2-3.5-0.4-5.3-0.7c-21.3-3.6-35.2-22.8-32-44.2
c2.7-18.2,17.7-31.4,36.8-32.5c17.2-0.9,33.8,11.4,38.2,28.5c0.8,3.1,1.1,6.3,1.6,9.5c0,0.3,0,0.7,0,1c-0.2,0.9-0.4,1.8-0.5,2.7
c-1.3,16.3-12.9,30.1-28.8,34C359.3,90.4,356.3,90.7,353.3,91.2z M353.7,83.9c8.3,0,16.1-3.4,22.6-9.9c2.2-2.2,2-3.1-0.7-4.5
c-3.9-1.9-7.8-3.7-11.7-5.6c-4-2-4.6-8.1-1.1-10.8c2-1.5,2.4-3.7,2.1-5.9c-0.2-1.8-1-3.5-1.2-5.3c-0.6-6-5.2-10.2-11.1-10.1
s-10.4,4.8-10.6,10.9c-0.1,1.4-0.4,2.8-0.9,4.1c-0.6,1.9,0.1,4.9,1.7,6.3c3.8,3.1,3.1,9-1.4,11.2c-3.6,1.7-7.2,3.4-10.8,5.2
c-3.4,1.6-3.6,2.5-0.8,5.1C336.2,80.6,343.8,83.9,353.7,83.9z"/>
<polygon class="st0" points="445.3,14.1 484.6,14.1 461.5,38.2 485.6,41.4 422.2,86.9 441.2,53.4 425.6,49.3 "/>
<path class="st0" d="M564.7,53.2l0.1-8.7l-22.9-0.2c0.1-0.5,0.2-1.1,0.2-1.6c0.2-6.1,4.7-10.8,10.6-10.9c5.9-0.1,10.5,4.1,11.1,10.1
c0.1,0.9,0.3,1.7,0.6,2.6l26.7,0.2c-3.3-17.9-19-31.4-37.9-31.4c-18.6,0-34.2,13.2-37.8,30.8l26.4,0.2l0,0l-0.9,0l-0.1,8.7
l-26.2-0.2c0.5,20.8,17.5,37.6,38.5,37.6c20.7,0,37.6-16.4,38.5-36.9L564.7,53.2z M576.3,74c-6.5,6.5-14.3,9.9-22.6,9.9
c-9.9,0-17.5-3.3-23.9-9.3c-2.8-2.6-2.6-3.5,0.8-5.1c3.6-1.8,7.2-3.5,10.8-5.2c4.5-2.2,5.2-8.1,1.4-11.2c0,0-0.1-0.1-0.1-0.1l20,0.2
c-3.4,2.7-2.8,8.7,1.2,10.7c3.9,1.9,7.8,3.7,11.7,5.6C578.3,70.9,578.5,71.8,576.3,74z"/>
<g>
<path class="st0" d="M638.5,48.2c-1.2-1-2.3-2.2-3.2-3.5h-16.7v9.2h29.1c-1.4-0.6-2.6-1.3-4-2.2C641.6,50.4,640.3,49.7,638.5,48.2z
"/>
<path class="st0" d="M684.9,44.7h-31.3c1,0.5,2.1,1,3.2,1.5c2.2,1,3.5,1.8,5.6,3.1c2.1,1.3,4.2,2.3,6,3.8c0.4,0.3,0.7,0.6,1,0.9
h15.5V44.7z"/>
<path class="st0" d="M651.5,44.2c-1.4-0.7-2.6-1.4-3.6-2.2c-1.1-0.8-1.9-1.7-2.5-2.8c-0.6-1-0.9-2.2-0.9-3.7c0-2.1,0.7-3.8,2-5.1
c1.3-1.3,3.2-2,5.7-2c1.1,0,2.2,0.1,3.2,0.4c1,0.3,1.9,0.8,2.7,1.4c0.8,0.7,1.5,1.6,2,2.7c0.5,1.1,0.9,2.5,1.1,4.2h7.4
c0.2,0,0.5,0,0.9,0c0.4,0,0.9,0,1.3,0c0.5,0,0.9,0,1.3,0c0.4,0,0.7,0,0.9,0c-0.1-3.3-0.7-6.1-1.8-8.5s-2.5-4.4-4.4-5.9
c-1.9-1.5-4-2.7-6.5-3.4c-2.5-0.7-5.1-1.1-8-1.1c-2.6,0-5,0.4-7.4,1.1c-2.4,0.7-4.4,1.8-6.3,3.2c-1.8,1.4-3.3,3.2-4.3,5.3
c-1.1,2.1-1.6,4.6-1.6,7.6c0,3,0.6,5.5,1.7,7.5c0.3,0.6,0.7,1.2,1.1,1.8h17.3C652.2,44.5,651.9,44.3,651.5,44.2z"/>
<path class="st0" d="M651.1,55c1.5,0.7,2.9,1.3,4.2,1.9s2.6,1.3,3.6,2.1c1,0.8,1.9,1.7,2.5,2.8s0.9,2.4,0.9,4c0,1.7-0.3,3.2-1,4.3
c-0.7,1.1-1.5,2-2.5,2.7c-1,0.7-2.1,1.1-3.3,1.4c-1.2,0.3-2.4,0.4-3.5,0.4c-1.3,0-2.5-0.2-3.7-0.7c-1.1-0.4-2.1-1.2-2.9-2.1
c-0.8-1-1.5-2.3-2-3.8c-0.5-1.6-0.8-3.5-0.8-5.7h-12.1c0.1,3.7,0.8,7,1.9,9.8c1.2,2.8,2.7,5.2,4.7,7c1.9,1.9,4.2,3.3,6.7,4.2
s5.3,1.4,8.1,1.4c2.9,0,5.8-0.4,8.4-1.1c2.7-0.7,5-1.9,7.1-3.5c2-1.6,3.7-3.6,4.9-6.1s1.8-5.5,1.8-9.1c0-2.6-0.5-4.9-1.6-6.8
c-0.9-1.5-2-2.9-3.3-4.1h-20.7C649.5,54.3,650.3,54.6,651.1,55z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View file

@ -45,11 +45,13 @@
#include <ResourceScriptingInterface.h>
#include <AccountManager.h>
#include <AddressManager.h>
#include <AnimDebugDraw.h>
#include <BuildInfo.h>
#include <AssetClient.h>
#include <AutoUpdater.h>
#include <AudioInjectorManager.h>
#include <CursorManager.h>
#include <DebugDraw.h>
#include <DeferredLightingEffect.h>
#include <display-plugins/DisplayPlugin.h>
#include <EntityScriptingInterface.h>
@ -101,7 +103,7 @@
#include <Preferences.h>
#include <display-plugins/CompositorHelper.h>
#include "AnimDebugDraw.h"
#include "AudioClient.h"
#include "audio/AudioScope.h"
#include "avatar/AvatarManager.h"
@ -210,9 +212,7 @@ const QHash<QString, Application::AcceptURLMethod> Application::_acceptedExtensi
class DeadlockWatchdogThread : public QThread {
public:
static const unsigned long HEARTBEAT_CHECK_INTERVAL_SECS = 1;
static const unsigned long HEARTBEAT_UPDATE_INTERVAL_SECS = 1;
static const unsigned long HEARTBEAT_REPORT_INTERVAL_USECS = 5 * USECS_PER_SECOND;
static const unsigned long MAX_HEARTBEAT_AGE_USECS = 30 * USECS_PER_SECOND;
static const int WARNING_ELAPSED_HEARTBEAT = 500 * USECS_PER_MSEC; // warn if elapsed heartbeat average is large
static const int HEARTBEAT_SAMPLES = 100000; // ~5 seconds worth of samples
@ -239,8 +239,6 @@ public:
*crashTrigger = 0xDEAD10CC;
}
static void setSuppressStatus(bool suppress) { _suppressStatus = suppress; }
void run() override {
while (!_quit) {
QThread::sleep(HEARTBEAT_UPDATE_INTERVAL_SECS);
@ -248,7 +246,6 @@ public:
uint64_t lastHeartbeat = _heartbeat; // sample atomic _heartbeat, because we could context switch away and have it updated on us
uint64_t now = usecTimestampNow();
auto lastHeartbeatAge = (now > lastHeartbeat) ? now - lastHeartbeat : 0;
auto sinceLastReport = (now > _lastReport) ? now - _lastReport : 0;
auto elapsedMovingAverage = _movingAverage.getAverage();
if (elapsedMovingAverage > _maxElapsedAverage) {
@ -274,21 +271,10 @@ public:
if (elapsedMovingAverage > WARNING_ELAPSED_HEARTBEAT) {
qDebug() << "DEADLOCK WATCHDOG WARNING:"
<< "lastHeartbeatAge:" << lastHeartbeatAge
<< "elapsedMovingAverage:" << elapsedMovingAverage << "** OVER EXPECTED VALUE**"
<< "elapsedMovingAverage:" << elapsedMovingAverage << "** OVER EXPECTED VALUE **"
<< "maxElapsed:" << _maxElapsed
<< "maxElapsedAverage:" << _maxElapsedAverage
<< "samples:" << _movingAverage.getSamples();
_lastReport = now;
}
if (!_suppressStatus && sinceLastReport > HEARTBEAT_REPORT_INTERVAL_USECS) {
qDebug() << "DEADLOCK WATCHDOG STATUS:"
<< "lastHeartbeatAge:" << lastHeartbeatAge
<< "elapsedMovingAverage:" << elapsedMovingAverage
<< "maxElapsed:" << _maxElapsed
<< "maxElapsedAverage:" << _maxElapsedAverage
<< "samples:" << _movingAverage.getSamples();
_lastReport = now;
}
if (lastHeartbeatAge > MAX_HEARTBEAT_AGE_USECS) {
@ -310,9 +296,7 @@ public:
}
}
static std::atomic<bool> _suppressStatus;
static std::atomic<uint64_t> _heartbeat;
static std::atomic<uint64_t> _lastReport;
static std::atomic<uint64_t> _maxElapsed;
static std::atomic<int> _maxElapsedAverage;
static ThreadSafeMovingAverage<int, HEARTBEAT_SAMPLES> _movingAverage;
@ -320,17 +304,11 @@ public:
bool _quit { false };
};
std::atomic<bool> DeadlockWatchdogThread::_suppressStatus;
std::atomic<uint64_t> DeadlockWatchdogThread::_heartbeat;
std::atomic<uint64_t> DeadlockWatchdogThread::_lastReport;
std::atomic<uint64_t> DeadlockWatchdogThread::_maxElapsed;
std::atomic<int> DeadlockWatchdogThread::_maxElapsedAverage;
ThreadSafeMovingAverage<int, DeadlockWatchdogThread::HEARTBEAT_SAMPLES> DeadlockWatchdogThread::_movingAverage;
void Application::toggleSuppressDeadlockWatchdogStatus(bool checked) {
DeadlockWatchdogThread::setSuppressStatus(checked);
}
#ifdef Q_OS_WIN
class MyNativeEventFilter : public QAbstractNativeEventFilter {
public:
@ -509,6 +487,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
_sessionRunTimer(startupTimer),
_previousSessionCrashed(setupEssentials(argc, argv)),
_undoStackScriptingInterface(&_undoStack),
_entitySimulation(new PhysicalEntitySimulation()),
_physicsEngine(new PhysicsEngine(Vectors::ZERO)),
_entityClipboardRenderer(false, this, this),
_entityClipboard(new EntityTree()),
@ -584,12 +563,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
// put the NodeList and datagram processing on the node thread
nodeList->moveToThread(nodeThread);
// Model background downloads need to happen on the Datagram Processor Thread. The idle loop will
// emit checkBackgroundDownloads to cause the ModelCache to check it's queue for requested background
// downloads.
auto modelCache = DependencyManager::get<ModelCache>();
connect(this, &Application::checkBackgroundDownloads, modelCache.data(), &ModelCache::checkAsynchronousGets);
// put the audio processing on a separate thread
QThread* audioThread = new QThread();
audioThread->setObjectName("Audio Thread");
@ -702,9 +675,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
// The value will be 0 if the user blew away settings this session, which is both a feature and a bug.
UserActivityLogger::getInstance().launch(applicationVersion(), _previousSessionCrashed, sessionRunTime.get());
// once the event loop has started, check and signal for an access token
QMetaObject::invokeMethod(&accountManager, "checkAndSignalForAccessToken", Qt::QueuedConnection);
auto addressManager = DependencyManager::get<AddressManager>();
// use our MyAvatar position and quat for address manager path
@ -1118,6 +1088,11 @@ void Application::checkChangeCursor() {
_cursorNeedsChanging = false;
}
// After all of the constructor is completed, then set firstRun to false.
Setting::Handle<bool> firstRun{ Settings::firstRun, true };
firstRun.set(false);
}
void Application::showCursor(const QCursor& cursor) {
@ -1327,8 +1302,6 @@ void Application::initializeGL() {
// update before the first render
update(0);
InfoView::show(INFO_HELP_PATH, true);
}
FrameTimingsScriptingInterface _frameTimingsScriptingInterface;
@ -1505,10 +1478,17 @@ void Application::paintGL() {
auto lodManager = DependencyManager::get<LODManager>();
RenderArgs renderArgs(_gpuContext, getEntities(), getViewFrustum(), lodManager->getOctreeSizeScale(),
{
QMutexLocker viewLocker(&_viewMutex);
_viewFrustum.calculate();
}
RenderArgs renderArgs(_gpuContext, getEntities(), lodManager->getOctreeSizeScale(),
lodManager->getBoundaryLevelAdjust(), RenderArgs::DEFAULT_RENDER_MODE,
RenderArgs::MONO, RenderArgs::RENDER_DEBUG_NONE);
{
QMutexLocker viewLocker(&_viewMutex);
renderArgs.setViewFrustum(_viewFrustum);
}
PerformanceWarning::setSuppressShortTimings(Menu::getInstance()->isOptionChecked(MenuOption::SuppressShortTimings));
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
@ -1675,7 +1655,7 @@ void Application::paintGL() {
renderArgs._context->enableStereo(true);
mat4 eyeOffsets[2];
mat4 eyeProjections[2];
auto baseProjection = renderArgs._viewFrustum->getProjection();
auto baseProjection = renderArgs.getViewFrustum().getProjection();
auto hmdInterface = DependencyManager::get<HMDScriptingInterface>();
float IPDScale = hmdInterface->getIPDScale();
mat4 headPose = displayPlugin->getHeadPose();
@ -1805,7 +1785,10 @@ void Application::resizeGL() {
_myCamera.setProjection(glm::perspective(glm::radians(_fieldOfView.get()), aspectRatio,
DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP));
// Possible change in aspect ratio
loadViewFrustum(_myCamera, _viewFrustum);
{
QMutexLocker viewLocker(&_viewMutex);
loadViewFrustum(_myCamera, _viewFrustum);
}
auto offscreenUi = DependencyManager::get<OffscreenUi>();
auto uiSize = displayPlugin->getRecommendedUiSize();
@ -2132,6 +2115,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
case Qt::Key_J:
if (isShifted) {
QMutexLocker viewLocker(&_viewMutex);
_viewFrustum.setFocalLength(_viewFrustum.getFocalLength() - 0.1f);
} else {
_myCamera.setEyeOffsetPosition(_myCamera.getEyeOffsetPosition() + glm::vec3(-0.001, 0, 0));
@ -2141,6 +2125,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
case Qt::Key_M:
if (isShifted) {
QMutexLocker viewLocker(&_viewMutex);
_viewFrustum.setFocalLength(_viewFrustum.getFocalLength() + 0.1f);
} else {
_myCamera.setEyeOffsetPosition(_myCamera.getEyeOffsetPosition() + glm::vec3(0.001, 0, 0));
@ -2746,9 +2731,6 @@ void Application::idle(uint64_t now) {
}
_overlayConductor.update(secondsSinceLastUpdate);
// check for any requested background downloads.
emit checkBackgroundDownloads();
}
void Application::setLowVelocityFilter(bool lowVelocityFilter) {
@ -2979,7 +2961,21 @@ void Application::init() {
addressLookupString = arguments().value(urlIndex + 1);
}
DependencyManager::get<AddressManager>()->loadSettings(addressLookupString);
Setting::Handle<bool> firstRun { Settings::firstRun, true };
if (addressLookupString.isEmpty() && firstRun.get()) {
qDebug() << "First run and no URL passed... attempting to go to Home or Entry...";
DependencyManager::get<AddressManager>()->ifLocalSandboxRunningElse([](){
qDebug() << "Home sandbox appears to be running, going to Home.";
DependencyManager::get<AddressManager>()->goToLocalSandbox();
},
[](){
qDebug() << "Home sandbox does not appear to be running, going to Entry.";
DependencyManager::get<AddressManager>()->goToEntry();
});
} else {
qDebug() << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString);
DependencyManager::get<AddressManager>()->loadSettings(addressLookupString);
}
qCDebug(interfaceapp) << "Loaded settings";
@ -2989,19 +2985,22 @@ void Application::init() {
DependencyManager::get<NodeList>()->sendDomainServerCheckIn();
getEntities()->init();
getEntities()->setViewFrustum(getViewFrustum());
{
QMutexLocker viewLocker(&_viewMutex);
getEntities()->setViewFrustum(_viewFrustum);
}
ObjectMotionState::setShapeManager(&_shapeManager);
_physicsEngine->init();
EntityTreePointer tree = getEntities()->getTree();
_entitySimulation.init(tree, _physicsEngine, &_entityEditSender);
tree->setSimulation(&_entitySimulation);
_entitySimulation->init(tree, _physicsEngine, &_entityEditSender);
tree->setSimulation(_entitySimulation);
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
// connect the _entityCollisionSystem to our EntityTreeRenderer since that's what handles running entity scripts
connect(&_entitySimulation, &EntitySimulation::entityCollisionWithEntity,
connect(_entitySimulation.get(), &EntitySimulation::entityCollisionWithEntity,
getEntities(), &EntityTreeRenderer::entityCollisionWithEntity);
// connect the _entities (EntityTreeRenderer) to our script engine's EntityScriptingInterface for firing
@ -3009,12 +3008,26 @@ void Application::init() {
getEntities()->connectSignalsToSlots(entityScriptingInterface.data());
_entityClipboardRenderer.init();
_entityClipboardRenderer.setViewFrustum(getViewFrustum());
{
QMutexLocker viewLocker(&_viewMutex);
_entityClipboardRenderer.setViewFrustum(_viewFrustum);
}
_entityClipboardRenderer.setTree(_entityClipboard);
// Make sure any new sounds are loaded as soon as know about them.
connect(tree.get(), &EntityTree::newCollisionSoundURL, DependencyManager::get<SoundCache>().data(), &SoundCache::getSound);
connect(getMyAvatar(), &MyAvatar::newCollisionSoundURL, DependencyManager::get<SoundCache>().data(), &SoundCache::getSound);
connect(tree.get(), &EntityTree::newCollisionSoundURL, this, [this](QUrl newURL, EntityItemID id) {
EntityTreePointer tree = getEntities()->getTree();
if (auto entity = tree->findEntityByEntityItemID(id)) {
auto sound = DependencyManager::get<SoundCache>()->getSound(newURL);
entity->setCollisionSound(sound);
}
}, Qt::QueuedConnection);
connect(getMyAvatar(), &MyAvatar::newCollisionSoundURL, this, [this](QUrl newURL) {
if (auto avatar = getMyAvatar()) {
auto sound = DependencyManager::get<SoundCache>()->getSound(newURL);
avatar->setCollisionSound(sound);
}
}, Qt::QueuedConnection);
}
void Application::updateLOD() const {
@ -3027,9 +3040,9 @@ void Application::updateLOD() const {
}
}
void Application::pushPreRenderLambda(void* key, std::function<void()> func) {
std::unique_lock<std::mutex> guard(_preRenderLambdasLock);
_preRenderLambdas[key] = func;
void Application::pushPostUpdateLambda(void* key, std::function<void()> func) {
std::unique_lock<std::mutex> guard(_postUpdateLambdasLock);
_postUpdateLambdas[key] = func;
}
// Called during Application::update immediately before AvatarManager::updateMyAvatar, updating my data that is then sent to everyone.
@ -3212,9 +3225,12 @@ void Application::resetPhysicsReadyInformation() {
void Application::reloadResourceCaches() {
resetPhysicsReadyInformation();
{
QMutexLocker viewLocker(&_viewMutex);
_viewFrustum.setPosition(glm::vec3(0.0f, 0.0f, TREE_SCALE));
_viewFrustum.setOrientation(glm::quat());
}
// Clear entities out of view frustum
_viewFrustum.setPosition(glm::vec3(0.0f, 0.0f, TREE_SCALE));
_viewFrustum.setOrientation(glm::quat());
queryOctree(NodeType::EntityServer, PacketType::EntityQuery, _entityServerJurisdictions);
DependencyManager::get<AssetClient>()->clearCache();
@ -3404,22 +3420,22 @@ void Application::update(float deltaTime) {
PerformanceTimer perfTimer("updateStates)");
static VectorOfMotionStates motionStates;
_entitySimulation.getObjectsToRemoveFromPhysics(motionStates);
_entitySimulation->getObjectsToRemoveFromPhysics(motionStates);
_physicsEngine->removeObjects(motionStates);
_entitySimulation.deleteObjectsRemovedFromPhysics();
_entitySimulation->deleteObjectsRemovedFromPhysics();
getEntities()->getTree()->withReadLock([&] {
_entitySimulation.getObjectsToAddToPhysics(motionStates);
_entitySimulation->getObjectsToAddToPhysics(motionStates);
_physicsEngine->addObjects(motionStates);
});
getEntities()->getTree()->withReadLock([&] {
_entitySimulation.getObjectsToChange(motionStates);
_entitySimulation->getObjectsToChange(motionStates);
VectorOfMotionStates stillNeedChange = _physicsEngine->changeObjects(motionStates);
_entitySimulation.setObjectsToChange(stillNeedChange);
_entitySimulation->setObjectsToChange(stillNeedChange);
});
_entitySimulation.applyActionChanges();
_entitySimulation->applyActionChanges();
avatarManager->getObjectsToRemoveFromPhysics(motionStates);
_physicsEngine->removeObjects(motionStates);
@ -3447,7 +3463,7 @@ void Application::update(float deltaTime) {
getEntities()->getTree()->withWriteLock([&] {
PerformanceTimer perfTimer("handleOutgoingChanges");
const VectorOfMotionStates& outgoingChanges = _physicsEngine->getOutgoingChanges();
_entitySimulation.handleOutgoingChanges(outgoingChanges);
_entitySimulation->handleOutgoingChanges(outgoingChanges);
avatarManager->handleOutgoingChanges(outgoingChanges);
});
@ -3460,7 +3476,7 @@ void Application::update(float deltaTime) {
PerformanceTimer perfTimer("entities");
// Collision events (and their scripts) must not be handled when we're locked, above. (That would risk
// deadlock.)
_entitySimulation.handleCollisionEvents(collisionEvents);
_entitySimulation->handleCollisionEvents(collisionEvents);
// NOTE: the getEntities()->update() call below will wait for lock
// and will simulate entity motion (the EntityTree has been given an EntitySimulation).
@ -3503,7 +3519,7 @@ void Application::update(float deltaTime) {
// actually need to calculate the view frustum planes to send these details
// to the server.
{
PerformanceTimer perfTimer("loadViewFrustum");
QMutexLocker viewLocker(&_viewMutex);
loadViewFrustum(_myCamera, _viewFrustum);
}
@ -3512,6 +3528,7 @@ void Application::update(float deltaTime) {
// Update my voxel servers with my current voxel query...
{
PROFILE_RANGE_EX("QueryOctree", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
QMutexLocker viewLocker(&_viewMutex);
PerformanceTimer perfTimer("queryOctree");
quint64 sinceLastQuery = now - _lastQueriedTime;
const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND;
@ -3547,15 +3564,19 @@ void Application::update(float deltaTime) {
}
}
avatarManager->postUpdate(deltaTime);
{
PROFILE_RANGE_EX("PreRenderLambdas", 0xffff0000, (uint64_t)0);
std::unique_lock<std::mutex> guard(_preRenderLambdasLock);
for (auto& iter : _preRenderLambdas) {
std::unique_lock<std::mutex> guard(_postUpdateLambdasLock);
for (auto& iter : _postUpdateLambdas) {
iter.second();
}
_preRenderLambdas.clear();
_postUpdateLambdas.clear();
}
AnimDebugDraw::getInstance().update();
}
@ -3623,14 +3644,16 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
//qCDebug(interfaceapp) << ">>> inside... queryOctree()... _viewFrustum.getFieldOfView()=" << _viewFrustum.getFieldOfView();
bool wantExtraDebugging = getLogger()->extraDebugging();
_octreeQuery.setCameraPosition(_viewFrustum.getPosition());
_octreeQuery.setCameraOrientation(_viewFrustum.getOrientation());
_octreeQuery.setCameraFov(_viewFrustum.getFieldOfView());
_octreeQuery.setCameraAspectRatio(_viewFrustum.getAspectRatio());
_octreeQuery.setCameraNearClip(_viewFrustum.getNearClip());
_octreeQuery.setCameraFarClip(_viewFrustum.getFarClip());
ViewFrustum viewFrustum;
copyViewFrustum(viewFrustum);
_octreeQuery.setCameraPosition(viewFrustum.getPosition());
_octreeQuery.setCameraOrientation(viewFrustum.getOrientation());
_octreeQuery.setCameraFov(viewFrustum.getFieldOfView());
_octreeQuery.setCameraAspectRatio(viewFrustum.getAspectRatio());
_octreeQuery.setCameraNearClip(viewFrustum.getNearClip());
_octreeQuery.setCameraFarClip(viewFrustum.getFarClip());
_octreeQuery.setCameraEyeOffsetPosition(glm::vec3());
_octreeQuery.setCameraCenterRadius(_viewFrustum.getCenterRadius());
_octreeQuery.setCameraCenterRadius(viewFrustum.getCenterRadius());
auto lodManager = DependencyManager::get<LODManager>();
_octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale());
_octreeQuery.setBoundaryLevelAdjust(lodManager->getBoundaryLevelAdjust());
@ -3666,7 +3689,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
rootDetails.y * TREE_SCALE,
rootDetails.z * TREE_SCALE) - glm::vec3(HALF_TREE_SCALE),
rootDetails.s * TREE_SCALE);
if (_viewFrustum.cubeIntersectsKeyhole(serverBounds)) {
if (viewFrustum.cubeIntersectsKeyhole(serverBounds)) {
inViewServers++;
}
}
@ -3732,11 +3755,9 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
rootDetails.s * TREE_SCALE);
inView = _viewFrustum.cubeIntersectsKeyhole(serverBounds);
} else {
if (wantExtraDebugging) {
qCDebug(interfaceapp) << "Jurisdiction without RootCode for node " << *node << ". That's unusual!";
}
inView = viewFrustum.cubeIntersectsKeyhole(serverBounds);
} else if (wantExtraDebugging) {
qCDebug(interfaceapp) << "Jurisdiction without RootCode for node " << *node << ". That's unusual!";
}
}
@ -3827,6 +3848,7 @@ QRect Application::getDesirableApplicationGeometry() const {
// or the "myCamera".
//
void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) {
PerformanceTimer perfTimer("loadViewFrustum");
PROFILE_RANGE(__FUNCTION__);
// We will use these below, from either the camera or head vectors calculated above
viewFrustum.setProjection(camera.getProjection());
@ -3855,7 +3877,8 @@ PickRay Application::computePickRay(float x, float y) const {
getApplicationCompositor().computeHmdPickRay(pickPoint, result.origin, result.direction);
} else {
pickPoint /= getCanvasSize();
getViewFrustum()->computePickRay(pickPoint.x, pickPoint.y, result.origin, result.direction);
QMutexLocker viewLocker(&_viewMutex);
_viewFrustum.computePickRay(pickPoint.x, pickPoint.y, result.origin, result.direction);
}
return result;
}
@ -3868,44 +3891,19 @@ glm::vec3 Application::getAvatarPosition() const {
return getMyAvatar()->getPosition();
}
ViewFrustum* Application::getViewFrustum() {
#ifdef DEBUG
if (QThread::currentThread() == activeRenderingThread) {
// FIXME, figure out a better way to do this
//qWarning() << "Calling Application::getViewFrustum() from the active rendering thread, did you mean Application::getDisplayViewFrustum()?";
}
#endif
return &_viewFrustum;
void Application::copyViewFrustum(ViewFrustum& viewOut) const {
QMutexLocker viewLocker(&_viewMutex);
viewOut = _viewFrustum;
}
const ViewFrustum* Application::getViewFrustum() const {
#ifdef DEBUG
if (QThread::currentThread() == activeRenderingThread) {
// FIXME, figure out a better way to do this
//qWarning() << "Calling Application::getViewFrustum() from the active rendering thread, did you mean Application::getDisplayViewFrustum()?";
}
#endif
return &_viewFrustum;
void Application::copyDisplayViewFrustum(ViewFrustum& viewOut) const {
QMutexLocker viewLocker(&_viewMutex);
viewOut = _displayViewFrustum;
}
ViewFrustum* Application::getDisplayViewFrustum() {
#ifdef DEBUG
if (QThread::currentThread() != activeRenderingThread) {
// FIXME, figure out a better way to do this
// qWarning() << "Calling Application::getDisplayViewFrustum() from outside the active rendering thread or outside rendering, did you mean Application::getViewFrustum()?";
}
#endif
return &_displayViewFrustum;
}
const ViewFrustum* Application::getDisplayViewFrustum() const {
#ifdef DEBUG
if (QThread::currentThread() != activeRenderingThread) {
// FIXME, figure out a better way to do this
// qWarning() << "Calling Application::getDisplayViewFrustum() from outside the active rendering thread or outside rendering, did you mean Application::getViewFrustum()?";
}
#endif
return &_displayViewFrustum;
void Application::copyShadowViewFrustum(ViewFrustum& viewOut) const {
QMutexLocker viewLocker(&_viewMutex);
viewOut = _shadowViewFrustum;
}
// WorldBox Render Data & rendering functions
@ -3970,7 +3968,7 @@ namespace render {
auto skybox = skyStage->getSkybox();
if (skybox) {
PerformanceTimer perfTimer("skybox");
skybox->render(batch, *(args->_viewFrustum));
skybox->render(batch, args->getViewFrustum());
break;
}
}
@ -4000,13 +3998,10 @@ namespace render {
void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool selfAvatarOnly) {
// FIXME: This preRender call is temporary until we create a separate render::scene for the mirror rendering.
// FIXME: This preDisplayRender call is temporary until we create a separate render::scene for the mirror rendering.
// Then we can move this logic into the Avatar::simulate call.
auto myAvatar = getMyAvatar();
myAvatar->preRender(renderArgs);
// Update animation debug draw renderer
AnimDebugDraw::getInstance().update();
myAvatar->preDisplaySide(renderArgs);
activeRenderingThread = QThread::currentThread();
PROFILE_RANGE(__FUNCTION__);
@ -4014,7 +4009,10 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide()");
// load the view frustum
loadViewFrustum(theCamera, _displayViewFrustum);
{
QMutexLocker viewLocker(&_viewMutex);
loadViewFrustum(theCamera, _displayViewFrustum);
}
// TODO fix shadows and make them use the GPU library
@ -4082,7 +4080,10 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
{
PerformanceTimer perfTimer("EngineRun");
renderArgs->_viewFrustum = getDisplayViewFrustum();
{
QMutexLocker viewLocker(&_viewMutex);
renderArgs->setViewFrustum(_displayViewFrustum);
}
_renderEngine->getRenderContext()->args = renderArgs;
// Before the deferred pass, let's try to use the render engine
@ -4951,24 +4952,19 @@ qreal Application::getDevicePixelRatio() {
return (_window && _window->windowHandle()) ? _window->windowHandle()->devicePixelRatio() : 1.0;
}
DisplayPlugin* Application::getActiveDisplayPlugin() {
DisplayPlugin* result = nullptr;
if (QThread::currentThread() == thread()) {
if (nullptr == _displayPlugin) {
updateDisplayMode();
Q_ASSERT(_displayPlugin);
}
result = _displayPlugin.get();
} else {
DisplayPluginPointer Application::getActiveDisplayPlugin() const {
if (QThread::currentThread() != thread()) {
std::unique_lock<std::mutex> lock(_displayPluginLock);
result = _displayPlugin.get();
return _displayPlugin;
}
return result;
if (!_displayPlugin) {
const_cast<Application*>(this)->updateDisplayMode();
Q_ASSERT(_displayPlugin);
}
return _displayPlugin;
}
const DisplayPlugin* Application::getActiveDisplayPlugin() const {
return const_cast<Application*>(this)->getActiveDisplayPlugin();
}
static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool active = false) {
auto menu = Menu::getInstance();
@ -5212,10 +5208,10 @@ void Application::updateInputModes() {
}
mat4 Application::getEyeProjection(int eye) const {
QMutexLocker viewLocker(&_viewMutex);
if (isHMDMode()) {
return getActiveDisplayPlugin()->getEyeProjection((Eye)eye, _viewFrustum.getProjection());
}
return _viewFrustum.getProjection();
}

View file

@ -128,14 +128,12 @@ public:
Camera* getCamera() { return &_myCamera; }
const Camera* getCamera() const { return &_myCamera; }
// Represents the current view frustum of the avatar.
ViewFrustum* getViewFrustum();
const ViewFrustum* getViewFrustum() const;
void copyViewFrustum(ViewFrustum& viewOut) const;
// Represents the view frustum of the current rendering pass,
// which might be different from the viewFrustum, i.e. shadowmap
// passes, mirror window passes, etc
ViewFrustum* getDisplayViewFrustum();
const ViewFrustum* getDisplayViewFrustum() const;
ViewFrustum* getShadowViewFrustum() override { return &_shadowViewFrustum; }
void copyDisplayViewFrustum(ViewFrustum& viewOut) const;
void copyShadowViewFrustum(ViewFrustum& viewOut) const override;
const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; }
EntityTreeRenderer* getEntities() const { return DependencyManager::get<EntityTreeRenderer>().data(); }
QUndoStack* getUndoStack() { return &_undoStack; }
@ -169,7 +167,7 @@ public:
virtual controller::ScriptingInterface* getControllerScriptingInterface() { return _controllerScriptingInterface; }
virtual void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) override;
virtual ViewFrustum* getCurrentViewFrustum() override { return getDisplayViewFrustum(); }
virtual void copyCurrentViewFrustum(ViewFrustum& viewOut) const override { copyDisplayViewFrustum(viewOut); }
virtual QThread* getMainThread() override { return thread(); }
virtual PickRay computePickRay(float x, float y) const override;
virtual glm::vec3 getAvatarPosition() const override;
@ -177,8 +175,7 @@ public:
void setActiveDisplayPlugin(const QString& pluginName);
DisplayPlugin* getActiveDisplayPlugin();
const DisplayPlugin* getActiveDisplayPlugin() const;
DisplayPluginPointer getActiveDisplayPlugin() const;
FileLogger* getLogger() const { return _logger; }
@ -212,7 +209,7 @@ public:
render::EnginePointer getRenderEngine() override { return _renderEngine; }
gpu::ContextPointer getGPUContext() const { return _gpuContext; }
virtual void pushPreRenderLambda(void* key, std::function<void()> func) override;
virtual void pushPostUpdateLambda(void* key, std::function<void()> func) override;
const QRect& getMirrorViewRect() const { return _mirrorViewRect; }
@ -224,8 +221,6 @@ public:
signals:
void svoImportRequested(const QString& url);
void checkBackgroundDownloads();
void fullAvatarURLChanged(const QString& newValue, const QString& modelName);
void beforeAboutToQuit();
@ -255,7 +250,6 @@ public slots:
void resetSensors(bool andReload = false);
void setActiveFaceTracker() const;
void toggleSuppressDeadlockWatchdogStatus(bool checked);
#ifdef HAVE_IVIEWHMD
void setActiveEyeTracker();
@ -388,7 +382,7 @@ private:
OffscreenGLCanvas* _offscreenContext { nullptr };
DisplayPluginPointer _displayPlugin;
std::mutex _displayPluginLock;
mutable std::mutex _displayPluginLock;
InputPluginList _activeInputPlugins;
bool _activatingDisplayPlugin { false };
@ -408,12 +402,13 @@ private:
QElapsedTimer _lastTimeUpdated;
ShapeManager _shapeManager;
PhysicalEntitySimulation _entitySimulation;
PhysicalEntitySimulationPointer _entitySimulation;
PhysicsEnginePointer _physicsEngine;
EntityTreeRenderer _entityClipboardRenderer;
EntityTreePointer _entityClipboard;
mutable QMutex _viewMutex { QMutex::Recursive };
ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc.
ViewFrustum _lastQueriedViewFrustum; /// last view frustum used to query octree servers (voxels)
ViewFrustum _displayViewFrustum;
@ -513,8 +508,8 @@ private:
QThread* _deadlockWatchdogThread;
std::map<void*, std::function<void()>> _preRenderLambdas;
std::mutex _preRenderLambdasLock;
std::map<void*, std::function<void()>> _postUpdateLambdas;
std::mutex _postUpdateLambdasLock;
std::atomic<uint32_t> _fullSceneReceivedCounter { 0 }; // how many times have we received a full-scene octree stats packet
uint32_t _fullSceneCounterAtLastPhysicsCheck { 0 }; // _fullSceneReceivedCounter last time we checked physics ready

View file

@ -10,6 +10,7 @@
//
#include <SettingHandle.h>
#include <OctreeUtils.h>
#include <Util.h>
#include "Application.h"
@ -216,7 +217,7 @@ QString LODManager::getLODFeedbackText() {
bool LODManager::shouldRender(const RenderArgs* args, const AABox& bounds) {
// FIXME - eventually we want to use the render accuracy as an indicator for the level of detail
// to use in rendering.
float renderAccuracy = args->_viewFrustum->calculateRenderAccuracy(bounds, args->_sizeScale, args->_boundaryLevelAdjust);
float renderAccuracy = calculateRenderAccuracy(args->getViewFrustum().getPosition(), bounds, args->_sizeScale, args->_boundaryLevelAdjust);
return (renderAccuracy > 0.0f);
};
@ -228,7 +229,6 @@ void LODManager::setBoundaryLevelAdjust(int boundaryLevelAdjust) {
_boundaryLevelAdjust = boundaryLevelAdjust;
}
void LODManager::loadSettings() {
setDesktopLODDecreaseFPS(desktopLODDecreaseFPS.get());
setHMDLODDecreaseFPS(hmdLODDecreaseFPS.get());
@ -239,4 +239,3 @@ void LODManager::saveSettings() {
hmdLODDecreaseFPS.set(getHMDLODDecreaseFPS());
}

View file

@ -480,10 +480,8 @@ Menu::Menu() {
avatarManager.data(), SLOT(setShouldShowReceiveStats(bool)));
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderBoundingCollisionShapes);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderLookAtVectors, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderLookAtTargets, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowWhosLookingAtMe, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderMyLookAtVectors, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderOtherLookAtVectors, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false);
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawDefaultPose, 0, false,
avatar, SLOT(setEnableDebugDrawDefaultPose(bool)));
@ -568,8 +566,6 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::PipelineWarnings);
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::LogExtraTimings);
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::SuppressShortTimings);
addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::SupressDeadlockWatchdogStatus, 0, false,
qApp, SLOT(toggleSuppressDeadlockWatchdogStatus(bool)));
// Developer > Audio >>>

View file

@ -147,9 +147,8 @@ namespace MenuOption {
const QString ReloadAllScripts = "Reload All Scripts";
const QString ReloadContent = "Reload Content (Clears all caches)";
const QString RenderBoundingCollisionShapes = "Show Bounding Collision Shapes";
const QString RenderFocusIndicator = "Show Eye Focus";
const QString RenderLookAtTargets = "Show Look-at Targets";
const QString RenderLookAtVectors = "Show Look-at Vectors";
const QString RenderMyLookAtVectors = "Show My Eye Vectors";
const QString RenderOtherLookAtVectors = "Show Other Eye Vectors";
const QString RenderMaxTextureMemory = "Maximum Texture Memory";
const QString RenderMaxTextureAutomatic = "Automatic Texture Memory";
const QString RenderMaxTexture64MB = "64 MB";
@ -174,7 +173,6 @@ namespace MenuOption {
const QString ShowDSConnectTable = "Show Domain Connection Timing";
const QString ShowBordersEntityNodes = "Show Entity Nodes";
const QString ShowRealtimeEntityStats = "Show Realtime Entity Stats";
const QString ShowWhosLookingAtMe = "Show Who's Looking at Me";
const QString StandingHMDSensorMode = "Standing HMD Sensor Mode";
const QString SimulateEyeTracking = "Simulate";
const QString SMIEyeTracking = "SMI Eye Tracking";
@ -182,7 +180,6 @@ namespace MenuOption {
const QString Stats = "Stats";
const QString StopAllScripts = "Stop All Scripts";
const QString SuppressShortTimings = "Suppress Timings Less than 10ms";
const QString SupressDeadlockWatchdogStatus = "Supress Deadlock Watchdog Status";
const QString ThirdPerson = "Third Person";
const QString ThreePointCalibration = "3 Point Calibration";
const QString ThrottleFPSIfNotFocus = "Throttle FPS If Not Focus"; // FIXME - this value duplicated in Basic2DWindowOpenGLDisplayPlugin.cpp

View file

@ -54,7 +54,7 @@ QOpenGLContext* PluginContainerProxy::getPrimaryContext() {
return qApp->_glWidget->context()->contextHandle();
}
const DisplayPlugin* PluginContainerProxy::getActiveDisplayPlugin() const {
const DisplayPluginPointer PluginContainerProxy::getActiveDisplayPlugin() const {
return qApp->getActiveDisplayPlugin();
}

View file

@ -24,7 +24,7 @@ class PluginContainerProxy : public QObject, PluginContainer {
virtual ui::Menu* getPrimaryMenu() override;
virtual QOpenGLContext* getPrimaryContext() override;
virtual bool isForeground() override;
virtual const DisplayPlugin* getActiveDisplayPlugin() const override;
virtual const DisplayPluginPointer getActiveDisplayPlugin() const override;
friend class Application;

View file

@ -194,8 +194,8 @@ void Stars::render(RenderArgs* renderArgs, float alpha) {
gpu::Batch& batch = *renderArgs->_batch;
batch.setViewTransform(Transform());
batch.setProjectionTransform(renderArgs->_viewFrustum->getProjection());
batch.setModelTransform(Transform().setRotation(glm::inverse(renderArgs->_viewFrustum->getOrientation()) *
batch.setProjectionTransform(renderArgs->getViewFrustum().getProjection());
batch.setModelTransform(Transform().setRotation(glm::inverse(renderArgs->getViewFrustum().getOrientation()) *
quat(vec3(TILT, 0, 0))));
batch.setResourceTexture(0, textureCache->getWhiteTexture());

View file

@ -25,11 +25,13 @@
#include <LODManager.h>
#include <NodeList.h>
#include <NumericalConstants.h>
#include <OctreeUtils.h>
#include <udt/PacketHeaders.h>
#include <PerfStat.h>
#include <SharedUtil.h>
#include <TextRenderer3D.h>
#include <TextureCache.h>
#include <DebugDraw.h>
#include "Application.h"
#include "Avatar.h"
@ -65,11 +67,6 @@ namespace render {
}
template <> void payloadRender(const AvatarSharedPointer& avatar, RenderArgs* args) {
auto avatarPtr = static_pointer_cast<Avatar>(avatar);
bool renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::RenderLookAtVectors);
avatarPtr->setDisplayingLookatVectors(renderLookAtVectors);
bool renderLookAtTarget = Menu::getInstance()->isOptionChecked(MenuOption::RenderLookAtTargets);
avatarPtr->setDisplayingLookatTarget(renderLookAtTarget);
if (avatarPtr->isInitialized() && args) {
PROFILE_RANGE_BATCH(*args->_batch, "renderAvatarPayload");
avatarPtr->render(args, qApp->getCamera()->getPosition());
@ -171,7 +168,10 @@ void Avatar::simulate(float deltaTime) {
// update the shouldAnimate flag to match whether or not we will render the avatar.
const float MINIMUM_VISIBILITY_FOR_ON = 0.4f;
const float MAXIMUM_VISIBILITY_FOR_OFF = 0.6f;
float visibility = qApp->getViewFrustum()->calculateRenderAccuracy(getBounds(), DependencyManager::get<LODManager>()->getOctreeSizeScale());
ViewFrustum viewFrustum;
qApp->copyViewFrustum(viewFrustum);
float visibility = calculateRenderAccuracy(viewFrustum.getPosition(),
getBounds(), DependencyManager::get<LODManager>()->getOctreeSizeScale());
if (!_shouldAnimate) {
if (visibility > MINIMUM_VISIBILITY_FOR_ON) {
_shouldAnimate = true;
@ -184,8 +184,9 @@ void Avatar::simulate(float deltaTime) {
// simple frustum check
float boundingRadius = getBoundingRadius();
bool avatarPositionInView = qApp->getDisplayViewFrustum()->sphereIntersectsFrustum(getPosition(), boundingRadius);
bool avatarMeshInView = qApp->getDisplayViewFrustum()->boxIntersectsFrustum(_skeletonModel->getRenderableMeshBound());
qApp->copyDisplayViewFrustum(viewFrustum);
bool avatarPositionInView = viewFrustum.sphereIntersectsFrustum(getPosition(), boundingRadius);
bool avatarMeshInView = viewFrustum.boxIntersectsFrustum(_skeletonModel->getRenderableMeshBound());
if (_shouldAnimate && !_shouldSkipRender && (avatarPositionInView || avatarMeshInView)) {
{
@ -318,6 +319,39 @@ void Avatar::updateRenderItem(render::PendingChanges& pendingChanges) {
}
}
void Avatar::postUpdate(float deltaTime) {
bool renderLookAtVectors;
if (isMyAvatar()) {
renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::RenderMyLookAtVectors);
} else {
renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::RenderOtherLookAtVectors);
}
if (renderLookAtVectors) {
const float EYE_RAY_LENGTH = 10.0;
const glm::vec4 BLUE(0.0f, 0.0f, 1.0f, 1.0f);
const glm::vec4 RED(1.0f, 0.0f, 0.0f, 1.0f);
int leftEyeJoint = getJointIndex("LeftEye");
glm::vec3 leftEyePosition;
glm::quat leftEyeRotation;
if (_skeletonModel->getJointPositionInWorldFrame(leftEyeJoint, leftEyePosition) &&
_skeletonModel->getJointRotationInWorldFrame(leftEyeJoint, leftEyeRotation)) {
DebugDraw::getInstance().drawRay(leftEyePosition, leftEyePosition + leftEyeRotation * Vectors::UNIT_Z * EYE_RAY_LENGTH, BLUE);
}
int rightEyeJoint = getJointIndex("RightEye");
glm::vec3 rightEyePosition;
glm::quat rightEyeRotation;
if (_skeletonModel->getJointPositionInWorldFrame(rightEyeJoint, rightEyePosition) &&
_skeletonModel->getJointRotationInWorldFrame(rightEyeJoint, rightEyeRotation)) {
DebugDraw::getInstance().drawRay(rightEyePosition, rightEyePosition + rightEyeRotation * Vectors::UNIT_Z * EYE_RAY_LENGTH, RED);
}
}
}
void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
auto& batch = *renderArgs->_batch;
PROFILE_RANGE_BATCH(batch, __FUNCTION__);
@ -381,39 +415,23 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
}
}
// simple frustum check
float boundingRadius = getBoundingRadius();
ViewFrustum* frustum = nullptr;
if (renderArgs->_renderMode == RenderArgs::SHADOW_RENDER_MODE) {
frustum = qApp->getShadowViewFrustum();
} else {
frustum = qApp->getDisplayViewFrustum();
}
if (!frustum->sphereIntersectsFrustum(getPosition(), boundingRadius)) {
return;
{ // simple frustum check
ViewFrustum frustum;
if (renderArgs->_renderMode == RenderArgs::SHADOW_RENDER_MODE) {
qApp->copyShadowViewFrustum(frustum);
} else {
qApp->copyDisplayViewFrustum(frustum);
}
if (!frustum.sphereIntersectsFrustum(getPosition(), getBoundingRadius())) {
return;
}
}
glm::vec3 toTarget = cameraPosition - getPosition();
float distanceToTarget = glm::length(toTarget);
{
// glow when moving far away
const float GLOW_DISTANCE = 20.0f;
const float GLOW_MAX_LOUDNESS = 2500.0f;
const float MAX_GLOW = 0.5f;
float GLOW_FROM_AVERAGE_LOUDNESS = ((this == DependencyManager::get<AvatarManager>()->getMyAvatar())
? 0.0f
: MAX_GLOW * getHeadData()->getAudioLoudness() / GLOW_MAX_LOUDNESS);
GLOW_FROM_AVERAGE_LOUDNESS = 0.0f;
float glowLevel = _moving && distanceToTarget > GLOW_DISTANCE && renderArgs->_renderMode == RenderArgs::NORMAL_RENDER_MODE
? 1.0f
: GLOW_FROM_AVERAGE_LOUDNESS;
// render body
renderBody(renderArgs, frustum, glowLevel);
fixupModelsInScene();
if (renderArgs->_renderMode != RenderArgs::SHADOW_RENDER_MODE) {
// add local lights
@ -437,64 +455,6 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
const float BOUNDING_SHAPE_ALPHA = 0.7f;
_skeletonModel->renderBoundingCollisionShapes(*renderArgs->_batch, getUniformScale(), BOUNDING_SHAPE_ALPHA);
}
// If this is the avatar being looked at, render a little ball above their head
if (_isLookAtTarget && Menu::getInstance()->isOptionChecked(MenuOption::RenderFocusIndicator)) {
static const float INDICATOR_OFFSET = 0.22f;
static const float INDICATOR_RADIUS = 0.03f;
static const glm::vec4 LOOK_AT_INDICATOR_COLOR = { 0.8f, 0.0f, 0.0f, 0.75f };
glm::vec3 avatarPosition = getPosition();
glm::vec3 position = glm::vec3(avatarPosition.x, getDisplayNamePosition().y + INDICATOR_OFFSET, avatarPosition.z);
PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderFocusIndicator");
Transform transform;
transform.setTranslation(position);
transform.postScale(INDICATOR_RADIUS);
batch.setModelTransform(transform);
DependencyManager::get<GeometryCache>()->renderSolidSphereInstance(batch, LOOK_AT_INDICATOR_COLOR);
}
// If the avatar is looking at me, indicate that they are
if (getHead()->isLookingAtMe() && Menu::getInstance()->isOptionChecked(MenuOption::ShowWhosLookingAtMe)) {
PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderLookingAtMe");
const glm::vec3 LOOKING_AT_ME_COLOR = { 1.0f, 1.0f, 1.0f };
const float LOOKING_AT_ME_ALPHA_START = 0.8f;
const float LOOKING_AT_ME_DURATION = 0.5f; // seconds
quint64 now = usecTimestampNow();
float alpha = LOOKING_AT_ME_ALPHA_START
* (1.0f - ((float)(now - getHead()->getLookingAtMeStarted()))
/ (LOOKING_AT_ME_DURATION * (float)USECS_PER_SECOND));
if (alpha > 0.0f) {
if (_skeletonModel->isLoaded()) {
const auto& geometry = _skeletonModel->getFBXGeometry();
const float DEFAULT_EYE_DIAMETER = 0.048f; // Typical human eye
const float RADIUS_INCREMENT = 0.005f;
batch.setModelTransform(Transform());
glm::vec3 position = getHead()->getLeftEyePosition();
Transform transform;
transform.setTranslation(position);
float eyeDiameter = geometry.leftEyeSize;
if (eyeDiameter == 0.0f) {
eyeDiameter = DEFAULT_EYE_DIAMETER;
}
batch.setModelTransform(Transform(transform).postScale(eyeDiameter * getUniformScale() / 2.0f + RADIUS_INCREMENT));
DependencyManager::get<GeometryCache>()->renderSolidSphereInstance(batch,
glm::vec4(LOOKING_AT_ME_COLOR, alpha));
position = getHead()->getRightEyePosition();
transform.setTranslation(position);
eyeDiameter = geometry.rightEyeSize;
if (eyeDiameter == 0.0f) {
eyeDiameter = DEFAULT_EYE_DIAMETER;
}
batch.setModelTransform(Transform(transform).postScale(eyeDiameter * getUniformScale() / 2.0f + RADIUS_INCREMENT));
DependencyManager::get<GeometryCache>()->renderSolidSphereInstance(batch,
glm::vec4(LOOKING_AT_ME_COLOR, alpha));
}
}
}
}
const float DISPLAYNAME_DISTANCE = 20.0f;
@ -502,9 +462,8 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
auto cameraMode = qApp->getCamera()->getMode();
if (!isMyAvatar() || cameraMode != CAMERA_MODE_FIRST_PERSON) {
auto& frustum = *renderArgs->_viewFrustum;
auto& frustum = renderArgs->getViewFrustum();
auto textPosition = getDisplayNamePosition();
if (frustum.pointIntersectsFrustum(textPosition)) {
renderDisplayName(batch, frustum, textPosition);
}
@ -553,11 +512,6 @@ void Avatar::fixupModelsInScene() {
scene->enqueuePendingChanges(pendingChanges);
}
void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel) {
fixupModelsInScene();
getHead()->renderLookAts(renderArgs);
}
bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const {
return true;
}

View file

@ -77,9 +77,9 @@ public:
void updateRenderItem(render::PendingChanges& pendingChanges);
virtual void postUpdate(float deltaTime);
//setters
void setDisplayingLookatVectors(bool displayingLookatVectors) { getHead()->setRenderLookatVectors(displayingLookatVectors); }
void setDisplayingLookatTarget(bool displayingLookatTarget) { getHead()->setRenderLookatTarget(displayingLookatTarget); }
void setIsLookAtTarget(const bool isLookAtTarget) { _isLookAtTarget = isLookAtTarget; }
bool getIsLookAtTarget() const { return _isLookAtTarget; }
//getters
@ -232,7 +232,6 @@ protected:
Transform calculateDisplayNameTransform(const ViewFrustum& view, const glm::vec3& textPosition) const;
void renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const glm::vec3& textPosition) const;
virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel = 0.0f);
virtual bool shouldRenderHead(const RenderArgs* renderArgs) const;
virtual void fixupModelsInScene();
@ -251,7 +250,7 @@ private:
bool _initialized;
bool _shouldAnimate { true };
bool _shouldSkipRender { false };
bool _isLookAtTarget;
bool _isLookAtTarget { false };
float getBoundingRadius() const;

View file

@ -93,13 +93,13 @@ void AvatarActionHold::prepareForPhysicsSimulation() {
activateBody(true);
}
std::shared_ptr<Avatar> AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position,
glm::vec3& linearVelocity, glm::vec3& angularVelocity) {
bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position,
glm::vec3& linearVelocity, glm::vec3& angularVelocity) {
auto avatarManager = DependencyManager::get<AvatarManager>();
auto holdingAvatar = std::static_pointer_cast<Avatar>(avatarManager->getAvatarBySessionID(_holderID));
if (!holdingAvatar) {
return holdingAvatar;
return false;;
}
withReadLock([&]{
@ -171,62 +171,17 @@ std::shared_ptr<Avatar> AvatarActionHold::getTarget(float deltaTimeStep, glm::qu
linearVelocity = linearVelocity + glm::cross(angularVelocity, position - palmPosition);
});
return holdingAvatar;
return true;
}
void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
glm::quat rotation;
glm::vec3 position;
glm::vec3 linearVelocity;
glm::vec3 angularVelocity;
bool valid = false;
int holdCount = 0;
auto ownerEntity = _ownerEntity.lock();
if (!ownerEntity) {
return;
}
QList<EntityActionPointer> holdActions = ownerEntity->getActionsOfType(ACTION_TYPE_HOLD);
foreach (EntityActionPointer action, holdActions) {
std::shared_ptr<AvatarActionHold> holdAction = std::static_pointer_cast<AvatarActionHold>(action);
glm::quat rotationForAction;
glm::vec3 positionForAction;
glm::vec3 linearVelocityForAction, angularVelocityForAction;
std::shared_ptr<Avatar> holdingAvatar = holdAction->getTarget(deltaTimeStep, rotationForAction, positionForAction, linearVelocityForAction, angularVelocityForAction);
if (holdingAvatar) {
holdCount ++;
if (holdAction.get() == this) {
// only use the rotation for this action
valid = true;
rotation = rotationForAction;
}
position += positionForAction;
linearVelocity += linearVelocityForAction;
angularVelocity += angularVelocityForAction;
}
}
if (valid && holdCount > 0) {
position /= holdCount;
linearVelocity /= holdCount;
angularVelocity /= holdCount;
withWriteLock([&]{
_positionalTarget = position;
_rotationalTarget = rotation;
_linearVelocityTarget = linearVelocity;
_angularVelocityTarget = angularVelocity;
_positionalTargetSet = true;
_rotationalTargetSet = true;
_active = true;
});
if (_kinematic) {
if (_kinematic) {
if (prepareForSpringUpdate(deltaTimeStep)) {
doKinematicUpdate(deltaTimeStep);
} else {
forceBodyNonStatic();
ObjectActionSpring::updateActionWorker(deltaTimeStep);
}
} else {
forceBodyNonStatic();
ObjectActionSpring::updateActionWorker(deltaTimeStep);
}
}

View file

@ -36,8 +36,8 @@ public:
virtual bool shouldSuppressLocationEdits() override { return _active && !_ownerEntity.expired(); }
bool getAvatarRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation);
std::shared_ptr<Avatar> getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position,
glm::vec3& linearVelocity, glm::vec3& angularVelocity);
virtual bool getTarget(float deltaTimeStep, glm::quat& rotation, glm::vec3& position,
glm::vec3& linearVelocity, glm::vec3& angularVelocity) override;
virtual void prepareForPhysicsSimulation() override;
@ -51,9 +51,6 @@ private:
QString _hand { "right" };
QUuid _holderID;
glm::vec3 _linearVelocityTarget;
glm::vec3 _angularVelocityTarget;
bool _kinematic { false };
bool _kinematicSetVelocity { false };
bool _previousSet { false };

View file

@ -156,6 +156,15 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
simulateAvatarFades(deltaTime);
}
void AvatarManager::postUpdate(float deltaTime) {
auto hashCopy = getHashCopy();
AvatarHash::iterator avatarIterator = hashCopy.begin();
for (avatarIterator = hashCopy.begin(); avatarIterator != hashCopy.end(); avatarIterator++) {
auto avatar = std::static_pointer_cast<Avatar>(avatarIterator.value());
avatar->postUpdate(deltaTime);
}
}
void AvatarManager::simulateAvatarFades(float deltaTime) {
QVector<AvatarSharedPointer>::iterator fadingIterator = _avatarFades.begin();
@ -309,9 +318,11 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents
// my avatar. (Other user machines will make a similar analysis and inject sound for their collisions.)
if (collision.idA.isNull() || collision.idB.isNull()) {
MyAvatar* myAvatar = getMyAvatar();
const QString& collisionSoundURL = myAvatar->getCollisionSoundURL();
if (!collisionSoundURL.isEmpty()) {
const float velocityChange = glm::length(collision.velocityChange);
auto collisionSound = myAvatar->getCollisionSound();
if (collisionSound) {
const auto characterController = myAvatar->getCharacterController();
const float avatarVelocityChange = (characterController ? glm::length(characterController->getVelocityChange()) : 0.0f);
const float velocityChange = glm::length(collision.velocityChange) + avatarVelocityChange;
const float MIN_AVATAR_COLLISION_ACCELERATION = 0.01f;
const bool isSound = (collision.type == CONTACT_EVENT_TYPE_START) && (velocityChange > MIN_AVATAR_COLLISION_ACCELERATION);
@ -327,7 +338,7 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents
// but most avatars are roughly the same size, so let's not be so fancy yet.
const float AVATAR_STRETCH_FACTOR = 1.0f;
AudioInjector::playSound(collisionSoundURL, energyFactorOfFull, AVATAR_STRETCH_FACTOR, myAvatar->getPosition());
AudioInjector::playSound(collisionSound, energyFactorOfFull, AVATAR_STRETCH_FACTOR, myAvatar->getPosition());
myAvatar->collisionWithEntity(collision);
return;
}

View file

@ -44,6 +44,8 @@ public:
void updateMyAvatar(float deltaTime);
void updateOtherAvatars(float deltaTime);
void postUpdate(float deltaTime);
void clearOtherAvatars();
void clearAllAvatars();

View file

@ -46,8 +46,6 @@ Head::Head(Avatar* owningAvatar) :
_mouth3(0.0f),
_mouth4(0.0f),
_mouthTime(0.0f),
_renderLookatVectors(false),
_renderLookatTarget(false),
_saccade(0.0f, 0.0f, 0.0f),
_saccadeTarget(0.0f, 0.0f, 0.0f),
_leftEyeBlinkVelocity(0.0f),
@ -316,22 +314,6 @@ void Head::relaxLean(float deltaTime) {
_deltaLeanForward *= relaxationFactor;
}
void Head::render(RenderArgs* renderArgs, float alpha, ViewFrustum* renderFrustum) {
}
void Head::renderLookAts(RenderArgs* renderArgs) {
renderLookAts(renderArgs, _leftEyePosition, _rightEyePosition);
}
void Head::renderLookAts(RenderArgs* renderArgs, glm::vec3 leftEyePosition, glm::vec3 rightEyePosition) {
if (_renderLookatVectors) {
renderLookatVectors(renderArgs, leftEyePosition, rightEyePosition, getCorrectedLookAtPosition());
}
if (_renderLookatTarget) {
renderLookatTarget(renderArgs, getCorrectedLookAtPosition());
}
}
void Head::setScale (float scale) {
if (_scale == scale) {
return;
@ -442,31 +424,3 @@ void Head::addLeanDeltas(float sideways, float forward) {
_deltaLeanSideways += sideways;
_deltaLeanForward += forward;
}
void Head::renderLookatVectors(RenderArgs* renderArgs, glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition) {
auto& batch = *renderArgs->_batch;
auto transform = Transform{};
batch.setModelTransform(transform);
// FIXME: THe line width of 2.0f is not supported anymore, we ll need a workaround
glm::vec4 startColor(0.2f, 0.2f, 0.2f, 1.0f);
glm::vec4 endColor(1.0f, 1.0f, 1.0f, 0.0f);
auto geometryCache = DependencyManager::get<GeometryCache>();
geometryCache->bindSimpleProgram(batch);
geometryCache->renderLine(batch, leftEyePosition, lookatPosition, startColor, endColor, _leftEyeLookAtID);
geometryCache->renderLine(batch, rightEyePosition, lookatPosition, startColor, endColor, _rightEyeLookAtID);
}
void Head::renderLookatTarget(RenderArgs* renderArgs, glm::vec3 lookatPosition) {
auto& batch = *renderArgs->_batch;
auto transform = Transform{};
transform.setTranslation(lookatPosition);
auto geometryCache = DependencyManager::get<GeometryCache>();
const float LOOK_AT_TARGET_RADIUS = 0.075f;
transform.postScale(LOOK_AT_TARGET_RADIUS);
const glm::vec4 LOOK_AT_TARGET_COLOR = { 0.8f, 0.0f, 0.0f, 0.75f };
batch.setModelTransform(transform);
geometryCache->renderSolidSphereInstance(batch, LOOK_AT_TARGET_COLOR);
}

View file

@ -28,29 +28,24 @@ class Avatar;
class Head : public HeadData {
public:
explicit Head(Avatar* owningAvatar);
void init();
void reset();
void simulate(float deltaTime, bool isMine, bool billboard = false);
void render(RenderArgs* renderArgs, float alpha, ViewFrustum* renderFrustum);
void setScale(float scale);
void setPosition(glm::vec3 position) { _position = position; }
void setAverageLoudness(float averageLoudness) { _averageLoudness = averageLoudness; }
void setReturnToCenter (bool returnHeadToCenter) { _returnHeadToCenter = returnHeadToCenter; }
void setRenderLookatVectors(bool onOff) { _renderLookatVectors = onOff; }
void setRenderLookatTarget(bool onOff) { _renderLookatTarget = onOff; }
void renderLookAts(RenderArgs* renderArgs);
void renderLookAts(RenderArgs* renderArgs, glm::vec3 leftEyePosition, glm::vec3 rightEyePosition);
/// \return orientationBase+Delta
glm::quat getFinalOrientationInLocalFrame() const;
/// \return orientationBody * (orientationBase+Delta)
glm::quat getFinalOrientationInWorldFrame() const;
/// \return orientationBody * orientationBasePitch
glm::quat getCameraOrientation () const;
void setCorrectedLookAtPosition(glm::vec3 correctedLookAtPosition);
glm::vec3 getCorrectedLookAtPosition();
void clearCorrectedLookAtPosition() { _isLookingAtMe = false; }
@ -66,9 +61,9 @@ public:
glm::vec3 getFrontDirection() const { return getOrientation() * IDENTITY_FRONT; }
float getFinalLeanSideways() const { return _leanSideways + _deltaLeanSideways; }
float getFinalLeanForward() const { return _leanForward + _deltaLeanForward; }
glm::quat getEyeRotation(const glm::vec3& eyePosition) const;
const glm::vec3& getRightEyePosition() const { return _rightEyePosition; }
const glm::vec3& getLeftEyePosition() const { return _leftEyePosition; }
glm::vec3 getRightEarPosition() const { return _rightEyePosition + (getRightDirection() * EYE_EAR_GAP) + (getFrontDirection() * -EYE_EAR_GAP); }
@ -85,10 +80,10 @@ public:
void setDeltaYaw(float yaw) { _deltaYaw = yaw; }
float getDeltaYaw() const { return _deltaYaw; }
void setDeltaRoll(float roll) { _deltaRoll = roll; }
float getDeltaRoll() const { return _deltaRoll; }
virtual void setFinalYaw(float finalYaw);
virtual void setFinalPitch(float finalPitch);
virtual void setFinalRoll(float finalRoll);
@ -100,7 +95,7 @@ public:
void addLeanDeltas(float sideways, float forward);
float getTimeWithoutTalking() const { return _timeWithoutTalking; }
private:
glm::vec3 calculateAverageEyePosition() const { return _leftEyePosition + (_rightEyePosition - _leftEyePosition ) * 0.5f; }
@ -114,7 +109,7 @@ private:
glm::vec3 _leftEyePosition;
glm::vec3 _rightEyePosition;
glm::vec3 _eyePosition;
float _scale;
float _lastLoudness;
float _longTermAverageLoudness;
@ -125,8 +120,7 @@ private:
float _mouth3;
float _mouth4;
float _mouthTime;
bool _renderLookatVectors;
bool _renderLookatTarget;
glm::vec3 _saccade;
glm::vec3 _saccadeTarget;
float _leftEyeBlinkVelocity;
@ -146,15 +140,13 @@ private:
bool _isLookingAtMe;
quint64 _lookingAtMeStarted;
quint64 _wasLastLookingAtMe;
glm::vec3 _correctedLookAtPosition;
int _leftEyeLookAtID;
int _rightEyeLookAtID;
// private methods
void renderLookatVectors(RenderArgs* renderArgs, glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition);
void renderLookatTarget(RenderArgs* renderArgs, glm::vec3 lookatPosition);
void calculateMouthShapes();
void applyEyelidOffset(glm::quat headOrientation);
};

View file

@ -23,7 +23,6 @@
#include <AccountManager.h>
#include <AddressManager.h>
#include <AudioClient.h>
#include <DependencyManager.h>
#include <display-plugins/DisplayPlugin.h>
#include <FSTReader.h>
#include <GeometryUtil.h>
@ -32,6 +31,7 @@
#include <PathUtils.h>
#include <PerfStat.h>
#include <SharedUtil.h>
#include <SoundCache.h>
#include <TextRenderer3D.h>
#include <UserActivityLogger.h>
#include <AnimDebugDraw.h>
@ -97,7 +97,6 @@ MyAvatar::MyAvatar(RigPointer rig) :
_scriptedMotorTimescale(DEFAULT_SCRIPTED_MOTOR_TIMESCALE),
_scriptedMotorFrame(SCRIPTED_MOTOR_CAMERA_FRAME),
_motionBehaviors(AVATAR_MOTION_DEFAULTS),
_collisionSoundURL(""),
_characterController(this),
_lookAtTargetAvatar(),
_shouldRender(true),
@ -546,7 +545,9 @@ void MyAvatar::updateFromTrackers(float deltaTime) {
head->setDeltaYaw(estimatedRotation.y);
head->setDeltaRoll(estimatedRotation.z);
} else {
float magnifyFieldOfView = qApp->getViewFrustum()->getFieldOfView() / _realWorldFieldOfView.get();
ViewFrustum viewFrustum;
qApp->copyViewFrustum(viewFrustum);
float magnifyFieldOfView = viewFrustum.getFieldOfView() / _realWorldFieldOfView.get();
head->setDeltaPitch(estimatedRotation.x * magnifyFieldOfView);
head->setDeltaYaw(estimatedRotation.y * magnifyFieldOfView);
head->setDeltaRoll(estimatedRotation.z);
@ -929,15 +930,17 @@ void MyAvatar::updateLookAtTargetAvatar() {
// (We will be adding that offset to the camera position, after making some other adjustments.)
glm::vec3 gazeOffset = lookAtPosition - getHead()->getEyePosition();
ViewFrustum viewFrustum;
qApp->copyViewFrustum(viewFrustum);
// scale gazeOffset by IPD, if wearing an HMD.
if (qApp->isHMDMode()) {
glm::mat4 leftEye = qApp->getEyeOffset(Eye::Left);
glm::mat4 rightEye = qApp->getEyeOffset(Eye::Right);
glm::vec3 leftEyeHeadLocal = glm::vec3(leftEye[3]);
glm::vec3 rightEyeHeadLocal = glm::vec3(rightEye[3]);
auto humanSystem = qApp->getViewFrustum();
glm::vec3 humanLeftEye = humanSystem->getPosition() + (humanSystem->getOrientation() * leftEyeHeadLocal);
glm::vec3 humanRightEye = humanSystem->getPosition() + (humanSystem->getOrientation() * rightEyeHeadLocal);
glm::vec3 humanLeftEye = viewFrustum.getPosition() + (viewFrustum.getOrientation() * leftEyeHeadLocal);
glm::vec3 humanRightEye = viewFrustum.getPosition() + (viewFrustum.getOrientation() * rightEyeHeadLocal);
auto hmdInterface = DependencyManager::get<HMDScriptingInterface>();
float ipdScale = hmdInterface->getIPDScale();
@ -951,7 +954,7 @@ void MyAvatar::updateLookAtTargetAvatar() {
}
// And now we can finally add that offset to the camera.
glm::vec3 corrected = qApp->getViewFrustum()->getPosition() + gazeOffset;
glm::vec3 corrected = viewFrustum.getPosition() + gazeOffset;
avatar->getHead()->setCorrectedLookAtPosition(corrected);
@ -1232,12 +1235,20 @@ void MyAvatar::clearScriptableSettings() {
}
void MyAvatar::setCollisionSoundURL(const QString& url) {
_collisionSoundURL = url;
if (!url.isEmpty() && (url != _collisionSoundURL)) {
emit newCollisionSoundURL(QUrl(url));
if (url != _collisionSoundURL) {
_collisionSoundURL = url;
emit newCollisionSoundURL(QUrl(_collisionSoundURL));
}
}
SharedSoundPointer MyAvatar::getCollisionSound() {
if (!_collisionSound) {
_collisionSound = DependencyManager::get<SoundCache>()->getSound(_collisionSoundURL);
}
return _collisionSound;
}
void MyAvatar::attach(const QString& modelURL, const QString& jointName,
const glm::vec3& translation, const glm::quat& rotation,
float scale, bool isSoft,
@ -1259,40 +1270,6 @@ void MyAvatar::attach(const QString& modelURL, const QString& jointName,
Avatar::attach(modelURL, jointName, translation, rotation, scale, isSoft, allowDuplicates, useSaved);
}
void MyAvatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel) {
if (!_skeletonModel->isRenderable()) {
return; // wait until all models are loaded
}
fixupModelsInScene();
// Render head so long as the camera isn't inside it
if (shouldRenderHead(renderArgs)) {
getHead()->render(renderArgs, 1.0f, renderFrustum);
}
// This is drawing the lookat vectors from our avatar to wherever we're looking.
if (qApp->isHMDMode()) {
glm::vec3 cameraPosition = qApp->getCamera()->getPosition();
glm::mat4 headPose = qApp->getActiveDisplayPlugin()->getHeadPose();
glm::mat4 leftEyePose = qApp->getActiveDisplayPlugin()->getEyeToHeadTransform(Eye::Left);
leftEyePose = leftEyePose * headPose;
glm::vec3 leftEyePosition = extractTranslation(leftEyePose);
glm::mat4 rightEyePose = qApp->getActiveDisplayPlugin()->getEyeToHeadTransform(Eye::Right);
rightEyePose = rightEyePose * headPose;
glm::vec3 rightEyePosition = extractTranslation(rightEyePose);
glm::vec3 headPosition = extractTranslation(headPose);
getHead()->renderLookAts(renderArgs,
cameraPosition + getOrientation() * (leftEyePosition - headPosition),
cameraPosition + getOrientation() * (rightEyePosition - headPosition));
} else {
getHead()->renderLookAts(renderArgs);
}
}
void MyAvatar::setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visible) {
if (model->isActive() && model->isRenderable()) {
model->setVisibleInScene(visible, scene);
@ -1349,11 +1326,11 @@ void MyAvatar::destroyAnimGraph() {
_rig->destroyAnimGraph();
}
void MyAvatar::preRender(RenderArgs* renderArgs) {
void MyAvatar::postUpdate(float deltaTime) {
Avatar::postUpdate(deltaTime);
render::ScenePointer scene = qApp->getMain3DScene();
const bool shouldDrawHead = shouldRenderHead(renderArgs);
if (_skeletonModel->initWhenReady(scene)) {
initHeadBones();
_skeletonModel->setCauterizeBoneSet(_headBoneSet);
@ -1403,7 +1380,13 @@ void MyAvatar::preRender(RenderArgs* renderArgs) {
DebugDraw::getInstance().updateMyAvatarPos(getPosition());
DebugDraw::getInstance().updateMyAvatarRot(getOrientation());
}
void MyAvatar::preDisplaySide(RenderArgs* renderArgs) {
// toggle using the cauterizedBones depending on where the camera is and the rendering pass type.
const bool shouldDrawHead = shouldRenderHead(renderArgs);
if (shouldDrawHead != _prevShouldDrawHead) {
_skeletonModel->setCauterizeBones(!shouldDrawHead);
}

View file

@ -16,6 +16,7 @@
#include <SettingHandle.h>
#include <Rig.h>
#include <Sound.h>
#include <controllers/Pose.h>
@ -95,7 +96,8 @@ public:
Q_INVOKABLE void reset(bool andRecenter = false);
void update(float deltaTime);
void preRender(RenderArgs* renderArgs);
virtual void postUpdate(float deltaTime) override;
void preDisplaySide(RenderArgs* renderArgs);
const glm::mat4& getHMDSensorMatrix() const { return _hmdSensorMatrix; }
const glm::vec3& getHMDSensorPosition() const { return _hmdSensorPosition; }
@ -222,6 +224,9 @@ public:
const QString& getCollisionSoundURL() { return _collisionSoundURL; }
void setCollisionSoundURL(const QString& url);
SharedSoundPointer getCollisionSound();
void setCollisionSound(SharedSoundPointer sound) { _collisionSound = sound; }
void clearScriptableSettings();
float getBoomLength() const { return _boomLength; }
@ -306,7 +311,6 @@ private:
void simulate(float deltaTime);
void updateFromTrackers(float deltaTime);
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override;
virtual void renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel = 0.0f) override;
virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override;
void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; setEnableMeshVisible(shouldRender); }
bool getShouldRenderLocally() const { return _shouldRender; }
@ -362,6 +366,8 @@ private:
quint32 _motionBehaviors;
QString _collisionSoundURL;
SharedSoundPointer _collisionSound;
MyCharacterController _characterController;
AvatarWeakPointer _lookAtTargetAvatar;

View file

@ -13,6 +13,7 @@
#include <QMultiMap>
#include <recording/Deck.h>
#include <DebugDraw.h>
#include "Application.h"
#include "Avatar.h"
@ -92,7 +93,6 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
Head* head = _owningAvatar->getHead();
// make sure lookAt is not too close to face (avoid crosseyes)
glm::vec3 lookAt = _owningAvatar->isMyAvatar() ? head->getLookAtPosition() : head->getCorrectedLookAtPosition();
glm::vec3 focusOffset = lookAt - _owningAvatar->getHead()->getEyePosition();

View file

@ -203,7 +203,7 @@ int main(int argc, const char* argv[]) {
Application::shutdownPlugins();
qCDebug(interfaceapp, "Normal exit.");
#ifndef DEBUG
#if !defined(DEBUG) && !defined(Q_OS_LINUX)
// HACK: exit immediately (don't handle shutdown callbacks) for Release build
_exit(exitCode);
#endif

View file

@ -11,8 +11,11 @@
#include "MenuScriptingInterface.h"
#include "Menu.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QThread>
#include <MenuItemProperties.h>
#include "Menu.h"
MenuScriptingInterface* MenuScriptingInterface::getInstance() {
static MenuScriptingInterface sharedInstance;
@ -36,6 +39,9 @@ void MenuScriptingInterface::removeMenu(const QString& menu) {
}
bool MenuScriptingInterface::menuExists(const QString& menu) {
if (QThread::currentThread() == qApp->thread()) {
return Menu::getInstance()->menuExists(menu);
}
bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "menuExists", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, result),
@ -76,11 +82,14 @@ void MenuScriptingInterface::removeMenuItem(const QString& menu, const QString&
};
bool MenuScriptingInterface::menuItemExists(const QString& menu, const QString& menuitem) {
if (QThread::currentThread() == qApp->thread()) {
return Menu::getInstance()->menuItemExists(menu, menuitem);
}
bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "menuItemExists", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, result),
Q_ARG(const QString&, menu),
Q_ARG(const QString&, menuitem));
Q_RETURN_ARG(bool, result),
Q_ARG(const QString&, menu),
Q_ARG(const QString&, menuitem));
return result;
}
@ -101,6 +110,9 @@ void MenuScriptingInterface::removeActionGroup(const QString& groupName) {
}
bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) {
if (QThread::currentThread() == qApp->thread()) {
return Menu::getInstance()->isOptionChecked(menuOption);
}
bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "isOptionChecked", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, result),
@ -109,7 +121,7 @@ bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) {
}
void MenuScriptingInterface::setIsOptionChecked(const QString& menuOption, bool isChecked) {
QMetaObject::invokeMethod(Menu::getInstance(), "setIsOptionChecked", Qt::BlockingQueuedConnection,
QMetaObject::invokeMethod(Menu::getInstance(), "setIsOptionChecked",
Q_ARG(const QString&, menuOption),
Q_ARG(bool, isChecked));
}

View file

@ -31,10 +31,12 @@ UpdateDialog::UpdateDialog(QQuickItem* parent) :
_releaseNotes = "";
for (int i = latestVersion; i > currentVersion; i--) {
QString releaseNotes = applicationUpdater.data()->getBuildData()[i]["releaseNotes"];
releaseNotes.remove("<br />");
releaseNotes.remove(QRegExp("^\n+"));
_releaseNotes += "\n" + QString().sprintf("%d", i) + "\n" + releaseNotes + "\n";
if (applicationUpdater.data()->getBuildData().contains(i)) {
QString releaseNotes = applicationUpdater.data()->getBuildData()[i]["releaseNotes"];
releaseNotes.remove("<br />");
releaseNotes.remove(QRegExp("^\n+"));
_releaseNotes += "\n" + QString().sprintf("%d", i) + "\n" + releaseNotes + "\n";
}
}
}

View file

@ -65,7 +65,7 @@ void Grid3DOverlay::render(RenderArgs* args) {
// Get the camera position rounded to the nearest major grid line
// This grid is for UI and should lie on worldlines
auto cameraPosition =
(float)_majorGridEvery * glm::round(args->_viewFrustum->getPosition() / (float)_majorGridEvery);
(float)_majorGridEvery * glm::round(args->getViewFrustum().getPosition() / (float)_majorGridEvery);
position += glm::vec3(cameraPosition.x, 0.0f, cameraPosition.z);
}

View file

@ -104,7 +104,7 @@ void Image3DOverlay::render(RenderArgs* args) {
const render::ShapeKey Image3DOverlay::getShapeKey() {
auto builder = render::ShapeKey::Builder().withoutCullFace().withDepthBias();
if (_emissive) {
builder.withEmissive();
builder.withUnlit();
}
if (getAlpha() != 1.0f) {
builder.withTranslucent();

View file

@ -37,10 +37,10 @@ void LocalModelsOverlay::render(RenderArgs* args) {
auto batch = args ->_batch;
Transform transform = Transform();
transform.setTranslation(args->_viewFrustum->getPosition() + getPosition());
transform.setTranslation(args->getViewFrustum().getPosition() + getPosition());
batch->setViewTransform(transform);
_entityTreeRenderer->render(args);
transform.setTranslation(args->_viewFrustum->getPosition());
transform.setTranslation(args->getViewFrustum().getPosition());
batch->setViewTransform(transform);
}
}

View file

@ -36,7 +36,7 @@ AnimationPointer AnimationCache::getAnimation(const QUrl& url) {
}
QSharedPointer<Resource> AnimationCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
bool delayLoad, const void* extra) {
const void* extra) {
return QSharedPointer<Resource>(new Animation(url), &Resource::deleter);
}

View file

@ -35,8 +35,8 @@ public:
protected:
virtual QSharedPointer<Resource> createResource(const QUrl& url,
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra);
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
const void* extra);
private:
explicit AnimationCache(QObject* parent = NULL);
virtual ~AnimationCache() { }

View file

@ -1057,20 +1057,30 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) {
}
void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAtSpot, const glm::vec3& saccade) {
// TODO: does not properly handle avatar scale.
if (isIndexValid(index)) {
glm::mat4 rigToWorld = createMatFromQuatAndPos(modelRotation, modelTranslation);
glm::mat4 worldToRig = glm::inverse(rigToWorld);
glm::vec3 zAxis = glm::normalize(_internalPoseSet._absolutePoses[index].trans - transformPoint(worldToRig, lookAtSpot));
glm::vec3 lookAtVector = glm::normalize(transformPoint(worldToRig, lookAtSpot) - _internalPoseSet._absolutePoses[index].trans);
glm::quat desiredQuat = rotationBetween(IDENTITY_FRONT, zAxis);
glm::quat headQuat;
int headIndex = indexOfJoint("Head");
glm::quat headQuat;
if (headIndex >= 0) {
headQuat = _internalPoseSet._absolutePoses[headIndex].rot;
}
glm::vec3 headUp = headQuat * Vectors::UNIT_Y;
glm::vec3 z, y, x;
generateBasisVectors(lookAtVector, headUp, z, y, x);
glm::mat3 m(glm::cross(y, z), y, z);
glm::quat desiredQuat = glm::normalize(glm::quat_cast(m));
glm::quat deltaQuat = desiredQuat * glm::inverse(headQuat);
// limit rotation
// limit swing rotation of the deltaQuat by a 30 degree cone.
// TODO: use swing twist decomposition constraint instead, for off axis rotation clamping.
const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE;
if (fabsf(glm::angle(deltaQuat)) > MAX_ANGLE) {
deltaQuat = glm::angleAxis(glm::clamp(glm::angle(deltaQuat), -MAX_ANGLE, MAX_ANGLE), glm::axis(deltaQuat));

View file

@ -365,17 +365,9 @@ void AudioInjector::stopAndDeleteLater() {
QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection);
}
AudioInjector* AudioInjector::playSound(const QString& soundUrl, const float volume, const float stretchFactor, const glm::vec3 position) {
if (soundUrl.isEmpty()) {
return NULL;
}
auto soundCache = DependencyManager::get<SoundCache>();
if (soundCache.isNull()) {
return NULL;
}
SharedSoundPointer sound = soundCache->getSound(QUrl(soundUrl));
if (sound.isNull() || !sound->isReady()) {
return NULL;
AudioInjector* AudioInjector::playSound(SharedSoundPointer sound, const float volume, const float stretchFactor, const glm::vec3 position) {
if (!sound || !sound->isReady()) {
return nullptr;
}
AudioInjectorOptions options;
@ -385,7 +377,7 @@ AudioInjector* AudioInjector::playSound(const QString& soundUrl, const float vol
QByteArray samples = sound->getByteArray();
if (stretchFactor == 1.0f) {
return playSoundAndDelete(samples, options, NULL);
return playSoundAndDelete(samples, options, nullptr);
}
const int standardRate = AudioConstants::SAMPLE_RATE;
@ -403,7 +395,7 @@ AudioInjector* AudioInjector::playSound(const QString& soundUrl, const float vol
nInputFrames);
Q_UNUSED(nOutputFrames);
return playSoundAndDelete(resampled, options, NULL);
return playSoundAndDelete(resampled, options, nullptr);
}
AudioInjector* AudioInjector::playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface) {

View file

@ -60,7 +60,7 @@ public:
static AudioInjector* playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface);
static AudioInjector* playSound(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface);
static AudioInjector* playSound(const QString& soundUrl, const float volume, const float stretchFactor, const glm::vec3 position);
static AudioInjector* playSound(SharedSoundPointer sound, const float volume, const float stretchFactor, const glm::vec3 position);
public slots:
void restart();

View file

@ -35,7 +35,7 @@ SharedSoundPointer SoundCache::getSound(const QUrl& url) {
}
QSharedPointer<Resource> SoundCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
bool delayLoad, const void* extra) {
const void* extra) {
qCDebug(audio) << "Requesting sound at" << url.toString();
return QSharedPointer<Resource>(new Sound(url), &Resource::deleter);
}

View file

@ -25,8 +25,8 @@ public:
Q_INVOKABLE SharedSoundPointer getSound(const QUrl& url);
protected:
virtual QSharedPointer<Resource> createResource(const QUrl& url,
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra);
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
const void* extra);
private:
SoundCache(QObject* parent = NULL);
};

View file

@ -58,7 +58,7 @@ controller::UserInputMapper::UserInputMapper() {
namespace controller {
UserInputMapper::~UserInputMapper() {
}
@ -80,6 +80,7 @@ void UserInputMapper::registerDevice(InputDevice::Pointer device) {
recordDeviceOfType(device->getName());
qCDebug(controllers) << "Registered input device <" << device->getName() << "> deviceID = " << deviceID;
for (const auto& inputMapping : device->getAvailableInputs()) {
const auto& input = inputMapping.first;
// Ignore aliases
@ -102,6 +103,7 @@ void UserInputMapper::registerDevice(InputDevice::Pointer device) {
}
_registeredDevices[deviceID] = device;
auto mapping = loadMappings(device->getDefaultMappingConfigs());
if (mapping) {
_mappingsByDevice[deviceID] = mapping;
@ -111,15 +113,21 @@ void UserInputMapper::registerDevice(InputDevice::Pointer device) {
emit hardwareChanged();
}
// FIXME remove the associated device mappings
void UserInputMapper::removeDevice(int deviceID) {
Locker locker(_lock);
auto proxyEntry = _registeredDevices.find(deviceID);
if (_registeredDevices.end() == proxyEntry) {
qCWarning(controllers) << "Attempted to remove unknown device " << deviceID;
return;
}
auto proxy = proxyEntry->second;
auto device = proxyEntry->second;
qCDebug(controllers) << "Unregistering input device <" << device->getName() << "> deviceID = " << deviceID;
unloadMappings(device->getDefaultMappingConfigs());
auto mappingsEntry = _mappingsByDevice.find(deviceID);
if (_mappingsByDevice.end() != mappingsEntry) {
disableMapping(mappingsEntry->second);
@ -244,7 +252,7 @@ void UserInputMapper::update(float deltaTime) {
for (auto& channel : _actionStates) {
channel = 0.0f;
}
for (auto& channel : _poseStates) {
channel = Pose();
}
@ -705,11 +713,10 @@ Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile, bool enab
return Mapping::Pointer();
}
// Each mapping only needs to be loaded once
static QSet<QString> loaded;
if (loaded.contains(jsonFile)) {
if (_loadedRouteJsonFiles.contains(jsonFile)) {
return Mapping::Pointer();
}
loaded.insert(jsonFile);
_loadedRouteJsonFiles.insert(jsonFile);
QString json;
{
QFile file(jsonFile);
@ -741,6 +748,18 @@ MappingPointer UserInputMapper::loadMappings(const QStringList& jsonFiles) {
return result;
}
void UserInputMapper::unloadMappings(const QStringList& jsonFiles) {
for (const QString& jsonFile : jsonFiles) {
unloadMapping(jsonFile);
}
}
void UserInputMapper::unloadMapping(const QString& jsonFile) {
auto entry = _loadedRouteJsonFiles.find(jsonFile);
if (entry != _loadedRouteJsonFiles.end()) {
_loadedRouteJsonFiles.erase(entry);
}
}
static const QString JSON_NAME = QStringLiteral("name");
static const QString JSON_CHANNELS = QStringLiteral("channels");

View file

@ -111,9 +111,18 @@ namespace controller {
void loadDefaultMapping(uint16 deviceID);
void enableMapping(const QString& mappingName, bool enable = true);
void unloadMappings(const QStringList& jsonFiles);
void unloadMapping(const QString& jsonFile);
float getValue(const Input& input) const;
Pose getPose(const Input& input) const;
// perform an action when the UserInputMapper mutex is acquired.
using Locker = std::unique_lock<std::recursive_mutex>;
template <typename F>
void withLock(F&& f) { Locker locker(_lock); f(); }
signals:
void actionEvent(int action, float state);
void inputEvent(int input, float state);
@ -177,7 +186,7 @@ namespace controller {
RouteList _deviceRoutes;
RouteList _standardRoutes;
using Locker = std::unique_lock<std::recursive_mutex>;
QSet<QString> _loadedRouteJsonFiles;
mutable std::recursive_mutex _lock;
};

View file

@ -13,11 +13,12 @@
using namespace controller;
float DeadZoneFilter::apply(float value) const {
float scale = 1.0f / (1.0f - _min);
if (std::abs(value) < _min) {
float scale = ((value < 0.0f) ? -1.0f : 1.0f) / (1.0f - _min);
float magnitude = std::abs(value);
if (magnitude < _min) {
return 0.0f;
}
return (value - _min) * scale;
return (magnitude - _min) * scale;
}
bool DeadZoneFilter::parseParameters(const QJsonValue& parameters) {

View file

@ -16,6 +16,8 @@
#include <QtWidgets/QApplication>
#include <QtWidgets/QDesktopWidget>
#include <glm/gtc/type_ptr.hpp>
#include <QtGui/QWindow>
#include <QQuickWindow>
#include <ui/Menu.h>
#include <NumericalConstants.h>
@ -256,7 +258,7 @@ glm::vec2 CompositorHelper::getReticlePosition() const {
QMutexLocker locker(&_reticleLock);
return _reticlePositionInHMD;
}
return toGlm(QCursor::pos());
return toGlm(_renderingWidget->mapFromGlobal(QCursor::pos()));
}
bool CompositorHelper::getReticleOverDesktop() const {
@ -322,17 +324,8 @@ void CompositorHelper::setReticlePosition(const glm::vec2& position, bool sendFa
sendFakeMouseEvent();
}
} else {
// NOTE: This is some debugging code we will leave in while debugging various reticle movement strategies,
// remove it after we're done
const float REASONABLE_CHANGE = 50.0f;
glm::vec2 oldPos = toGlm(QCursor::pos());
auto distance = glm::distance(oldPos, position);
if (distance > REASONABLE_CHANGE) {
qDebug() << "Contrller::ScriptingInterface ---- UNREASONABLE CHANGE! distance:" <<
distance << " oldPos:" << oldPos.x << "," << oldPos.y << " newPos:" << position.x << "," << position.y;
}
QCursor::setPos(position.x, position.y);
const QPoint point(position.x, position.y);
QCursor::setPos(_renderingWidget->mapToGlobal(point));
}
}

View file

@ -237,12 +237,6 @@ bool OpenGLDisplayPlugin::activate() {
_vsyncSupported = _container->getPrimaryWidget()->isVsyncSupported();
// Child classes may override this in order to do things like initialize
// libraries, etc
if (!internalActivate()) {
return false;
}
#if THREADED_PRESENT
// Start the present thread if necessary
@ -258,8 +252,18 @@ bool OpenGLDisplayPlugin::activate() {
// Start execution
presentThread->start();
}
_presentThread = presentThread.data();
#endif
// Child classes may override this in order to do things like initialize
// libraries, etc
if (!internalActivate()) {
return false;
}
// This should not return until the new context has been customized
#if THREADED_PRESENT
// This should not return until the new context has been customized
// and the old context (if any) has been uncustomized
presentThread->setNewDisplayPlugin(this);
#else

View file

@ -103,6 +103,7 @@ protected:
virtual void updateFrameData();
QThread* _presentThread{ nullptr };
ProgramPtr _program;
int32_t _mvpUniform { -1 };
int32_t _alphaUniform { -1 };

View file

@ -63,7 +63,7 @@ bool HmdDisplayPlugin::internalActivate() {
}
static const char * REPROJECTION_VS = R"VS(#version 450 core
static const char * REPROJECTION_VS = R"VS(#version 410 core
in vec3 Position;
in vec2 TexCoord;
@ -78,15 +78,15 @@ void main() {
)VS";
static const GLint REPROJECTION_MATRIX_LOCATION = 0;
static const GLint INVERSE_PROJECTION_MATRIX_LOCATION = 4;
static const GLint PROJECTION_MATRIX_LOCATION = 12;
static const char * REPROJECTION_FS = R"FS(#version 450 core
static GLint REPROJECTION_MATRIX_LOCATION = -1;
static GLint INVERSE_PROJECTION_MATRIX_LOCATION = -1;
static GLint PROJECTION_MATRIX_LOCATION = -1;
static const char * REPROJECTION_FS = R"FS(#version 410 core
uniform sampler2D sampler;
layout (location = 0) uniform mat3 reprojection = mat3(1);
layout (location = 4) uniform mat4 inverseProjections[2];
layout (location = 12) uniform mat4 projections[2];
uniform mat3 reprojection = mat3(1);
uniform mat4 inverseProjections[2];
uniform mat4 projections[2];
in vec2 vTexCoord;
in vec3 vPosition;
@ -205,6 +205,11 @@ void HmdDisplayPlugin::customizeContext() {
_enablePreview = !isVsyncEnabled();
_sphereSection = loadSphereSection(_program, CompositorHelper::VIRTUAL_UI_TARGET_FOV.y, CompositorHelper::VIRTUAL_UI_ASPECT_RATIO);
compileProgram(_reprojectionProgram, REPROJECTION_VS, REPROJECTION_FS);
using namespace oglplus;
REPROJECTION_MATRIX_LOCATION = Uniform<glm::mat3>(*_reprojectionProgram, "reprojection").Location();
INVERSE_PROJECTION_MATRIX_LOCATION = Uniform<glm::mat4>(*_reprojectionProgram, "inverseProjections").Location();
PROJECTION_MATRIX_LOCATION = Uniform<glm::mat4>(*_reprojectionProgram, "projections").Location();
}
void HmdDisplayPlugin::uncustomizeContext() {

View file

@ -820,14 +820,14 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT
return;
}
QString collisionSoundURL;
SharedSoundPointer collisionSound;
float mass = 1.0; // value doesn't get used, but set it so compiler is quiet
AACube minAACube;
bool success = false;
_tree->withReadLock([&] {
EntityItemPointer entity = entityTree->findEntityByEntityItemID(id);
if (entity) {
collisionSoundURL = entity->getCollisionSoundURL();
collisionSound = entity->getCollisionSound();
mass = entity->computeMass();
minAACube = entity->getMinimumAACube(success);
}
@ -835,9 +835,10 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT
if (!success) {
return;
}
if (collisionSoundURL.isEmpty()) {
if (!collisionSound) {
return;
}
const float COLLISION_PENETRATION_TO_VELOCITY = 50; // as a subsitute for RELATIVE entity->getVelocity()
// The collision.penetration is a pretty good indicator of changed velocity AFTER the initial contact,
// but that first contact depends on exactly where we hit in the physics step.
@ -859,11 +860,10 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT
const float COLLISION_SOUND_COMPRESSION_RANGE = 1.0f; // This section could be removed when the value is 1, but let's see how it goes.
const float volume = (energyFactorOfFull * COLLISION_SOUND_COMPRESSION_RANGE) + (1.0f - COLLISION_SOUND_COMPRESSION_RANGE);
// Shift the pitch down by ln(1 + (size / COLLISION_SIZE_FOR_STANDARD_PITCH)) / ln(2)
const float COLLISION_SIZE_FOR_STANDARD_PITCH = 0.2f;
const float stretchFactor = log(1.0f + (minAACube.getLargestDimension() / COLLISION_SIZE_FOR_STANDARD_PITCH)) / log(2);
AudioInjector::playSound(collisionSoundURL, volume, stretchFactor, position);
AudioInjector::playSound(collisionSound, volume, stretchFactor, position);
}
void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB,

View file

@ -85,10 +85,14 @@ void makeEntityItemStatusGetters(EntityItemPointer entity, render::Item::Status:
auto nodeList = DependencyManager::get<NodeList>();
const QUuid& myNodeID = nodeList->getSessionUUID();
bool weOwnSimulation = entity->getSimulationOwner().matchesValidID(myNodeID);
bool otherOwnSimulation = !weOwnSimulation && !entity->getSimulationOwner().isNull();
if (weOwnSimulation) {
return render::Item::Status::Value(1.0f, render::Item::Status::Value::BLUE,
(unsigned char)RenderItemStatusIcon::SIMULATION_OWNER);
} else if (otherOwnSimulation) {
return render::Item::Status::Value(1.0f, render::Item::Status::Value::RED,
(unsigned char)RenderItemStatusIcon::OTHER_SIMULATION_OWNER);
}
return render::Item::Status::Value(0.0f, render::Item::Status::Value::BLUE,
(unsigned char)RenderItemStatusIcon::SIMULATION_OWNER);

View file

@ -25,6 +25,7 @@ enum class RenderItemStatusIcon {
PACKET_RECEIVED = 2,
SIMULATION_OWNER = 3,
HAS_ACTIONS = 4,
OTHER_SIMULATION_OWNER = 5,
NONE = 255
};

View file

@ -977,8 +977,8 @@ void RenderablePolyVoxEntityItem::compressVolumeDataAndSendEditPacket() {
properties.setVoxelDataDirty();
properties.setLastEdited(now);
EntitySimulation* simulation = tree ? tree->getSimulation() : nullptr;
PhysicalEntitySimulation* peSimulation = static_cast<PhysicalEntitySimulation*>(simulation);
EntitySimulationPointer simulation = tree ? tree->getSimulation() : nullptr;
PhysicalEntitySimulationPointer peSimulation = std::static_pointer_cast<PhysicalEntitySimulation>(simulation);
EntityEditPacketSender* packetSender = peSimulation ? peSimulation->getPacketSender() : nullptr;
if (packetSender) {
packetSender->queueEditEntityMessage(PacketType::EntityEdit, entity->getID(), properties);

View file

@ -51,7 +51,7 @@ void RenderableTextEntityItem::render(RenderArgs* args) {
}
if (getFaceCamera()) {
//rotate about vertical to face the camera
glm::vec3 dPosition = args->_viewFrustum->getPosition() - getPosition();
glm::vec3 dPosition = args->getViewFrustum().getPosition() - getPosition();
// If x and z are 0, atan(x, z) is undefined, so default to 0 degrees
float yawRotation = dPosition.x == 0.0f && dPosition.z == 0.0f ? 0.0f : glm::atan(dPosition.x, dPosition.z);
glm::quat orientation = glm::quat(glm::vec3(0.0f, yawRotation, 0.0f));

View file

@ -174,9 +174,14 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
#endif
if (!_webSurface) {
#if defined(Q_OS_LINUX)
// these don't seem to work on Linux
return;
#else
if (!buildWebSurface(static_cast<EntityTreeRenderer*>(args->_renderer))) {
return;
}
#endif
}
_lastRenderTime = usecTimestampNow();
@ -205,7 +210,7 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
}
DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, textured, culled, emissive);
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f));
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, 0.0f));
}
void RenderableWebEntityItem::setSourceUrl(const QString& value) {

View file

@ -20,6 +20,8 @@ class EntityItem;
class EntitySimulation;
using EntityItemPointer = std::shared_ptr<EntityItem>;
using EntityItemWeakPointer = std::weak_ptr<EntityItem>;
class EntitySimulation;
using EntitySimulationPointer = std::shared_ptr<EntitySimulation>;
enum EntityActionType {
// keep these synchronized with actionTypeFromString and actionTypeToString
@ -39,7 +41,7 @@ public:
bool isActive() { return _active; }
virtual void removeFromSimulation(EntitySimulation* simulation) const = 0;
virtual void removeFromSimulation(EntitySimulationPointer simulation) const = 0;
virtual EntityItemWeakPointer getOwnerEntity() const = 0;
virtual void setOwnerEntity(const EntityItemPointer ownerEntity) = 0;
virtual bool updateArguments(QVariantMap arguments) = 0;
@ -57,8 +59,6 @@ public:
virtual bool isMine() { return _isMine; }
virtual void setIsMine(bool value) { _isMine = value; }
bool locallyAddedButNotYetReceived = false;
virtual bool shouldSuppressLocationEdits() { return false; }
virtual void prepareForPhysicsSimulation() { }

View file

@ -89,7 +89,7 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) :
EntityItem::~EntityItem() {
// clear out any left-over actions
EntityTreePointer entityTree = _element ? _element->getTree() : nullptr;
EntitySimulation* simulation = entityTree ? entityTree->getSimulation() : nullptr;
EntitySimulationPointer simulation = entityTree ? entityTree->getSimulation() : nullptr;
if (simulation) {
clearActions(simulation);
}
@ -726,8 +726,13 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
READ_ENTITY_PROPERTY(PROP_DESCRIPTION, QString, setDescription);
READ_ENTITY_PROPERTY(PROP_ACTION_DATA, QByteArray, setActionData);
READ_ENTITY_PROPERTY(PROP_PARENT_ID, QUuid, setParentID);
READ_ENTITY_PROPERTY(PROP_PARENT_JOINT_INDEX, quint16, setParentJointIndex);
{ // parentID and parentJointIndex are also protected by simulation ownership
bool oldOverwrite = overwriteLocalData;
overwriteLocalData = overwriteLocalData && !weOwnSimulation;
READ_ENTITY_PROPERTY(PROP_PARENT_ID, QUuid, setParentID);
READ_ENTITY_PROPERTY(PROP_PARENT_JOINT_INDEX, quint16, setParentJointIndex);
overwriteLocalData = oldOverwrite;
}
READ_ENTITY_PROPERTY(PROP_QUERY_AA_CUBE, AACube, setQueryAACube);
@ -852,6 +857,23 @@ void EntityItem::setHref(QString value) {
_href = value;
}
void EntityItem::setCollisionSoundURL(const QString& value) {
if (_collisionSoundURL != value) {
_collisionSoundURL = value;
if (auto myTree = getTree()) {
myTree->notifyNewCollisionSoundURL(_collisionSoundURL, getEntityItemID());
}
}
}
SharedSoundPointer EntityItem::getCollisionSound() {
if (!_collisionSound) {
_collisionSound = DependencyManager::get<SoundCache>()->getSound(_collisionSoundURL);
}
return _collisionSound;
}
void EntityItem::simulate(const quint64& now) {
if (_lastSimulated == 0) {
_lastSimulated = now;
@ -1700,23 +1722,38 @@ void EntityItem::setPendingOwnershipPriority(quint8 priority, const quint64& tim
_simulationOwner.setPendingPriority(priority, timestamp);
}
bool EntityItem::addAction(EntitySimulation* simulation, EntityActionPointer action) {
QString EntityItem::actionsToDebugString() {
QString result;
QVector<QByteArray> serializedActions;
QHash<QUuid, EntityActionPointer>::const_iterator i = _objectActions.begin();
while (i != _objectActions.end()) {
const QUuid id = i.key();
EntityActionPointer action = _objectActions[id];
EntityActionType actionType = action->getType();
result += QString("") + actionType + ":" + action->getID().toString() + " ";
i++;
}
return result;
}
bool EntityItem::addAction(EntitySimulationPointer simulation, EntityActionPointer action) {
bool result;
withWriteLock([&] {
checkWaitingToRemove(simulation);
result = addActionInternal(simulation, action);
if (!result) {
removeActionInternal(action->getID());
if (result) {
action->setIsMine(true);
_actionDataDirty = true;
} else {
action->locallyAddedButNotYetReceived = true;
removeActionInternal(action->getID());
}
});
return result;
}
bool EntityItem::addActionInternal(EntitySimulation* simulation, EntityActionPointer action) {
bool EntityItem::addActionInternal(EntitySimulationPointer simulation, EntityActionPointer action) {
assert(action);
assert(simulation);
auto actionOwnerEntity = action->getOwnerEntity().lock();
@ -1740,7 +1777,7 @@ bool EntityItem::addActionInternal(EntitySimulation* simulation, EntityActionPoi
return success;
}
bool EntityItem::updateAction(EntitySimulation* simulation, const QUuid& actionID, const QVariantMap& arguments) {
bool EntityItem::updateAction(EntitySimulationPointer simulation, const QUuid& actionID, const QVariantMap& arguments) {
bool success = false;
withWriteLock([&] {
checkWaitingToRemove(simulation);
@ -1763,7 +1800,7 @@ bool EntityItem::updateAction(EntitySimulation* simulation, const QUuid& actionI
return success;
}
bool EntityItem::removeAction(EntitySimulation* simulation, const QUuid& actionID) {
bool EntityItem::removeAction(EntitySimulationPointer simulation, const QUuid& actionID) {
bool success = false;
withWriteLock([&] {
checkWaitingToRemove(simulation);
@ -1772,7 +1809,7 @@ bool EntityItem::removeAction(EntitySimulation* simulation, const QUuid& actionI
return success;
}
bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* simulation) {
bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPointer simulation) {
_previouslyDeletedActions.insert(actionID, usecTimestampNow());
if (_objectActions.contains(actionID)) {
if (!simulation) {
@ -1783,6 +1820,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* s
EntityActionPointer action = _objectActions[actionID];
action->setOwnerEntity(nullptr);
action->setIsMine(false);
_objectActions.remove(actionID);
if (simulation) {
@ -1798,7 +1836,7 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulation* s
return false;
}
bool EntityItem::clearActions(EntitySimulation* simulation) {
bool EntityItem::clearActions(EntitySimulationPointer simulation) {
withWriteLock([&] {
QHash<QUuid, EntityActionPointer>::iterator i = _objectActions.begin();
while (i != _objectActions.end()) {
@ -1834,7 +1872,7 @@ void EntityItem::deserializeActionsInternal() {
EntityTreePointer entityTree = getTree();
assert(entityTree);
EntitySimulation* simulation = entityTree ? entityTree->getSimulation() : nullptr;
EntitySimulationPointer simulation = entityTree ? entityTree->getSimulation() : nullptr;
assert(simulation);
QVector<QByteArray> serializedActions;
@ -1863,7 +1901,6 @@ void EntityItem::deserializeActionsInternal() {
if (!action->isMine()) {
action->deserialize(serializedAction);
}
action->locallyAddedButNotYetReceived = false;
updated << actionID;
} else {
auto actionFactory = DependencyManager::get<EntityActionFactoryInterface>();
@ -1871,7 +1908,6 @@ void EntityItem::deserializeActionsInternal() {
EntityActionPointer action = actionFactory->factoryBA(entity, serializedAction);
if (action) {
entity->addActionInternal(simulation, action);
action->locallyAddedButNotYetReceived = false;
updated << actionID;
} else {
static QString repeatedMessage =
@ -1889,8 +1925,12 @@ void EntityItem::deserializeActionsInternal() {
QUuid id = i.key();
if (!updated.contains(id)) {
EntityActionPointer action = i.value();
// if we've just added this action, don't remove it due to lack of mention in an incoming packet.
if (! action->locallyAddedButNotYetReceived) {
if (action->isMine()) {
// we just received an update that didn't include one of our actions. tell the server about it (again).
setActionDataNeedsTransmit(true);
} else {
// don't let someone else delete my action.
_actionsToRemove << id;
_previouslyDeletedActions.insert(id, now);
}
@ -1912,7 +1952,7 @@ void EntityItem::deserializeActionsInternal() {
return;
}
void EntityItem::checkWaitingToRemove(EntitySimulation* simulation) {
void EntityItem::checkWaitingToRemove(EntitySimulationPointer simulation) {
foreach(QUuid actionID, _actionsToRemove) {
removeActionInternal(actionID, simulation);
}

View file

@ -24,6 +24,7 @@
#include <PhysicsCollisionGroups.h>
#include <ShapeInfo.h>
#include <Transform.h>
#include <Sound.h>
#include <SpatiallyNestable.h>
#include "EntityItemID.h"
@ -250,7 +251,10 @@ public:
void setScriptTimestamp(const quint64 value) { _scriptTimestamp = value; }
const QString& getCollisionSoundURL() const { return _collisionSoundURL; }
void setCollisionSoundURL(const QString& value) { _collisionSoundURL = value; }
void setCollisionSoundURL(const QString& value);
SharedSoundPointer getCollisionSound();
void setCollisionSound(SharedSoundPointer sound) { _collisionSound = sound; }
const glm::vec3& getRegistrationPoint() const { return _registrationPoint; } /// registration point as ratio of entity
@ -378,10 +382,11 @@ public:
void grabSimulationOwnership();
void flagForMotionStateChange() { _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; }
bool addAction(EntitySimulation* simulation, EntityActionPointer action);
bool updateAction(EntitySimulation* simulation, const QUuid& actionID, const QVariantMap& arguments);
bool removeAction(EntitySimulation* simulation, const QUuid& actionID);
bool clearActions(EntitySimulation* simulation);
QString actionsToDebugString();
bool addAction(EntitySimulationPointer simulation, EntityActionPointer action);
bool updateAction(EntitySimulationPointer simulation, const QUuid& actionID, const QVariantMap& arguments);
bool removeAction(EntitySimulationPointer simulation, const QUuid& actionID);
bool clearActions(EntitySimulationPointer simulation);
void setActionData(QByteArray actionData);
const QByteArray getActionData() const;
bool hasActions() const { return !_objectActions.empty(); }
@ -478,6 +483,7 @@ protected:
quint64 _loadedScriptTimestamp{ ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP + 1 };
QString _collisionSoundURL;
SharedSoundPointer _collisionSound;
glm::vec3 _registrationPoint;
float _angularDamping;
bool _visible;
@ -516,8 +522,8 @@ protected:
void* _physicsInfo = nullptr; // set by EntitySimulation
bool _simulated; // set by EntitySimulation
bool addActionInternal(EntitySimulation* simulation, EntityActionPointer action);
bool removeActionInternal(const QUuid& actionID, EntitySimulation* simulation = nullptr);
bool addActionInternal(EntitySimulationPointer simulation, EntityActionPointer action);
bool removeActionInternal(const QUuid& actionID, EntitySimulationPointer simulation = nullptr);
void deserializeActionsInternal();
void serializeActions(bool& success, QByteArray& result) const;
QHash<QUuid, EntityActionPointer> _objectActions;
@ -528,7 +534,7 @@ protected:
// when an entity-server starts up, EntityItem::setActionData is called before the entity-tree is
// ready. This means we can't find our EntityItemPointer or add the action to the simulation. These
// are used to keep track of and work around this situation.
void checkWaitingToRemove(EntitySimulation* simulation = nullptr);
void checkWaitingToRemove(EntitySimulationPointer simulation = nullptr);
mutable QSet<QUuid> _actionsToRemove;
mutable bool _actionDataDirty = false;
mutable bool _actionDataNeedsTransmit = false;

View file

@ -1615,7 +1615,7 @@ void EntityItemProperties::setSimulationOwner(const QByteArray& data) {
QList<QString> EntityItemProperties::listChangedProperties() {
QList<QString> out;
if (containsPositionChange()) {
out += "posistion";
out += "position";
}
if (dimensionsChanged()) {
out += "dimensions";

View file

@ -766,7 +766,7 @@ bool EntityScriptingInterface::appendPoint(QUuid entityID, const glm::vec3& poin
bool EntityScriptingInterface::actionWorker(const QUuid& entityID,
std::function<bool(EntitySimulation*, EntityItemPointer)> actor) {
std::function<bool(EntitySimulationPointer, EntityItemPointer)> actor) {
if (!_entityTree) {
return false;
}
@ -774,7 +774,7 @@ bool EntityScriptingInterface::actionWorker(const QUuid& entityID,
EntityItemPointer entity;
bool doTransmit = false;
_entityTree->withWriteLock([&] {
EntitySimulation* simulation = _entityTree->getSimulation();
EntitySimulationPointer simulation = _entityTree->getSimulation();
entity = _entityTree->findEntityByEntityItemID(entityID);
if (!entity) {
qDebug() << "actionWorker -- unknown entity" << entityID;
@ -815,7 +815,7 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
QUuid actionID = QUuid::createUuid();
auto actionFactory = DependencyManager::get<EntityActionFactoryInterface>();
bool success = false;
actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
actionWorker(entityID, [&](EntitySimulationPointer simulation, EntityItemPointer entity) {
// create this action even if the entity doesn't have physics info. it will often be the
// case that a script adds an action immediately after an object is created, and the physicsInfo
// is computed asynchronously.
@ -843,7 +843,7 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
bool EntityScriptingInterface::updateAction(const QUuid& entityID, const QUuid& actionID, const QVariantMap& arguments) {
return actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
return actionWorker(entityID, [&](EntitySimulationPointer simulation, EntityItemPointer entity) {
bool success = entity->updateAction(simulation, actionID, arguments);
if (success) {
entity->grabSimulationOwnership();
@ -854,7 +854,7 @@ bool EntityScriptingInterface::updateAction(const QUuid& entityID, const QUuid&
bool EntityScriptingInterface::deleteAction(const QUuid& entityID, const QUuid& actionID) {
bool success = false;
actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
actionWorker(entityID, [&](EntitySimulationPointer simulation, EntityItemPointer entity) {
success = entity->removeAction(simulation, actionID);
if (success) {
// reduce from grab to poke
@ -867,7 +867,7 @@ bool EntityScriptingInterface::deleteAction(const QUuid& entityID, const QUuid&
QVector<QUuid> EntityScriptingInterface::getActionIDs(const QUuid& entityID) {
QVector<QUuid> result;
actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
actionWorker(entityID, [&](EntitySimulationPointer simulation, EntityItemPointer entity) {
QList<QUuid> actionIDs = entity->getActionIDs();
result = QVector<QUuid>::fromList(actionIDs);
return false; // don't send an edit packet
@ -877,7 +877,7 @@ QVector<QUuid> EntityScriptingInterface::getActionIDs(const QUuid& entityID) {
QVariantMap EntityScriptingInterface::getActionArguments(const QUuid& entityID, const QUuid& actionID) {
QVariantMap result;
actionWorker(entityID, [&](EntitySimulation* simulation, EntityItemPointer entity) {
actionWorker(entityID, [&](EntitySimulationPointer simulation, EntityItemPointer entity) {
result = entity->getActionArguments(actionID);
return false; // don't send an edit packet
});

View file

@ -200,11 +200,11 @@ signals:
void debitEnergySource(float value);
private:
bool actionWorker(const QUuid& entityID, std::function<bool(EntitySimulation*, EntityItemPointer)> actor);
bool actionWorker(const QUuid& entityID, std::function<bool(EntitySimulationPointer, EntityItemPointer)> actor);
bool setVoxels(QUuid entityID, std::function<bool(PolyVoxEntityItem&)> actor);
bool setPoints(QUuid entityID, std::function<bool(LineEntityItem&)> actor);
void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties);
EntityItemPointer checkForTreeEntityAndTypeMatch(const QUuid& entityID,
EntityTypes::EntityType entityType = EntityTypes::Unknown);

View file

@ -64,7 +64,7 @@ void EntitySimulation::prepareEntityForDelete(EntityItemPointer entity) {
assert(entity->isDead());
if (entity->isSimulated()) {
QMutexLocker lock(&_mutex);
entity->clearActions(this);
entity->clearActions(getThisPointer());
removeEntityInternal(entity);
_entitiesToDelete.insert(entity);
}

View file

@ -22,8 +22,9 @@
#include "EntityItem.h"
#include "EntityTree.h"
typedef QSet<EntityItemPointer> SetOfEntities;
typedef QVector<EntityItemPointer> VectorOfEntities;
using EntitySimulationPointer = std::shared_ptr<EntitySimulation>;
using SetOfEntities = QSet<EntityItemPointer>;
using VectorOfEntities = QVector<EntityItemPointer>;
// the EntitySimulation needs to know when these things change on an entity,
// so it can sort EntityItem or relay its state to the PhysicsEngine.
@ -41,12 +42,16 @@ const int DIRTY_SIMULATION_FLAGS =
Simulation::DIRTY_MATERIAL |
Simulation::DIRTY_SIMULATOR_ID;
class EntitySimulation : public QObject {
class EntitySimulation : public QObject, public std::enable_shared_from_this<EntitySimulation> {
Q_OBJECT
public:
EntitySimulation() : _mutex(QMutex::Recursive), _entityTree(NULL), _nextExpiry(quint64(-1)) { }
virtual ~EntitySimulation() { setEntityTree(NULL); }
inline EntitySimulationPointer getThisPointer() const {
return std::const_pointer_cast<EntitySimulation>(shared_from_this());
}
/// \param tree pointer to EntityTree which is stored internally
void setEntityTree(EntityTreePointer tree);

View file

@ -95,7 +95,6 @@ void EntityTree::postAddEntity(EntityItemPointer entity) {
}
_isDirty = true;
maybeNotifyNewCollisionSoundURL("", entity->getCollisionSoundURL());
emit addingEntity(entity->getEntityItemID());
// find and hook up any entities with this entity as a (previously) missing parent
@ -213,6 +212,8 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
properties.setVelocityChanged(false);
properties.setAngularVelocityChanged(false);
properties.setAccelerationChanged(false);
properties.setParentIDChanged(false);
properties.setParentJointIndexChanged(false);
if (wantTerseEditLogging()) {
qCDebug(entities) << (senderNode ? senderNode->getUUID() : "null") << "physical edits suppressed";
@ -223,7 +224,6 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
QString entityScriptBefore = entity->getScript();
quint64 entityScriptTimestampBefore = entity->getScriptTimestamp();
QString collisionSoundURLBefore = entity->getCollisionSoundURL();
uint32_t preFlags = entity->getDirtyFlags();
AACube newQueryAACube;
@ -295,7 +295,6 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
if (entityScriptBefore != entityScriptAfter || reload) {
emitEntityScriptChanging(entity->getEntityItemID(), reload); // the entity script has changed
}
maybeNotifyNewCollisionSoundURL(collisionSoundURLBefore, entity->getCollisionSoundURL());
}
// TODO: this final containingElement check should eventually be removed (or wrapped in an #ifdef DEBUG).
@ -362,13 +361,11 @@ void EntityTree::emitEntityScriptChanging(const EntityItemID& entityItemID, cons
emit entityScriptChanging(entityItemID, reload);
}
void EntityTree::maybeNotifyNewCollisionSoundURL(const QString& previousCollisionSoundURL, const QString& nextCollisionSoundURL) {
if (!nextCollisionSoundURL.isEmpty() && (nextCollisionSoundURL != previousCollisionSoundURL)) {
emit newCollisionSoundURL(QUrl(nextCollisionSoundURL));
}
void EntityTree::notifyNewCollisionSoundURL(const QString& newURL, const EntityItemID& entityID) {
emit newCollisionSoundURL(QUrl(newURL), entityID);
}
void EntityTree::setSimulation(EntitySimulation* simulation) {
void EntityTree::setSimulation(EntitySimulationPointer simulation) {
this->withWriteLock([&] {
if (simulation) {
// assert that the simulation's backpointer has already been properly connected
@ -848,6 +845,14 @@ void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList<Q
QString::number((int)center.y) + "," +
QString::number((int)center.z);
}
if (properties.positionChanged()) {
int index = changedProperties.indexOf("position");
glm::vec3 pos = properties.getPosition();
changedProperties[index] = QString("position:") +
QString::number((int)pos.x) + "," +
QString::number((int)pos.y) + "," +
QString::number((int)pos.z);
}
}
int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength,

View file

@ -194,8 +194,8 @@ public:
void emitEntityScriptChanging(const EntityItemID& entityItemID, const bool reload);
void setSimulation(EntitySimulation* simulation);
EntitySimulation* getSimulation() const { return _simulation; }
void setSimulation(EntitySimulationPointer simulation);
EntitySimulationPointer getSimulation() const { return _simulation; }
bool wantEditLogging() const { return _wantEditLogging; }
void setWantEditLogging(bool value) { _wantEditLogging = value; }
@ -249,6 +249,8 @@ public:
void forgetAvatarID(QUuid avatarID) { _avatarIDs -= avatarID; }
void deleteDescendantsOfAvatar(QUuid avatarID);
void notifyNewCollisionSoundURL(const QString& newCollisionSoundURL, const EntityItemID& entityID);
public slots:
void callLoader(EntityItemID entityID);
@ -256,7 +258,7 @@ signals:
void deletingEntity(const EntityItemID& entityID);
void addingEntity(const EntityItemID& entityID);
void entityScriptChanging(const EntityItemID& entityItemID, const bool reload);
void newCollisionSoundURL(const QUrl& url);
void newCollisionSoundURL(const QUrl& url, const EntityItemID& entityID);
void clearingEntities();
protected:
@ -297,11 +299,10 @@ protected:
mutable QReadWriteLock _entityToElementLock;
QHash<EntityItemID, EntityTreeElementPointer> _entityToElementMap;
EntitySimulation* _simulation;
EntitySimulationPointer _simulation;
bool _wantEditLogging = false;
bool _wantTerseEditLogging = false;
void maybeNotifyNewCollisionSoundURL(const QString& oldCollisionSoundURL, const QString& newCollisionSoundURL);
// some performance tracking properties - only used in server trees

View file

@ -13,6 +13,7 @@
#include <FBXReader.h>
#include <GeometryUtil.h>
#include <OctreeUtils.h>
#include "EntitiesLogging.h"
#include "EntityItemProperties.h"
@ -34,8 +35,6 @@ OctreeElementPointer EntityTreeElement::createNewElement(unsigned char* octalCod
return newChild;
}
void EntityTreeElement::init(unsigned char* octalCode) {
OctreeElement::init(octalCode);
_octreeMemoryUsage += sizeof(EntityTreeElement);
@ -297,7 +296,7 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData
entityTreeElementExtraEncodeData->entities.contains(entity->getEntityItemID());
}
if (includeThisEntity && params.viewFrustum) {
if (includeThisEntity || params.recurseEverything) {
// we want to use the maximum possible box for this, so that we don't have to worry about the nuance of
// simulation changing what's visible. consider the case where the entity contains an angular velocity
@ -305,7 +304,7 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData
// frustum culling on rendering.
bool success;
AACube entityCube = entity->getQueryAACube(success);
if (!success || !params.viewFrustum->cubeIntersectsKeyhole(entityCube)) {
if (!success || !params.viewFrustum.cubeIntersectsKeyhole(entityCube)) {
includeThisEntity = false; // out of view, don't include it
} else {
// Check the size of the entity, it's possible that a "too small to see" entity is included in a
@ -322,9 +321,10 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData
// AABox. If this happens, fall back to the queryAACube.
entityBounds = AABox(entityCube);
}
auto renderAccuracy = params.viewFrustum->calculateRenderAccuracy(entityBounds,
params.octreeElementSizeScale,
params.boundaryLevelAdjust);
auto renderAccuracy = calculateRenderAccuracy(params.viewFrustum.getPosition(),
entityBounds,
params.octreeElementSizeScale,
params.boundaryLevelAdjust);
if (renderAccuracy <= 0.0f) {
includeThisEntity = false; // too small, don't include it

View file

@ -22,7 +22,7 @@ EntityTreeHeadlessViewer::~EntityTreeHeadlessViewer() {
void EntityTreeHeadlessViewer::init() {
OctreeHeadlessViewer::init();
if (!_simulation) {
SimpleEntitySimulation* simpleSimulation = new SimpleEntitySimulation();
SimpleEntitySimulationPointer simpleSimulation { new SimpleEntitySimulation() };
EntityTreePointer entityTree = std::static_pointer_cast<EntityTree>(_tree);
simpleSimulation->setEntityTree(entityTree);
entityTree->setSimulation(simpleSimulation);

View file

@ -49,7 +49,7 @@ protected:
return newTree;
}
EntitySimulation* _simulation;
EntitySimulationPointer _simulation;
};
#endif // hifi_EntityTreeHeadlessViewer_h

View file

@ -14,6 +14,10 @@
#include "EntitySimulation.h"
class SimpleEntitySimulation;
using SimpleEntitySimulationPointer = std::shared_ptr<SimpleEntitySimulation>;
/// provides simple velocity + gravity extrapolation of EntityItem's
class SimpleEntitySimulation : public EntitySimulation {

View file

@ -892,7 +892,10 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
properties = true;
propertyName = "P";
index = 4;
} else if (subobject.name == "ShadingModel") {
material.shadingModel = subobject.properties.at(0).toString();
}
if (properties) {
std::vector<std::string> unknowns;
foreach(const FBXNode& property, subobject.children) {
@ -988,7 +991,6 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
QString propname = subobject.name.data();
int unknown = 0;
if ( (propname == "Version")
||(propname == "ShadingModel")
||(propname == "Multilayer")) {
} else {
unknown++;
@ -1130,7 +1132,6 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
ambientTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
} else if (type.contains("tex_ao_map")) {
occlusionTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
} else if (type == "lcl rotation") {
localRotations.insert(getID(connection.properties, 2), getID(connection.properties, 1));
} else if (type == "lcl translation") {

View file

@ -154,6 +154,7 @@ public:
QString materialID;
QString name;
QString shadingModel;
model::MaterialPointer _material;
FBXTexture normalTexture;

View file

@ -72,6 +72,10 @@ void FBXReader::consolidateFBXMaterials() {
// foreach (const QString& materialID, materials) {
for (QHash<QString, FBXMaterial>::iterator it = _fbxMaterials.begin(); it != _fbxMaterials.end(); it++) {
FBXMaterial& material = (*it);
// Maya is the exporting the shading model and we aretrying to use it
bool isMaterialLambert = (material.shadingModel.toLower() == "lambert");
// the pure material associated with this part
bool detectDifferentUVs = false;
FBXTexture diffuseTexture;
@ -171,6 +175,13 @@ void FBXReader::consolidateFBXMaterials() {
emissiveTexture = getTexture(emissiveTextureID);
detectDifferentUVs |= (emissiveTexture.texcoordSet != 0) || (!emissiveTexture.transform.isIdentity());
material.emissiveTexture = emissiveTexture;
if (isMaterialLambert) {
// If the emissiveTextureID comes from the Texture bound to Emissive when material is lambert, we know it s exported from maya
// And the EMissiveColor is forced to 0.5 by Maya which is bad
// So we need to force it to 1.0
material.emissiveColor = vec3(1.0);
}
}
FBXTexture occlusionTexture;
@ -198,7 +209,7 @@ void FBXReader::consolidateFBXMaterials() {
material._material = std::make_shared<model::Material>();
// Emissive color is the mix of emissiveColor with emissiveFactor
auto emissive = material.emissiveColor * material.emissiveFactor;
auto emissive = material.emissiveColor * (isMaterialLambert ? 1.0f : material.emissiveFactor); // In lambert there is not emissiveFactor
material._material->setEmissive(emissive);
// Final diffuse color is the mix of diffuseColor with diffuseFactor
@ -212,6 +223,18 @@ void FBXReader::consolidateFBXMaterials() {
material._material->setRoughness(model::Material::shininessToRoughness(material.shininess));
float metallic = std::max(material.specularColor.x, std::max(material.specularColor.y, material.specularColor.z));
material._material->setMetallic(metallic);
if (isMaterialLambert) {
if (!material._material->getKey().isAlbedo()) {
// switch emissive to material albedo as we tag the material to unlit
material._material->setUnlit(true);
material._material->setAlbedo(emissive);
if (!material.emissiveTexture.isNull()) {
material.albedoTexture = material.emissiveTexture;
}
}
}
}
if (material.opacity <= 0.0f) {

View file

@ -17,8 +17,15 @@ QOpenGLContext* QOpenGLContextWrapper::currentContext() {
return QOpenGLContext::currentContext();
}
QOpenGLContextWrapper::QOpenGLContextWrapper() :
_context(new QOpenGLContext)
_context(new QOpenGLContext)
{
}
QOpenGLContextWrapper::QOpenGLContextWrapper(QOpenGLContext* context) :
_context(context)
{
}
@ -50,3 +57,7 @@ bool isCurrentContext(QOpenGLContext* context) {
return QOpenGLContext::currentContext() == context;
}
void QOpenGLContextWrapper::moveToThread(QThread* thread) {
_context->moveToThread(thread);
}

View file

@ -15,16 +15,19 @@
class QOpenGLContext;
class QSurface;
class QSurfaceFormat;
class QThread;
class QOpenGLContextWrapper {
public:
QOpenGLContextWrapper();
QOpenGLContextWrapper(QOpenGLContext* context);
void setFormat(const QSurfaceFormat& format);
bool create();
void swapBuffers(QSurface* surface);
bool makeCurrent(QSurface* surface);
void doneCurrent();
void setShareContext(QOpenGLContext* otherContext);
void moveToThread(QThread* thread);
static QOpenGLContext* currentContext();

View file

@ -20,8 +20,12 @@
const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse";
void KeyboardMouseDevice::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) {
_inputDevice->update(deltaTime, inputCalibrationData, jointsCaptured);
void KeyboardMouseDevice::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData, bool jointsCaptured) {
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
userInputMapper->withLock([&, this]() {
_inputDevice->update(deltaTime, inputCalibrationData, jointsCaptured);
});
// For touch event, we need to check that the last event is not too long ago
// Maybe it's a Qt issue, but the touch event sequence (begin, update, end) is not always called properly

View file

@ -71,7 +71,7 @@ void GeometryMappingResource::downloadFinished(const QByteArray& data) {
GeometryExtra extra{ mapping, _textureBaseUrl };
// Get the raw GeometryResource, not the wrapped NetworkGeometry
_geometryResource = modelCache->getResource(url, QUrl(), false, &extra).staticCast<GeometryResource>();
_geometryResource = modelCache->getResource(url, QUrl(), &extra).staticCast<GeometryResource>();
// Avoid caching nested resources - their references will be held by the parent
_geometryResource->_isCacheable = false;
@ -236,7 +236,7 @@ ModelCache::ModelCache() {
}
QSharedPointer<Resource> ModelCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
bool delayLoad, const void* extra) {
const void* extra) {
Resource* resource = nullptr;
if (url.path().toLower().endsWith(".fst")) {
resource = new GeometryMappingResource(url);
@ -252,7 +252,7 @@ QSharedPointer<Resource> ModelCache::createResource(const QUrl& url, const QShar
std::shared_ptr<NetworkGeometry> ModelCache::getGeometry(const QUrl& url, const QVariantHash& mapping, const QUrl& textureBaseUrl) {
GeometryExtra geometryExtra = { mapping, textureBaseUrl };
GeometryResource::Pointer resource = getResource(url, QUrl(), true, &geometryExtra).staticCast<GeometryResource>();
GeometryResource::Pointer resource = getResource(url, QUrl(), &geometryExtra).staticCast<GeometryResource>();
if (resource) {
if (resource->isLoaded() && resource->shouldSetTextures()) {
resource->setTextures();

View file

@ -44,8 +44,8 @@ public:
protected:
friend class GeometryMappingResource;
virtual QSharedPointer<Resource> createResource(const QUrl& url,
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra);
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
const void* extra);
private:
ModelCache();

View file

@ -7,11 +7,8 @@
//
#include "ShaderCache.h"
NetworkShader::NetworkShader(const QUrl& url, bool delayLoad)
: Resource(url, delayLoad)
{
}
NetworkShader::NetworkShader(const QUrl& url) :
Resource(url) {}
void NetworkShader::downloadFinished(const QByteArray& data) {
_source = QString::fromUtf8(data);
@ -24,10 +21,11 @@ ShaderCache& ShaderCache::instance() {
}
NetworkShaderPointer ShaderCache::getShader(const QUrl& url) {
return ResourceCache::getResource(url, QUrl(), false, nullptr).staticCast<NetworkShader>();
return ResourceCache::getResource(url, QUrl(), nullptr).staticCast<NetworkShader>();
}
QSharedPointer<Resource> ShaderCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
return QSharedPointer<Resource>(new NetworkShader(url, delayLoad), &Resource::deleter);
QSharedPointer<Resource> ShaderCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
const void* extra) {
return QSharedPointer<Resource>(new NetworkShader(url), &Resource::deleter);
}

View file

@ -13,7 +13,7 @@
class NetworkShader : public Resource {
public:
NetworkShader(const QUrl& url, bool delayLoad);
NetworkShader(const QUrl& url);
virtual void downloadFinished(const QByteArray& data) override;
QString _source;
@ -28,7 +28,8 @@ public:
NetworkShaderPointer getShader(const QUrl& url);
protected:
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) override;
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
const void* extra) override;
};
#endif

View file

@ -167,7 +167,7 @@ ScriptableResource* TextureCache::prefetch(const QUrl& url, int type) {
NetworkTexturePointer TextureCache::getTexture(const QUrl& url, Type type, const QByteArray& content) {
TextureExtra extra = { type, content };
return ResourceCache::getResource(url, QUrl(), content.isEmpty(), &extra).staticCast<NetworkTexture>();
return ResourceCache::getResource(url, QUrl(), &extra).staticCast<NetworkTexture>();
}
@ -231,8 +231,8 @@ gpu::TexturePointer TextureCache::getImageTexture(const QString& path, Type type
return gpu::TexturePointer(loader(image, QUrl::fromLocalFile(path).fileName().toStdString()));
}
QSharedPointer<Resource> TextureCache::createResource(const QUrl& url,
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
QSharedPointer<Resource> TextureCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
const void* extra) {
const TextureExtra* textureExtra = static_cast<const TextureExtra*>(extra);
auto type = textureExtra ? textureExtra->type : Type::DEFAULT_TEXTURE;
auto content = textureExtra ? textureExtra->content : QByteArray();
@ -241,7 +241,7 @@ QSharedPointer<Resource> TextureCache::createResource(const QUrl& url,
}
NetworkTexture::NetworkTexture(const QUrl& url, Type type, const QByteArray& content) :
Resource(url, !content.isEmpty()),
Resource(url),
_type(type)
{
_textureSource = std::make_shared<gpu::TextureSource>();

View file

@ -131,8 +131,8 @@ protected:
// Overload ResourceCache::prefetch to allow specifying texture type for loads
Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, int type);
virtual QSharedPointer<Resource> createResource(const QUrl& url,
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra);
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
const void* extra);
private:
TextureCache();

View file

@ -74,6 +74,11 @@ void Material::setOpacity(float opacity) {
_schemaBuffer.edit<Schema>()._opacity = opacity;
}
void Material::setUnlit(bool value) {
_key.setUnlit(value);
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
}
void Material::setAlbedo(const Color& albedo, bool isSRGB) {
_key.setAlbedo(glm::any(glm::greaterThan(albedo, Color(0.0f))));
_schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();

View file

@ -28,14 +28,15 @@ class MaterialKey {
public:
enum FlagBit {
EMISSIVE_VAL_BIT = 0,
UNLIT_VAL_BIT,
ALBEDO_VAL_BIT,
METALLIC_VAL_BIT,
GLOSSY_VAL_BIT,
OPACITY_VAL_BIT,
OPACITY_MASK_MAP_BIT, // OPacity Map and Opacity MASK map are mutually exclusive
OPACITY_MASK_MAP_BIT, // Opacity Map and Opacity MASK map are mutually exclusive
OPACITY_TRANSLUCENT_MAP_BIT,
// THe map bits must be in the smae sequence as the enum names for the map channels
// THe map bits must be in the same sequence as the enum names for the map channels
EMISSIVE_MAP_BIT,
ALBEDO_MAP_BIT,
METALLIC_MAP_BIT,
@ -74,9 +75,12 @@ public:
MaterialKey build() const { return MaterialKey(_flags); }
Builder& withEmissive() { _flags.set(EMISSIVE_VAL_BIT); return (*this); }
Builder& withUnlit() { _flags.set(UNLIT_VAL_BIT); return (*this); }
Builder& withAlbedo() { _flags.set(ALBEDO_VAL_BIT); return (*this); }
Builder& withMetallic() { _flags.set(METALLIC_VAL_BIT); return (*this); }
Builder& withGlossy() { _flags.set(GLOSSY_VAL_BIT); return (*this); }
Builder& withTranslucentFactor() { _flags.set(OPACITY_VAL_BIT); return (*this); }
Builder& withEmissiveMap() { _flags.set(EMISSIVE_MAP_BIT); return (*this); }
@ -98,6 +102,9 @@ public:
void setEmissive(bool value) { _flags.set(EMISSIVE_VAL_BIT, value); }
bool isEmissive() const { return _flags[EMISSIVE_VAL_BIT]; }
void setUnlit(bool value) { _flags.set(UNLIT_VAL_BIT, value); }
bool isUnlit() const { return _flags[UNLIT_VAL_BIT]; }
void setEmissiveMap(bool value) { _flags.set(EMISSIVE_MAP_BIT, value); }
bool isEmissiveMap() const { return _flags[EMISSIVE_MAP_BIT]; }
@ -172,6 +179,9 @@ public:
Builder& withoutEmissiveMap() { _value.reset(MaterialKey::EMISSIVE_MAP_BIT); _mask.set(MaterialKey::EMISSIVE_MAP_BIT); return (*this); }
Builder& withEmissiveMap() { _value.set(MaterialKey::EMISSIVE_MAP_BIT); _mask.set(MaterialKey::EMISSIVE_MAP_BIT); return (*this); }
Builder& withoutUnlit() { _value.reset(MaterialKey::UNLIT_VAL_BIT); _mask.set(MaterialKey::UNLIT_VAL_BIT); return (*this); }
Builder& withUnlit() { _value.set(MaterialKey::UNLIT_VAL_BIT); _mask.set(MaterialKey::UNLIT_VAL_BIT); return (*this); }
Builder& withoutAlbedo() { _value.reset(MaterialKey::ALBEDO_VAL_BIT); _mask.set(MaterialKey::ALBEDO_VAL_BIT); return (*this); }
Builder& withAlbedo() { _value.set(MaterialKey::ALBEDO_VAL_BIT); _mask.set(MaterialKey::ALBEDO_VAL_BIT); return (*this); }
@ -250,6 +260,9 @@ public:
void setOpacity(float opacity);
float getOpacity() const { return _schemaBuffer.get<Schema>()._opacity; }
void setUnlit(bool value);
bool isUnlit() const { return _key.isUnlit(); }
void setAlbedo(const Color& albedo, bool isSRGB = true);
Color getAlbedo(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_schemaBuffer.get<Schema>()._albedo) : _schemaBuffer.get<Schema>()._albedo); }

View file

@ -40,20 +40,21 @@ float getMaterialShininess(Material m) { return 1.0 - getMaterialRoughness(m); }
int getMaterialKey(Material m) { return floatBitsToInt(m._spareKey.w); }
const int EMISSIVE_VAL_BIT = 0x00000001;
const int ALBEDO_VAL_BIT = 0x00000002;
const int METALLIC_VAL_BIT = 0x00000004;
const int GLOSSY_VAL_BIT = 0x00000008;
const int OPACITY_VAL_BIT = 0x00000010;
const int OPACITY_MASK_MAP_BIT = 0x00000020;
const int OPACITY_TRANSLUCENT_MAP_BIT = 0x00000040;
const int UNLIT_VAL_BIT = 0x00000002;
const int ALBEDO_VAL_BIT = 0x00000004;
const int METALLIC_VAL_BIT = 0x00000008;
const int GLOSSY_VAL_BIT = 0x00000010;
const int OPACITY_VAL_BIT = 0x00000020;
const int OPACITY_MASK_MAP_BIT = 0x00000040;
const int OPACITY_TRANSLUCENT_MAP_BIT = 0x00000080;
const int EMISSIVE_MAP_BIT = 0x00000080;
const int ALBEDO_MAP_BIT = 0x00000100;
const int METALLIC_MAP_BIT = 0x00000200;
const int ROUGHNESS_MAP_BIT = 0x00000400;
const int NORMAL_MAP_BIT = 0x00000800;
const int OCCLUSION_MAP_BIT = 0x00001000;
const int LIGHTMAP_MAP_BIT = 0x00002000;
const int EMISSIVE_MAP_BIT = 0x00000100;
const int ALBEDO_MAP_BIT = 0x00000200;
const int METALLIC_MAP_BIT = 0x00000400;
const int ROUGHNESS_MAP_BIT = 0x00000800;
const int NORMAL_MAP_BIT = 0x00001000;
const int OCCLUSION_MAP_BIT = 0x00002000;
const int LIGHTMAP_MAP_BIT = 0x00004000;
<@endif@>

View file

@ -105,19 +105,19 @@ const QImage& image, bool isLinear, bool doCompress) {
gpu::Semantic gpuSemantic;
gpu::Semantic mipSemantic;
if (isLinear) {
mipSemantic = gpu::SBGRA;
if (doCompress) {
gpuSemantic = gpu::COMPRESSED_SRGBA;
} else {
gpuSemantic = gpu::SRGBA;
}
} else {
mipSemantic = gpu::BGRA;
if (doCompress) {
gpuSemantic = gpu::COMPRESSED_RGBA;
} else {
gpuSemantic = gpu::RGBA;
}
} else {
mipSemantic = gpu::SBGRA;
if (doCompress) {
gpuSemantic = gpu::COMPRESSED_SRGBA;
} else {
gpuSemantic = gpu::SRGBA;
}
}
formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, gpuSemantic);
formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, mipSemantic);
@ -125,19 +125,19 @@ const QImage& image, bool isLinear, bool doCompress) {
gpu::Semantic gpuSemantic;
gpu::Semantic mipSemantic;
if (isLinear) {
mipSemantic = gpu::SRGB;
if (doCompress) {
gpuSemantic = gpu::COMPRESSED_SRGB;
} else {
gpuSemantic = gpu::SRGB;
}
} else {
mipSemantic = gpu::RGB;
if (doCompress) {
gpuSemantic = gpu::COMPRESSED_RGB;
} else {
gpuSemantic = gpu::RGB;
}
} else {
mipSemantic = gpu::SRGB;
if (doCompress) {
gpuSemantic = gpu::COMPRESSED_SRGB;
} else {
gpuSemantic = gpu::SRGB;
}
}
formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, gpuSemantic);
formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, mipSemantic);
@ -171,14 +171,6 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag
if (generateMips) {
theTexture->autoGenerateMips(-1);
auto levels = theTexture->maxMip();
uvec2 size(image.width(), image.height());
for (uint8_t i = 1; i <= levels; ++i) {
size >>= 1;
size = glm::max(size, uvec2(1));
image = image.scaled(size.x, size.y, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
theTexture->assignStoredMip(i, formatMip, image.byteCount(), image.constBits());
}
}
}
@ -186,20 +178,20 @@ gpu::Texture* TextureUsage::process2DTextureColorFromImage(const QImage& srcImag
}
gpu::Texture* TextureUsage::create2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
return process2DTextureColorFromImage(srcImage, true, false, true);
return process2DTextureColorFromImage(srcImage, false, false, true);
}
gpu::Texture* TextureUsage::createAlbedoTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
return process2DTextureColorFromImage(srcImage, true, true, true);
return process2DTextureColorFromImage(srcImage, false, true, true);
}
gpu::Texture* TextureUsage::createEmissiveTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
return process2DTextureColorFromImage(srcImage, true, true, true);
return process2DTextureColorFromImage(srcImage, false, true, true);
}
gpu::Texture* TextureUsage::createLightmapTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
return process2DTextureColorFromImage(srcImage, true, true, true);
return process2DTextureColorFromImage(srcImage, false, true, true);
}

View file

@ -629,3 +629,32 @@ void AddressManager::addCurrentAddressToHistory(LookupTrigger trigger) {
}
}
}
void AddressManager::ifLocalSandboxRunningElse(std::function<void()> localSandboxRunningDoThis,
std::function<void()> localSandboxNotRunningDoThat) {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest sandboxStatus(SANDBOX_STATUS_URL);
sandboxStatus.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
QNetworkReply* reply = networkAccessManager.get(sandboxStatus);
connect(reply, &QNetworkReply::finished, this, [reply, localSandboxRunningDoThis, localSandboxNotRunningDoThat]() {
auto statusData = reply->readAll();
auto statusJson = QJsonDocument::fromJson(statusData);
if (!statusJson.isEmpty()) {
auto statusObject = statusJson.object();
auto serversValue = statusObject.value("servers");
if (!serversValue.isUndefined() && serversValue.isObject()) {
auto serversObject = serversValue.toObject();
auto serversCount = serversObject.size();
const int MINIMUM_EXPECTED_SERVER_COUNT = 5;
if (serversCount >= MINIMUM_EXPECTED_SERVER_COUNT) {
localSandboxRunningDoThis();
return;
}
}
}
localSandboxNotRunningDoThat();
});
}

View file

@ -24,6 +24,8 @@
const QString HIFI_URL_SCHEME = "hifi";
const QString DEFAULT_HIFI_ADDRESS = "hifi://entry";
const QString SANDBOX_HIFI_ADDRESS = "hifi://localhost";
const QString SANDBOX_STATUS_URL = "http://localhost:60332/status";
const QString INDEX_PATH = "/";
const QString GET_PLACE = "/api/v1/places/%1";
@ -65,6 +67,11 @@ public:
const QStack<QUrl>& getBackStack() const { return _backStack; }
const QStack<QUrl>& getForwardStack() const { return _forwardStack; }
/// determines if the local sandbox is likely running. It does not account for custom setups, and is only
/// intended to detect the standard local sandbox install.
void ifLocalSandboxRunningElse(std::function<void()> localSandboxRunningDoThis,
std::function<void()> localSandboxNotRunningDoThat);
public slots:
void handleLookupString(const QString& lookupString);
@ -74,6 +81,8 @@ public slots:
void goBack();
void goForward();
void goToLocalSandbox(LookupTrigger trigger = LookupTrigger::StartupFromSettings) { handleUrl(SANDBOX_HIFI_ADDRESS, trigger); }
void goToEntry(LookupTrigger trigger = LookupTrigger::StartupFromSettings) { handleUrl(DEFAULT_HIFI_ADDRESS, trigger); }
void goToUser(const QString& username);

View file

@ -40,15 +40,14 @@ void ResourceCacheSharedItems::appendPendingRequest(QWeakPointer<Resource> resou
QList<QSharedPointer<Resource>> ResourceCacheSharedItems::getPendingRequests() {
QList<QSharedPointer<Resource>> result;
Lock lock(_mutex);
{
Lock lock(_mutex);
foreach(QSharedPointer<Resource> resource, _pendingRequests) {
if (resource) {
result.append(resource);
}
foreach(QSharedPointer<Resource> resource, _pendingRequests) {
if (resource) {
result.append(resource);
}
}
return result;
}
@ -59,20 +58,20 @@ uint32_t ResourceCacheSharedItems::getPendingRequestsCount() const {
QList<QSharedPointer<Resource>> ResourceCacheSharedItems::getLoadingRequests() {
QList<QSharedPointer<Resource>> result;
Lock lock(_mutex);
{
Lock lock(_mutex);
foreach(QSharedPointer<Resource> resource, _loadingRequests) {
if (resource) {
result.append(resource);
}
foreach(QSharedPointer<Resource> resource, _loadingRequests) {
if (resource) {
result.append(resource);
}
}
return result;
}
void ResourceCacheSharedItems::removeRequest(QWeakPointer<Resource> resource) {
Lock lock(_mutex);
// resource can only be removed if it still has a ref-count, as
// QWeakPointer has no operator== implementation for two weak ptrs, so
// manually loop in case resource has been freed.
@ -88,11 +87,11 @@ void ResourceCacheSharedItems::removeRequest(QWeakPointer<Resource> resource) {
}
QSharedPointer<Resource> ResourceCacheSharedItems::getHighestPendingRequest() {
Lock lock(_mutex);
// look for the highest priority pending request
int highestIndex = -1;
float highestPriority = -FLT_MAX;
QSharedPointer<Resource> highestResource;
Lock lock(_mutex);
for (int i = 0; i < _pendingRequests.size();) {
// Clear any freed resources
@ -139,17 +138,17 @@ void ScriptableResource::setInScript(bool isInScript) {
}
void ScriptableResource::loadingChanged() {
emit stateChanged(LOADING);
setState(LOADING);
}
void ScriptableResource::loadedChanged() {
emit stateChanged(LOADED);
setState(LOADED);
}
void ScriptableResource::finished(bool success) {
disconnectHelper();
emit stateChanged(success ? FINISHED : FAILED);
setState(success ? FINISHED : FAILED);
}
void ScriptableResource::disconnectHelper() {
@ -180,7 +179,7 @@ ScriptableResource* ResourceCache::prefetch(const QUrl& url, void* extra) {
result = new ScriptableResource(url);
auto resource = getResource(url, QUrl(), false, extra);
auto resource = getResource(url, QUrl(), extra);
result->_resource = resource;
result->setObjectName(url.toString());
@ -262,12 +261,14 @@ void ResourceCache::refreshAll() {
clearUnusedResource();
resetResourceCounters();
_resourcesLock.lockForRead();
auto resourcesCopy = _resources;
_resourcesLock.unlock();
QHash<QUrl, QWeakPointer<Resource>> resources;
{
QReadLocker locker(&_resourcesLock);
resources = _resources;
}
// Refresh all remaining resources in use
foreach (QSharedPointer<Resource> resource, resourcesCopy) {
foreach (QSharedPointer<Resource> resource, resources) {
if (resource) {
resource->refresh();
}
@ -315,25 +316,7 @@ void ResourceCache::setRequestLimit(int limit) {
}
}
void ResourceCache::getResourceAsynchronously(const QUrl& url) {
qCDebug(networking) << "ResourceCache::getResourceAsynchronously" << url.toString();
_resourcesToBeGottenLock.lockForWrite();
_resourcesToBeGotten.enqueue(QUrl(url));
_resourcesToBeGottenLock.unlock();
}
void ResourceCache::checkAsynchronousGets() {
assert(QThread::currentThread() == thread());
if (!_resourcesToBeGotten.isEmpty()) {
_resourcesToBeGottenLock.lockForWrite();
QUrl url = _resourcesToBeGotten.dequeue();
_resourcesToBeGottenLock.unlock();
getResource(url);
}
}
QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl& fallback,
bool delayLoad, void* extra) {
QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl& fallback, void* extra) {
QSharedPointer<Resource> resource;
{
QReadLocker locker(&_resourcesLock);
@ -345,17 +328,21 @@ QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl&
}
if (QThread::currentThread() != thread()) {
assert(delayLoad);
getResourceAsynchronously(url);
qCDebug(networking) << "Fetching asynchronously:" << url;
QMetaObject::invokeMethod(this, "getResource",
Q_ARG(QUrl, url), Q_ARG(QUrl, fallback));
// Cannot use extra parameter as it might be freed before the invocation
return QSharedPointer<Resource>();
}
if (!url.isValid() && !url.isEmpty() && fallback.isValid()) {
return getResource(fallback, QUrl(), delayLoad);
return getResource(fallback, QUrl());
}
resource = createResource(url, fallback.isValid() ?
getResource(fallback, QUrl(), true) : QSharedPointer<Resource>(), delayLoad, extra);
resource = createResource(
url,
fallback.isValid() ? getResource(fallback, QUrl()) : QSharedPointer<Resource>(),
extra);
resource->setSelf(resource);
resource->setCache(this);
connect(resource.data(), &Resource::updateSize, this, &ResourceCache::updateTotalSize);
@ -399,8 +386,10 @@ void ResourceCache::removeUnusedResource(const QSharedPointer<Resource>& resourc
if (_unusedResources.contains(resource->getLRUKey())) {
_unusedResources.remove(resource->getLRUKey());
_unusedResourcesSize -= resource->getBytes();
locker.unlock();
resetResourceCounters();
}
resetResourceCounters();
}
void ResourceCache::reserveUnusedResource(qint64 resourceSize) {
@ -413,7 +402,9 @@ void ResourceCache::reserveUnusedResource(qint64 resourceSize) {
it.value()->setCache(nullptr);
auto size = it.value()->getBytes();
locker.unlock();
removeResource(it.value()->getURL(), size);
locker.relock();
_unusedResourcesSize -= size;
_unusedResources.erase(it);
@ -433,8 +424,16 @@ void ResourceCache::clearUnusedResource() {
}
void ResourceCache::resetResourceCounters() {
_numTotalResources = _resources.size();
_numUnusedResources = _unusedResources.size();
{
QReadLocker locker(&_resourcesLock);
_numTotalResources = _resources.size();
}
{
QReadLocker locker(&_unusedResourcesLock);
_numUnusedResources = _unusedResources.size();
}
emit dirty();
}
@ -495,7 +494,7 @@ const int DEFAULT_REQUEST_LIMIT = 10;
int ResourceCache::_requestLimit = DEFAULT_REQUEST_LIMIT;
int ResourceCache::_requestsActive = 0;
Resource::Resource(const QUrl& url, bool delayLoad) :
Resource::Resource(const QUrl& url) :
_url(url),
_activeUrl(url),
_request(nullptr) {

View file

@ -64,6 +64,7 @@ class ResourceCacheSharedItems : public Dependency {
using Mutex = std::mutex;
using Lock = std::unique_lock<Mutex>;
public:
void appendPendingRequest(QWeakPointer<Resource> newRequest);
void appendActiveRequest(QWeakPointer<Resource> newRequest);
@ -113,6 +114,9 @@ signals:
void progressChanged(uint64_t bytesReceived, uint64_t bytesTotal);
void stateChanged(int state);
protected:
void setState(State state) { _state = state; emit stateChanged(_state); }
private slots:
void loadingChanged();
void loadedChanged();
@ -175,9 +179,6 @@ public:
signals:
void dirty();
public slots:
void checkAsynchronousGets();
protected slots:
void updateTotalSize(const qint64& deltaSize);
@ -186,6 +187,14 @@ protected slots:
// and delegate to it (see TextureCache::prefetch(const QUrl&, int).
ScriptableResource* prefetch(const QUrl& url, void* extra);
/// Loads a resource from the specified URL and returns it.
/// If the caller is on a different thread than the ResourceCache,
/// returns an empty smart pointer and loads its asynchronously.
/// \param fallback a fallback URL to load if the desired one is unavailable
/// \param extra extra data to pass to the creator, if appropriate
QSharedPointer<Resource> getResource(const QUrl& url, const QUrl& fallback = QUrl(),
void* extra = NULL);
private slots:
void clearATPAssets();
@ -196,16 +205,9 @@ protected:
// the QScriptEngine will delete the pointer when it is garbage collected.
Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url) { return prefetch(url, nullptr); }
/// Loads a resource from the specified URL.
/// \param fallback a fallback URL to load if the desired one is unavailable
/// \param delayLoad if true, don't load the resource immediately; wait until load is first requested
/// \param extra extra data to pass to the creator, if appropriate
QSharedPointer<Resource> getResource(const QUrl& url, const QUrl& fallback = QUrl(),
bool delayLoad = false, void* extra = NULL);
/// Creates a new resource.
virtual QSharedPointer<Resource> createResource(const QUrl& url,
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) = 0;
virtual QSharedPointer<Resource> createResource(const QUrl& url, const QSharedPointer<Resource>& fallback,
const void* extra) = 0;
void addUnusedResource(const QSharedPointer<Resource>& resource);
void removeUnusedResource(const QSharedPointer<Resource>& resource);
@ -224,26 +226,30 @@ private:
void resetResourceCounters();
void removeResource(const QUrl& url, qint64 size = 0);
QReadWriteLock _resourcesLock { QReadWriteLock::Recursive };
QHash<QUrl, QWeakPointer<Resource>> _resources;
int _lastLRUKey = 0;
void getResourceAsynchronously(const QUrl& url);
static int _requestLimit;
static int _requestsActive;
void getResourceAsynchronously(const QUrl& url);
QReadWriteLock _resourcesToBeGottenLock { QReadWriteLock::Recursive };
QQueue<QUrl> _resourcesToBeGotten;
std::atomic<size_t> _numTotalResources { 0 };
std::atomic<size_t> _numUnusedResources { 0 };
// Resources
QHash<QUrl, QWeakPointer<Resource>> _resources;
QReadWriteLock _resourcesLock { QReadWriteLock::Recursive };
int _lastLRUKey = 0;
std::atomic<size_t> _numTotalResources { 0 };
std::atomic<qint64> _totalResourcesSize { 0 };
// Cached resources
QMap<int, QSharedPointer<Resource>> _unusedResources;
QReadWriteLock _unusedResourcesLock { QReadWriteLock::Recursive };
qint64 _unusedResourcesMaxSize = DEFAULT_UNUSED_MAX_SIZE;
std::atomic<size_t> _numUnusedResources { 0 };
std::atomic<qint64> _unusedResourcesSize { 0 };
qint64 _unusedResourcesMaxSize = DEFAULT_UNUSED_MAX_SIZE;
QReadWriteLock _unusedResourcesLock { QReadWriteLock::Recursive };
QMap<int, QSharedPointer<Resource>> _unusedResources;
// Pending resources
QQueue<QUrl> _resourcesToBeGotten;
QReadWriteLock _resourcesToBeGottenLock { QReadWriteLock::Recursive };
};
/// Base class for resources.
@ -252,7 +258,7 @@ class Resource : public QObject {
public:
Resource(const QUrl& url, bool delayLoad = false);
Resource(const QUrl& url);
~Resource();
/// Returns the key last used to identify this resource in the unused map.

View file

@ -33,6 +33,7 @@
#include <QString>
#include <GeometryUtil.h>
#include <Gzip.h>
#include <LogHandler.h>
#include <NetworkAccessManager.h>
#include <OctalCode.h>
@ -40,12 +41,12 @@
#include <ResourceManager.h>
#include <SharedUtil.h>
#include <PathUtils.h>
#include <Gzip.h>
#include <ViewFrustum.h>
#include "OctreeConstants.h"
#include "OctreeElementBag.h"
#include "Octree.h"
#include "ViewFrustum.h"
#include "OctreeUtils.h"
#include "OctreeLogging.h"
@ -898,7 +899,7 @@ int Octree::encodeTreeBitstream(OctreeElementPointer element,
}
// If we're at a element that is out of view, then we can return, because no nodes below us will be in view!
if (params.viewFrustum && !element->isInView(*params.viewFrustum)) {
if (!params.recurseEverything && !element->isInView(params.viewFrustum)) {
params.stopReason = EncodeBitstreamParams::OUT_OF_VIEW;
return bytesWritten;
}
@ -1014,15 +1015,12 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
}
ViewFrustum::intersection nodeLocationThisView = ViewFrustum::INSIDE; // assume we're inside
// caller can pass NULL as viewFrustum if they want everything
if (params.viewFrustum) {
float distance = element->distanceToCamera(*params.viewFrustum);
if (!params.recurseEverything) {
float boundaryDistance = boundaryDistanceForRenderLevel(element->getLevel() + params.boundaryLevelAdjust,
params.octreeElementSizeScale);
// If we're too far away for our render level, then just return
if (distance >= boundaryDistance) {
if (element->distanceToCamera(params.viewFrustum) >= boundaryDistance) {
if (params.stats) {
params.stats->skippedDistance(element);
}
@ -1034,7 +1032,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
// if we are INSIDE, INTERSECT, or OUTSIDE
if (parentLocationThisView != ViewFrustum::INSIDE) {
assert(parentLocationThisView != ViewFrustum::OUTSIDE); // we shouldn't be here if our parent was OUTSIDE!
nodeLocationThisView = element->computeViewIntersection(*params.viewFrustum);
nodeLocationThisView = element->computeViewIntersection(params.viewFrustum);
}
// If we're at a element that is out of view, then we can return, because no nodes below us will be in view!
@ -1052,8 +1050,8 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
// because we don't send nodes from the previously know in view frustum.
bool wasInView = false;
if (params.deltaViewFrustum && params.lastViewFrustum) {
ViewFrustum::intersection location = element->computeViewIntersection(*params.lastViewFrustum);
if (params.deltaView) {
ViewFrustum::intersection location = element->computeViewIntersection(params.lastViewFrustum);
// If we're a leaf, then either intersect or inside is considered "formerly in view"
if (element->isLeaf()) {
@ -1067,10 +1065,9 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
// to it, and so therefore it may now be visible from an LOD perspective, in which case we don't consider it
// as "was in view"...
if (wasInView) {
float distance = element->distanceToCamera(*params.lastViewFrustum);
float boundaryDistance = boundaryDistanceForRenderLevel(element->getLevel() + params.boundaryLevelAdjust,
params.octreeElementSizeScale);
if (distance >= boundaryDistance) {
if (element->distanceToCamera(params.lastViewFrustum) >= boundaryDistance) {
// This would have been invisible... but now should be visible (we wouldn't be here otherwise)...
wasInView = false;
}
@ -1078,9 +1075,9 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
}
// If we were previously in the view, then we normally will return out of here and stop recursing. But
// if we're in deltaViewFrustum mode, and this element has changed since it was last sent, then we do
// if we're in deltaView mode, and this element has changed since it was last sent, then we do
// need to send it.
if (wasInView && !(params.deltaViewFrustum && element->hasChangedSince(params.lastViewFrustumSent - CHANGE_FUDGE))) {
if (wasInView && !(params.deltaView && element->hasChangedSince(params.lastViewFrustumSent - CHANGE_FUDGE))) {
if (params.stats) {
params.stats->skippedWasInView(element);
}
@ -1090,7 +1087,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
// If we're not in delta sending mode, and we weren't asked to do a force send, and the voxel hasn't changed,
// then we can also bail early and save bits
if (!params.forceSendScene && !params.deltaViewFrustum &&
if (!params.forceSendScene && !params.deltaView &&
!element->hasChangedSince(params.lastViewFrustumSent - CHANGE_FUDGE)) {
if (params.stats) {
params.stats->skippedNoChange(element);
@ -1179,10 +1176,10 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
int originalIndex = indexOfChildren[i];
bool childIsInView = (childElement &&
( !params.viewFrustum || // no view frustum was given, everything is assumed in view
(nodeLocationThisView == ViewFrustum::INSIDE) || // parent was fully in view, we can assume ALL children are
(params.recurseEverything ||
(nodeLocationThisView == ViewFrustum::INSIDE) || // parent was fully in view, we can assume ALL children are
(nodeLocationThisView == ViewFrustum::INTERSECT &&
childElement->isInView(*params.viewFrustum)) // the parent intersects and the child is in view
childElement->isInView(params.viewFrustum)) // the parent intersects and the child is in view
));
if (!childIsInView) {
@ -1192,12 +1189,11 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
}
} else {
// Before we consider this further, let's see if it's in our LOD scope...
float distance = distancesToChildren[i];
float boundaryDistance = !params.viewFrustum ? 1 :
boundaryDistanceForRenderLevel(childElement->getLevel() + params.boundaryLevelAdjust,
float boundaryDistance = params.recurseEverything ? 1 :
boundaryDistanceForRenderLevel(childElement->getLevel() + params.boundaryLevelAdjust,
params.octreeElementSizeScale);
if (!(distance < boundaryDistance)) {
if (!(distancesToChildren[i] < boundaryDistance)) {
// don't need to check childElement here, because we can't get here with no childElement
if (params.stats) {
params.stats->skippedDistance(childElement);
@ -1215,10 +1211,9 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
bool childIsOccluded = false; // assume it's not occluded
bool shouldRender = !params.viewFrustum
? true
: childElement->calculateShouldRender(params.viewFrustum,
params.octreeElementSizeScale, params.boundaryLevelAdjust);
bool shouldRender = params.recurseEverything ||
childElement->calculateShouldRender(params.viewFrustum,
params.octreeElementSizeScale, params.boundaryLevelAdjust);
// track some stats
if (params.stats) {
@ -1236,8 +1231,8 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
if (shouldRender && !childIsOccluded) {
bool childWasInView = false;
if (childElement && params.deltaViewFrustum && params.lastViewFrustum) {
ViewFrustum::intersection location = childElement->computeViewIntersection(*params.lastViewFrustum);
if (childElement && params.deltaView) {
ViewFrustum::intersection location = childElement->computeViewIntersection(params.lastViewFrustum);
// If we're a leaf, then either intersect or inside is considered "formerly in view"
if (childElement->isLeaf()) {
@ -1251,7 +1246,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
// Or if we were previously in the view, but this element has changed since it was last sent, then we do
// need to send it.
if (!childWasInView ||
(params.deltaViewFrustum &&
(params.deltaView &&
childElement->hasChangedSince(params.lastViewFrustumSent - CHANGE_FUDGE))){
childrenDataBits += (1 << (7 - originalIndex));
@ -1456,7 +1451,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElementPointer element,
// called databits), then we wouldn't send the children. So those types of Octree's should tell us to keep
// recursing, by returning TRUE in recurseChildrenWithData().
if (recurseChildrenWithData() || !params.viewFrustum || !oneAtBit(childrenDataBits, originalIndex)) {
if (params.recurseEverything || recurseChildrenWithData() || !oneAtBit(childrenDataBits, originalIndex)) {
// Allow the datatype a chance to determine if it really wants to recurse this tree. Usually this
// will be true. But if the tree has already been encoded, we will skip this.
@ -1978,7 +1973,8 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElementPointer element)
bool lastPacketWritten = false;
while (OctreeElementPointer subTree = elementBag.extract()) {
EncodeBitstreamParams params(INT_MAX, IGNORE_VIEW_FRUSTUM, NO_EXISTS_BITS);
EncodeBitstreamParams params(INT_MAX, NO_EXISTS_BITS);
params.recurseEverything = true;
withReadLock([&] {
params.extraEncodeData = &extraEncodeData;
bytesWritten = encodeTreeBitstream(subTree, &packetData, elementBag, params);

View file

@ -20,9 +20,9 @@
#include <shared/ReadWriteLockable.h>
#include <SimpleMovingAverage.h>
#include <ViewFrustum.h>
#include "JurisdictionMap.h"
#include "ViewFrustum.h"
#include "OctreeElement.h"
#include "OctreeElementBag.h"
#include "OctreePacketData.h"
@ -61,22 +61,22 @@ const int LOW_RES_MOVING_ADJUST = 1;
const quint64 IGNORE_LAST_SENT = 0;
#define IGNORE_SCENE_STATS NULL
#define IGNORE_VIEW_FRUSTUM NULL
#define IGNORE_COVERAGE_MAP NULL
#define IGNORE_JURISDICTION_MAP NULL
class EncodeBitstreamParams {
public:
ViewFrustum viewFrustum;
ViewFrustum lastViewFrustum;
quint64 lastViewFrustumSent;
int maxEncodeLevel;
int maxLevelReached;
const ViewFrustum* viewFrustum;
bool includeExistsBits;
int chopLevels;
bool deltaViewFrustum;
const ViewFrustum* lastViewFrustum;
bool deltaView;
bool recurseEverything { false };
int boundaryLevelAdjust;
float octreeElementSizeScale;
quint64 lastViewFrustumSent;
bool forceSendScene;
OctreeSceneStats* stats;
JurisdictionMap* jurisdictionMap;
@ -99,11 +99,9 @@ public:
EncodeBitstreamParams(
int maxEncodeLevel = INT_MAX,
const ViewFrustum* viewFrustum = IGNORE_VIEW_FRUSTUM,
bool includeExistsBits = WANT_EXISTS_BITS,
int chopLevels = 0,
bool deltaViewFrustum = false,
const ViewFrustum* lastViewFrustum = IGNORE_VIEW_FRUSTUM,
bool useDeltaView = false,
int boundaryLevelAdjust = NO_BOUNDARY_ADJUST,
float octreeElementSizeScale = DEFAULT_OCTREE_SIZE_SCALE,
quint64 lastViewFrustumSent = IGNORE_LAST_SENT,
@ -111,22 +109,22 @@ public:
OctreeSceneStats* stats = IGNORE_SCENE_STATS,
JurisdictionMap* jurisdictionMap = IGNORE_JURISDICTION_MAP,
OctreeElementExtraEncodeData* extraEncodeData = NULL) :
lastViewFrustumSent(lastViewFrustumSent),
maxEncodeLevel(maxEncodeLevel),
maxLevelReached(0),
viewFrustum(viewFrustum),
includeExistsBits(includeExistsBits),
chopLevels(chopLevels),
deltaViewFrustum(deltaViewFrustum),
lastViewFrustum(lastViewFrustum),
deltaView(useDeltaView),
boundaryLevelAdjust(boundaryLevelAdjust),
octreeElementSizeScale(octreeElementSizeScale),
lastViewFrustumSent(lastViewFrustumSent),
forceSendScene(forceSendScene),
stats(stats),
jurisdictionMap(jurisdictionMap),
extraEncodeData(extraEncodeData),
stopReason(UNKNOWN)
{}
{
lastViewFrustum.invalidate();
}
void displayStopReason() {
printf("StopReason: ");
@ -341,7 +339,7 @@ public:
bool getIsClient() const { return !_isServer; } /// Is this a client based tree. Allows guards for certain operations
void setIsClient(bool isClient) { _isServer = !isClient; }
virtual void dumpTree() { }
virtual void pruneTree() { }
@ -352,7 +350,6 @@ public:
virtual quint64 getAverageCreateTime() const { return 0; }
virtual quint64 getAverageLoggingTime() const { return 0; }
signals:
void importSize(float x, float y, float z);
void importProgress(int progress);

View file

@ -22,10 +22,11 @@
#include "AACube.h"
#include "OctalCode.h"
#include "Octree.h"
#include "OctreeConstants.h"
#include "OctreeElement.h"
#include "Octree.h"
#include "OctreeLogging.h"
#include "OctreeUtils.h"
#include "SharedUtil.h"
AtomicUIntStat OctreeElement::_octreeMemoryUsage { 0 };
@ -471,11 +472,11 @@ ViewFrustum::intersection OctreeElement::computeViewIntersection(const ViewFrust
// Since, if we know the camera position and orientation, we can know which of the corners is the "furthest"
// corner. We can use we can use this corner as our "voxel position" to do our distance calculations off of.
// By doing this, we don't need to test each child voxel's position vs the LOD boundary
bool OctreeElement::calculateShouldRender(const ViewFrustum* viewFrustum, float voxelScaleSize, int boundaryLevelAdjust) const {
bool OctreeElement::calculateShouldRender(const ViewFrustum& viewFrustum, float voxelScaleSize, int boundaryLevelAdjust) const {
bool shouldRender = false;
if (hasContent()) {
float furthestDistance = furthestDistanceToCamera(*viewFrustum);
float furthestDistance = furthestDistanceToCamera(viewFrustum);
float childBoundary = boundaryDistanceForRenderLevel(getLevel() + 1 + boundaryLevelAdjust, voxelScaleSize);
bool inChildBoundary = (furthestDistance <= childBoundary);
if (hasDetailedContent() && inChildBoundary) {

Some files were not shown because too many files have changed in this diff Show more