diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp
index 76d701f0e3..062fe39dbd 100644
--- a/assignment-client/src/octree/OctreeServer.cpp
+++ b/assignment-client/src/octree/OctreeServer.cpp
@@ -971,12 +971,7 @@ void OctreeServer::readConfiguration() {
strcpy(_persistFilename, qPrintable(persistFilename));
qDebug("persistFilename=%s", _persistFilename);
- QString persistAsFileType;
- if (!readOptionString(QString("persistAsFileType"), settingsSectionObject, persistAsFileType)) {
- persistAsFileType = "svo";
- }
- _persistAsFileType = persistAsFileType;
- qDebug() << "persistAsFileType=" << _persistAsFileType;
+ _persistAsFileType = "json.gz";
_persistInterval = OctreePersistThread::DEFAULT_PERSIST_INTERVAL;
readOptionInt(QString("persistInterval"), settingsSectionObject, _persistInterval);
diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json
index 696a87d2b8..b04ea95a23 100644
--- a/domain-server/resources/describe-settings.json
+++ b/domain-server/resources/describe-settings.json
@@ -371,30 +371,8 @@
"name": "persistFilename",
"label": "Entities Filename",
"help": "the path to the file entities are stored in. Make sure the path exists.",
- "placeholder": "resources/models.svo",
- "default": "resources/models.svo",
- "advanced": true
- },
- {
- "name": "persistAsFileType",
- "label": "File format for entity server's persistent data",
- "help": "This defines how the entity server will save entities to disk.",
- "default": "svo",
- "type": "select",
- "options": [
- {
- "value": "svo",
- "label": "Entity server persists data as SVO"
- },
- {
- "value": "json",
- "label": "Entity server persists data as JSON"
- },
- {
- "value": "json.gz",
- "label": "Entity server persists data as gzipped JSON"
- }
- ],
+ "placeholder": "resources/models.json.gz",
+ "default": "resources/models.json.gz",
"advanced": true
},
{
diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html
index a2358b40d5..ad489afddf 100644
--- a/examples/html/entityProperties.html
+++ b/examples/html/entityProperties.html
@@ -1072,6 +1072,7 @@
+
diff --git a/examples/libraries/toolBars.js b/examples/libraries/toolBars.js
index 16c68a73fb..abe8de8cc3 100644
--- a/examples/libraries/toolBars.js
+++ b/examples/libraries/toolBars.js
@@ -362,8 +362,11 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit
this.fractionKey = optionalPersistenceKey + '.fraction';
this.save = function () {
var screenSize = Controller.getViewportDimensions();
- var fraction = {x: that.x / screenSize.x, y: that.y / screenSize.y};
- Settings.setValue(this.fractionKey, JSON.stringify(fraction));
+ if (screenSize.x > 0 && screenSize.y > 0) {
+ // Guard against invalid screen size that can occur at shut-down.
+ var fraction = {x: that.x / screenSize.x, y: that.y / screenSize.y};
+ Settings.setValue(this.fractionKey, JSON.stringify(fraction));
+ }
}
} else {
this.save = function () { }; // Called on move. Can be overriden or extended by clients.
diff --git a/examples/pointer.js b/examples/pointer.js
index 2791e06466..99c2bcb23b 100644
--- a/examples/pointer.js
+++ b/examples/pointer.js
@@ -12,6 +12,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
var lineEntityID = null;
+var sphereEntityID = null;
var lineIsRezzed = false;
var BUTTON_SIZE = 32;
@@ -56,16 +57,24 @@ function nearLinePoint(targetPosition) {
function removeLine() {
- if (lineIsRezzed) {
- Entities.deleteEntity(lineEntityID);
- lineEntityID = null;
- lineIsRezzed = false;
- }
+ if (lineIsRezzed) {
+ Entities.deleteEntity(lineEntityID);
+ if (sphereEntityID) {
+ Entities.deleteEntity(sphereEntityID);
+ }
+ lineEntityID = null;
+ sphereEntityID = null;
+ lineIsRezzed = false;
+ }
}
function createOrUpdateLine(event) {
var pickRay = Camera.computePickRay(event.x, event.y);
+ if (sphereEntityID) {
+ Entities.deleteEntity(sphereEntityID);
+ sphereEntityID = null;
+ }
var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking
var props = Entities.getEntityProperties(intersection.entityID);
@@ -78,7 +87,6 @@ function createOrUpdateLine(event) {
position: MyAvatar.position,
lifetime: 15 + props.lifespan // renew lifetime
});
- // Entities.setAllPoints(lineEntityID, points);
} else {
lineIsRezzed = true;
lineEntityID = Entities.addEntity({
@@ -90,6 +98,15 @@ function createOrUpdateLine(event) {
lifetime: 15 // if someone crashes while pointing, don't leave the line there forever.
});
}
+
+ sphereEntityID = Entities.addEntity({
+ type: "Sphere",
+ position: intersection.intersection,
+ ignoreForCollisions: 1,
+ dimensions: { x: 0.6, y: 0.6, z: 0.6 },
+ color: { red: 0, green: 255, blue: 0 },
+ lifetime: 15 // if someone crashes while pointing, don't leave the line there forever.
+ });
} else {
removeLine();
}
diff --git a/examples/voxels.js b/examples/voxels.js
index 9627b40701..cc3453202a 100644
--- a/examples/voxels.js
+++ b/examples/voxels.js
@@ -1,32 +1,37 @@
var controlHeld = false;
var shiftHeld = false;
-
-function attemptVoxelChange(intersection) {
- var ids = Entities.findEntities(intersection.intersection, 10);
- var success = false;
- for (var i = 0; i < ids.length; i++) {
- var id = ids[i];
- if (controlHeld) {
- // hold control to erase a sphere
- if (Entities.setVoxelSphere(id, intersection.intersection, 1.0, 0)) {
- success = true;
- }
- } else if (shiftHeld) {
- // hold shift to set all voxels to 255
- if (Entities.setAllVoxels(id, 255)) {
- success = true;
- }
- } else {
- // no modifier key means to add a sphere
- if (Entities.setVoxelSphere(id, intersection.intersection, 1.0, 255)) {
- success = true;
- }
- }
- }
- return success;
+function floorVector(v) {
+ return {x: Math.floor(v.x), y: Math.floor(v.y), z: Math.floor(v.z)};
}
+function attemptVoxelChange(pickRayDir, intersection) {
+
+ var properties = Entities.getEntityProperties(intersection.entityID);
+ if (properties.type != "PolyVox") {
+ return false;
+ }
+
+ var voxelPosition = Entities.worldCoordsToVoxelCoords(intersection.entityID, intersection.intersection);
+ voxelPosition = Vec3.subtract(voxelPosition, {x: 0.5, y: 0.5, z: 0.5});
+ var pickRayDirInVoxelSpace = Entities.localCoordsToVoxelCoords(intersection.entityID, pickRayDir);
+ pickRayDirInVoxelSpace = Vec3.normalize(pickRayDirInVoxelSpace);
+
+ if (controlHeld) {
+ // hold control to erase a voxel
+ var toErasePosition = Vec3.sum(voxelPosition, Vec3.multiply(pickRayDirInVoxelSpace, 0.1));
+ return Entities.setVoxel(intersection.entityID, floorVector(toErasePosition), 0);
+ } else if (shiftHeld) {
+ // hold shift to set all voxels to 255
+ return Entities.setAllVoxels(intersection.entityID, 255);
+ } else {
+ // no modifier key to add a voxel
+ var toDrawPosition = Vec3.subtract(voxelPosition, Vec3.multiply(pickRayDirInVoxelSpace, 0.1));
+ return Entities.setVoxel(intersection.entityID, floorVector(toDrawPosition), 255);
+ }
+
+ // Entities.setVoxelSphere(id, intersection.intersection, radius, 0)
+}
function mousePressEvent(event) {
if (!event.isLeftButton) {
@@ -36,10 +41,8 @@ function mousePressEvent(event) {
var pickRay = Camera.computePickRay(event.x, event.y);
var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking
- // we've used a picking ray to decide where to add the new sphere of voxels. If we pick nothing
- // or if we pick a non-PolyVox entity, we fall through to the next picking attempt.
if (intersection.intersects) {
- if (attemptVoxelChange(intersection)) {
+ if (attemptVoxelChange(pickRay.direction, intersection)) {
return;
}
}
@@ -48,7 +51,7 @@ function mousePressEvent(event) {
// bounding box, instead.
intersection = Entities.findRayIntersection(pickRay, false); // bounding box picking
if (intersection.intersects) {
- attemptVoxelChange(intersection);
+ attemptVoxelChange(pickRay.direction, intersection);
}
}
diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml
index 996f9f02ef..b7081afadf 100644
--- a/interface/resources/qml/Stats.qml
+++ b/interface/resources/qml/Stats.qml
@@ -47,6 +47,11 @@ Item {
font.pixelSize: root.fontSize
text: "Framerate: " + root.framerate
}
+ Text {
+ color: root.fontColor;
+ font.pixelSize: root.fontSize
+ text: "Simrate: " + root.simrate
+ }
Text {
color: root.fontColor;
font.pixelSize: root.fontSize
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 449afae5d1..e633faf510 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -560,7 +560,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
_toolWindow = new ToolWindow();
- _toolWindow->setWindowFlags(_toolWindow->windowFlags() | Qt::WindowStaysOnTopHint);
+ _toolWindow->setWindowFlags((_toolWindow->windowFlags() | Qt::WindowStaysOnTopHint) & ~Qt::WindowMinimizeButtonHint);
_toolWindow->setWindowTitle("Tools");
_offscreenContext->makeCurrent();
@@ -712,6 +712,20 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
_keyboardFocusHighlightID = getOverlays().addOverlay(_keyboardFocusHighlight);
}
}
+ if (_keyboardFocusedItem == UNKNOWN_ENTITY_ID && _keyboardFocusHighlight) {
+ _keyboardFocusHighlight->setVisible(false);
+ }
+
+ }
+ });
+
+ connect(entityScriptingInterface.data(), &EntityScriptingInterface::deletingEntity,
+ [=](const EntityItemID& entityItemID) {
+ if (entityItemID == _keyboardFocusedItem) {
+ _keyboardFocusedItem = UNKNOWN_ENTITY_ID;
+ if (_keyboardFocusHighlight) {
+ _keyboardFocusHighlight->setVisible(false);
+ }
}
});
@@ -719,7 +733,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
connect(getEntities(), &EntityTreeRenderer::mousePressOffEntity,
[=](const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event, unsigned int deviceId) {
_keyboardFocusedItem = UNKNOWN_ENTITY_ID;
- _keyboardFocusHighlight->setVisible(false);
+ if (_keyboardFocusHighlight) {
+ _keyboardFocusHighlight->setVisible(false);
+ }
});
}
@@ -1998,10 +2014,19 @@ void Application::checkFPS() {
void Application::idle() {
PROFILE_RANGE(__FUNCTION__);
static SimpleAverage interIdleDurations;
+
+ static uint64_t lastIdleStart{ 0 };
static uint64_t lastIdleEnd{ 0 };
+ uint64_t now = usecTimestampNow();
+ uint64_t idleStartToStartDuration = now - lastIdleStart;
+
+ if (lastIdleStart > 0 && idleStartToStartDuration > 0) {
+ _simsPerSecond.updateAverage((float)USECS_PER_SECOND / (float)idleStartToStartDuration);
+ }
+
+ lastIdleStart = now;
if (lastIdleEnd != 0) {
- uint64_t now = usecTimestampNow();
interIdleDurations.update(now - lastIdleEnd);
static uint64_t lastReportTime = now;
if ((now - lastReportTime) >= (USECS_PER_SECOND)) {
@@ -2096,6 +2121,16 @@ void Application::idle() {
lastIdleEnd = usecTimestampNow();
}
+float Application::getAverageSimsPerSecond() {
+ uint64_t now = usecTimestampNow();
+
+ if (now - _lastSimsPerSecondUpdate > USECS_PER_SECOND) {
+ _simsPerSecondReport = _simsPerSecond.getAverage();
+ _lastSimsPerSecondUpdate = now;
+ }
+ return _simsPerSecondReport;
+}
+
void Application::setLowVelocityFilter(bool lowVelocityFilter) {
InputDevice::setLowVelocityFilter(lowVelocityFilter);
}
@@ -4062,23 +4097,12 @@ bool Application::askToSetAvatarUrl(const QString& url) {
QMessageBox msgBox;
msgBox.setIcon(QMessageBox::Question);
msgBox.setWindowTitle("Set Avatar");
- QPushButton* headButton = NULL;
- QPushButton* bodyButton = NULL;
QPushButton* bodyAndHeadButton = NULL;
QString modelName = fstMapping["name"].toString();
QString message;
QString typeInfo;
switch (modelType) {
- case FSTReader::HEAD_MODEL:
- message = QString("Would you like to use '") + modelName + QString("' for your avatar head?");
- headButton = msgBox.addButton(tr("Yes"), QMessageBox::ActionRole);
- break;
-
- case FSTReader::BODY_ONLY_MODEL:
- message = QString("Would you like to use '") + modelName + QString("' for your avatar body?");
- bodyButton = msgBox.addButton(tr("Yes"), QMessageBox::ActionRole);
- break;
case FSTReader::HEAD_AND_BODY_MODEL:
message = QString("Would you like to use '") + modelName + QString("' for your avatar?");
@@ -4086,10 +4110,7 @@ bool Application::askToSetAvatarUrl(const QString& url) {
break;
default:
- message = QString("Would you like to use '") + modelName + QString("' for some part of your avatar head?");
- headButton = msgBox.addButton(tr("Use for Head"), QMessageBox::ActionRole);
- bodyButton = msgBox.addButton(tr("Use for Body"), QMessageBox::ActionRole);
- bodyAndHeadButton = msgBox.addButton(tr("Use for Body and Head"), QMessageBox::ActionRole);
+ message = QString(modelName + QString("Does not support a head and body as required."));
break;
}
@@ -4098,14 +4119,7 @@ bool Application::askToSetAvatarUrl(const QString& url) {
msgBox.exec();
- if (msgBox.clickedButton() == headButton) {
- _myAvatar->useHeadURL(url, modelName);
- emit headURLChanged(url, modelName);
- } else if (msgBox.clickedButton() == bodyButton) {
- _myAvatar->useBodyURL(url, modelName);
- emit bodyURLChanged(url, modelName);
- } else if (msgBox.clickedButton() == bodyAndHeadButton) {
- _myAvatar->useFullAvatarURL(url, modelName);
+ if (msgBox.clickedButton() == bodyAndHeadButton) {
emit fullAvatarURLChanged(url, modelName);
} else {
qCDebug(interfaceapp) << "Declined to use the avatar: " << url;
@@ -4592,7 +4606,7 @@ void Application::checkSkeleton() {
msgBox.setIcon(QMessageBox::Warning);
msgBox.exec();
- _myAvatar->useBodyURL(DEFAULT_BODY_MODEL_URL, "Default");
+ _myAvatar->useFullAvatarURL(AvatarData::defaultFullAvatarModelUrl(), DEFAULT_FULL_AVATAR_MODEL_NAME);
} else {
_physicsEngine.setCharacterController(_myAvatar->getCharacterController());
}
diff --git a/interface/src/Application.h b/interface/src/Application.h
index e0d4fa559d..4819bd08a4 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -38,6 +38,7 @@
#include
#include
#include
+#include
#include "AudioClient.h"
#include "Bookmarks.h"
@@ -350,6 +351,8 @@ public:
const QRect& getMirrorViewRect() const { return _mirrorViewRect; }
+ float getAverageSimsPerSecond();
+
signals:
/// Fired when we're simulating; allows external parties to hook in.
@@ -681,6 +684,10 @@ private:
EntityItemID _keyboardFocusedItem;
quint64 _lastAcceptedKeyPress = 0;
+
+ SimpleMovingAverage _simsPerSecond{10};
+ int _simsPerSecondReport = 0;
+ quint64 _lastSimsPerSecondUpdate = 0;
};
#endif // hifi_Application_h
diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp
index 939d733a13..f074dc5ac7 100644
--- a/interface/src/Menu.cpp
+++ b/interface/src/Menu.cpp
@@ -258,7 +258,7 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true);
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::BlueSpeechSphere, 0, true);
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::EnableCharacterController, 0, true,
- avatar, SLOT(updateMotionBehavior()));
+ avatar, SLOT(updateMotionBehaviorFromMenu()));
MenuWrapper* viewMenu = addMenu("View");
addActionToQMenuAndActionHash(viewMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()));
diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp
index 55809646c0..c1a1d11268 100644
--- a/interface/src/avatar/Avatar.cpp
+++ b/interface/src/avatar/Avatar.cpp
@@ -26,7 +26,6 @@
#include
#include
#include
-#include
#include
#include
#include
@@ -563,6 +562,9 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const {
}
void Avatar::fixupModelsInScene() {
+ if (!(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) {
+ return;
+ }
// check to see if when we added our models to the scene they were ready, if they were not ready, then
// fix them up in the scene
@@ -688,6 +690,23 @@ glm::vec3 Avatar::getDisplayNamePosition() const {
const float HEAD_PROPORTION = 0.75f;
namePosition = _position + getBodyUpDirection() * (getBillboardSize() * HEAD_PROPORTION);
}
+#ifdef DEBUG
+ // TODO: Temporary logging to track cause of invalid scale value; remove once cause has been fixed.
+ // See other TODO below.
+ if (glm::isnan(namePosition.x) || glm::isnan(namePosition.y) || glm::isnan(namePosition.z)
+ || glm::isinf(namePosition.x) || glm::isinf(namePosition.y) || glm::isinf(namePosition.z)) {
+ qDebug() << "namePosition =" << namePosition;
+ glm::vec3 tempPosition(0.0f);
+ if (getSkeletonModel().getNeckPosition(tempPosition)) {
+ qDebug() << "getBodyUpDirection() =" << getBodyUpDirection();
+ qDebug() << "getHeadHeight() =" << getHeadHeight();
+ } else {
+ qDebug() << "_position =" << _position;
+ qDebug() << "getBodyUpDirection() =" << getBodyUpDirection();
+ qDebug() << "getBillboardSize() =" << getBillboardSize();
+ }
+ }
+#endif
return namePosition;
}
@@ -722,7 +741,8 @@ Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, floa
// Compute correct scale to apply
float scale = DESIRED_HIGHT_ON_SCREEN / (fontSize * pixelHeight) * devicePixelRatio;
#ifdef DEBUG
- // TODO: Temporary logging to track cause of invalid scale vale; remove once cause has been fixed.
+ // TODO: Temporary logging to track cause of invalid scale value; remove once cause has been fixed.
+ // Problem is probably due to an invalid getDisplayNamePosition(). See extra logging above.
if (scale == 0.0f || glm::isnan(scale) || glm::isinf(scale)) {
if (scale == 0.0f) {
qDebug() << "ASSERT because scale == 0.0f";
@@ -733,6 +753,7 @@ Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, floa
if (glm::isinf(scale)) {
qDebug() << "ASSERT because isinf(scale)";
}
+ qDebug() << "textPosition =" << textPosition;
qDebug() << "windowSizeY =" << windowSizeY;
qDebug() << "p1.y =" << p1.y;
qDebug() << "p1.w =" << p1.w;
@@ -957,20 +978,12 @@ void Avatar::scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const {
void Avatar::setFaceModelURL(const QUrl& faceModelURL) {
AvatarData::setFaceModelURL(faceModelURL);
- const QUrl DEFAULT_FACE_MODEL_URL = QUrl::fromLocalFile(PathUtils::resourcesPath() + "meshes/defaultAvatar_head.fst");
- getHead()->getFaceModel().setURL(_faceModelURL, DEFAULT_FACE_MODEL_URL, true, !isMyAvatar());
+ getHead()->getFaceModel().setURL(_faceModelURL, AvatarData::defaultFullAvatarModelUrl(), true, !isMyAvatar());
}
void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
AvatarData::setSkeletonModelURL(skeletonModelURL);
- const QUrl DEFAULT_FULL_MODEL_URL = QUrl::fromLocalFile(PathUtils::resourcesPath() + "meshes/defaultAvatar_full.fst");
- const QUrl DEFAULT_SKELETON_MODEL_URL = QUrl::fromLocalFile(PathUtils::resourcesPath() + "meshes/defaultAvatar_body.fst");
- if (isMyAvatar()) {
- _skeletonModel.setURL(_skeletonModelURL,
- getUseFullAvatar() ? DEFAULT_FULL_MODEL_URL : DEFAULT_SKELETON_MODEL_URL, true, !isMyAvatar());
- } else {
- _skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL, true, !isMyAvatar());
- }
+ _skeletonModel.setURL(_skeletonModelURL, AvatarData::defaultFullAvatarModelUrl(), true, !isMyAvatar());
}
void Avatar::setAttachmentData(const QVector& attachmentData) {
diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h
index 44e26b617f..a7cece2b45 100644
--- a/interface/src/avatar/Avatar.h
+++ b/interface/src/avatar/Avatar.h
@@ -150,8 +150,6 @@ public:
Q_INVOKABLE glm::vec3 getAcceleration() const { return _acceleration; }
Q_INVOKABLE glm::vec3 getAngularVelocity() const { return _angularVelocity; }
Q_INVOKABLE glm::vec3 getAngularAcceleration() const { return _angularAcceleration; }
-
- virtual bool getUseFullAvatar() const { return false; }
/// Scales a world space position vector relative to the avatar position and scale
/// \param vector position to be scaled. Will store the result
diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp
index e31c804185..0e8e5a6a91 100644
--- a/interface/src/avatar/FaceModel.cpp
+++ b/interface/src/avatar/FaceModel.cpp
@@ -47,66 +47,10 @@ void FaceModel::simulate(float deltaTime, bool fullUpdate) {
if (isActive()) {
setOffset(-_geometry->getFBXGeometry().neckPivot);
-
- for (int i = 0; i < _rig->getJointStateCount(); i++) {
- maybeUpdateNeckAndEyeRotation(i);
- }
-
Model::simulateInternal(deltaTime);
}
}
-void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const JointState& state, int index) {
- // get the rotation axes in joint space and use them to adjust the rotation
- glm::mat3 axes = glm::mat3_cast(glm::quat());
- glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() *
- glm::translate(_rig->getJointDefaultTranslationInConstrainedFrame(index)) *
- state.getPreTransform() * glm::mat4_cast(state.getPreRotation())));
- glm::vec3 pitchYawRoll = safeEulerAngles(_owningHead->getFinalOrientationInLocalFrame());
- glm::vec3 lean = glm::radians(glm::vec3(_owningHead->getFinalLeanForward(),
- _owningHead->getTorsoTwist(),
- _owningHead->getFinalLeanSideways()));
- pitchYawRoll -= lean;
- _rig->setJointRotationInConstrainedFrame(index,
- glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * axes[2]))
- * glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * axes[1]))
- * glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * axes[0]))
- * state.getDefaultRotation(), DEFAULT_PRIORITY);
-}
-
-void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentState, const JointState& state, int index) {
- // likewise with the eye joints
- // NOTE: at the moment we do the math in the world-frame, hence the inverse transform is more complex than usual.
- glm::mat4 inverse = glm::inverse(glm::mat4_cast(model->getRotation()) * parentState.getTransform() *
- glm::translate(_rig->getJointDefaultTranslationInConstrainedFrame(index)) *
- state.getPreTransform() * glm::mat4_cast(state.getPreRotation() * state.getDefaultRotation()));
- glm::vec3 front = glm::vec3(inverse * glm::vec4(_owningHead->getFinalOrientationInWorldFrame() * IDENTITY_FRONT, 0.0f));
- glm::vec3 lookAtDelta = _owningHead->getCorrectedLookAtPosition() - model->getTranslation();
- glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(lookAtDelta + glm::length(lookAtDelta) * _owningHead->getSaccade(), 1.0f));
- glm::quat between = rotationBetween(front, lookAt);
- const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE;
- _rig->setJointRotationInConstrainedFrame(index, glm::angleAxis(glm::clamp(glm::angle(between),
- -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) *
- state.getDefaultRotation(), DEFAULT_PRIORITY);
-}
-
-void FaceModel::maybeUpdateNeckAndEyeRotation(int index) {
- const JointState& state = _rig->getJointState(index);
- const FBXGeometry& geometry = _geometry->getFBXGeometry();
- const int parentIndex = state.getParentIndex();
-
- // guard against out-of-bounds access to _jointStates
- if (parentIndex != -1 && parentIndex >= 0 && parentIndex < _rig->getJointStateCount()) {
- const JointState& parentState = _rig->getJointState(parentIndex);
- if (index == geometry.neckJointIndex) {
- maybeUpdateNeckRotation(parentState, state, index);
-
- } else if (index == geometry.leftEyeJointIndex || index == geometry.rightEyeJointIndex) {
- maybeUpdateEyeRotation(this, parentState, state, index);
- }
- }
-}
-
bool FaceModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const {
if (!isActive()) {
return false;
diff --git a/interface/src/avatar/FaceModel.h b/interface/src/avatar/FaceModel.h
index ed1ea28508..5a19a8ea29 100644
--- a/interface/src/avatar/FaceModel.h
+++ b/interface/src/avatar/FaceModel.h
@@ -26,10 +26,6 @@ public:
virtual void simulate(float deltaTime, bool fullUpdate = true);
- void maybeUpdateNeckRotation(const JointState& parentState, const JointState& state, int index);
- void maybeUpdateEyeRotation(Model* model, const JointState& parentState, const JointState& state, int index);
- void maybeUpdateNeckAndEyeRotation(int index);
-
/// Retrieve the positions of up to two eye meshes.
/// \return whether or not both eye meshes were found
bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index f51a077304..52473a6d47 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -652,12 +652,7 @@ void MyAvatar::saveData() {
settings.setValue("leanScale", _leanScale);
settings.setValue("scale", _targetScale);
- settings.setValue("useFullAvatar", _useFullAvatar);
settings.setValue("fullAvatarURL", _fullAvatarURLFromPreferences);
- settings.setValue("faceModelURL", _headURLFromPreferences);
- settings.setValue("skeletonModelURL", _skeletonURLFromPreferences);
- settings.setValue("headModelName", _headModelName);
- settings.setValue("bodyModelName", _bodyModelName);
settings.setValue("fullAvatarModelName", _fullAvatarModelName);
settings.beginWriteArray("attachmentData");
@@ -727,61 +722,10 @@ void MyAvatar::loadData() {
_targetScale = loadSetting(settings, "scale", 1.0f);
setScale(_scale);
- // The old preferences only stored the face and skeleton URLs, we didn't track if the user wanted to use 1 or 2 urls
- // for their avatar, So we need to attempt to detect this old case and set our new preferences accordingly. If
- // the head URL is empty, then we will assume they are using a full url...
- bool isOldSettings = !(settings.contains("useFullAvatar") || settings.contains("fullAvatarURL"));
-
- _useFullAvatar = settings.value("useFullAvatar").toBool();
- _headURLFromPreferences = settings.value("faceModelURL", DEFAULT_HEAD_MODEL_URL).toUrl();
- _fullAvatarURLFromPreferences = settings.value("fullAvatarURL", DEFAULT_FULL_AVATAR_MODEL_URL).toUrl();
- _skeletonURLFromPreferences = settings.value("skeletonModelURL", DEFAULT_BODY_MODEL_URL).toUrl();
- _headModelName = settings.value("headModelName", DEFAULT_HEAD_MODEL_NAME).toString();
- _bodyModelName = settings.value("bodyModelName", DEFAULT_BODY_MODEL_NAME).toString();
+ _fullAvatarURLFromPreferences = settings.value("fullAvatarURL", AvatarData::defaultFullAvatarModelUrl()).toUrl();
_fullAvatarModelName = settings.value("fullAvatarModelName", DEFAULT_FULL_AVATAR_MODEL_NAME).toString();
- if (isOldSettings) {
- bool assumeFullAvatar = _headURLFromPreferences.isEmpty();
- _useFullAvatar = assumeFullAvatar;
-
- if (_useFullAvatar) {
- _fullAvatarURLFromPreferences = settings.value("skeletonModelURL").toUrl();
- _headURLFromPreferences = DEFAULT_HEAD_MODEL_URL;
- _skeletonURLFromPreferences = DEFAULT_BODY_MODEL_URL;
-
- QVariantHash fullAvatarFST = FSTReader::downloadMapping(_fullAvatarURLFromPreferences.toString());
-
- _headModelName = "Default";
- _bodyModelName = "Default";
- _fullAvatarModelName = fullAvatarFST["name"].toString();
-
- } else {
- _fullAvatarURLFromPreferences = DEFAULT_FULL_AVATAR_MODEL_URL;
- _skeletonURLFromPreferences = settings.value("skeletonModelURL", DEFAULT_BODY_MODEL_URL).toUrl();
-
- if (_skeletonURLFromPreferences == DEFAULT_BODY_MODEL_URL) {
- _bodyModelName = DEFAULT_BODY_MODEL_NAME;
- } else {
- QVariantHash bodyFST = FSTReader::downloadMapping(_skeletonURLFromPreferences.toString());
- _bodyModelName = bodyFST["name"].toString();
- }
-
- if (_headURLFromPreferences == DEFAULT_HEAD_MODEL_URL) {
- _headModelName = DEFAULT_HEAD_MODEL_NAME;
- } else {
- QVariantHash headFST = FSTReader::downloadMapping(_headURLFromPreferences.toString());
- _headModelName = headFST["name"].toString();
- }
-
- _fullAvatarModelName = "Default";
- }
- }
-
- if (_useFullAvatar) {
- useFullAvatarURL(_fullAvatarURLFromPreferences, _fullAvatarModelName);
- } else {
- useHeadAndBodyURLs(_headURLFromPreferences, _skeletonURLFromPreferences, _headModelName, _bodyModelName);
- }
+ useFullAvatarURL(_fullAvatarURLFromPreferences, _fullAvatarModelName);
QVector attachmentData;
int attachmentCount = settings.beginReadArray("attachmentData");
@@ -1012,29 +956,6 @@ void MyAvatar::clearJointAnimationPriorities() {
}
}
-QString MyAvatar::getModelDescription() const {
- QString result;
- if (_useFullAvatar) {
- if (!getFullAvartarModelName().isEmpty()) {
- result = "Full Avatar \"" + getFullAvartarModelName() + "\"";
- } else {
- result = "Full Avatar \"" + _fullAvatarURLFromPreferences.fileName() + "\"";
- }
- } else {
- if (!getHeadModelName().isEmpty()) {
- result = "Head \"" + getHeadModelName() + "\"";
- } else {
- result = "Head \"" + _headURLFromPreferences.fileName() + "\"";
- }
- if (!getBodyModelName().isEmpty()) {
- result += " and Body \"" + getBodyModelName() + "\"";
- } else {
- result += " and Body \"" + _skeletonURLFromPreferences.fileName() + "\"";
- }
- }
- return result;
-}
-
void MyAvatar::setFaceModelURL(const QUrl& faceModelURL) {
Avatar::setFaceModelURL(faceModelURL);
@@ -1061,8 +982,6 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN
return;
}
- _useFullAvatar = true;
-
if (_fullAvatarURLFromPreferences != fullAvatarURL) {
_fullAvatarURLFromPreferences = fullAvatarURL;
if (modelName.isEmpty()) {
@@ -1077,66 +996,14 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN
setFaceModelURL(QString());
}
- if (fullAvatarURL != getSkeletonModelURL()) {
+ const QString& urlString = fullAvatarURL.toString();
+ if (urlString.isEmpty() || (fullAvatarURL != getSkeletonModelURL())) {
setSkeletonModelURL(fullAvatarURL);
- UserActivityLogger::getInstance().changedModel("skeleton", fullAvatarURL.toString());
+ UserActivityLogger::getInstance().changedModel("skeleton", urlString);
}
sendIdentityPacket();
}
-void MyAvatar::useHeadURL(const QUrl& headURL, const QString& modelName) {
- useHeadAndBodyURLs(headURL, _skeletonURLFromPreferences, modelName, _bodyModelName);
-}
-
-void MyAvatar::useBodyURL(const QUrl& bodyURL, const QString& modelName) {
- useHeadAndBodyURLs(_headURLFromPreferences, bodyURL, _headModelName, modelName);
-}
-
-void MyAvatar::useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL, const QString& headName, const QString& bodyName) {
- if (QThread::currentThread() != thread()) {
- QMetaObject::invokeMethod(this, "useFullAvatarURL", Qt::BlockingQueuedConnection,
- Q_ARG(const QUrl&, headURL),
- Q_ARG(const QUrl&, bodyURL),
- Q_ARG(const QString&, headName),
- Q_ARG(const QString&, bodyName));
- return;
- }
-
- _useFullAvatar = false;
-
- if (_headURLFromPreferences != headURL) {
- _headURLFromPreferences = headURL;
- if (headName.isEmpty()) {
- QVariantHash headFST = FSTReader::downloadMapping(_headURLFromPreferences.toString());
- _headModelName = headFST["name"].toString();
- } else {
- _headModelName = headName;
- }
- }
-
- if (_skeletonURLFromPreferences != bodyURL) {
- _skeletonURLFromPreferences = bodyURL;
- if (bodyName.isEmpty()) {
- QVariantHash bodyFST = FSTReader::downloadMapping(_skeletonURLFromPreferences.toString());
- _bodyModelName = bodyFST["name"].toString();
- } else {
- _bodyModelName = bodyName;
- }
- }
-
- if (headURL != getFaceModelURL()) {
- setFaceModelURL(headURL);
- UserActivityLogger::getInstance().changedModel("head", headURL.toString());
- }
-
- if (bodyURL != getSkeletonModelURL()) {
- setSkeletonModelURL(bodyURL);
- UserActivityLogger::getInstance().changedModel("skeleton", bodyURL.toString());
- }
- sendIdentityPacket();
-}
-
-
void MyAvatar::setAttachmentData(const QVector& attachmentData) {
Avatar::setAttachmentData(attachmentData);
if (QThread::currentThread() != thread()) {
@@ -1310,11 +1177,7 @@ void MyAvatar::preRender(RenderArgs* renderArgs) {
}
if (shouldDrawHead != _prevShouldDrawHead) {
- if (_useFullAvatar) {
- _skeletonModel.setCauterizeBones(!shouldDrawHead);
- } else {
- getHead()->getFaceModel().setVisibleInScene(shouldDrawHead, scene);
- }
+ _skeletonModel.setCauterizeBones(!shouldDrawHead);
}
_prevShouldDrawHead = shouldDrawHead;
}
diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h
index 88649429f5..8ff9211101 100644
--- a/interface/src/avatar/MyAvatar.h
+++ b/interface/src/avatar/MyAvatar.h
@@ -126,21 +126,8 @@ public:
virtual void clearJointsData();
Q_INVOKABLE void useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelName = QString());
- Q_INVOKABLE void useHeadURL(const QUrl& headURL, const QString& modelName = QString());
- Q_INVOKABLE void useBodyURL(const QUrl& bodyURL, const QString& modelName = QString());
- Q_INVOKABLE void useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL,
- const QString& headName = QString(), const QString& bodyName = QString());
-
- Q_INVOKABLE virtual bool getUseFullAvatar() const { return _useFullAvatar; }
Q_INVOKABLE const QUrl& getFullAvatarURLFromPreferences() const { return _fullAvatarURLFromPreferences; }
- Q_INVOKABLE const QUrl& getHeadURLFromPreferences() const { return _headURLFromPreferences; }
- Q_INVOKABLE const QUrl& getBodyURLFromPreferences() const { return _skeletonURLFromPreferences; }
-
- Q_INVOKABLE const QString& getHeadModelName() const { return _headModelName; }
- Q_INVOKABLE const QString& getBodyModelName() const { return _bodyModelName; }
- Q_INVOKABLE const QString& getFullAvartarModelName() const { return _fullAvatarModelName; }
-
- Q_INVOKABLE QString getModelDescription() const;
+ Q_INVOKABLE const QString& getFullAvatarModelName() const { return _fullAvatarModelName; }
virtual void setAttachmentData(const QVector& attachmentData);
@@ -298,18 +285,9 @@ private:
void initHeadBones();
// Avatar Preferences
- bool _useFullAvatar = false;
QUrl _fullAvatarURLFromPreferences;
- QUrl _headURLFromPreferences;
- QUrl _skeletonURLFromPreferences;
-
- QString _headModelName;
- QString _bodyModelName;
QString _fullAvatarModelName;
- RigPointer _rig;
- bool _prevShouldDrawHead;
-
// cache of the current HMD sensor position and orientation
// in sensor space.
glm::mat4 _hmdSensorMatrix;
@@ -330,6 +308,8 @@ private:
glm::quat _goToOrientation;
std::unordered_set _headBoneSet;
+ RigPointer _rig;
+ bool _prevShouldDrawHead;
};
#endif // hifi_MyAvatar_h
diff --git a/interface/src/ui/AvatarAppearanceDialog.cpp b/interface/src/ui/AvatarAppearanceDialog.cpp
deleted file mode 100644
index 54e48dca26..0000000000
--- a/interface/src/ui/AvatarAppearanceDialog.cpp
+++ /dev/null
@@ -1,215 +0,0 @@
-//
-// AvatarAppearanceDialog.cpp
-// interface/src/ui
-//
-// Created by Stojce Slavkovski on 2/20/14.
-// Copyright 2014 High Fidelity, Inc.
-//
-// Distributed under the Apache License, Version 2.0.
-// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-#include
-#include
-
-#include
-#include
-#include
-#include
-
-#include "Application.h"
-#include "MainWindow.h"
-#include "LODManager.h"
-#include "Menu.h"
-#include "AvatarAppearanceDialog.h"
-#include "Snapshot.h"
-#include "UserActivityLogger.h"
-#include "UIUtil.h"
-#include "ui/DialogsManager.h"
-#include "ui/PreferencesDialog.h"
-
-AvatarAppearanceDialog::AvatarAppearanceDialog(QWidget* parent) :
- QDialog(parent) {
-
- setAttribute(Qt::WA_DeleteOnClose);
-
- ui.setupUi(this);
-
- loadAvatarAppearance();
-
- connect(ui.defaultButton, &QPushButton::clicked, this, &AvatarAppearanceDialog::accept);
-
- connect(ui.buttonBrowseHead, &QPushButton::clicked, this, &AvatarAppearanceDialog::openHeadModelBrowser);
- connect(ui.buttonBrowseBody, &QPushButton::clicked, this, &AvatarAppearanceDialog::openBodyModelBrowser);
- connect(ui.buttonBrowseFullAvatar, &QPushButton::clicked, this, &AvatarAppearanceDialog::openFullAvatarModelBrowser);
-
- connect(ui.useSeparateBodyAndHead, &QRadioButton::clicked, this, &AvatarAppearanceDialog::useSeparateBodyAndHead);
- connect(ui.useFullAvatar, &QRadioButton::clicked, this, &AvatarAppearanceDialog::useFullAvatar);
-
- connect(Application::getInstance(), &Application::headURLChanged, this, &AvatarAppearanceDialog::headURLChanged);
- connect(Application::getInstance(), &Application::bodyURLChanged, this, &AvatarAppearanceDialog::bodyURLChanged);
- connect(Application::getInstance(), &Application::fullAvatarURLChanged, this, &AvatarAppearanceDialog::fullAvatarURLChanged);
-
- auto myAvatar = DependencyManager::get()->getMyAvatar();
-
- ui.bodyNameLabel->setText("Body - " + myAvatar->getBodyModelName());
- ui.headNameLabel->setText("Head - " + myAvatar->getHeadModelName());
- ui.fullAvatarNameLabel->setText("Full Avatar - " + myAvatar->getFullAvartarModelName());
-
- UIUtil::scaleWidgetFontSizes(this);
-}
-
-void AvatarAppearanceDialog::useSeparateBodyAndHead(bool checked) {
- QUrl headURL(ui.faceURLEdit->text());
- QUrl bodyURL(ui.skeletonURLEdit->text());
- DependencyManager::get()->getMyAvatar()->useHeadAndBodyURLs(headURL, bodyURL);
- setUseFullAvatar(!checked);
-}
-
-void AvatarAppearanceDialog::useFullAvatar(bool checked) {
- QUrl fullAvatarURL(ui.fullAvatarURLEdit->text());
- DependencyManager::get()->getMyAvatar()->useFullAvatarURL(fullAvatarURL);
- setUseFullAvatar(checked);
-}
-
-void AvatarAppearanceDialog::setUseFullAvatar(bool useFullAvatar) {
- _useFullAvatar = useFullAvatar;
- ui.faceURLEdit->setEnabled(!_useFullAvatar);
- ui.skeletonURLEdit->setEnabled(!_useFullAvatar);
- ui.fullAvatarURLEdit->setEnabled(_useFullAvatar);
-
- ui.useFullAvatar->setChecked(_useFullAvatar);
- ui.useSeparateBodyAndHead->setChecked(!_useFullAvatar);
-
- QPointer prefs = DependencyManager::get()->getPreferencesDialog();
- if (prefs) { // Preferences dialog may have been closed
- prefs->avatarDescriptionChanged();
- }
-}
-
-void AvatarAppearanceDialog::headURLChanged(const QString& newValue, const QString& modelName) {
- ui.faceURLEdit->setText(newValue);
- setUseFullAvatar(false);
- ui.headNameLabel->setText("Head - " + modelName);
-}
-
-void AvatarAppearanceDialog::bodyURLChanged(const QString& newValue, const QString& modelName) {
- ui.skeletonURLEdit->setText(newValue);
- setUseFullAvatar(false);
- ui.bodyNameLabel->setText("Body - " + modelName);
-}
-
-void AvatarAppearanceDialog::fullAvatarURLChanged(const QString& newValue, const QString& modelName) {
- ui.fullAvatarURLEdit->setText(newValue);
- setUseFullAvatar(true);
- ui.fullAvatarNameLabel->setText("Full Avatar - " + modelName);
-}
-
-void AvatarAppearanceDialog::accept() {
- saveAvatarAppearance();
-
- QPointer prefs = DependencyManager::get()->getPreferencesDialog();
- if (prefs) { // Preferences dialog may have been closed
- prefs->avatarDescriptionChanged();
- }
-
- close();
- delete _marketplaceWindow;
- _marketplaceWindow = NULL;
-}
-
-void AvatarAppearanceDialog::setHeadUrl(QString modelUrl) {
- ui.faceURLEdit->setText(modelUrl);
-}
-
-void AvatarAppearanceDialog::setSkeletonUrl(QString modelUrl) {
- ui.skeletonURLEdit->setText(modelUrl);
-}
-
-void AvatarAppearanceDialog::openFullAvatarModelBrowser() {
- auto MARKETPLACE_URL = NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/marketplace?category=avatars";
- auto WIDTH = 900;
- auto HEIGHT = 700;
- if (!_marketplaceWindow) {
- _marketplaceWindow = new WebWindowClass("Marketplace", MARKETPLACE_URL, WIDTH, HEIGHT, false);
- }
- _marketplaceWindow->setVisible(true);
-}
-
-void AvatarAppearanceDialog::openHeadModelBrowser() {
- auto MARKETPLACE_URL = NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/marketplace?category=avatars";
- auto WIDTH = 900;
- auto HEIGHT = 700;
- if (!_marketplaceWindow) {
- _marketplaceWindow = new WebWindowClass("Marketplace", MARKETPLACE_URL, WIDTH, HEIGHT, false);
- }
- _marketplaceWindow->setVisible(true);
-}
-
-void AvatarAppearanceDialog::openBodyModelBrowser() {
- auto MARKETPLACE_URL = NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/marketplace?category=avatars";
- auto WIDTH = 900;
- auto HEIGHT = 700;
- if (!_marketplaceWindow) {
- _marketplaceWindow = new WebWindowClass("Marketplace", MARKETPLACE_URL, WIDTH, HEIGHT, false);
- }
- _marketplaceWindow->setVisible(true);
-}
-
-void AvatarAppearanceDialog::resizeEvent(QResizeEvent *resizeEvent) {
-
- // keep buttons panel at the bottom
- ui.buttonsPanel->setGeometry(0,
- size().height() - ui.buttonsPanel->height(),
- size().width(),
- ui.buttonsPanel->height());
-
- // set width and height of srcollarea to match bottom panel and width
- ui.scrollArea->setGeometry(ui.scrollArea->geometry().x(), ui.scrollArea->geometry().y(),
- size().width(),
- size().height() - ui.buttonsPanel->height() - ui.scrollArea->geometry().y());
-
-}
-
-void AvatarAppearanceDialog::loadAvatarAppearance() {
-
- MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar();
-
- _useFullAvatar = myAvatar->getUseFullAvatar();
- _fullAvatarURLString = myAvatar->getFullAvatarURLFromPreferences().toString();
- _headURLString = myAvatar->getHeadURLFromPreferences().toString();
- _bodyURLString = myAvatar->getBodyURLFromPreferences().toString();
-
- ui.fullAvatarURLEdit->setText(_fullAvatarURLString);
- ui.faceURLEdit->setText(_headURLString);
- ui.skeletonURLEdit->setText(_bodyURLString);
- setUseFullAvatar(_useFullAvatar);
-}
-
-void AvatarAppearanceDialog::saveAvatarAppearance() {
-
- MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar();
-
- QUrl headURL(ui.faceURLEdit->text());
- QString headURLString = headURL.toString();
-
- QUrl bodyURL(ui.skeletonURLEdit->text());
- QString bodyURLString = bodyURL.toString();
-
- QUrl fullAvatarURL(ui.fullAvatarURLEdit->text());
- QString fullAvatarURLString = fullAvatarURL.toString();
-
- bool somethingChanged =
- _useFullAvatar != myAvatar->getUseFullAvatar() ||
- fullAvatarURLString != myAvatar->getFullAvatarURLFromPreferences().toString() ||
- headURLString != myAvatar->getHeadURLFromPreferences().toString() ||
- bodyURLString != myAvatar->getBodyURLFromPreferences().toString();
-
- if (somethingChanged) {
- if (_useFullAvatar) {
- myAvatar->useFullAvatarURL(fullAvatarURL);
- } else {
- myAvatar->useHeadAndBodyURLs(headURL, bodyURL);
- }
- }
-}
diff --git a/interface/src/ui/AvatarAppearanceDialog.h b/interface/src/ui/AvatarAppearanceDialog.h
deleted file mode 100644
index be30caeedb..0000000000
--- a/interface/src/ui/AvatarAppearanceDialog.h
+++ /dev/null
@@ -1,64 +0,0 @@
-//
-// AvatarAppearanceDialog.h
-// interface/src/ui
-//
-// Created by Stojce Slavkovski on 2/20/14.
-// Copyright 2014 High Fidelity, Inc.
-//
-// Distributed under the Apache License, Version 2.0.
-// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-
-#ifndef hifi_AvatarAppearanceDialog_h
-#define hifi_AvatarAppearanceDialog_h
-
-#include "ui_avatarAppearance.h"
-
-#include
-#include
-
-#include "scripting/WebWindowClass.h"
-
-class AvatarAppearanceDialog : public QDialog {
- Q_OBJECT
-
-public:
- AvatarAppearanceDialog(QWidget* parent = nullptr);
-
-protected:
- void resizeEvent(QResizeEvent* resizeEvent);
-
-private:
- void loadAvatarAppearance();
- void saveAvatarAppearance();
- void openHeadModelBrowser();
- void openBodyModelBrowser();
- void openFullAvatarModelBrowser();
- void setUseFullAvatar(bool useFullAvatar);
-
- Ui_AvatarAppearanceDialog ui;
-
- bool _useFullAvatar;
- QString _headURLString;
- QString _bodyURLString;
- QString _fullAvatarURLString;
-
-
- QString _displayNameString;
-
- WebWindowClass* _marketplaceWindow = NULL;
-
-private slots:
- void accept();
- void setHeadUrl(QString modelUrl);
- void setSkeletonUrl(QString modelUrl);
- void headURLChanged(const QString& newValue, const QString& modelName);
- void bodyURLChanged(const QString& newValue, const QString& modelName);
- void fullAvatarURLChanged(const QString& newValue, const QString& modelName);
- void useSeparateBodyAndHead(bool checked);
- void useFullAvatar(bool checked);
-
-
-};
-
-#endif // hifi_AvatarAppearanceDialog_h
diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp
index 8131aa49f2..58d2550752 100644
--- a/interface/src/ui/DialogsManager.cpp
+++ b/interface/src/ui/DialogsManager.cpp
@@ -20,7 +20,6 @@
#include "AddressBarDialog.h"
#include "AnimationsDialog.h"
#include "AttachmentsDialog.h"
-#include "AvatarAppearanceDialog.h"
#include "BandwidthDialog.h"
#include "CachesSizeDialog.h"
#include "DiskCacheEditor.h"
@@ -88,15 +87,6 @@ void DialogsManager::editPreferences() {
}
}
-void DialogsManager::changeAvatarAppearance() {
- if (!_avatarAppearanceDialog) {
- maybeCreateDialog(_avatarAppearanceDialog);
- _avatarAppearanceDialog->show();
- } else {
- _avatarAppearanceDialog->close();
- }
-}
-
void DialogsManager::editAttachments() {
if (!_attachmentsDialog) {
maybeCreateDialog(_attachmentsDialog);
diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h
index 09e0274d86..2db700e72a 100644
--- a/interface/src/ui/DialogsManager.h
+++ b/interface/src/ui/DialogsManager.h
@@ -34,7 +34,6 @@ class OctreeStatsDialog;
class PreferencesDialog;
class ScriptEditorWindow;
class QMessageBox;
-class AvatarAppearanceDialog;
class DomainConnectionDialog;
class UpdateDialog;
@@ -66,7 +65,6 @@ public slots:
void hmdTools(bool showTools);
void showScriptEditor();
void showIRCLink();
- void changeAvatarAppearance();
void showDomainConnectionDialog();
// Application Update
@@ -110,7 +108,6 @@ private:
QPointer _octreeStatsDialog;
QPointer _preferencesDialog;
QPointer _scriptEditor;
- QPointer _avatarAppearanceDialog;
QPointer _domainConnectionDialog;
QPointer _updateDialog;
};
diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp
index 8e9c164563..a6340d3955 100644
--- a/interface/src/ui/PreferencesDialog.cpp
+++ b/interface/src/ui/PreferencesDialog.cpp
@@ -47,45 +47,45 @@ PreferencesDialog::PreferencesDialog(QWidget* parent) :
connect(ui.buttonBrowseScriptsLocation, &QPushButton::clicked, this, &PreferencesDialog::openScriptsLocationBrowser);
connect(ui.buttonReloadDefaultScripts, &QPushButton::clicked, Application::getInstance(), &Application::loadDefaultScripts);
- DialogsManager* dialogsManager = DependencyManager::get().data();
- connect(ui.buttonChangeApperance, &QPushButton::clicked, dialogsManager, &DialogsManager::changeAvatarAppearance);
-
- connect(Application::getInstance(), &Application::headURLChanged, this, &PreferencesDialog::headURLChanged);
- connect(Application::getInstance(), &Application::bodyURLChanged, this, &PreferencesDialog::bodyURLChanged);
+ connect(ui.buttonChangeAppearance, &QPushButton::clicked, this, &PreferencesDialog::openFullAvatarModelBrowser);
+ connect(ui.appearanceDescription, &QLineEdit::textChanged, this, [this](const QString& url) {
+ this->fullAvatarURLChanged(url, "");
+ });
connect(Application::getInstance(), &Application::fullAvatarURLChanged, this, &PreferencesDialog::fullAvatarURLChanged);
// move dialog to left side
move(parentWidget()->geometry().topLeft());
setFixedHeight(parentWidget()->size().height() - PREFERENCES_HEIGHT_PADDING);
- ui.apperanceDescription->setText(DependencyManager::get()->getMyAvatar()->getModelDescription());
-
UIUtil::scaleWidgetFontSizes(this);
}
-void PreferencesDialog::avatarDescriptionChanged() {
- ui.apperanceDescription->setText(DependencyManager::get()->getMyAvatar()->getModelDescription());
-}
-
-void PreferencesDialog::headURLChanged(const QString& newValue, const QString& modelName) {
- ui.apperanceDescription->setText(DependencyManager::get()->getMyAvatar()->getModelDescription());
-}
-
-void PreferencesDialog::bodyURLChanged(const QString& newValue, const QString& modelName) {
- ui.apperanceDescription->setText(DependencyManager::get()->getMyAvatar()->getModelDescription());
-}
-
void PreferencesDialog::fullAvatarURLChanged(const QString& newValue, const QString& modelName) {
- ui.apperanceDescription->setText(DependencyManager::get()->getMyAvatar()->getModelDescription());
+ ui.appearanceDescription->setText(newValue);
+ const QString APPEARANCE_LABEL_TEXT("Appearance: ");
+ ui.appearanceLabel->setText(APPEARANCE_LABEL_TEXT + modelName);
+ DependencyManager::get()->getMyAvatar()->useFullAvatarURL(newValue, modelName);
}
void PreferencesDialog::accept() {
+ MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar();
+ _lastGoodAvatarURL = myAvatar->getFullAvatarURLFromPreferences();
+ _lastGoodAvatarName = myAvatar->getFullAvatarModelName();
savePreferences();
close();
delete _marketplaceWindow;
_marketplaceWindow = NULL;
}
+void PreferencesDialog::restoreLastGoodAvatar() {
+ fullAvatarURLChanged(_lastGoodAvatarURL.toString(), _lastGoodAvatarName);
+}
+
+void PreferencesDialog::reject() {
+ restoreLastGoodAvatar();
+ QDialog::reject();
+}
+
void PreferencesDialog::openSnapshotLocationBrowser() {
QString dir = QFileDialog::getExistingDirectory(this, tr("Snapshots Location"),
QStandardPaths::writableLocation(QStandardPaths::DesktopLocation),
@@ -103,6 +103,16 @@ void PreferencesDialog::openScriptsLocationBrowser() {
ui.scriptsLocationEdit->setText(dir);
}
}
+void PreferencesDialog::openFullAvatarModelBrowser() {
+ const auto MARKETPLACE_URL = NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/marketplace?category=avatars";
+ const auto WIDTH = 900;
+ const auto HEIGHT = 700;
+ if (!_marketplaceWindow) {
+ _marketplaceWindow = new WebWindowClass("Marketplace", MARKETPLACE_URL, WIDTH, HEIGHT, false);
+ }
+ _marketplaceWindow->setVisible(true);
+
+}
void PreferencesDialog::resizeEvent(QResizeEvent *resizeEvent) {
@@ -129,6 +139,10 @@ void PreferencesDialog::loadPreferences() {
ui.collisionSoundURLEdit->setText(myAvatar->getCollisionSoundURL());
+ _lastGoodAvatarURL = myAvatar->getFullAvatarURLFromPreferences();
+ _lastGoodAvatarName = myAvatar->getFullAvatarModelName();
+ fullAvatarURLChanged(_lastGoodAvatarURL.toString(), _lastGoodAvatarName);
+
ui.sendDataCheckBox->setChecked(!menuInstance->isOptionChecked(MenuOption::DisableActivityLogger));
ui.snapshotLocationEdit->setText(Snapshot::snapshotsLocation.get());
@@ -211,6 +225,11 @@ void PreferencesDialog::savePreferences() {
myAvatar->setCollisionSoundURL(ui.collisionSoundURLEdit->text());
+ // MyAvatar persists its own data. If it doesn't agree with what the user has explicitly accepted, set it back to old values.
+ if (_lastGoodAvatarURL != myAvatar->getFullAvatarURLFromPreferences()) {
+ restoreLastGoodAvatar();
+ }
+
if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableActivityLogger)
!= ui.sendDataCheckBox->isChecked()) {
Menu::getInstance()->triggerOption(MenuOption::DisableActivityLogger);
diff --git a/interface/src/ui/PreferencesDialog.h b/interface/src/ui/PreferencesDialog.h
index 8e699c80a2..6d7a87b97c 100644
--- a/interface/src/ui/PreferencesDialog.h
+++ b/interface/src/ui/PreferencesDialog.h
@@ -33,6 +33,9 @@ protected:
private:
void loadPreferences();
void savePreferences();
+ QUrl _lastGoodAvatarURL;
+ QString _lastGoodAvatarName;
+ void restoreLastGoodAvatar();
Ui_PreferencesDialog ui;
@@ -42,10 +45,10 @@ private:
private slots:
void accept();
+ void reject();
+ void openFullAvatarModelBrowser();
void openSnapshotLocationBrowser();
void openScriptsLocationBrowser();
- void headURLChanged(const QString& newValue, const QString& modelName);
- void bodyURLChanged(const QString& newValue, const QString& modelName);
void fullAvatarURLChanged(const QString& newValue, const QString& modelName);
};
diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp
index d7c4bb5ed7..74e3a8f44f 100644
--- a/interface/src/ui/Stats.cpp
+++ b/interface/src/ui/Stats.cpp
@@ -114,6 +114,7 @@ void Stats::updateStats() {
STAT_UPDATE(avatarCount, avatarManager->size() - 1);
STAT_UPDATE(serverCount, nodeList->size());
STAT_UPDATE(framerate, (int)qApp->getFps());
+ STAT_UPDATE(simrate, (int)Application::getInstance()->getAverageSimsPerSecond());
auto bandwidthRecorder = DependencyManager::get();
STAT_UPDATE(packetInCount, bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond());
diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h
index af6824d1a3..4f0619d9c8 100644
--- a/interface/src/ui/Stats.h
+++ b/interface/src/ui/Stats.h
@@ -30,6 +30,7 @@ class Stats : public QQuickItem {
STATS_PROPERTY(int, serverCount, 0)
STATS_PROPERTY(int, framerate, 0)
+ STATS_PROPERTY(int, simrate, 0)
STATS_PROPERTY(int, avatarCount, 0)
STATS_PROPERTY(int, packetInCount, 0)
STATS_PROPERTY(int, packetOutCount, 0)
@@ -95,6 +96,7 @@ signals:
void timingExpandedChanged();
void serverCountChanged();
void framerateChanged();
+ void simrateChanged();
void avatarCountChanged();
void packetInCountChanged();
void packetOutCountChanged();
diff --git a/interface/ui/avatarAppearance.ui b/interface/ui/avatarAppearance.ui
deleted file mode 100644
index 5ebf2c705b..0000000000
--- a/interface/ui/avatarAppearance.ui
+++ /dev/null
@@ -1,471 +0,0 @@
-
-
- AvatarAppearanceDialog
-
-
-
- 0
- 0
- 500
- 350
-
-
-
-
- 0
- 0
-
-
-
-
- 500
- 350
-
-
-
-
- 500
- 16777215
-
-
-
-
- 13
-
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
- -
-
-
-
- 0
- 0
-
-
-
- QFrame::NoFrame
-
-
- QFrame::Plain
-
-
- 0
-
-
- true
-
-
-
-
- 0
- -107
- 485
- 1550
-
-
-
-
- 0
-
-
- 30
-
-
- 0
-
-
- 30
-
-
- 10
-
-
-
-
-
-
-
- 0
-
-
- 7
-
-
- 7
-
-
-
-
-
-
-
-
-
- Arial
-
-
-
- Use single avatar with Body and Head
-
-
-
-
-
-
- -
-
-
- 0
-
-
- 10
-
-
- 0
-
-
- 0
-
-
-
-
-
-
-
-
- Arial
-
-
-
- Full Avatar
-
-
- Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft
-
-
- fullAvatarURLEdit
-
-
-
-
- -
-
-
-
-
-
-
-
- 0
- 0
-
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
- QSizePolicy::Fixed
-
-
-
- 5
- 0
-
-
-
-
-
- -
-
-
-
- Arial
-
-
-
- Browse
-
-
-
- 0
- 0
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
- Arial
-
-
-
- Use separate Body and Head avatar files
-
-
-
-
-
- -
-
-
- 0
-
-
- 10
-
-
- 7
-
-
- 7
-
-
-
-
-
-
-
-
-
- Arial
-
-
-
- Head
-
-
- Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft
-
-
-
-
-
-
- -
-
-
- 0
-
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
- QSizePolicy::Fixed
-
-
-
- 5
- 20
-
-
-
-
- -
-
-
-
- Arial
-
-
-
- Browse
-
-
-
- 0
- 0
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
- 0
-
-
- 10
-
-
- 7
-
-
- 7
-
-
-
-
-
-
-
- Arial
-
-
-
- Body
-
-
- Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft
-
-
- skeletonURLEdit
-
-
-
-
- -
-
-
- 0
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
- QSizePolicy::Fixed
-
-
-
- 5
- 20
-
-
-
-
- -
-
-
-
- Arial
-
-
-
- Browse
-
-
-
- 0
- 0
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
- QFrame::NoFrame
-
-
- QFrame::Raised
-
-
-
-
-
-
-
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
-
- Arial
-
-
-
- Close
-
-
- true
-
-
- false
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/interface/ui/preferencesDialog.ui b/interface/ui/preferencesDialog.ui
index df6d28c07b..b91367dcf1 100644
--- a/interface/ui/preferencesDialog.ui
+++ b/interface/ui/preferencesDialog.ui
@@ -275,7 +275,7 @@
Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft
- apperanceDescription
+ appearanceDescription
@@ -283,26 +283,21 @@
-
-
+
0
-
-
+
Arial
-
-
- false
-
-
-
-
+
Qt::Horizontal
@@ -318,7 +313,7 @@
-
-
+
Arial
diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp
index 02a4a4408a..9ef1f94c55 100644
--- a/libraries/avatars/src/AvatarData.cpp
+++ b/libraries/avatars/src/AvatarData.cpp
@@ -70,6 +70,16 @@ AvatarData::~AvatarData() {
delete _referential;
}
+// We cannot have a file-level variable (const or otherwise) in the header if it uses PathUtils, because that references Application, which will not yet initialized.
+// Thus we have a static class getter, referencing a static class var.
+QUrl AvatarData::_defaultFullAvatarModelUrl = {}; // In C++, if this initialization were in the header, every file would have it's own copy, even for class vars.
+const QUrl AvatarData::defaultFullAvatarModelUrl() {
+ if (_defaultFullAvatarModelUrl.isEmpty()) {
+ _defaultFullAvatarModelUrl = QUrl::fromLocalFile(PathUtils::resourcesPath() + "meshes/defaultAvatar_full.fst");
+ }
+ return _defaultFullAvatarModelUrl;
+}
+
const glm::vec3& AvatarData::getPosition() const {
if (_referential) {
_referential->update();
@@ -899,7 +909,7 @@ void AvatarData::setFaceModelURL(const QUrl& faceModelURL) {
}
void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) {
- _skeletonModelURL = skeletonModelURL.isEmpty() ? DEFAULT_BODY_MODEL_URL : skeletonModelURL;
+ _skeletonModelURL = skeletonModelURL.isEmpty() ? AvatarData::defaultFullAvatarModelUrl() : skeletonModelURL;
qCDebug(avatars) << "Changing skeleton model for avatar to" << _skeletonModelURL.toString();
diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h
index dbc7b9b9ec..8bb874bc71 100644
--- a/libraries/avatars/src/AvatarData.h
+++ b/libraries/avatars/src/AvatarData.h
@@ -54,6 +54,7 @@ typedef unsigned long long quint64;
#include "AABox.h"
#include "HandData.h"
#include "HeadData.h"
+#include "PathUtils.h"
#include "Player.h"
#include "Recorder.h"
#include "Referential.h"
@@ -107,12 +108,7 @@ const float MAX_AUDIO_LOUDNESS = 1000.0; // close enough for mouth animation
const int AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS = 1000;
const int AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS = 5000;
-const QUrl DEFAULT_HEAD_MODEL_URL = QUrl("http://public.highfidelity.io/models/heads/defaultAvatar_head.fst");
-const QUrl DEFAULT_BODY_MODEL_URL = QUrl("http://public.highfidelity.io/models/skeletons/defaultAvatar_body.fst");
-const QUrl DEFAULT_FULL_AVATAR_MODEL_URL = QUrl("http://public.highfidelity.io/marketplace/contents/029db3d4-da2c-4cb2-9c08-b9612ba576f5/02949063e7c4aed42ad9d1a58461f56d.fst");
-
-const QString DEFAULT_HEAD_MODEL_NAME = QString("Robot");
-const QString DEFAULT_BODY_MODEL_NAME = QString("Robot");
+// See also static AvatarData::defaultFullAvatarModelUrl().
const QString DEFAULT_FULL_AVATAR_MODEL_NAME = QString("Default");
@@ -163,6 +159,8 @@ public:
AvatarData();
virtual ~AvatarData();
+ static const QUrl defaultFullAvatarModelUrl();
+
virtual bool isMyAvatar() const { return false; }
const QUuid& getSessionUUID() const { return _sessionUUID; }
@@ -401,6 +399,7 @@ protected:
SimpleMovingAverage _averageBytesReceived;
private:
+ static QUrl _defaultFullAvatarModelUrl;
// privatize the copy constructor and assignment operator so they cannot be called
AvatarData(const AvatarData&);
AvatarData& operator= (const AvatarData&);
diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp
index 23093b9f3f..5d139a719a 100644
--- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp
@@ -9,6 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include
#include
#if defined(__GNUC__) && !defined(__clang__)
@@ -40,9 +41,10 @@
#include "EntityTreeRenderer.h"
#include "polyvox_vert.h"
#include "polyvox_frag.h"
-#include "RenderablePolyVoxEntityItem.h"
+#include "RenderablePolyVoxEntityItem.h"
gpu::PipelinePointer RenderablePolyVoxEntityItem::_pipeline = nullptr;
+const float MARCHING_CUBE_COLLISION_HULL_OFFSET = 0.5;
EntityItemPointer RenderablePolyVoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
return std::make_shared(entityID, properties);
@@ -66,7 +68,7 @@ RenderablePolyVoxEntityItem::~RenderablePolyVoxEntityItem() {
}
bool inUserBounds(const PolyVox::SimpleVolume* vol, PolyVoxEntityItem::PolyVoxSurfaceStyle surfaceStyle,
- int x, int y, int z) {
+ int x, int y, int z) {
// x, y, z are in user voxel-coords, not adjusted-for-edge voxel-coords.
switch (surfaceStyle) {
case PolyVoxEntityItem::SURFACE_MARCHING_CUBES:
@@ -78,6 +80,7 @@ bool inUserBounds(const PolyVox::SimpleVolume* vol, PolyVoxEntityItem::
return true;
case PolyVoxEntityItem::SURFACE_EDGED_CUBIC:
+ case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES:
if (x < 0 || y < 0 || z < 0 ||
x >= vol->getWidth() - 2 || y >= vol->getHeight() - 2 || z >= vol->getDepth() - 2) {
return false;
@@ -112,7 +115,7 @@ void RenderablePolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize)
_onCount = 0;
- if (_voxelSurfaceStyle == SURFACE_EDGED_CUBIC) {
+ if (_voxelSurfaceStyle == SURFACE_EDGED_CUBIC || _voxelSurfaceStyle == SURFACE_EDGED_MARCHING_CUBES) {
// with _EDGED_ we maintain an extra box of voxels around those that the user asked for. This
// changes how the surface extractor acts -- mainly it becomes impossible to have holes in the
// generated mesh. The non _EDGED_ modes will leave holes in the mesh at the edges of the
@@ -156,8 +159,10 @@ void RenderablePolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize)
void RenderablePolyVoxEntityItem::updateVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle) {
// if we are switching to or from "edged" we need to force a resize of _volData.
- if (voxelSurfaceStyle == SURFACE_EDGED_CUBIC ||
- _voxelSurfaceStyle == SURFACE_EDGED_CUBIC) {
+ bool wasEdged = (_voxelSurfaceStyle == SURFACE_EDGED_CUBIC || _voxelSurfaceStyle == SURFACE_EDGED_MARCHING_CUBES);
+ bool willBeEdged = (voxelSurfaceStyle == SURFACE_EDGED_CUBIC || voxelSurfaceStyle == SURFACE_EDGED_MARCHING_CUBES);
+
+ if (wasEdged != willBeEdged) {
if (_volData) {
delete _volData;
}
@@ -182,11 +187,11 @@ glm::vec3 RenderablePolyVoxEntityItem::getSurfacePositionAdjustment() const {
glm::vec3 scale = getDimensions() / _voxelVolumeSize; // meters / voxel-units
switch (_voxelSurfaceStyle) {
case PolyVoxEntityItem::SURFACE_MARCHING_CUBES:
- return scale / 2.0f;
- case PolyVoxEntityItem::SURFACE_EDGED_CUBIC:
- return scale / -2.0f;
case PolyVoxEntityItem::SURFACE_CUBIC:
return scale / 2.0f;
+ case PolyVoxEntityItem::SURFACE_EDGED_CUBIC:
+ case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES:
+ return scale / -2.0f;
}
return glm::vec3(0.0f, 0.0f, 0.0f);
}
@@ -202,6 +207,11 @@ glm::mat4 RenderablePolyVoxEntityItem::voxelToLocalMatrix() const {
return scaled;
}
+glm::mat4 RenderablePolyVoxEntityItem::localToVoxelMatrix() const {
+ glm::mat4 localToModelMatrix = glm::inverse(voxelToLocalMatrix());
+ return localToModelMatrix;
+}
+
glm::mat4 RenderablePolyVoxEntityItem::voxelToWorldMatrix() const {
glm::mat4 rotation = glm::mat4_cast(getRotation());
glm::mat4 translation = glm::translate(getPosition());
@@ -223,84 +233,115 @@ uint8_t RenderablePolyVoxEntityItem::getVoxel(int x, int y, int z) {
// voxels all around the requested voxel space. Having the empty voxels around
// the edges changes how the surface extractor behaves.
- if (_voxelSurfaceStyle == SURFACE_EDGED_CUBIC) {
+ if (_voxelSurfaceStyle == SURFACE_EDGED_CUBIC || _voxelSurfaceStyle == SURFACE_EDGED_MARCHING_CUBES) {
return _volData->getVoxelAt(x + 1, y + 1, z + 1);
}
return _volData->getVoxelAt(x, y, z);
}
-void RenderablePolyVoxEntityItem::setVoxelInternal(int x, int y, int z, uint8_t toValue) {
+bool RenderablePolyVoxEntityItem::setVoxelInternal(int x, int y, int z, uint8_t toValue) {
// set a voxel without recompressing the voxel data
assert(_volData);
+ bool result = false;
if (!inUserBounds(_volData, _voxelSurfaceStyle, x, y, z)) {
- return;
+ return false;
}
- updateOnCount(x, y, z, toValue);
+ result = updateOnCount(x, y, z, toValue);
- if (_voxelSurfaceStyle == SURFACE_EDGED_CUBIC) {
+ if (_voxelSurfaceStyle == SURFACE_EDGED_CUBIC || _voxelSurfaceStyle == SURFACE_EDGED_MARCHING_CUBES) {
_volData->setVoxelAt(x + 1, y + 1, z + 1, toValue);
} else {
_volData->setVoxelAt(x, y, z, toValue);
}
+
+ return result;
}
-
-void RenderablePolyVoxEntityItem::setVoxel(int x, int y, int z, uint8_t toValue) {
- if (_locked) {
- return;
+void RenderablePolyVoxEntityItem::clearEdges() {
+ // if we are in an edged mode, make sure the outside surfaces are zeroed out.
+ if (_voxelSurfaceStyle == SURFACE_EDGED_CUBIC || _voxelSurfaceStyle == SURFACE_EDGED_MARCHING_CUBES) {
+ for (int z = 0; z < _volData->getDepth(); z++) {
+ for (int y = 0; y < _volData->getHeight(); y++) {
+ for (int x = 0; x < _volData->getWidth(); x++) {
+ if (x == 0 || y == 0 || z == 0 ||
+ x == _volData->getWidth() - 1 ||
+ y == _volData->getHeight() - 1 ||
+ z == _volData->getDepth() - 1) {
+ _volData->setVoxelAt(x, y, z, 0);
+ }
+ }
+ }
+ }
}
- setVoxelInternal(x, y, z, toValue);
- compressVolumeData();
}
-void RenderablePolyVoxEntityItem::updateOnCount(int x, int y, int z, uint8_t toValue) {
+bool RenderablePolyVoxEntityItem::setVoxel(int x, int y, int z, uint8_t toValue) {
+ if (_locked) {
+ return false;
+ }
+ bool result = setVoxelInternal(x, y, z, toValue);
+ if (result) {
+ compressVolumeData();
+ }
+ return result;
+}
+
+bool RenderablePolyVoxEntityItem::updateOnCount(int x, int y, int z, uint8_t toValue) {
// keep _onCount up to date
if (!inUserBounds(_volData, _voxelSurfaceStyle, x, y, z)) {
- return;
+ return false;
}
uint8_t uVoxelValue = getVoxel(x, y, z);
if (toValue != 0) {
if (uVoxelValue == 0) {
_onCount++;
+ return true;
}
} else {
// toValue == 0
if (uVoxelValue != 0) {
_onCount--;
assert(_onCount >= 0);
+ return true;
}
}
+ return false;
}
-void RenderablePolyVoxEntityItem::setAll(uint8_t toValue) {
+bool RenderablePolyVoxEntityItem::setAll(uint8_t toValue) {
+ bool result = false;
if (_locked) {
- return;
+ return false;
}
for (int z = 0; z < _voxelVolumeSize.z; z++) {
for (int y = 0; y < _voxelVolumeSize.y; y++) {
for (int x = 0; x < _voxelVolumeSize.x; x++) {
- setVoxelInternal(x, y, z, toValue);
+ result |= setVoxelInternal(x, y, z, toValue);
}
}
}
- compressVolumeData();
+ if (result) {
+ compressVolumeData();
+ }
+ return result;
}
-void RenderablePolyVoxEntityItem::setVoxelInVolume(glm::vec3 position, uint8_t toValue) {
+bool RenderablePolyVoxEntityItem::setVoxelInVolume(glm::vec3 position, uint8_t toValue) {
if (_locked) {
- return;
+ return false;
}
// same as setVoxel but takes a vector rather than 3 floats.
- setVoxel((int)position.x, (int)position.y, (int)position.z, toValue);
+ return setVoxel(roundf(position.x), roundf(position.y), roundf(position.z), toValue);
}
-void RenderablePolyVoxEntityItem::setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue) {
+bool RenderablePolyVoxEntityItem::setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue) {
+ bool result = false;
if (_locked) {
- return;
+ return false;
}
// This three-level for loop iterates over every voxel in the volume
@@ -313,22 +354,24 @@ void RenderablePolyVoxEntityItem::setSphereInVolume(glm::vec3 center, float radi
float fDistToCenter = glm::distance(pos, center);
// If the current voxel is less than 'radius' units from the center then we make it solid.
if (fDistToCenter <= radius) {
- updateOnCount(x, y, z, toValue);
- setVoxelInternal(x, y, z, toValue);
+ result |= setVoxelInternal(x, y, z, toValue);
}
}
}
}
- compressVolumeData();
+ if (result) {
+ compressVolumeData();
+ }
+ return result;
}
-void RenderablePolyVoxEntityItem::setSphere(glm::vec3 centerWorldCoords, float radiusWorldCoords, uint8_t toValue) {
+bool RenderablePolyVoxEntityItem::setSphere(glm::vec3 centerWorldCoords, float radiusWorldCoords, uint8_t toValue) {
// glm::vec3 centerVoxelCoords = worldToVoxelCoordinates(centerWorldCoords);
glm::vec4 centerVoxelCoords = worldToVoxelMatrix() * glm::vec4(centerWorldCoords, 1.0f);
glm::vec3 scale = getDimensions() / _voxelVolumeSize; // meters / voxel-units
float scaleY = scale.y;
float radiusVoxelCoords = radiusWorldCoords / scaleY;
- setSphereInVolume(glm::vec3(centerVoxelCoords), radiusVoxelCoords, toValue);
+ return setSphereInVolume(glm::vec3(centerVoxelCoords), radiusVoxelCoords, toValue);
}
class RaycastFunctor
@@ -340,9 +383,10 @@ public:
}
bool operator()(PolyVox::SimpleVolume::Sampler& sampler)
{
- int x = sampler.getPosition().getX();
- int y = sampler.getPosition().getY();
- int z = sampler.getPosition().getZ();
+ PolyVox::Vector3DInt32 positionIndex = sampler.getPosition();
+ int x = positionIndex.getX();
+ int y = positionIndex.getY();
+ int z = positionIndex.getZ();
if (!inBounds(_vol, x, y, z)) {
return true;
@@ -351,8 +395,8 @@ public:
if (sampler.getVoxel() == 0) {
return true; // keep raycasting
}
- PolyVox::Vector3DInt32 positionIndex = sampler.getPosition();
- _result = glm::vec4(positionIndex.getX(), positionIndex.getY(), positionIndex.getZ(), 1.0f);
+
+ _result = glm::vec4((float)x, (float)y, (float)z, 1.0f);
return false;
}
glm::vec4 _result;
@@ -367,13 +411,16 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o
void** intersectedObject,
bool precisionPicking) const
{
+ // TODO -- correctly pick against marching-cube generated meshes
+
if (_needsModelReload || !precisionPicking) {
// just intersect with bounding box
return true;
}
- // the PolyVox ray intersection code requires a near and far point.
glm::mat4 wtvMatrix = worldToVoxelMatrix();
+ glm::mat4 vtwMatrix = voxelToWorldMatrix();
+ glm::mat4 vtlMatrix = voxelToLocalMatrix();
glm::vec3 normDirection = glm::normalize(direction);
// the PolyVox ray intersection code requires a near and far point.
@@ -396,23 +443,58 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o
return false;
}
- glm::vec4 result = callback._result;
- switch (_voxelSurfaceStyle) {
- case PolyVoxEntityItem::SURFACE_EDGED_CUBIC:
- result -= glm::vec4(1, 1, 1, 0); // compensate for the extra voxel border
- break;
- case PolyVoxEntityItem::SURFACE_MARCHING_CUBES:
- case PolyVoxEntityItem::SURFACE_CUBIC:
- break;
+ // result is in voxel-space coordinates.
+ glm::vec4 result = callback._result - glm::vec4(0.5f, 0.5f, 0.5f, 0.0f);
+
+ // set up ray tests against each face of the voxel.
+ glm::vec3 minXPosition = glm::vec3(vtwMatrix * (result + glm::vec4(0.0f, 0.5f, 0.5f, 0.0f)));
+ glm::vec3 maxXPosition = glm::vec3(vtwMatrix * (result + glm::vec4(1.0f, 0.5f, 0.5f, 0.0f)));
+ glm::vec3 minYPosition = glm::vec3(vtwMatrix * (result + glm::vec4(0.5f, 0.0f, 0.5f, 0.0f)));
+ glm::vec3 maxYPosition = glm::vec3(vtwMatrix * (result + glm::vec4(0.5f, 1.0f, 0.5f, 0.0f)));
+ glm::vec3 minZPosition = glm::vec3(vtwMatrix * (result + glm::vec4(0.5f, 0.5f, 0.0f, 0.0f)));
+ glm::vec3 maxZPosition = glm::vec3(vtwMatrix * (result + glm::vec4(0.5f, 0.5f, 1.0f, 0.0f)));
+
+ glm::vec4 baseDimensions = glm::vec4(1.0, 1.0, 1.0, 0.0);
+ glm::vec3 worldDimensions = glm::vec3(vtlMatrix * baseDimensions);
+ glm::vec2 xDimensions = glm::vec2(worldDimensions.z, worldDimensions.y);
+ glm::vec2 yDimensions = glm::vec2(worldDimensions.x, worldDimensions.z);
+ glm::vec2 zDimensions = glm::vec2(worldDimensions.x, worldDimensions.y);
+
+ glm::quat vtwRotation = extractRotation(vtwMatrix);
+ glm::quat minXRotation = vtwRotation * glm::quat(glm::vec3(0.0f, PI_OVER_TWO, 0.0f));
+ glm::quat maxXRotation = vtwRotation * glm::quat(glm::vec3(0.0f, PI_OVER_TWO, 0.0f));
+ glm::quat minYRotation = vtwRotation * glm::quat(glm::vec3(PI_OVER_TWO, 0.0f, 0.0f));
+ glm::quat maxYRotation = vtwRotation * glm::quat(glm::vec3(PI_OVER_TWO, 0.0f, 0.0f));
+ glm::quat minZRotation = vtwRotation * glm::quat(glm::vec3(0.0f, 0.0f, 0.0f));
+ glm::quat maxZRotation = vtwRotation * glm::quat(glm::vec3(0.0f, 0.0f, 0.0f));
+
+ float bestDx = FLT_MAX;
+ bool hit[ 6 ];
+ float dx[ 6 ] = {FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX};
+
+ hit[0] = findRayRectangleIntersection(origin, direction, minXRotation, minXPosition, xDimensions, dx[0]);
+ hit[1] = findRayRectangleIntersection(origin, direction, maxXRotation, maxXPosition, xDimensions, dx[1]);
+ hit[2] = findRayRectangleIntersection(origin, direction, minYRotation, minYPosition, yDimensions, dx[2]);
+ hit[3] = findRayRectangleIntersection(origin, direction, maxYRotation, maxYPosition, yDimensions, dx[3]);
+ hit[4] = findRayRectangleIntersection(origin, direction, minZRotation, minZPosition, zDimensions, dx[4]);
+ hit[5] = findRayRectangleIntersection(origin, direction, maxZRotation, maxZPosition, zDimensions, dx[5]);
+
+ bool ok = false;
+ for (int i = 0; i < 6; i ++) {
+ if (hit[ i ] && dx[ i ] < bestDx) {
+ face = (BoxFace)i;
+ distance = dx[ i ];
+ ok = true;
+ bestDx = dx[ i ];
+ }
}
- result -= glm::vec4(0.5f, 0.5f, 0.5f, 0.0f);
-
- glm::vec4 intersectedWorldPosition = voxelToWorldMatrix() * result;
-
- distance = glm::distance(glm::vec3(intersectedWorldPosition), origin);
-
- face = BoxFace::MIN_X_FACE; // XXX
+ if (!ok) {
+ // if the attempt to put the ray against one of the voxel-faces fails, just return the center
+ glm::vec4 intersectedWorldPosition = vtwMatrix * (result + vec4(0.5f, 0.5f, 0.5f, 0.0f));
+ distance = glm::distance(glm::vec3(intersectedWorldPosition), origin);
+ face = BoxFace::MIN_X_FACE;
+ }
return true;
}
@@ -421,6 +503,10 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o
// compress the data in _volData and save the results. The compressed form is used during
// saves to disk and for transmission over the wire
void RenderablePolyVoxEntityItem::compressVolumeData() {
+ #ifdef WANT_DEBUG
+ auto startTime = usecTimestampNow();
+ #endif
+
quint16 voxelXSize = _voxelVolumeSize.x;
quint16 voxelYSize = _voxelVolumeSize.y;
quint16 voxelZSize = _voxelVolumeSize.z;
@@ -471,6 +557,10 @@ void RenderablePolyVoxEntityItem::compressVolumeData() {
_dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS;
_needsModelReload = true;
+
+ #ifdef WANT_DEBUG
+ qDebug() << "RenderablePolyVoxEntityItem::compressVolumeData" << (usecTimestampNow() - startTime) << getName();
+ #endif
}
@@ -506,11 +596,11 @@ void RenderablePolyVoxEntityItem::decompressVolumeData() {
for (int y = 0; y < voxelYSize; y++) {
for (int x = 0; x < voxelXSize; x++) {
int uncompressedIndex = (z * voxelYSize * voxelXSize) + (y * voxelZSize) + x;
- updateOnCount(x, y, z, uncompressedData[uncompressedIndex]);
setVoxelInternal(x, y, z, uncompressedData[uncompressedIndex]);
}
}
}
+ clearEdges();
#ifdef WANT_DEBUG
qDebug() << "--------------- voxel decompress ---------------";
@@ -553,53 +643,112 @@ void RenderablePolyVoxEntityItem::computeShapeInfo(ShapeInfo& info) {
}
_points.clear();
- unsigned int i = 0;
-
- glm::mat4 wToM = voxelToLocalMatrix();
-
AABox box;
+ glm::mat4 vtoM = voxelToLocalMatrix();
- for (int z = 0; z < _voxelVolumeSize.z; z++) {
- for (int y = 0; y < _voxelVolumeSize.y; y++) {
- for (int x = 0; x < _voxelVolumeSize.x; x++) {
- if (getVoxel(x, y, z) > 0) {
- QVector pointsInPart;
+ if (_voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_MARCHING_CUBES ||
+ _voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES) {
+ unsigned int i = 0;
+ /* pull top-facing triangles into polyhedrons so they can be walked on */
+ const model::MeshPointer& mesh = _modelGeometry.getMesh();
+ const gpu::BufferView vertexBufferView = mesh->getVertexBuffer();
+ const gpu::BufferView& indexBufferView = mesh->getIndexBuffer();
+ gpu::BufferView::Iterator it = indexBufferView.cbegin();
+ while (it != indexBufferView.cend()) {
+ uint32_t p0Index = *(it++);
+ uint32_t p1Index = *(it++);
+ uint32_t p2Index = *(it++);
- float offL = -0.5f;
- float offH = 0.5f;
+ const glm::vec3& p0 = vertexBufferView.get(p0Index);
+ const glm::vec3& p1 = vertexBufferView.get(p1Index);
+ const glm::vec3& p2 = vertexBufferView.get(p2Index);
- glm::vec3 p000 = glm::vec3(wToM * glm::vec4(x + offL, y + offL, z + offL, 1.0f));
- glm::vec3 p001 = glm::vec3(wToM * glm::vec4(x + offL, y + offL, z + offH, 1.0f));
- glm::vec3 p010 = glm::vec3(wToM * glm::vec4(x + offL, y + offH, z + offL, 1.0f));
- glm::vec3 p011 = glm::vec3(wToM * glm::vec4(x + offL, y + offH, z + offH, 1.0f));
- glm::vec3 p100 = glm::vec3(wToM * glm::vec4(x + offH, y + offL, z + offL, 1.0f));
- glm::vec3 p101 = glm::vec3(wToM * glm::vec4(x + offH, y + offL, z + offH, 1.0f));
- glm::vec3 p110 = glm::vec3(wToM * glm::vec4(x + offH, y + offH, z + offL, 1.0f));
- glm::vec3 p111 = glm::vec3(wToM * glm::vec4(x + offH, y + offH, z + offH, 1.0f));
+ glm::vec3 av = (p0 + p1 + p2) / 3.0f; // center of the triangular face
+ glm::vec3 normal = glm::normalize(glm::cross(p1 - p0, p2 - p0));
+ glm::vec3 p3 = av - normal * MARCHING_CUBE_COLLISION_HULL_OFFSET;
- box += p000;
- box += p001;
- box += p010;
- box += p011;
- box += p100;
- box += p101;
- box += p110;
- box += p111;
+ glm::vec3 p0Model = glm::vec3(vtoM * glm::vec4(p0, 1.0f));
+ glm::vec3 p1Model = glm::vec3(vtoM * glm::vec4(p1, 1.0f));
+ glm::vec3 p2Model = glm::vec3(vtoM * glm::vec4(p2, 1.0f));
+ glm::vec3 p3Model = glm::vec3(vtoM * glm::vec4(p3, 1.0f));
- pointsInPart << p000;
- pointsInPart << p001;
- pointsInPart << p010;
- pointsInPart << p011;
- pointsInPart << p100;
- pointsInPart << p101;
- pointsInPart << p110;
- pointsInPart << p111;
+ box += p0Model;
+ box += p1Model;
+ box += p2Model;
+ box += p3Model;
- // add next convex hull
- QVector newMeshPoints;
- _points << newMeshPoints;
- // add points to the new convex hull
- _points[i++] << pointsInPart;
+ QVector pointsInPart;
+ pointsInPart << p0Model;
+ pointsInPart << p1Model;
+ pointsInPart << p2Model;
+ pointsInPart << p3Model;
+ // add next convex hull
+ QVector newMeshPoints;
+ _points << newMeshPoints;
+ // add points to the new convex hull
+ _points[i++] << pointsInPart;
+ }
+ } else {
+ unsigned int i = 0;
+
+ for (int z = 0; z < _voxelVolumeSize.z; z++) {
+ for (int y = 0; y < _voxelVolumeSize.y; y++) {
+ for (int x = 0; x < _voxelVolumeSize.x; x++) {
+ if (getVoxel(x, y, z) > 0) {
+
+ if ((x > 0 && getVoxel(x - 1, y, z) > 0) &&
+ (y > 0 && getVoxel(x, y - 1, z) > 0) &&
+ (z > 0 && getVoxel(x, y, z - 1) > 0) &&
+ (x < _voxelVolumeSize.x - 1 && getVoxel(x + 1, y, z) > 0) &&
+ (y < _voxelVolumeSize.y - 1 && getVoxel(x, y + 1, z) > 0) &&
+ (z < _voxelVolumeSize.z - 1 && getVoxel(x, y, z + 1) > 0)) {
+ // this voxel has neighbors in every cardinal direction, so there's no need
+ // to include it in the collision hull.
+ continue;
+ }
+
+ QVector pointsInPart;
+
+ float offL = -0.5f;
+ float offH = 0.5f;
+ if (_voxelSurfaceStyle == PolyVoxEntityItem::SURFACE_EDGED_CUBIC) {
+ offL += 1.0f;
+ offH += 1.0f;
+ }
+
+ glm::vec3 p000 = glm::vec3(vtoM * glm::vec4(x + offL, y + offL, z + offL, 1.0f));
+ glm::vec3 p001 = glm::vec3(vtoM * glm::vec4(x + offL, y + offL, z + offH, 1.0f));
+ glm::vec3 p010 = glm::vec3(vtoM * glm::vec4(x + offL, y + offH, z + offL, 1.0f));
+ glm::vec3 p011 = glm::vec3(vtoM * glm::vec4(x + offL, y + offH, z + offH, 1.0f));
+ glm::vec3 p100 = glm::vec3(vtoM * glm::vec4(x + offH, y + offL, z + offL, 1.0f));
+ glm::vec3 p101 = glm::vec3(vtoM * glm::vec4(x + offH, y + offL, z + offH, 1.0f));
+ glm::vec3 p110 = glm::vec3(vtoM * glm::vec4(x + offH, y + offH, z + offL, 1.0f));
+ glm::vec3 p111 = glm::vec3(vtoM * glm::vec4(x + offH, y + offH, z + offH, 1.0f));
+
+ box += p000;
+ box += p001;
+ box += p010;
+ box += p011;
+ box += p100;
+ box += p101;
+ box += p110;
+ box += p111;
+
+ pointsInPart << p000;
+ pointsInPart << p001;
+ pointsInPart << p010;
+ pointsInPart << p011;
+ pointsInPart << p100;
+ pointsInPart << p101;
+ pointsInPart << p110;
+ pointsInPart << p111;
+
+ // add next convex hull
+ QVector newMeshPoints;
+ _points << newMeshPoints;
+ // add points to the new convex hull
+ _points[i++] << pointsInPart;
+ }
}
}
}
@@ -629,10 +778,15 @@ void RenderablePolyVoxEntityItem::setZTextureURL(QString zTextureURL) {
}
void RenderablePolyVoxEntityItem::getModel() {
+ #ifdef WANT_DEBUG
+ auto startTime = usecTimestampNow();
+ #endif
+
// A mesh object to hold the result of surface extraction
PolyVox::SurfaceMesh polyVoxMesh;
switch (_voxelSurfaceStyle) {
+ case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES:
case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: {
PolyVox::MarchingCubesSurfaceExtractor> surfaceExtractor
(_volData, _volData->getEnclosingRegion(), &polyVoxMesh);
@@ -682,6 +836,10 @@ void RenderablePolyVoxEntityItem::getModel() {
#endif
_needsModelReload = false;
+
+ #ifdef WANT_DEBUG
+ qDebug() << "RenderablePolyVoxEntityItem::getModel" << (usecTimestampNow() - startTime) << getName();
+ #endif
}
void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
@@ -737,8 +895,6 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
_zTexture = DependencyManager::get()->getTexture(_zTextureURL);
}
- batch._glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
-
if (_xTexture) {
batch.setResourceTexture(0, _xTexture->getGPUTexture());
} else {
@@ -802,3 +958,19 @@ namespace render {
}
}
}
+
+glm::vec3 RenderablePolyVoxEntityItem::voxelCoordsToWorldCoords(glm::vec3& voxelCoords) const {
+ return glm::vec3(voxelToWorldMatrix() * glm::vec4(voxelCoords, 1.0f));
+}
+
+glm::vec3 RenderablePolyVoxEntityItem::worldCoordsToVoxelCoords(glm::vec3& worldCoords) const {
+ return glm::vec3(worldToVoxelMatrix() * glm::vec4(worldCoords, 1.0f));
+}
+
+glm::vec3 RenderablePolyVoxEntityItem::voxelCoordsToLocalCoords(glm::vec3& voxelCoords) const {
+ return glm::vec3(voxelToLocalMatrix() * glm::vec4(voxelCoords, 0.0f));
+}
+
+glm::vec3 RenderablePolyVoxEntityItem::localCoordsToVoxelCoords(glm::vec3& localCoords) const {
+ return glm::vec3(localToVoxelMatrix() * glm::vec4(localCoords, 0.0f));
+}
diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h
index d495900ce9..e2fcdedc31 100644
--- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h
+++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h
@@ -54,9 +54,9 @@ public:
}
virtual uint8_t getVoxel(int x, int y, int z);
- virtual void setVoxel(int x, int y, int z, uint8_t toValue);
+ virtual bool setVoxel(int x, int y, int z, uint8_t toValue);
- void updateOnCount(int x, int y, int z, uint8_t new_value);
+ bool updateOnCount(int x, int y, int z, uint8_t new_value);
void render(RenderArgs* args);
virtual bool supportsDetailedRayIntersection() const { return true; }
@@ -71,23 +71,26 @@ public:
virtual void setVoxelVolumeSize(glm::vec3 voxelVolumeSize);
glm::vec3 getSurfacePositionAdjustment() const;
glm::mat4 voxelToWorldMatrix() const;
- glm::mat4 voxelToLocalMatrix() const;
glm::mat4 worldToVoxelMatrix() const;
+ glm::mat4 voxelToLocalMatrix() const;
+ glm::mat4 localToVoxelMatrix() const;
virtual ShapeType getShapeType() const;
virtual bool isReadyToComputeShape();
virtual void computeShapeInfo(ShapeInfo& info);
+ virtual glm::vec3 voxelCoordsToWorldCoords(glm::vec3& voxelCoords) const;
+ virtual glm::vec3 worldCoordsToVoxelCoords(glm::vec3& worldCoords) const;
+ virtual glm::vec3 voxelCoordsToLocalCoords(glm::vec3& voxelCoords) const;
+ virtual glm::vec3 localCoordsToVoxelCoords(glm::vec3& localCoords) const;
// coords are in voxel-volume space
- virtual void setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue);
+ virtual bool setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue);
+ virtual bool setVoxelInVolume(glm::vec3 position, uint8_t toValue);
// coords are in world-space
- virtual void setSphere(glm::vec3 center, float radius, uint8_t toValue);
-
- virtual void setAll(uint8_t toValue);
-
- virtual void setVoxelInVolume(glm::vec3 position, uint8_t toValue);
+ virtual bool setSphere(glm::vec3 center, float radius, uint8_t toValue);
+ virtual bool setAll(uint8_t toValue);
virtual void setXTextureURL(QString xTextureURL);
virtual void setYTextureURL(QString yTextureURL);
@@ -107,10 +110,10 @@ private:
// The PolyVoxEntityItem class has _voxelData which contains dimensions and compressed voxel data. The dimensions
// may not match _voxelVolumeSize.
- void setVoxelInternal(int x, int y, int z, uint8_t toValue);
+ bool setVoxelInternal(int x, int y, int z, uint8_t toValue);
void compressVolumeData();
void decompressVolumeData();
-
+ void clearEdges();
PolyVox::SimpleVolume* _volData = nullptr;
model::Geometry _modelGeometry;
diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h
index 1e8cf51ef3..31c2ffad1e 100644
--- a/libraries/entities/src/EntityItemProperties.h
+++ b/libraries/entities/src/EntityItemProperties.h
@@ -154,9 +154,9 @@ public:
DEFINE_PROPERTY_REF(PROP_HREF, Href, href, QString);
DEFINE_PROPERTY_REF(PROP_DESCRIPTION, Description, description, QString);
DEFINE_PROPERTY(PROP_FACE_CAMERA, FaceCamera, faceCamera, bool);
+ DEFINE_PROPERTY_REF(PROP_ACTION_DATA, ActionData, actionData, QByteArray);
DEFINE_PROPERTY(PROP_NORMALS, Normals, normals, QVector);
DEFINE_PROPERTY(PROP_STROKE_WIDTHS, StrokeWidths, strokeWidths, QVector);
- DEFINE_PROPERTY_REF(PROP_ACTION_DATA, ActionData, actionData, QByteArray);
DEFINE_PROPERTY_REF(PROP_X_TEXTURE_URL, XTextureURL, xTextureURL, QString);
DEFINE_PROPERTY_REF(PROP_Y_TEXTURE_URL, YTextureURL, yTextureURL, QString);
DEFINE_PROPERTY_REF(PROP_Z_TEXTURE_URL, ZTextureURL, zTextureURL, QString);
diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp
index 9654de0fc4..41ca6a91ac 100644
--- a/libraries/entities/src/EntityScriptingInterface.cpp
+++ b/libraries/entities/src/EntityScriptingInterface.cpp
@@ -415,7 +415,7 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra
}
bool EntityScriptingInterface::setVoxels(QUuid entityID,
- std::function actor) {
+ std::function actor) {
if (!_entityTree) {
return false;
}
@@ -435,7 +435,7 @@ bool EntityScriptingInterface::setVoxels(QUuid entityID,
auto polyVoxEntity = std::dynamic_pointer_cast(entity);
_entityTree->lockForWrite();
- actor(*polyVoxEntity);
+ bool result = actor(*polyVoxEntity);
entity->setLastEdited(now);
entity->setLastBroadcast(now);
_entityTree->unlock();
@@ -448,42 +448,41 @@ bool EntityScriptingInterface::setVoxels(QUuid entityID,
properties.setLastEdited(now);
queueEntityMessage(PacketType::EntityEdit, entityID, properties);
- return true;
+ return result;
}
bool EntityScriptingInterface::setPoints(QUuid entityID, std::function actor) {
if (!_entityTree) {
return false;
}
-
+
EntityItemPointer entity = static_cast(_entityTree->findEntityByEntityItemID(entityID));
if (!entity) {
qCDebug(entities) << "EntityScriptingInterface::setPoints no entity with ID" << entityID;
}
-
+
EntityTypes::EntityType entityType = entity->getType();
-
+
if (entityType != EntityTypes::Line) {
return false;
}
-
+
auto now = usecTimestampNow();
-
+
auto lineEntity = std::static_pointer_cast(entity);
_entityTree->lockForWrite();
bool success = actor(*lineEntity);
entity->setLastEdited(now);
entity->setLastBroadcast(now);
_entityTree->unlock();
-
+
_entityTree->lockForRead();
EntityItemProperties properties = entity->getProperties();
_entityTree->unlock();
-
+
properties.setLinePointsDirty();
properties.setLastEdited(now);
-
-
+
queueEntityMessage(PacketType::EntityEdit, entityID, properties);
return success;
}
@@ -491,19 +490,19 @@ bool EntityScriptingInterface::setPoints(QUuid entityID, std::functiongetType();
-
+
if (entityType == EntityTypes::Line) {
return setPoints(entityID, [points](LineEntityItem& lineEntity) -> bool
{
return (LineEntityItem*)lineEntity.setLinePoints(points);
});
}
-
+
return false;
}
@@ -658,3 +657,83 @@ QVariantMap EntityScriptingInterface::getActionArguments(const QUuid& entityID,
});
return result;
}
+
+glm::vec3 EntityScriptingInterface::voxelCoordsToWorldCoords(const QUuid& entityID, glm::vec3 voxelCoords) {
+ if (!_entityTree) {
+ return glm::vec3(0.0f);
+ }
+
+ EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
+ if (!entity) {
+ qCDebug(entities) << "EntityScriptingInterface::voxelCoordsToWorldCoords no entity with ID" << entityID;
+ return glm::vec3(0.0f);
+ }
+
+ EntityTypes::EntityType entityType = entity->getType();
+ if (entityType != EntityTypes::PolyVox) {
+ return glm::vec3(0.0f);
+ }
+
+ auto polyVoxEntity = std::dynamic_pointer_cast(entity);
+ return polyVoxEntity->voxelCoordsToWorldCoords(voxelCoords);
+}
+
+glm::vec3 EntityScriptingInterface::worldCoordsToVoxelCoords(const QUuid& entityID, glm::vec3 worldCoords) {
+ if (!_entityTree) {
+ return glm::vec3(0.0f);
+ }
+
+ EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
+ if (!entity) {
+ qCDebug(entities) << "EntityScriptingInterface::worldCoordsToVoxelCoords no entity with ID" << entityID;
+ return glm::vec3(0.0f);
+ }
+
+ EntityTypes::EntityType entityType = entity->getType();
+ if (entityType != EntityTypes::PolyVox) {
+ return glm::vec3(0.0f);
+ }
+
+ auto polyVoxEntity = std::dynamic_pointer_cast(entity);
+ return polyVoxEntity->worldCoordsToVoxelCoords(worldCoords);
+}
+
+glm::vec3 EntityScriptingInterface::voxelCoordsToLocalCoords(const QUuid& entityID, glm::vec3 voxelCoords) {
+ if (!_entityTree) {
+ return glm::vec3(0.0f);
+ }
+
+ EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
+ if (!entity) {
+ qCDebug(entities) << "EntityScriptingInterface::voxelCoordsToLocalCoords no entity with ID" << entityID;
+ return glm::vec3(0.0f);
+ }
+
+ EntityTypes::EntityType entityType = entity->getType();
+ if (entityType != EntityTypes::PolyVox) {
+ return glm::vec3(0.0f);
+ }
+
+ auto polyVoxEntity = std::dynamic_pointer_cast(entity);
+ return polyVoxEntity->voxelCoordsToLocalCoords(voxelCoords);
+}
+
+glm::vec3 EntityScriptingInterface::localCoordsToVoxelCoords(const QUuid& entityID, glm::vec3 localCoords) {
+ if (!_entityTree) {
+ return glm::vec3(0.0f);
+ }
+
+ EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
+ if (!entity) {
+ qCDebug(entities) << "EntityScriptingInterface::localCoordsToVoxelCoords no entity with ID" << entityID;
+ return glm::vec3(0.0f);
+ }
+
+ EntityTypes::EntityType entityType = entity->getType();
+ if (entityType != EntityTypes::PolyVox) {
+ return glm::vec3(0.0f);
+ }
+
+ auto polyVoxEntity = std::dynamic_pointer_cast(entity);
+ return polyVoxEntity->localCoordsToVoxelCoords(localCoords);
+}
diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h
index 434134d2cd..a51ebfb61c 100644
--- a/libraries/entities/src/EntityScriptingInterface.h
+++ b/libraries/entities/src/EntityScriptingInterface.h
@@ -134,6 +134,11 @@ public slots:
Q_INVOKABLE QVector getActionIDs(const QUuid& entityID);
Q_INVOKABLE QVariantMap getActionArguments(const QUuid& entityID, const QUuid& actionID);
+ Q_INVOKABLE glm::vec3 voxelCoordsToWorldCoords(const QUuid& entityID, glm::vec3 voxelCoords);
+ Q_INVOKABLE glm::vec3 worldCoordsToVoxelCoords(const QUuid& entityID, glm::vec3 worldCoords);
+ Q_INVOKABLE glm::vec3 voxelCoordsToLocalCoords(const QUuid& entityID, glm::vec3 voxelCoords);
+ Q_INVOKABLE glm::vec3 localCoordsToVoxelCoords(const QUuid& entityID, glm::vec3 localCoords);
+
signals:
void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
@@ -162,7 +167,7 @@ signals:
private:
bool actionWorker(const QUuid& entityID, std::function actor);
- bool setVoxels(QUuid entityID, std::function actor);
+ bool setVoxels(QUuid entityID, std::function actor);
bool setPoints(QUuid entityID, std::function actor);
void queueEntityMessage(PacketType::Value packetType, EntityItemID entityID, const EntityItemProperties& properties);
diff --git a/libraries/entities/src/PolyLineEntityItem.cpp b/libraries/entities/src/PolyLineEntityItem.cpp
index 9ade80f3be..196fde47fb 100644
--- a/libraries/entities/src/PolyLineEntityItem.cpp
+++ b/libraries/entities/src/PolyLineEntityItem.cpp
@@ -170,9 +170,10 @@ bool PolyLineEntityItem::setLinePoints(const QVector& points) {
for (int i = 0; i < points.size(); i++) {
glm::vec3 point = points.at(i);
- glm::vec3 pos = getPosition();
glm::vec3 halfBox = getDimensions() * 0.5f;
- if ( (point.x < - halfBox.x || point.x > halfBox.x) || (point.y < -halfBox.y || point.y > halfBox.y) || (point.z < - halfBox.z || point.z > halfBox.z) ) {
+ if ((point.x < - halfBox.x || point.x > halfBox.x) ||
+ (point.y < -halfBox.y || point.y > halfBox.y) ||
+ (point.z < - halfBox.z || point.z > halfBox.z)) {
qDebug() << "Point is outside entity's bounding box";
return false;
}
diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp
index c9f3705712..30dded3714 100644
--- a/libraries/entities/src/PolyVoxEntityItem.cpp
+++ b/libraries/entities/src/PolyVoxEntityItem.cpp
@@ -22,7 +22,7 @@
const glm::vec3 PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE = glm::vec3(32, 32, 32);
-const float PolyVoxEntityItem::MAX_VOXEL_DIMENSION = 32.0f;
+const float PolyVoxEntityItem::MAX_VOXEL_DIMENSION = 128.0f;
const QByteArray PolyVoxEntityItem::DEFAULT_VOXEL_DATA(PolyVoxEntityItem::makeEmptyVoxelData());
const PolyVoxEntityItem::PolyVoxSurfaceStyle PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE =
PolyVoxEntityItem::SURFACE_MARCHING_CUBES;
diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h
index 0d0ab060f9..9fbaade407 100644
--- a/libraries/entities/src/PolyVoxEntityItem.h
+++ b/libraries/entities/src/PolyVoxEntityItem.h
@@ -58,7 +58,8 @@ class PolyVoxEntityItem : public EntityItem {
enum PolyVoxSurfaceStyle {
SURFACE_MARCHING_CUBES,
SURFACE_CUBIC,
- SURFACE_EDGED_CUBIC
+ SURFACE_EDGED_CUBIC,
+ SURFACE_EDGED_MARCHING_CUBES
};
void setVoxelSurfaceStyle(PolyVoxSurfaceStyle voxelSurfaceStyle);
@@ -73,17 +74,20 @@ class PolyVoxEntityItem : public EntityItem {
static const PolyVoxSurfaceStyle DEFAULT_VOXEL_SURFACE_STYLE;
// coords are in voxel-volume space
- virtual void setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue) {}
+ virtual bool setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue) { return false; }
+ virtual bool setVoxelInVolume(glm::vec3 position, uint8_t toValue) { return false; }
+
+ virtual glm::vec3 voxelCoordsToWorldCoords(glm::vec3& voxelCoords) const { return glm::vec3(0.0f); }
+ virtual glm::vec3 worldCoordsToVoxelCoords(glm::vec3& worldCoords) const { return glm::vec3(0.0f); }
+ virtual glm::vec3 voxelCoordsToLocalCoords(glm::vec3& voxelCoords) const { return glm::vec3(0.0f); }
+ virtual glm::vec3 localCoordsToVoxelCoords(glm::vec3& localCoords) const { return glm::vec3(0.0f); }
// coords are in world-space
- virtual void setSphere(glm::vec3 center, float radius, uint8_t toValue) {}
-
- virtual void setAll(uint8_t toValue) {}
-
- virtual void setVoxelInVolume(glm::vec3 position, uint8_t toValue) {}
+ virtual bool setSphere(glm::vec3 center, float radius, uint8_t toValue) { return false; }
+ virtual bool setAll(uint8_t toValue) { return false; }
virtual uint8_t getVoxel(int x, int y, int z) { return 0; }
- virtual void setVoxel(int x, int y, int z, uint8_t toValue) {}
+ virtual bool setVoxel(int x, int y, int z, uint8_t toValue) { return false; }
static QByteArray makeEmptyVoxelData(quint16 voxelXSize = 16, quint16 voxelYSize = 16, quint16 voxelZSize = 16);
diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp
index 5e7647263a..2db5f5fa51 100644
--- a/libraries/fbx/src/FBXReader.cpp
+++ b/libraries/fbx/src/FBXReader.cpp
@@ -1108,11 +1108,10 @@ ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex) {
if (subdata.name == "Materials") {
materials = getIntVector(subdata);
} else if (subdata.name == "MappingInformationType") {
- if (subdata.properties.at(0) == "ByPolygon") {
+ if (subdata.properties.at(0) == "ByPolygon")
isMaterialPerPolygon = true;
- } else {
- isMaterialPerPolygon = false;
- }
+ } else {
+ isMaterialPerPolygon = false;
}
}
@@ -1126,7 +1125,6 @@ ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex) {
}
}
-
bool isMultiMaterial = false;
if (isMaterialPerPolygon) {
isMultiMaterial = true;
diff --git a/libraries/gpu/src/gpu/State.h b/libraries/gpu/src/gpu/State.h
index 5e53284f36..5500f20e06 100755
--- a/libraries/gpu/src/gpu/State.h
+++ b/libraries/gpu/src/gpu/State.h
@@ -1,408 +1,408 @@
-//
-// State
-// libraries/gpu/src/gpu
-//
-// Created by Sam Gateau on 3/8/2015.
-// Copyright 2014 High Fidelity, Inc.
-//
-// Distributed under the Apache License, Version 2.0.
-// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
-//
-#ifndef hifi_gpu_State_h
-#define hifi_gpu_State_h
-
-#include "Format.h"
-
-#include
-#include
-#include
-#include
-
-// Why a macro and not a fancy template you will ask me ?
-// Because some of the fields are bool packed tightly in the State::Cache class
-// and it s just not good anymore for template T& variable manipulation...
-#define SET_FIELD(field, defaultValue, value, dest) {\
- dest = value;\
- if (value == defaultValue) {\
- _signature.reset(field);\
- } else {\
- _signature.set(field);\
- }\
- _stamp++;\
-}\
-
-
-namespace gpu {
-
-class GPUObject;
-
-class State {
-public:
- State();
- virtual ~State();
-
- Stamp getStamp() const { return _stamp; }
-
- typedef ::gpu::ComparisonFunction ComparisonFunction;
-
- enum FillMode {
- FILL_POINT = 0,
- FILL_LINE,
- FILL_FACE,
-
- NUM_FILL_MODES,
- };
-
- enum CullMode {
- CULL_NONE = 0,
- CULL_FRONT,
- CULL_BACK,
-
- NUM_CULL_MODES,
- };
-
- enum StencilOp {
- STENCIL_OP_KEEP = 0,
- STENCIL_OP_ZERO,
- STENCIL_OP_REPLACE,
- STENCIL_OP_INCR_SAT,
- STENCIL_OP_DECR_SAT,
- STENCIL_OP_INVERT,
- STENCIL_OP_INCR,
- STENCIL_OP_DECR,
-
- NUM_STENCIL_OPS,
- };
-
- enum BlendArg {
- ZERO = 0,
- ONE,
- SRC_COLOR,
- INV_SRC_COLOR,
- SRC_ALPHA,
- INV_SRC_ALPHA,
- DEST_ALPHA,
- INV_DEST_ALPHA,
- DEST_COLOR,
- INV_DEST_COLOR,
- SRC_ALPHA_SAT,
- FACTOR_COLOR,
- INV_FACTOR_COLOR,
- FACTOR_ALPHA,
- INV_FACTOR_ALPHA,
-
- NUM_BLEND_ARGS,
- };
-
- enum BlendOp {
- BLEND_OP_ADD = 0,
- BLEND_OP_SUBTRACT,
- BLEND_OP_REV_SUBTRACT,
- BLEND_OP_MIN,
- BLEND_OP_MAX,
-
- NUM_BLEND_OPS,
- };
-
- enum ColorMask
- {
- WRITE_NONE = 0,
- WRITE_RED = 1,
- WRITE_GREEN = 2,
- WRITE_BLUE = 4,
- WRITE_ALPHA = 8,
- WRITE_ALL = (WRITE_RED | WRITE_GREEN | WRITE_BLUE | WRITE_ALPHA ),
- };
-
- class DepthTest {
- uint8 _function = LESS;
- uint8 _writeMask = true;
- uint8 _enabled = false;
- uint8 _spare = 0;
- public:
- DepthTest(bool enabled = false, bool writeMask = true, ComparisonFunction func = LESS) :
- _function(func), _writeMask(writeMask), _enabled(enabled) {}
-
- bool isEnabled() const { return _enabled; }
- ComparisonFunction getFunction() const { return ComparisonFunction(_function); }
- bool getWriteMask() const { return _writeMask; }
-
- int32 getRaw() const { return *(reinterpret_cast(this)); }
- DepthTest(int32 raw) { *(reinterpret_cast(this)) = raw; }
- bool operator== (const DepthTest& right) const { return getRaw() == right.getRaw(); }
- bool operator!= (const DepthTest& right) const { return getRaw() != right.getRaw(); }
- };
-
- class StencilTest {
- static const int FUNC_MASK = 0x000f;
- static const int FAIL_OP_MASK = 0x00f0;
- static const int DEPTH_FAIL_OP_MASK = 0x0f00;
- static const int PASS_OP_MASK = 0xf000;
- static const int FAIL_OP_OFFSET = 4;
- static const int DEPTH_FAIL_OP_OFFSET = 8;
- static const int PASS_OP_OFFSET = 12;
-
- uint16 _functionAndOperations;
- uint8 _reference = 0;
- uint8 _readMask = 0xff;
- public:
-
- StencilTest(uint8 reference = 0, uint8 readMask =0xFF, ComparisonFunction func = ALWAYS, StencilOp failOp = STENCIL_OP_KEEP, StencilOp depthFailOp = STENCIL_OP_KEEP, StencilOp passOp = STENCIL_OP_KEEP) :
- _functionAndOperations(func | (failOp << FAIL_OP_OFFSET) | (depthFailOp << DEPTH_FAIL_OP_OFFSET) | (passOp << PASS_OP_OFFSET)),
- _reference(reference), _readMask(readMask)
- {}
-
- ComparisonFunction getFunction() const { return ComparisonFunction(_functionAndOperations & FUNC_MASK); }
- StencilOp getFailOp() const { return StencilOp((_functionAndOperations & FAIL_OP_MASK) >> FAIL_OP_OFFSET); }
- StencilOp getDepthFailOp() const { return StencilOp((_functionAndOperations & DEPTH_FAIL_OP_MASK) >> DEPTH_FAIL_OP_OFFSET); }
- StencilOp getPassOp() const { return StencilOp((_functionAndOperations & PASS_OP_MASK) >> PASS_OP_OFFSET); }
-
- uint8 getReference() const { return _reference; }
- uint8 getReadMask() const { return _readMask; }
-
- int32 getRaw() const { return *(reinterpret_cast(this)); }
- StencilTest(int32 raw) { *(reinterpret_cast(this)) = raw; }
- bool operator== (const StencilTest& right) const { return getRaw() == right.getRaw(); }
- bool operator!= (const StencilTest& right) const { return getRaw() != right.getRaw(); }
- };
-
- class StencilActivation {
- uint8 _frontWriteMask = 0xFF;
- uint8 _backWriteMask = 0xFF;
- uint16 _enabled = 0;
- public:
-
- StencilActivation(bool enabled, uint8 frontWriteMask = 0xFF, uint8 backWriteMask = 0xFF) :
- _frontWriteMask(frontWriteMask), _backWriteMask(backWriteMask), _enabled(enabled) {}
-
- bool isEnabled() const { return (_enabled != 0); }
- uint8 getWriteMaskFront() const { return _frontWriteMask; }
- uint8 getWriteMaskBack() const { return _backWriteMask; }
-
- int32 getRaw() const { return *(reinterpret_cast(this)); }
- StencilActivation(int32 raw) { *(reinterpret_cast(this)) = raw; }
- bool operator== (const StencilActivation& right) const { return getRaw() == right.getRaw(); }
- bool operator!= (const StencilActivation& right) const { return getRaw() != right.getRaw(); }
- };
-
- class BlendFunction {
- static const int COLOR_MASK = 0x0f;
- static const int ALPHA_MASK = 0xf0;
- static const int ALPHA_OFFSET = 4;
-
- uint8 _enabled;
- uint8 _source;
- uint8 _destination;
- uint8 _operation;
- public:
-
- BlendFunction(bool enabled,
- BlendArg sourceColor, BlendOp operationColor, BlendArg destinationColor,
- BlendArg sourceAlpha, BlendOp operationAlpha, BlendArg destinationAlpha) :
- _enabled(enabled),
- _source(sourceColor | (sourceAlpha << ALPHA_OFFSET)),
- _destination(destinationColor | (destinationAlpha << ALPHA_OFFSET)),
- _operation(operationColor | (operationAlpha << ALPHA_OFFSET)) {}
-
- BlendFunction(bool enabled, BlendArg source = ONE, BlendOp operation = BLEND_OP_ADD, BlendArg destination = ZERO) :
- _enabled(enabled),
- _source(source | (source << ALPHA_OFFSET)),
- _destination(destination | (destination << ALPHA_OFFSET)),
- _operation(operation | (operation << ALPHA_OFFSET)) {}
-
- bool isEnabled() const { return (_enabled != 0); }
-
- BlendArg getSourceColor() const { return BlendArg(_source & COLOR_MASK); }
- BlendArg getDestinationColor() const { return BlendArg(_destination & COLOR_MASK); }
- BlendOp getOperationColor() const { return BlendOp(_operation & COLOR_MASK); }
-
- BlendArg getSourceAlpha() const { return BlendArg((_source & ALPHA_MASK) >> ALPHA_OFFSET); }
- BlendArg getDestinationAlpha() const { return BlendArg((_destination & ALPHA_MASK) >> ALPHA_OFFSET); }
- BlendOp getOperationAlpha() const { return BlendOp((_operation & ALPHA_MASK) >> ALPHA_OFFSET); }
-
- int32 getRaw() const { return *(reinterpret_cast(this)); }
- BlendFunction(int32 raw) { *(reinterpret_cast(this)) = raw; }
- bool operator== (const BlendFunction& right) const { return getRaw() == right.getRaw(); }
- bool operator!= (const BlendFunction& right) const { return getRaw() != right.getRaw(); }
- };
-
- // The Data class is the full explicit description of the State class fields value.
- // Useful for having one const static called Default for reference or for the gpu::Backend to keep track of the current value
- class Data {
- public:
- float depthBias = 0.0f;
- float depthBiasSlopeScale = 0.0f;
-
- DepthTest depthTest = DepthTest(false, true, LESS);
-
- StencilActivation stencilActivation = StencilActivation(false);
- StencilTest stencilTestFront = StencilTest(0, 0xff, ALWAYS, STENCIL_OP_KEEP, STENCIL_OP_KEEP, STENCIL_OP_KEEP);
- StencilTest stencilTestBack = StencilTest(0, 0xff, ALWAYS, STENCIL_OP_KEEP, STENCIL_OP_KEEP, STENCIL_OP_KEEP);
-
- uint32 sampleMask = 0xFFFFFFFF;
-
- BlendFunction blendFunction = BlendFunction(false);
-
- uint8 fillMode = FILL_FACE;
- uint8 cullMode = CULL_NONE;
-
- uint8 colorWriteMask = WRITE_ALL;
-
- bool frontFaceClockwise : 1;
- bool depthClampEnable : 1;
- bool scissorEnable : 1;
- bool multisampleEnable : 1;
- bool antialisedLineEnable : 1;
- bool alphaToCoverageEnable : 1;
-
- Data() :
- frontFaceClockwise(false),
- depthClampEnable(false),
- scissorEnable(false),
- multisampleEnable(false),
- antialisedLineEnable(true),
- alphaToCoverageEnable(false)
- {}
- };
-
- // The unique default values for all the fields
- static const Data DEFAULT;
- void setFillMode(FillMode fill) { SET_FIELD(FILL_MODE, DEFAULT.fillMode, fill, _values.fillMode); }
- FillMode getFillMode() const { return FillMode(_values.fillMode); }
-
- void setCullMode(CullMode cull) { SET_FIELD(CULL_MODE, DEFAULT.cullMode, cull, _values.cullMode); }
- CullMode getCullMode() const { return CullMode(_values.cullMode); }
-
- void setFrontFaceClockwise(bool isClockwise) { SET_FIELD(FRONT_FACE_CLOCKWISE, DEFAULT.frontFaceClockwise, isClockwise, _values.frontFaceClockwise); }
- bool isFrontFaceClockwise() const { return _values.frontFaceClockwise; }
-
- void setDepthClampEnable(bool enable) { SET_FIELD(DEPTH_CLAMP_ENABLE, DEFAULT.depthClampEnable, enable, _values.depthClampEnable); }
- bool isDepthClampEnable() const { return _values.depthClampEnable; }
-
- void setScissorEnable(bool enable) { SET_FIELD(SCISSOR_ENABLE, DEFAULT.scissorEnable, enable, _values.scissorEnable); }
- bool isScissorEnable() const { return _values.scissorEnable; }
-
- void setMultisampleEnable(bool enable) { SET_FIELD(MULTISAMPLE_ENABLE, DEFAULT.multisampleEnable, enable, _values.multisampleEnable); }
- bool isMultisampleEnable() const { return _values.multisampleEnable; }
-
- void setAntialiasedLineEnable(bool enable) { SET_FIELD(ANTIALISED_LINE_ENABLE, DEFAULT.antialisedLineEnable, enable, _values.antialisedLineEnable); }
- bool isAntialiasedLineEnable() const { return _values.antialisedLineEnable; }
-
- // Depth Bias
- void setDepthBias(float bias) { SET_FIELD(DEPTH_BIAS, DEFAULT.depthBias, bias, _values.depthBias); }
- float getDepthBias() const { return _values.depthBias; }
-
- void setDepthBiasSlopeScale(float scale) { SET_FIELD(DEPTH_BIAS_SLOPE_SCALE, DEFAULT.depthBiasSlopeScale, scale, _values.depthBiasSlopeScale); }
- float getDepthBiasSlopeScale() const { return _values.depthBiasSlopeScale; }
-
- // Depth Test
- void setDepthTest(DepthTest depthTest) { SET_FIELD(DEPTH_TEST, DEFAULT.depthTest, depthTest, _values.depthTest); }
- void setDepthTest(bool enable, bool writeMask, ComparisonFunction func) { setDepthTest(DepthTest(enable, writeMask, func)); }
- DepthTest getDepthTest() const { return _values.depthTest; }
-
- bool isDepthTestEnabled() const { return getDepthTest().isEnabled(); }
- bool getDepthTestWriteMask() const { return getDepthTest().getWriteMask(); }
- ComparisonFunction getDepthTestFunc() const { return getDepthTest().getFunction(); }
-
- // Stencil test
- void setStencilTest(bool enabled, uint8 frontWriteMask, StencilTest frontTest, uint8 backWriteMask, StencilTest backTest) {
- SET_FIELD(STENCIL_ACTIVATION, DEFAULT.stencilActivation, StencilActivation(enabled, frontWriteMask, backWriteMask), _values.stencilActivation);
- SET_FIELD(STENCIL_TEST_FRONT, DEFAULT.stencilTestFront, frontTest, _values.stencilTestFront);
- SET_FIELD(STENCIL_TEST_BACK, DEFAULT.stencilTestBack, backTest, _values.stencilTestBack); }
- void setStencilTest(bool enabled, uint8 frontWriteMask, StencilTest frontTest) {
- setStencilTest(enabled, frontWriteMask, frontTest, frontWriteMask, frontTest); }
-
- StencilActivation getStencilActivation() const { return _values.stencilActivation; }
- StencilTest getStencilTestFront() const { return _values.stencilTestFront; }
- StencilTest getStencilTestBack() const { return _values.stencilTestBack; }
-
- bool isStencilEnabled() const { return getStencilActivation().isEnabled(); }
- uint8 getStencilWriteMaskFront() const { return getStencilActivation().getWriteMaskFront(); }
- uint8 getStencilWriteMaskBack() const { return getStencilActivation().getWriteMaskBack(); }
-
- // Alpha to coverage
- void setAlphaToCoverageEnable(bool enable) { SET_FIELD(ALPHA_TO_COVERAGE_ENABLE, DEFAULT.alphaToCoverageEnable, enable, _values.alphaToCoverageEnable); }
- bool isAlphaToCoverageEnabled() const { return _values.alphaToCoverageEnable; }
-
- // Sample mask
- void setSampleMask(uint32 mask) { SET_FIELD(SAMPLE_MASK, DEFAULT.sampleMask, mask, _values.sampleMask); }
- uint32 getSampleMask() const { return _values.sampleMask; }
-
- // Blend Function
- void setBlendFunction(BlendFunction function) { SET_FIELD(BLEND_FUNCTION, DEFAULT.blendFunction, function, _values.blendFunction); }
- BlendFunction getBlendFunction() const { return _values.blendFunction; }
-
- void setBlendFunction(bool enabled, BlendArg sourceColor, BlendOp operationColor, BlendArg destinationColor, BlendArg sourceAlpha, BlendOp operationAlpha, BlendArg destinationAlpha) {
- setBlendFunction(BlendFunction(enabled, sourceColor, operationColor, destinationColor, sourceAlpha, operationAlpha, destinationAlpha)); }
- void setBlendFunction(bool enabled, BlendArg source, BlendOp operation, BlendArg destination) {
- setBlendFunction(BlendFunction(enabled, source, operation, destination)); }
-
- bool isBlendEnabled() const { return getBlendFunction().isEnabled(); }
-
- // Color write mask
- void setColorWriteMask(uint8 mask) { SET_FIELD(COLOR_WRITE_MASK, DEFAULT.colorWriteMask, mask, _values.colorWriteMask); }
- void setColorWriteMask(bool red, bool green, bool blue, bool alpha) { uint32 value = ((WRITE_RED * red) | (WRITE_GREEN * green) | (WRITE_BLUE * blue) | (WRITE_ALPHA * alpha)); SET_FIELD(COLOR_WRITE_MASK, DEFAULT.colorWriteMask, value, _values.colorWriteMask); }
- uint8 getColorWriteMask() const { return _values.colorWriteMask; }
-
- // All the possible fields
- enum Field {
- FILL_MODE,
- CULL_MODE,
- FRONT_FACE_CLOCKWISE,
- DEPTH_CLAMP_ENABLE,
- SCISSOR_ENABLE,
- MULTISAMPLE_ENABLE,
- ANTIALISED_LINE_ENABLE,
-
- DEPTH_BIAS,
- DEPTH_BIAS_SLOPE_SCALE,
-
- DEPTH_TEST,
-
- STENCIL_ACTIVATION,
- STENCIL_TEST_FRONT,
- STENCIL_TEST_BACK,
-
- SAMPLE_MASK,
- ALPHA_TO_COVERAGE_ENABLE,
-
- BLEND_FUNCTION,
-
- COLOR_WRITE_MASK,
-
- NUM_FIELDS, // not a valid field, just the count
- };
-
- // The signature of the state tells which fields of the state are not default
- // this way during rendering the Backend can compare it's current state and try to minimize the job to do
- typedef std::bitset Signature;
-
- Signature getSignature() const { return _signature; }
-
- static Signature evalSignature(const Data& state);
-
- // For convenience, create a State from the values directly
- State(const Data& values);
- const Data& getValues() const { return _values; }
-
-protected:
- State(const State& state);
- State& operator=(const State& state);
-
- Data _values;
- Signature _signature{0};
- Stamp _stamp{0};
-
- // This shouldn't be used by anything else than the Backend class with the proper casting.
- mutable GPUObject* _gpuObject = nullptr;
- void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; }
- GPUObject* getGPUObject() const { return _gpuObject; }
- friend class Backend;
-};
-
-typedef std::shared_ptr< State > StatePointer;
-typedef std::vector< StatePointer > States;
-
-};
-
-#endif
+//
+// State
+// libraries/gpu/src/gpu
+//
+// Created by Sam Gateau on 3/8/2015.
+// Copyright 2014 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+#ifndef hifi_gpu_State_h
+#define hifi_gpu_State_h
+
+#include "Format.h"
+
+#include
+#include
+#include
+#include
+
+// Why a macro and not a fancy template you will ask me ?
+// Because some of the fields are bool packed tightly in the State::Cache class
+// and it s just not good anymore for template T& variable manipulation...
+#define SET_FIELD(field, defaultValue, value, dest) {\
+ dest = value;\
+ if (value == defaultValue) {\
+ _signature.reset(field);\
+ } else {\
+ _signature.set(field);\
+ }\
+ _stamp++;\
+}\
+
+
+namespace gpu {
+
+class GPUObject;
+
+class State {
+public:
+ State();
+ virtual ~State();
+
+ Stamp getStamp() const { return _stamp; }
+
+ typedef ::gpu::ComparisonFunction ComparisonFunction;
+
+ enum FillMode {
+ FILL_POINT = 0,
+ FILL_LINE,
+ FILL_FACE,
+
+ NUM_FILL_MODES,
+ };
+
+ enum CullMode {
+ CULL_NONE = 0,
+ CULL_FRONT,
+ CULL_BACK,
+
+ NUM_CULL_MODES,
+ };
+
+ enum StencilOp {
+ STENCIL_OP_KEEP = 0,
+ STENCIL_OP_ZERO,
+ STENCIL_OP_REPLACE,
+ STENCIL_OP_INCR_SAT,
+ STENCIL_OP_DECR_SAT,
+ STENCIL_OP_INVERT,
+ STENCIL_OP_INCR,
+ STENCIL_OP_DECR,
+
+ NUM_STENCIL_OPS,
+ };
+
+ enum BlendArg {
+ ZERO = 0,
+ ONE,
+ SRC_COLOR,
+ INV_SRC_COLOR,
+ SRC_ALPHA,
+ INV_SRC_ALPHA,
+ DEST_ALPHA,
+ INV_DEST_ALPHA,
+ DEST_COLOR,
+ INV_DEST_COLOR,
+ SRC_ALPHA_SAT,
+ FACTOR_COLOR,
+ INV_FACTOR_COLOR,
+ FACTOR_ALPHA,
+ INV_FACTOR_ALPHA,
+
+ NUM_BLEND_ARGS,
+ };
+
+ enum BlendOp {
+ BLEND_OP_ADD = 0,
+ BLEND_OP_SUBTRACT,
+ BLEND_OP_REV_SUBTRACT,
+ BLEND_OP_MIN,
+ BLEND_OP_MAX,
+
+ NUM_BLEND_OPS,
+ };
+
+ enum ColorMask
+ {
+ WRITE_NONE = 0,
+ WRITE_RED = 1,
+ WRITE_GREEN = 2,
+ WRITE_BLUE = 4,
+ WRITE_ALPHA = 8,
+ WRITE_ALL = (WRITE_RED | WRITE_GREEN | WRITE_BLUE | WRITE_ALPHA ),
+ };
+
+ class DepthTest {
+ uint8 _function = LESS;
+ uint8 _writeMask = true;
+ uint8 _enabled = false;
+ uint8 _spare = 0;
+ public:
+ DepthTest(bool enabled = false, bool writeMask = true, ComparisonFunction func = LESS) :
+ _function(func), _writeMask(writeMask), _enabled(enabled) {}
+
+ bool isEnabled() const { return _enabled != 0; }
+ ComparisonFunction getFunction() const { return ComparisonFunction(_function); }
+ uint8 getWriteMask() const { return _writeMask; }
+
+ int32 getRaw() const { return *(reinterpret_cast(this)); }
+ DepthTest(int32 raw) { *(reinterpret_cast(this)) = raw; }
+ bool operator== (const DepthTest& right) const { return getRaw() == right.getRaw(); }
+ bool operator!= (const DepthTest& right) const { return getRaw() != right.getRaw(); }
+ };
+
+ class StencilTest {
+ static const int FUNC_MASK = 0x000f;
+ static const int FAIL_OP_MASK = 0x00f0;
+ static const int DEPTH_FAIL_OP_MASK = 0x0f00;
+ static const int PASS_OP_MASK = 0xf000;
+ static const int FAIL_OP_OFFSET = 4;
+ static const int DEPTH_FAIL_OP_OFFSET = 8;
+ static const int PASS_OP_OFFSET = 12;
+
+ uint16 _functionAndOperations;
+ uint8 _reference = 0;
+ uint8 _readMask = 0xff;
+ public:
+
+ StencilTest(uint8 reference = 0, uint8 readMask =0xFF, ComparisonFunction func = ALWAYS, StencilOp failOp = STENCIL_OP_KEEP, StencilOp depthFailOp = STENCIL_OP_KEEP, StencilOp passOp = STENCIL_OP_KEEP) :
+ _functionAndOperations(func | (failOp << FAIL_OP_OFFSET) | (depthFailOp << DEPTH_FAIL_OP_OFFSET) | (passOp << PASS_OP_OFFSET)),
+ _reference(reference), _readMask(readMask)
+ {}
+
+ ComparisonFunction getFunction() const { return ComparisonFunction(_functionAndOperations & FUNC_MASK); }
+ StencilOp getFailOp() const { return StencilOp((_functionAndOperations & FAIL_OP_MASK) >> FAIL_OP_OFFSET); }
+ StencilOp getDepthFailOp() const { return StencilOp((_functionAndOperations & DEPTH_FAIL_OP_MASK) >> DEPTH_FAIL_OP_OFFSET); }
+ StencilOp getPassOp() const { return StencilOp((_functionAndOperations & PASS_OP_MASK) >> PASS_OP_OFFSET); }
+
+ uint8 getReference() const { return _reference; }
+ uint8 getReadMask() const { return _readMask; }
+
+ int32 getRaw() const { return *(reinterpret_cast(this)); }
+ StencilTest(int32 raw) { *(reinterpret_cast(this)) = raw; }
+ bool operator== (const StencilTest& right) const { return getRaw() == right.getRaw(); }
+ bool operator!= (const StencilTest& right) const { return getRaw() != right.getRaw(); }
+ };
+
+ class StencilActivation {
+ uint8 _frontWriteMask = 0xFF;
+ uint8 _backWriteMask = 0xFF;
+ uint16 _enabled = 0;
+ public:
+
+ StencilActivation(bool enabled, uint8 frontWriteMask = 0xFF, uint8 backWriteMask = 0xFF) :
+ _frontWriteMask(frontWriteMask), _backWriteMask(backWriteMask), _enabled(enabled) {}
+
+ bool isEnabled() const { return (_enabled != 0); }
+ uint8 getWriteMaskFront() const { return _frontWriteMask; }
+ uint8 getWriteMaskBack() const { return _backWriteMask; }
+
+ int32 getRaw() const { return *(reinterpret_cast(this)); }
+ StencilActivation(int32 raw) { *(reinterpret_cast(this)) = raw; }
+ bool operator== (const StencilActivation& right) const { return getRaw() == right.getRaw(); }
+ bool operator!= (const StencilActivation& right) const { return getRaw() != right.getRaw(); }
+ };
+
+ class BlendFunction {
+ static const int COLOR_MASK = 0x0f;
+ static const int ALPHA_MASK = 0xf0;
+ static const int ALPHA_OFFSET = 4;
+
+ uint8 _enabled;
+ uint8 _source;
+ uint8 _destination;
+ uint8 _operation;
+ public:
+
+ BlendFunction(bool enabled,
+ BlendArg sourceColor, BlendOp operationColor, BlendArg destinationColor,
+ BlendArg sourceAlpha, BlendOp operationAlpha, BlendArg destinationAlpha) :
+ _enabled(enabled),
+ _source(sourceColor | (sourceAlpha << ALPHA_OFFSET)),
+ _destination(destinationColor | (destinationAlpha << ALPHA_OFFSET)),
+ _operation(operationColor | (operationAlpha << ALPHA_OFFSET)) {}
+
+ BlendFunction(bool enabled, BlendArg source = ONE, BlendOp operation = BLEND_OP_ADD, BlendArg destination = ZERO) :
+ _enabled(enabled),
+ _source(source | (source << ALPHA_OFFSET)),
+ _destination(destination | (destination << ALPHA_OFFSET)),
+ _operation(operation | (operation << ALPHA_OFFSET)) {}
+
+ bool isEnabled() const { return (_enabled != 0); }
+
+ BlendArg getSourceColor() const { return BlendArg(_source & COLOR_MASK); }
+ BlendArg getDestinationColor() const { return BlendArg(_destination & COLOR_MASK); }
+ BlendOp getOperationColor() const { return BlendOp(_operation & COLOR_MASK); }
+
+ BlendArg getSourceAlpha() const { return BlendArg((_source & ALPHA_MASK) >> ALPHA_OFFSET); }
+ BlendArg getDestinationAlpha() const { return BlendArg((_destination & ALPHA_MASK) >> ALPHA_OFFSET); }
+ BlendOp getOperationAlpha() const { return BlendOp((_operation & ALPHA_MASK) >> ALPHA_OFFSET); }
+
+ int32 getRaw() const { return *(reinterpret_cast(this)); }
+ BlendFunction(int32 raw) { *(reinterpret_cast(this)) = raw; }
+ bool operator== (const BlendFunction& right) const { return getRaw() == right.getRaw(); }
+ bool operator!= (const BlendFunction& right) const { return getRaw() != right.getRaw(); }
+ };
+
+ // The Data class is the full explicit description of the State class fields value.
+ // Useful for having one const static called Default for reference or for the gpu::Backend to keep track of the current value
+ class Data {
+ public:
+ float depthBias = 0.0f;
+ float depthBiasSlopeScale = 0.0f;
+
+ DepthTest depthTest = DepthTest(false, true, LESS);
+
+ StencilActivation stencilActivation = StencilActivation(false);
+ StencilTest stencilTestFront = StencilTest(0, 0xff, ALWAYS, STENCIL_OP_KEEP, STENCIL_OP_KEEP, STENCIL_OP_KEEP);
+ StencilTest stencilTestBack = StencilTest(0, 0xff, ALWAYS, STENCIL_OP_KEEP, STENCIL_OP_KEEP, STENCIL_OP_KEEP);
+
+ uint32 sampleMask = 0xFFFFFFFF;
+
+ BlendFunction blendFunction = BlendFunction(false);
+
+ uint8 fillMode = FILL_FACE;
+ uint8 cullMode = CULL_NONE;
+
+ uint8 colorWriteMask = WRITE_ALL;
+
+ bool frontFaceClockwise : 1;
+ bool depthClampEnable : 1;
+ bool scissorEnable : 1;
+ bool multisampleEnable : 1;
+ bool antialisedLineEnable : 1;
+ bool alphaToCoverageEnable : 1;
+
+ Data() :
+ frontFaceClockwise(false),
+ depthClampEnable(false),
+ scissorEnable(false),
+ multisampleEnable(false),
+ antialisedLineEnable(true),
+ alphaToCoverageEnable(false)
+ {}
+ };
+
+ // The unique default values for all the fields
+ static const Data DEFAULT;
+ void setFillMode(FillMode fill) { SET_FIELD(FILL_MODE, DEFAULT.fillMode, fill, _values.fillMode); }
+ FillMode getFillMode() const { return FillMode(_values.fillMode); }
+
+ void setCullMode(CullMode cull) { SET_FIELD(CULL_MODE, DEFAULT.cullMode, cull, _values.cullMode); }
+ CullMode getCullMode() const { return CullMode(_values.cullMode); }
+
+ void setFrontFaceClockwise(bool isClockwise) { SET_FIELD(FRONT_FACE_CLOCKWISE, DEFAULT.frontFaceClockwise, isClockwise, _values.frontFaceClockwise); }
+ bool isFrontFaceClockwise() const { return _values.frontFaceClockwise; }
+
+ void setDepthClampEnable(bool enable) { SET_FIELD(DEPTH_CLAMP_ENABLE, DEFAULT.depthClampEnable, enable, _values.depthClampEnable); }
+ bool isDepthClampEnable() const { return _values.depthClampEnable; }
+
+ void setScissorEnable(bool enable) { SET_FIELD(SCISSOR_ENABLE, DEFAULT.scissorEnable, enable, _values.scissorEnable); }
+ bool isScissorEnable() const { return _values.scissorEnable; }
+
+ void setMultisampleEnable(bool enable) { SET_FIELD(MULTISAMPLE_ENABLE, DEFAULT.multisampleEnable, enable, _values.multisampleEnable); }
+ bool isMultisampleEnable() const { return _values.multisampleEnable; }
+
+ void setAntialiasedLineEnable(bool enable) { SET_FIELD(ANTIALISED_LINE_ENABLE, DEFAULT.antialisedLineEnable, enable, _values.antialisedLineEnable); }
+ bool isAntialiasedLineEnable() const { return _values.antialisedLineEnable; }
+
+ // Depth Bias
+ void setDepthBias(float bias) { SET_FIELD(DEPTH_BIAS, DEFAULT.depthBias, bias, _values.depthBias); }
+ float getDepthBias() const { return _values.depthBias; }
+
+ void setDepthBiasSlopeScale(float scale) { SET_FIELD(DEPTH_BIAS_SLOPE_SCALE, DEFAULT.depthBiasSlopeScale, scale, _values.depthBiasSlopeScale); }
+ float getDepthBiasSlopeScale() const { return _values.depthBiasSlopeScale; }
+
+ // Depth Test
+ void setDepthTest(DepthTest depthTest) { SET_FIELD(DEPTH_TEST, DEFAULT.depthTest, depthTest, _values.depthTest); }
+ void setDepthTest(bool enable, bool writeMask, ComparisonFunction func) { setDepthTest(DepthTest(enable, writeMask, func)); }
+ DepthTest getDepthTest() const { return _values.depthTest; }
+
+ bool isDepthTestEnabled() const { return getDepthTest().isEnabled(); }
+ uint8 getDepthTestWriteMask() const { return getDepthTest().getWriteMask(); }
+ ComparisonFunction getDepthTestFunc() const { return getDepthTest().getFunction(); }
+
+ // Stencil test
+ void setStencilTest(bool enabled, uint8 frontWriteMask, StencilTest frontTest, uint8 backWriteMask, StencilTest backTest) {
+ SET_FIELD(STENCIL_ACTIVATION, DEFAULT.stencilActivation, StencilActivation(enabled, frontWriteMask, backWriteMask), _values.stencilActivation);
+ SET_FIELD(STENCIL_TEST_FRONT, DEFAULT.stencilTestFront, frontTest, _values.stencilTestFront);
+ SET_FIELD(STENCIL_TEST_BACK, DEFAULT.stencilTestBack, backTest, _values.stencilTestBack); }
+ void setStencilTest(bool enabled, uint8 frontWriteMask, StencilTest frontTest) {
+ setStencilTest(enabled, frontWriteMask, frontTest, frontWriteMask, frontTest); }
+
+ StencilActivation getStencilActivation() const { return _values.stencilActivation; }
+ StencilTest getStencilTestFront() const { return _values.stencilTestFront; }
+ StencilTest getStencilTestBack() const { return _values.stencilTestBack; }
+
+ bool isStencilEnabled() const { return getStencilActivation().isEnabled(); }
+ uint8 getStencilWriteMaskFront() const { return getStencilActivation().getWriteMaskFront(); }
+ uint8 getStencilWriteMaskBack() const { return getStencilActivation().getWriteMaskBack(); }
+
+ // Alpha to coverage
+ void setAlphaToCoverageEnable(bool enable) { SET_FIELD(ALPHA_TO_COVERAGE_ENABLE, DEFAULT.alphaToCoverageEnable, enable, _values.alphaToCoverageEnable); }
+ bool isAlphaToCoverageEnabled() const { return _values.alphaToCoverageEnable; }
+
+ // Sample mask
+ void setSampleMask(uint32 mask) { SET_FIELD(SAMPLE_MASK, DEFAULT.sampleMask, mask, _values.sampleMask); }
+ uint32 getSampleMask() const { return _values.sampleMask; }
+
+ // Blend Function
+ void setBlendFunction(BlendFunction function) { SET_FIELD(BLEND_FUNCTION, DEFAULT.blendFunction, function, _values.blendFunction); }
+ BlendFunction getBlendFunction() const { return _values.blendFunction; }
+
+ void setBlendFunction(bool enabled, BlendArg sourceColor, BlendOp operationColor, BlendArg destinationColor, BlendArg sourceAlpha, BlendOp operationAlpha, BlendArg destinationAlpha) {
+ setBlendFunction(BlendFunction(enabled, sourceColor, operationColor, destinationColor, sourceAlpha, operationAlpha, destinationAlpha)); }
+ void setBlendFunction(bool enabled, BlendArg source, BlendOp operation, BlendArg destination) {
+ setBlendFunction(BlendFunction(enabled, source, operation, destination)); }
+
+ bool isBlendEnabled() const { return getBlendFunction().isEnabled(); }
+
+ // Color write mask
+ void setColorWriteMask(uint8 mask) { SET_FIELD(COLOR_WRITE_MASK, DEFAULT.colorWriteMask, mask, _values.colorWriteMask); }
+ void setColorWriteMask(bool red, bool green, bool blue, bool alpha) { uint32 value = ((WRITE_RED * red) | (WRITE_GREEN * green) | (WRITE_BLUE * blue) | (WRITE_ALPHA * alpha)); SET_FIELD(COLOR_WRITE_MASK, DEFAULT.colorWriteMask, value, _values.colorWriteMask); }
+ uint8 getColorWriteMask() const { return _values.colorWriteMask; }
+
+ // All the possible fields
+ enum Field {
+ FILL_MODE,
+ CULL_MODE,
+ FRONT_FACE_CLOCKWISE,
+ DEPTH_CLAMP_ENABLE,
+ SCISSOR_ENABLE,
+ MULTISAMPLE_ENABLE,
+ ANTIALISED_LINE_ENABLE,
+
+ DEPTH_BIAS,
+ DEPTH_BIAS_SLOPE_SCALE,
+
+ DEPTH_TEST,
+
+ STENCIL_ACTIVATION,
+ STENCIL_TEST_FRONT,
+ STENCIL_TEST_BACK,
+
+ SAMPLE_MASK,
+ ALPHA_TO_COVERAGE_ENABLE,
+
+ BLEND_FUNCTION,
+
+ COLOR_WRITE_MASK,
+
+ NUM_FIELDS, // not a valid field, just the count
+ };
+
+ // The signature of the state tells which fields of the state are not default
+ // this way during rendering the Backend can compare it's current state and try to minimize the job to do
+ typedef std::bitset Signature;
+
+ Signature getSignature() const { return _signature; }
+
+ static Signature evalSignature(const Data& state);
+
+ // For convenience, create a State from the values directly
+ State(const Data& values);
+ const Data& getValues() const { return _values; }
+
+protected:
+ State(const State& state);
+ State& operator=(const State& state);
+
+ Data _values;
+ Signature _signature{0};
+ Stamp _stamp{0};
+
+ // This shouldn't be used by anything else than the Backend class with the proper casting.
+ mutable GPUObject* _gpuObject = nullptr;
+ void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; }
+ GPUObject* getGPUObject() const { return _gpuObject; }
+ friend class Backend;
+};
+
+typedef std::shared_ptr< State > StatePointer;
+typedef std::vector< StatePointer > States;
+
+};
+
+#endif
diff --git a/libraries/physics/src/ObjectActionSpring.cpp b/libraries/physics/src/ObjectActionSpring.cpp
index 196cc8d3ea..313b089d1c 100644
--- a/libraries/physics/src/ObjectActionSpring.cpp
+++ b/libraries/physics/src/ObjectActionSpring.cpp
@@ -92,7 +92,7 @@ void ObjectActionSpring::updateActionWorker(btScalar deltaTimeStep) {
// dQ = Q1 * Q0^
btQuaternion deltaQ = target * bodyRotation.inverse();
float angle = deltaQ.getAngle();
- const float MIN_ANGLE = 1.0e-4;
+ const float MIN_ANGLE = 1.0e-4f;
if (angle > MIN_ANGLE) {
targetVelocity = (angle / _angularTimeScale) * deltaQ.getAxis();
}
diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp
index 3080d965a1..7452c32ed2 100644
--- a/libraries/render-utils/src/Model.cpp
+++ b/libraries/render-utils/src/Model.cpp
@@ -112,14 +112,14 @@ void Model::RenderPipelineLib::addRenderPipeline(Model::RenderKey key,
gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vertexShader, pixelShader));
gpu::Shader::makeProgram(*program, slotBindings);
-
-
+
+
auto locations = std::make_shared();
initLocations(program, *locations);
-
+
auto state = std::make_shared();
-
+
// Backface on shadow
if (key.isShadow()) {
state->setCullMode(gpu::State::CULL_FRONT);
@@ -140,36 +140,36 @@ void Model::RenderPipelineLib::addRenderPipeline(Model::RenderKey key,
// Good to go add the brand new pipeline
auto pipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state));
insert(value_type(key.getRaw(), RenderPipeline(pipeline, locations)));
-
-
+
+
if (!key.isWireFrame()) {
-
+
RenderKey wireframeKey(key.getRaw() | RenderKey::IS_WIREFRAME);
auto wireframeState = std::make_shared(state->getValues());
-
+
wireframeState->setFillMode(gpu::State::FILL_LINE);
-
+
// create a new RenderPipeline with the same shader side and the mirrorState
auto wireframePipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, wireframeState));
insert(value_type(wireframeKey.getRaw(), RenderPipeline(wireframePipeline, locations)));
}
-
+
// If not a shadow pass, create the mirror version from the same state, just change the FrontFace
if (!key.isShadow()) {
-
+
RenderKey mirrorKey(key.getRaw() | RenderKey::IS_MIRROR);
auto mirrorState = std::make_shared(state->getValues());
// create a new RenderPipeline with the same shader side and the mirrorState
auto mirrorPipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, mirrorState));
insert(value_type(mirrorKey.getRaw(), RenderPipeline(mirrorPipeline, locations)));
-
+
if (!key.isWireFrame()) {
RenderKey wireframeKey(key.getRaw() | RenderKey::IS_MIRROR | RenderKey::IS_WIREFRAME);
auto wireframeState = std::make_shared(state->getValues());
-
+
wireframeState->setFillMode(gpu::State::FILL_LINE);
-
+
// create a new RenderPipeline with the same shader side and the mirrorState
auto wireframePipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, wireframeState));
insert(value_type(wireframeKey.getRaw(), RenderPipeline(wireframePipeline, locations)));
@@ -214,12 +214,12 @@ void Model::setScaleInternal(const glm::vec3& scale) {
}
}
-void Model::setOffset(const glm::vec3& offset) {
- _offset = offset;
-
+void Model::setOffset(const glm::vec3& offset) {
+ _offset = offset;
+
// if someone manually sets our offset, then we are no longer snapped to center
- _snapModelToRegistrationPoint = false;
- _snappedToRegistrationPoint = false;
+ _snapModelToRegistrationPoint = false;
+ _snappedToRegistrationPoint = false;
}
QVector Model::createJointStates(const FBXGeometry& geometry) {
@@ -267,7 +267,7 @@ void Model::init() {
auto modelLightmapNormalSpecularMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_lightmap_normal_specular_map_frag)));
// Fill the renderPipelineLib
-
+
_renderPipelineLib.addRenderPipeline(
RenderKey(0),
modelVertex, modelPixel);
@@ -284,7 +284,7 @@ void Model::init() {
RenderKey(RenderKey::HAS_TANGENTS | RenderKey::HAS_SPECULAR),
modelNormalMapVertex, modelNormalSpecularMapPixel);
-
+
_renderPipelineLib.addRenderPipeline(
RenderKey(RenderKey::IS_TRANSLUCENT),
modelVertex, modelTranslucentPixel);
@@ -292,7 +292,7 @@ void Model::init() {
_renderPipelineLib.addRenderPipeline(
RenderKey(RenderKey::IS_TRANSLUCENT | RenderKey::HAS_LIGHTMAP),
modelVertex, modelTranslucentPixel);
-
+
_renderPipelineLib.addRenderPipeline(
RenderKey(RenderKey::HAS_TANGENTS | RenderKey::IS_TRANSLUCENT),
modelNormalMapVertex, modelTranslucentPixel);
@@ -440,15 +440,15 @@ bool Model::updateGeometry() {
}
_geometry->setLoadPriority(this, -_lodDistance);
_geometry->ensureLoading();
-
+
if (needToRebuild) {
const FBXGeometry& fbxGeometry = geometry->getFBXGeometry();
foreach (const FBXMesh& mesh, fbxGeometry.meshes) {
MeshState state;
state.clusterMatrices.resize(mesh.clusters.size());
state.cauterizedClusterMatrices.resize(mesh.clusters.size());
- _meshStates.append(state);
-
+ _meshStates.append(state);
+
auto buffer = std::make_shared();
if (!mesh.blendshapes.isEmpty()) {
buffer->resize((mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3));
@@ -486,7 +486,7 @@ void Model::initJointStates(QVector states) {
rightShoulderJointIndex);
}
-bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance,
+bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance,
BoxFace& face, QString& extraInfo, bool pickAgainstTriangles) {
bool intersectedSomething = false;
@@ -495,7 +495,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
if (!isActive()) {
return intersectedSomething;
}
-
+
// extents is the entity relative, scaled, centered extents of the entity
glm::vec3 position = _translation;
glm::mat4 rotation = glm::mat4_cast(_rotation);
@@ -504,7 +504,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
glm::mat4 worldToModelMatrix = glm::inverse(modelToWorldMatrix);
Extents modelExtents = getMeshExtents(); // NOTE: unrotated
-
+
glm::vec3 dimensions = modelExtents.maximum - modelExtents.minimum;
glm::vec3 corner = -(dimensions * _registrationPoint); // since we're going to do the ray picking in the model frame of reference
AABox modelFrameBox(corner, dimensions);
@@ -543,7 +543,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
int t = 0;
foreach (const Triangle& triangle, meshTriangles) {
t++;
-
+
float thisTriangleDistance;
if (findRayTriangleIntersection(origin, direction, triangle, thisTriangleDistance)) {
if (thisTriangleDistance < bestDistance) {
@@ -562,7 +562,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
extraInfo = geometry.getModelNameOfMesh(subMeshIndex);
}
}
- }
+ }
subMeshIndex++;
}
_mutex.unlock();
@@ -570,7 +570,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
if (intersectedSomething) {
distance = bestDistance;
}
-
+
return intersectedSomething;
}
@@ -582,22 +582,22 @@ bool Model::convexHullContains(glm::vec3 point) {
if (!isActive()) {
return false;
}
-
+
// extents is the entity relative, scaled, centered extents of the entity
glm::vec3 position = _translation;
glm::mat4 rotation = glm::mat4_cast(_rotation);
glm::mat4 translation = glm::translate(position);
glm::mat4 modelToWorldMatrix = translation * rotation;
glm::mat4 worldToModelMatrix = glm::inverse(modelToWorldMatrix);
-
+
Extents modelExtents = getMeshExtents(); // NOTE: unrotated
-
+
glm::vec3 dimensions = modelExtents.maximum - modelExtents.minimum;
glm::vec3 corner = -(dimensions * _registrationPoint);
AABox modelFrameBox(corner, dimensions);
-
+
glm::vec3 modelFramePoint = glm::vec3(worldToModelMatrix * glm::vec4(point, 1.0f));
-
+
// we can use the AABox's contains() by mapping our point into the model frame
// and testing there.
if (modelFrameBox.contains(modelFramePoint)){
@@ -605,7 +605,7 @@ bool Model::convexHullContains(glm::vec3 point) {
if (!_calculatedMeshTrianglesValid) {
recalculateMeshBoxes(true);
}
-
+
// If we are inside the models box, then consider the submeshes...
int subMeshIndex = 0;
foreach(const AABox& subMeshBox, _calculatedMeshBoxes) {
@@ -619,7 +619,7 @@ bool Model::convexHullContains(glm::vec3 point) {
insideMesh = false;
break;
}
-
+
}
if (insideMesh) {
// It's inside this mesh, return true.
@@ -658,7 +658,7 @@ void Model::recalculateMeshPartOffsets() {
// Any script might trigger findRayIntersectionAgainstSubMeshes (and maybe convexHullContains), so these
// can occur multiple times. In addition, rendering does it's own ray picking in order to decide which
// entity-scripts to call. I think it would be best to do the picking once-per-frame (in cpu, or gpu if possible)
-// and then the calls use the most recent such result.
+// and then the calls use the most recent such result.
void Model::recalculateMeshBoxes(bool pickAgainstTriangles) {
PROFILE_RANGE(__FUNCTION__);
bool calculatedMeshTrianglesNeeded = pickAgainstTriangles && !_calculatedMeshTrianglesValid;
@@ -703,7 +703,7 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) {
glm::vec3 mv1 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i1], 1.0f));
glm::vec3 mv2 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i2], 1.0f));
glm::vec3 mv3 = glm::vec3(mesh.modelTransform * glm::vec4(mesh.vertices[i3], 1.0f));
-
+
// track the mesh parts in model space
if (!atLeastOnePointInBounds) {
thisPartBounds.setBox(mv0, 0.0f);
@@ -719,18 +719,18 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) {
glm::vec3 v1 = calculateScaledOffsetPoint(mv1);
glm::vec3 v2 = calculateScaledOffsetPoint(mv2);
glm::vec3 v3 = calculateScaledOffsetPoint(mv3);
-
+
// Sam's recommended triangle slices
Triangle tri1 = { v0, v1, v3 };
Triangle tri2 = { v1, v2, v3 };
-
+
// NOTE: Random guy on the internet's recommended triangle slices
//Triangle tri1 = { v0, v1, v2 };
//Triangle tri2 = { v2, v3, v0 };
-
+
thisMeshTriangles.push_back(tri1);
thisMeshTriangles.push_back(tri2);
-
+
}
}
@@ -792,7 +792,7 @@ void Model::renderSetup(RenderArgs* args) {
_dilatedTextures.append(dilated);
}
}
-
+
if (!_meshGroupsKnown && isLoaded()) {
segregateMeshGroups();
}
@@ -805,7 +805,7 @@ public:
transparent(transparent), model(model), url(model->getURL()), meshIndex(meshIndex), partIndex(partIndex) { }
typedef render::Payload Payload;
typedef Payload::DataPointer Pointer;
-
+
bool transparent;
Model* model;
QUrl url;
@@ -814,14 +814,14 @@ public:
};
namespace render {
- template <> const ItemKey payloadGetKey(const MeshPartPayload::Pointer& payload) {
+ template <> const ItemKey payloadGetKey(const MeshPartPayload::Pointer& payload) {
if (!payload->model->isVisible()) {
return ItemKey::Builder().withInvisible().build();
}
return payload->transparent ? ItemKey::Builder::transparentShape() : ItemKey::Builder::opaqueShape();
}
-
- template <> const Item::Bound payloadGetBound(const MeshPartPayload::Pointer& payload) {
+
+ template <> const Item::Bound payloadGetBound(const MeshPartPayload::Pointer& payload) {
if (payload) {
return payload->model->getPartBounds(payload->meshIndex, payload->partIndex);
}
@@ -875,7 +875,7 @@ bool Model::addToScene(std::shared_ptr scene, render::PendingChan
_renderItems.insert(item, renderPayload);
somethingAdded = true;
}
-
+
_readyWhenAdded = readyToAddToScene();
return somethingAdded;
@@ -907,7 +907,7 @@ bool Model::addToScene(std::shared_ptr scene, render::PendingChan
_renderItems.insert(item, renderPayload);
somethingAdded = true;
}
-
+
_readyWhenAdded = readyToAddToScene();
return somethingAdded;
@@ -929,7 +929,7 @@ void Model::renderDebugMeshBoxes(gpu::Batch& batch) {
_debugMeshBoxesID = DependencyManager::get()->allocateID();
}
QVector points;
-
+
glm::vec3 brn = box.getCorner();
glm::vec3 bln = brn + glm::vec3(box.getDimensions().x, 0, 0);
glm::vec3 brf = brn + glm::vec3(0, 0, box.getDimensions().z);
@@ -963,12 +963,12 @@ void Model::renderDebugMeshBoxes(gpu::Batch& batch) {
{ 1.0f, 1.0f, 0.0f, 1.0f }, // yellow
{ 0.0f, 1.0f, 1.0f, 1.0f }, // cyan
{ 1.0f, 1.0f, 1.0f, 1.0f }, // white
- { 0.0f, 0.5f, 0.0f, 1.0f },
- { 0.0f, 0.0f, 0.5f, 1.0f },
- { 0.5f, 0.0f, 0.5f, 1.0f },
- { 0.5f, 0.5f, 0.0f, 1.0f },
+ { 0.0f, 0.5f, 0.0f, 1.0f },
+ { 0.0f, 0.0f, 0.5f, 1.0f },
+ { 0.5f, 0.0f, 0.5f, 1.0f },
+ { 0.5f, 0.5f, 0.0f, 1.0f },
{ 0.0f, 0.5f, 0.5f, 1.0f } };
-
+
DependencyManager::get()->updateVertices(_debugMeshBoxesID, points, color[colorNdx]);
DependencyManager::get()->renderVertices(batch, gpu::LINES, _debugMeshBoxesID);
colorNdx++;
@@ -1003,7 +1003,7 @@ Extents Model::getUnscaledMeshExtents() const {
if (!isActive()) {
return Extents();
}
-
+
const Extents& extents = _geometry->getFBXGeometry().meshExtents;
// even though our caller asked for "unscaled" we need to include any fst scaling, translation, and rotation, which
@@ -1011,7 +1011,7 @@ Extents Model::getUnscaledMeshExtents() const {
glm::vec3 minimum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f));
glm::vec3 maximum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f));
Extents scaledExtents = { minimum, maximum };
-
+
return scaledExtents;
}
@@ -1020,12 +1020,12 @@ Extents Model::calculateScaledOffsetExtents(const Extents& extents) const {
glm::vec3 minimum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.minimum, 1.0f));
glm::vec3 maximum = glm::vec3(_geometry->getFBXGeometry().offset * glm::vec4(extents.maximum, 1.0f));
- Extents scaledOffsetExtents = { ((minimum + _offset) * _scale),
+ Extents scaledOffsetExtents = { ((minimum + _offset) * _scale),
((maximum + _offset) * _scale) };
Extents rotatedExtents = scaledOffsetExtents.getRotated(_rotation);
- Extents translatedExtents = { rotatedExtents.minimum + _translation,
+ Extents translatedExtents = { rotatedExtents.minimum + _translation,
rotatedExtents.maximum + _translation };
return translatedExtents;
@@ -1084,7 +1084,7 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo
onInvalidate();
- // if so instructed, keep the current geometry until the new one is loaded
+ // if so instructed, keep the current geometry until the new one is loaded
_nextGeometry = DependencyManager::get()->getGeometry(url, fallback, delayLoad);
_nextLODHysteresis = NetworkGeometry::NO_HYSTERESIS;
if (!retainCurrent || !isActive() || (_nextGeometry && _nextGeometry->isLoaded())) {
@@ -1094,14 +1094,14 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo
void Model::geometryRefreshed() {
QObject* sender = QObject::sender();
-
+
if (sender == _geometry) {
_readyWhenAdded = false; // reset out render items.
_needsReload = true;
invalidCalculatedMeshBoxes();
-
+
onInvalidate();
-
+
// if so instructed, keep the current geometry until the new one is loaded
_nextGeometry = DependencyManager::get()->getGeometry(_url);
_nextLODHysteresis = NetworkGeometry::NO_HYSTERESIS;
@@ -1121,7 +1121,7 @@ const QSharedPointer Model::getCollisionGeometry(bool delayLoad
if (_collisionGeometry && _collisionGeometry->isLoaded()) {
return _collisionGeometry;
}
-
+
return QSharedPointer();
}
@@ -1176,11 +1176,11 @@ public:
Blender(Model* model, int blendNumber, const QWeakPointer& geometry,
const QVector& meshes, const QVector& blendshapeCoefficients);
-
+
virtual void run();
private:
-
+
QPointer _model;
int _blendNumber;
QWeakPointer _geometry;
@@ -1254,10 +1254,10 @@ void Model::setScaleToFit(bool scaleToFit, float largestDimension, bool forceRes
}
return;
}
-
+
if (forceRescale || _scaleToFit != scaleToFit || glm::length(_scaleToFitDimensions) != largestDimension) {
_scaleToFit = scaleToFit;
-
+
// we only need to do this work if we're "turning on" scale to fit.
if (scaleToFit) {
Extents modelMeshExtents = getUnscaledMeshExtents();
@@ -1278,7 +1278,7 @@ void Model::scaleToFit() {
// we didn't yet have an active mesh. We can only enter this scaleToFit() in this state
// if we now do have an active mesh, so we take this opportunity to actually determine
// the correct scale.
- if (_scaleToFit && _scaleToFitDimensions.y == FAKE_DIMENSION_PLACEHOLDER
+ if (_scaleToFit && _scaleToFitDimensions.y == FAKE_DIMENSION_PLACEHOLDER
&& _scaleToFitDimensions.z == FAKE_DIMENSION_PLACEHOLDER) {
setScaleToFit(_scaleToFit, _scaleToFitDimensions.x);
}
@@ -1313,7 +1313,7 @@ void Model::simulate(float deltaTime, bool fullUpdate) {
PROFILE_RANGE(__FUNCTION__);
fullUpdate = updateGeometry() || fullUpdate || (_scaleToFit && !_scaledToFit)
|| (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint);
-
+
if (isActive() && fullUpdate) {
// NOTE: This is overly aggressive and we are invalidating the MeshBoxes when in fact they may not be invalid
// they really only become invalid if something about the transform to world space has changed. This is
@@ -1440,7 +1440,7 @@ void Model::setBlendedVertices(int blendNumber, const QWeakPointergetFBXGeometry();
+ const FBXGeometry& fbxGeometry = _geometry->getFBXGeometry();
int index = 0;
for (int i = 0; i < fbxGeometry.meshes.size(); i++) {
const FBXMesh& mesh = fbxGeometry.meshes.at(i);
@@ -1461,7 +1461,7 @@ void Model::setGeometry(const QSharedPointer& newGeometry) {
if (_geometry == newGeometry) {
return;
}
-
+
if (_geometry) {
_geometry->disconnect(_geometry.data(), &Resource::onRefresh, this, &Model::geometryRefreshed);
}
@@ -1474,10 +1474,10 @@ void Model::applyNextGeometry() {
deleteGeometry();
_dilatedTextures.clear();
_lodHysteresis = _nextLODHysteresis;
-
+
// we retain a reference to the base geometry so that its reference count doesn't fall to zero
setGeometry(_nextGeometry);
-
+
_meshGroupsKnown = false;
_readyWhenAdded = false; // in case any of our users are using scenes
_needsReload = false; // we are loaded now!
@@ -1509,9 +1509,9 @@ AABox Model::getPartBounds(int meshIndex, int partIndex) {
return calculateScaledOffsetAABox(_geometry->getFBXGeometry().meshExtents);
}
}
-
+
if (_geometry->getFBXGeometry().meshes.size() > meshIndex) {
-
+
// FIX ME! - This is currently a hack because for some mesh parts our efforts to calculate the bounding
// box of the mesh part fails. It seems to create boxes that are not consistent with where the
// geometry actually renders. If instead we make all the parts share the bounds of the entire subMesh
@@ -1536,7 +1536,7 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
if (!_readyWhenAdded) {
return; // bail asap
}
-
+
// We need to make sure we have valid offsets calculated before we can render
if (!_calculatedMeshPartOffsetValid) {
_mutex.lock();
@@ -1561,13 +1561,13 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
// guard against partially loaded meshes
if (meshIndex >= networkMeshes.size() || meshIndex >= geometry.meshes.size() || meshIndex >= _meshStates.size() ) {
- return;
+ return;
}
const NetworkMesh& networkMesh = networkMeshes.at(meshIndex);
const FBXMesh& mesh = geometry.meshes.at(meshIndex);
const MeshState& state = _meshStates.at(meshIndex);
-
+
bool translucentMesh = translucent; // networkMesh.getTranslucentPartCount(mesh) == networkMesh.parts.size();
bool hasTangents = !mesh.tangents.isEmpty();
bool hasSpecular = mesh.hasSpecularTexture();
@@ -1597,7 +1597,7 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
DependencyManager::get()->renderWireCube(batch, 1.0f, cubeColor);
}
#endif //def DEBUG_BOUNDING_PARTS
-
+
if (wireframe) {
translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false;
}
@@ -1614,14 +1614,14 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
// if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown
// to false to rebuild out mesh groups.
-
+
if (meshIndex < 0 || meshIndex >= networkMeshes.size() || meshIndex > geometry.meshes.size()) {
_meshGroupsKnown = false; // regenerate these lists next time around.
_readyWhenAdded = false; // in case any of our users are using scenes
invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid
return; // FIXME!
}
-
+
batch.setIndexBuffer(gpu::UINT32, (networkMesh._indexBuffer), 0);
int vertexCount = mesh.vertices.size();
if (vertexCount == 0) {
@@ -1633,7 +1633,7 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
if (_transforms.empty()) {
_transforms.push_back(Transform());
}
-
+
if (isSkinned) {
const float* bones;
if (_cauterizeBones) {
@@ -1682,7 +1682,7 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
qCDebug(renderutils) << "WARNING: material == nullptr!!!";
}
#endif
-
+
if (material != nullptr) {
// apply material properties
@@ -1724,12 +1724,12 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
batch._glUniformMatrix4fv(locations->texcoordMatrices, 2, false, (const float*) &texcoordTransform);
}
- if (!mesh.tangents.isEmpty()) {
+ if (!mesh.tangents.isEmpty()) {
NetworkTexture* normalMap = networkPart.normalTexture.data();
batch.setResourceTexture(1, (!normalMap || !normalMap->isLoaded()) ?
textureCache->getBlueTexture() : normalMap->getGPUTexture());
}
-
+
if (locations->specularTextureUnit >= 0) {
NetworkTexture* specularMap = networkPart.specularTexture.data();
batch.setResourceTexture(locations->specularTextureUnit, (!specularMap || !specularMap->isLoaded()) ?
@@ -1747,18 +1747,18 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
float emissiveOffset = part.emissiveParams.x;
float emissiveScale = part.emissiveParams.y;
batch._glUniform2f(locations->emissiveParams, emissiveOffset, emissiveScale);
-
+
NetworkTexture* emissiveMap = networkPart.emissiveTexture.data();
batch.setResourceTexture(locations->emissiveTextureUnit, (!emissiveMap || !emissiveMap->isLoaded()) ?
textureCache->getGrayTexture() : emissiveMap->getGPUTexture());
}
-
+
if (translucent && locations->lightBufferUnit >= 0) {
DependencyManager::get()->setupTransparent(args, locations->lightBufferUnit);
}
}
}
-
+
qint64 offset;
{
// FIXME_STUTTER: We should n't have any lock here
@@ -1798,7 +1798,7 @@ void Model::segregateMeshGroups() {
qDebug() << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet.";
return;
}
-
+
_transparentRenderItems.clear();
_opaqueRenderItems.clear();
@@ -1807,7 +1807,6 @@ void Model::segregateMeshGroups() {
const NetworkMesh& networkMesh = networkMeshes.at(i);
const FBXMesh& mesh = geometry.meshes.at(i);
const MeshState& state = _meshStates.at(i);
-
bool translucentMesh = networkMesh.getTranslucentPartCount(mesh) == networkMesh.parts.size();
bool hasTangents = !mesh.tangents.isEmpty();
@@ -1815,7 +1814,7 @@ void Model::segregateMeshGroups() {
bool hasLightmap = mesh.hasEmissiveTexture();
bool isSkinned = state.clusterMatrices.size() > 1;
bool wireframe = isWireframe();
-
+
if (wireframe) {
translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false;
}
@@ -1831,7 +1830,7 @@ void Model::segregateMeshGroups() {
}
}
_meshGroupsKnown = true;
-}
+}
void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, float alphaThreshold,
bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args,
@@ -1851,7 +1850,7 @@ void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, f
gpu::ShaderPointer program = (*pipeline).second._pipeline->getProgram();
locations = (*pipeline).second._locations.get();
-
+
// Setup the One pipeline
batch.setPipeline((*pipeline).second._pipeline);
@@ -1865,7 +1864,7 @@ void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, f
}
if ((locations->normalFittingMapUnit > -1)) {
- batch.setResourceTexture(locations->normalFittingMapUnit,
+ batch.setResourceTexture(locations->normalFittingMapUnit,
DependencyManager::get()->getNormalFittingTexture());
}
}
diff --git a/libraries/render-utils/src/OffscreenQmlSurface.cpp b/libraries/render-utils/src/OffscreenQmlSurface.cpp
index 0dfd95a725..9923849aab 100644
--- a/libraries/render-utils/src/OffscreenQmlSurface.cpp
+++ b/libraries/render-utils/src/OffscreenQmlSurface.cpp
@@ -239,7 +239,7 @@ private:
return;
}
- Q_ASSERT(toGlm(_quickWindow->geometry().size()) == _size);
+ //Q_ASSERT(toGlm(_quickWindow->geometry().size()) == _size);
//Q_ASSERT(toGlm(_quickWindow->geometry().size()) == _textures._size);
_renderControl->sync();
diff --git a/libraries/shared/src/PhysicsCollisionGroups.h b/libraries/shared/src/PhysicsCollisionGroups.h
index 08d83a29ca..42599a1b28 100644
--- a/libraries/shared/src/PhysicsCollisionGroups.h
+++ b/libraries/shared/src/PhysicsCollisionGroups.h
@@ -44,7 +44,7 @@ const int16_t COLLISION_GROUP_OTHER_AVATAR = 1 << 6;
const int16_t COLLISION_GROUP_MY_ATTACHMENT = 1 << 7;
const int16_t COLLISION_GROUP_OTHER_ATTACHMENT = 1 << 8;
// ...
-const int16_t COLLISION_GROUP_COLLISIONLESS = 1 << 15;
+const int16_t COLLISION_GROUP_COLLISIONLESS = 1 << 14;
/* Note: In order for objectA to collide with objectB at the filter stage