mirror of
https://github.com/overte-org/overte.git
synced 2025-04-22 08:53:28 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into centerOrigin
This commit is contained in:
commit
684ee3bdd0
42 changed files with 1134 additions and 1790 deletions
assignment-client/src/octree
domain-server/resources
examples
interface
libraries
avatars/src
entities-renderer/src
entities/src
EntityItemProperties.hEntityScriptingInterface.cppEntityScriptingInterface.hPolyLineEntityItem.cppPolyVoxEntityItem.cppPolyVoxEntityItem.h
fbx/src
gpu/src/gpu
physics/src
render-utils/src
shared/src
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1072,6 +1072,7 @@
|
|||
<option value='0'>marching cubes</option>
|
||||
<option value='1'>cubic</option>
|
||||
<option value='2'>edged cubic</option>
|
||||
<option value='3'>edged marching cubes</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<float> 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());
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include <ViewFrustum.h>
|
||||
#include <plugins/PluginContainer.h>
|
||||
#include <plugins/PluginManager.h>
|
||||
#include <SimpleMovingAverage.h>
|
||||
|
||||
#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
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
#include <NodeList.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <udt/PacketHeaders.h>
|
||||
#include <PathUtils.h>
|
||||
#include <PerfStat.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <TextRenderer3D.h>
|
||||
|
@ -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>& attachmentData) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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> 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>& 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;
|
||||
}
|
||||
|
|
|
@ -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>& 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<int> _headBoneSet;
|
||||
RigPointer _rig;
|
||||
bool _prevShouldDrawHead;
|
||||
};
|
||||
|
||||
#endif // hifi_MyAvatar_h
|
||||
|
|
|
@ -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 <QFileDialog>
|
||||
#include <QFont>
|
||||
|
||||
#include <AudioClient.h>
|
||||
#include <avatar/AvatarManager.h>
|
||||
#include <devices/Faceshift.h>
|
||||
#include <NetworkingConstants.h>
|
||||
|
||||
#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<AvatarManager>()->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<AvatarManager>()->getMyAvatar()->useHeadAndBodyURLs(headURL, bodyURL);
|
||||
setUseFullAvatar(!checked);
|
||||
}
|
||||
|
||||
void AvatarAppearanceDialog::useFullAvatar(bool checked) {
|
||||
QUrl fullAvatarURL(ui.fullAvatarURLEdit->text());
|
||||
DependencyManager::get<AvatarManager>()->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<PreferencesDialog> prefs = DependencyManager::get<DialogsManager>()->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<PreferencesDialog> prefs = DependencyManager::get<DialogsManager>()->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<AvatarManager>()->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<AvatarManager>()->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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 <QDialog>
|
||||
#include <QString>
|
||||
|
||||
#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
|
|
@ -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);
|
||||
|
|
|
@ -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> _octreeStatsDialog;
|
||||
QPointer<PreferencesDialog> _preferencesDialog;
|
||||
QPointer<ScriptEditorWindow> _scriptEditor;
|
||||
QPointer<AvatarAppearanceDialog> _avatarAppearanceDialog;
|
||||
QPointer<DomainConnectionDialog> _domainConnectionDialog;
|
||||
QPointer<UpdateDialog> _updateDialog;
|
||||
};
|
||||
|
|
|
@ -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<DialogsManager>().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<AvatarManager>()->getMyAvatar()->getModelDescription());
|
||||
|
||||
UIUtil::scaleWidgetFontSizes(this);
|
||||
}
|
||||
|
||||
void PreferencesDialog::avatarDescriptionChanged() {
|
||||
ui.apperanceDescription->setText(DependencyManager::get<AvatarManager>()->getMyAvatar()->getModelDescription());
|
||||
}
|
||||
|
||||
void PreferencesDialog::headURLChanged(const QString& newValue, const QString& modelName) {
|
||||
ui.apperanceDescription->setText(DependencyManager::get<AvatarManager>()->getMyAvatar()->getModelDescription());
|
||||
}
|
||||
|
||||
void PreferencesDialog::bodyURLChanged(const QString& newValue, const QString& modelName) {
|
||||
ui.apperanceDescription->setText(DependencyManager::get<AvatarManager>()->getMyAvatar()->getModelDescription());
|
||||
}
|
||||
|
||||
void PreferencesDialog::fullAvatarURLChanged(const QString& newValue, const QString& modelName) {
|
||||
ui.apperanceDescription->setText(DependencyManager::get<AvatarManager>()->getMyAvatar()->getModelDescription());
|
||||
ui.appearanceDescription->setText(newValue);
|
||||
const QString APPEARANCE_LABEL_TEXT("Appearance: ");
|
||||
ui.appearanceLabel->setText(APPEARANCE_LABEL_TEXT + modelName);
|
||||
DependencyManager::get<AvatarManager>()->getMyAvatar()->useFullAvatarURL(newValue, modelName);
|
||||
}
|
||||
|
||||
void PreferencesDialog::accept() {
|
||||
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->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);
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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<BandwidthRecorder>();
|
||||
STAT_UPDATE(packetInCount, bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond());
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -1,471 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AvatarAppearanceDialog</class>
|
||||
<widget class="QDialog" name="AvatarAppearanceDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>500</width>
|
||||
<height>350</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>500</width>
|
||||
<height>350</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>500</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>13</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>-107</y>
|
||||
<width>485</width>
|
||||
<height>1550</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>30</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>30</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
|
||||
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>7</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>7</number>
|
||||
</property>
|
||||
|
||||
|
||||
|
||||
<item>
|
||||
<widget class="QRadioButton" name="useFullAvatar">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use single avatar with Body and Head</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
|
||||
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_fullAvatar">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
|
||||
|
||||
<item>
|
||||
<widget class="QLabel" name="fullAvatarNameLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Full Avatar</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>fullAvatarURLEdit</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_fullAvatar">
|
||||
|
||||
<item>
|
||||
<widget class="QLineEdit" name="fullAvatarURLEdit">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_fullAvatar">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>5</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonBrowseFullAvatar">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Browse</string>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
|
||||
|
||||
</layout>
|
||||
</item>
|
||||
|
||||
|
||||
|
||||
</layout>
|
||||
|
||||
|
||||
|
||||
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<widget class="QRadioButton" name="useSeparateBodyAndHead">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use separate Body and Head avatar files</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_separateParts_head">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>7</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>7</number>
|
||||
</property>
|
||||
|
||||
|
||||
|
||||
<item>
|
||||
<widget class="QLabel" name="headNameLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Head</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||
</property>
|
||||
</widget>
|
||||
|
||||
</item>
|
||||
|
||||
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="faceURLEdit"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>5</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonBrowseHead">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Browse</string>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
|
||||
|
||||
</layout>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_separateParts_body">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>7</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>7</number>
|
||||
</property>
|
||||
|
||||
<item>
|
||||
<widget class="QLabel" name="bodyNameLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Body</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>skeletonURLEdit</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_skeletonURL">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="skeletonURLEdit">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_10">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>5</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonBrowseBody">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Browse</string>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
|
||||
|
||||
|
||||
|
||||
</layout>
|
||||
</item>
|
||||
|
||||
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="buttonsPanel">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_12">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="buttonsHBox_2">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_14">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="defaultButton">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
</ui>
|
|
@ -275,7 +275,7 @@
|
|||
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>apperanceDescription</cstring>
|
||||
<cstring>appearanceDescription</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -283,26 +283,21 @@
|
|||
|
||||
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_1_apperanceDescription">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_1_appearanceDescription">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="apperanceDescription">
|
||||
<widget class="QLineEdit" name="appearanceDescription">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
</font>
|
||||
</property>
|
||||
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_1_apperanceDescription">
|
||||
<spacer name="horizontalSpacer_1_appearanceDescription">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
|
@ -318,7 +313,7 @@
|
|||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonChangeApperance">
|
||||
<widget class="QPushButton" name="buttonChangeAppearance">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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&);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <math.h>
|
||||
#include <QByteArray>
|
||||
|
||||
#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<RenderablePolyVoxEntityItem>(entityID, properties);
|
||||
|
@ -66,7 +68,7 @@ RenderablePolyVoxEntityItem::~RenderablePolyVoxEntityItem() {
|
|||
}
|
||||
|
||||
bool inUserBounds(const PolyVox::SimpleVolume<uint8_t>* 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<uint8_t>* 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<unsigned char>::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<glm::vec3> 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<const uint32_t> it = indexBufferView.cbegin<uint32_t>();
|
||||
while (it != indexBufferView.cend<uint32_t>()) {
|
||||
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<const glm::vec3>(p0Index);
|
||||
const glm::vec3& p1 = vertexBufferView.get<const glm::vec3>(p1Index);
|
||||
const glm::vec3& p2 = vertexBufferView.get<const glm::vec3>(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<glm::vec3> newMeshPoints;
|
||||
_points << newMeshPoints;
|
||||
// add points to the new convex hull
|
||||
_points[i++] << pointsInPart;
|
||||
QVector<glm::vec3> pointsInPart;
|
||||
pointsInPart << p0Model;
|
||||
pointsInPart << p1Model;
|
||||
pointsInPart << p2Model;
|
||||
pointsInPart << p3Model;
|
||||
// add next convex hull
|
||||
QVector<glm::vec3> 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<glm::vec3> 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<glm::vec3> 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<PolyVox::PositionMaterialNormal> polyVoxMesh;
|
||||
|
||||
switch (_voxelSurfaceStyle) {
|
||||
case PolyVoxEntityItem::SURFACE_EDGED_MARCHING_CUBES:
|
||||
case PolyVoxEntityItem::SURFACE_MARCHING_CUBES: {
|
||||
PolyVox::MarchingCubesSurfaceExtractor<PolyVox::SimpleVolume<uint8_t>> 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<TextureCache>()->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));
|
||||
}
|
||||
|
|
|
@ -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<uint8_t>* _volData = nullptr;
|
||||
model::Geometry _modelGeometry;
|
||||
|
|
|
@ -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<glm::vec3>);
|
||||
DEFINE_PROPERTY(PROP_STROKE_WIDTHS, StrokeWidths, strokeWidths, QVector<float>);
|
||||
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);
|
||||
|
|
|
@ -415,7 +415,7 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra
|
|||
}
|
||||
|
||||
bool EntityScriptingInterface::setVoxels(QUuid entityID,
|
||||
std::function<void(PolyVoxEntityItem&)> actor) {
|
||||
std::function<bool(PolyVoxEntityItem&)> actor) {
|
||||
if (!_entityTree) {
|
||||
return false;
|
||||
}
|
||||
|
@ -435,7 +435,7 @@ bool EntityScriptingInterface::setVoxels(QUuid entityID,
|
|||
|
||||
auto polyVoxEntity = std::dynamic_pointer_cast<PolyVoxEntityItem>(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<bool(LineEntityItem&)> actor) {
|
||||
if (!_entityTree) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
EntityItemPointer entity = static_cast<EntityItemPointer>(_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<LineEntityItem>(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::function<bool(Line
|
|||
|
||||
bool EntityScriptingInterface::setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value) {
|
||||
return setVoxels(entityID, [center, radius, value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
polyVoxEntity.setSphere(center, radius, value);
|
||||
return polyVoxEntity.setSphere(center, radius, value);
|
||||
});
|
||||
}
|
||||
|
||||
bool EntityScriptingInterface::setVoxel(QUuid entityID, const glm::vec3& position, int value) {
|
||||
return setVoxels(entityID, [position, value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
polyVoxEntity.setVoxelInVolume(position, value);
|
||||
return polyVoxEntity.setVoxelInVolume(position, value);
|
||||
});
|
||||
}
|
||||
|
||||
bool EntityScriptingInterface::setAllVoxels(QUuid entityID, int value) {
|
||||
return setVoxels(entityID, [value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
polyVoxEntity.setAll(value);
|
||||
return polyVoxEntity.setAll(value);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -512,16 +511,16 @@ bool EntityScriptingInterface::setAllPoints(QUuid entityID, const QVector<glm::v
|
|||
if (!entity) {
|
||||
qCDebug(entities) << "EntityScriptingInterface::setPoints no entity with ID" << entityID;
|
||||
}
|
||||
|
||||
|
||||
EntityTypes::EntityType entityType = entity->getType();
|
||||
|
||||
|
||||
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<PolyVoxEntityItem>(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<PolyVoxEntityItem>(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<PolyVoxEntityItem>(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<PolyVoxEntityItem>(entity);
|
||||
return polyVoxEntity->localCoordsToVoxelCoords(localCoords);
|
||||
}
|
||||
|
|
|
@ -134,6 +134,11 @@ public slots:
|
|||
Q_INVOKABLE QVector<QUuid> 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<bool(EntitySimulation*, EntityItemPointer)> actor);
|
||||
bool setVoxels(QUuid entityID, std::function<void(PolyVoxEntityItem&)> actor);
|
||||
bool setVoxels(QUuid entityID, std::function<bool(PolyVoxEntityItem&)> actor);
|
||||
bool setPoints(QUuid entityID, std::function<bool(LineEntityItem&)> actor);
|
||||
void queueEntityMessage(PacketType::Value packetType, EntityItemID entityID, const EntityItemProperties& properties);
|
||||
|
||||
|
|
|
@ -170,9 +170,10 @@ bool PolyLineEntityItem::setLinePoints(const QVector<glm::vec3>& 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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 <memory>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <bitset>
|
||||
|
||||
// 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<const int32*>(this)); }
|
||||
DepthTest(int32 raw) { *(reinterpret_cast<int32*>(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<const int32*>(this)); }
|
||||
StencilTest(int32 raw) { *(reinterpret_cast<int32*>(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<const int32*>(this)); }
|
||||
StencilActivation(int32 raw) { *(reinterpret_cast<int32*>(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<const int32*>(this)); }
|
||||
BlendFunction(int32 raw) { *(reinterpret_cast<int32*>(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<NUM_FIELDS> 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 <memory>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <bitset>
|
||||
|
||||
// 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<const int32*>(this)); }
|
||||
DepthTest(int32 raw) { *(reinterpret_cast<int32*>(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<const int32*>(this)); }
|
||||
StencilTest(int32 raw) { *(reinterpret_cast<int32*>(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<const int32*>(this)); }
|
||||
StencilActivation(int32 raw) { *(reinterpret_cast<int32*>(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<const int32*>(this)); }
|
||||
BlendFunction(int32 raw) { *(reinterpret_cast<int32*>(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<NUM_FIELDS> 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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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<Locations>();
|
||||
initLocations(program, *locations);
|
||||
|
||||
|
||||
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
|
||||
|
||||
// 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<gpu::State>(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<gpu::State>(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<gpu::State>(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<JointState> 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<gpu::Buffer>();
|
||||
if (!mesh.blendshapes.isEmpty()) {
|
||||
buffer->resize((mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3));
|
||||
|
@ -486,7 +486,7 @@ void Model::initJointStates(QVector<JointState> 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<MeshPartPayload> 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<render::Scene> scene, render::PendingChan
|
|||
_renderItems.insert(item, renderPayload);
|
||||
somethingAdded = true;
|
||||
}
|
||||
|
||||
|
||||
_readyWhenAdded = readyToAddToScene();
|
||||
|
||||
return somethingAdded;
|
||||
|
@ -907,7 +907,7 @@ bool Model::addToScene(std::shared_ptr<render::Scene> 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<GeometryCache>()->allocateID();
|
||||
}
|
||||
QVector<glm::vec3> 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<GeometryCache>()->updateVertices(_debugMeshBoxesID, points, color[colorNdx]);
|
||||
DependencyManager::get<GeometryCache>()->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<GeometryCache>()->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<GeometryCache>()->getGeometry(_url);
|
||||
_nextLODHysteresis = NetworkGeometry::NO_HYSTERESIS;
|
||||
|
@ -1121,7 +1121,7 @@ const QSharedPointer<NetworkGeometry> Model::getCollisionGeometry(bool delayLoad
|
|||
if (_collisionGeometry && _collisionGeometry->isLoaded()) {
|
||||
return _collisionGeometry;
|
||||
}
|
||||
|
||||
|
||||
return QSharedPointer<NetworkGeometry>();
|
||||
}
|
||||
|
||||
|
@ -1176,11 +1176,11 @@ public:
|
|||
|
||||
Blender(Model* model, int blendNumber, const QWeakPointer<NetworkGeometry>& geometry,
|
||||
const QVector<FBXMesh>& meshes, const QVector<float>& blendshapeCoefficients);
|
||||
|
||||
|
||||
virtual void run();
|
||||
|
||||
private:
|
||||
|
||||
|
||||
QPointer<Model> _model;
|
||||
int _blendNumber;
|
||||
QWeakPointer<NetworkGeometry> _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 QWeakPointer<NetworkGeomet
|
|||
return;
|
||||
}
|
||||
_appliedBlendNumber = blendNumber;
|
||||
const FBXGeometry& fbxGeometry = _geometry->getFBXGeometry();
|
||||
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<NetworkGeometry>& 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<DeferredLightingEffect>()->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<DeferredLightingEffect>()->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<TextureCache>()->getNormalFittingTexture());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue