Merge branch 'master' of https://github.com/highfidelity/hifi into workload

This commit is contained in:
samcake 2018-04-20 09:31:36 -07:00
commit 0f4aa7e27e
81 changed files with 414 additions and 2116 deletions

View file

@ -11,7 +11,7 @@ Should you choose not to install Qt5 via a package manager that handles dependen
## Ubuntu 16.04 specific build guide
### Prepare environment
hifiqt5.10.1
Install qt:
```bash
wget http://debian.highfidelity.com/pool/h/hi/hifiqt5.10.1_5.10.1_amd64.deb

View file

@ -48,8 +48,6 @@ private:
void preDistributionProcessing() override;
bool hasSomethingToSend(OctreeQueryNode* nodeData) override { return !_sendQueue.empty(); }
bool shouldStartNewTraversal(OctreeQueryNode* nodeData, bool viewFrustumChanged) override { return viewFrustumChanged || _traversal.finished(); }
void preStartNewScene(OctreeQueryNode* nodeData, bool isFullScene) override {};
bool shouldTraverseAndSend(OctreeQueryNode* nodeData) override { return true; }
DiffTraversal _traversal;
EntityPriorityQueue _sendQueue;

View file

@ -304,23 +304,6 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode*
return numPackets;
}
void OctreeSendThread::preStartNewScene(OctreeQueryNode* nodeData, bool isFullScene) {
// If we're starting a full scene, then definitely we want to empty the elementBag
if (isFullScene) {
nodeData->elementBag.deleteAll();
}
// This is the start of "resending" the scene.
bool dontRestartSceneOnMove = false; // this is experimental
if (dontRestartSceneOnMove) {
if (nodeData->elementBag.isEmpty()) {
nodeData->elementBag.insert(_myServer->getOctree()->getRoot());
}
} else {
nodeData->elementBag.insert(_myServer->getOctree()->getRoot());
}
}
/// Version of octree element distributor that sends the deepest LOD level at once
int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged) {
OctreeServer::didPacketDistributor(this);
@ -366,16 +349,8 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
// the current view frustum for things to send.
if (shouldStartNewTraversal(nodeData, viewFrustumChanged)) {
// if our view has changed, we need to reset these things...
if (viewFrustumChanged) {
if (nodeData->moveShouldDump() || nodeData->hasLodChanged()) {
nodeData->dumpOutOfView();
}
}
// track completed scenes and send out the stats packet accordingly
nodeData->stats.sceneCompleted();
nodeData->setLastRootTimestamp(_myServer->getOctree()->getRoot()->getLastChanged());
_myServer->getOctree()->releaseSceneEncodeData(&nodeData->extraEncodeData);
// TODO: add these to stats page
@ -389,111 +364,74 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
// TODO: add these to stats page
//::startSceneSleepTime = _usleepTime;
nodeData->sceneStart(usecTimestampNow() - CHANGE_FUDGE);
// start tracking our stats
nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, _myServer->getOctree()->getRoot());
preStartNewScene(nodeData, isFullScene);
}
// If we have something in our elementBag, then turn them into packets and send them out...
if (shouldTraverseAndSend(nodeData)) {
quint64 start = usecTimestampNow();
quint64 start = usecTimestampNow();
_myServer->getOctree()->withReadLock([&]{
traverseTreeAndSendContents(node, nodeData, viewFrustumChanged, isFullScene);
});
_myServer->getOctree()->withReadLock([&]{
traverseTreeAndSendContents(node, nodeData, viewFrustumChanged, isFullScene);
});
// Here's where we can/should allow the server to send other data...
// send the environment packet
// TODO: should we turn this into a while loop to better handle sending multiple special packets
if (_myServer->hasSpecialPacketsToSend(node) && !nodeData->isShuttingDown()) {
int specialPacketsSent = 0;
int specialBytesSent = _myServer->sendSpecialPackets(node, nodeData, specialPacketsSent);
nodeData->resetOctreePacket(); // because nodeData's _sequenceNumber has changed
_truePacketsSent += specialPacketsSent;
_trueBytesSent += specialBytesSent;
_packetsSentThisInterval += specialPacketsSent;
// Here's where we can/should allow the server to send other data...
// send the environment packet
// TODO: should we turn this into a while loop to better handle sending multiple special packets
if (_myServer->hasSpecialPacketsToSend(node) && !nodeData->isShuttingDown()) {
int specialPacketsSent = 0;
int specialBytesSent = _myServer->sendSpecialPackets(node, nodeData, specialPacketsSent);
nodeData->resetOctreePacket(); // because nodeData's _sequenceNumber has changed
_truePacketsSent += specialPacketsSent;
_trueBytesSent += specialBytesSent;
_packetsSentThisInterval += specialPacketsSent;
_totalPackets += specialPacketsSent;
_totalBytes += specialBytesSent;
_totalPackets += specialPacketsSent;
_totalBytes += specialBytesSent;
_totalSpecialPackets += specialPacketsSent;
_totalSpecialBytes += specialBytesSent;
_totalSpecialPackets += specialPacketsSent;
_totalSpecialBytes += specialBytesSent;
}
// calculate max number of packets that can be sent during this interval
int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxQueryPacketsPerSecond() / INTERVALS_PER_SECOND));
int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval());
// Re-send packets that were nacked by the client
while (nodeData->hasNextNackedPacket() && _packetsSentThisInterval < maxPacketsPerInterval) {
const NLPacket* packet = nodeData->getNextNackedPacket();
if (packet) {
DependencyManager::get<NodeList>()->sendUnreliablePacket(*packet, *node);
int numBytes = packet->getDataSize();
_truePacketsSent++;
_trueBytesSent += numBytes;
_packetsSentThisInterval++;
_totalPackets++;
_totalBytes += numBytes;
_totalWastedBytes += udt::MAX_PACKET_SIZE - packet->getDataSize();
}
}
// calculate max number of packets that can be sent during this interval
int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxQueryPacketsPerSecond() / INTERVALS_PER_SECOND));
int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval());
quint64 end = usecTimestampNow();
int elapsedmsec = (end - start) / USECS_PER_MSEC;
OctreeServer::trackLoopTime(elapsedmsec);
// Re-send packets that were nacked by the client
while (nodeData->hasNextNackedPacket() && _packetsSentThisInterval < maxPacketsPerInterval) {
const NLPacket* packet = nodeData->getNextNackedPacket();
if (packet) {
DependencyManager::get<NodeList>()->sendUnreliablePacket(*packet, *node);
int numBytes = packet->getDataSize();
_truePacketsSent++;
_trueBytesSent += numBytes;
_packetsSentThisInterval++;
// if we've sent everything, then we want to remember that we've sent all
// the octree elements from the current view frustum
if (!hasSomethingToSend(nodeData)) {
nodeData->setViewSent(true);
_totalPackets++;
_totalBytes += numBytes;
_totalWastedBytes += udt::MAX_PACKET_SIZE - packet->getDataSize();
}
// If this was a full scene then make sure we really send out a stats packet at this point so that
// the clients will know the scene is stable
if (isFullScene) {
nodeData->stats.sceneCompleted();
handlePacketSend(node, nodeData, true);
}
quint64 end = usecTimestampNow();
int elapsedmsec = (end - start) / USECS_PER_MSEC;
OctreeServer::trackLoopTime(elapsedmsec);
// if after sending packets we've emptied our bag, then we want to remember that we've sent all
// the octree elements from the current view frustum
if (!hasSomethingToSend(nodeData)) {
nodeData->updateLastKnownViewFrustum();
nodeData->setViewSent(true);
// If this was a full scene then make sure we really send out a stats packet at this point so that
// the clients will know the scene is stable
if (isFullScene) {
nodeData->stats.sceneCompleted();
handlePacketSend(node, nodeData, true);
}
}
} // end if bag wasn't empty, and so we sent stuff...
}
return _truePacketsSent;
}
bool OctreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) {
bool somethingToSend = false;
OctreeQueryNode* nodeData = static_cast<OctreeQueryNode*>(params.nodeData);
if (!nodeData->elementBag.isEmpty()) {
quint64 encodeStart = usecTimestampNow();
quint64 lockWaitStart = encodeStart;
_myServer->getOctree()->withReadLock([&]{
OctreeServer::trackTreeWaitTime((float)(usecTimestampNow() - lockWaitStart));
OctreeElementPointer subTree = nodeData->elementBag.extract();
if (subTree) {
// NOTE: this is where the tree "contents" are actually packed
nodeData->stats.encodeStarted();
_myServer->getOctree()->encodeTreeBitstream(subTree, &_packetData, nodeData->elementBag, params);
nodeData->stats.encodeStopped();
somethingToSend = true;
}
});
OctreeServer::trackEncodeTime((float)(usecTimestampNow() - encodeStart));
} else {
OctreeServer::trackTreeWaitTime(OctreeServer::SKIP_TIME);
OctreeServer::trackEncodeTime(OctreeServer::SKIP_TIME);
}
return somethingToSend;
}
void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged, bool isFullScene) {
// calculate max number of packets that can be sent during this interval
int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxQueryPacketsPerSecond() / INTERVALS_PER_SECOND));
@ -502,21 +440,12 @@ void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, Octre
int extraPackingAttempts = 0;
// init params once outside the while loop
int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust();
int boundaryLevelAdjust = boundaryLevelAdjustClient +
(viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
float octreeSizeScale = nodeData->getOctreeSizeScale();
EncodeBitstreamParams params(INT_MAX, WANT_EXISTS_BITS, DONT_CHOP,
viewFrustumChanged, boundaryLevelAdjust, octreeSizeScale,
isFullScene, nodeData);
EncodeBitstreamParams params(WANT_EXISTS_BITS, nodeData);
// Our trackSend() function is implemented by the server subclass, and will be called back as new entities/data elements are sent
params.trackSend = [this](const QUuid& dataID, quint64 dataEdited) {
_myServer->trackSend(dataID, dataEdited, _nodeUuid);
};
nodeData->copyCurrentViewFrustum(params.viewFrustum);
if (viewFrustumChanged) {
nodeData->copyLastKnownViewFrustum(params.lastViewFrustum);
}
bool somethingToSend = true; // assume we have something
bool hadSomething = hasSomethingToSend(nodeData);
@ -536,8 +465,8 @@ void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, Octre
extraPackingAttempts++;
}
// If the bag had contents but is now empty then we know we've sent the entire scene.
bool completedScene = hadSomething && nodeData->elementBag.isEmpty();
// If we had something to send, but now we don't, then we know we've sent the entire scene.
bool completedScene = hadSomething;
if (completedScene || lastNodeDidntFit) {
// we probably want to flush what has accumulated in nodeData but:
// do we have more data to send? and is there room?

View file

@ -54,7 +54,7 @@ protected:
virtual void traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
bool viewFrustumChanged, bool isFullScene);
virtual bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters);
virtual bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) = 0;
OctreePacketData _packetData;
QWeakPointer<Node> _node;
@ -63,14 +63,12 @@ protected:
private:
/// Called before a packetDistributor pass to allow for pre-distribution processing
virtual void preDistributionProcessing() {};
virtual void preDistributionProcessing() = 0;
int handlePacketSend(SharedNodePointer node, OctreeQueryNode* nodeData, bool dontSuppressDuplicate = false);
int packetDistributor(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged);
virtual bool hasSomethingToSend(OctreeQueryNode* nodeData) { return !nodeData->elementBag.isEmpty(); }
virtual bool shouldStartNewTraversal(OctreeQueryNode* nodeData, bool viewFrustumChanged) { return viewFrustumChanged || !hasSomethingToSend(nodeData); }
virtual void preStartNewScene(OctreeQueryNode* nodeData, bool isFullScene);
virtual bool shouldTraverseAndSend(OctreeQueryNode* nodeData) { return hasSomethingToSend(nodeData); }
virtual bool hasSomethingToSend(OctreeQueryNode* nodeData) = 0;
virtual bool shouldStartNewTraversal(OctreeQueryNode* nodeData, bool viewFrustumChanged) = 0;
int _truePacketsSent { 0 }; // available for debug stats
int _trueBytesSent { 0 }; // available for debug stats

View file

@ -876,10 +876,6 @@ void OctreeServer::parsePayload() {
}
}
OctreeServer::UniqueSendThread OctreeServer::newSendThread(const SharedNodePointer& node) {
return std::unique_ptr<OctreeSendThread>(new OctreeSendThread(this, node));
}
OctreeServer::UniqueSendThread OctreeServer::createSendThread(const SharedNodePointer& node) {
auto sendThread = newSendThread(node);

View file

@ -174,7 +174,7 @@ protected:
void beginRunning(QByteArray replaceData);
UniqueSendThread createSendThread(const SharedNodePointer& node);
virtual UniqueSendThread newSendThread(const SharedNodePointer& node);
virtual UniqueSendThread newSendThread(const SharedNodePointer& node) = 0;
int _argc;
const char** _argv;

View file

@ -30,6 +30,16 @@ Item {
color: "black"
opacity: 0.5
radius: popupRadius
MouseArea {
anchors.fill: parent;
hoverEnabled: true;
acceptedButtons: Qt.LeftButton;
propagateComposedEvents: false;
onClicked: {
letterbox.visible = false;
}
}
}
Rectangle {
id: textContainer;
@ -38,6 +48,14 @@ Item {
anchors.centerIn: parent
radius: popupRadius
color: "white"
// Prevent dismissing the popup by clicking on the textContainer
MouseArea {
anchors.fill: parent;
hoverEnabled: true;
propagateComposedEvents: false;
}
Item {
id: contentContainer
width: parent.width - 50
@ -135,11 +153,4 @@ Item {
}
}
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton
onClicked: {
letterbox.visible = false;
}
}
}

View file

@ -14,8 +14,8 @@ FocusScope {
id: tabletMenu
objectName: "tabletMenu"
width: 480
height: 720
width: parent.width
height: parent.height
property var rootMenu: Menu { objectName:"rootMenu" }
property var point: Qt.point(50, 50);

View file

@ -16,6 +16,8 @@ import "."
Item {
id: root
anchors.fill: parent
width: parent.width
height: parent.height
objectName: "tabletMenuHandlerItem"
StackView {

View file

@ -40,10 +40,10 @@ FocusScope {
id: listView
x: 0
y: 0
width: 480
height: 720
contentWidth: 480
contentHeight: 720
width: parent.width
height: parent.height
contentWidth: parent.width
contentHeight: parent.height
objectName: "menuList"
topMargin: hifi.dimensions.menuPadding.y

View file

@ -188,7 +188,7 @@ void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device, bool isHMD
for (auto i = 0; i < _devices.size(); ++i) {
std::shared_ptr<AudioDevice> device = _devices[i];
bool &isSelected = isHMD ? device->selectedHMD : device->selectedDesktop;
bool& isSelected = isHMD ? device->selectedHMD : device->selectedDesktop;
if (isSelected && device->info != selectedDevice) {
isSelected = false;
} else if (device->info == selectedDevice) {
@ -259,7 +259,7 @@ void AudioDeviceList::onDevicesChanged(const QList<QAudioDeviceInfo>& devices) {
foreach(const QAudioDeviceInfo& deviceInfo, devices) {
for (bool isHMD : {false, true}) {
auto &backupSelectedDeviceName = isHMD ? _backupSelectedHMDDeviceName : _backupSelectedDesktopDeviceName;
auto& backupSelectedDeviceName = isHMD ? _backupSelectedHMDDeviceName : _backupSelectedDesktopDeviceName;
if (deviceInfo.deviceName() == backupSelectedDeviceName) {
QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice;
selectedDevice = deviceInfo;
@ -278,7 +278,7 @@ void AudioDeviceList::onDevicesChanged(const QList<QAudioDeviceInfo>& devices) {
for (bool isHMD : {false, true}) {
QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice;
bool &isSelected = isHMD ? device.selectedHMD : device.selectedDesktop;
bool& isSelected = isHMD ? device.selectedHMD : device.selectedDesktop;
if (!selectedDevice.isNull()) {
isSelected = (device.info == selectedDevice);

View file

@ -120,7 +120,6 @@ public:
void markAsChangedOnServer();
quint64 getLastChangedOnServer() const;
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const;
virtual OctreeElement::AppendState appendEntityData(OctreePacketData* packetData, EncodeBitstreamParams& params,

View file

@ -695,8 +695,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
* @typedef {object} Entities.EntityProperties-Material
* @property {string} materialURL="" - URL to a {@link MaterialResource}. If you append <code>?name</code> to the URL, the
* material with that name in the {@link MaterialResource} will be applied to the entity. <br />
* Alternatively, set the property value to <code>"userData"</code> to use the {@link Entities.EntityProperties|userData}
* entity property to live edit the material resource values.
* Alternatively, set the property value to <code>"materialData"</code> to use the <code>materialData</code> property
* for the {@link MaterialResource} values.
* @property {number} priority=0 - The priority for applying the material to its parent. Only the highest priority material is
* applied, with materials of the same priority randomly assigned. Materials that come with the model have a priority of
* <code>0</code>.
@ -710,6 +710,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
* <code>{ x: 0, y: 0 }</code> &ndash; <code>{ x: 1, y: 1 }</code>.
* @property {Vec2} materialMappingScale=1,1 - How much to scale the material within the parent's UV-space.
* @property {number} materialMappingRot=0 - How much to rotate the material within the parent's UV-space, in degrees.
* @property {string} materialData="" - Used to store {@link MaterialResource} data as a JSON string. You can use
* <code>JSON.parse()</code> to parse the string into a JavaScript object which you can manipulate the properties of, and
* use <code>JSON.stringify()</code> to convert the object into a string to put in the property.
* @example <caption>Color a sphere using a Material entity.</caption>
* var entityID = Entities.addEntity({
* type: "Sphere",
@ -722,13 +725,14 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
* var materialID = Entities.addEntity({
* type: "Material",
* parentID: entityID,
* materialURL: "userData",
* materialURL: "materialData",
* priority: 1,
* userData: JSON.stringify({
* materialData: JSON.stringify({
* materialVersion: 1,
* materials: {
* // Can only set albedo on a Shape entity.
* // Value overrides entity's "color" property.
* albedo: [1.0, 0, 0]
* albedo: [1.0, 1.0, 0] // Yellow
* }
* }),
* });

View file

@ -88,7 +88,6 @@ public:
// These methods will allow the OctreeServer to send your tree inbound edit packets of your
// own definition. Implement these to allow your octree based server to support editing
virtual bool getWantSVOfileVersions() const override { return true; }
virtual PacketType expectedDataPacketType() const override { return PacketType::EntityData; }
virtual bool handlesEditPacketType(PacketType packetType) const override;
void fixupTerseEditLogging(EntityItemProperties& properties, QList<QString>& changedProperties);
@ -107,11 +106,7 @@ public:
virtual bool rootElementHasData() const override { return true; }
// the root at least needs to store the number of entities in the packet/buffer
virtual int minimumRequiredRootDataBytes() const override { return sizeof(uint16_t); }
virtual bool suppressEmptySubtrees() const override { return false; }
virtual void releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncodeData) const override;
virtual bool mustIncludeAllChildData() const override { return false; }
virtual void update() override { update(true); }

View file

@ -67,455 +67,6 @@ void EntityTreeElement::debugExtraEncodeData(EncodeBitstreamParams& params) cons
}
}
void EntityTreeElement::initializeExtraEncodeData(EncodeBitstreamParams& params) {
auto entityNodeData = static_cast<EntityNodeData*>(params.nodeData);
assert(entityNodeData);
OctreeElementExtraEncodeData* extraEncodeData = &entityNodeData->extraEncodeData;
assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes
// Check to see if this element yet has encode data... if it doesn't create it
if (!extraEncodeData->contains(this)) {
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData { new EntityTreeElementExtraEncodeData() };
entityTreeElementExtraEncodeData->elementCompleted = (_entityItems.size() == 0);
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
EntityTreeElementPointer child = getChildAtIndex(i);
if (!child) {
entityTreeElementExtraEncodeData->childCompleted[i] = true; // if no child exists, it is completed
} else {
if (child->hasEntities()) {
entityTreeElementExtraEncodeData->childCompleted[i] = false; // HAS ENTITIES NEEDS ENCODING
} else {
entityTreeElementExtraEncodeData->childCompleted[i] = true; // child doesn't have enities, it is completed
}
}
}
forEachEntity([&](EntityItemPointer entity) {
entityTreeElementExtraEncodeData->entities.insert(entity->getEntityItemID(), entity->getEntityProperties(params));
});
// TODO: some of these inserts might be redundant!!!
extraEncodeData->insert(this, entityTreeElementExtraEncodeData);
}
}
bool EntityTreeElement::shouldIncludeChildData(int childIndex, EncodeBitstreamParams& params) const {
auto entityNodeData = static_cast<EntityNodeData*>(params.nodeData);
assert(entityNodeData);
OctreeElementExtraEncodeData* extraEncodeData = &entityNodeData->extraEncodeData;
assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes
if (extraEncodeData->contains(this)) {
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData
= std::static_pointer_cast<EntityTreeElementExtraEncodeData>((*extraEncodeData)[this]);
bool childCompleted = entityTreeElementExtraEncodeData->childCompleted[childIndex];
// If we haven't completely sent the child yet, then we should include it
return !childCompleted;
}
// I'm not sure this should ever happen, since we should have the extra encode data if we're considering
// the child data for this element
assert(false);
return false;
}
bool EntityTreeElement::shouldRecurseChildTree(int childIndex, EncodeBitstreamParams& params) const {
EntityTreeElementPointer childElement = getChildAtIndex(childIndex);
if (childElement->alreadyFullyEncoded(params)) {
return false;
}
return true; // if we don't know otherwise than recurse!
}
bool EntityTreeElement::alreadyFullyEncoded(EncodeBitstreamParams& params) const {
auto entityNodeData = static_cast<EntityNodeData*>(params.nodeData);
assert(entityNodeData);
OctreeElementExtraEncodeData* extraEncodeData = &entityNodeData->extraEncodeData;
assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes
if (extraEncodeData->contains(this)) {
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData
= std::static_pointer_cast<EntityTreeElementExtraEncodeData>((*extraEncodeData)[this]);
// If we know that ALL subtrees below us have already been recursed, then we don't
// need to recurse this child.
return entityTreeElementExtraEncodeData->subtreeCompleted;
}
return false;
}
void EntityTreeElement::updateEncodedData(int childIndex, AppendState childAppendState, EncodeBitstreamParams& params) const {
auto entityNodeData = static_cast<EntityNodeData*>(params.nodeData);
assert(entityNodeData);
OctreeElementExtraEncodeData* extraEncodeData = &entityNodeData->extraEncodeData;
assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes
if (extraEncodeData->contains(this)) {
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData
= std::static_pointer_cast<EntityTreeElementExtraEncodeData>((*extraEncodeData)[this]);
if (childAppendState == OctreeElement::COMPLETED) {
entityTreeElementExtraEncodeData->childCompleted[childIndex] = true;
}
} else {
assert(false); // this shouldn't happen!
}
}
void EntityTreeElement::elementEncodeComplete(EncodeBitstreamParams& params) const {
const bool wantDebug = false;
if (wantDebug) {
qCDebug(entities) << "EntityTreeElement::elementEncodeComplete() element:" << _cube;
}
auto entityNodeData = static_cast<EntityNodeData*>(params.nodeData);
assert(entityNodeData);
OctreeElementExtraEncodeData* extraEncodeData = &entityNodeData->extraEncodeData;
assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes
assert(extraEncodeData->contains(this));
EntityTreeElementExtraEncodeDataPointer thisExtraEncodeData
= std::static_pointer_cast<EntityTreeElementExtraEncodeData>((*extraEncodeData)[this]);
// Note: this will be called when OUR element has finished running through encodeTreeBitstreamRecursion()
// which means, it's possible that our parent element hasn't finished encoding OUR data... so
// in this case, our children may be complete, and we should clean up their encode data...
// but not necessarily cleanup our own encode data...
//
// If we're really complete here's what must be true...
// 1) our own data must be complete
// 2) the data for all our immediate children must be complete.
// However, the following might also be the case...
// 1) it's ok for our child trees to not yet be fully encoded/complete...
// SO LONG AS... the our child's node is in the bag ready for encoding
bool someChildTreeNotComplete = false;
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
EntityTreeElementPointer childElement = getChildAtIndex(i);
if (childElement) {
// why would this ever fail???
// If we've encoding this element before... but we're coming back a second time in an attempt to
// encode our parent... this might happen.
if (extraEncodeData->contains(childElement.get())) {
EntityTreeElementExtraEncodeDataPointer childExtraEncodeData
= std::static_pointer_cast<EntityTreeElementExtraEncodeData>((*extraEncodeData)[childElement.get()]);
if (wantDebug) {
qCDebug(entities) << "checking child: " << childElement->_cube;
qCDebug(entities) << " childElement->isLeaf():" << childElement->isLeaf();
qCDebug(entities) << " childExtraEncodeData->elementCompleted:" << childExtraEncodeData->elementCompleted;
qCDebug(entities) << " childExtraEncodeData->subtreeCompleted:" << childExtraEncodeData->subtreeCompleted;
}
if (childElement->isLeaf() && childExtraEncodeData->elementCompleted) {
if (wantDebug) {
qCDebug(entities) << " CHILD IS LEAF -- AND CHILD ELEMENT DATA COMPLETED!!!";
}
childExtraEncodeData->subtreeCompleted = true;
}
if (!childExtraEncodeData->elementCompleted || !childExtraEncodeData->subtreeCompleted) {
someChildTreeNotComplete = true;
}
}
}
}
if (wantDebug) {
qCDebug(entities) << "for this element: " << _cube;
qCDebug(entities) << " WAS elementCompleted:" << thisExtraEncodeData->elementCompleted;
qCDebug(entities) << " WAS subtreeCompleted:" << thisExtraEncodeData->subtreeCompleted;
}
thisExtraEncodeData->subtreeCompleted = !someChildTreeNotComplete;
if (wantDebug) {
qCDebug(entities) << " NOW elementCompleted:" << thisExtraEncodeData->elementCompleted;
qCDebug(entities) << " NOW subtreeCompleted:" << thisExtraEncodeData->subtreeCompleted;
if (thisExtraEncodeData->subtreeCompleted) {
qCDebug(entities) << " YEAH!!!!! >>>>>>>>>>>>>> NOW subtreeCompleted:" << thisExtraEncodeData->subtreeCompleted;
}
}
}
OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData* packetData,
EncodeBitstreamParams& params) const {
OctreeElement::AppendState appendElementState = OctreeElement::COMPLETED; // assume the best...
auto entityNodeData = static_cast<EntityNodeData*>(params.nodeData);
Q_ASSERT_X(entityNodeData, "EntityTreeElement::appendElementData", "expected params.nodeData not to be null");
// first, check the params.extraEncodeData to see if there's any partial re-encode data for this element
OctreeElementExtraEncodeData* extraEncodeData = &entityNodeData->extraEncodeData;
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData = NULL;
bool hadElementExtraData = false;
if (extraEncodeData && extraEncodeData->contains(this)) {
entityTreeElementExtraEncodeData =
std::static_pointer_cast<EntityTreeElementExtraEncodeData>((*extraEncodeData)[this]);
hadElementExtraData = true;
} else {
// if there wasn't one already, then create one
entityTreeElementExtraEncodeData.reset(new EntityTreeElementExtraEncodeData());
entityTreeElementExtraEncodeData->elementCompleted = !hasContent();
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
EntityTreeElementPointer child = getChildAtIndex(i);
if (!child) {
entityTreeElementExtraEncodeData->childCompleted[i] = true; // if no child exists, it is completed
} else {
if (child->hasEntities()) {
entityTreeElementExtraEncodeData->childCompleted[i] = false;
} else {
// if the child doesn't have enities, it is completed
entityTreeElementExtraEncodeData->childCompleted[i] = true;
}
}
}
forEachEntity([&](EntityItemPointer entity) {
entityTreeElementExtraEncodeData->entities.insert(entity->getEntityItemID(), entity->getEntityProperties(params));
});
}
//assert(extraEncodeData);
//assert(extraEncodeData->contains(this));
//entityTreeElementExtraEncodeData = std::static_pointer_cast<EntityTreeElementExtraEncodeData>((*extraEncodeData)[this]);
LevelDetails elementLevel = packetData->startLevel();
// write our entities out... first determine which of the entities are in view based on our params
uint16_t numberOfEntities = 0;
uint16_t actualNumberOfEntities = 0;
int numberOfEntitiesOffset = 0;
withReadLock([&] {
QVector<uint16_t> indexesOfEntitiesToInclude;
// It's possible that our element has been previous completed. In this case we'll simply not include any of our
// entities for encoding. This is needed because we encode the element data at the "parent" level, and so we
// need to handle the case where our sibling elements need encoding but we don't.
if (!entityTreeElementExtraEncodeData->elementCompleted) {
// we have an EntityNodeData instance
// so we should assume that means we might have JSON filters to check
auto jsonFilters = entityNodeData->getJSONParameters();
for (uint16_t i = 0; i < _entityItems.size(); i++) {
EntityItemPointer entity = _entityItems[i];
bool includeThisEntity = true;
if (!params.forceSendScene && entity->getLastChangedOnServer() < entityNodeData->getLastTimeBagEmpty()) {
includeThisEntity = false;
}
// if this entity has been updated since our last full send and there are json filters, check them
if (includeThisEntity && !jsonFilters.isEmpty()) {
// if params include JSON filters, check if this entity matches
bool entityMatchesFilters = entity->matchesJSONFilters(jsonFilters);
if (entityMatchesFilters) {
// make sure this entity is in the set of entities sent last frame
entityNodeData->insertSentFilteredEntity(entity->getID());
} else if (entityNodeData->sentFilteredEntity(entity->getID())) {
// this entity matched in the previous frame - we send it still so the client realizes it just
// fell outside of their filter
entityNodeData->removeSentFilteredEntity(entity->getID());
} else if (!entityNodeData->isEntityFlaggedAsExtra(entity->getID())) {
// we don't send this entity because
// (1) it didn't match our filter
// (2) it didn't match our filter last frame
// (3) it isn't one the JSON query flags told us we should still include
includeThisEntity = false;
}
}
if (includeThisEntity && hadElementExtraData) {
includeThisEntity = entityTreeElementExtraEncodeData->entities.contains(entity->getEntityItemID());
}
// we only check the bounds against our frustum and LOD if the query has asked us to check against the frustum
// which can sometimes not be the case when JSON filters are sent
if (entityNodeData->getUsesFrustum() && (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
// the entity may not be in view and then in view a frame later, let the client side handle it's view
// frustum culling on rendering.
bool success;
AACube entityCube = entity->getQueryAACube(success);
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
// larger octree cell because of its position (for example if it crosses the boundary of a cell it
// pops to the next higher cell. So we want to check to see that the entity is large enough to be seen
// before we consider including it.
success = true;
// we can't cull a parent-entity by its dimensions because the child may be larger. we need to
// avoid sending details about a child but not the parent. the parent's queryAACube should have
// been adjusted to encompass the queryAACube of the child.
AABox entityBounds = entity->hasChildren() ? AABox(entityCube) : entity->getAABox(success);
if (!success) {
// if this entity is a child of an avatar, the entity-server wont be able to determine its
// AABox. If this happens, fall back to the queryAACube.
entityBounds = AABox(entityCube);
}
auto renderAccuracy = calculateRenderAccuracy(params.viewFrustum.getPosition(),
entityBounds,
params.octreeElementSizeScale,
params.boundaryLevelAdjust);
if (renderAccuracy <= 0.0f) {
includeThisEntity = false; // too small, don't include it
#ifdef WANT_LOD_DEBUGGING
qCDebug(entities) << "skipping entity - TOO SMALL - \n"
<< "......id:" << entity->getID() << "\n"
<< "....name:" << entity->getName() << "\n"
<< "..bounds:" << entityBounds << "\n"
<< "....cell:" << getAACube();
#endif
}
}
}
if (includeThisEntity) {
#ifdef WANT_LOD_DEBUGGING
qCDebug(entities) << "including entity - \n"
<< "......id:" << entity->getID() << "\n"
<< "....name:" << entity->getName() << "\n"
<< "....cell:" << getAACube();
#endif
indexesOfEntitiesToInclude << i;
numberOfEntities++;
} else {
// if the extra data included this entity, and we've decided to not include the entity, then
// we can treat it as if it was completed.
entityTreeElementExtraEncodeData->entities.remove(entity->getEntityItemID());
}
}
}
numberOfEntitiesOffset = packetData->getUncompressedByteOffset();
bool successAppendEntityCount = packetData->appendValue(numberOfEntities);
if (successAppendEntityCount) {
foreach(uint16_t i, indexesOfEntitiesToInclude) {
EntityItemPointer entity = _entityItems[i];
LevelDetails entityLevel = packetData->startLevel();
OctreeElement::AppendState appendEntityState = entity->appendEntityData(packetData,
params, entityTreeElementExtraEncodeData);
// If none of this entity data was able to be appended, then discard it
// and don't include it in our entity count
if (appendEntityState == OctreeElement::NONE) {
packetData->discardLevel(entityLevel);
} else {
// If either ALL or some of it got appended, then end the level (commit it)
// and include the entity in our final count of entities
packetData->endLevel(entityLevel);
actualNumberOfEntities++;
// If the entity item got completely appended, then we can remove it from the extra encode data
if (appendEntityState == OctreeElement::COMPLETED) {
entityTreeElementExtraEncodeData->entities.remove(entity->getEntityItemID());
}
}
// If any part of the entity items didn't fit, then the element is considered partial
// NOTE: if the entity item didn't fit or only partially fit, then the entity item should have
// added itself to the extra encode data.
if (appendEntityState != OctreeElement::COMPLETED) {
appendElementState = OctreeElement::PARTIAL;
}
}
} else {
// we we couldn't add the entity count, then we couldn't add anything for this element and we're in a NONE state
appendElementState = OctreeElement::NONE;
}
});
// If we were provided with extraEncodeData, and we allocated and/or got entityTreeElementExtraEncodeData
// then we need to do some additional processing, namely make sure our extraEncodeData is up to date for
// this octree element.
if (extraEncodeData && entityTreeElementExtraEncodeData) {
// After processing, if we are PARTIAL or COMPLETED then we need to re-include our extra data.
// Only our parent can remove our extra data in these cases and only after it knows that all of its
// children have been encoded.
//
// FIXME -- this comment seems wrong....
//
// If we weren't able to encode ANY data about ourselves, then we go ahead and remove our element data
// since that will signal that the entire element needs to be encoded on the next attempt
if (appendElementState == OctreeElement::NONE) {
if (!entityTreeElementExtraEncodeData->elementCompleted && entityTreeElementExtraEncodeData->entities.size() == 0) {
// TODO: we used to delete the extra encode data here. But changing the logic around
// this is now a dead code branch. Clean this up!
} else {
// TODO: some of these inserts might be redundant!!!
extraEncodeData->insert(this, entityTreeElementExtraEncodeData);
}
} else {
// If we weren't previously completed, check to see if we are
if (!entityTreeElementExtraEncodeData->elementCompleted) {
// If all of our items have been encoded, then we are complete as an element.
if (entityTreeElementExtraEncodeData->entities.size() == 0) {
entityTreeElementExtraEncodeData->elementCompleted = true;
}
}
// TODO: some of these inserts might be redundant!!!
extraEncodeData->insert(this, entityTreeElementExtraEncodeData);
}
}
// Determine if no entities at all were able to fit
bool noEntitiesFit = (numberOfEntities > 0 && actualNumberOfEntities == 0);
// If we wrote fewer entities than we expected, update the number of entities in our packet
bool successUpdateEntityCount = true;
if (numberOfEntities != actualNumberOfEntities) {
successUpdateEntityCount = packetData->updatePriorBytes(numberOfEntitiesOffset,
(const unsigned char*)&actualNumberOfEntities, sizeof(actualNumberOfEntities));
}
// If we weren't able to update our entity count, or we couldn't fit any entities, then
// we should discard our element and return a result of NONE
if (!successUpdateEntityCount) {
packetData->discardLevel(elementLevel);
appendElementState = OctreeElement::NONE;
} else {
if (noEntitiesFit) {
//appendElementState = OctreeElement::PARTIAL;
packetData->discardLevel(elementLevel);
appendElementState = OctreeElement::NONE;
} else {
packetData->endLevel(elementLevel);
}
}
return appendElementState;
}
bool EntityTreeElement::containsEntityBounds(EntityItemPointer entity) const {
bool success;
auto queryCube = entity->getQueryAACube(success);

View file

@ -121,17 +121,6 @@ public:
virtual bool requiresSplit() const override { return false; }
virtual void debugExtraEncodeData(EncodeBitstreamParams& params) const override;
virtual void initializeExtraEncodeData(EncodeBitstreamParams& params) override;
virtual bool shouldIncludeChildData(int childIndex, EncodeBitstreamParams& params) const override;
virtual bool shouldRecurseChildTree(int childIndex, EncodeBitstreamParams& params) const override;
virtual void updateEncodedData(int childIndex, AppendState childAppendState, EncodeBitstreamParams& params) const override;
virtual void elementEncodeComplete(EncodeBitstreamParams& params) const override;
bool alreadyFullyEncoded(EncodeBitstreamParams& params) const;
/// Override to serialize the state of this element. This is used for persistance and for transmission across the network.
virtual OctreeElement::AppendState appendElementData(OctreePacketData* packetData,
EncodeBitstreamParams& params) const override;
/// Override to deserialize the state of this element. This is used for loading from a persisted file or from reading
/// from the network.

View file

@ -186,7 +186,6 @@ int LightEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
}
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
EntityPropertyFlags LightEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
requestedProperties += PROP_IS_SPOTLIGHT;

View file

@ -128,7 +128,6 @@ int LineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
}
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
EntityPropertyFlags LineEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
requestedProperties += PROP_COLOR;

View file

@ -26,7 +26,6 @@ class LineEntityItem : public EntityItem {
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
virtual bool setProperties(const EntityItemProperties& properties) override;
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,

View file

@ -89,8 +89,6 @@ int MaterialEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* da
return bytesRead;
}
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
EntityPropertyFlags MaterialEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
requestedProperties += PROP_MATERIAL_URL;

View file

@ -32,7 +32,6 @@ public:
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
virtual bool setProperties(const EntityItemProperties& properties) override;
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,

View file

@ -144,7 +144,6 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
return bytesRead;
}
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);

View file

@ -30,7 +30,6 @@ public:
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
virtual bool setProperties(const EntityItemProperties& properties) override;
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,

View file

@ -503,8 +503,6 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch
return bytesRead;
}
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);

View file

@ -216,8 +216,6 @@ int PolyLineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* da
return bytesRead;
}
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
EntityPropertyFlags PolyLineEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
requestedProperties += PROP_COLOR;

View file

@ -26,7 +26,6 @@ class PolyLineEntityItem : public EntityItem {
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
virtual bool setProperties(const EntityItemProperties& properties) override;
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,

View file

@ -183,8 +183,6 @@ int PolyVoxEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* dat
return bytesRead;
}
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
EntityPropertyFlags PolyVoxEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
requestedProperties += PROP_VOXEL_VOLUME_SIZE;

View file

@ -26,7 +26,6 @@ class PolyVoxEntityItem : public EntityItem {
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
virtual bool setProperties(const EntityItemProperties& properties) override;
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,

View file

@ -188,8 +188,6 @@ int ShapeEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
return bytesRead;
}
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
EntityPropertyFlags ShapeEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
requestedProperties += PROP_SHAPE;

View file

@ -98,8 +98,6 @@ int TextEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
return bytesRead;
}
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
EntityPropertyFlags TextEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
requestedProperties += PROP_TEXT;

View file

@ -30,7 +30,6 @@ public:
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
virtual bool setProperties(const EntityItemProperties& properties) override;
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,

View file

@ -83,8 +83,6 @@ int WebEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, i
return bytesRead;
}
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
EntityPropertyFlags WebEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
requestedProperties += PROP_SOURCE_URL;

View file

@ -29,7 +29,6 @@ public:
virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const override;
virtual bool setProperties(const EntityItemProperties& properties) override;
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,

View file

@ -191,8 +191,6 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
return bytesRead;
}
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);

View file

@ -33,7 +33,6 @@ public:
virtual bool setProperties(const EntityItemProperties& properties) override;
virtual bool setSubClassProperties(const EntityItemProperties& properties) override;
// TODO: eventually only include properties changed since the params.nodeData->getLastTimeBagEmpty() time
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,

View file

@ -57,7 +57,7 @@ namespace graphics {
// limit range and altitude to no less than 1.0 metres
static inline float convertHazeRangeToHazeRangeFactor(const float hazeRange) { return -LOG_P_005 / glm::max(hazeRange, 1.0f); }
static inline float convertHazeAltitudeToHazeAltitudeFactor(const float hazeHeight) { return -LOG_P_005 / glm::max(hazeHeight, 1.0f); }
static inline float convertHazeAltitudeToHazeAltitudeFactor(const float hazeHeight) { return -(LOG_P_005 * glm::sign(hazeHeight)) / glm::max(glm::abs(hazeHeight), 1.0f); }
// Derivation (s is the proportion of sun blend, a is the angle at which the blend is 50%, solve for m = 0.5
// s = dot(lookAngle, sunAngle) = cos(a)

View file

@ -194,7 +194,7 @@ std::pair<std::string, std::shared_ptr<NetworkMaterial>> NetworkMaterialResource
} else if (key == "emissiveMap") {
auto value = materialJSON.value(key);
if (value.isString()) {
material->setEmissiveMap(value.toString());
material->setEmissiveMap(baseUrl.resolved(value.toString()));
}
} else if (key == "albedoMap") {
auto value = materialJSON.value(key);

View file

@ -45,7 +45,6 @@
#include "Octree.h"
#include "OctreeConstants.h"
#include "OctreeElementBag.h"
#include "OctreeLogging.h"
#include "OctreeQueryNode.h"
#include "OctreeUtils.h"
@ -57,7 +56,6 @@ Octree::Octree(bool shouldReaverage) :
_rootElement(NULL),
_isDirty(true),
_shouldReaverage(shouldReaverage),
_stopImport(false),
_isViewing(false),
_isServer(false)
{
@ -463,131 +461,6 @@ void Octree::readBitstreamToTree(const unsigned char * bitstream, uint64_t buffe
// skip bitstream to new startPoint
bitstreamAt += theseBytesRead;
bytesRead += theseBytesRead;
if (args.wantImportProgress) {
emit importProgress((100 * (bitstreamAt - bitstream)) / bufferSizeBytes);
}
}
}
void Octree::deleteOctreeElementAt(float x, float y, float z, float s) {
unsigned char* octalCode = pointToOctalCode(x,y,z,s);
deleteOctalCodeFromTree(octalCode);
delete[] octalCode; // cleanup memory
}
class DeleteOctalCodeFromTreeArgs {
public:
bool collapseEmptyTrees;
const unsigned char* codeBuffer;
int lengthOfCode;
bool deleteLastChild;
bool pathChanged;
};
// Note: uses the codeColorBuffer format, but the color's are ignored, because
// this only finds and deletes the element from the tree.
void Octree::deleteOctalCodeFromTree(const unsigned char* codeBuffer, bool collapseEmptyTrees) {
// recurse the tree while decoding the codeBuffer, once you find the element in question, recurse
// back and implement color reaveraging, and marking of lastChanged
DeleteOctalCodeFromTreeArgs args;
args.collapseEmptyTrees = collapseEmptyTrees;
args.codeBuffer = codeBuffer;
args.lengthOfCode = numberOfThreeBitSectionsInCode(codeBuffer);
args.deleteLastChild = false;
args.pathChanged = false;
withWriteLock([&] {
deleteOctalCodeFromTreeRecursion(_rootElement, &args);
});
}
void Octree::deleteOctalCodeFromTreeRecursion(const OctreeElementPointer& element, void* extraData) {
DeleteOctalCodeFromTreeArgs* args = (DeleteOctalCodeFromTreeArgs*)extraData;
int lengthOfElementCode = numberOfThreeBitSectionsInCode(element->getOctalCode());
// Since we traverse the tree in code order, we know that if our code
// matches, then we've reached our target element.
if (lengthOfElementCode == args->lengthOfCode) {
// we've reached our target, depending on how we're called we may be able to operate on it
// it here, we need to recurse up, and delete it there. So we handle these cases the same to keep
// the logic consistent.
args->deleteLastChild = true;
return;
}
// Ok, we know we haven't reached our target element yet, so keep looking
int childIndex = branchIndexWithDescendant(element->getOctalCode(), args->codeBuffer);
OctreeElementPointer childElement = element->getChildAtIndex(childIndex);
// If there is no child at the target location, and the current parent element is a colored leaf,
// then it means we were asked to delete a child out of a larger leaf voxel.
// We support this by breaking up the parent voxel into smaller pieces.
if (!childElement && element->requiresSplit()) {
// we need to break up ancestors until we get to the right level
OctreeElementPointer ancestorElement = element;
while (true) {
int index = branchIndexWithDescendant(ancestorElement->getOctalCode(), args->codeBuffer);
// we end up with all the children, even the one we want to delete
ancestorElement->splitChildren();
int lengthOfAncestorElement = numberOfThreeBitSectionsInCode(ancestorElement->getOctalCode());
// If we've reached the parent of the target, then stop breaking up children
if (lengthOfAncestorElement == (args->lengthOfCode - 1)) {
// since we created all the children when we split, we need to delete this target one
ancestorElement->deleteChildAtIndex(index);
break;
}
ancestorElement = ancestorElement->getChildAtIndex(index);
}
_isDirty = true;
args->pathChanged = true;
// ends recursion, unwinds up stack
return;
}
// if we don't have a child and we reach this point, then we actually know that the parent
// isn't a colored leaf, and the child branch doesn't exist, so there's nothing to do below and
// we can safely return, ending the recursion and unwinding
if (!childElement) {
return;
}
// If we got this far then we have a child for the branch we're looking for, but we're not there yet
// recurse till we get there
deleteOctalCodeFromTreeRecursion(childElement, args);
// If the lower level determined it needs to be deleted, then we should delete now.
if (args->deleteLastChild) {
element->deleteChildAtIndex(childIndex); // note: this will track dirtiness and lastChanged for this element
// track our tree dirtiness
_isDirty = true;
// track that path has changed
args->pathChanged = true;
// If we're in collapseEmptyTrees mode, and this was the last child of this element, then we also want
// to delete this element. This will collapse the empty tree above us.
if (args->collapseEmptyTrees && element->getChildCount() == 0) {
// Can't delete the root this way.
if (element == _rootElement) {
args->deleteLastChild = false; // reset so that further up the unwinding chain we don't do anything
}
} else {
args->deleteLastChild = false; // reset so that further up the unwinding chain we don't do anything
}
}
// If the lower level did some work, then we need to let this element know, so it can
// do any bookkeeping it wants to, like color re-averaging, time stamp marking, etc
if (args->pathChanged) {
element->handleSubtreeChanged(shared_from_this());
}
}
@ -856,720 +729,6 @@ OctreeElementPointer Octree::getElementEnclosingPoint(const glm::vec3& point, Oc
return args.element;
}
int Octree::encodeTreeBitstream(const OctreeElementPointer& element,
OctreePacketData* packetData, OctreeElementBag& bag,
EncodeBitstreamParams& params) {
// How many bytes have we written so far at this level;
int bytesWritten = 0;
// you can't call this without a valid element
if (!element) {
qCDebug(octree, "WARNING! encodeTreeBitstream() called with element=NULL");
params.stopReason = EncodeBitstreamParams::NULL_NODE;
return bytesWritten;
}
// you can't call this without a valid nodeData
auto octreeQueryNode = static_cast<OctreeQueryNode*>(params.nodeData);
if (!octreeQueryNode) {
qCDebug(octree, "WARNING! encodeTreeBitstream() called with nodeData=NULL");
params.stopReason = EncodeBitstreamParams::NULL_NODE_DATA;
return bytesWritten;
}
// 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 (octreeQueryNode->getUsesFrustum() && !params.recurseEverything && !element->isInView(params.viewFrustum)) {
params.stopReason = EncodeBitstreamParams::OUT_OF_VIEW;
return bytesWritten;
}
// write the octal code
bool roomForOctalCode = false; // assume the worst
int codeLength = 1; // assume root
if (params.chopLevels) {
unsigned char* newCode = chopOctalCode(element->getOctalCode(), params.chopLevels);
roomForOctalCode = packetData->startSubTree(newCode);
if (newCode) {
codeLength = numberOfThreeBitSectionsInCode(newCode);
delete[] newCode;
} else {
codeLength = 1;
}
} else {
roomForOctalCode = packetData->startSubTree(element->getOctalCode());
codeLength = (int)bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(element->getOctalCode()));
}
// If the octalcode couldn't fit, then we can return, because no nodes below us will fit...
if (!roomForOctalCode) {
bag.insert(element);
params.stopReason = EncodeBitstreamParams::DIDNT_FIT;
return bytesWritten;
}
bytesWritten += codeLength; // keep track of byte count
int currentEncodeLevel = 0;
// record some stats, this is the one element that we won't record below in the recursion function, so we need to
// track it here
octreeQueryNode->stats.traversed(element);
ViewFrustum::intersection parentLocationThisView = ViewFrustum::INTERSECT; // assume parent is in view, but not fully
int childBytesWritten = encodeTreeBitstreamRecursion(element, packetData, bag, params,
currentEncodeLevel, parentLocationThisView);
// if childBytesWritten == 1 then something went wrong... that's not possible
assert(childBytesWritten != 1);
// if childBytesWritten == 2, then it can only mean that the lower level trees don't exist or for some
// reason couldn't be written... so reset them here... This isn't true for the non-color included case
if (suppressEmptySubtrees() && childBytesWritten == 2) {
childBytesWritten = 0;
//params.stopReason = EncodeBitstreamParams::UNKNOWN; // possibly should be DIDNT_FIT...
}
// if we wrote child bytes, then return our result of all bytes written
if (childBytesWritten) {
bytesWritten += childBytesWritten;
} else {
// otherwise... if we didn't write any child bytes, then pretend like we also didn't write our octal code
bytesWritten = 0;
//params.stopReason = EncodeBitstreamParams::DIDNT_FIT;
}
if (bytesWritten == 0) {
packetData->discardSubTree();
} else {
packetData->endSubTree();
}
return bytesWritten;
}
int Octree::encodeTreeBitstreamRecursion(const OctreeElementPointer& element,
OctreePacketData* packetData, OctreeElementBag& bag,
EncodeBitstreamParams& params, int& currentEncodeLevel,
const ViewFrustum::intersection& parentLocationThisView) const {
const bool wantDebug = false;
// The append state of this level/element.
OctreeElement::AppendState elementAppendState = OctreeElement::COMPLETED; // assume the best
// How many bytes have we written so far at this level;
int bytesAtThisLevel = 0;
// you can't call this without a valid element
if (!element) {
qCDebug(octree, "WARNING! encodeTreeBitstreamRecursion() called with element=NULL");
params.stopReason = EncodeBitstreamParams::NULL_NODE;
return bytesAtThisLevel;
}
// you can't call this without a valid nodeData
auto octreeQueryNode = static_cast<OctreeQueryNode*>(params.nodeData);
if (!octreeQueryNode) {
qCDebug(octree, "WARNING! encodeTreeBitstream() called with nodeData=NULL");
params.stopReason = EncodeBitstreamParams::NULL_NODE_DATA;
return bytesAtThisLevel;
}
// Keep track of how deep we've encoded.
currentEncodeLevel++;
params.maxLevelReached = std::max(currentEncodeLevel, params.maxLevelReached);
// If we've reached our max Search Level, then stop searching.
if (currentEncodeLevel >= params.maxEncodeLevel) {
params.stopReason = EncodeBitstreamParams::TOO_DEEP;
return bytesAtThisLevel;
}
ViewFrustum::intersection nodeLocationThisView = ViewFrustum::INSIDE; // assume we're inside
if (octreeQueryNode->getUsesFrustum() && !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 (element->distanceToCamera(params.viewFrustum) >= boundaryDistance) {
octreeQueryNode->stats.skippedDistance(element);
params.stopReason = EncodeBitstreamParams::LOD_SKIP;
return bytesAtThisLevel;
}
// if the parent isn't known to be INSIDE, then it must be INTERSECT, and we should double check to see
// 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);
}
// If we're at a element that is out of view, then we can return, because no nodes below us will be in view!
// although technically, we really shouldn't ever be here, because our callers shouldn't be calling us if
// we're out of view
if (nodeLocationThisView == ViewFrustum::OUTSIDE) {
octreeQueryNode->stats.skippedOutOfView(element);
params.stopReason = EncodeBitstreamParams::OUT_OF_VIEW;
return bytesAtThisLevel;
}
// Ok, we are in view, but if we're in delta mode, then we also want to make sure we weren't already in view
// because we don't send nodes from the previously know in view frustum.
bool wasInView = false;
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()) {
wasInView = location != ViewFrustum::OUTSIDE;
} else {
wasInView = location == ViewFrustum::INSIDE;
}
// If we were in view, double check that we didn't switch LOD visibility... namely, the was in view doesn't
// tell us if it was so small we wouldn't have rendered it. Which may be the case. And we may have moved closer
// 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 boundaryDistance = boundaryDistanceForRenderLevel(element->getLevel() + params.boundaryLevelAdjust,
params.octreeElementSizeScale);
if (element->distanceToCamera(params.lastViewFrustum) >= boundaryDistance) {
// This would have been invisible... but now should be visible (we wouldn't be here otherwise)...
wasInView = false;
}
}
}
// If we were previously in the view, then we normally will return out of here and stop recursing. But
// 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.deltaView && element->hasChangedSince(octreeQueryNode->getLastTimeBagEmpty() - CHANGE_FUDGE))) {
octreeQueryNode->stats.skippedWasInView(element);
params.stopReason = EncodeBitstreamParams::WAS_IN_VIEW;
return bytesAtThisLevel;
}
}
// 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.deltaView &&
!element->hasChangedSince(octreeQueryNode->getLastTimeBagEmpty() - CHANGE_FUDGE)) {
octreeQueryNode->stats.skippedNoChange(element);
params.stopReason = EncodeBitstreamParams::NO_CHANGE;
return bytesAtThisLevel;
}
bool keepDiggingDeeper = true; // Assuming we're in view we have a great work ethic, we're always ready for more!
// At any given point in writing the bitstream, the largest minimum we might need to flesh out the current level
// is 1 byte for child colors + 3*NUMBER_OF_CHILDREN bytes for the actual colors + 1 byte for child trees.
// There could be sub trees below this point, which might take many more bytes, but that's ok, because we can
// always mark our subtrees as not existing and stop the packet at this point, then start up with a new packet
// for the remaining sub trees.
unsigned char childrenExistInTreeBits = 0;
unsigned char childrenExistInPacketBits = 0;
unsigned char childrenDataBits = 0;
// Make our local buffer large enough to handle writing at this level in case we need to.
LevelDetails thisLevelKey = packetData->startLevel();
int requiredBytes = sizeof(childrenDataBits) + sizeof(childrenExistInPacketBits);
if (params.includeExistsBits) {
requiredBytes += sizeof(childrenExistInTreeBits);
}
// If this datatype allows root elements to include data, and this is the root, then ask the tree for the
// minimum bytes needed for root data and reserve those also
if (element == _rootElement && rootElementHasData()) {
requiredBytes += minimumRequiredRootDataBytes();
}
bool continueThisLevel = packetData->reserveBytes(requiredBytes);
// If we can't reserve our minimum bytes then we can discard this level and return as if none of this level fits
if (!continueThisLevel) {
packetData->discardLevel(thisLevelKey);
params.stopReason = EncodeBitstreamParams::DIDNT_FIT;
bag.insert(element);
return bytesAtThisLevel;
}
int inViewCount = 0;
int inViewNotLeafCount = 0;
int inViewWithColorCount = 0;
OctreeElementPointer sortedChildren[NUMBER_OF_CHILDREN] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
float distancesToChildren[NUMBER_OF_CHILDREN] = { 0, 0, 0, 0, 0, 0, 0, 0 };
int indexOfChildren[NUMBER_OF_CHILDREN] = { 0, 0, 0, 0, 0, 0, 0, 0 };
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
OctreeElementPointer childElement = element->getChildAtIndex(i);
// if the caller wants to include childExistsBits, then include them
if (params.includeExistsBits && childElement) {
childrenExistInTreeBits += (1 << (7 - i));
}
sortedChildren[i] = childElement;
indexOfChildren[i] = i;
distancesToChildren[i] = 0.0f;
// track stats
// must check childElement here, because it could be we got here with no childElement
if (childElement) {
octreeQueryNode->stats.traversed(childElement);
}
}
// for each child element in Distance sorted order..., check to see if they exist, are colored, and in view, and if so
// add them to our distance ordered array of children
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
OctreeElementPointer childElement = sortedChildren[i];
int originalIndex = indexOfChildren[i];
bool childIsInView = (childElement &&
(params.recurseEverything || !octreeQueryNode->getUsesFrustum() ||
(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
));
if (!childIsInView) {
// must check childElement here, because it could be we got here because there was no childElement
if (childElement) {
octreeQueryNode->stats.skippedOutOfView(childElement);
}
} else {
// Before we consider this further, let's see if it's in our LOD scope...
float boundaryDistance = params.recurseEverything || !octreeQueryNode->getUsesFrustum() ? 1 :
boundaryDistanceForRenderLevel(childElement->getLevel() + params.boundaryLevelAdjust,
params.octreeElementSizeScale);
if (!(distancesToChildren[i] < boundaryDistance)) {
// don't need to check childElement here, because we can't get here with no childElement
octreeQueryNode->stats.skippedDistance(childElement);
} else {
inViewCount++;
// track children in view as existing and not a leaf, if they're a leaf,
// we don't care about recursing deeper on them, and we don't consider their
// subtree to exist
if (!(childElement && childElement->isLeaf())) {
childrenExistInPacketBits += (1 << (7 - originalIndex));
inViewNotLeafCount++;
}
bool childIsOccluded = false; // assume it's not occluded
bool shouldRender = params.recurseEverything || !octreeQueryNode->getUsesFrustum() ||
childElement->calculateShouldRender(params.viewFrustum,
params.octreeElementSizeScale, params.boundaryLevelAdjust);
// track some stats
// don't need to check childElement here, because we can't get here with no childElement
if (!shouldRender && childElement->isLeaf()) {
octreeQueryNode->stats.skippedDistance(childElement);
}
// don't need to check childElement here, because we can't get here with no childElement
if (childIsOccluded) {
octreeQueryNode->stats.skippedOccluded(childElement);
}
// track children with actual color, only if the child wasn't previously in view!
if (shouldRender && !childIsOccluded) {
bool childWasInView = false;
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()) {
childWasInView = location != ViewFrustum::OUTSIDE;
} else {
childWasInView = location == ViewFrustum::INSIDE;
}
}
// If our child wasn't in view (or we're ignoring wasInView) then we add it to our sending items.
// 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.deltaView &&
childElement->hasChangedSince(octreeQueryNode->getLastTimeBagEmpty() - CHANGE_FUDGE))){
childrenDataBits += (1 << (7 - originalIndex));
inViewWithColorCount++;
} else {
// otherwise just track stats of the items we discarded
// don't need to check childElement here, because we can't get here with no childElement
if (childWasInView) {
octreeQueryNode->stats.skippedWasInView(childElement);
} else {
octreeQueryNode->stats.skippedNoChange(childElement);
}
}
}
}
}
}
// NOTE: the childrenDataBits indicates that there is an array of child element data included in this packet.
// We will write this bit mask but we may come back later and update the bits that are actually included
packetData->releaseReservedBytes(sizeof(childrenDataBits));
continueThisLevel = packetData->appendBitMask(childrenDataBits);
int childDataBitsPlaceHolder = packetData->getUncompressedByteOffset(sizeof(childrenDataBits));
unsigned char actualChildrenDataBits = 0;
assert(continueThisLevel); // since we used reserved bits, this really shouldn't fail
bytesAtThisLevel += sizeof(childrenDataBits); // keep track of byte count
octreeQueryNode->stats.colorBitsWritten(); // really data bits not just color bits
// NOW might be a good time to give our tree subclass and this element a chance to set up and check any extra encode data
element->initializeExtraEncodeData(params);
// write the child element data...
// NOTE: the format of the bitstream is generally this:
// [octalcode]
// [bitmask for existence of child data]
// N x [child data]
// [bitmask for existence of child elements in tree]
// [bitmask for existence of child elements in buffer]
// N x [ ... tree for children ...]
//
// This section of the code, is writing the "N x [child data]" portion of this bitstream
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
if (oneAtBit(childrenDataBits, i)) {
OctreeElementPointer childElement = element->getChildAtIndex(i);
// the childrenDataBits were set up by the in view/LOD logic, it may contain children that we've already
// processed and sent the data bits for. Let our tree subclass determine if it really wants to send the
// data for this child at this point
if (childElement && element->shouldIncludeChildData(i, params)) {
int bytesBeforeChild = packetData->getUncompressedSize();
// a childElement may "partially" write it's data. for example, the model server where the entire
// contents of the element may be larger than can fit in a single MTU/packetData. In this case,
// we want to allow the appendElementData() to respond that it produced partial data, which should be
// written, but that the childElement needs to be reprocessed in an additional pass or passes
// to be completed.
LevelDetails childDataLevelKey = packetData->startLevel();
OctreeElement::AppendState childAppendState = childElement->appendElementData(packetData, params);
// allow our tree subclass to do any additional bookkeeping it needs to do with encoded data state
element->updateEncodedData(i, childAppendState, params);
// Continue this level so long as some part of this child element was appended.
bool childFit = (childAppendState != OctreeElement::NONE);
// some datatypes (like Voxels) assume that all child data will fit, if it doesn't fit
// the data type wants to bail on this element level completely
if (!childFit && mustIncludeAllChildData()) {
continueThisLevel = false;
break;
}
// If the child was partially or fully appended, then mark the actualChildrenDataBits as including
// this child data
if (childFit) {
actualChildrenDataBits += (1 << (7 - i));
continueThisLevel = packetData->endLevel(childDataLevelKey);
} else {
packetData->discardLevel(childDataLevelKey);
elementAppendState = OctreeElement::PARTIAL;
params.stopReason = EncodeBitstreamParams::DIDNT_FIT;
}
// If this child was partially appended, then consider this element to be partially appended
if (childAppendState == OctreeElement::PARTIAL) {
elementAppendState = OctreeElement::PARTIAL;
params.stopReason = EncodeBitstreamParams::DIDNT_FIT;
}
int bytesAfterChild = packetData->getUncompressedSize();
bytesAtThisLevel += (bytesAfterChild - bytesBeforeChild); // keep track of byte count for this child
// don't need to check childElement here, because we can't get here with no childElement
if (childAppendState != OctreeElement::NONE) {
octreeQueryNode->stats.colorSent(childElement);
}
}
}
}
if (!mustIncludeAllChildData() && !continueThisLevel) {
qCDebug(octree) << "WARNING UNEXPECTED CASE: reached end of child element data loop with continueThisLevel=FALSE";
qCDebug(octree) << "This is not expected!!!! -- continueThisLevel=FALSE....";
}
if (continueThisLevel && actualChildrenDataBits != childrenDataBits) {
// repair the child data mask
continueThisLevel = packetData->updatePriorBitMask(childDataBitsPlaceHolder, actualChildrenDataBits);
if (!continueThisLevel) {
qCDebug(octree) << "WARNING UNEXPECTED CASE: Failed to update childDataBitsPlaceHolder";
qCDebug(octree) << "This is not expected!!!! -- continueThisLevel=FALSE....";
}
}
// if the caller wants to include childExistsBits, then include them even if not in view, put them before the
// childrenExistInPacketBits, so that the lower code can properly repair the packet exists bits
if (continueThisLevel && params.includeExistsBits) {
packetData->releaseReservedBytes(sizeof(childrenExistInTreeBits));
continueThisLevel = packetData->appendBitMask(childrenExistInTreeBits);
if (continueThisLevel) {
bytesAtThisLevel += sizeof(childrenExistInTreeBits); // keep track of byte count
octreeQueryNode->stats.existsBitsWritten();
} else {
qCDebug(octree) << "WARNING UNEXPECTED CASE: Failed to append childrenExistInTreeBits";
qCDebug(octree) << "This is not expected!!!! -- continueThisLevel=FALSE....";
}
}
// write the child exist bits
if (continueThisLevel) {
packetData->releaseReservedBytes(sizeof(childrenExistInPacketBits));
continueThisLevel = packetData->appendBitMask(childrenExistInPacketBits);
if (continueThisLevel) {
bytesAtThisLevel += sizeof(childrenExistInPacketBits); // keep track of byte count
octreeQueryNode->stats.existsInPacketBitsWritten();
} else {
qCDebug(octree) << "WARNING UNEXPECTED CASE: Failed to append childrenExistInPacketBits";
qCDebug(octree) << "This is not expected!!!! -- continueThisLevel=FALSE....";
}
}
// We only need to keep digging, if there is at least one child that is inView, and not a leaf.
keepDiggingDeeper = (inViewNotLeafCount > 0);
//
// NOTE: the format of the bitstream is generally this:
// [octalcode]
// [bitmask for existence of child data]
// N x [child data]
// [bitmask for existence of child elements in tree]
// [bitmask for existence of child elements in buffer]
// N x [ ... tree for children ...]
//
// This section of the code, is writing the "N x [ ... tree for children ...]" portion of this bitstream
//
if (continueThisLevel && keepDiggingDeeper) {
// at this point, we need to iterate the children who are in view, even if not colored
// and we need to determine if there's a deeper tree below them that we care about.
//
// Since this recursive function assumes we're already writing, we know we've already written our
// childrenExistInPacketBits. But... we don't really know how big the child tree will be. And we don't know if
// we'll have room in our buffer to actually write all these child trees. What we kinda would like to do is
// write our childExistsBits as a place holder. Then let each potential tree have a go at it. If they
// write something, we keep them in the bits, if they don't, we take them out.
//
// we know the last thing we wrote to the packet was our childrenExistInPacketBits. Let's remember where that was!
int childExistsPlaceHolder = packetData->getUncompressedByteOffset(sizeof(childrenExistInPacketBits));
// we are also going to recurse these child trees in "distance" sorted order, but we need to pack them in the
// final packet in standard order. So what we're going to do is keep track of how big each subtree was in bytes,
// and then later reshuffle these sections of our output buffer back into normal order. This allows us to make
// a single recursive pass in distance sorted order, but retain standard order in our encoded packet
// for each child element in Distance sorted order..., check to see if they exist, are colored, and in view, and if so
// add them to our distance ordered array of children
for (int indexByDistance = 0; indexByDistance < NUMBER_OF_CHILDREN; indexByDistance++) {
OctreeElementPointer childElement = sortedChildren[indexByDistance];
int originalIndex = indexOfChildren[indexByDistance];
if (oneAtBit(childrenExistInPacketBits, originalIndex)) {
int thisLevel = currentEncodeLevel;
int childTreeBytesOut = 0;
// NOTE: some octree styles (like models and particles) will store content in parent elements, and child
// elements. In this case, if we stop recursion when we include any data (the colorbits should really be
// 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 (params.recurseEverything || !octreeQueryNode->getUsesFrustum()
|| 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.
if (element->shouldRecurseChildTree(originalIndex, params)) {
childTreeBytesOut = encodeTreeBitstreamRecursion(childElement, packetData, bag, params,
thisLevel, nodeLocationThisView);
} else {
childTreeBytesOut = 0;
}
}
// if the child wrote 0 bytes, it means that nothing below exists or was in view, or we ran out of space,
// basically, the children below don't contain any info.
// if the child tree wrote 1 byte??? something must have gone wrong... because it must have at least the color
// byte and the child exist byte.
//
assert(childTreeBytesOut != 1);
// if the child tree wrote just 2 bytes, then it means: it had no colors and no child nodes, because...
// if it had colors it would write 1 byte for the color mask,
// and at least a color's worth of bytes for the element of colors.
// if it had child trees (with something in them) then it would have the 1 byte for child mask
// and some number of bytes of lower children...
// so, if the child returns 2 bytes out, we can actually consider that an empty tree also!!
//
// we can make this act like no bytes out, by just resetting the bytes out in this case
if (suppressEmptySubtrees() && !params.includeExistsBits && childTreeBytesOut == 2) {
childTreeBytesOut = 0; // this is the degenerate case of a tree with no colors and no child trees
}
bytesAtThisLevel += childTreeBytesOut;
// If we had previously started writing, and if the child DIDN'T write any bytes,
// then we want to remove their bit from the childExistsPlaceHolder bitmask
if (childTreeBytesOut == 0) {
// remove this child's bit...
childrenExistInPacketBits -= (1 << (7 - originalIndex));
// repair the child exists mask
continueThisLevel = packetData->updatePriorBitMask(childExistsPlaceHolder, childrenExistInPacketBits);
if (!continueThisLevel) {
qCDebug(octree) << "WARNING UNEXPECTED CASE: Failed to update childExistsPlaceHolder";
qCDebug(octree) << "This is not expected!!!! -- continueThisLevel=FALSE....";
}
// If this is the last of the child exists bits, then we're actually be rolling out the entire tree
if (childrenExistInPacketBits == 0) {
octreeQueryNode->stats.childBitsRemoved(params.includeExistsBits);
}
if (!continueThisLevel) {
if (wantDebug) {
qCDebug(octree) << " WARNING line:" << __LINE__;
qCDebug(octree) << " breaking the child recursion loop with continueThisLevel=false!!!";
qCDebug(octree) << " AFTER attempting to updatePriorBitMask() for empty sub tree....";
qCDebug(octree) << " IS THIS ACCEPTABLE!!!!";
}
break; // can't continue...
}
// Note: no need to move the pointer, cause we already stored this
} // end if (childTreeBytesOut == 0)
} // end if (oneAtBit(childrenExistInPacketBits, originalIndex))
} // end for
} // end keepDiggingDeeper
// If we made it this far, then we've written all of our child data... if this element is the root
// element, then we also allow the root element to write out it's data...
if (continueThisLevel && element == _rootElement && rootElementHasData()) {
int bytesBeforeChild = packetData->getUncompressedSize();
// release the bytes we reserved...
packetData->releaseReservedBytes(minimumRequiredRootDataBytes());
LevelDetails rootDataLevelKey = packetData->startLevel();
OctreeElement::AppendState rootAppendState = element->appendElementData(packetData, params);
bool partOfRootFit = (rootAppendState != OctreeElement::NONE);
bool allOfRootFit = (rootAppendState == OctreeElement::COMPLETED);
if (partOfRootFit) {
continueThisLevel = packetData->endLevel(rootDataLevelKey);
if (!continueThisLevel) {
qCDebug(octree) << " UNEXPECTED ROOT ELEMENT -- could not packetData->endLevel(rootDataLevelKey) -- line:" << __LINE__;
}
} else {
packetData->discardLevel(rootDataLevelKey);
}
if (!allOfRootFit) {
elementAppendState = OctreeElement::PARTIAL;
params.stopReason = EncodeBitstreamParams::DIDNT_FIT;
}
// do we really ever NOT want to continue this level???
//continueThisLevel = (rootAppendState == OctreeElement::COMPLETED);
int bytesAfterChild = packetData->getUncompressedSize();
if (continueThisLevel) {
bytesAtThisLevel += (bytesAfterChild - bytesBeforeChild); // keep track of byte count for this child
octreeQueryNode->stats.colorSent(element);
}
if (!continueThisLevel) {
qCDebug(octree) << "WARNING UNEXPECTED CASE: Something failed in packing ROOT data";
qCDebug(octree) << "This is not expected!!!! -- continueThisLevel=FALSE....";
}
}
// if we were unable to fit this level in our packet, then rewind and add it to the element bag for
// sending later...
if (continueThisLevel) {
continueThisLevel = packetData->endLevel(thisLevelKey);
} else {
packetData->discardLevel(thisLevelKey);
if (!mustIncludeAllChildData()) {
qCDebug(octree) << "WARNING UNEXPECTED CASE: Something failed in attempting to pack this element";
qCDebug(octree) << "This is not expected!!!! -- continueThisLevel=FALSE....";
}
}
// This happens if the element could not be written at all. In the case of Octree's that support partial
// element data, continueThisLevel will be true. So this only happens if the full element needs to be
// added back to the element bag.
if (!continueThisLevel) {
if (!mustIncludeAllChildData()) {
qCDebug(octree) << "WARNING UNEXPECTED CASE - Something failed in attempting to pack this element.";
qCDebug(octree) << " If the datatype requires all child data, then this might happen. Otherwise" ;
qCDebug(octree) << " this is an unexpected case and we should research a potential logic error." ;
}
bag.insert(element);
// don't need to check element here, because we can't get here with no element
octreeQueryNode->stats.didntFit(element);
params.stopReason = EncodeBitstreamParams::DIDNT_FIT;
bytesAtThisLevel = 0; // didn't fit
} else {
// assuming we made it here with continueThisLevel == true, we STILL might want
// to add our element back to the bag for additional encoding, specifically if
// the appendState is PARTIAL, in this case, we re-add our element to the bag
// and assume that the appendElementData() has stored any required state data
// in the params extraEncodeData
if (elementAppendState == OctreeElement::PARTIAL) {
bag.insert(element);
}
}
// If our element is completed let the element know so it can do any cleanup it of extra wants
if (elementAppendState == OctreeElement::COMPLETED) {
element->elementEncodeComplete(params);
}
return bytesAtThisLevel;
}
bool Octree::readFromFile(const char* fileName) {
QString qFileName = findMostRecentFileExtension(fileName, PERSIST_EXTENSIONS);
@ -1588,14 +747,10 @@ bool Octree::readFromFile(const char* fileName) {
QFileInfo fileInfo(qFileName);
uint64_t fileLength = fileInfo.size();
emit importSize(1.0f, 1.0f, 1.0f);
emit importProgress(0);
qCDebug(octree) << "Loading file" << qFileName << "...";
bool success = readFromStream(fileLength, fileInputStream);
emit importProgress(100);
file.close();
return success;
@ -1836,7 +991,3 @@ bool Octree::countOctreeElementsOperation(const OctreeElementPointer& element, v
(*(uint64_t*)extraData)++;
return true; // keep going
}
void Octree::cancelImport() {
_stopImport = true;
}

View file

@ -49,126 +49,62 @@ public:
// Callback function, for recuseTreeWithOperation
using RecurseOctreeOperation = std::function<bool(const OctreeElementPointer&, void*)>;
typedef enum {GRADIENT, RANDOM, NATURAL} creationMode;
typedef QHash<uint, AACube> CubeList;
const bool NO_EXISTS_BITS = false;
const bool WANT_EXISTS_BITS = true;
const bool COLLAPSE_EMPTY_TREE = true;
const bool DONT_COLLAPSE = false;
const int DONT_CHOP = 0;
const int NO_BOUNDARY_ADJUST = 0;
const int LOW_RES_MOVING_ADJUST = 1;
#define IGNORE_COVERAGE_MAP NULL
class EncodeBitstreamParams {
public:
ViewFrustum viewFrustum;
ViewFrustum lastViewFrustum;
int maxEncodeLevel;
int maxLevelReached;
bool includeExistsBits;
int chopLevels;
bool deltaView;
bool recurseEverything { false };
int boundaryLevelAdjust;
float octreeElementSizeScale;
bool forceSendScene;
NodeData* nodeData;
// output hints from the encode process
typedef enum {
UNKNOWN,
DIDNT_FIT,
NULL_NODE,
NULL_NODE_DATA,
TOO_DEEP,
LOD_SKIP,
OUT_OF_VIEW,
WAS_IN_VIEW,
NO_CHANGE,
OCCLUDED,
FINISHED
} reason;
reason stopReason;
EncodeBitstreamParams(
int maxEncodeLevel = INT_MAX,
bool includeExistsBits = WANT_EXISTS_BITS,
int chopLevels = 0,
bool useDeltaView = false,
int boundaryLevelAdjust = NO_BOUNDARY_ADJUST,
float octreeElementSizeScale = DEFAULT_OCTREE_SIZE_SCALE,
bool forceSendScene = true,
NodeData* nodeData = nullptr) :
maxEncodeLevel(maxEncodeLevel),
maxLevelReached(0),
EncodeBitstreamParams(bool includeExistsBits = WANT_EXISTS_BITS,
NodeData* nodeData = nullptr) :
includeExistsBits(includeExistsBits),
chopLevels(chopLevels),
deltaView(useDeltaView),
boundaryLevelAdjust(boundaryLevelAdjust),
octreeElementSizeScale(octreeElementSizeScale),
forceSendScene(forceSendScene),
nodeData(nodeData),
stopReason(UNKNOWN)
{
lastViewFrustum.invalidate();
}
void displayStopReason() {
printf("StopReason: ");
switch (stopReason) {
default:
case UNKNOWN: qDebug("UNKNOWN"); break;
case DIDNT_FIT: qDebug("DIDNT_FIT"); break;
case NULL_NODE: qDebug("NULL_NODE"); break;
case TOO_DEEP: qDebug("TOO_DEEP"); break;
case LOD_SKIP: qDebug("LOD_SKIP"); break;
case OUT_OF_VIEW: qDebug("OUT_OF_VIEW"); break;
case WAS_IN_VIEW: qDebug("WAS_IN_VIEW"); break;
case NO_CHANGE: qDebug("NO_CHANGE"); break;
case OCCLUDED: qDebug("OCCLUDED"); break;
case FINISHED: qDebug("FINISHED"); break;
}
}
QString getStopReason() {
switch (stopReason) {
default:
case UNKNOWN: return QString("UNKNOWN"); break;
case DIDNT_FIT: return QString("DIDNT_FIT"); break;
case NULL_NODE: return QString("NULL_NODE"); break;
case TOO_DEEP: return QString("TOO_DEEP"); break;
case LOD_SKIP: return QString("LOD_SKIP"); break;
case OUT_OF_VIEW: return QString("OUT_OF_VIEW"); break;
case WAS_IN_VIEW: return QString("WAS_IN_VIEW"); break;
case NO_CHANGE: return QString("NO_CHANGE"); break;
case OCCLUDED: return QString("OCCLUDED"); break;
case FINISHED: return QString("FINISHED"); break;
}
}
std::function<void(const QUuid& dataID, quint64 itemLastEdited)> trackSend { [](const QUuid&, quint64){} };
};
class ReadElementBufferToTreeArgs {
public:
const unsigned char* buffer;
int length;
bool destructive;
bool pathChanged;
};
class ReadBitstreamToTreeParams {
public:
bool includeExistsBits;
OctreeElementPointer destinationElement;
QUuid sourceUUID;
SharedNodePointer sourceNode;
bool wantImportProgress;
PacketVersion bitstreamVersion;
int elementsPerPacket = 0;
int entitiesPerPacket = 0;
@ -176,15 +112,11 @@ public:
bool includeExistsBits = WANT_EXISTS_BITS,
OctreeElementPointer destinationElement = NULL,
QUuid sourceUUID = QUuid(),
SharedNodePointer sourceNode = SharedNodePointer(),
bool wantImportProgress = false,
PacketVersion bitstreamVersion = 0) :
SharedNodePointer sourceNode = SharedNodePointer()) :
includeExistsBits(includeExistsBits),
destinationElement(destinationElement),
sourceUUID(sourceUUID),
sourceNode(sourceNode),
wantImportProgress(wantImportProgress),
bitstreamVersion(bitstreamVersion)
sourceNode(sourceNode)
{}
};
@ -199,7 +131,6 @@ public:
// These methods will allow the OctreeServer to send your tree inbound edit packets of your
// own definition. Implement these to allow your octree based server to support editing
virtual bool getWantSVOfileVersions() const { return false; }
virtual PacketType expectedDataPacketType() const { return PacketType::Unknown; }
virtual PacketVersion expectedVersion() const { return versionForPacketType(expectedDataPacketType()); }
virtual bool handlesEditPacketType(PacketType packetType) const { return false; }
@ -209,12 +140,8 @@ public:
virtual void processChallengeOwnershipReplyPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { return; }
virtual void processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { return; }
virtual bool recurseChildrenWithData() const { return true; }
virtual bool rootElementHasData() const { return false; }
virtual int minimumRequiredRootDataBytes() const { return 0; }
virtual bool suppressEmptySubtrees() const { return true; }
virtual void releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncodeData) const { }
virtual bool mustIncludeAllChildData() const { return true; }
virtual void update() { } // nothing to do by default
@ -223,11 +150,8 @@ public:
virtual void eraseAllOctreeElements(bool createNewRoot = true);
virtual void readBitstreamToTree(const unsigned char* bitstream, uint64_t bufferSizeBytes, ReadBitstreamToTreeParams& args);
void deleteOctalCodeFromTree(const unsigned char* codeBuffer, bool collapseEmptyTrees = DONT_COLLAPSE);
void reaverageOctreeElements(OctreeElementPointer startElement = OctreeElementPointer());
void deleteOctreeElementAt(float x, float y, float z, float s);
/// Find the voxel at position x,y,z,s
/// \return pointer to the OctreeElement or NULL if none at x,y,z,s.
OctreeElementPointer getOctreeElementAt(float x, float y, float z, float s) const;
@ -250,8 +174,6 @@ public:
void recurseTreeWithOperator(RecurseOctreeOperator* operatorObject);
int encodeTreeBitstream(const OctreeElementPointer& element, OctreePacketData* packetData, OctreeElementBag& bag,
EncodeBitstreamParams& params) ;
bool isDirty() const { return _isDirty; }
void clearDirtyBit() { _isDirty = false; }
@ -344,22 +266,10 @@ public:
void incrementPersistDataVersion() { _persistDataVersion++; }
signals:
void importSize(float x, float y, float z);
void importProgress(int progress);
public slots:
void cancelImport();
protected:
void deleteOctalCodeFromTreeRecursion(const OctreeElementPointer& element, void* extraData);
int encodeTreeBitstreamRecursion(const OctreeElementPointer& element,
OctreePacketData* packetData, OctreeElementBag& bag,
EncodeBitstreamParams& params, int& currentEncodeLevel,
const ViewFrustum::intersection& parentLocationThisView) const;
static bool countOctreeElementsOperation(const OctreeElementPointer& element, void* extraData);
OctreeElementPointer nodeForOctalCode(const OctreeElementPointer& ancestorElement, const unsigned char* needleCode, OctreeElementPointer* parentOfFoundElement) const;
@ -374,7 +284,6 @@ protected:
bool _isDirty;
bool _shouldReaverage;
bool _stopImport;
bool _isViewing;
bool _isServer;

View file

@ -457,33 +457,6 @@ ViewFrustum::intersection OctreeElement::computeViewIntersection(const ViewFrust
return viewFrustum.calculateCubeKeyholeIntersection(_cube);
}
// There are two types of nodes for which we want to "render"
// 1) Leaves that are in the LOD
// 2) Non-leaves are more complicated though... usually you don't want to render them, but if their children
// wouldn't be rendered, then you do want to render them. But sometimes they have some children that ARE
// in the LOD, and others that are not. In this case we want to render the parent, and none of the children.
//
// 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 shouldRender = false;
if (hasContent()) {
float furthestDistance = furthestDistanceToCamera(viewFrustum);
float childBoundary = boundaryDistanceForRenderLevel(getLevel() + 1 + boundaryLevelAdjust, voxelScaleSize);
bool inChildBoundary = (furthestDistance <= childBoundary);
if (hasDetailedContent() && inChildBoundary) {
shouldRender = true;
} else {
float boundary = childBoundary * 2.0f; // the boundary is always twice the distance of the child boundary
bool inBoundary = (furthestDistance <= boundary);
shouldRender = inBoundary && !inChildBoundary;
}
}
return shouldRender;
}
// Calculates the distance to the furthest point of the voxel to the camera
// does as much math as possible in voxel scale and then scales up to TREE_SCALE at end
float OctreeElement::furthestDistanceToCamera(const ViewFrustum& viewFrustum) const {

View file

@ -85,16 +85,6 @@ public:
typedef enum { COMPLETED, PARTIAL, NONE } AppendState;
virtual void debugExtraEncodeData(EncodeBitstreamParams& params) const { }
virtual void initializeExtraEncodeData(EncodeBitstreamParams& params) { }
virtual bool shouldIncludeChildData(int childIndex, EncodeBitstreamParams& params) const { return true; }
virtual bool shouldRecurseChildTree(int childIndex, EncodeBitstreamParams& params) const { return true; }
virtual void updateEncodedData(int childIndex, AppendState childAppendState, EncodeBitstreamParams& params) const { }
virtual void elementEncodeComplete(EncodeBitstreamParams& params) const { }
/// Override to serialize the state of this element. This is used for persistance and for transmission across the network.
virtual AppendState appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const
{ return COMPLETED; }
/// Override to deserialize the state of this element. This is used for loading from a persisted file or from reading
/// from the network.
@ -139,9 +129,6 @@ public:
float distanceToCamera(const ViewFrustum& viewFrustum) const;
float furthestDistanceToCamera(const ViewFrustum& viewFrustum) const;
bool calculateShouldRender(const ViewFrustum& viewFrustum,
float voxelSizeScale = DEFAULT_OCTREE_SIZE_SCALE, int boundaryLevelAdjust = 0) const;
// points are assumed to be in Voxel Coordinates (not TREE_SCALE'd)
float distanceSquareToPoint(const glm::vec3& point) const; // when you don't need the actual distance, use this.
float distanceToPoint(const glm::vec3& point) const;

View file

@ -1,40 +0,0 @@
//
// OctreeElementBag.cpp
// libraries/octree/src
//
// Created by Brad Hefta-Gaub on 4/25/2013.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "OctreeElementBag.h"
#include <OctalCode.h>
void OctreeElementBag::deleteAll() {
_bagElements = Bag();
}
/// does the bag contain elements?
/// if all of the contained elements are expired, they will not report as empty, and
/// a single last item will be returned by extract as a null pointer
bool OctreeElementBag::isEmpty() {
return _bagElements.empty();
}
void OctreeElementBag::insert(const OctreeElementPointer& element) {
_bagElements[element.get()] = element;
}
OctreeElementPointer OctreeElementBag::extract() {
OctreeElementPointer result;
// Find the first element still alive
Bag::iterator it = _bagElements.begin();
while (it != _bagElements.end() && !result) {
result = it->second.lock();
it = _bagElements.erase(it);
}
return result;
}

View file

@ -16,30 +16,8 @@
#ifndef hifi_OctreeElementBag_h
#define hifi_OctreeElementBag_h
#include <unordered_map>
#include "OctreeElement.h"
class OctreeElementBag {
using Bag = std::unordered_map<OctreeElement*, OctreeElementWeakPointer>;
public:
void insert(const OctreeElementPointer& element); // put a element into the bag
OctreeElementPointer extract(); /// pull a element out of the bag (could come in any order) and if all of the
/// elements have expired, a single null pointer will be returned
bool isEmpty(); /// does the bag contain elements,
/// if all of the contained elements are expired, they will not report as empty, and
/// a single last item will be returned by extract as a null pointer
void deleteAll();
size_t size() const { return _bagElements.size(); }
private:
Bag _bagElements;
};
class OctreeElementExtraEncodeDataBase {
public:
OctreeElementExtraEncodeDataBase() {}

View file

@ -117,7 +117,7 @@ void OctreeProcessor::processDatagram(ReceivedMessage& message, SharedNodePointe
if (sectionLength) {
// ask the VoxelTree to read the bitstream into the tree
ReadBitstreamToTreeParams args(WANT_EXISTS_BITS, NULL,
sourceUUID, sourceNode, false, message.getVersion());
sourceUUID, sourceNode);
quint64 startUncompress, startLock = usecTimestampNow();
quint64 startReadBitsteam, endReadBitsteam;
// FIXME STUTTER - there may be an opportunity to bump this lock outside of the

View file

@ -144,11 +144,6 @@ void OctreeQueryNode::copyCurrentViewFrustum(ViewFrustum& viewOut) const {
viewOut = _currentViewFrustum;
}
void OctreeQueryNode::copyLastKnownViewFrustum(ViewFrustum& viewOut) const {
QMutexLocker viewLocker(&_viewMutex);
viewOut = _lastKnownViewFrustum;
}
bool OctreeQueryNode::updateCurrentViewFrustum() {
// if shutting down, return immediately
if (_isShuttingDown) {
@ -229,70 +224,6 @@ void OctreeQueryNode::setViewSent(bool viewSent) {
}
}
void OctreeQueryNode::updateLastKnownViewFrustum() {
// if shutting down, return immediately
if (_isShuttingDown) {
return;
}
{
QMutexLocker viewLocker(&_viewMutex);
bool frustumChanges = !_lastKnownViewFrustum.isVerySimilar(_currentViewFrustum);
if (frustumChanges) {
// save our currentViewFrustum into our lastKnownViewFrustum
_lastKnownViewFrustum = _currentViewFrustum;
}
}
// save that we know the view has been sent.
setLastTimeBagEmpty();
}
bool OctreeQueryNode::moveShouldDump() const {
// if shutting down, return immediately
if (_isShuttingDown) {
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;
return glm::distance(newPosition, oldPosition) > MAXIMUM_MOVE_WITHOUT_DUMP;
}
void OctreeQueryNode::dumpOutOfView() {
// if shutting down, return immediately
if (_isShuttingDown) {
return;
}
int stillInView = 0;
int outOfView = 0;
OctreeElementBag tempBag;
ViewFrustum viewCopy;
copyCurrentViewFrustum(viewCopy);
while (OctreeElementPointer elementToCheck = elementBag.extract()) {
if (elementToCheck->isInView(viewCopy)) {
tempBag.insert(elementToCheck);
stillInView++;
} else {
outOfView++;
}
}
if (stillInView > 0) {
while (OctreeElementPointer elementToKeepInBag = tempBag.extract()) {
if (elementToKeepInBag->isInView(viewCopy)) {
elementBag.insert(elementToKeepInBag);
}
}
}
}
void OctreeQueryNode::packetSent(const NLPacket& packet) {
_sentPacketHistory.packetSent(_sequenceNumber, packet);
_sequenceNumber++;

View file

@ -46,23 +46,14 @@ public:
bool shouldSuppressDuplicatePacket();
unsigned int getAvailable() const { return _octreePacket->bytesAvailableForWrite(); }
int getMaxSearchLevel() const { return _maxSearchLevel; }
void resetMaxSearchLevel() { _maxSearchLevel = 1; }
void incrementMaxSearchLevel() { _maxSearchLevel++; }
int getMaxLevelReached() const { return _maxLevelReachedInLastSearch; }
void setMaxLevelReached(int maxLevelReached) { _maxLevelReachedInLastSearch = maxLevelReached; }
OctreeElementBag elementBag;
OctreeElementExtraEncodeData extraEncodeData;
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
bool updateCurrentViewFrustum();
void updateLastKnownViewFrustum();
bool getViewSent() const { return _viewSent; }
void setViewSent(bool viewSent);
@ -70,24 +61,13 @@ public:
bool getViewFrustumChanging() const { return _viewFrustumChanging; }
bool getViewFrustumJustStoppedChanging() const { return _viewFrustumJustStoppedChanging; }
bool moveShouldDump() const;
quint64 getLastTimeBagEmpty() const { return _lastTimeBagEmpty; }
void setLastTimeBagEmpty() { _lastTimeBagEmpty = _sceneSendStartTime; }
bool hasLodChanged() const { return _lodChanged; }
OctreeSceneStats stats;
void dumpOutOfView();
quint64 getLastRootTimestamp() const { return _lastRootTimestamp; }
void setLastRootTimestamp(quint64 timestamp) { _lastRootTimestamp = timestamp; }
unsigned int getlastOctreePacketLength() const { return _lastOctreePacketLength; }
int getDuplicatePacketCount() const { return _duplicatePacketCount; }
void sceneStart(quint64 sceneSendStartTime) { _sceneSendStartTime = sceneSendStartTime; }
void nodeKilled();
bool isShuttingDown() const { return _isShuttingDown; }
@ -118,18 +98,11 @@ private:
int _duplicatePacketCount { 0 };
quint64 _firstSuppressedPacket { usecTimestampNow() };
int _maxSearchLevel { 1 };
int _maxLevelReachedInLastSearch { 1 };
mutable QMutex _viewMutex { QMutex::Recursive };
ViewFrustum _currentViewFrustum;
ViewFrustum _lastKnownViewFrustum;
quint64 _lastTimeBagEmpty { 0 };
bool _viewFrustumChanging { false };
bool _viewFrustumJustStoppedChanging { true };
OctreeSendThread* _octreeSendThread { nullptr };
// watch for LOD changes
int _lastClientBoundaryLevelAdjust { 0 };
float _lastClientOctreeSizeScale { DEFAULT_OCTREE_SIZE_SCALE };
@ -138,16 +111,12 @@ private:
OCTREE_PACKET_SEQUENCE _sequenceNumber { 0 };
quint64 _lastRootTimestamp { 0 };
PacketType _myPacketType { PacketType::Unknown };
bool _isShuttingDown { false };
SentPacketHistory _sentPacketHistory;
QQueue<OCTREE_PACKET_SEQUENCE> _nackedSequenceNumbers;
quint64 _sceneSendStartTime = 0;
std::array<char, udt::MAX_PACKET_SIZE> _lastOctreePayload;
QJsonObject _lastCheckJSONParameters;

View file

@ -284,10 +284,6 @@ void OctreeSceneStats::didntFit(const OctreeElementPointer& element) {
}
}
void OctreeSceneStats::colorBitsWritten() {
_colorBitsWritten++;
}
void OctreeSceneStats::existsBitsWritten() {
_existsBitsWritten++;
}

View file

@ -79,9 +79,6 @@ public:
/// Track that a element was due to be sent, but didn't fit in the packet and was moved to next packet
void didntFit(const OctreeElementPointer& element);
/// Track that the color bitmask was was sent as part of computation of a scene
void colorBitsWritten();
/// Track that the exists in tree bitmask was was sent as part of computation of a scene
void existsBitsWritten();

View file

@ -20,7 +20,6 @@
<@include LightAmbient.slh@>
<@include LightDirectional.slh@>
<@func fetchGlobalLight()@>
// Get light
Light light = getKeyLight();
@ -32,12 +31,13 @@
vec3 color = vec3(0.0);
<@endfunc@>
<@func prepareGlobalLight(isScattering)@>
<@func prepareGlobalLight(positionES, normalWS)@>
// prepareGlobalLight
// Transform directions to worldspace
vec3 fragNormal = vec3((normal));
vec3 fragEyeVector = vec3(invViewMat * vec4(-position, 0.0));
vec3 fragEyeDir = normalize(fragEyeVector);
vec3 fragNormalWS = vec3(<$normalWS$>);
vec3 fragPositionWS = vec3(invViewMat * vec4(<$positionES$>, 1.0));
vec3 fragEyeVectorWS = invViewMat[3].xyz - fragPositionWS;
vec3 fragEyeDirWS = normalize(fragEyeVectorWS);
<$fetchGlobalLight()$>
@ -46,7 +46,7 @@
<@func declareEvalAmbientGlobalColor()@>
vec3 evalAmbientGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 fresnel, float metallic, float roughness) {
<$prepareGlobalLight()$>
<$prepareGlobalLight(position, normal)$>
color += albedo * getLightColor(light) * obscurance * getLightAmbientIntensity(lightAmbient);
return color;
}
@ -67,14 +67,14 @@ vec3 albedo, vec3 fresnel, float metallic, float roughness
, float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature
<@endif@> ) {
<$prepareGlobalLight($supportScattering$)$>
<$prepareGlobalLight(position, normal)$>
SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir);
SurfaceData surfaceWS = initSurfaceData(roughness, fragNormalWS, fragEyeDirWS);
// Ambient
vec3 ambientDiffuse;
vec3 ambientSpecular;
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surfaceWS, metallic, fresnel, albedo, obscurance
<@if supportScattering@>
,scattering, midNormalCurvature, lowNormalCurvature
<@endif@> );
@ -85,7 +85,7 @@ vec3 albedo, vec3 fresnel, float metallic, float roughness
// Directional
vec3 directionalDiffuse;
vec3 directionalSpecular;
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surfaceWS, metallic, fresnel, albedo, shadowAttenuation
<@if supportScattering@>
,scattering, midNormalCurvature, lowNormalCurvature
<@endif@> );
@ -114,14 +114,14 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu
, float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature
<@endif@>
) {
<$prepareGlobalLight($supportScattering$)$>
<$prepareGlobalLight(position, normal)$>
SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir);
SurfaceData surfaceWS = initSurfaceData(roughness, fragNormalWS, fragEyeDirWS);
// Ambient
vec3 ambientDiffuse;
vec3 ambientSpecular;
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surfaceWS, metallic, fresnel, albedo, obscurance
<@if supportScattering@>
,scattering, midNormalCurvature, lowNormalCurvature
<@endif@>
@ -131,7 +131,7 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu
vec3 directionalDiffuse;
vec3 directionalSpecular;
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surfaceWS, metallic, fresnel, albedo, shadowAttenuation
<@if supportScattering@>
,scattering, midNormalCurvature, lowNormalCurvature
<@endif@>
@ -141,7 +141,7 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu
// Attenuate the light if haze effect selected
if ((hazeParams.hazeMode & HAZE_MODE_IS_KEYLIGHT_ATTENUATED) == HAZE_MODE_IS_KEYLIGHT_ATTENUATED) {
color = computeHazeColorKeyLightAttenuation(color, lightDirection, position);
color = computeHazeColorKeyLightAttenuation(color, lightDirection, fragPositionWS);
}
return color;
@ -180,9 +180,9 @@ vec3 evalLightmappedColor(mat4 invViewMat, float shadowAttenuation, float obscur
<$declareLightingDirectional()$>
vec3 evalGlobalLightingAlphaBlended(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 fresnel, float metallic, vec3 emissive, float roughness, float opacity, vec3 prevLighting) {
<$prepareGlobalLight()$>
<$prepareGlobalLight(position, normal)$>
SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir);
SurfaceData surfaceWS = initSurfaceData(roughness, fragNormalWS, fragEyeDirWS);
color = prevLighting;
color += emissive * isEmissiveEnabled();
@ -190,16 +190,15 @@ vec3 evalGlobalLightingAlphaBlended(mat4 invViewMat, float shadowAttenuation, fl
// Ambient
vec3 ambientDiffuse;
vec3 ambientSpecular;
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance);
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surfaceWS, metallic, fresnel, albedo, obscurance);
color += ambientDiffuse;
color += ambientSpecular / opacity;
// Directional
vec3 directionalDiffuse;
vec3 directionalSpecular;
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation);
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surfaceWS, metallic, fresnel, albedo, shadowAttenuation);
color += directionalDiffuse;
color += directionalSpecular / opacity;
color += (ambientSpecular + directionalSpecular) / opacity;
return color;
}
@ -211,37 +210,37 @@ vec3 evalGlobalLightingAlphaBlended(mat4 invViewMat, float shadowAttenuation, fl
<$declareLightingDirectional()$>
vec3 evalGlobalLightingAlphaBlendedWithHaze(
mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal,
mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 positionES, vec3 normalWS,
vec3 albedo, vec3 fresnel, float metallic, vec3 emissive, float roughness, float opacity)
{
<$prepareGlobalLight()$>
SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir);
<$prepareGlobalLight(positionES, normalWS)$>
SurfaceData surfaceWS = initSurfaceData(roughness, fragNormalWS, fragEyeDirWS);
color += emissive * isEmissiveEnabled();
// Ambient
vec3 ambientDiffuse;
vec3 ambientSpecular;
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance);
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surfaceWS, metallic, fresnel, albedo, obscurance);
color += ambientDiffuse;
color += ambientSpecular / opacity;
// Directional
vec3 directionalDiffuse;
vec3 directionalSpecular;
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation);
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surfaceWS, metallic, fresnel, albedo, shadowAttenuation);
color += directionalDiffuse;
color += directionalSpecular / opacity;
color += (ambientSpecular + directionalSpecular) / opacity;
// Haze
if ((hazeParams.hazeMode & HAZE_MODE_IS_ACTIVE) == HAZE_MODE_IS_ACTIVE) {
vec4 colorV4 = computeHazeColor(
vec4(color, 1.0), // fragment original color
position, // fragment position in eye coordinates
fragEyeVector, // fragment position in world coordinates
invViewMat[3].y, // eye height in world coordinates
lightDirection // keylight direction vector
positionES, // fragment position in eye coordinates
fragPositionWS, // fragment position in world coordinates
invViewMat[3].xyz, // eye position in world coordinates
lightDirection // keylight direction vector in world coordinates
);
color = colorV4.rgb;
@ -251,7 +250,7 @@ vec3 evalGlobalLightingAlphaBlendedWithHaze(
}
vec3 evalGlobalLightingAlphaBlendedWithHaze(
mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position,
mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 positionES, vec3 positionWS,
vec3 albedo, vec3 fresnel, float metallic, vec3 emissive, SurfaceData surface, float opacity, vec3 prevLighting)
{
<$fetchGlobalLight()$>
@ -276,9 +275,9 @@ vec3 evalGlobalLightingAlphaBlendedWithHaze(
if ((hazeParams.hazeMode & HAZE_MODE_IS_ACTIVE) == HAZE_MODE_IS_ACTIVE) {
vec4 colorV4 = computeHazeColor(
vec4(color, 1.0), // fragment original color
position, // fragment position in eye coordinates
surface.eyeDir, // fragment eye vector in world coordinates
invViewMat[3].y, // eye height in world coordinates
positionES, // fragment position in eye coordinates
positionWS, // fragment position in world coordinates
invViewMat[3].xyz, // eye position in world coordinates
lightDirection // keylight direction vector
);

View file

@ -20,13 +20,13 @@
<@include LightAmbient.slh@>
<@include LightDirectional.slh@>
<@func prepareGlobalLight(isScattering)@>
<@func prepareGlobalLight(positionES, normalWS)@>
// prepareGlobalLight
// Transform directions to worldspace
vec3 fragNormal = vec3((normal));
vec3 fragEyeVector = vec3(invViewMat * vec4(-1.0*position, 0.0));
vec3 fragEyeDir = normalize(fragEyeVector);
vec3 fragNormalWS = vec3(<$normalWS$>);
vec3 fragPositionWS = vec3(invViewMat * vec4(<$positionES$>, 1.0));
vec3 fragEyeVectorWS = invViewMat[3].xyz - fragPositionWS;
vec3 fragEyeDirWS = normalize(fragEyeVectorWS);
// Get light
Light light = getKeyLight();
@ -42,7 +42,7 @@
<@func declareEvalAmbientGlobalColor()@>
vec3 evalAmbientGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 fresnel, float metallic, float roughness) {
<$prepareGlobalLight()$>
<$prepareGlobalLight(position, normal)$>
color += albedo * getLightColor(light) * obscurance * getLightAmbientIntensity(lightAmbient);
return color;
}
@ -63,14 +63,14 @@ vec3 albedo, vec3 fresnel, float metallic, float roughness
, float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature
<@endif@> ) {
<$prepareGlobalLight($supportScattering$)$>
<$prepareGlobalLight(position, normal)$>
SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir);
SurfaceData surfaceWS = initSurfaceData(roughness, fragNormalWS, fragEyeDirWS);
// Ambient
vec3 ambientDiffuse;
vec3 ambientSpecular;
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surfaceWS, metallic, fresnel, albedo, obscurance
<@if supportScattering@>
,scattering, midNormalCurvature, lowNormalCurvature
<@endif@> );
@ -81,7 +81,7 @@ vec3 albedo, vec3 fresnel, float metallic, float roughness
// Directional
vec3 directionalDiffuse;
vec3 directionalSpecular;
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surfaceWS, metallic, fresnel, albedo, shadowAttenuation
<@if supportScattering@>
,scattering, midNormalCurvature, lowNormalCurvature
<@endif@> );
@ -109,14 +109,14 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu
, float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature
<@endif@>
) {
<$prepareGlobalLight($supportScattering$)$>
<$prepareGlobalLight(position, normal)$>
SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir);
SurfaceData surfaceWS = initSurfaceData(roughness, fragNormalWS, fragEyeDirWS);
// Ambient
vec3 ambientDiffuse;
vec3 ambientSpecular;
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surfaceWS, metallic, fresnel, albedo, obscurance
<@if supportScattering@>
,scattering, midNormalCurvature, lowNormalCurvature
<@endif@>
@ -126,7 +126,7 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu
vec3 directionalDiffuse;
vec3 directionalSpecular;
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surfaceWS, metallic, fresnel, albedo, shadowAttenuation
<@if supportScattering@>
,scattering, midNormalCurvature, lowNormalCurvature
<@endif@>
@ -137,7 +137,7 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu
// FIXME - temporarily removed until we support it for forward...
// Attenuate the light if haze effect selected
// if ((hazeParams.hazeMode & HAZE_MODE_IS_KEYLIGHT_ATTENUATED) == HAZE_MODE_IS_KEYLIGHT_ATTENUATED) {
// color = computeHazeColorKeyLightAttenuation(color, lightDirection, position);
// color = computeHazeColorKeyLightAttenuation(color, lightDirection, fragPositionWS);
// }
return color;
@ -180,25 +180,24 @@ vec3 evalLightmappedColor(mat4 invViewMat, float shadowAttenuation, float obscur
<$declareLightingDirectional()$>
vec3 evalGlobalLightingAlphaBlended(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 fresnel, float metallic, vec3 emissive, float roughness, float opacity) {
<$prepareGlobalLight()$>
<$prepareGlobalLight(position, normal)$>
SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir);
SurfaceData surfaceWS = initSurfaceData(roughness, fragNormalWS, fragEyeDirWS);
color += emissive * isEmissiveEnabled();
// Ambient
vec3 ambientDiffuse;
vec3 ambientSpecular;
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance);
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surfaceWS, metallic, fresnel, albedo, obscurance);
color += ambientDiffuse;
color += ambientSpecular / opacity;
// Directional
vec3 directionalDiffuse;
vec3 directionalSpecular;
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation);
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surfaceWS, metallic, fresnel, albedo, shadowAttenuation);
color += directionalDiffuse;
color += directionalSpecular / opacity;
color += (ambientSpecular + directionalSpecular) / opacity;
return color;
}
@ -207,34 +206,33 @@ vec3 evalGlobalLightingAlphaBlendedWithHaze(
mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal,
vec3 albedo, vec3 fresnel, float metallic, vec3 emissive, float roughness, float opacity)
{
<$prepareGlobalLight()$>
<$prepareGlobalLight(position, normal)$>
SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir);
SurfaceData surfaceWS = initSurfaceData(roughness, fragNormalWS, fragEyeDirWS);
color += emissive * isEmissiveEnabled();
// Ambient
vec3 ambientDiffuse;
vec3 ambientSpecular;
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance);
evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surfaceWS, metallic, fresnel, albedo, obscurance);
color += ambientDiffuse;
color += ambientSpecular / opacity;
// Directional
vec3 directionalDiffuse;
vec3 directionalSpecular;
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation);
evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surfaceWS, metallic, fresnel, albedo, shadowAttenuation);
color += directionalDiffuse;
color += directionalSpecular / opacity;
color += (ambientSpecular + directionalSpecular) / opacity;
// Haze
// FIXME - temporarily removed until we support it for forward...
/* if ((hazeParams.hazeMode & HAZE_MODE_IS_ACTIVE) == HAZE_MODE_IS_ACTIVE) {
vec4 colorV4 = computeHazeColor(
vec4(color, 1.0), // fragment original color
position, // fragment position in eye coordinates
fragEyeVector, // fragment position in world coordinates
invViewMat[3].y, // eye height in world coordinates
positionES, // fragment position in eye coordinates
fragPositionWS, // fragment position in world coordinates
invViewMat[3].xyz, // eye position in world coordinates
lightDirection // keylight direction vector
);

View file

@ -47,14 +47,14 @@ void main(void) {
}
vec4 fragColor = texture(colorMap, varTexCoord0);
vec4 eyeFragPos = unpackPositionFromZeye(varTexCoord0);
vec4 fragPositionES = unpackPositionFromZeye(varTexCoord0);
mat4 viewInverse = getViewInverse();
vec4 worldFragPos = viewInverse * eyeFragPos;
vec4 worldEyePos = viewInverse[3];
vec4 fragPositionWS = viewInverse * fragPositionES;
vec4 eyePositionWS = viewInverse[3];
Light light = getKeyLight();
vec3 lightDirection = getLightDirection(light);
vec3 lightDirectionWS = getLightDirection(light);
outFragColor = computeHazeColor(fragColor, eyeFragPos.xyz, worldFragPos.xyz, worldEyePos.y, lightDirection);
outFragColor = computeHazeColor(fragColor, fragPositionES.xyz, fragPositionWS.xyz, eyePositionWS.xyz, lightDirectionWS);
}

View file

@ -43,16 +43,16 @@ layout(std140) uniform hazeBuffer {
// Input:
// color - fragment original color
// lightDirection - parameters of the keylight
// worldFragPos - fragment position in world coordinates
// lightDirectionWS - parameters of the keylight
// fragPositionWS - fragment position in world coordinates
// Output:
// fragment colour after haze effect
//
// General algorithm taken from http://www.iquilezles.org/www/articles/fog/fog.htm, with permission
//
vec3 computeHazeColorKeyLightAttenuation(vec3 color, vec3 lightDirection, vec3 worldFragPos) {
vec3 computeHazeColorKeyLightAttenuation(vec3 color, vec3 lightDirectionWS, vec3 fragPositionWS) {
// Directional light attenuation is simulated by assuming the light source is at a fixed height above the
// fragment. This height is where the haze density is reduced by 95% from the haze at the fragment's height
// fragment. This height is where the haze density is reduced by 95% from the haze at the fragment's height
//
// The distance is computed from the height and the directional light orientation
// The distance is limited to height * 1,000, which gives an angle of ~0.057 degrees
@ -65,7 +65,7 @@ vec3 computeHazeColorKeyLightAttenuation(vec3 color, vec3 lightDirection, vec3 w
}
// Note that we need the sine to be positive
float sin_pitch = abs(lightDirection.y);
float sin_pitch = abs(lightDirectionWS.y);
float distance;
const float minimumSinPitch = 0.001;
@ -79,7 +79,7 @@ vec3 computeHazeColorKeyLightAttenuation(vec3 color, vec3 lightDirection, vec3 w
// Note that the haze base reference affects only the haze density as function of altitude
float hazeDensityDistribution =
hazeParams.hazeKeyLightRangeFactor *
exp(-hazeParams.hazeKeyLightAltitudeFactor * (worldFragPos.y - hazeParams.hazeBaseReference));
exp(-hazeParams.hazeKeyLightAltitudeFactor * (fragPositionWS.y - hazeParams.hazeBaseReference));
float hazeIntegral = hazeDensityDistribution * distance;
@ -92,33 +92,34 @@ vec3 computeHazeColorKeyLightAttenuation(vec3 color, vec3 lightDirection, vec3 w
}
// Input:
// fragColor - fragment original color
// eyeFragPos - fragment position in eye coordinates
// worldFragPos - fragment position in world coordinates
// worldEyeHeight - eye height in world coordinates
// fragColor - fragment original color
// fragPositionES - fragment position in eye coordinates
// fragPositionWS - fragment position in world coordinates
// eyePositionWS - eye position in world coordinates
// Output:
// fragment colour after haze effect
//
// General algorithm taken from http://www.iquilezles.org/www/articles/fog/fog.htm, with permission
//
vec4 computeHazeColor(vec4 fragColor, vec3 eyeFragPos, vec3 worldFragPos, float worldEyeHeight, vec3 lightDirection) {
vec4 computeHazeColor(vec4 fragColor, vec3 fragPositionES, vec3 fragPositionWS, vec3 eyePositionWS, vec3 lightDirectionWS) {
// Distance to fragment
float distance = length(eyeFragPos);
float distance = length(fragPositionES);
float eyeWorldHeight = eyePositionWS.y;
// Convert haze colour from uniform into a vec4
vec4 hazeColor = vec4(hazeParams.hazeColor, 1.0);
// Directional light component is a function of the angle from the eye, between the fragment and the sun
vec3 eyeFragDir = normalize(worldFragPos);
float glareComponent = max(0.0, dot(eyeFragDir, -lightDirection));
float power = min(1.0, pow(glareComponent, hazeParams.hazeGlareBlend));
vec4 glareColor = vec4(hazeParams.hazeGlareColor, 1.0);
// Use the haze colour for the glare colour, if blend is not enabled
vec4 blendedHazeColor;
if ((hazeParams.hazeMode & HAZE_MODE_IS_ENABLE_LIGHT_BLEND) == HAZE_MODE_IS_ENABLE_LIGHT_BLEND) {
// Directional light component is a function of the angle from the eye, between the fragment and the sun
vec3 fragToEyeDirWS = normalize(fragPositionWS - eyePositionWS);
float glareComponent = max(0.0, dot(fragToEyeDirWS, -lightDirectionWS));
float power = min(1.0, pow(glareComponent, hazeParams.hazeGlareBlend));
vec4 glareColor = vec4(hazeParams.hazeGlareColor, 1.0);
blendedHazeColor = mix(hazeColor, glareColor, power);
} else {
blendedHazeColor = hazeColor;
@ -134,12 +135,12 @@ vec4 computeHazeColor(vec4 fragColor, vec3 eyeFragPos, vec3 worldFragPos, float
// Note that the haze base reference affects only the haze density as function of altitude
vec3 hazeDensityDistribution =
hazeParams.colorModulationFactor *
exp(-hazeParams.hazeHeightFactor * (worldEyeHeight - hazeParams.hazeBaseReference));
exp(-hazeParams.hazeHeightFactor * (eyeWorldHeight - hazeParams.hazeBaseReference));
vec3 hazeIntegral = hazeDensityDistribution * distance;
vec3 hazeIntegral = hazeDensityDistribution * distance;
const float slopeThreshold = 0.01;
float deltaHeight = worldFragPos.y - worldEyeHeight;
float deltaHeight = fragPositionWS.y - eyeWorldHeight;
if (abs(deltaHeight) > slopeThreshold) {
float t = hazeParams.hazeHeightFactor * deltaHeight;
hazeIntegral *= (1.0 - exp (-t)) / t;
@ -162,12 +163,12 @@ vec4 computeHazeColor(vec4 fragColor, vec3 eyeFragPos, vec3 worldFragPos, float
// Note that the haze base reference affects only the haze density as function of altitude
float hazeDensityDistribution =
hazeParams.hazeRangeFactor *
exp(-hazeParams.hazeHeightFactor * (worldEyeHeight - hazeParams.hazeBaseReference));
exp(-hazeParams.hazeHeightFactor * (eyeWorldHeight - hazeParams.hazeBaseReference));
float hazeIntegral = hazeDensityDistribution * distance;
const float slopeThreshold = 0.01;
float deltaHeight = worldFragPos.y - worldEyeHeight;
float deltaHeight = fragPositionWS.y - eyeWorldHeight;
if (abs(deltaHeight) > slopeThreshold) {
float t = hazeParams.hazeHeightFactor * deltaHeight;
// Protect from wild values

View file

@ -228,14 +228,14 @@ vec3 fetchLightmapMap(vec2 uv) {
}
<@endfunc@>
<@func evalMaterialNormalLOD(fragPos, fetchedNormal, interpolatedNormal, interpolatedTangent, normal)@>
<@func evalMaterialNormalLOD(fragPosES, fetchedNormal, interpolatedNormal, interpolatedTangent, normal)@>
{
vec3 normalizedNormal = normalize(<$interpolatedNormal$>.xyz);
vec3 normalizedTangent = normalize(<$interpolatedTangent$>.xyz);
vec3 normalizedBitangent = cross(normalizedNormal, normalizedTangent);
// attenuate the normal map divergence from the mesh normal based on distance
// The attenuation range [20,100] meters from the eye is arbitrary for now
vec3 localNormal = mix(<$fetchedNormal$>, vec3(0.0, 1.0, 0.0), smoothstep(20.0, 100.0, (-<$fragPos$>).z));
vec3 localNormal = mix(<$fetchedNormal$>, vec3(0.0, 1.0, 0.0), smoothstep(20.0, 100.0, (-<$fragPosES$>).z));
<$normal$> = vec3(normalizedBitangent * localNormal.x + normalizedNormal * localNormal.y + normalizedTangent * localNormal.z);
}
<@endfunc@>

View file

@ -23,11 +23,11 @@
<@include MaterialTextures.slh@>
<$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, METALLIC, EMISSIVE, OCCLUSION)$>
in vec4 _position;
in vec4 _positionES;
in vec2 _texCoord0;
in vec2 _texCoord1;
in vec3 _normal;
in vec3 _tangent;
in vec3 _normalWS;
in vec3 _tangentWS;
in vec3 _color;
layout(location = 0) out vec4 _fragColor0;
@ -56,9 +56,9 @@ void main(void) {
<$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>;
vec3 fresnel = getFresnelF0(metallic, albedo);
vec3 fragPosition = _position.xyz;
vec3 fragPosition = _positionES.xyz;
vec3 fragNormal;
<$evalMaterialNormal(normalTex, _normal, _tangent, fragNormal)$>
<$evalMaterialNormal(normalTex, _normalWS, _tangentWS, fragNormal)$>
TransformCamera cam = getTransformCamera();

View file

@ -22,19 +22,19 @@
<@include Fade.slh@>
<$declareFadeFragment()$>
in vec4 _position;
in vec4 _positionES;
in vec2 _texCoord0;
in vec2 _texCoord1;
in vec3 _normal;
in vec3 _normalWS;
in vec3 _color;
in vec4 _worldPosition;
in vec4 _positionWS;
void main(void) {
vec3 fadeEmissive;
FadeObjectParams fadeParams;
<$fetchFadeObjectParams(fadeParams)$>
applyFade(fadeParams, _worldPosition.xyz, fadeEmissive);
applyFade(fadeParams, _positionWS.xyz, fadeEmissive);
Material mat = getMaterial();
BITFIELD matKey = getMaterialKey(mat);
@ -61,7 +61,7 @@ void main(void) {
float scattering = getMaterialScattering(mat);
packDeferredFragment(
normalize(_normal),
normalize(_normalWS),
opacity,
albedo,
roughness,

View file

@ -22,9 +22,9 @@
out float _alpha;
out vec2 _texCoord0;
out vec2 _texCoord1;
out vec4 _position;
out vec4 _worldPosition;
out vec3 _normal;
out vec4 _positionES;
out vec4 _positionWS;
out vec3 _normalWS;
out vec3 _color;
void main(void) {
@ -38,7 +38,7 @@ void main(void) {
// standard transform
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$>
<$transformModelToWorldPos(obj, inPosition, _worldPosition)$>
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normal)$>
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _positionES, gl_Position)$>
<$transformModelToWorldPos(obj, inPosition, _positionWS)$>
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normalWS)$>
}

View file

@ -19,11 +19,11 @@
<@include MaterialTextures.slh@>
<$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, METALLIC, EMISSIVE, OCCLUSION, SCATTERING)$>
in vec4 _position;
in vec4 _positionES;
in vec2 _texCoord0;
in vec2 _texCoord1;
in vec3 _normal;
in vec3 _tangent;
in vec3 _normalWS;
in vec3 _tangentWS;
in vec3 _color;
void main(void) {
@ -46,8 +46,8 @@ void main(void) {
vec3 emissive = getMaterialEmissive(mat);
<$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>;
vec3 fragNormal;
<$evalMaterialNormalLOD(_position, normalTex, _normal, _tangent, fragNormal)$>
vec3 fragNormalWS;
<$evalMaterialNormalLOD(_positionES, normalTex, _normalWS, _tangentWS, fragNormalWS)$>
float metallic = getMaterialMetallic(mat);
<$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>;
@ -56,7 +56,7 @@ void main(void) {
<$evalMaterialScattering(scatteringTex, scattering, matKey, scattering)$>;
packDeferredFragment(
normalize(fragNormal.xyz),
normalize(fragNormalWS.xyz),
opacity,
albedo,
roughness,

View file

@ -20,11 +20,11 @@
<@include MaterialTextures.slh@>
<$declareMaterialTexMapArrayBuffer()$>
out vec4 _position;
out vec4 _positionES;
out vec2 _texCoord0;
out vec2 _texCoord1;
out vec3 _normal;
out vec3 _tangent;
out vec3 _normalWS;
out vec3 _tangentWS;
out vec3 _color;
out float _alpha;
@ -40,7 +40,7 @@ void main(void) {
// standard transform
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$>
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normal)$>
<$transformModelToWorldDir(cam, obj, inTangent.xyz, _tangent)$>
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _positionES, gl_Position)$>
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normalWS)$>
<$transformModelToWorldDir(cam, obj, inTangent.xyz, _tangentWS)$>
}

View file

@ -2,7 +2,7 @@
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// model_normal_specular_map_fade.frag
// model_normal_map_fade.frag
// fragment shader
//
// Created by Olivier Prat on 06/05/17.
@ -22,20 +22,20 @@
<@include Fade.slh@>
<$declareFadeFragment()$>
in vec4 _position;
in vec4 _positionES;
in vec2 _texCoord0;
in vec2 _texCoord1;
in vec3 _normal;
in vec3 _tangent;
in vec3 _normalWS;
in vec3 _tangentWS;
in vec3 _color;
in vec4 _worldPosition;
in vec4 _positionWS;
void main(void) {
vec3 fadeEmissive;
FadeObjectParams fadeParams;
<$fetchFadeObjectParams(fadeParams)$>
applyFade(fadeParams, _worldPosition.xyz, fadeEmissive);
applyFade(fadeParams, _positionWS.xyz, fadeEmissive);
Material mat = getMaterial();
BITFIELD matKey = getMaterialKey(mat);
@ -56,8 +56,8 @@ void main(void) {
vec3 emissive = getMaterialEmissive(mat);
<$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>;
vec3 fragNormal;
<$evalMaterialNormalLOD(_position, normalTex, _normal, _tangent, fragNormal)$>
vec3 fragNormalWS;
<$evalMaterialNormalLOD(_positionES, normalTex, _normalWS, _tangentWS, fragNormalWS)$>
float metallic = getMaterialMetallic(mat);
<$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>;
@ -65,12 +65,12 @@ void main(void) {
float scattering = getMaterialScattering(mat);
packDeferredFragment(
normalize(fragNormal.xyz),
normalize(fragNormalWS.xyz),
opacity,
albedo,
roughness,
metallic,
emissive+fadeEmissive,
emissive + fadeEmissive,
occlusionTex,
scattering);
}

View file

@ -20,12 +20,12 @@
<@include MaterialTextures.slh@>
<$declareMaterialTexMapArrayBuffer()$>
out vec4 _position;
out vec4 _worldPosition;
out vec4 _positionES;
out vec4 _positionWS;
out vec2 _texCoord0;
out vec2 _texCoord1;
out vec3 _normal;
out vec3 _tangent;
out vec3 _normalWS;
out vec3 _tangentWS;
out vec3 _color;
out float _alpha;
@ -41,8 +41,8 @@ void main(void) {
// standard transform
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$>
<$transformModelToWorldPos(obj, inPosition, _worldPosition)$>
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normal)$>
<$transformModelToWorldDir(cam, obj, inTangent.xyz, _tangent)$>
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _positionES, gl_Position)$>
<$transformModelToWorldPos(obj, inPosition, _positionWS)$>
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normalWS)$>
<$transformModelToWorldDir(cam, obj, inTangent.xyz, _tangentWS)$>
}

View file

@ -28,9 +28,9 @@
in vec2 _texCoord0;
in vec2 _texCoord1;
in vec4 _position;
in vec4 _worldPosition;
in vec3 _normal;
in vec4 _positionES;
in vec4 _positionWS;
in vec3 _normalWS;
in vec3 _color;
in float _alpha;
@ -58,33 +58,35 @@ void main(void) {
vec3 emissive = getMaterialEmissive(mat);
<$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>;
vec3 fragPosition = _position.xyz;
vec3 fragPositionES = _positionES.xyz;
vec3 fragPositionWS = _positionWS.xyz;
// Lighting is done in world space
vec3 fragNormal = normalize(_normal);
vec3 fragNormalWS = normalize(_normalWS);
TransformCamera cam = getTransformCamera();
vec3 fragEyeVector = vec3(cam._viewInverse * vec4(-fragPosition, 0.0));
vec3 fragEyeDir = normalize(fragEyeVector);
SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir);
vec3 fragToEyeWS = cam._viewInverse[3].xyz - fragPositionWS;
vec3 fragToEyeDirWS = normalize(fragToEyeWS);
SurfaceData surfaceWS = initSurfaceData(roughness, fragNormalWS, fragToEyeDirWS);
vec4 localLighting = vec4(0.0);
<$fetchClusterInfo(_worldPosition)$>;
<$fetchClusterInfo(_positionWS)$>;
if (hasLocalLights(numLights, clusterPos, dims)) {
localLighting = evalLocalLighting(cluster, numLights, _worldPosition.xyz, surface,
localLighting = evalLocalLighting(cluster, numLights, fragPositionWS, surfaceWS,
metallic, fresnel, albedo, 0.0,
vec4(0), vec4(0), opacity);
}
_fragColor = vec4(evalGlobalLightingAlphaBlendedWithHaze(
_fragColor = vec4(evalGlobalLightingAlphaBlendedWithHaze(
cam._viewInverse,
1.0,
occlusionTex,
fragPosition,
fragPositionES,
fragPositionWS,
albedo,
fresnel,
metallic,
emissive,
surface, opacity, localLighting.rgb),
surfaceWS, opacity, localLighting.rgb),
opacity);
}

View file

@ -22,9 +22,9 @@
out float _alpha;
out vec2 _texCoord0;
out vec2 _texCoord1;
out vec4 _position;
out vec4 _worldPosition;
out vec3 _normal;
out vec4 _positionES;
out vec4 _positionWS;
out vec3 _normalWS;
out vec3 _color;
void main(void) {
@ -38,7 +38,7 @@ void main(void) {
// standard transform
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$>
<$transformModelToWorldPos(obj, inPosition, _worldPosition)$>
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normal)$>
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _positionES, gl_Position)$>
<$transformModelToWorldPos(obj, inPosition, _positionWS)$>
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normalWS)$>
}

View file

@ -24,11 +24,11 @@
in vec2 _texCoord0;
in vec2 _texCoord1;
in vec4 _position;
in vec3 _normal;
in vec4 _positionES;
in vec4 _positionWS;
in vec3 _normalWS;
in vec3 _color;
in float _alpha;
in vec4 _worldPosition;
out vec4 _fragColor;
@ -37,7 +37,7 @@ void main(void) {
FadeObjectParams fadeParams;
<$fetchFadeObjectParams(fadeParams)$>
applyFade(fadeParams, _worldPosition.xyz, fadeEmissive);
applyFade(fadeParams, _positionWS.xyz, fadeEmissive);
Material mat = getMaterial();
BITFIELD matKey = getMaterialKey(mat);
@ -60,20 +60,21 @@ void main(void) {
vec3 emissive = getMaterialEmissive(mat);
<$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>;
vec3 fragPosition = _position.xyz;
vec3 fragPositionES = _positionES.xyz;
vec3 fragPositionWS = _positionWS.xyz;
// Lighting is done in world space
vec3 fragNormal = normalize(_normal);
vec3 fragNormalWS = normalize(_normalWS);
TransformCamera cam = getTransformCamera();
vec3 fragEyeVector = vec3(cam._viewInverse * vec4(-fragPosition, 0.0));
vec3 fragEyeDir = normalize(fragEyeVector);
SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir);
vec3 fragToEyeWS = cam._viewInverse[3].xyz - fragPositionWS;
vec3 fragToEyeDirWS = normalize(fragToEyeWS);
SurfaceData surfaceWS = initSurfaceData(roughness, fragNormalWS, fragToEyeDirWS);
vec4 localLighting = vec4(0.0);
<$fetchClusterInfo(_worldPosition)$>;
<$fetchClusterInfo(_positionWS)$>;
if (hasLocalLights(numLights, clusterPos, dims)) {
localLighting = evalLocalLighting(cluster, numLights, _worldPosition.xyz, surface,
localLighting = evalLocalLighting(cluster, numLights, fragPositionWS, surfaceWS,
metallic, fresnel, albedo, 0.0,
vec4(0), vec4(0), opacity);
}
@ -82,11 +83,12 @@ void main(void) {
cam._viewInverse,
1.0,
occlusionTex,
fragPosition,
fragPositionES,
fragPositionWS,
albedo,
fresnel,
metallic,
emissive+fadeEmissive,
surface, opacity, localLighting.rgb),
emissive + fadeEmissive,
surfaceWS, opacity, localLighting.rgb),
opacity);
}

View file

@ -28,10 +28,10 @@
in vec2 _texCoord0;
in vec2 _texCoord1;
in vec4 _position;
in vec4 _worldPosition;
in vec3 _normal;
in vec3 _tangent;
in vec4 _positionES;
in vec4 _positionWS;
in vec3 _normalWS;
in vec3 _tangentWS;
in vec3 _color;
in float _alpha;
@ -59,20 +59,22 @@ void main(void) {
vec3 emissive = getMaterialEmissive(mat);
<$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>;
vec3 fragPosition = _position.xyz;
vec3 fragNormal;
<$evalMaterialNormalLOD(_position, normalTex, _normal, _tangent, fragNormal)$>
vec3 fragPositionES = _positionES.xyz;
vec3 fragPositionWS = _positionWS.xyz;
// Lighting is done in world space
vec3 fragNormalWS;
<$evalMaterialNormalLOD(_positionES, normalTex, _normalWS, _tangentWS, fragNormalWS)$>
TransformCamera cam = getTransformCamera();
vec3 fragEyeVector = vec3(cam._viewInverse * vec4(-fragPosition, 0.0));
vec3 fragEyeDir = normalize(fragEyeVector);
SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir);
vec3 fragToEyeWS = cam._viewInverse[3].xyz - fragPositionWS;
vec3 fragToEyeDirWS = normalize(fragToEyeWS);
SurfaceData surfaceWS = initSurfaceData(roughness, fragNormalWS, fragToEyeDirWS);
vec4 localLighting = vec4(0.0);
<$fetchClusterInfo(_worldPosition)$>;
<$fetchClusterInfo(_positionWS)$>;
if (hasLocalLights(numLights, clusterPos, dims)) {
localLighting = evalLocalLighting(cluster, numLights, _worldPosition.xyz, surface,
localLighting = evalLocalLighting(cluster, numLights, fragPositionWS, surfaceWS,
metallic, fresnel, albedo, 0.0,
vec4(0), vec4(0), opacity);
}
@ -81,11 +83,12 @@ void main(void) {
cam._viewInverse,
1.0,
occlusionTex,
fragPosition,
fragPositionES,
fragPositionWS,
albedo,
fresnel,
metallic,
emissive,
surface, opacity, localLighting.rgb),
surfaceWS, opacity, localLighting.rgb),
opacity);
}

View file

@ -22,10 +22,10 @@
out float _alpha;
out vec2 _texCoord0;
out vec2 _texCoord1;
out vec4 _position;
out vec4 _worldPosition;
out vec3 _normal;
out vec3 _tangent;
out vec4 _positionES;
out vec4 _positionWS;
out vec3 _normalWS;
out vec3 _tangentWS;
out vec3 _color;
void main(void) {
@ -39,8 +39,8 @@ void main(void) {
// standard transform
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$>
<$transformModelToWorldPos(obj, inPosition, _worldPosition)$>
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normal)$>
<$transformModelToWorldDir(cam, obj, inTangent.xyz, _tangent)$>
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _positionES, gl_Position)$>
<$transformModelToWorldPos(obj, inPosition, _positionWS)$>
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normalWS)$>
<$transformModelToWorldDir(cam, obj, inTangent.xyz, _tangentWS)$>
}

View file

@ -31,12 +31,12 @@
in vec2 _texCoord0;
in vec2 _texCoord1;
in vec4 _position;
in vec3 _normal;
in vec3 _tangent;
in vec4 _positionES;
in vec3 _normalWS;
in vec3 _tangentWS;
in vec3 _color;
in float _alpha;
in vec4 _worldPosition;
in vec4 _positionWS;
out vec4 _fragColor;
@ -45,7 +45,7 @@ void main(void) {
FadeObjectParams fadeParams;
<$fetchFadeObjectParams(fadeParams)$>
applyFade(fadeParams, _worldPosition.xyz, fadeEmissive);
applyFade(fadeParams, _positionWS.xyz, fadeEmissive);
Material mat = getMaterial();
int matKey = getMaterialKey(mat);
@ -68,21 +68,22 @@ void main(void) {
vec3 emissive = getMaterialEmissive(mat);
<$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>;
vec3 fragPosition = _position.xyz;
vec3 fragPositionES = _positionES.xyz;
vec3 fragPositionWS = _positionWS.xyz;
// Lighting is done in world space
vec3 fragNormal;
<$evalMaterialNormalLOD(_position, normalTex, _normal, _tangent, fragNormal)$>
vec3 fragNormalWS;
<$evalMaterialNormalLOD(_positionES, normalTex, _normalWS, _tangentWS, fragNormalWS)$>
TransformCamera cam = getTransformCamera();
vec3 fragEyeVector = vec3(cam._viewInverse * vec4(-fragPosition, 0.0));
vec3 fragEyeDir = normalize(fragEyeVector);
SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir);
vec3 fragToEyeWS = cam._viewInverse[3].xyz - fragPositionWS;
vec3 fragToEyeDirWS = normalize(fragToEyeWS);
SurfaceData surfaceWS = initSurfaceData(roughness, fragNormalWS, fragToEyeDirWS);
vec4 localLighting = vec4(0.0);
<$fetchClusterInfo(_worldPosition)$>;
<$fetchClusterInfo(_positionWS)$>;
if (hasLocalLights(numLights, clusterPos, dims)) {
localLighting = evalLocalLighting(cluster, numLights, _worldPosition.xyz, surface,
localLighting = evalLocalLighting(cluster, numLights, fragPositionWS, surfaceWS,
metallic, fresnel, albedo, 0.0,
vec4(0), vec4(0), opacity);
}
@ -91,11 +92,12 @@ void main(void) {
cam._viewInverse,
1.0,
occlusionTex,
fragPosition,
fragPositionES,
fragPositionWS,
albedo,
fresnel,
metallic,
emissive+fadeEmissive,
surface, opacity, localLighting.rgb),
emissive + fadeEmissive,
surfaceWS, opacity, localLighting.rgb),
opacity);
}

View file

@ -24,7 +24,7 @@
in vec2 _texCoord0;
in vec3 _color;
in float _alpha;
in vec4 _worldPosition;
in vec4 _positionWS;
out vec4 _fragColor;
@ -33,7 +33,7 @@ void main(void) {
FadeObjectParams fadeParams;
<$fetchFadeObjectParams(fadeParams)$>
applyFade(fadeParams, _worldPosition.xyz, fadeEmissive);
applyFade(fadeParams, _positionWS.xyz, fadeEmissive);
Material mat = getMaterial();
BITFIELD matKey = getMaterialKey(mat);

View file

@ -23,17 +23,17 @@
<$declareMaterialTextures(ALBEDO)$>
in vec2 _texCoord0;
in vec3 _normal;
in vec3 _normalWS;
in vec3 _color;
in float _alpha;
in vec4 _worldPosition;
in vec4 _positionWS;
void main(void) {
vec3 fadeEmissive;
FadeObjectParams fadeParams;
<$fetchFadeObjectParams(fadeParams)$>
applyFade(fadeParams, _worldPosition.xyz, fadeEmissive);
applyFade(fadeParams, _positionWS.xyz, fadeEmissive);
Material mat = getMaterial();
BITFIELD matKey = getMaterialKey(mat);
@ -48,7 +48,7 @@ void main(void) {
albedo *= _color;
albedo += fadeEmissive;
packDeferredFragmentUnlit(
normalize(_normal),
normalize(_normalWS),
opacity,
albedo * isUnlitEnabled());
}

View file

@ -23,13 +23,13 @@
<@include MaterialTextures.slh@>
<$declareMaterialTexMapArrayBuffer()$>
out vec4 _position;
out vec4 _positionES;
out vec2 _texCoord0;
out vec2 _texCoord1;
out vec3 _normal;
out vec3 _normalWS;
out vec3 _color;
out float _alpha;
out vec4 _worldPosition;
out vec4 _positionWS;
void main(void) {
vec4 position = vec4(0.0, 0.0, 0.0, 0.0);
@ -48,7 +48,7 @@ void main(void) {
// standard transform
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();
<$transformModelToEyeAndClipPos(cam, obj, position, _position, gl_Position)$>
<$transformModelToWorldPos(obj, position, _worldPosition)$>
<$transformModelToWorldDir(cam, obj, interpolatedNormal.xyz, _normal.xyz)$>
<$transformModelToEyeAndClipPos(cam, obj, position, _positionES, gl_Position)$>
<$transformModelToWorldPos(obj, position, _positionWS)$>
<$transformModelToWorldDir(cam, obj, interpolatedNormal.xyz, _normalWS.xyz)$>
}

View file

@ -23,13 +23,13 @@
<@include MaterialTextures.slh@>
<$declareMaterialTexMapArrayBuffer()$>
out vec4 _position;
out vec4 _positionES;
out vec2 _texCoord0;
out vec2 _texCoord1;
out vec3 _normal;
out vec3 _normalWS;
out vec3 _color;
out float _alpha;
out vec4 _worldPosition;
out vec4 _positionWS;
void main(void) {
vec4 position = vec4(0.0, 0.0, 0.0, 0.0);
@ -48,7 +48,7 @@ void main(void) {
// standard transform
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();
<$transformModelToEyeAndClipPos(cam, obj, position, _position, gl_Position)$>
<$transformModelToWorldPos(obj, position, _worldPosition)$>
<$transformModelToWorldDir(cam, obj, interpolatedNormal.xyz, _normal.xyz)$>
<$transformModelToEyeAndClipPos(cam, obj, position, _positionES, gl_Position)$>
<$transformModelToWorldPos(obj, position, _positionWS)$>
<$transformModelToWorldDir(cam, obj, interpolatedNormal.xyz, _normalWS.xyz)$>
}

View file

@ -23,11 +23,11 @@
<@include MaterialTextures.slh@>
<$declareMaterialTexMapArrayBuffer()$>
out vec4 _position;
out vec4 _positionES;
out vec2 _texCoord0;
out vec2 _texCoord1;
out vec3 _normal;
out vec3 _tangent;
out vec3 _normalWS;
out vec3 _tangentWS;
out vec3 _color;
out float _alpha;
@ -52,10 +52,10 @@ void main(void) {
// standard transform
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();
<$transformModelToEyeAndClipPos(cam, obj, position, _position, gl_Position)$>
<$transformModelToEyeAndClipPos(cam, obj, position, _positionES, gl_Position)$>
<$transformModelToWorldDir(cam, obj, interpolatedNormal.xyz, interpolatedNormal.xyz)$>
<$transformModelToWorldDir(cam, obj, interpolatedTangent.xyz, interpolatedTangent.xyz)$>
_normal = interpolatedNormal.xyz;
_tangent = interpolatedTangent.xyz;
_normalWS = interpolatedNormal.xyz;
_tangentWS = interpolatedTangent.xyz;
}

View file

@ -23,11 +23,11 @@
<@include MaterialTextures.slh@>
<$declareMaterialTexMapArrayBuffer()$>
out vec4 _position;
out vec4 _positionES;
out vec2 _texCoord0;
out vec2 _texCoord1;
out vec3 _normal;
out vec3 _tangent;
out vec3 _normalWS;
out vec3 _tangentWS;
out vec3 _color;
out float _alpha;
@ -52,10 +52,10 @@ void main(void) {
// standard transform
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();
<$transformModelToEyeAndClipPos(cam, obj, position, _position, gl_Position)$>
<$transformModelToEyeAndClipPos(cam, obj, position, _positionES, gl_Position)$>
<$transformModelToWorldDir(cam, obj, interpolatedNormal.xyz, interpolatedNormal.xyz)$>
<$transformModelToWorldDir(cam, obj, interpolatedTangent.xyz, interpolatedTangent.xyz)$>
_normal = interpolatedNormal.xyz;
_tangent = interpolatedTangent.xyz;
_normalWS = interpolatedNormal.xyz;
_tangentWS = interpolatedTangent.xyz;
}

View file

@ -23,14 +23,14 @@
<@include MaterialTextures.slh@>
<$declareMaterialTexMapArrayBuffer()$>
out vec4 _position;
out vec4 _positionES;
out vec2 _texCoord0;
out vec2 _texCoord1;
out vec3 _normal;
out vec3 _tangent;
out vec3 _normalWS;
out vec3 _tangentWS;
out vec3 _color;
out float _alpha;
out vec4 _worldPosition;
out vec4 _positionWS;
void main(void) {
vec4 position = vec4(0.0, 0.0, 0.0, 0.0);
@ -53,11 +53,11 @@ void main(void) {
// standard transform
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();
<$transformModelToEyeAndClipPos(cam, obj, position, _position, gl_Position)$>
<$transformModelToWorldPos(obj, position, _worldPosition)$>
<$transformModelToEyeAndClipPos(cam, obj, position, _positionES, gl_Position)$>
<$transformModelToWorldPos(obj, position, _positionWS)$>
<$transformModelToWorldDir(cam, obj, interpolatedNormal.xyz, interpolatedNormal.xyz)$>
<$transformModelToWorldDir(cam, obj, interpolatedTangent.xyz, interpolatedTangent.xyz)$>
_normal = interpolatedNormal.xyz;
_tangent = interpolatedTangent.xyz;
_normalWS = interpolatedNormal.xyz;
_tangentWS = interpolatedTangent.xyz;
}

View file

@ -23,14 +23,14 @@
<@include MaterialTextures.slh@>
<$declareMaterialTexMapArrayBuffer()$>
out vec4 _position;
out vec4 _positionES;
out vec2 _texCoord0;
out vec2 _texCoord1;
out vec3 _normal;
out vec3 _tangent;
out vec3 _normalWS;
out vec3 _tangentWS;
out vec3 _color;
out float _alpha;
out vec4 _worldPosition;
out vec4 _positionWS;
void main(void) {
vec4 position = vec4(0.0, 0.0, 0.0, 0.0);
@ -53,11 +53,11 @@ void main(void) {
// standard transform
TransformCamera cam = getTransformCamera();
TransformObject obj = getTransformObject();
<$transformModelToEyeAndClipPos(cam, obj, position, _position, gl_Position)$>
<$transformModelToWorldPos(obj, position, _worldPosition)$>
<$transformModelToEyeAndClipPos(cam, obj, position, _positionES, gl_Position)$>
<$transformModelToWorldPos(obj, position, _positionWS)$>
<$transformModelToWorldDir(cam, obj, interpolatedNormal.xyz, interpolatedNormal.xyz)$>
<$transformModelToWorldDir(cam, obj, interpolatedTangent.xyz, interpolatedTangent.xyz)$>
_normal = interpolatedNormal.xyz;
_tangent = interpolatedTangent.xyz;
_normalWS = interpolatedNormal.xyz;
_tangentWS = interpolatedTangent.xyz;
}

View file

@ -248,22 +248,6 @@ void setOctalCodeSectionValue(unsigned char* octalCode, int section, char sectio
}
}
unsigned char* chopOctalCode(const unsigned char* originalOctalCode, int chopLevels) {
int codeLength = numberOfThreeBitSectionsInCode(originalOctalCode);
unsigned char* newCode = NULL;
if (codeLength > chopLevels) {
int newLength = codeLength - chopLevels;
newCode = new unsigned char[newLength+1];
*newCode = newLength; // set the length byte
for (int section = chopLevels; section < codeLength; section++) {
char sectionValue = getOctalCodeSectionValue(originalOctalCode, section);
setOctalCodeSectionValue(newCode, section - chopLevels, sectionValue);
}
}
return newCode;
}
bool isAncestorOf(const unsigned char* possibleAncestor, const unsigned char* possibleDescendent, int descendentsChild) {
if (!possibleAncestor || !possibleDescendent) {
return false;

View file

@ -40,8 +40,6 @@ const int UNKNOWN_OCTCODE_LENGTH = -2;
/// \param int maxBytes number of bytes that octalCode is expected to be, -1 if unknown
int numberOfThreeBitSectionsInCode(const unsigned char* octalCode, int maxBytes = UNKNOWN_OCTCODE_LENGTH);
unsigned char* chopOctalCode(const unsigned char* originalOctalCode, int chopLevels);
const int CHECK_NODE_ONLY = -1;
bool isAncestorOf(const unsigned char* possibleAncestor, const unsigned char* possibleDescendent,
int descendentsChild = CHECK_NODE_ONLY);

View file

@ -1303,7 +1303,7 @@ Script.scriptEnding.connect(function () {
Settings.setValue(SETTING_EDIT_PREFIX + MENU_CREATE_ENTITIES_GRABBABLE, Menu.isOptionChecked(MENU_CREATE_ENTITIES_GRABBABLE));
Settings.setValue(SETTING_EDIT_PREFIX + MENU_ALLOW_SELECTION_LARGE, Menu.isOptionChecked(MENU_ALLOW_SELECTION_LARGE));
Settings.setValue(SETTING_EDIT_PREFIX + MENU_ALLOW_SELECTION_SMALL, Menu.isOptionChecked(MENU_ALLOW_SELECTION_SMALL));
Settings.setValue(SETTING_EDIT_PREFIX + MENU_ALLOW_SELECTION_LIGHTS, Menu.isOptionChecked(GRABBABLE_ENTITIES_MENU_ITEM));
Settings.setValue(SETTING_EDIT_PREFIX + MENU_ALLOW_SELECTION_LIGHTS, Menu.isOptionChecked(MENU_ALLOW_SELECTION_LIGHTS));
progressDialog.cleanup();

View file

@ -15,7 +15,7 @@ Script.include(Script.resolvePath("../libraries/controllers.js"));
Script.include(Script.resolvePath("../libraries/Xform.js"));
var Y_AXIS = {x: 0, y: 1, z: 0};
var DEFAULT_DPI = 34;
var DEFAULT_DPI = 31;
var DEFAULT_WIDTH = 0.4375;
var DEFAULT_VERTICAL_FIELD_OF_VIEW = 45; // degrees
var SENSOR_TO_ROOM_MATRIX = -2;
@ -31,12 +31,12 @@ var DELAY_FOR_30HZ = 33; // milliseconds
// will need to be recaclulated if dimensions of fbx model change.
var TABLET_NATURAL_DIMENSIONS = {x: 33.797, y: 50.129, z: 2.269};
var TABLET_NATURAL_DIMENSIONS = {x: 32.083, y: 48.553, z: 2.269};
var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-close.png";
// var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-close.png";
// var TABLET_MODEL_PATH = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx";
var LOCAL_TABLET_MODEL_PATH = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx";
var LOCAL_TABLET_MODEL_PATH = Script.resourcesPath() + "meshes/tablet-with-home-button-small-bezel.fbx";
// returns object with two fields:
// * position - position in front of the user
@ -44,14 +44,28 @@ var LOCAL_TABLET_MODEL_PATH = Script.resourcesPath() + "meshes/tablet-with-home-
function calcSpawnInfo(hand, landscape) {
var finalPosition;
var LEFT_HAND = Controller.Standard.LeftHand;
var sensorToWorldScale = MyAvatar.sensorToWorldScale;
var headPos = (HMD.active && Camera.mode === "first person") ? HMD.position : Camera.position;
var headRot = (HMD.active && Camera.mode === "first person") ? HMD.orientation : Camera.orientation;
var dominantHandRotation = MyAvatar.getDominantHand() === "right" ? -20 : 20;
var offsetRotation = Quat.fromPitchYawRollDegrees(0, dominantHandRotation, 0);
var forward = Vec3.multiplyQbyV(offsetRotation, Quat.getForward(Quat.cancelOutRollAndPitch(headRot)));
var FORWARD_OFFSET = 0.5 * MyAvatar.sensorToWorldScale;
finalPosition = Vec3.sum(headPos, Vec3.multiply(FORWARD_OFFSET, forward));
var orientation = Quat.lookAt({x: 0, y: 0, z: 0}, forward, Vec3.multiplyQbyV(MyAvatar.orientation, Vec3.UNIT_Y));
var headRot = Quat.cancelOutRollAndPitch((HMD.active && Camera.mode === "first person") ?
HMD.orientation : Camera.orientation);
var right = Quat.getRight(headRot);
var forward = Quat.getForward(headRot);
var up = Quat.getUp(headRot);
var FORWARD_OFFSET = 0.5 * sensorToWorldScale;
var UP_OFFSET = -0.16 * sensorToWorldScale;
var RIGHT_OFFSET = ((hand === LEFT_HAND) ? -0.18 : 0.18) * sensorToWorldScale;
var forwardPosition = Vec3.sum(headPos, Vec3.multiply(FORWARD_OFFSET, forward));
var lateralPosition = Vec3.sum(forwardPosition, Vec3.multiply(RIGHT_OFFSET, right));
finalPosition = Vec3.sum(lateralPosition, Vec3.multiply(UP_OFFSET, up));
var MY_EYES = { x: 0.0, y: 0.15, z: 0.0 };
var lookAtEndPosition = Vec3.sum(Vec3.multiply(RIGHT_OFFSET, right), Vec3.multiply(FORWARD_OFFSET, forward));
var orientation = Quat.lookAt(MY_EYES, lookAtEndPosition, Vec3.multiplyQbyV(MyAvatar.orientation, Vec3.UNIT_Y));
return {
position: finalPosition,
rotation: landscape ? Quat.multiply(orientation, ROT_LANDSCAPE) : Quat.multiply(orientation, ROT_Y_180)
@ -119,11 +133,11 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) {
Overlays.deleteOverlay(this.webOverlayID);
}
var RAYPICK_OFFSET = 0.0001; // Sufficient for raypick to reliably intersect tablet screen before tablet model.
var RAYPICK_OFFSET = 0.0007; // Sufficient for raypick to reliably intersect tablet screen before tablet model.
var WEB_ENTITY_Z_OFFSET = (tabletDepth / 2.0) / sensorScaleFactor + RAYPICK_OFFSET;
var WEB_ENTITY_Y_OFFSET = 0.004;
var screenWidth = 0.82 * tabletWidth;
var screenHeight = 0.81 * tabletHeight;
var WEB_ENTITY_Y_OFFSET = 1 * tabletScaleFactor;
var screenWidth = 0.9275 * tabletWidth;
var screenHeight = 0.8983 * tabletHeight;
this.webOverlayID = Overlays.addOverlay("web3d", {
name: "WebTablet Web",
url: url,
@ -139,7 +153,7 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) {
visible: visible
});
var HOME_BUTTON_Y_OFFSET = ((tabletHeight / 2) - (tabletHeight / 20)) * (1 / sensorScaleFactor) - 0.003;
var HOME_BUTTON_Y_OFFSET = (tabletHeight / 2) - (tabletHeight / 20) + 0.003 * sensorScaleFactor;
// FIXME: Circle3D overlays currently at the wrong dimensions, so we need to account for that here
var homeButtonDim = 4.0 * tabletScaleFactor / 3.0;
this.homeButtonID = Overlays.addOverlay("circle3d", {
@ -277,8 +291,8 @@ WebTablet.prototype.setLandscape = function(newLandscapeValue) {
var tabletWidth = getTabletWidthFromSettings() * MyAvatar.sensorToWorldScale;
var tabletScaleFactor = tabletWidth / TABLET_NATURAL_DIMENSIONS.x;
var tabletHeight = TABLET_NATURAL_DIMENSIONS.y * tabletScaleFactor;
var screenWidth = 0.82 * tabletWidth;
var screenHeight = 0.81 * tabletHeight;
var screenWidth = 0.9275 * tabletWidth;
var screenHeight = 0.8983 * tabletHeight;
Overlays.editOverlay(this.webOverlayID, {
rotation: Quat.multiply(cameraOrientation, ROT_LANDSCAPE_WINDOW),
dimensions: {x: this.landscape ? screenHeight : screenWidth, y: this.landscape ? screenWidth : screenHeight, z: 0.1}

View file

@ -373,7 +373,6 @@ resizeTablet = function (width, newParentJointIndex, sensorToWorldScaleOverride)
if (!HMD.tabletID || !HMD.tabletScreenID || !HMD.homeButtonID || !HMD.homeButtonHighlightID) {
return;
}
var sensorScaleFactor = sensorToWorldScaleOverride || MyAvatar.sensorToWorldScale;
var sensorScaleOffsetOverride = 1;
var SENSOR_TO_ROOM_MATRIX = 65534;
@ -383,8 +382,8 @@ resizeTablet = function (width, newParentJointIndex, sensorToWorldScaleOverride)
}
// will need to be recaclulated if dimensions of fbx model change.
var TABLET_NATURAL_DIMENSIONS = {x: 33.797, y: 50.129, z: 2.269};
var DEFAULT_DPI = 34;
var TABLET_NATURAL_DIMENSIONS = {x: 32.083, y: 48.553, z: 2.269};
var DEFAULT_DPI = 31;
var DEFAULT_WIDTH = 0.4375;
// scale factor of natural tablet dimensions.
@ -402,9 +401,10 @@ resizeTablet = function (width, newParentJointIndex, sensorToWorldScaleOverride)
// update webOverlay
var RAYPICK_OFFSET = 0.0007; // Sufficient for raypick to reliably intersect tablet screen before tablet model.
var WEB_ENTITY_Z_OFFSET = (tabletDepth / 2.0) * sensorScaleOffsetOverride + RAYPICK_OFFSET;
var WEB_ENTITY_Y_OFFSET = 0.004 * sensorScaleFactor * sensorScaleOffsetOverride;
var screenWidth = 0.82 * tabletWidth;
var screenHeight = 0.81 * tabletHeight;
var WEB_ENTITY_Y_OFFSET = 1 * tabletScaleFactor;
print(WEB_ENTITY_Y_OFFSET);
var screenWidth = 0.9275 * tabletWidth;
var screenHeight = 0.8983 * tabletHeight;
var landscape = Tablet.getTablet("com.highfidelity.interface.tablet.system").landscape;
Overlays.editOverlay(HMD.tabletScreenID, {
localPosition: { x: 0, y: WEB_ENTITY_Y_OFFSET, z: -WEB_ENTITY_Z_OFFSET },
@ -413,7 +413,7 @@ resizeTablet = function (width, newParentJointIndex, sensorToWorldScaleOverride)
});
// update homeButton
var HOME_BUTTON_Y_OFFSET = ((tabletHeight / 2) - (tabletHeight / 20) - 0.003 * sensorScaleFactor) * sensorScaleOffsetOverride;
var HOME_BUTTON_Y_OFFSET = ((tabletHeight / 2) - (tabletHeight / 20) + 0.003 * sensorScaleFactor) * sensorScaleOffsetOverride;
// FIXME: Circle3D overlays currently at the wrong dimensions, so we need to account for that here
var homeButtonDim = 4.0 * tabletScaleFactor / 3.0;
Overlays.editOverlay(HMD.homeButtonID, {

View file

@ -160,7 +160,6 @@ int main(int argc, char** argv) {
QByteArray packet = file.readAll();
EntityItemPointer item = ShapeEntityItem::boxFactory(EntityItemID(), EntityItemProperties());
ReadBitstreamToTreeParams params;
params.bitstreamVersion = 33;
auto start = usecTimestampNow();
for (int i = 0; i < 1000; ++i) {