mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-06-18 01:28:45 +02:00
Merge remote-tracking branch 'upstream/master' into moreshelfstuff
This commit is contained in:
commit
e87cb2bd54
53 changed files with 803 additions and 332 deletions
|
@ -339,21 +339,18 @@ bool AudioMixer::prepareMixForListeningNode(Node* node) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
int nonZeroSamples = 0;
|
// use the per listner AudioLimiter to render the mixed data...
|
||||||
|
listenerNodeData->audioLimiter.render(_mixedSamples, _clampedSamples, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
|
||||||
|
|
||||||
// enumerate the mixed samples and clamp any samples outside the min/max
|
// check for silent audio after the peak limitor has converted the samples
|
||||||
// also check if we ended up with a silent frame
|
bool hasAudio = false;
|
||||||
for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; ++i) {
|
for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; ++i) {
|
||||||
|
if (_clampedSamples[i] != 0) {
|
||||||
_clampedSamples[i] = int16_t(glm::clamp(int(_mixedSamples[i] * AudioConstants::MAX_SAMPLE_VALUE),
|
hasAudio = true;
|
||||||
AudioConstants::MIN_SAMPLE_VALUE,
|
break;
|
||||||
AudioConstants::MAX_SAMPLE_VALUE));
|
|
||||||
if (_clampedSamples[i] != 0.0f) {
|
|
||||||
++nonZeroSamples;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return hasAudio;
|
||||||
return (nonZeroSamples > 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) {
|
void AudioMixer::sendAudioEnvironmentPacket(SharedNodePointer node) {
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
AudioMixerClientData::AudioMixerClientData(const QUuid& nodeID) :
|
AudioMixerClientData::AudioMixerClientData(const QUuid& nodeID) :
|
||||||
NodeData(nodeID),
|
NodeData(nodeID),
|
||||||
|
audioLimiter(AudioConstants::SAMPLE_RATE, AudioConstants::STEREO),
|
||||||
_outgoingMixedAudioSequenceNumber(0),
|
_outgoingMixedAudioSequenceNumber(0),
|
||||||
_downstreamAudioStreamStats()
|
_downstreamAudioStreamStats()
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,11 +16,13 @@
|
||||||
|
|
||||||
#include <AABox.h>
|
#include <AABox.h>
|
||||||
#include <AudioHRTF.h>
|
#include <AudioHRTF.h>
|
||||||
|
#include <AudioLimiter.h>
|
||||||
#include <UUIDHasher.h>
|
#include <UUIDHasher.h>
|
||||||
|
|
||||||
#include "PositionalAudioStream.h"
|
#include "PositionalAudioStream.h"
|
||||||
#include "AvatarAudioStream.h"
|
#include "AvatarAudioStream.h"
|
||||||
|
|
||||||
|
|
||||||
class AudioMixerClientData : public NodeData {
|
class AudioMixerClientData : public NodeData {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
@ -61,6 +63,8 @@ public:
|
||||||
// uses randomization to have the AudioMixer send a stats packet to this node around every second
|
// uses randomization to have the AudioMixer send a stats packet to this node around every second
|
||||||
bool shouldSendStats(int frameNumber);
|
bool shouldSendStats(int frameNumber);
|
||||||
|
|
||||||
|
AudioLimiter audioLimiter;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void injectorStreamFinished(const QUuid& streamIdentifier);
|
void injectorStreamFinished(const QUuid& streamIdentifier);
|
||||||
|
|
||||||
|
|
|
@ -111,7 +111,7 @@
|
||||||
{
|
{
|
||||||
"name": "maximum_user_capacity",
|
"name": "maximum_user_capacity",
|
||||||
"label": "Maximum User Capacity",
|
"label": "Maximum User Capacity",
|
||||||
"help": "The limit on how many avatars can be connected at once. 0 means no limit.",
|
"help": "The limit on how many users can be connected at once (0 means no limit). Avatars connected from the same machine will not count towards this limit.",
|
||||||
"placeholder": "0",
|
"placeholder": "0",
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"advanced": false
|
"advanced": false
|
||||||
|
|
|
@ -715,10 +715,14 @@ void DomainServer::processListRequestPacket(QSharedPointer<ReceivedMessage> mess
|
||||||
unsigned int DomainServer::countConnectedUsers() {
|
unsigned int DomainServer::countConnectedUsers() {
|
||||||
unsigned int result = 0;
|
unsigned int result = 0;
|
||||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
nodeList->eachNode([&](const SharedNodePointer& otherNode){
|
nodeList->eachNode([&](const SharedNodePointer& node){
|
||||||
if (otherNode->getType() == NodeType::Agent) {
|
// only count unassigned agents (i.e., users)
|
||||||
|
if (node->getType() == NodeType::Agent) {
|
||||||
|
auto nodeData = static_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||||
|
if (nodeData && !nodeData->wasAssigned()) {
|
||||||
result++;
|
result++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,10 +26,10 @@ import "fileDialog"
|
||||||
ModalWindow {
|
ModalWindow {
|
||||||
id: root
|
id: root
|
||||||
resizable: true
|
resizable: true
|
||||||
implicitWidth: 640
|
implicitWidth: 480
|
||||||
implicitHeight: 480
|
implicitHeight: 360
|
||||||
|
|
||||||
minSize: Qt.vector2d(300, 240)
|
minSize: Qt.vector2d(360, 240)
|
||||||
draggable: true
|
draggable: true
|
||||||
|
|
||||||
HifiConstants { id: hifi }
|
HifiConstants { id: hifi }
|
||||||
|
@ -79,6 +79,9 @@ ModalWindow {
|
||||||
fileTableModel.folder = initialFolder;
|
fileTableModel.folder = initialFolder;
|
||||||
|
|
||||||
iconText = root.title !== "" ? hifi.glyphs.scriptUpload : "";
|
iconText = root.title !== "" ? hifi.glyphs.scriptUpload : "";
|
||||||
|
|
||||||
|
// Clear selection when click on external frame.
|
||||||
|
frameClicked.connect(function() { d.clearSelection(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
@ -87,6 +90,13 @@ ModalWindow {
|
||||||
height: pane.height
|
height: pane.height
|
||||||
anchors.margins: 0
|
anchors.margins: 0
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
// Clear selection when click on internal unused area.
|
||||||
|
anchors.fill: parent
|
||||||
|
drag.target: root
|
||||||
|
onClicked: d.clearSelection()
|
||||||
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: navControls
|
id: navControls
|
||||||
anchors {
|
anchors {
|
||||||
|
@ -202,6 +212,8 @@ ModalWindow {
|
||||||
function update() {
|
function update() {
|
||||||
var row = fileTableView.currentRow;
|
var row = fileTableView.currentRow;
|
||||||
|
|
||||||
|
openButton.text = root.selectDirectory && row === -1 ? "Choose" : "Open"
|
||||||
|
|
||||||
if (row === -1) {
|
if (row === -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -226,6 +238,12 @@ ModalWindow {
|
||||||
fileTableModel.folder = homeDestination;
|
fileTableModel.folder = homeDestination;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clearSelection() {
|
||||||
|
fileTableView.selection.clear();
|
||||||
|
fileTableView.currentRow = -1;
|
||||||
|
update();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FolderListModel {
|
FolderListModel {
|
||||||
|
@ -389,6 +407,8 @@ ModalWindow {
|
||||||
|
|
||||||
rows++;
|
rows++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
d.clearSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,8 +653,15 @@ ModalWindow {
|
||||||
Action {
|
Action {
|
||||||
id: okAction
|
id: okAction
|
||||||
text: root.saveDialog ? "Save" : (root.selectDirectory ? "Choose" : "Open")
|
text: root.saveDialog ? "Save" : (root.selectDirectory ? "Choose" : "Open")
|
||||||
enabled: currentSelection.text ? true : false
|
enabled: currentSelection.text || !root.selectDirectory && d.currentSelectionIsFolder ? true : false
|
||||||
onTriggered: okActionTimer.start();
|
onTriggered: {
|
||||||
|
if (!root.selectDirectory && !d.currentSelectionIsFolder
|
||||||
|
|| root.selectDirectory && fileTableView.currentRow === -1) {
|
||||||
|
okActionTimer.start();
|
||||||
|
} else {
|
||||||
|
fileTableView.navigateToCurrentRow();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
|
|
|
@ -27,6 +27,8 @@ Frame {
|
||||||
readonly property int frameMarginTop: hifi.dimensions.modalDialogMargin.y + (frameContent.hasTitle ? hifi.dimensions.modalDialogTitleHeight + 10 : 0)
|
readonly property int frameMarginTop: hifi.dimensions.modalDialogMargin.y + (frameContent.hasTitle ? hifi.dimensions.modalDialogTitleHeight + 10 : 0)
|
||||||
readonly property int frameMarginBottom: hifi.dimensions.modalDialogMargin.y
|
readonly property int frameMarginBottom: hifi.dimensions.modalDialogMargin.y
|
||||||
|
|
||||||
|
signal frameClicked();
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
fill: parent
|
fill: parent
|
||||||
topMargin: -frameMarginTop
|
topMargin: -frameMarginTop
|
||||||
|
@ -47,6 +49,7 @@ Frame {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
drag.target: window
|
drag.target: window
|
||||||
enabled: window.draggable
|
enabled: window.draggable
|
||||||
|
onClicked: window.frameClicked();
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
|
|
@ -22,5 +22,7 @@ Window {
|
||||||
property int colorScheme: hifi.colorSchemes.light
|
property int colorScheme: hifi.colorSchemes.light
|
||||||
property bool draggable: false
|
property bool draggable: false
|
||||||
|
|
||||||
|
signal frameClicked();
|
||||||
|
|
||||||
anchors.centerIn: draggable ? undefined : parent
|
anchors.centerIn: draggable ? undefined : parent
|
||||||
}
|
}
|
||||||
|
|
|
@ -994,7 +994,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
||||||
if (_keyboardFocusedItem != entityItemID) {
|
if (_keyboardFocusedItem != entityItemID) {
|
||||||
_keyboardFocusedItem = UNKNOWN_ENTITY_ID;
|
_keyboardFocusedItem = UNKNOWN_ENTITY_ID;
|
||||||
auto properties = entityScriptingInterface->getEntityProperties(entityItemID);
|
auto properties = entityScriptingInterface->getEntityProperties(entityItemID);
|
||||||
if (EntityTypes::Web == properties.getType() && !properties.getLocked()) {
|
if (EntityTypes::Web == properties.getType() && !properties.getLocked() && properties.getVisible()) {
|
||||||
auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(entityItemID);
|
auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(entityItemID);
|
||||||
RenderableWebEntityItem* webEntity = dynamic_cast<RenderableWebEntityItem*>(entity.get());
|
RenderableWebEntityItem* webEntity = dynamic_cast<RenderableWebEntityItem*>(entity.get());
|
||||||
if (webEntity) {
|
if (webEntity) {
|
||||||
|
@ -1049,6 +1049,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(this, &Application::aboutToQuit, [=]() {
|
||||||
|
_keyboardFocusedItem = UNKNOWN_ENTITY_ID;
|
||||||
|
if (_keyboardFocusHighlight) {
|
||||||
|
_keyboardFocusHighlight->setVisible(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Make sure we don't time out during slow operations at startup
|
// Make sure we don't time out during slow operations at startup
|
||||||
updateHeartbeat();
|
updateHeartbeat();
|
||||||
|
|
||||||
|
@ -1503,7 +1510,13 @@ void Application::paintGL() {
|
||||||
// FIXME not needed anymore?
|
// FIXME not needed anymore?
|
||||||
_offscreenContext->makeCurrent();
|
_offscreenContext->makeCurrent();
|
||||||
|
|
||||||
displayPlugin->beginFrameRender(_frameCount);
|
// If a display plugin loses it's underlying support, it
|
||||||
|
// needs to be able to signal us to not use it
|
||||||
|
if (!displayPlugin->beginFrameRender(_frameCount)) {
|
||||||
|
_inPaint = false;
|
||||||
|
updateDisplayMode();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// update the avatar with a fresh HMD pose
|
// update the avatar with a fresh HMD pose
|
||||||
getMyAvatar()->updateFromHMDSensorMatrix(getHMDSensorPose());
|
getMyAvatar()->updateFromHMDSensorMatrix(getHMDSensorPose());
|
||||||
|
@ -5098,9 +5111,17 @@ void Application::updateDisplayMode() {
|
||||||
|
|
||||||
foreach(auto displayPlugin, standard) {
|
foreach(auto displayPlugin, standard) {
|
||||||
addDisplayPluginToMenu(displayPlugin, first);
|
addDisplayPluginToMenu(displayPlugin, first);
|
||||||
|
auto displayPluginName = displayPlugin->getName();
|
||||||
QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged, [this](const QSize & size) {
|
QObject::connect(displayPlugin.get(), &DisplayPlugin::recommendedFramebufferSizeChanged, [this](const QSize & size) {
|
||||||
resizeGL();
|
resizeGL();
|
||||||
});
|
});
|
||||||
|
QObject::connect(displayPlugin.get(), &DisplayPlugin::outputDeviceLost, [this, displayPluginName] {
|
||||||
|
PluginManager::getInstance()->disableDisplayPlugin(displayPluginName);
|
||||||
|
auto menu = Menu::getInstance();
|
||||||
|
if (menu->menuItemExists(MenuOption::OutputMenu, displayPluginName)) {
|
||||||
|
menu->removeMenuItem(MenuOption::OutputMenu, displayPluginName);
|
||||||
|
}
|
||||||
|
});
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5116,6 +5137,10 @@ void Application::updateDisplayMode() {
|
||||||
foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) {
|
foreach(DisplayPluginPointer displayPlugin, PluginManager::getInstance()->getDisplayPlugins()) {
|
||||||
QString name = displayPlugin->getName();
|
QString name = displayPlugin->getName();
|
||||||
QAction* action = menu->getActionForOption(name);
|
QAction* action = menu->getActionForOption(name);
|
||||||
|
// Menu might have been removed if the display plugin lost
|
||||||
|
if (!action) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (action->isChecked()) {
|
if (action->isChecked()) {
|
||||||
newDisplayPlugin = displayPlugin;
|
newDisplayPlugin = displayPlugin;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -322,7 +322,7 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents
|
||||||
const auto characterController = myAvatar->getCharacterController();
|
const auto characterController = myAvatar->getCharacterController();
|
||||||
const float avatarVelocityChange = (characterController ? glm::length(characterController->getVelocityChange()) : 0.0f);
|
const float avatarVelocityChange = (characterController ? glm::length(characterController->getVelocityChange()) : 0.0f);
|
||||||
const float velocityChange = glm::length(collision.velocityChange) + avatarVelocityChange;
|
const float velocityChange = glm::length(collision.velocityChange) + avatarVelocityChange;
|
||||||
const float MIN_AVATAR_COLLISION_ACCELERATION = 0.01f;
|
const float MIN_AVATAR_COLLISION_ACCELERATION = 2.4f; // walking speed
|
||||||
const bool isSound = (collision.type == CONTACT_EVENT_TYPE_START) && (velocityChange > MIN_AVATAR_COLLISION_ACCELERATION);
|
const bool isSound = (collision.type == CONTACT_EVENT_TYPE_START) && (velocityChange > MIN_AVATAR_COLLISION_ACCELERATION);
|
||||||
|
|
||||||
if (!isSound) {
|
if (!isSound) {
|
||||||
|
@ -330,14 +330,24 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents
|
||||||
}
|
}
|
||||||
// Your avatar sound is personal to you, so let's say the "mass" part of the kinetic energy is already accounted for.
|
// Your avatar sound is personal to you, so let's say the "mass" part of the kinetic energy is already accounted for.
|
||||||
const float energy = velocityChange * velocityChange;
|
const float energy = velocityChange * velocityChange;
|
||||||
const float COLLISION_ENERGY_AT_FULL_VOLUME = 0.5f;
|
const float COLLISION_ENERGY_AT_FULL_VOLUME = 10.0f;
|
||||||
const float energyFactorOfFull = fmin(1.0f, energy / COLLISION_ENERGY_AT_FULL_VOLUME);
|
const float energyFactorOfFull = fmin(1.0f, energy / COLLISION_ENERGY_AT_FULL_VOLUME);
|
||||||
|
|
||||||
// For general entity collisionSoundURL, playSound supports changing the pitch for the sound based on the size of the object,
|
// For general entity collisionSoundURL, playSound supports changing the pitch for the sound based on the size of the object,
|
||||||
// but most avatars are roughly the same size, so let's not be so fancy yet.
|
// but most avatars are roughly the same size, so let's not be so fancy yet.
|
||||||
const float AVATAR_STRETCH_FACTOR = 1.0f;
|
const float AVATAR_STRETCH_FACTOR = 1.0f;
|
||||||
|
|
||||||
AudioInjector::playSound(collisionSound, energyFactorOfFull, AVATAR_STRETCH_FACTOR, myAvatar->getPosition());
|
|
||||||
|
_collisionInjectors.remove_if([](QPointer<AudioInjector>& injector) {
|
||||||
|
return !injector || injector->isFinished();
|
||||||
|
});
|
||||||
|
|
||||||
|
static const int MAX_INJECTOR_COUNT = 3;
|
||||||
|
if (_collisionInjectors.size() < MAX_INJECTOR_COUNT) {
|
||||||
|
auto injector = AudioInjector::playSound(collisionSound, energyFactorOfFull, AVATAR_STRETCH_FACTOR,
|
||||||
|
myAvatar->getPosition());
|
||||||
|
_collisionInjectors.emplace_back(injector);
|
||||||
|
}
|
||||||
myAvatar->collisionWithEntity(collision);
|
myAvatar->collisionWithEntity(collision);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "AvatarMotionState.h"
|
#include "AvatarMotionState.h"
|
||||||
|
|
||||||
class MyAvatar;
|
class MyAvatar;
|
||||||
|
class AudioInjector;
|
||||||
|
|
||||||
class AvatarManager : public AvatarHashMap {
|
class AvatarManager : public AvatarHashMap {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -94,6 +95,8 @@ private:
|
||||||
|
|
||||||
bool _shouldShowReceiveStats = false;
|
bool _shouldShowReceiveStats = false;
|
||||||
|
|
||||||
|
std::list<QPointer<AudioInjector>> _collisionInjectors;
|
||||||
|
|
||||||
SetOfAvatarMotionStates _motionStatesThatMightUpdate;
|
SetOfAvatarMotionStates _motionStatesThatMightUpdate;
|
||||||
SetOfMotionStates _motionStatesToAddToPhysics;
|
SetOfMotionStates _motionStatesToAddToPhysics;
|
||||||
VectorOfMotionStates _motionStatesToRemoveFromPhysics;
|
VectorOfMotionStates _motionStatesToRemoveFromPhysics;
|
||||||
|
|
|
@ -18,6 +18,9 @@
|
||||||
|
|
||||||
namespace AudioConstants {
|
namespace AudioConstants {
|
||||||
const int SAMPLE_RATE = 24000;
|
const int SAMPLE_RATE = 24000;
|
||||||
|
const int MONO = 1;
|
||||||
|
const int STEREO = 2;
|
||||||
|
|
||||||
|
|
||||||
typedef int16_t AudioSample;
|
typedef int16_t AudioSample;
|
||||||
|
|
||||||
|
|
|
@ -25,24 +25,43 @@ layout(location = 2) out vec4 _fragColor2;
|
||||||
|
|
||||||
// the alpha threshold
|
// the alpha threshold
|
||||||
uniform float alphaThreshold;
|
uniform float alphaThreshold;
|
||||||
uniform sampler2D normalFittingMap;
|
|
||||||
|
|
||||||
vec3 bestFitNormal(vec3 normal) {
|
vec2 signNotZero(vec2 v) {
|
||||||
vec3 absNorm = abs(normal);
|
return vec2((v.x >= 0.0) ? +1.0 : -1.0, (v.y >= 0.0) ? +1.0 : -1.0);
|
||||||
float maxNAbs = max(absNorm.z, max(absNorm.x, absNorm.y));
|
}
|
||||||
|
|
||||||
vec2 texcoord = (absNorm.z < maxNAbs ?
|
vec2 float32x3_to_oct(in vec3 v) {
|
||||||
(absNorm.y < maxNAbs ? absNorm.yz : absNorm.xz) :
|
vec2 p = v.xy * (1.0 / (abs(v.x) + abs(v.y) + abs(v.z)));
|
||||||
absNorm.xy);
|
return ((v.z <= 0.0) ? ((1.0 - abs(p.yx)) * signNotZero(p)) : p);
|
||||||
texcoord = (texcoord.x < texcoord.y ? texcoord.yx : texcoord.xy);
|
|
||||||
texcoord.y /= texcoord.x;
|
|
||||||
vec3 cN = normal / maxNAbs;
|
|
||||||
float fittingScale = texture(normalFittingMap, texcoord).a;
|
|
||||||
cN *= fittingScale;
|
|
||||||
return (cN * 0.5 + 0.5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
vec3 oct_to_float32x3(in vec2 e) {
|
||||||
|
vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y));
|
||||||
|
if (v.z < 0) {
|
||||||
|
v.xy = (1.0 - abs(v.yx)) * signNotZero(v.xy);
|
||||||
|
}
|
||||||
|
return normalize(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 snorm12x2_to_unorm8x3(vec2 f) {
|
||||||
|
vec2 u = vec2(round(clamp(f, -1.0, 1.0) * 2047.0 + 2047.0));
|
||||||
|
float t = floor(u.y / 256.0);
|
||||||
|
|
||||||
|
return floor(vec3(
|
||||||
|
u.x / 16.0,
|
||||||
|
fract(u.x / 16.0) * 256.0 + t,
|
||||||
|
u.y - t * 256.0
|
||||||
|
)) / 255.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 unorm8x3_to_snorm12x2(vec3 u) {
|
||||||
|
u *= 255.0;
|
||||||
|
u.y *= (1.0 / 16.0);
|
||||||
|
vec2 s = vec2( u.x * 16.0 + floor(u.y),
|
||||||
|
fract(u.y) * (16.0 * 256.0) + u.z);
|
||||||
|
return clamp(s * (1.0 / 2047.0) - 1.0, vec2(-1.0), vec2(1.0));
|
||||||
|
}
|
||||||
|
|
||||||
float mod289(float x) {
|
float mod289(float x) {
|
||||||
return x - floor(x * (1.0 / 289.0)) * 289.0;
|
return x - floor(x * (1.0 / 289.0)) * 289.0;
|
||||||
|
@ -322,7 +341,7 @@ void main(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
vec4 diffuse = vec4(_color.rgb, alpha);
|
vec4 diffuse = vec4(_color.rgb, alpha);
|
||||||
vec4 normal = vec4(normalize(bestFitNormal(_normal)), 0.5);
|
vec4 normal = vec4(packNormal(normalize(_normal)), 0.5);
|
||||||
|
|
||||||
_fragColor0 = diffuse;
|
_fragColor0 = diffuse;
|
||||||
_fragColor1 = normal;
|
_fragColor1 = normal;
|
||||||
|
@ -355,7 +374,7 @@ void main(void) {
|
||||||
float emissiveAmount = getProceduralColors(diffuse, specular, shininess);
|
float emissiveAmount = getProceduralColors(diffuse, specular, shininess);
|
||||||
|
|
||||||
_fragColor0 = vec4(diffuse.rgb, 1.0);
|
_fragColor0 = vec4(diffuse.rgb, 1.0);
|
||||||
_fragColor1 = vec4(bestFitNormal(normalize(_normal.xyz)), 1.0 - (emissiveAmount / 2.0));
|
_fragColor1 = vec4(packNormal(normalize(_normal.xyz)), 1.0 - (emissiveAmount / 2.0));
|
||||||
_fragColor2 = vec4(specular, shininess / 128.0);
|
_fragColor2 = vec4(specular, shininess / 128.0);
|
||||||
}
|
}
|
||||||
)SCRIBE";
|
)SCRIBE";
|
||||||
|
|
|
@ -226,10 +226,15 @@ void RenderableWebEntityItem::setSourceUrl(const QString& value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderableWebEntityItem::setProxyWindow(QWindow* proxyWindow) {
|
void RenderableWebEntityItem::setProxyWindow(QWindow* proxyWindow) {
|
||||||
|
if (_webSurface) {
|
||||||
_webSurface->setProxyWindow(proxyWindow);
|
_webSurface->setProxyWindow(proxyWindow);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject* RenderableWebEntityItem::getEventHandler() {
|
QObject* RenderableWebEntityItem::getEventHandler() {
|
||||||
|
if (!_webSurface) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
return _webSurface->getEventHandler();
|
return _webSurface->getEventHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -687,15 +687,80 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{ // When we own the simulation we don't accept updates to the entity's transform/velocities
|
{ // When we own the simulation we don't accept updates to the entity's transform/velocities
|
||||||
// but since we're using macros below we have to temporarily modify overwriteLocalData.
|
// we also want to ignore any duplicate packets that have the same "recently updated" values
|
||||||
bool oldOverwrite = overwriteLocalData;
|
// as a packet we've already recieved. This is because we want multiple edits of the same
|
||||||
overwriteLocalData = overwriteLocalData && !weOwnSimulation;
|
// information to be idempotent, but if we applied new physics properties we'd resimulation
|
||||||
READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, updatePositionFromNetwork);
|
// with small differences in results.
|
||||||
READ_ENTITY_PROPERTY(PROP_ROTATION, glm::quat, updateRotationFromNetwork);
|
|
||||||
READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, updateVelocityFromNetwork);
|
// Because the regular streaming property "setters" only have access to the new value, we've
|
||||||
READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocityFromNetwork);
|
// made these lambdas that can access other details about the previous updates to suppress
|
||||||
READ_ENTITY_PROPERTY(PROP_ACCELERATION, glm::vec3, setAcceleration);
|
// any duplicates.
|
||||||
overwriteLocalData = oldOverwrite;
|
|
||||||
|
// Note: duplicate packets are expected and not wrong. They may be sent for any number of
|
||||||
|
// reasons and the contract is that the client handles them in an idempotent manner.
|
||||||
|
auto lastEdited = lastEditedFromBufferAdjusted;
|
||||||
|
auto customUpdatePositionFromNetwork = [this, lastEdited, overwriteLocalData, weOwnSimulation](glm::vec3 value){
|
||||||
|
bool simulationChanged = lastEdited > _lastUpdatedPositionTimestamp;
|
||||||
|
bool valueChanged = value != _lastUpdatedPositionValue;
|
||||||
|
bool shouldUpdate = overwriteLocalData && !weOwnSimulation && simulationChanged && valueChanged;
|
||||||
|
if (shouldUpdate) {
|
||||||
|
updatePositionFromNetwork(value);
|
||||||
|
_lastUpdatedPositionTimestamp = lastEdited;
|
||||||
|
_lastUpdatedPositionValue = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto customUpdateRotationFromNetwork = [this, lastEdited, overwriteLocalData, weOwnSimulation](glm::quat value){
|
||||||
|
bool simulationChanged = lastEdited > _lastUpdatedRotationTimestamp;
|
||||||
|
bool valueChanged = value != _lastUpdatedRotationValue;
|
||||||
|
bool shouldUpdate = overwriteLocalData && !weOwnSimulation && simulationChanged && valueChanged;
|
||||||
|
if (shouldUpdate) {
|
||||||
|
updateRotationFromNetwork(value);
|
||||||
|
_lastUpdatedRotationTimestamp = lastEdited;
|
||||||
|
_lastUpdatedRotationValue = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto customUpdateVelocityFromNetwork = [this, lastEdited, overwriteLocalData, weOwnSimulation](glm::vec3 value){
|
||||||
|
bool simulationChanged = lastEdited > _lastUpdatedVelocityTimestamp;
|
||||||
|
bool valueChanged = value != _lastUpdatedVelocityValue;
|
||||||
|
bool shouldUpdate = overwriteLocalData && !weOwnSimulation && simulationChanged && valueChanged;
|
||||||
|
if (shouldUpdate) {
|
||||||
|
updateVelocityFromNetwork(value);
|
||||||
|
_lastUpdatedVelocityTimestamp = lastEdited;
|
||||||
|
_lastUpdatedVelocityValue = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto customUpdateAngularVelocityFromNetwork = [this, lastEdited, overwriteLocalData, weOwnSimulation](glm::vec3 value){
|
||||||
|
bool simulationChanged = lastEdited > _lastUpdatedAngularVelocityTimestamp;
|
||||||
|
bool valueChanged = value != _lastUpdatedAngularVelocityValue;
|
||||||
|
bool shouldUpdate = overwriteLocalData && !weOwnSimulation && simulationChanged && valueChanged;
|
||||||
|
if (shouldUpdate) {
|
||||||
|
updateAngularVelocityFromNetwork(value);
|
||||||
|
_lastUpdatedAngularVelocityTimestamp = lastEdited;
|
||||||
|
_lastUpdatedAngularVelocityValue = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto customSetAcceleration = [this, lastEdited, overwriteLocalData, weOwnSimulation](glm::vec3 value){
|
||||||
|
bool simulationChanged = lastEdited > _lastUpdatedAccelerationTimestamp;
|
||||||
|
bool valueChanged = value != _lastUpdatedAccelerationValue;
|
||||||
|
bool shouldUpdate = overwriteLocalData && !weOwnSimulation && simulationChanged && valueChanged;
|
||||||
|
if (shouldUpdate) {
|
||||||
|
setAcceleration(value);
|
||||||
|
_lastUpdatedAccelerationTimestamp = lastEdited;
|
||||||
|
_lastUpdatedAccelerationValue = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, customUpdatePositionFromNetwork);
|
||||||
|
READ_ENTITY_PROPERTY(PROP_ROTATION, glm::quat, customUpdateRotationFromNetwork);
|
||||||
|
READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, customUpdateVelocityFromNetwork);
|
||||||
|
READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, customUpdateAngularVelocityFromNetwork);
|
||||||
|
READ_ENTITY_PROPERTY(PROP_ACCELERATION, glm::vec3, customSetAcceleration);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, updateDimensions);
|
READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, updateDimensions);
|
||||||
|
@ -922,14 +987,12 @@ void EntityItem::simulate(const quint64& now) {
|
||||||
qCDebug(entities) << " ********** EntityItem::simulate() .... SETTING _lastSimulated=" << _lastSimulated;
|
qCDebug(entities) << " ********** EntityItem::simulate() .... SETTING _lastSimulated=" << _lastSimulated;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!hasActions()) {
|
|
||||||
if (!stepKinematicMotion(timeElapsed)) {
|
if (!stepKinematicMotion(timeElapsed)) {
|
||||||
// this entity is no longer moving
|
// this entity is no longer moving
|
||||||
// flag it to transition from KINEMATIC to STATIC
|
// flag it to transition from KINEMATIC to STATIC
|
||||||
_dirtyFlags |= Simulation::DIRTY_MOTION_TYPE;
|
_dirtyFlags |= Simulation::DIRTY_MOTION_TYPE;
|
||||||
setAcceleration(Vectors::ZERO);
|
setAcceleration(Vectors::ZERO);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
_lastSimulated = now;
|
_lastSimulated = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -550,6 +550,22 @@ protected:
|
||||||
|
|
||||||
bool _clientOnly { false };
|
bool _clientOnly { false };
|
||||||
QUuid _owningAvatarID;
|
QUuid _owningAvatarID;
|
||||||
|
|
||||||
|
// physics related changes from the network to suppress any duplicates and make
|
||||||
|
// sure redundant applications are idempotent
|
||||||
|
glm::vec3 _lastUpdatedPositionValue;
|
||||||
|
glm::quat _lastUpdatedRotationValue;
|
||||||
|
glm::vec3 _lastUpdatedVelocityValue;
|
||||||
|
glm::vec3 _lastUpdatedAngularVelocityValue;
|
||||||
|
glm::vec3 _lastUpdatedAccelerationValue;
|
||||||
|
|
||||||
|
quint64 _lastUpdatedPositionTimestamp { 0 };
|
||||||
|
quint64 _lastUpdatedRotationTimestamp { 0 };
|
||||||
|
quint64 _lastUpdatedVelocityTimestamp { 0 };
|
||||||
|
quint64 _lastUpdatedAngularVelocityTimestamp { 0 };
|
||||||
|
quint64 _lastUpdatedAccelerationTimestamp { 0 };
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_EntityItem_h
|
#endif // hifi_EntityItem_h
|
||||||
|
|
|
@ -119,6 +119,15 @@ TransformObject getTransformObject() {
|
||||||
}
|
}
|
||||||
<@endfunc@>
|
<@endfunc@>
|
||||||
|
|
||||||
|
<@func transformModelToWorldDir(cameraTransform, objectTransform, modelDir, worldDir)@>
|
||||||
|
{ // transformModelToEyeDir
|
||||||
|
vec3 mr0 = <$objectTransform$>._modelInverse[0].xyz;
|
||||||
|
vec3 mr1 = <$objectTransform$>._modelInverse[1].xyz;
|
||||||
|
vec3 mr2 = <$objectTransform$>._modelInverse[2].xyz;
|
||||||
|
|
||||||
|
<$worldDir$> = vec3(dot(mr0, <$modelDir$>), dot(mr1, <$modelDir$>), dot(mr2, <$modelDir$>));
|
||||||
|
}
|
||||||
|
<@endfunc@>
|
||||||
|
|
||||||
<@func transformModelToEyeDir(cameraTransform, objectTransform, modelDir, eyeDir)@>
|
<@func transformModelToEyeDir(cameraTransform, objectTransform, modelDir, eyeDir)@>
|
||||||
{ // transformModelToEyeDir
|
{ // transformModelToEyeDir
|
||||||
|
|
|
@ -401,11 +401,10 @@ glm::vec3 CharacterController::getLinearVelocity() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 CharacterController::getVelocityChange() const {
|
glm::vec3 CharacterController::getVelocityChange() const {
|
||||||
glm::vec3 velocity(0.0f);
|
|
||||||
if (_rigidBody) {
|
if (_rigidBody) {
|
||||||
velocity = bulletToGLM(_rigidBody->getLinearVelocity());
|
return bulletToGLM(_velocityChange);
|
||||||
}
|
}
|
||||||
return velocity;
|
return glm::vec3(0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterController::clearMotors() {
|
void CharacterController::clearMotors() {
|
||||||
|
|
|
@ -137,7 +137,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// will query the underlying hmd api to compute the most recent head pose
|
// will query the underlying hmd api to compute the most recent head pose
|
||||||
virtual void beginFrameRender(uint32_t frameIndex) {}
|
virtual bool beginFrameRender(uint32_t frameIndex) { return true; }
|
||||||
|
|
||||||
// returns a copy of the most recent head pose, computed via updateHeadPose
|
// returns a copy of the most recent head pose, computed via updateHeadPose
|
||||||
virtual glm::mat4 getHeadPose() const {
|
virtual glm::mat4 getHeadPose() const {
|
||||||
|
@ -170,6 +170,10 @@ public:
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void recommendedFramebufferSizeChanged(const QSize & size);
|
void recommendedFramebufferSizeChanged(const QSize & size);
|
||||||
|
// Indicates that this display plugin is no longer valid for use.
|
||||||
|
// For instance if a user exits Oculus Home or Steam VR while
|
||||||
|
// using the corresponding plugin, that plugin should be disabled.
|
||||||
|
void outputDeviceLost();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void incrementPresentCount();
|
void incrementPresentCount();
|
||||||
|
|
|
@ -62,11 +62,10 @@ PluginManager::PluginManager() {
|
||||||
extern DisplayPluginList getDisplayPlugins();
|
extern DisplayPluginList getDisplayPlugins();
|
||||||
extern InputPluginList getInputPlugins();
|
extern InputPluginList getInputPlugins();
|
||||||
extern void saveInputPluginSettings(const InputPluginList& plugins);
|
extern void saveInputPluginSettings(const InputPluginList& plugins);
|
||||||
|
static DisplayPluginList displayPlugins;
|
||||||
|
|
||||||
const DisplayPluginList& PluginManager::getDisplayPlugins() {
|
const DisplayPluginList& PluginManager::getDisplayPlugins() {
|
||||||
static DisplayPluginList displayPlugins;
|
|
||||||
static std::once_flag once;
|
static std::once_flag once;
|
||||||
|
|
||||||
std::call_once(once, [&] {
|
std::call_once(once, [&] {
|
||||||
// Grab the built in plugins
|
// Grab the built in plugins
|
||||||
displayPlugins = ::getDisplayPlugins();
|
displayPlugins = ::getDisplayPlugins();
|
||||||
|
@ -90,6 +89,16 @@ const DisplayPluginList& PluginManager::getDisplayPlugins() {
|
||||||
return displayPlugins;
|
return displayPlugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PluginManager::disableDisplayPlugin(const QString& name) {
|
||||||
|
for (size_t i = 0; i < displayPlugins.size(); ++i) {
|
||||||
|
if (displayPlugins[i]->getName() == name) {
|
||||||
|
displayPlugins.erase(displayPlugins.begin() + i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const InputPluginList& PluginManager::getInputPlugins() {
|
const InputPluginList& PluginManager::getInputPlugins() {
|
||||||
static InputPluginList inputPlugins;
|
static InputPluginList inputPlugins;
|
||||||
static std::once_flag once;
|
static std::once_flag once;
|
||||||
|
|
|
@ -17,6 +17,7 @@ public:
|
||||||
PluginManager();
|
PluginManager();
|
||||||
|
|
||||||
const DisplayPluginList& getDisplayPlugins();
|
const DisplayPluginList& getDisplayPlugins();
|
||||||
|
void disableDisplayPlugin(const QString& name);
|
||||||
const InputPluginList& getInputPlugins();
|
const InputPluginList& getInputPlugins();
|
||||||
void saveSettings();
|
void saveSettings();
|
||||||
};
|
};
|
||||||
|
|
|
@ -51,4 +51,70 @@ float packUnlit() {
|
||||||
return FRAG_PACK_UNLIT;
|
return FRAG_PACK_UNLIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
vec2 signNotZero(vec2 v) {
|
||||||
|
return vec2((v.x >= 0.0) ? +1.0 : -1.0, (v.y >= 0.0) ? +1.0 : -1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 float32x3_to_oct(in vec3 v) {
|
||||||
|
vec2 p = v.xy * (1.0 / (abs(v.x) + abs(v.y) + abs(v.z)));
|
||||||
|
return ((v.z <= 0.0) ? ((1.0 - abs(p.yx)) * signNotZero(p)) : p);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
vec3 oct_to_float32x3(in vec2 e) {
|
||||||
|
vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y));
|
||||||
|
if (v.z < 0) {
|
||||||
|
v.xy = (1.0 - abs(v.yx)) * signNotZero(v.xy);
|
||||||
|
}
|
||||||
|
return normalize(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 snorm12x2_to_unorm8x3(vec2 f) {
|
||||||
|
vec2 u = vec2(round(clamp(f, -1.0, 1.0) * 2047.0 + 2047.0));
|
||||||
|
float t = floor(u.y / 256.0);
|
||||||
|
|
||||||
|
return floor(vec3(
|
||||||
|
u.x / 16.0,
|
||||||
|
fract(u.x / 16.0) * 256.0 + t,
|
||||||
|
u.y - t * 256.0
|
||||||
|
)) / 255.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 unorm8x3_to_snorm12x2(vec3 u) {
|
||||||
|
u *= 255.0;
|
||||||
|
u.y *= (1.0 / 16.0);
|
||||||
|
vec2 s = vec2( u.x * 16.0 + floor(u.y),
|
||||||
|
fract(u.y) * (16.0 * 256.0) + u.z);
|
||||||
|
return clamp(s * (1.0 / 2047.0) - 1.0, vec2(-1.0), vec2(1.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
uniform sampler2D normalFittingMap;
|
||||||
|
|
||||||
|
vec3 bestFitNormal(vec3 normal) {
|
||||||
|
vec3 absNorm = abs(normal);
|
||||||
|
float maxNAbs = max(absNorm.z, max(absNorm.x, absNorm.y));
|
||||||
|
|
||||||
|
vec2 texcoord = (absNorm.z < maxNAbs ?
|
||||||
|
(absNorm.y < maxNAbs ? absNorm.yz : absNorm.xz) :
|
||||||
|
absNorm.xy);
|
||||||
|
texcoord = (texcoord.x < texcoord.y ? texcoord.yx : texcoord.xy);
|
||||||
|
texcoord.y /= texcoord.x;
|
||||||
|
vec3 cN = normal / maxNAbs;
|
||||||
|
|
||||||
|
float fittingScale = texture(normalFittingMap, texcoord).a;
|
||||||
|
cN *= fittingScale;
|
||||||
|
|
||||||
|
return (cN * 0.5 + 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 packNormal(in vec3 n) {
|
||||||
|
return snorm12x2_to_unorm8x3(float32x3_to_oct(n));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 unpackNormal(in vec3 p) {
|
||||||
|
return oct_to_float32x3(unorm8x3_to_snorm12x2(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
<@endif@>
|
<@endif@>
|
||||||
|
|
|
@ -100,7 +100,7 @@ DeferredFragment unpackDeferredFragmentNoPosition(vec2 texcoord) {
|
||||||
frag.obscurance = texture(obscuranceMap, texcoord).x;
|
frag.obscurance = texture(obscuranceMap, texcoord).x;
|
||||||
|
|
||||||
// Unpack the normal from the map
|
// Unpack the normal from the map
|
||||||
frag.normal = normalize(frag.normalVal.xyz * 2.0 - vec3(1.0));
|
frag.normal = unpackNormal(frag.normalVal.xyz);
|
||||||
frag.roughness = frag.normalVal.a;
|
frag.roughness = frag.normalVal.a;
|
||||||
|
|
||||||
// Diffuse color and unpack the mode and the metallicness
|
// Diffuse color and unpack the mode and the metallicness
|
||||||
|
|
|
@ -17,24 +17,6 @@ layout(location = 0) out vec4 _fragColor0;
|
||||||
layout(location = 1) out vec4 _fragColor1;
|
layout(location = 1) out vec4 _fragColor1;
|
||||||
layout(location = 2) out vec4 _fragColor2;
|
layout(location = 2) out vec4 _fragColor2;
|
||||||
|
|
||||||
uniform sampler2D normalFittingMap;
|
|
||||||
|
|
||||||
vec3 bestFitNormal(vec3 normal) {
|
|
||||||
vec3 absNorm = abs(normal);
|
|
||||||
float maxNAbs = max(absNorm.z, max(absNorm.x, absNorm.y));
|
|
||||||
|
|
||||||
vec2 texcoord = (absNorm.z < maxNAbs ?
|
|
||||||
(absNorm.y < maxNAbs ? absNorm.yz : absNorm.xz) :
|
|
||||||
absNorm.xy);
|
|
||||||
texcoord = (texcoord.x < texcoord.y ? texcoord.yx : texcoord.xy);
|
|
||||||
texcoord.y /= texcoord.x;
|
|
||||||
vec3 cN = normal / maxNAbs;
|
|
||||||
|
|
||||||
float fittingScale = texture(normalFittingMap, texcoord).a;
|
|
||||||
cN *= fittingScale;
|
|
||||||
return (cN * 0.5 + 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// the alpha threshold
|
// the alpha threshold
|
||||||
const float alphaThreshold = 0.5;
|
const float alphaThreshold = 0.5;
|
||||||
|
@ -55,7 +37,7 @@ void packDeferredFragment(vec3 normal, float alpha, vec3 albedo, float roughness
|
||||||
discard;
|
discard;
|
||||||
}
|
}
|
||||||
_fragColor0 = vec4(albedo, packShadedMetallic(metallic));
|
_fragColor0 = vec4(albedo, packShadedMetallic(metallic));
|
||||||
_fragColor1 = vec4(bestFitNormal(normal), clamp(roughness, 0.0, 1.0));
|
_fragColor1 = vec4(packNormal(normal), clamp(roughness, 0.0, 1.0));
|
||||||
_fragColor2 = vec4(emissive, occlusion);
|
_fragColor2 = vec4(emissive, occlusion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +47,7 @@ void packDeferredFragmentLightmap(vec3 normal, float alpha, vec3 albedo, float r
|
||||||
discard;
|
discard;
|
||||||
}
|
}
|
||||||
_fragColor0 = vec4(albedo, packLightmappedMetallic(metallic));
|
_fragColor0 = vec4(albedo, packLightmappedMetallic(metallic));
|
||||||
_fragColor1 = vec4(bestFitNormal(normal), clamp(roughness, 0.0, 1.0));
|
_fragColor1 = vec4(packNormal(normal), clamp(roughness, 0.0, 1.0));
|
||||||
_fragColor2 = vec4(emissive, 1.0);
|
_fragColor2 = vec4(emissive, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +56,7 @@ void packDeferredFragmentUnlit(vec3 normal, float alpha, vec3 color) {
|
||||||
discard;
|
discard;
|
||||||
}
|
}
|
||||||
_fragColor0 = vec4(color, packUnlit());
|
_fragColor0 = vec4(color, packUnlit());
|
||||||
_fragColor1 = vec4(bestFitNormal(normal), 1.0);
|
_fragColor1 = vec4(packNormal(normal), 1.0);
|
||||||
//_fragColor2 = vec4(vec3(0.0), 1.0); // If unlit, do not worry about the emissive color target
|
//_fragColor2 = vec4(vec3(0.0), 1.0); // If unlit, do not worry about the emissive color target
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,8 @@ vec3 evalGlobalSpecularIrradiance(Light light, vec3 fragEyeDir, vec3 fragNormal,
|
||||||
// prepareGlobalLight
|
// prepareGlobalLight
|
||||||
|
|
||||||
// Transform directions to worldspace
|
// Transform directions to worldspace
|
||||||
vec3 fragNormal = vec3(invViewMat * vec4(normal, 0.0));
|
// vec3 fragNormal = vec3(invViewMat * vec4(normal, 0.0));
|
||||||
|
vec3 fragNormal = vec3((normal));
|
||||||
vec3 fragEyeVector = vec3(invViewMat * vec4(-position, 0.0));
|
vec3 fragEyeVector = vec3(invViewMat * vec4(-position, 0.0));
|
||||||
vec3 fragEyeDir = normalize(fragEyeVector);
|
vec3 fragEyeDir = normalize(fragEyeVector);
|
||||||
|
|
||||||
|
|
|
@ -59,8 +59,8 @@ void FramebufferCache::createPrimaryFramebuffer() {
|
||||||
_deferredFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create());
|
_deferredFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create());
|
||||||
_deferredFramebufferDepthColor = gpu::FramebufferPointer(gpu::Framebuffer::create());
|
_deferredFramebufferDepthColor = gpu::FramebufferPointer(gpu::Framebuffer::create());
|
||||||
|
|
||||||
// auto colorFormat = gpu::Element::COLOR_RGBA_32;
|
|
||||||
auto colorFormat = gpu::Element::COLOR_SRGBA_32;
|
auto colorFormat = gpu::Element::COLOR_SRGBA_32;
|
||||||
|
auto linearFormat = gpu::Element::COLOR_RGBA_32;
|
||||||
auto width = _frameBufferSize.width();
|
auto width = _frameBufferSize.width();
|
||||||
auto height = _frameBufferSize.height();
|
auto height = _frameBufferSize.height();
|
||||||
|
|
||||||
|
@ -70,7 +70,8 @@ void FramebufferCache::createPrimaryFramebuffer() {
|
||||||
_primaryFramebuffer->setRenderBuffer(0, _primaryColorTexture);
|
_primaryFramebuffer->setRenderBuffer(0, _primaryColorTexture);
|
||||||
|
|
||||||
_deferredColorTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler));
|
_deferredColorTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler));
|
||||||
_deferredNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler));
|
|
||||||
|
_deferredNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(linearFormat, width, height, defaultSampler));
|
||||||
_deferredSpecularTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler));
|
_deferredSpecularTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler));
|
||||||
|
|
||||||
_deferredFramebuffer->setRenderBuffer(0, _deferredColorTexture);
|
_deferredFramebuffer->setRenderBuffer(0, _deferredColorTexture);
|
||||||
|
|
|
@ -36,5 +36,5 @@ void main(void) {
|
||||||
TransformCamera cam = getTransformCamera();
|
TransformCamera cam = getTransformCamera();
|
||||||
TransformObject obj = getTransformObject();
|
TransformObject obj = getTransformObject();
|
||||||
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$>
|
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$>
|
||||||
<$transformModelToEyeDir(cam, obj, inNormal.xyz, _normal)$>
|
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normal)$>
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,6 @@ void main(void) {
|
||||||
TransformCamera cam = getTransformCamera();
|
TransformCamera cam = getTransformCamera();
|
||||||
TransformObject obj = getTransformObject();
|
TransformObject obj = getTransformObject();
|
||||||
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$>
|
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$>
|
||||||
<$transformModelToEyeDir(cam, obj, inNormal.xyz, _normal)$>
|
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normal)$>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,6 @@ void main(void) {
|
||||||
TransformCamera cam = getTransformCamera();
|
TransformCamera cam = getTransformCamera();
|
||||||
TransformObject obj = getTransformObject();
|
TransformObject obj = getTransformObject();
|
||||||
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$>
|
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$>
|
||||||
<$transformModelToEyeDir(cam, obj, inNormal.xyz, _normal)$>
|
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normal)$>
|
||||||
<$transformModelToEyeDir(cam, obj, inTangent.xyz, _tangent)$>
|
<$transformModelToWorldDir(cam, obj, inTangent.xyz, _tangent)$>
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,6 @@ void main(void) {
|
||||||
TransformCamera cam = getTransformCamera();
|
TransformCamera cam = getTransformCamera();
|
||||||
TransformObject obj = getTransformObject();
|
TransformObject obj = getTransformObject();
|
||||||
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$>
|
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$>
|
||||||
<$transformModelToEyeDir(cam, obj, inNormal.xyz, _normal)$>
|
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normal)$>
|
||||||
<$transformModelToEyeDir(cam, obj, inTangent.xyz, _tangent)$>
|
<$transformModelToWorldDir(cam, obj, inTangent.xyz, _tangent)$>
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,5 +32,5 @@ void main(void) {
|
||||||
TransformCamera cam = getTransformCamera();
|
TransformCamera cam = getTransformCamera();
|
||||||
TransformObject obj = getTransformObject();
|
TransformObject obj = getTransformObject();
|
||||||
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$>
|
<$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$>
|
||||||
<$transformModelToEyeDir(cam, obj, inNormal.xyz, _normal)$>
|
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normal)$>
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ void main(void) {
|
||||||
vec3 fragLightDir = fragLightVec / fragLightDistance;
|
vec3 fragLightDir = fragLightVec / fragLightDistance;
|
||||||
|
|
||||||
// Eval shading
|
// Eval shading
|
||||||
vec3 fragNormal = vec3(invViewMat * vec4(frag.normal, 0.0));
|
vec3 fragNormal = vec3(frag.normal);
|
||||||
vec4 fragEyeVector = invViewMat * vec4(-frag.position.xyz, 0.0);
|
vec4 fragEyeVector = invViewMat * vec4(-frag.position.xyz, 0.0);
|
||||||
vec3 fragEyeDir = normalize(fragEyeVector.xyz);
|
vec3 fragEyeDir = normalize(fragEyeVector.xyz);
|
||||||
vec4 shading = evalFragShading(fragNormal, fragLightDir, fragEyeDir, frag.metallic, frag.specular, frag.roughness);
|
vec4 shading = evalFragShading(fragNormal, fragLightDir, fragEyeDir, frag.metallic, frag.specular, frag.roughness);
|
||||||
|
|
|
@ -27,5 +27,5 @@ void main() {
|
||||||
TransformCamera cam = getTransformCamera();
|
TransformCamera cam = getTransformCamera();
|
||||||
TransformObject obj = getTransformObject();
|
TransformObject obj = getTransformObject();
|
||||||
<$transformModelToClipPos(cam, obj, inPosition, gl_Position)$>
|
<$transformModelToClipPos(cam, obj, inPosition, gl_Position)$>
|
||||||
<$transformModelToEyeDir(cam, obj, inNormal.xyz, _normal.xyz)$>
|
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normal.xyz)$>
|
||||||
}
|
}
|
|
@ -34,5 +34,5 @@ void main(void) {
|
||||||
TransformCamera cam = getTransformCamera();
|
TransformCamera cam = getTransformCamera();
|
||||||
TransformObject obj = getTransformObject();
|
TransformObject obj = getTransformObject();
|
||||||
<$transformModelToClipPos(cam, obj, inPosition, gl_Position)$>
|
<$transformModelToClipPos(cam, obj, inPosition, gl_Position)$>
|
||||||
<$transformModelToEyeDir(cam, obj, inNormal.xyz, _normal)$>
|
<$transformModelToWorldDir(cam, obj, inNormal.xyz, _normal)$>
|
||||||
}
|
}
|
|
@ -45,6 +45,5 @@ void main(void) {
|
||||||
TransformCamera cam = getTransformCamera();
|
TransformCamera cam = getTransformCamera();
|
||||||
TransformObject obj = getTransformObject();
|
TransformObject obj = getTransformObject();
|
||||||
<$transformModelToEyeAndClipPos(cam, obj, position, _position, gl_Position)$>
|
<$transformModelToEyeAndClipPos(cam, obj, position, _position, gl_Position)$>
|
||||||
<$transformModelToEyeDir(cam, obj, interpolatedNormal.xyz, interpolatedNormal.xyz)$>
|
<$transformModelToWorldDir(cam, obj, interpolatedNormal.xyz, _normal.xyz)$>
|
||||||
_normal = interpolatedNormal.xyz;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,8 +50,8 @@ void main(void) {
|
||||||
TransformCamera cam = getTransformCamera();
|
TransformCamera cam = getTransformCamera();
|
||||||
TransformObject obj = getTransformObject();
|
TransformObject obj = getTransformObject();
|
||||||
<$transformModelToEyeAndClipPos(cam, obj, position, _position, gl_Position)$>
|
<$transformModelToEyeAndClipPos(cam, obj, position, _position, gl_Position)$>
|
||||||
<$transformModelToEyeDir(cam, obj, interpolatedNormal.xyz, interpolatedNormal.xyz)$>
|
<$transformModelToWorldDir(cam, obj, interpolatedNormal.xyz, interpolatedNormal.xyz)$>
|
||||||
<$transformModelToEyeDir(cam, obj, interpolatedTangent.xyz, interpolatedTangent.xyz)$>
|
<$transformModelToWorldDir(cam, obj, interpolatedTangent.xyz, interpolatedTangent.xyz)$>
|
||||||
|
|
||||||
_normal = interpolatedNormal.xyz;
|
_normal = interpolatedNormal.xyz;
|
||||||
_tangent = interpolatedTangent.xyz;
|
_tangent = interpolatedTangent.xyz;
|
||||||
|
|
|
@ -68,7 +68,7 @@ void main(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eval shading
|
// Eval shading
|
||||||
vec3 fragNormal = vec3(invViewMat * vec4(frag.normal, 0.0));
|
vec3 fragNormal = vec3(frag.normal);
|
||||||
vec4 fragEyeVector = invViewMat * vec4(-frag.position.xyz, 0.0);
|
vec4 fragEyeVector = invViewMat * vec4(-frag.position.xyz, 0.0);
|
||||||
vec3 fragEyeDir = normalize(fragEyeVector.xyz);
|
vec3 fragEyeDir = normalize(fragEyeVector.xyz);
|
||||||
vec4 shading = evalFragShading(fragNormal, fragLightDir, fragEyeDir, frag.metallic, frag.specular, frag.roughness);
|
vec4 shading = evalFragShading(fragNormal, fragLightDir, fragEyeDir, frag.metallic, frag.specular, frag.roughness);
|
||||||
|
|
|
@ -30,6 +30,6 @@ void main(void) {
|
||||||
TransformCamera cam = getTransformCamera();
|
TransformCamera cam = getTransformCamera();
|
||||||
TransformObject obj = getTransformObject();
|
TransformObject obj = getTransformObject();
|
||||||
<$transformModelToClipPos(cam, obj, inPosition, gl_Position)$>
|
<$transformModelToClipPos(cam, obj, inPosition, gl_Position)$>
|
||||||
<$transformModelToEyeDir(cam, obj, inNormal.xyz, varNormal)$>
|
<$transformModelToWorldDir(cam, obj, inNormal.xyz, varNormal)$>
|
||||||
varPosition = inPosition.xyz;
|
varPosition = inPosition.xyz;
|
||||||
}
|
}
|
|
@ -17,7 +17,7 @@ void OculusBaseDisplayPlugin::resetSensors() {
|
||||||
_currentRenderFrameInfo.renderPose = glm::mat4(); // identity
|
_currentRenderFrameInfo.renderPose = glm::mat4(); // identity
|
||||||
}
|
}
|
||||||
|
|
||||||
void OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
|
bool OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
|
||||||
_currentRenderFrameInfo = FrameInfo();
|
_currentRenderFrameInfo = FrameInfo();
|
||||||
_currentRenderFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds();;
|
_currentRenderFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds();;
|
||||||
_currentRenderFrameInfo.predictedDisplayTime = ovr_GetPredictedDisplayTime(_session, frameIndex);
|
_currentRenderFrameInfo.predictedDisplayTime = ovr_GetPredictedDisplayTime(_session, frameIndex);
|
||||||
|
@ -26,6 +26,7 @@ void OculusBaseDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
|
||||||
_currentRenderFrameInfo.presentPose = _currentRenderFrameInfo.renderPose;
|
_currentRenderFrameInfo.presentPose = _currentRenderFrameInfo.renderPose;
|
||||||
Lock lock(_mutex);
|
Lock lock(_mutex);
|
||||||
_frameInfos[frameIndex] = _currentRenderFrameInfo;
|
_frameInfos[frameIndex] = _currentRenderFrameInfo;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OculusBaseDisplayPlugin::isSupported() const {
|
bool OculusBaseDisplayPlugin::isSupported() const {
|
||||||
|
|
|
@ -16,11 +16,11 @@
|
||||||
class OculusBaseDisplayPlugin : public HmdDisplayPlugin {
|
class OculusBaseDisplayPlugin : public HmdDisplayPlugin {
|
||||||
using Parent = HmdDisplayPlugin;
|
using Parent = HmdDisplayPlugin;
|
||||||
public:
|
public:
|
||||||
virtual bool isSupported() const override;
|
bool isSupported() const override;
|
||||||
|
|
||||||
// Stereo specific methods
|
// Stereo specific methods
|
||||||
virtual void resetSensors() override final;
|
void resetSensors() override final;
|
||||||
virtual void beginFrameRender(uint32_t frameIndex) override;
|
bool beginFrameRender(uint32_t frameIndex) override;
|
||||||
float getTargetFrameRate() const override { return _hmdDesc.DisplayRefreshRate; }
|
float getTargetFrameRate() const override { return _hmdDesc.DisplayRefreshRate; }
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -40,13 +40,14 @@ void OculusLegacyDisplayPlugin::resetSensors() {
|
||||||
ovrHmd_RecenterPose(_hmd);
|
ovrHmd_RecenterPose(_hmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OculusLegacyDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
|
bool OculusLegacyDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
|
||||||
_currentRenderFrameInfo = FrameInfo();
|
_currentRenderFrameInfo = FrameInfo();
|
||||||
_currentRenderFrameInfo.predictedDisplayTime = _currentRenderFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds();
|
_currentRenderFrameInfo.predictedDisplayTime = _currentRenderFrameInfo.sensorSampleTime = ovr_GetTimeInSeconds();
|
||||||
_trackingState = ovrHmd_GetTrackingState(_hmd, _currentRenderFrameInfo.predictedDisplayTime);
|
_trackingState = ovrHmd_GetTrackingState(_hmd, _currentRenderFrameInfo.predictedDisplayTime);
|
||||||
_currentRenderFrameInfo.rawRenderPose = _currentRenderFrameInfo.renderPose = toGlm(_trackingState.HeadPose.ThePose);
|
_currentRenderFrameInfo.rawRenderPose = _currentRenderFrameInfo.renderPose = toGlm(_trackingState.HeadPose.ThePose);
|
||||||
Lock lock(_mutex);
|
Lock lock(_mutex);
|
||||||
_frameInfos[frameIndex] = _currentRenderFrameInfo;
|
_frameInfos[frameIndex] = _currentRenderFrameInfo;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OculusLegacyDisplayPlugin::isSupported() const {
|
bool OculusLegacyDisplayPlugin::isSupported() const {
|
||||||
|
|
|
@ -27,7 +27,7 @@ public:
|
||||||
|
|
||||||
// Stereo specific methods
|
// Stereo specific methods
|
||||||
void resetSensors() override;
|
void resetSensors() override;
|
||||||
void beginFrameRender(uint32_t frameIndex) override;
|
bool beginFrameRender(uint32_t frameIndex) override;
|
||||||
|
|
||||||
float getTargetFrameRate() const override;
|
float getTargetFrameRate() const override;
|
||||||
|
|
||||||
|
|
|
@ -121,7 +121,12 @@ void OpenVrDisplayPlugin::resetSensors() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
|
bool OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
|
||||||
|
handleOpenVrEvents();
|
||||||
|
if (openVrQuitRequested()) {
|
||||||
|
emit outputDeviceLost();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
double displayFrequency = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_DisplayFrequency_Float);
|
double displayFrequency = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_DisplayFrequency_Float);
|
||||||
double frameDuration = 1.f / displayFrequency;
|
double frameDuration = 1.f / displayFrequency;
|
||||||
double vsyncToPhotons = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SecondsFromVsyncToPhotons_Float);
|
double vsyncToPhotons = _system->GetFloatTrackedDeviceProperty(vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SecondsFromVsyncToPhotons_Float);
|
||||||
|
@ -148,6 +153,7 @@ void OpenVrDisplayPlugin::beginFrameRender(uint32_t frameIndex) {
|
||||||
|
|
||||||
Lock lock(_mutex);
|
Lock lock(_mutex);
|
||||||
_frameInfos[frameIndex] = _currentRenderFrameInfo;
|
_frameInfos[frameIndex] = _currentRenderFrameInfo;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenVrDisplayPlugin::hmdPresent() {
|
void OpenVrDisplayPlugin::hmdPresent() {
|
||||||
|
|
|
@ -18,16 +18,16 @@ const float TARGET_RATE_OpenVr = 90.0f; // FIXME: get from sdk tracked device p
|
||||||
class OpenVrDisplayPlugin : public HmdDisplayPlugin {
|
class OpenVrDisplayPlugin : public HmdDisplayPlugin {
|
||||||
using Parent = HmdDisplayPlugin;
|
using Parent = HmdDisplayPlugin;
|
||||||
public:
|
public:
|
||||||
virtual bool isSupported() const override;
|
bool isSupported() const override;
|
||||||
virtual const QString& getName() const override { return NAME; }
|
const QString& getName() const override { return NAME; }
|
||||||
|
|
||||||
virtual float getTargetFrameRate() const override { return TARGET_RATE_OpenVr; }
|
float getTargetFrameRate() const override { return TARGET_RATE_OpenVr; }
|
||||||
|
|
||||||
virtual void customizeContext() override;
|
void customizeContext() override;
|
||||||
|
|
||||||
// Stereo specific methods
|
// Stereo specific methods
|
||||||
virtual void resetSensors() override;
|
void resetSensors() override;
|
||||||
virtual void beginFrameRender(uint32_t frameIndex) override;
|
bool beginFrameRender(uint32_t frameIndex) override;
|
||||||
void cycleDebugOutput() override { _lockCurrentTexture = !_lockCurrentTexture; }
|
void cycleDebugOutput() override { _lockCurrentTexture = !_lockCurrentTexture; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -26,6 +26,11 @@ using Lock = std::unique_lock<Mutex>;
|
||||||
static int refCount { 0 };
|
static int refCount { 0 };
|
||||||
static Mutex mutex;
|
static Mutex mutex;
|
||||||
static vr::IVRSystem* activeHmd { nullptr };
|
static vr::IVRSystem* activeHmd { nullptr };
|
||||||
|
static bool _openVrQuitRequested { false };
|
||||||
|
|
||||||
|
bool openVrQuitRequested() {
|
||||||
|
return _openVrQuitRequested;
|
||||||
|
}
|
||||||
|
|
||||||
static const uint32_t RELEASE_OPENVR_HMD_DELAY_MS = 5000;
|
static const uint32_t RELEASE_OPENVR_HMD_DELAY_MS = 5000;
|
||||||
|
|
||||||
|
@ -56,17 +61,17 @@ vr::IVRSystem* acquireOpenVrSystem() {
|
||||||
if (hmdPresent) {
|
if (hmdPresent) {
|
||||||
Lock lock(mutex);
|
Lock lock(mutex);
|
||||||
if (!activeHmd) {
|
if (!activeHmd) {
|
||||||
qCDebug(displayplugins) << "openvr: No vr::IVRSystem instance active, building";
|
qCDebug(displayplugins) << "OpenVR: No vr::IVRSystem instance active, building";
|
||||||
vr::EVRInitError eError = vr::VRInitError_None;
|
vr::EVRInitError eError = vr::VRInitError_None;
|
||||||
activeHmd = vr::VR_Init(&eError, vr::VRApplication_Scene);
|
activeHmd = vr::VR_Init(&eError, vr::VRApplication_Scene);
|
||||||
qCDebug(displayplugins) << "openvr display: HMD is " << activeHmd << " error is " << eError;
|
qCDebug(displayplugins) << "OpenVR display: HMD is " << activeHmd << " error is " << eError;
|
||||||
}
|
}
|
||||||
if (activeHmd) {
|
if (activeHmd) {
|
||||||
qCDebug(displayplugins) << "openvr: incrementing refcount";
|
qCDebug(displayplugins) << "OpenVR: incrementing refcount";
|
||||||
++refCount;
|
++refCount;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qCDebug(displayplugins) << "openvr: no hmd present";
|
qCDebug(displayplugins) << "OpenVR: no hmd present";
|
||||||
}
|
}
|
||||||
return activeHmd;
|
return activeHmd;
|
||||||
}
|
}
|
||||||
|
@ -74,12 +79,38 @@ vr::IVRSystem* acquireOpenVrSystem() {
|
||||||
void releaseOpenVrSystem() {
|
void releaseOpenVrSystem() {
|
||||||
if (activeHmd) {
|
if (activeHmd) {
|
||||||
Lock lock(mutex);
|
Lock lock(mutex);
|
||||||
qCDebug(displayplugins) << "openvr: decrementing refcount";
|
qCDebug(displayplugins) << "OpenVR: decrementing refcount";
|
||||||
--refCount;
|
--refCount;
|
||||||
if (0 == refCount) {
|
if (0 == refCount) {
|
||||||
qCDebug(displayplugins) << "openvr: zero refcount, deallocate VR system";
|
qCDebug(displayplugins) << "OpenVR: zero refcount, deallocate VR system";
|
||||||
vr::VR_Shutdown();
|
vr::VR_Shutdown();
|
||||||
|
_openVrQuitRequested = false;
|
||||||
activeHmd = nullptr;
|
activeHmd = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handleOpenVrEvents() {
|
||||||
|
if (!activeHmd) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Lock lock(mutex);
|
||||||
|
if (!activeHmd) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vr::VREvent_t event;
|
||||||
|
while (activeHmd->PollNextEvent(&event, sizeof(event))) {
|
||||||
|
switch (event.eventType) {
|
||||||
|
case vr::VREvent_Quit:
|
||||||
|
_openVrQuitRequested = true;
|
||||||
|
activeHmd->AcknowledgeQuit_Exiting();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
qDebug() << "OpenVR: Event " << event.eventType;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@ bool openVrSupported();
|
||||||
|
|
||||||
vr::IVRSystem* acquireOpenVrSystem();
|
vr::IVRSystem* acquireOpenVrSystem();
|
||||||
void releaseOpenVrSystem();
|
void releaseOpenVrSystem();
|
||||||
|
void handleOpenVrEvents();
|
||||||
|
bool openVrQuitRequested();
|
||||||
|
|
||||||
template<typename F>
|
template<typename F>
|
||||||
void openvr_for_each_eye(F f) {
|
void openvr_for_each_eye(F f) {
|
||||||
|
|
|
@ -214,6 +214,11 @@ void ViveControllerManager::renderHand(const controller::Pose& pose, gpu::Batch&
|
||||||
|
|
||||||
void ViveControllerManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) {
|
void ViveControllerManager::pluginUpdate(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) {
|
||||||
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
|
auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
|
||||||
|
handleOpenVrEvents();
|
||||||
|
if (openVrQuitRequested()) {
|
||||||
|
deactivate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// because update mutates the internal state we need to lock
|
// because update mutates the internal state we need to lock
|
||||||
userInputMapper->withLock([&, this]() {
|
userInputMapper->withLock([&, this]() {
|
||||||
|
|
19
script-archive/tests/audio/testPeakLimiter.js
Normal file
19
script-archive/tests/audio/testPeakLimiter.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
var audioOptions = {
|
||||||
|
volume: 1.0,
|
||||||
|
loop: true,
|
||||||
|
position: MyAvatar.position
|
||||||
|
}
|
||||||
|
|
||||||
|
//var sineWave = Script.resolvePath("./1760sine.wav"); // use relative file
|
||||||
|
var sineWave = "https://s3-us-west-1.amazonaws.com/highfidelity-dev/1760sine.wav"; // use file from S3
|
||||||
|
var sound = SoundCache.getSound(sineWave);
|
||||||
|
var injectorCount = 0;
|
||||||
|
var MAX_INJECTOR_COUNT = 40;
|
||||||
|
|
||||||
|
Script.update.connect(function() {
|
||||||
|
if (sound.downloaded && injectorCount < MAX_INJECTOR_COUNT) {
|
||||||
|
injectorCount++;
|
||||||
|
print("stating injector:" + injectorCount);
|
||||||
|
Audio.playSound(sound, audioOptions);
|
||||||
|
}
|
||||||
|
});
|
|
@ -9,10 +9,12 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <QVector>
|
|
||||||
#include "VHACDUtil.h"
|
#include "VHACDUtil.h"
|
||||||
|
|
||||||
const float COLLISION_TETRAHEDRON_SCALE = 0.25f;
|
#include <unordered_map>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
#include <NumericalConstants.h>
|
||||||
|
|
||||||
|
|
||||||
// FBXReader jumbles the order of the meshes by reading them back out of a hashtable. This will put
|
// FBXReader jumbles the order of the meshes by reading them back out of a hashtable. This will put
|
||||||
|
@ -27,13 +29,16 @@ void reSortFBXGeometryMeshes(FBXGeometry& geometry) {
|
||||||
|
|
||||||
// Read all the meshes from provided FBX file
|
// Read all the meshes from provided FBX file
|
||||||
bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) {
|
bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) {
|
||||||
|
if (_verbose) {
|
||||||
|
qDebug() << "reading FBX file =" << filename << "...";
|
||||||
|
}
|
||||||
|
|
||||||
// open the fbx file
|
// open the fbx file
|
||||||
QFile fbx(filename);
|
QFile fbx(filename);
|
||||||
if (!fbx.open(QIODevice::ReadOnly)) {
|
if (!fbx.open(QIODevice::ReadOnly)) {
|
||||||
|
qWarning() << "unable to open FBX file =" << filename;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::cout << "Reading FBX.....\n";
|
|
||||||
try {
|
try {
|
||||||
QByteArray fbxContents = fbx.readAll();
|
QByteArray fbxContents = fbx.readAll();
|
||||||
FBXGeometry* geom;
|
FBXGeometry* geom;
|
||||||
|
@ -42,14 +47,14 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) {
|
||||||
} else if (filename.toLower().endsWith(".fbx")) {
|
} else if (filename.toLower().endsWith(".fbx")) {
|
||||||
geom = readFBX(fbxContents, QVariantHash(), filename);
|
geom = readFBX(fbxContents, QVariantHash(), filename);
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "unknown file extension";
|
qWarning() << "file has unknown extension" << filename;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
result = *geom;
|
result = *geom;
|
||||||
|
|
||||||
reSortFBXGeometryMeshes(result);
|
reSortFBXGeometryMeshes(result);
|
||||||
} catch (const QString& error) {
|
} catch (const QString& error) {
|
||||||
qDebug() << "Error reading " << filename << ": " << error;
|
qWarning() << "error reading" << filename << ":" << error;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,68 +62,62 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unsigned int getTrianglesInMeshPart(const FBXMeshPart &meshPart, std::vector<int>& triangles) {
|
void getTrianglesInMeshPart(const FBXMeshPart &meshPart, std::vector<int>& triangleIndices) {
|
||||||
// append all the triangles (and converted quads) from this mesh-part to triangles
|
// append triangle indices
|
||||||
std::vector<int> meshPartTriangles = meshPart.triangleIndices.toStdVector();
|
triangleIndices.reserve(triangleIndices.size() + (size_t)meshPart.triangleIndices.size());
|
||||||
triangles.insert(triangles.end(), meshPartTriangles.begin(), meshPartTriangles.end());
|
for (auto index : meshPart.triangleIndices) {
|
||||||
|
triangleIndices.push_back(index);
|
||||||
// convert quads to triangles
|
|
||||||
unsigned int triangleCount = meshPart.triangleIndices.size() / 3;
|
|
||||||
unsigned int quadCount = meshPart.quadIndices.size() / 4;
|
|
||||||
for (unsigned int i = 0; i < quadCount; i++) {
|
|
||||||
unsigned int p0Index = meshPart.quadIndices[i * 4];
|
|
||||||
unsigned int p1Index = meshPart.quadIndices[i * 4 + 1];
|
|
||||||
unsigned int p2Index = meshPart.quadIndices[i * 4 + 2];
|
|
||||||
unsigned int p3Index = meshPart.quadIndices[i * 4 + 3];
|
|
||||||
// split each quad into two triangles
|
|
||||||
triangles.push_back(p0Index);
|
|
||||||
triangles.push_back(p1Index);
|
|
||||||
triangles.push_back(p2Index);
|
|
||||||
triangles.push_back(p0Index);
|
|
||||||
triangles.push_back(p2Index);
|
|
||||||
triangles.push_back(p3Index);
|
|
||||||
triangleCount += 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return triangleCount;
|
// convert quads to triangles
|
||||||
|
const uint32_t QUAD_STRIDE = 4;
|
||||||
|
uint32_t numIndices = (uint32_t)meshPart.quadIndices.size();
|
||||||
|
for (uint32_t i = 0; i < numIndices; i += QUAD_STRIDE) {
|
||||||
|
uint32_t p0Index = meshPart.quadIndices[i];
|
||||||
|
uint32_t p1Index = meshPart.quadIndices[i + 1];
|
||||||
|
uint32_t p2Index = meshPart.quadIndices[i + 2];
|
||||||
|
uint32_t p3Index = meshPart.quadIndices[i + 3];
|
||||||
|
// split each quad into two triangles
|
||||||
|
triangleIndices.push_back(p0Index);
|
||||||
|
triangleIndices.push_back(p1Index);
|
||||||
|
triangleIndices.push_back(p2Index);
|
||||||
|
triangleIndices.push_back(p0Index);
|
||||||
|
triangleIndices.push_back(p2Index);
|
||||||
|
triangleIndices.push_back(p3Index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void vhacd::VHACDUtil::fattenMesh(const FBXMesh& mesh, const glm::mat4& geometryOffset, FBXMesh& result) const {
|
||||||
void vhacd::VHACDUtil::fattenMeshes(const FBXMesh& mesh, FBXMesh& result,
|
|
||||||
unsigned int& meshPartCount,
|
|
||||||
unsigned int startMeshIndex, unsigned int endMeshIndex) const {
|
|
||||||
// this is used to make meshes generated from a highfield collidable. each triangle
|
// this is used to make meshes generated from a highfield collidable. each triangle
|
||||||
// is converted into a tetrahedron and made into its own mesh-part.
|
// is converted into a tetrahedron and made into its own mesh-part.
|
||||||
|
|
||||||
std::vector<int> triangles;
|
std::vector<int> triangleIndices;
|
||||||
foreach (const FBXMeshPart &meshPart, mesh.parts) {
|
foreach (const FBXMeshPart &meshPart, mesh.parts) {
|
||||||
if (meshPartCount < startMeshIndex || meshPartCount >= endMeshIndex) {
|
getTrianglesInMeshPart(meshPart, triangleIndices);
|
||||||
meshPartCount++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
getTrianglesInMeshPart(meshPart, triangles);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto triangleCount = triangles.size() / 3;
|
if (triangleIndices.size() == 0) {
|
||||||
if (triangleCount == 0) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int indexStartOffset = result.vertices.size();
|
int indexStartOffset = result.vertices.size();
|
||||||
|
|
||||||
// new mesh gets the transformed points from the original
|
// new mesh gets the transformed points from the original
|
||||||
|
glm::mat4 totalTransform = geometryOffset * mesh.modelTransform;
|
||||||
for (int i = 0; i < mesh.vertices.size(); i++) {
|
for (int i = 0; i < mesh.vertices.size(); i++) {
|
||||||
// apply the source mesh's transform to the points
|
// apply the source mesh's transform to the points
|
||||||
glm::vec4 v = mesh.modelTransform * glm::vec4(mesh.vertices[i], 1.0f);
|
glm::vec4 v = totalTransform * glm::vec4(mesh.vertices[i], 1.0f);
|
||||||
result.vertices += glm::vec3(v);
|
result.vertices += glm::vec3(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
// turn each triangle into a tetrahedron
|
// turn each triangle into a tetrahedron
|
||||||
|
|
||||||
for (unsigned int i = 0; i < triangleCount; i++) {
|
const uint32_t TRIANGLE_STRIDE = 3;
|
||||||
int index0 = triangles[i * 3] + indexStartOffset;
|
const float COLLISION_TETRAHEDRON_SCALE = 0.25f;
|
||||||
int index1 = triangles[i * 3 + 1] + indexStartOffset;
|
for (uint32_t i = 0; i < triangleIndices.size(); i += TRIANGLE_STRIDE) {
|
||||||
int index2 = triangles[i * 3 + 2] + indexStartOffset;
|
int index0 = triangleIndices[i] + indexStartOffset;
|
||||||
|
int index1 = triangleIndices[i + 1] + indexStartOffset;
|
||||||
|
int index2 = triangleIndices[i + 2] + indexStartOffset;
|
||||||
|
|
||||||
// TODO: skip triangles with a normal that points more negative-y than positive-y
|
// TODO: skip triangles with a normal that points more negative-y than positive-y
|
||||||
|
|
||||||
|
@ -155,156 +154,304 @@ void vhacd::VHACDUtil::fattenMeshes(const FBXMesh& mesh, FBXMesh& result,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AABox getAABoxForMeshPart(const FBXMesh& mesh, const FBXMeshPart &meshPart) {
|
AABox getAABoxForMeshPart(const FBXMesh& mesh, const FBXMeshPart &meshPart) {
|
||||||
AABox aaBox;
|
AABox aaBox;
|
||||||
unsigned int triangleCount = meshPart.triangleIndices.size() / 3;
|
const int TRIANGLE_STRIDE = 3;
|
||||||
for (unsigned int i = 0; i < triangleCount; ++i) {
|
for (int i = 0; i < meshPart.triangleIndices.size(); i += TRIANGLE_STRIDE) {
|
||||||
aaBox += mesh.vertices[meshPart.triangleIndices[i * 3]];
|
aaBox += mesh.vertices[meshPart.triangleIndices[i]];
|
||||||
aaBox += mesh.vertices[meshPart.triangleIndices[i * 3 + 1]];
|
aaBox += mesh.vertices[meshPart.triangleIndices[i + 1]];
|
||||||
aaBox += mesh.vertices[meshPart.triangleIndices[i * 3 + 2]];
|
aaBox += mesh.vertices[meshPart.triangleIndices[i + 2]];
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int quadCount = meshPart.quadIndices.size() / 4;
|
const int QUAD_STRIDE = 4;
|
||||||
for (unsigned int i = 0; i < quadCount; ++i) {
|
for (int i = 0; i < meshPart.quadIndices.size(); i += QUAD_STRIDE) {
|
||||||
aaBox += mesh.vertices[meshPart.quadIndices[i * 4]];
|
aaBox += mesh.vertices[meshPart.quadIndices[i]];
|
||||||
aaBox += mesh.vertices[meshPart.quadIndices[i * 4 + 1]];
|
aaBox += mesh.vertices[meshPart.quadIndices[i + 1]];
|
||||||
aaBox += mesh.vertices[meshPart.quadIndices[i * 4 + 2]];
|
aaBox += mesh.vertices[meshPart.quadIndices[i + 2]];
|
||||||
aaBox += mesh.vertices[meshPart.quadIndices[i * 4 + 3]];
|
aaBox += mesh.vertices[meshPart.quadIndices[i + 3]];
|
||||||
}
|
}
|
||||||
|
|
||||||
return aaBox;
|
return aaBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TriangleEdge {
|
||||||
|
public:
|
||||||
|
TriangleEdge() {}
|
||||||
|
TriangleEdge(uint32_t A, uint32_t B) {
|
||||||
|
setIndices(A, B);
|
||||||
|
}
|
||||||
|
void setIndices(uint32_t A, uint32_t B) {
|
||||||
|
if (A < B) {
|
||||||
|
_indexA = A;
|
||||||
|
_indexB = B;
|
||||||
|
} else {
|
||||||
|
_indexA = B;
|
||||||
|
_indexB = A;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool operator==(const TriangleEdge& other) const {
|
||||||
|
return _indexA == other._indexA && _indexB == other._indexB;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t getIndexA() const { return _indexA; }
|
||||||
|
uint32_t getIndexB() const { return _indexB; }
|
||||||
|
private:
|
||||||
|
uint32_t _indexA { (uint32_t)(-1) };
|
||||||
|
uint32_t _indexB { (uint32_t)(-1) };
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template <>
|
||||||
|
struct hash<TriangleEdge> {
|
||||||
|
std::size_t operator()(const TriangleEdge& edge) const {
|
||||||
|
// use Cantor's pairing function to generate a hash of ZxZ --> Z
|
||||||
|
uint32_t ab = edge.getIndexA() + edge.getIndexB();
|
||||||
|
return hash<int>()((ab * (ab + 1)) / 2 + edge.getIndexB());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns false if any edge has only one adjacent triangle
|
||||||
|
bool isClosedManifold(const std::vector<int>& triangleIndices) {
|
||||||
|
using EdgeList = std::unordered_map<TriangleEdge, int>;
|
||||||
|
EdgeList edges;
|
||||||
|
|
||||||
|
// count the triangles for each edge
|
||||||
|
const uint32_t TRIANGLE_STRIDE = 3;
|
||||||
|
for (uint32_t i = 0; i < triangleIndices.size(); i += TRIANGLE_STRIDE) {
|
||||||
|
TriangleEdge edge;
|
||||||
|
// the triangles indices are stored in sequential order
|
||||||
|
for (uint32_t j = 0; j < 3; ++j) {
|
||||||
|
edge.setIndices(triangleIndices[i + j], triangleIndices[i + ((j + 1) % 3)]);
|
||||||
|
|
||||||
|
EdgeList::iterator edgeEntry = edges.find(edge);
|
||||||
|
if (edgeEntry == edges.end()) {
|
||||||
|
edges.insert(std::pair<TriangleEdge, uint32_t>(edge, 1));
|
||||||
|
} else {
|
||||||
|
edgeEntry->second += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// scan for outside edge
|
||||||
|
for (auto& edgeEntry : edges) {
|
||||||
|
if (edgeEntry.second == 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vhacd::VHACDUtil::getConvexResults(VHACD::IVHACD* convexifier, FBXMesh& resultMesh) const {
|
||||||
|
// Number of hulls for this input meshPart
|
||||||
|
uint32_t numHulls = convexifier->GetNConvexHulls();
|
||||||
|
if (_verbose) {
|
||||||
|
qDebug() << " hulls =" << numHulls;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create an output meshPart for each convex hull
|
||||||
|
const uint32_t TRIANGLE_STRIDE = 3;
|
||||||
|
const uint32_t POINT_STRIDE = 3;
|
||||||
|
for (uint32_t j = 0; j < numHulls; j++) {
|
||||||
|
VHACD::IVHACD::ConvexHull hull;
|
||||||
|
convexifier->GetConvexHull(j, hull);
|
||||||
|
|
||||||
|
resultMesh.parts.append(FBXMeshPart());
|
||||||
|
FBXMeshPart& resultMeshPart = resultMesh.parts.last();
|
||||||
|
|
||||||
|
int hullIndexStart = resultMesh.vertices.size();
|
||||||
|
resultMesh.vertices.reserve(hullIndexStart + hull.m_nPoints);
|
||||||
|
uint32_t numIndices = hull.m_nPoints * POINT_STRIDE;
|
||||||
|
for (uint32_t i = 0; i < numIndices; i += POINT_STRIDE) {
|
||||||
|
float x = hull.m_points[i];
|
||||||
|
float y = hull.m_points[i + 1];
|
||||||
|
float z = hull.m_points[i + 2];
|
||||||
|
resultMesh.vertices.append(glm::vec3(x, y, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
numIndices = hull.m_nTriangles * TRIANGLE_STRIDE;
|
||||||
|
resultMeshPart.triangleIndices.reserve(resultMeshPart.triangleIndices.size() + numIndices);
|
||||||
|
for (uint32_t i = 0; i < numIndices; i += TRIANGLE_STRIDE) {
|
||||||
|
resultMeshPart.triangleIndices.append(hull.m_triangles[i] + hullIndexStart);
|
||||||
|
resultMeshPart.triangleIndices.append(hull.m_triangles[i + 1] + hullIndexStart);
|
||||||
|
resultMeshPart.triangleIndices.append(hull.m_triangles[i + 2] + hullIndexStart);
|
||||||
|
}
|
||||||
|
if (_verbose) {
|
||||||
|
qDebug() << " hull" << j << " vertices =" << hull.m_nPoints
|
||||||
|
<< " triangles =" << hull.m_nTriangles
|
||||||
|
<< " FBXMeshVertices =" << resultMesh.vertices.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float computeDt(uint64_t start) {
|
||||||
|
return (float)(usecTimestampNow() - start) / (float)USECS_PER_SECOND;
|
||||||
|
}
|
||||||
|
|
||||||
bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry,
|
bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry,
|
||||||
VHACD::IVHACD::Parameters params,
|
VHACD::IVHACD::Parameters params,
|
||||||
FBXGeometry& result,
|
FBXGeometry& result,
|
||||||
int startMeshIndex,
|
|
||||||
int endMeshIndex,
|
|
||||||
float minimumMeshSize, float maximumMeshSize) {
|
float minimumMeshSize, float maximumMeshSize) {
|
||||||
|
if (_verbose) {
|
||||||
|
qDebug() << "meshes =" << geometry.meshes.size();
|
||||||
|
}
|
||||||
|
|
||||||
// count the mesh-parts
|
// count the mesh-parts
|
||||||
int meshCount = 0;
|
int numParts = 0;
|
||||||
foreach (const FBXMesh& mesh, geometry.meshes) {
|
foreach (const FBXMesh& mesh, geometry.meshes) {
|
||||||
meshCount += mesh.parts.size();
|
numParts += mesh.parts.size();
|
||||||
|
}
|
||||||
|
if (_verbose) {
|
||||||
|
qDebug() << "total parts =" << numParts;
|
||||||
}
|
}
|
||||||
|
|
||||||
VHACD::IVHACD * interfaceVHACD = VHACD::CreateVHACD();
|
VHACD::IVHACD * convexifier = VHACD::CreateVHACD();
|
||||||
|
|
||||||
if (startMeshIndex < 0) {
|
|
||||||
startMeshIndex = 0;
|
|
||||||
}
|
|
||||||
if (endMeshIndex < 0) {
|
|
||||||
endMeshIndex = meshCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "Performing V-HACD computation on " << endMeshIndex - startMeshIndex << " meshes ..... " << std::endl;
|
|
||||||
|
|
||||||
result.meshExtents.reset();
|
result.meshExtents.reset();
|
||||||
result.meshes.append(FBXMesh());
|
result.meshes.append(FBXMesh());
|
||||||
FBXMesh &resultMesh = result.meshes.last();
|
FBXMesh &resultMesh = result.meshes.last();
|
||||||
|
|
||||||
int count = 0;
|
const uint32_t POINT_STRIDE = 3;
|
||||||
|
const uint32_t TRIANGLE_STRIDE = 3;
|
||||||
|
|
||||||
|
int meshIndex = 0;
|
||||||
|
int validPartsFound = 0;
|
||||||
foreach (const FBXMesh& mesh, geometry.meshes) {
|
foreach (const FBXMesh& mesh, geometry.meshes) {
|
||||||
|
|
||||||
|
// find duplicate points
|
||||||
|
int numDupes = 0;
|
||||||
|
std::vector<int> dupeIndexMap;
|
||||||
|
dupeIndexMap.reserve(mesh.vertices.size());
|
||||||
|
for (int i = 0; i < mesh.vertices.size(); ++i) {
|
||||||
|
dupeIndexMap.push_back(i);
|
||||||
|
for (int j = 0; j < i; ++j) {
|
||||||
|
float distance = glm::distance2(mesh.vertices[i], mesh.vertices[j]);
|
||||||
|
const float MAX_DUPE_DISTANCE_SQUARED = 0.000001f;
|
||||||
|
if (distance < MAX_DUPE_DISTANCE_SQUARED) {
|
||||||
|
dupeIndexMap[i] = j;
|
||||||
|
++numDupes;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// each mesh has its own transform to move it to model-space
|
// each mesh has its own transform to move it to model-space
|
||||||
std::vector<glm::vec3> vertices;
|
std::vector<glm::vec3> vertices;
|
||||||
|
glm::mat4 totalTransform = geometry.offset * mesh.modelTransform;
|
||||||
foreach (glm::vec3 vertex, mesh.vertices) {
|
foreach (glm::vec3 vertex, mesh.vertices) {
|
||||||
vertices.push_back(glm::vec3(mesh.modelTransform * glm::vec4(vertex, 1.0f)));
|
vertices.push_back(glm::vec3(totalTransform * glm::vec4(vertex, 1.0f)));
|
||||||
}
|
}
|
||||||
|
uint32_t numVertices = (uint32_t)vertices.size();
|
||||||
|
|
||||||
|
if (_verbose) {
|
||||||
|
qDebug() << "mesh" << meshIndex << ": "
|
||||||
|
<< " parts =" << mesh.parts.size() << " clusters =" << mesh.clusters.size()
|
||||||
|
<< " vertices =" << numVertices;
|
||||||
|
}
|
||||||
|
++meshIndex;
|
||||||
|
|
||||||
|
std::vector<int> openParts;
|
||||||
|
|
||||||
|
int partIndex = 0;
|
||||||
|
std::vector<int> triangleIndices;
|
||||||
foreach (const FBXMeshPart &meshPart, mesh.parts) {
|
foreach (const FBXMeshPart &meshPart, mesh.parts) {
|
||||||
|
triangleIndices.clear();
|
||||||
if (count < startMeshIndex || count >= endMeshIndex) {
|
getTrianglesInMeshPart(meshPart, triangleIndices);
|
||||||
count ++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug() << "--------------------";
|
|
||||||
|
|
||||||
std::vector<int> triangles;
|
|
||||||
unsigned int triangleCount = getTrianglesInMeshPart(meshPart, triangles);
|
|
||||||
|
|
||||||
// only process meshes with triangles
|
// only process meshes with triangles
|
||||||
if (triangles.size() <= 0) {
|
if (triangleIndices.size() <= 0) {
|
||||||
qDebug() << " Skipping (no triangles)...";
|
if (_verbose) {
|
||||||
count++;
|
qDebug() << " skip part" << partIndex << "(zero triangles)";
|
||||||
|
}
|
||||||
|
++partIndex;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto nPoints = vertices.size();
|
// collapse dupe indices
|
||||||
|
for (auto& index : triangleIndices) {
|
||||||
|
index = dupeIndexMap[index];
|
||||||
|
}
|
||||||
|
|
||||||
AABox aaBox = getAABoxForMeshPart(mesh, meshPart);
|
AABox aaBox = getAABoxForMeshPart(mesh, meshPart);
|
||||||
const float largestDimension = aaBox.getLargestDimension();
|
const float largestDimension = aaBox.getLargestDimension();
|
||||||
|
|
||||||
qDebug() << "Mesh " << count << " -- " << nPoints << " points, " << triangleCount << " triangles, "
|
|
||||||
<< "size =" << largestDimension;
|
|
||||||
|
|
||||||
if (largestDimension < minimumMeshSize) {
|
if (largestDimension < minimumMeshSize) {
|
||||||
qDebug() << " Skipping (too small)...";
|
if (_verbose) {
|
||||||
count++;
|
qDebug() << " skip part" << partIndex << ": dimension =" << largestDimension << "(too small)";
|
||||||
|
}
|
||||||
|
++partIndex;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maximumMeshSize > 0.0f && largestDimension > maximumMeshSize) {
|
if (maximumMeshSize > 0.0f && largestDimension > maximumMeshSize) {
|
||||||
qDebug() << " Skipping (too large)...";
|
if (_verbose) {
|
||||||
count++;
|
qDebug() << " skip part" << partIndex << ": dimension =" << largestDimension << "(too large)";
|
||||||
|
}
|
||||||
|
++partIndex;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// figure out if the mesh is a closed manifold or not
|
||||||
|
bool closed = isClosedManifold(triangleIndices);
|
||||||
|
if (closed) {
|
||||||
|
uint32_t triangleCount = (uint32_t)(triangleIndices.size()) / TRIANGLE_STRIDE;
|
||||||
|
if (_verbose) {
|
||||||
|
qDebug() << " process closed part" << partIndex << ": " << " triangles =" << triangleCount;
|
||||||
|
}
|
||||||
|
|
||||||
// compute approximate convex decomposition
|
// compute approximate convex decomposition
|
||||||
bool res = interfaceVHACD->Compute(&vertices[0].x, 3, (uint)nPoints, &triangles[0], 3, triangleCount, params);
|
bool success = convexifier->Compute(&vertices[0].x, POINT_STRIDE, numVertices,
|
||||||
if (!res){
|
&triangleIndices[0], TRIANGLE_STRIDE, triangleCount, params);
|
||||||
qDebug() << "V-HACD computation failed for Mesh : " << count;
|
if (success) {
|
||||||
count++;
|
getConvexResults(convexifier, resultMesh);
|
||||||
continue;
|
} else if (_verbose) {
|
||||||
|
qDebug() << " failed to convexify";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_verbose) {
|
||||||
|
qDebug() << " postpone open part" << partIndex;
|
||||||
|
}
|
||||||
|
openParts.push_back(partIndex);
|
||||||
|
}
|
||||||
|
++partIndex;
|
||||||
|
++validPartsFound;
|
||||||
|
}
|
||||||
|
if (! openParts.empty()) {
|
||||||
|
// combine open meshes in an attempt to produce a closed mesh
|
||||||
|
|
||||||
|
triangleIndices.clear();
|
||||||
|
for (auto index : openParts) {
|
||||||
|
const FBXMeshPart &meshPart = mesh.parts[index];
|
||||||
|
getTrianglesInMeshPart(meshPart, triangleIndices);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Number of hulls for this input meshPart
|
// collapse dupe indices
|
||||||
unsigned int nConvexHulls = interfaceVHACD->GetNConvexHulls();
|
for (auto& index : triangleIndices) {
|
||||||
|
index = dupeIndexMap[index];
|
||||||
// create an output meshPart for each convex hull
|
|
||||||
for (unsigned int j = 0; j < nConvexHulls; j++) {
|
|
||||||
VHACD::IVHACD::ConvexHull hull;
|
|
||||||
interfaceVHACD->GetConvexHull(j, hull);
|
|
||||||
|
|
||||||
resultMesh.parts.append(FBXMeshPart());
|
|
||||||
FBXMeshPart &resultMeshPart = resultMesh.parts.last();
|
|
||||||
|
|
||||||
int hullIndexStart = resultMesh.vertices.size();
|
|
||||||
for (unsigned int i = 0; i < hull.m_nPoints; i++) {
|
|
||||||
float x = hull.m_points[i * 3];
|
|
||||||
float y = hull.m_points[i * 3 + 1];
|
|
||||||
float z = hull.m_points[i * 3 + 2];
|
|
||||||
resultMesh.vertices.append(glm::vec3(x, y, z));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned int i = 0; i < hull.m_nTriangles; i++) {
|
// this time we don't care if the parts are closed or not
|
||||||
int index0 = hull.m_triangles[i * 3] + hullIndexStart;
|
uint32_t triangleCount = (uint32_t)(triangleIndices.size()) / TRIANGLE_STRIDE;
|
||||||
int index1 = hull.m_triangles[i * 3 + 1] + hullIndexStart;
|
if (_verbose) {
|
||||||
int index2 = hull.m_triangles[i * 3 + 2] + hullIndexStart;
|
qDebug() << " process remaining open parts =" << openParts.size() << ": "
|
||||||
resultMeshPart.triangleIndices.append(index0);
|
<< " triangles =" << triangleCount;
|
||||||
resultMeshPart.triangleIndices.append(index1);
|
|
||||||
resultMeshPart.triangleIndices.append(index2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
count++;
|
// compute approximate convex decomposition
|
||||||
|
bool success = convexifier->Compute(&vertices[0].x, POINT_STRIDE, numVertices,
|
||||||
|
&triangleIndices[0], TRIANGLE_STRIDE, triangleCount, params);
|
||||||
|
if (success) {
|
||||||
|
getConvexResults(convexifier, resultMesh);
|
||||||
|
} else if (_verbose) {
|
||||||
|
qDebug() << " failed to convexify";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//release memory
|
//release memory
|
||||||
interfaceVHACD->Clean();
|
convexifier->Clean();
|
||||||
interfaceVHACD->Release();
|
convexifier->Release();
|
||||||
|
|
||||||
if (count > 0){
|
return validPartsFound > 0;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vhacd::VHACDUtil:: ~VHACDUtil(){
|
vhacd::VHACDUtil:: ~VHACDUtil(){
|
||||||
|
@ -319,16 +466,9 @@ void vhacd::ProgressCallback::Update(const double overallProgress,
|
||||||
const char* const operation) {
|
const char* const operation) {
|
||||||
int progress = (int)(overallProgress + 0.5);
|
int progress = (int)(overallProgress + 0.5);
|
||||||
|
|
||||||
if (progress < 10){
|
|
||||||
std::cout << "\b\b";
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
std::cout << "\b\b\b";
|
std::cout << "\b\b\b";
|
||||||
}
|
std::cout << progress << "%" << std::flush;
|
||||||
|
if (progress >= 100) {
|
||||||
std::cout << progress << "%";
|
|
||||||
|
|
||||||
if (progress >= 100){
|
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,18 +25,23 @@
|
||||||
namespace vhacd {
|
namespace vhacd {
|
||||||
class VHACDUtil {
|
class VHACDUtil {
|
||||||
public:
|
public:
|
||||||
|
void setVerbose(bool verbose) { _verbose = verbose; }
|
||||||
|
|
||||||
bool loadFBX(const QString filename, FBXGeometry& result);
|
bool loadFBX(const QString filename, FBXGeometry& result);
|
||||||
|
|
||||||
void fattenMeshes(const FBXMesh& mesh, FBXMesh& result,
|
void fattenMesh(const FBXMesh& mesh, const glm::mat4& gometryOffset, FBXMesh& result) const;
|
||||||
unsigned int& meshPartCount,
|
|
||||||
unsigned int startMeshIndex, unsigned int endMeshIndex) const;
|
|
||||||
|
|
||||||
bool computeVHACD(FBXGeometry& geometry,
|
bool computeVHACD(FBXGeometry& geometry,
|
||||||
VHACD::IVHACD::Parameters params,
|
VHACD::IVHACD::Parameters params,
|
||||||
FBXGeometry& result,
|
FBXGeometry& result,
|
||||||
int startMeshIndex, int endMeshIndex,
|
|
||||||
float minimumMeshSize, float maximumMeshSize);
|
float minimumMeshSize, float maximumMeshSize);
|
||||||
|
|
||||||
|
void getConvexResults(VHACD::IVHACD* convexifier, FBXMesh& resultMesh) const;
|
||||||
|
|
||||||
~VHACDUtil();
|
~VHACDUtil();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _verbose { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
class ProgressCallback : public VHACD::IVHACD::IUserCallback {
|
class ProgressCallback : public VHACD::IVHACD::IUserCallback {
|
||||||
|
|
|
@ -19,7 +19,6 @@ using namespace std;
|
||||||
using namespace VHACD;
|
using namespace VHACD;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
QString formatFloat(double n) {
|
QString formatFloat(double n) {
|
||||||
// limit precision to 6, but don't output trailing zeros.
|
// limit precision to 6, but don't output trailing zeros.
|
||||||
QString s = QString::number(n, 'f', 6);
|
QString s = QString::number(n, 'f', 6);
|
||||||
|
@ -33,14 +32,15 @@ QString formatFloat(double n) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool writeOBJ(QString outFileName, FBXGeometry& geometry, bool outputCentimeters, int whichMeshPart = -1) {
|
bool VHACDUtilApp::writeOBJ(QString outFileName, FBXGeometry& geometry, bool outputCentimeters, int whichMeshPart) {
|
||||||
QFile file(outFileName);
|
QFile file(outFileName);
|
||||||
if (!file.open(QIODevice::WriteOnly)) {
|
if (!file.open(QIODevice::WriteOnly)) {
|
||||||
qDebug() << "Unable to write to " << outFileName;
|
qWarning() << "unable to write to" << outFileName;
|
||||||
|
_returnCode = VHACD_RETURN_CODE_FAILURE_TO_WRITE;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
QTextStream out(&file);
|
|
||||||
|
|
||||||
|
QTextStream out(&file);
|
||||||
if (outputCentimeters) {
|
if (outputCentimeters) {
|
||||||
out << "# This file uses centimeters as units\n\n";
|
out << "# This file uses centimeters as units\n\n";
|
||||||
}
|
}
|
||||||
|
@ -105,6 +105,9 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
|
||||||
|
|
||||||
const QCommandLineOption helpOption = parser.addHelpOption();
|
const QCommandLineOption helpOption = parser.addHelpOption();
|
||||||
|
|
||||||
|
const QCommandLineOption verboseOutput("v", "verbose output");
|
||||||
|
parser.addOption(verboseOutput);
|
||||||
|
|
||||||
const QCommandLineOption splitOption("split", "split input-file into one mesh per output-file");
|
const QCommandLineOption splitOption("split", "split input-file into one mesh per output-file");
|
||||||
parser.addOption(splitOption);
|
parser.addOption(splitOption);
|
||||||
|
|
||||||
|
@ -123,12 +126,6 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
|
||||||
const QCommandLineOption outputCentimetersOption("c", "output units are centimeters");
|
const QCommandLineOption outputCentimetersOption("c", "output units are centimeters");
|
||||||
parser.addOption(outputCentimetersOption);
|
parser.addOption(outputCentimetersOption);
|
||||||
|
|
||||||
const QCommandLineOption startMeshIndexOption("s", "start-mesh index", "0");
|
|
||||||
parser.addOption(startMeshIndexOption);
|
|
||||||
|
|
||||||
const QCommandLineOption endMeshIndexOption("e", "end-mesh index", "0");
|
|
||||||
parser.addOption(endMeshIndexOption);
|
|
||||||
|
|
||||||
const QCommandLineOption minimumMeshSizeOption("m", "minimum mesh (diagonal) size to consider", "0");
|
const QCommandLineOption minimumMeshSizeOption("m", "minimum mesh (diagonal) size to consider", "0");
|
||||||
parser.addOption(minimumMeshSizeOption);
|
parser.addOption(minimumMeshSizeOption);
|
||||||
|
|
||||||
|
@ -195,8 +192,10 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
|
||||||
Q_UNREACHABLE();
|
Q_UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool outputCentimeters = parser.isSet(outputCentimetersOption);
|
bool verbose = parser.isSet(verboseOutput);
|
||||||
|
vUtil.setVerbose(verbose);
|
||||||
|
|
||||||
|
bool outputCentimeters = parser.isSet(outputCentimetersOption);
|
||||||
bool fattenFaces = parser.isSet(fattenFacesOption);
|
bool fattenFaces = parser.isSet(fattenFacesOption);
|
||||||
bool generateHulls = parser.isSet(generateHullsOption);
|
bool generateHulls = parser.isSet(generateHullsOption);
|
||||||
bool splitModel = parser.isSet(splitOption);
|
bool splitModel = parser.isSet(splitOption);
|
||||||
|
@ -225,16 +224,6 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
|
||||||
Q_UNREACHABLE();
|
Q_UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
int startMeshIndex = -1;
|
|
||||||
if (parser.isSet(startMeshIndexOption)) {
|
|
||||||
startMeshIndex = parser.value(startMeshIndexOption).toInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
int endMeshIndex = -1;
|
|
||||||
if (parser.isSet(endMeshIndexOption)) {
|
|
||||||
endMeshIndex = parser.value(endMeshIndexOption).toInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
float minimumMeshSize = 0.0f;
|
float minimumMeshSize = 0.0f;
|
||||||
if (parser.isSet(minimumMeshSizeOption)) {
|
if (parser.isSet(minimumMeshSizeOption)) {
|
||||||
minimumMeshSize = parser.value(minimumMeshSizeOption).toFloat();
|
minimumMeshSize = parser.value(minimumMeshSizeOption).toFloat();
|
||||||
|
@ -301,17 +290,20 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
|
||||||
Q_UNREACHABLE();
|
Q_UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// load the mesh
|
// load the mesh
|
||||||
|
|
||||||
FBXGeometry fbx;
|
FBXGeometry fbx;
|
||||||
auto begin = std::chrono::high_resolution_clock::now();
|
auto begin = std::chrono::high_resolution_clock::now();
|
||||||
if (!vUtil.loadFBX(inputFilename, fbx)){
|
if (!vUtil.loadFBX(inputFilename, fbx)){
|
||||||
cout << "Error in opening FBX file....";
|
_returnCode = VHACD_RETURN_CODE_FAILURE_TO_READ;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
auto end = std::chrono::high_resolution_clock::now();
|
auto end = std::chrono::high_resolution_clock::now();
|
||||||
auto loadDuration = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count();
|
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
auto loadDuration = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count();
|
||||||
|
const double NANOSECS_PER_SECOND = 1.0e9;
|
||||||
|
qDebug() << "load time =" << (double)loadDuration / NANOSECS_PER_SECOND << "seconds";
|
||||||
|
}
|
||||||
|
|
||||||
if (splitModel) {
|
if (splitModel) {
|
||||||
QVector<QString> infileExtensions = {"fbx", "obj"};
|
QVector<QString> infileExtensions = {"fbx", "obj"};
|
||||||
|
@ -329,10 +321,14 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
|
||||||
|
|
||||||
if (generateHulls) {
|
if (generateHulls) {
|
||||||
VHACD::IVHACD::Parameters params;
|
VHACD::IVHACD::Parameters params;
|
||||||
vhacd::ProgressCallback pCallBack;
|
vhacd::ProgressCallback progressCallback;
|
||||||
|
|
||||||
//set parameters for V-HACD
|
//set parameters for V-HACD
|
||||||
params.m_callback = &pCallBack; //progress callback
|
if (verbose) {
|
||||||
|
params.m_callback = &progressCallback; //progress callback
|
||||||
|
} else {
|
||||||
|
params.m_callback = nullptr;
|
||||||
|
}
|
||||||
params.m_resolution = vHacdResolution;
|
params.m_resolution = vHacdResolution;
|
||||||
params.m_depth = vHacdDepth;
|
params.m_depth = vHacdDepth;
|
||||||
params.m_concavity = vHacdConcavity;
|
params.m_concavity = vHacdConcavity;
|
||||||
|
@ -346,44 +342,51 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
|
||||||
params.m_mode = 0; // 0: voxel-based (recommended), 1: tetrahedron-based
|
params.m_mode = 0; // 0: voxel-based (recommended), 1: tetrahedron-based
|
||||||
params.m_maxNumVerticesPerCH = vHacdMaxVerticesPerCH;
|
params.m_maxNumVerticesPerCH = vHacdMaxVerticesPerCH;
|
||||||
params.m_minVolumePerCH = 0.0001; // 0.0001
|
params.m_minVolumePerCH = 0.0001; // 0.0001
|
||||||
params.m_callback = 0; // 0
|
params.m_logger = nullptr;
|
||||||
params.m_logger = 0; // 0
|
|
||||||
params.m_convexhullApproximation = true; // true
|
params.m_convexhullApproximation = true; // true
|
||||||
params.m_oclAcceleration = true; // true
|
params.m_oclAcceleration = true; // true
|
||||||
|
|
||||||
//perform vhacd computation
|
//perform vhacd computation
|
||||||
|
if (verbose) {
|
||||||
|
qDebug() << "running V-HACD algorithm ...";
|
||||||
|
}
|
||||||
begin = std::chrono::high_resolution_clock::now();
|
begin = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
FBXGeometry result;
|
FBXGeometry result;
|
||||||
if (!vUtil.computeVHACD(fbx, params, result, startMeshIndex, endMeshIndex,
|
bool success = vUtil.computeVHACD(fbx, params, result, minimumMeshSize, maximumMeshSize);
|
||||||
minimumMeshSize, maximumMeshSize)) {
|
|
||||||
cout << "Compute Failed...";
|
|
||||||
}
|
|
||||||
end = std::chrono::high_resolution_clock::now();
|
end = std::chrono::high_resolution_clock::now();
|
||||||
auto computeDuration = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count();
|
auto computeDuration = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count();
|
||||||
|
if (verbose) {
|
||||||
|
qDebug() << "run time =" << (double)computeDuration / 1000000000.00 << " seconds";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
if (verbose) {
|
||||||
|
qDebug() << "failed to convexify model";
|
||||||
|
}
|
||||||
|
_returnCode = VHACD_RETURN_CODE_FAILURE_TO_CONVEXIFY;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int totalVertices = 0;
|
int totalVertices = 0;
|
||||||
int totalTriangles = 0;
|
int totalTriangles = 0;
|
||||||
int totalMeshParts = 0;
|
|
||||||
foreach (const FBXMesh& mesh, result.meshes) {
|
foreach (const FBXMesh& mesh, result.meshes) {
|
||||||
totalVertices += mesh.vertices.size();
|
totalVertices += mesh.vertices.size();
|
||||||
foreach (const FBXMeshPart &meshPart, mesh.parts) {
|
foreach (const FBXMeshPart &meshPart, mesh.parts) {
|
||||||
totalTriangles += meshPart.triangleIndices.size() / 3;
|
totalTriangles += meshPart.triangleIndices.size() / 3;
|
||||||
// each quad was made into two triangles
|
// each quad was made into two triangles
|
||||||
totalTriangles += 2 * meshPart.quadIndices.size() / 4;
|
totalTriangles += 2 * meshPart.quadIndices.size() / 4;
|
||||||
totalMeshParts++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
int totalHulls = result.meshes[0].parts.size();
|
int totalHulls = result.meshes[0].parts.size();
|
||||||
cout << endl << "Summary of V-HACD Computation..................." << endl;
|
qDebug() << "output file =" << outputFilename;
|
||||||
cout << "File Path : " << inputFilename.toStdString() << endl;
|
qDebug() << "vertices =" << totalVertices;
|
||||||
cout << "Number Of Meshes : " << totalMeshParts << endl;
|
qDebug() << "triangles =" << totalTriangles;
|
||||||
cout << "Total vertices : " << totalVertices << endl;
|
qDebug() << "hulls =" << totalHulls;
|
||||||
cout << "Total Triangles : " << totalTriangles << endl;
|
}
|
||||||
cout << "Total Convex Hulls : " << totalHulls << endl;
|
|
||||||
cout << "Total FBX load time: " << (double)loadDuration / 1000000000.00 << " seconds" << endl;
|
|
||||||
cout << "V-HACD Compute time: " << (double)computeDuration / 1000000000.00 << " seconds" << endl;
|
|
||||||
|
|
||||||
writeOBJ(outputFilename, result, outputCentimeters);
|
writeOBJ(outputFilename, result, outputCentimeters);
|
||||||
}
|
}
|
||||||
|
@ -398,17 +401,9 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
|
||||||
meshCount += mesh.parts.size();
|
meshCount += mesh.parts.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (startMeshIndex < 0) {
|
|
||||||
startMeshIndex = 0;
|
|
||||||
}
|
|
||||||
if (endMeshIndex < 0) {
|
|
||||||
endMeshIndex = meshCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int meshPartCount = 0;
|
|
||||||
result.modelTransform = glm::mat4(); // Identity matrix
|
result.modelTransform = glm::mat4(); // Identity matrix
|
||||||
foreach (const FBXMesh& mesh, fbx.meshes) {
|
foreach (const FBXMesh& mesh, fbx.meshes) {
|
||||||
vUtil.fattenMeshes(mesh, result, meshPartCount, startMeshIndex, endMeshIndex);
|
vUtil.fattenMesh(mesh, fbx.offset, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
newFbx.meshes.append(result);
|
newFbx.meshes.append(result);
|
||||||
|
|
|
@ -15,12 +15,25 @@
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
|
||||||
|
#include <FBXReader.h>
|
||||||
|
|
||||||
|
const int VHACD_RETURN_CODE_FAILURE_TO_READ = 1;
|
||||||
|
const int VHACD_RETURN_CODE_FAILURE_TO_WRITE = 2;
|
||||||
|
const int VHACD_RETURN_CODE_FAILURE_TO_CONVEXIFY = 3;
|
||||||
|
|
||||||
|
|
||||||
class VHACDUtilApp : public QCoreApplication {
|
class VHACDUtilApp : public QCoreApplication {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
VHACDUtilApp(int argc, char* argv[]);
|
VHACDUtilApp(int argc, char* argv[]);
|
||||||
~VHACDUtilApp();
|
~VHACDUtilApp();
|
||||||
|
|
||||||
|
bool writeOBJ(QString outFileName, FBXGeometry& geometry, bool outputCentimeters, int whichMeshPart = -1);
|
||||||
|
|
||||||
|
int getReturnCode() const { return _returnCode; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int _returnCode { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -23,5 +23,5 @@ using namespace VHACD;
|
||||||
|
|
||||||
int main(int argc, char * argv[]) {
|
int main(int argc, char * argv[]) {
|
||||||
VHACDUtilApp app(argc, argv);
|
VHACDUtilApp app(argc, argv);
|
||||||
return 0;
|
return app.getReturnCode();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue