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

This commit is contained in:
Atlante45 2015-03-11 14:09:32 +01:00
commit a949af3296
101 changed files with 4476 additions and 900 deletions

View file

@ -37,7 +37,7 @@ macro(COPY_DLLS_BESIDE_WINDOWS_EXECUTABLE)
add_custom_command(
TARGET ${TARGET_NAME}
POST_BUILD
COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} --no-libraries $<TARGET_FILE:${TARGET_NAME}>"
COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} $<TARGET_FILE:${TARGET_NAME}>"
)
endif ()
endmacro()

View file

@ -48,7 +48,7 @@ var rightHandEntity = false;
var newSound = SoundCache.getSound("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/throw.raw");
var catchSound = SoundCache.getSound("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/catch.raw");
var throwSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Switches%20and%20sliders/slider%20-%20whoosh1.raw");
var targetRadius = 1.0;
var targetRadius = 0.25;
var wantDebugging = false;
@ -120,30 +120,33 @@ function checkControllerSide(whichSide) {
var closestEntity = Entities.findClosestEntity(palmPosition, targetRadius);
if (closestEntity.isKnownID) {
var foundProperties = Entities.getEntityProperties(closestEntity);
if (Vec3.length(foundProperties.velocity) > 0.0) {
debugPrint(handMessage + " HAND- CAUGHT SOMETHING!!");
debugPrint(handMessage + " - Catching a moving object!");
if (whichSide == LEFT_PALM) {
leftBallAlreadyInHand = true;
leftHandEntity = closestEntity;
} else {
rightBallAlreadyInHand = true;
rightHandEntity = closestEntity;
if (whichSide == LEFT_PALM) {
leftBallAlreadyInHand = true;
leftHandEntity = closestEntity;
} else {
rightBallAlreadyInHand = true;
rightHandEntity = closestEntity;
}
var ballPosition = getBallHoldPosition(whichSide);
var properties = { position: { x: ballPosition.x,
y: ballPosition.y,
z: ballPosition.z },
rotation: palmRotation,
color: HELD_COLOR,
velocity : { x: 0, y: 0, z: 0},
gravity: { x: 0, y: 0, z: 0}
};
Entities.editEntity(closestEntity, properties);
Audio.playSound(catchSound, { position: ballPosition });
return; // exit early
}
var ballPosition = getBallHoldPosition(whichSide);
var properties = { position: { x: ballPosition.x,
y: ballPosition.y,
z: ballPosition.z },
rotation: palmRotation,
color: HELD_COLOR,
velocity : { x: 0, y: 0, z: 0},
gravity: { x: 0, y: 0, z: 0}
};
Entities.editEntity(closestEntity, properties);
Audio.playSound(catchSound, { position: ballPosition });
return; // exit early
}
}

View file

@ -86,7 +86,7 @@ keyboard.onKeyRelease = function(event) {
var textLines = textText.split("\n");
var maxLineWidth = Overlays.textSize(textSizeMeasureOverlay, textText).width;
var usernameLine = "--" + GlobalServices.myUsername;
var usernameLine = "--" + GlobalServices.username;
var usernameWidth = Overlays.textSize(textSizeMeasureOverlay, usernameLine).width;
if (maxLineWidth < usernameWidth) {
maxLineWidth = usernameWidth;

View file

@ -385,7 +385,7 @@ var toolBar = (function () {
} else if (browseModelsButtonDown) {
var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
if (browseModelsButton === toolBar.clicked(clickedOverlay)) {
url = Window.s3Browse(".*(fbx|FBX)");
url = Window.s3Browse(".*(fbx|FBX|obj|OBJ)");
if (url !== null && url !== "") {
addModel(url);
}
@ -699,7 +699,7 @@ function setupModelMenus() {
Menu.addMenuItem({ menuName: "File", menuItemName: "Models", isSeparator: true, beforeItem: "Settings" });
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Entities", shortcutKey: "CTRL+META+E", afterItem: "Models" });
Menu.addMenuItem({ menuName: "File", menuItemName: "Import Entities", shortcutKey: "CTRL+META+I", afterItem: "Export Entities" });
Menu.addMenuItem({ menuName: "File", menuItemName: "Import Entities from URL", shortcutKey: "CTRL+META+U", afterItem: "Import Entities" });
Menu.addMenuItem({ menuName: "View", menuItemName: MENU_AUTO_FOCUS_ON_SELECT, afterItem: MENU_INSPECT_TOOL_ENABLED,
isCheckable: true, isChecked: Settings.getValue(SETTING_AUTO_FOCUS_ON_SELECT) == "true" });
@ -726,6 +726,7 @@ function cleanupModelMenus() {
Menu.removeSeparator("File", "Models");
Menu.removeMenuItem("File", "Export Entities");
Menu.removeMenuItem("File", "Import Entities");
Menu.removeMenuItem("File", "Import Entities from URL");
Menu.removeMenuItem("View", MENU_INSPECT_TOOL_ENABLED);
Menu.removeMenuItem("View", MENU_AUTO_FOCUS_ON_SELECT);
@ -795,10 +796,17 @@ function handeMenuEvent(menuItem) {
}
}
}
} else if (menuItem == "Import Entities") {
var filename = Window.browse("Select models to import", "", "*.svo")
if (filename) {
var success = Clipboard.importEntities(filename);
} else if (menuItem == "Import Entities" || menuItem == "Import Entities from URL") {
var importURL;
if (menuItem == "Import Entities") {
importURL = Window.browse("Select models to import", "", "*.svo");
} else {
importURL = Window.prompt("URL of SVO to import", "");
}
if (importURL) {
var success = Clipboard.importEntities(importURL);
if (success) {
var distance = cameraManager.enabled ? cameraManager.zoomDistance : DEFAULT_ENTITY_DRAG_DROP_DISTANCE;

View file

@ -158,6 +158,7 @@
var elModelSections = document.querySelectorAll(".model-section");
var elModelURL = document.getElementById("property-model-url");
var elCollisionModelURL = document.getElementById("property-collision-model-url");
var elModelAnimationURL = document.getElementById("property-model-animation-url");
var elModelAnimationPlaying = document.getElementById("property-model-animation-playing");
var elModelAnimationFPS = document.getElementById("property-model-animation-fps");
@ -287,6 +288,7 @@
}
elModelURL.value = properties.modelURL;
elCollisionModelURL.value = properties.collisionModelURL;
elModelAnimationURL.value = properties.animationURL;
elModelAnimationPlaying.checked = properties.animationIsPlaying;
elModelAnimationFPS.value = properties.animationFPS;
@ -411,6 +413,7 @@
elLightCutoff.addEventListener('change', createEmitNumberPropertyUpdateFunction('cutoff'));
elModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('modelURL'));
elCollisionModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('collisionModelURL'));
elModelAnimationURL.addEventListener('change', createEmitTextPropertyUpdateFunction('animationURL'));
elModelAnimationPlaying.addEventListener('change', createEmitCheckedPropertyUpdateFunction('animationIsPlaying'));
elModelAnimationFPS.addEventListener('change', createEmitNumberPropertyUpdateFunction('animationFPS'));
@ -642,6 +645,12 @@
<input type="text" id="property-model-url" class="url"></input>
</div>
</div>
<div class="model-section property">
<div class="label">Collision Model URL</div>
<div class="value">
<input type="text" id="property-collision-model-url" class="url"></input>
</div>
</div>
<div class="model-section property">
<div class="label">Animation URL</div>
<div class="value">

View file

@ -53,6 +53,7 @@ function Tooltip() {
text += "ID: " + properties.id + "\n"
if (properties.type == "Model") {
text += "Model URL: " + properties.modelURL + "\n"
text += "Collision Model URL: " + properties.collisionModelURL + "\n"
text += "Animation URL: " + properties.animationURL + "\n"
text += "Animation is playing: " + properties.animationIsPlaying + "\n"
if (properties.sittingPoints && properties.sittingPoints.length > 0) {

View file

@ -52,6 +52,8 @@ EntityPropertyDialogBox = (function () {
if (properties.type == "Model") {
array.push({ label: "Model URL:", value: properties.modelURL });
index++;
array.push({ label: "Collision Model URL:", value: properties.collisionModelURL });
index++;
array.push({ label: "Animation URL:", value: properties.animationURL });
index++;
array.push({ label: "Animation is playing:", type: "checkbox", value: properties.animationIsPlaying });
@ -275,6 +277,7 @@ EntityPropertyDialogBox = (function () {
properties.locked = array[index++].value;
if (properties.type == "Model") {
properties.modelURL = array[index++].value;
properties.collisionModelURL = array[index++].value;
properties.animationURL = array[index++].value;
var newAnimationIsPlaying = array[index++].value;

View file

@ -38,6 +38,13 @@ function touchUpdateEvent(event) {
if (wantDebugging) {
print("touchUpdateEvent event.x,y=" + event.x + ", " + event.y);
}
if (!startedTouching) {
// handle Qt 5.4.x bug where we get touch update without a touch begin event
startedTouching = true;
lastX = event.x;
lastY = event.y;
}
var MOUSE_YAW_SCALE = -0.25;
var MOUSE_PITCH_SCALE = -12.5;

View file

@ -20,11 +20,11 @@ var trailingLoudness = 0.0;
var soundClip = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/Tabla+Loops/Tabla1.wav");
var properties = {
type: "Box",
position: orbitCenter,
dimensions: { x: 0.25, y: 0.25, z: 0.25 },
color: { red: 100, green: 0, blue : 0 }
};
type: "Box",
position: orbitCenter,
dimensions: { x: 0.25, y: 0.25, z: 0.25 },
color: { red: 100, green: 0, blue : 0 }
};
var objectId = Entities.addEntity(properties);
var sound = Audio.playSound(soundClip, { position: orbitCenter, loop: true, volume: 0.5 });
@ -34,7 +34,7 @@ function update(deltaTime) {
currentPosition = { x: orbitCenter.x + Math.cos(time * SPEED) * RADIUS, y: orbitCenter.y, z: orbitCenter.z + Math.sin(time * SPEED) * RADIUS };
trailingLoudness = 0.9 * trailingLoudness + 0.1 * sound.loudness;
Entities.editEntity( objectId, { position: currentPosition, color: { red: Math.min(trailingLoudness * 2000, 255), green: 0, blue: 0 } } );
sound.setOptions({ position: currentPosition });
sound.options = { position: currentPosition };
}
Script.scriptEnding.connect(function() {

View file

@ -87,6 +87,7 @@
#include "Application.h"
#include "AudioClient.h"
#include "DiscoverabilityManager.h"
#include "InterfaceVersion.h"
#include "LODManager.h"
#include "Menu.h"
@ -246,6 +247,7 @@ bool setupEssentials(int& argc, char** argv) {
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
auto speechRecognizer = DependencyManager::set<SpeechRecognizer>();
#endif
auto discoverabilityManager = DependencyManager::set<DiscoverabilityManager>();
return true;
}
@ -367,11 +369,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
connect(&domainHandler, &DomainHandler::hostnameChanged,
DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress);
// update our location every 5 seconds in the data-server, assuming that we are authenticated with one
// update our location every 5 seconds in the metaverse server, assuming that we are authenticated with one
const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000;
locationUpdateTimer = new QTimer(this);
connect(locationUpdateTimer, &QTimer::timeout, this, &Application::updateLocationInServer);
auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
connect(locationUpdateTimer, &QTimer::timeout, discoverabilityManager.data(), &DiscoverabilityManager::updateLocation);
locationUpdateTimer->start(DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS);
connect(nodeList.data(), &NodeList::nodeAdded, this, &Application::nodeAdded);
@ -459,6 +462,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
// enable mouse tracking; otherwise, we only get drag events
_glWidget->setMouseTracking(true);
_fullscreenMenuWidget->setParent(_glWidget);
_menuBarHeight = Menu::getInstance()->height();
if (Menu::getInstance()->isOptionChecked(MenuOption::Fullscreen)) {
setFullscreen(true); // Initialize menu bar show/hide
}
_toolWindow = new ToolWindow();
_toolWindow->setWindowFlags(_toolWindow->windowFlags() | Qt::WindowStaysOnTopHint);
_toolWindow->setWindowTitle("Tools");
@ -599,7 +608,10 @@ Application::~Application() {
_myAvatar = NULL;
ModelEntityItem::cleanupLoadedAnimations() ;
ModelEntityItem::cleanupLoadedAnimations();
// stop the glWidget frame timer so it doesn't call paintGL
_glWidget->stopFrameTimer();
DependencyManager::destroy<AnimationCache>();
DependencyManager::destroy<TextureCache>();
@ -611,7 +623,7 @@ Application::~Application() {
}
void Application::initializeGL() {
qDebug( "Created Display Window.");
qDebug() << "Created Display Window.";
// initialize glut for shape drawing; Qt apparently initializes it on OS X
#ifndef __APPLE__
@ -1143,6 +1155,10 @@ void Application::keyPressEvent(QKeyEvent* event) {
break;
}
case Qt::Key_Comma: {
_myAvatar->togglePhysicsEnabled();
}
default:
event->ignore();
@ -1258,6 +1274,18 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
return;
}
if (Menu::getInstance()->isOptionChecked(MenuOption::Fullscreen)
&& !Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode)) {
// Show/hide menu bar in fullscreen
if (event->globalY() > _menuBarHeight) {
_fullscreenMenuWidget->setFixedHeight(0);
Menu::getInstance()->setFixedHeight(0);
} else {
_fullscreenMenuWidget->setFixedHeight(_menuBarHeight);
Menu::getInstance()->setFixedHeight(_menuBarHeight);
}
}
_entities.mouseMoveEvent(event, deviceID);
_controllerScriptingInterface.emitMouseMoveEvent(event, deviceID); // send events to any registered scripts
@ -1523,14 +1551,47 @@ void Application::setFullscreen(bool fullscreen) {
if (Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode)) {
if (fullscreen) {
// Menu show() after hide() doesn't work with Rift VR display so set height instead.
// Menu hide() disables menu commands, and show() after hide() doesn't work with Rift VR display.
// So set height instead.
_window->menuBar()->setMaximumHeight(0);
} else {
_window->menuBar()->setMaximumHeight(QWIDGETSIZE_MAX);
}
} else {
if (fullscreen) {
// Move menu to a QWidget floating above _glWidget so that show/hide doesn't adjust viewport.
_menuBarHeight = Menu::getInstance()->height();
Menu::getInstance()->setParent(_fullscreenMenuWidget);
Menu::getInstance()->setFixedWidth(_window->windowHandle()->screen()->size().width());
_fullscreenMenuWidget->show();
} else {
// Restore menu to being part of MainWindow.
_fullscreenMenuWidget->hide();
_window->setMenuBar(Menu::getInstance());
_window->menuBar()->setMaximumHeight(QWIDGETSIZE_MAX);
}
}
_window->setWindowState(fullscreen ? (_window->windowState() | Qt::WindowFullScreen) :
(_window->windowState() & ~Qt::WindowFullScreen));
// Work around Qt bug that prevents floating menus being shown when in fullscreen mode.
// https://bugreports.qt.io/browse/QTBUG-41883
// Known issue: Top-level menu items don't highlight when cursor hovers. This is probably a side-effect of the work-around.
// TODO: Remove this work-around once the bug has been fixed and restore the following lines.
//_window->setWindowState(fullscreen ? (_window->windowState() | Qt::WindowFullScreen) :
// (_window->windowState() & ~Qt::WindowFullScreen));
_window->hide();
if (fullscreen) {
_window->setWindowState(_window->windowState() | Qt::WindowFullScreen);
// The next line produces the following warning in the log:
// [WARNING][03 / 06 12:17 : 58] QWidget::setMinimumSize: (/ MainWindow) Negative sizes
// (0, -1) are not possible
// This is better than the alternative which is to have the window slightly less than fullscreen with a visible line
// of pixels for the remainder of the screen.
_window->setContentsMargins(0, 0, 0, -1);
} else {
_window->setWindowState(_window->windowState() & ~Qt::WindowFullScreen);
_window->setContentsMargins(0, 0, 0, 0);
}
if (!_aboutToQuit) {
_window->show();
}
@ -1714,9 +1775,17 @@ void Application::saveSettings() {
_myAvatar->saveData();
}
bool Application::importEntities(const QString& filename) {
bool Application::importEntities(const QString& urlOrFilename) {
_entityClipboard.eraseAllOctreeElements();
bool success = _entityClipboard.readFromSVOFile(filename.toLocal8Bit().constData());
QUrl url(urlOrFilename);
// if the URL appears to be invalid or relative, then it is probably a local file
if (!url.isValid() || url.isRelative()) {
url = QUrl::fromLocalFile(urlOrFilename);
}
bool success = _entityClipboard.readFromSVOURL(url.toString());
if (success) {
_entityClipboard.reaverageOctreeElements();
}
@ -1807,6 +1876,9 @@ void Application::init() {
tree->setSimulation(&_physicsEngine);
_physicsEngine.init(&_entityEditSender);
_physicsEngine.setAvatarData(_myAvatar);
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
connect(&_physicsEngine, &EntitySimulation::entityCollisionWithEntity,
@ -2869,6 +2941,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs
DependencyManager::get<DeferredLightingEffect>()->setAmbientLightMode(getRenderAmbientLight());
auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();
DependencyManager::get<DeferredLightingEffect>()->setGlobalLight(skyStage->getSunLight()->getDirection(), skyStage->getSunLight()->getColor(), skyStage->getSunLight()->getIntensity());
DependencyManager::get<DeferredLightingEffect>()->setGlobalAtmosphere(skyStage->getAtmosphere());
PROFILE_RANGE("DeferredLighting");
PerformanceTimer perfTimer("lighting");
@ -3184,45 +3257,6 @@ void Application::updateWindowTitle(){
_window->setWindowTitle(title);
}
void Application::updateLocationInServer() {
AccountManager& accountManager = AccountManager::getInstance();
auto addressManager = DependencyManager::get<AddressManager>();
DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
if (accountManager.isLoggedIn() && domainHandler.isConnected()
&& (!addressManager->getRootPlaceID().isNull() || !domainHandler.getUUID().isNull())) {
// construct a QJsonObject given the user's current address information
QJsonObject rootObject;
QJsonObject locationObject;
QString pathString = addressManager->currentPath();
const QString LOCATION_KEY_IN_ROOT = "location";
const QString PATH_KEY_IN_LOCATION = "path";
locationObject.insert(PATH_KEY_IN_LOCATION, pathString);
if (!addressManager->getRootPlaceID().isNull()) {
const QString PLACE_ID_KEY_IN_LOCATION = "place_id";
locationObject.insert(PLACE_ID_KEY_IN_LOCATION,
uuidStringWithoutCurlyBraces(addressManager->getRootPlaceID()));
} else {
const QString DOMAIN_ID_KEY_IN_LOCATION = "domain_id";
locationObject.insert(DOMAIN_ID_KEY_IN_LOCATION,
uuidStringWithoutCurlyBraces(domainHandler.getUUID()));
}
rootObject.insert(LOCATION_KEY_IN_ROOT, locationObject);
accountManager.authenticatedRequest("/api/v1/user/location", QNetworkAccessManager::PutOperation,
JSONCallbackParameters(), QJsonDocument(rootObject).toJson());
}
}
void Application::clearDomainOctreeDetails() {
qDebug() << "Clearing domain octree details...";
// reset the environment so that we don't erroneously end up with multiple
@ -3908,8 +3942,8 @@ void Application::takeSnapshot() {
}
void Application::setVSyncEnabled() {
bool vsyncOn = Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerateVSyncOn);
#if defined(Q_OS_WIN)
bool vsyncOn = Menu::getInstance()->isOptionChecked(MenuOption::RenderTargetFramerateVSyncOn);
if (wglewGetExtension("WGL_EXT_swap_control")) {
wglSwapIntervalEXT(vsyncOn);
int swapInterval = wglGetSwapIntervalEXT();
@ -3932,7 +3966,6 @@ void Application::setVSyncEnabled() {
#else
qDebug("V-Sync is FORCED ON on this system\n");
#endif
vsyncOn = true; // Turns off unused variable warning
}
bool Application::isVSyncOn() const {

View file

@ -318,7 +318,6 @@ signals:
public slots:
void domainChanged(const QString& domainHostname);
void updateWindowTitle();
void updateLocationInServer();
void nodeAdded(SharedNodePointer node);
void nodeKilled(SharedNodePointer node);
void packetSent(quint64 length);
@ -326,7 +325,7 @@ public slots:
QVector<EntityItemID> pasteEntities(float x, float y, float z);
bool exportEntities(const QString& filename, const QVector<EntityItemID>& entityIDs);
bool exportEntities(const QString& filename, float x, float y, float z, float scale);
bool importEntities(const QString& filename);
bool importEntities(const QString& url);
void setLowVelocityFilter(bool lowVelocityFilter);
void loadDialog();
@ -587,6 +586,9 @@ private:
GLCanvas* _glWidget = new GLCanvas(); // our GLCanvas has a couple extra features
void checkSkeleton();
QWidget* _fullscreenMenuWidget = new QWidget();
int _menuBarHeight;
};
#endif // hifi_Application_h

View file

@ -0,0 +1,88 @@
//
// DiscoverabilityManager.cpp
// interface/src
//
// Created by Stephen Birarda on 2015-03-09.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QtCore/QJsonDocument>
#include <AccountManager.h>
#include <AddressManager.h>
#include <DomainHandler.h>
#include <NodeList.h>
#include <UUID.h>
#include "DiscoverabilityManager.h"
const Discoverability::Mode DEFAULT_DISCOVERABILITY_MODE = Discoverability::All;
DiscoverabilityManager::DiscoverabilityManager() :
_mode("discoverabilityMode", DEFAULT_DISCOVERABILITY_MODE)
{
}
const QString API_USER_LOCATION_PATH = "/api/v1/user/location";
void DiscoverabilityManager::updateLocation() {
if (_mode.get() != Discoverability::None) {
AccountManager& accountManager = AccountManager::getInstance();
auto addressManager = DependencyManager::get<AddressManager>();
DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
if (accountManager.isLoggedIn() && domainHandler.isConnected()
&& (!addressManager->getRootPlaceID().isNull() || !domainHandler.getUUID().isNull())) {
// construct a QJsonObject given the user's current address information
QJsonObject rootObject;
QJsonObject locationObject;
QString pathString = addressManager->currentPath();
const QString LOCATION_KEY_IN_ROOT = "location";
const QString PATH_KEY_IN_LOCATION = "path";
locationObject.insert(PATH_KEY_IN_LOCATION, pathString);
if (!addressManager->getRootPlaceID().isNull()) {
const QString PLACE_ID_KEY_IN_LOCATION = "place_id";
locationObject.insert(PLACE_ID_KEY_IN_LOCATION,
uuidStringWithoutCurlyBraces(addressManager->getRootPlaceID()));
} else {
const QString DOMAIN_ID_KEY_IN_LOCATION = "domain_id";
locationObject.insert(DOMAIN_ID_KEY_IN_LOCATION,
uuidStringWithoutCurlyBraces(domainHandler.getUUID()));
}
rootObject.insert(LOCATION_KEY_IN_ROOT, locationObject);
accountManager.authenticatedRequest(API_USER_LOCATION_PATH, QNetworkAccessManager::PutOperation,
JSONCallbackParameters(), QJsonDocument(rootObject).toJson());
}
}
}
void DiscoverabilityManager::removeLocation() {
AccountManager& accountManager = AccountManager::getInstance();
accountManager.authenticatedRequest(API_USER_LOCATION_PATH, QNetworkAccessManager::DeleteOperation);
}
void DiscoverabilityManager::setDiscoverabilityMode(Discoverability::Mode discoverabilityMode) {
if (static_cast<Discoverability::Mode>(_mode.get()) != discoverabilityMode) {
// update the setting to the new value
_mode.set(static_cast<int>(discoverabilityMode));
if (static_cast<int>(_mode.get()) == Discoverability::None) {
// if we just got set to no discoverability, make sure that we delete our location in DB
removeLocation();
}
}
}

View file

@ -0,0 +1,45 @@
//
// DiscoverabilityManager.h
// interface/src
//
// Created by Stephen Birarda on 2015-03-09.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_DiscoverabilityManager_h
#define hifi_DiscoverabilityManager_h
#include <DependencyManager.h>
#include <SettingHandle.h>
namespace Discoverability {
enum Mode {
None,
Friends,
All
};
}
Q_DECLARE_METATYPE(Discoverability::Mode);
class DiscoverabilityManager : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
public slots:
void updateLocation();
void removeLocation();
Discoverability::Mode getDiscoverabilityMode() { return static_cast<Discoverability::Mode>(_mode.get()); }
void setDiscoverabilityMode(Discoverability::Mode discoverabilityMode);
private:
DiscoverabilityManager();
Setting::Handle<int> _mode;
};
#endif // hifi_DiscoverabilityManager_h

View file

@ -31,6 +31,10 @@ GLCanvas::GLCanvas() : QGLWidget(QGL::NoDepthBuffer | QGL::NoStencilBuffer),
#endif
}
void GLCanvas::stopFrameTimer() {
_frameTimer.stop();
}
bool GLCanvas::isThrottleRendering() const {
return _throttleRendering || Application::getInstance()->getWindow()->isMinimized();
}

View file

@ -22,6 +22,8 @@ class GLCanvas : public QGLWidget {
public:
GLCanvas();
void stopFrameTimer();
bool isThrottleRendering() const;

View file

@ -332,16 +332,12 @@ void ModelUploader::populateBasicMapping(QVariantHash& mapping, QString filename
// mixamo blendshapes - in the event that a mixamo file was edited by some other tool, it's likely the applicationName will
// be rewritten, so we detect the existence of several different blendshapes which indicate we're likely a mixamo file
bool likelyMixamoFile = geometry.applicationName == "mixamo.com" ||
(geometry.blendshapeChannelNames.contains("Facial_Blends") &&
geometry.blendshapeChannelNames.contains("BrowsDown_Right") &&
(geometry.blendshapeChannelNames.contains("BrowsDown_Right") &&
geometry.blendshapeChannelNames.contains("MouthOpen") &&
geometry.blendshapeChannelNames.contains("Blink_Left") &&
geometry.blendshapeChannelNames.contains("Blink_Right") &&
geometry.blendshapeChannelNames.contains("Squint_Right"));
qDebug() << "likelyMixamoFile:" << likelyMixamoFile;
qDebug() << "geometry.blendshapeChannelNames:" << geometry.blendshapeChannelNames;
if (!mapping.contains(BLENDSHAPE_FIELD) && likelyMixamoFile) {
QVariantHash blendshapes;
blendshapes.insertMulti("BrowsD_L", QVariantList() << "BrowsDown_Left" << 1.0);

View file

@ -63,7 +63,6 @@ Avatar::Avatar() :
_skeletonModel(this),
_skeletonOffset(0.0f),
_bodyYawDelta(0.0f),
_velocity(0.0f),
_positionDeltaAccumulator(0.0f),
_lastVelocity(0.0f),
_acceleration(0.0f),

View file

@ -155,7 +155,6 @@ public:
Q_INVOKABLE glm::vec3 getNeckPosition() const;
Q_INVOKABLE glm::vec3 getVelocity() const { return _velocity; }
Q_INVOKABLE glm::vec3 getAcceleration() const { return _acceleration; }
Q_INVOKABLE glm::vec3 getAngularVelocity() const { return _angularVelocity; }
Q_INVOKABLE glm::vec3 getAngularAcceleration() const { return _angularAcceleration; }
@ -184,11 +183,9 @@ protected:
QVector<Model*> _attachmentModels;
float _bodyYawDelta;
glm::vec3 _velocity;
// These position histories and derivatives are in the world-frame.
// The derivatives are the MEASURED results of all external and internal forces
// and are therefor READ-ONLY --> motion control of the Avatar is NOT obtained
// and are therefore READ-ONLY --> motion control of the Avatar is NOT obtained
// by setting these values.
// Floating point error prevents us from accurately measuring velocity using a naive approach
// (e.g. vel = (pos - lastPos)/dt) so instead we use _positionDeltaAccumulator.

View file

@ -181,7 +181,11 @@ void MyAvatar::simulate(float deltaTime) {
{
PerformanceTimer perfTimer("transform");
updateOrientation(deltaTime);
updatePosition(deltaTime);
if (isPhysicsEnabled()) {
updatePositionWithPhysics(deltaTime);
} else {
updatePosition(deltaTime);
}
}
{
@ -1397,6 +1401,25 @@ void MyAvatar::updatePosition(float deltaTime) {
measureMotionDerivatives(deltaTime);
}
void MyAvatar::updatePositionWithPhysics(float deltaTime) {
// rotate velocity into camera frame
glm::quat rotation = getHead()->getCameraOrientation();
glm::vec3 localVelocity = glm::inverse(rotation) * _velocity;
bool hasFloor = false;
glm::vec3 newLocalVelocity = applyKeyboardMotor(deltaTime, localVelocity, hasFloor);
newLocalVelocity = applyScriptedMotor(deltaTime, newLocalVelocity);
// cap avatar speed
float speed = glm::length(newLocalVelocity);
if (speed > MAX_WALKING_SPEED) {
newLocalVelocity *= MAX_WALKING_SPEED / speed;
}
// rotate back into world-frame
_velocity = rotation * newLocalVelocity;
}
void MyAvatar::updateCollisionWithEnvironment(float deltaTime, float radius) {
glm::vec3 up = getBodyUpDirection();
const float ENVIRONMENT_SURFACE_ELASTICITY = 0.0f;

View file

@ -164,8 +164,6 @@ public slots:
glm::vec3 getThrust() { return _thrust; };
void setThrust(glm::vec3 newThrust) { _thrust = newThrust; }
void setVelocity(const glm::vec3 velocity) { _velocity = velocity; }
void updateMotionBehavior();
void onToggleRagdoll();
@ -233,6 +231,7 @@ private:
glm::vec3 applyKeyboardMotor(float deltaTime, const glm::vec3& velocity, bool walkingOnFloor);
glm::vec3 applyScriptedMotor(float deltaTime, const glm::vec3& velocity);
void updatePosition(float deltaTime);
void updatePositionWithPhysics(float deltaTime);
void updateCollisionWithAvatars(float deltaTime);
void updateCollisionWithEnvironment(float deltaTime, float radius);
void updateCollisionWithVoxels(float deltaTime, float radius);

View file

@ -11,6 +11,7 @@
#include "AccountManager.h"
#include "Application.h"
#include "DiscoverabilityManager.h"
#include "ResourceCache.h"
#include "GlobalServicesScriptingInterface.h"
@ -36,7 +37,7 @@ GlobalServicesScriptingInterface* GlobalServicesScriptingInterface::getInstance(
return &sharedInstance;
}
QString GlobalServicesScriptingInterface::getMyUsername() {
const QString& GlobalServicesScriptingInterface::getUsername() const {
return AccountManager::getInstance().getAccountInfo().getUsername();
}
@ -44,6 +45,32 @@ void GlobalServicesScriptingInterface::loggedOut() {
emit GlobalServicesScriptingInterface::disconnected(QString("logout"));
}
QString GlobalServicesScriptingInterface::getFindableBy() const {
auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
if (discoverabilityManager->getDiscoverabilityMode() == Discoverability::None) {
return "none";
} else if (discoverabilityManager->getDiscoverabilityMode() == Discoverability::Friends) {
return "friends";
} else {
return "all";
}
}
void GlobalServicesScriptingInterface::setFindableBy(const QString& discoverabilityMode) {
auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();
if (discoverabilityMode.toLower() == "none") {
discoverabilityManager->setDiscoverabilityMode(Discoverability::None);
} else if (discoverabilityMode.toLower() == "friends") {
discoverabilityManager->setDiscoverabilityMode(Discoverability::Friends);
} else if (discoverabilityMode.toLower() == "all") {
discoverabilityManager->setDiscoverabilityMode(Discoverability::All);
} else {
qDebug() << "GlobalServices setFindableBy called with an unrecognized value. Did not change discoverability.";
}
}
DownloadInfoResult::DownloadInfoResult() :
downloading(QList<float>()),
pending(0.0f)

View file

@ -31,16 +31,16 @@ Q_DECLARE_METATYPE(DownloadInfoResult)
QScriptValue DownloadInfoResultToScriptValue(QScriptEngine* engine, const DownloadInfoResult& result);
void DownloadInfoResultFromScriptValue(const QScriptValue& object, DownloadInfoResult& result);
class GlobalServicesScriptingInterface : public QObject {
Q_OBJECT
Q_PROPERTY(QString myUsername READ getMyUsername)
GlobalServicesScriptingInterface();
~GlobalServicesScriptingInterface();
Q_PROPERTY(QString username READ getUsername)
Q_PROPERTY(QString findableBy READ getFindableBy WRITE setFindableBy)
public:
static GlobalServicesScriptingInterface* getInstance();
QString getMyUsername();
const QString& getUsername() const;
public slots:
DownloadInfoResult getDownloadInfo();
@ -49,16 +49,20 @@ public slots:
private slots:
void loggedOut();
void checkDownloadInfo();
QString getFindableBy() const;
void setFindableBy(const QString& discoverabilityMode);
signals:
void connected();
void disconnected(const QString& reason);
void incomingMessage(const QString& username, const QString& message);
void onlineUsersChanged(const QStringList& usernames);
void myUsernameChanged(const QString& username);
void downloadInfoChanged(DownloadInfoResult info);
private:
GlobalServicesScriptingInterface();
~GlobalServicesScriptingInterface();
bool _downloading;
};

View file

@ -58,6 +58,7 @@ void LoginDialog::reset() {
_ui->emailLineEdit->setFocus();
_ui->logoLabel->setPixmap(QPixmap(PathUtils::resourcesPath() + "images/hifi-logo.svg"));
_ui->loginButton->setIcon(QIcon(PathUtils::resourcesPath() + "images/login.svg"));
_ui->closeButton->setIcon(QIcon(PathUtils::resourcesPath() + "images/close.svg"));
_ui->infoLabel->setVisible(false);
_ui->errorLabel->setVisible(false);

View file

@ -320,6 +320,7 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice,
#ifdef Q_OS_ANDROID
adjustedAudioFormat.setSampleRate(FORTY_FOUR);
#else
const int HALF_FORTY_FOUR = FORTY_FOUR / 2;
if (audioDevice.supportedSampleRates().contains(AudioConstants::SAMPLE_RATE * 2)) {
@ -397,18 +398,24 @@ soxr_error_t possibleResampling(soxr_t resampler,
numSourceSamples,
sourceAudioFormat, destinationAudioFormat);
qDebug() << "resample from" << sourceAudioFormat << "to" << destinationAudioFormat
<< "from" << numChannelCoversionSamples << "to" << numDestinationSamples;
resampleError = soxr_process(resampler,
channelConversionSamples, numChannelCoversionSamples, NULL,
destinationSamples, numDestinationSamples, NULL);
delete[] channelConversionSamples;
} else {
unsigned int numAdjustedSourceSamples = numSourceSamples;
unsigned int numAdjustedDestinationSamples = numDestinationSamples;
if (sourceAudioFormat.channelCount() == 2 && destinationAudioFormat.channelCount() == 2) {
numAdjustedSourceSamples /= 2;
numAdjustedDestinationSamples /= 2;
}
resampleError = soxr_process(resampler,
sourceSamples, numSourceSamples, NULL,
destinationSamples, numDestinationSamples, NULL);
sourceSamples, numAdjustedSourceSamples, NULL,
destinationSamples, numAdjustedDestinationSamples, NULL);
}
return resampleError;
@ -429,9 +436,12 @@ soxr_t soxrResamplerFromInputFormatToOutputFormat(const QAudioFormat& sourceAudi
// setup soxr_quality_spec_t for quality options
soxr_quality_spec_t qualitySpec = soxr_quality_spec(SOXR_MQ, 0);
int channelCount = (sourceAudioFormat.channelCount() == 2 && destinationAudioFormat.channelCount() == 2)
? 2 : 1;
soxr_t newResampler = soxr_create(sourceAudioFormat.sampleRate(),
destinationAudioFormat.sampleRate(),
1,
channelCount,
&soxrError, &inputToNetworkSpec, &qualitySpec, 0);
if (soxrError) {
@ -1136,7 +1146,7 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice
if (!outputDeviceInfo.isNull()) {
qDebug() << "The audio output device " << outputDeviceInfo.deviceName() << "is available.";
_outputAudioDeviceName = outputDeviceInfo.deviceName().trimmed();
if (adjustedFormatForAudioDevice(outputDeviceInfo, _desiredOutputFormat, _outputFormat)) {
qDebug() << "The format to be used for audio output is" << _outputFormat;

View file

@ -30,6 +30,8 @@ class AbstractAudioInterface;
class AudioInjector : public QObject {
Q_OBJECT
Q_PROPERTY(AudioInjectorOptions options WRITE setOptions READ getOptions)
public:
AudioInjector(QObject* parent);
AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions);
@ -51,7 +53,9 @@ public slots:
void triggerDeleteAfterFinish() { _shouldDeleteAfterFinish = true; }
void stopAndDeleteLater();
void setOptions(AudioInjectorOptions& options) { _options = options; }
const AudioInjectorOptions& getOptions() const { return _options; }
void setOptions(const AudioInjectorOptions& options) { _options = options; }
void setCurrentSendPosition(int currentSendPosition) { _currentSendPosition = currentSendPosition; }
float getLoudness() const { return _loudness; }
bool isPlaying() const { return !_isFinished; }

View file

@ -94,30 +94,41 @@ void Sound::downloadFinished(QNetworkReply* reply) {
void Sound::downSample(const QByteArray& rawAudioByteArray) {
// assume that this was a RAW file and is now an array of samples that are
// signed, 16-bit, 48Khz, mono
// signed, 16-bit, 48Khz
// we want to convert it to the format that the audio-mixer wants
// which is signed, 16-bit, 24Khz, mono
_byteArray.resize(rawAudioByteArray.size() / 2);
// which is signed, 16-bit, 24Khz
int numSourceSamples = rawAudioByteArray.size() / sizeof(int16_t);
int numDestinationBytes = rawAudioByteArray.size() / 2;
if (_isStereo && numSourceSamples % 4 != 0) {
numDestinationBytes += 1;
}
_byteArray.resize(numDestinationBytes);
int16_t* sourceSamples = (int16_t*) rawAudioByteArray.data();
int16_t* destinationSamples = (int16_t*) _byteArray.data();
if (_isStereo) {
for (int i = 0; i < numSourceSamples; i += 4) {
destinationSamples[i / 2] = (sourceSamples[i] / 2) + (sourceSamples[i + 2] / 2);
destinationSamples[(i / 2) + 1] = (sourceSamples[i + 1] / 2) + (sourceSamples[i + 3] / 2);
if (i + 2 >= numSourceSamples) {
destinationSamples[i / 2] = sourceSamples[i];
destinationSamples[(i / 2) + 1] = sourceSamples[i + 1];
} else {
destinationSamples[i / 2] = (sourceSamples[i] + sourceSamples[i + 2]) / 2;
destinationSamples[(i / 2) + 1] = (sourceSamples[i + 1] + sourceSamples[i + 3]) / 2;
}
}
} else {
for (int i = 1; i < numSourceSamples; i += 2) {
if (i + 1 >= numSourceSamples) {
destinationSamples[(i - 1) / 2] = (sourceSamples[i - 1] / 2) + (sourceSamples[i] / 2);
destinationSamples[(i - 1) / 2] = (sourceSamples[i - 1] + sourceSamples[i]) / 2;
} else {
destinationSamples[(i - 1) / 2] = (sourceSamples[i - 1] / 4) + (sourceSamples[i] / 2)
+ (sourceSamples[i + 1] / 4);
destinationSamples[(i - 1) / 2] = ((sourceSamples[i - 1] + sourceSamples[i + 1]) / 4) + (sourceSamples[i] / 2);
}
}
}

View file

@ -55,7 +55,8 @@ AvatarData::AvatarData() :
_billboard(),
_errorLogExpiry(0),
_owningAvatarMixer(),
_lastUpdateTimer()
_lastUpdateTimer(),
_velocity(0.0f)
{
}

View file

@ -43,6 +43,7 @@ typedef unsigned long long quint64;
#include <QVariantMap>
#include <QVector>
#include <QtScript/QScriptable>
#include <QReadWriteLock>
#include <CollisionInfo.h>
#include <RegisteredMetaTypes.h>
@ -300,6 +301,19 @@ public:
const Referential* getReferential() const { return _referential; }
void togglePhysicsEnabled() { _enablePhysics = !_enablePhysics; }
bool isPhysicsEnabled() { return _enablePhysics; }
void setPhysicsEnabled(bool enablePhysics) { _enablePhysics = enablePhysics; }
void lockForRead() { _lock.lockForRead(); }
bool tryLockForRead() { return _lock.tryLockForRead(); }
void lockForWrite() { _lock.lockForWrite(); }
bool tryLockForWrite() { return _lock.tryLockForWrite(); }
void unlock() { _lock.unlock(); }
void setVelocity(const glm::vec3 velocity) { _velocity = velocity; }
Q_INVOKABLE glm::vec3 getVelocity() const { return _velocity; }
public slots:
void sendAvatarDataPacket();
void sendIdentityPacket();
@ -389,10 +403,15 @@ protected:
virtual void updateJointMappings();
void changeReferential(Referential* ref);
glm::vec3 _velocity;
private:
// privatize the copy constructor and assignment operator so they cannot be called
AvatarData(const AvatarData&);
AvatarData& operator= (const AvatarData&);
QReadWriteLock _lock;
bool _enablePhysics = false;
};
Q_DECLARE_METATYPE(AvatarData*)

View file

@ -501,7 +501,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
EntityPropertyFlags propertyFlags = encodedPropertyFlags;
dataAt += propertyFlags.getEncodedLength();
bytesRead += propertyFlags.getEncodedLength();
bool useMeters = (args.bitstreamVersion == VERSION_ENTITIES_USE_METERS_AND_RADIANS);
bool useMeters = (args.bitstreamVersion >= VERSION_ENTITIES_USE_METERS_AND_RADIANS);
if (useMeters) {
READ_ENTITY_PROPERTY_SETTER(PROP_POSITION, glm::vec3, updatePosition);
} else {

View file

@ -40,6 +40,7 @@ EntityItemProperties::EntityItemProperties() :
CONSTRUCT_PROPERTY(script, ENTITY_ITEM_DEFAULT_SCRIPT),
CONSTRUCT_PROPERTY(color, ),
CONSTRUCT_PROPERTY(modelURL, ""),
CONSTRUCT_PROPERTY(collisionModelURL, ""),
CONSTRUCT_PROPERTY(animationURL, ""),
CONSTRUCT_PROPERTY(animationFPS, ModelEntityItem::DEFAULT_ANIMATION_FPS),
CONSTRUCT_PROPERTY(animationFrameIndex, ModelEntityItem::DEFAULT_ANIMATION_FRAME_INDEX),
@ -158,6 +159,7 @@ void EntityItemProperties::debugDump() const {
qDebug() << " _position=" << _position.x << "," << _position.y << "," << _position.z;
qDebug() << " _dimensions=" << getDimensions();
qDebug() << " _modelURL=" << _modelURL;
qDebug() << " _collisionModelURL=" << _collisionModelURL;
qDebug() << " changed properties...";
EntityPropertyFlags props = getChangedProperties();
props.debugDumpBits();
@ -213,6 +215,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_SCRIPT, script);
CHECK_PROPERTY_CHANGE(PROP_COLOR, color);
CHECK_PROPERTY_CHANGE(PROP_MODEL_URL, modelURL);
CHECK_PROPERTY_CHANGE(PROP_COLLISION_MODEL_URL, collisionModelURL);
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_URL, animationURL);
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_PLAYING, animationIsPlaying);
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FRAME_INDEX, animationFrameIndex);
@ -276,6 +279,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons
COPY_PROPERTY_TO_QSCRIPTVALUE(visible);
COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR(color);
COPY_PROPERTY_TO_QSCRIPTVALUE(modelURL);
COPY_PROPERTY_TO_QSCRIPTVALUE(collisionModelURL);
COPY_PROPERTY_TO_QSCRIPTVALUE(animationURL);
COPY_PROPERTY_TO_QSCRIPTVALUE(animationIsPlaying);
COPY_PROPERTY_TO_QSCRIPTVALUE(animationFPS);
@ -356,6 +360,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) {
COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(visible, setVisible);
COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(color, setColor);
COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(modelURL, setModelURL);
COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(collisionModelURL, setCollisionModelURL);
COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(animationURL, setAnimationURL);
COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(animationIsPlaying, setAnimationIsPlaying);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(animationFPS, setAnimationFPS);
@ -541,6 +546,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
if (properties.getType() == EntityTypes::Model) {
APPEND_ENTITY_PROPERTY(PROP_MODEL_URL, appendValue, properties.getModelURL());
APPEND_ENTITY_PROPERTY(PROP_COLLISION_MODEL_URL, appendValue, properties.getCollisionModelURL());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, appendValue, properties.getAnimationURL());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, appendValue, properties.getAnimationFPS());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, appendValue, properties.getAnimationFrameIndex());
@ -769,6 +775,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
if (properties.getType() == EntityTypes::Model) {
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_MODEL_URL, setModelURL);
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_COLLISION_MODEL_URL, setCollisionModelURL);
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_ANIMATION_URL, setAnimationURL);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_FPS, float, setAnimationFPS);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_FRAME_INDEX, float, setAnimationFrameIndex);
@ -845,6 +852,7 @@ void EntityItemProperties::markAllChanged() {
_visibleChanged = true;
_colorChanged = true;
_modelURLChanged = true;
_collisionModelURLChanged = true;
_animationURLChanged = true;
_animationIsPlayingChanged = true;
_animationFrameIndexChanged = true;

View file

@ -102,6 +102,7 @@ enum EntityPropertyList {
PROP_TEXT = PROP_MODEL_URL,
PROP_LINE_HEIGHT = PROP_ANIMATION_URL,
PROP_BACKGROUND_COLOR = PROP_ANIMATION_FPS,
PROP_COLLISION_MODEL_URL,
};
typedef PropertyFlags<EntityPropertyList> EntityPropertyFlags;
@ -164,6 +165,7 @@ public:
DEFINE_PROPERTY_REF(PROP_SCRIPT, Script, script, QString);
DEFINE_PROPERTY_REF(PROP_COLOR, Color, color, xColor);
DEFINE_PROPERTY_REF(PROP_MODEL_URL, ModelURL, modelURL, QString);
DEFINE_PROPERTY_REF(PROP_COLLISION_MODEL_URL, CollisionModelURL, collisionModelURL, QString);
DEFINE_PROPERTY_REF(PROP_ANIMATION_URL, AnimationURL, animationURL, QString);
DEFINE_PROPERTY(PROP_ANIMATION_FPS, AnimationFPS, animationFPS, float);
DEFINE_PROPERTY(PROP_ANIMATION_FRAME_INDEX, AnimationFrameIndex, animationFrameIndex, float);
@ -291,6 +293,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Script, script, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Color, color, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ModelURL, modelURL, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, CollisionModelURL, collisionModelURL, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationURL, animationURL, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationFPS, animationFPS, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationFrameIndex, animationFrameIndex, "");

View file

@ -99,10 +99,11 @@ void EntitySimulation::sortEntitiesThatMoved() {
_mortalEntities.remove(entity);
_updateableEntities.remove(entity);
removeEntityInternal(entity);
itemItr = _entitiesToBeSorted.erase(itemItr);
} else {
moveOperator.addEntityToMoveList(entity, newCube);
++itemItr;
}
++itemItr;
}
if (moveOperator.hasMovingEntities()) {
PerformanceTimer perfTimer("recurseTreeWithOperator");

View file

@ -35,7 +35,7 @@ const int DIRTY_SIMULATION_FLAGS =
class EntitySimulation : public QObject {
Q_OBJECT
public:
EntitySimulation() : _mutex(QMutex::Recursive), _entityTree(NULL) { }
EntitySimulation() : _mutex(QMutex::Recursive), _entityTree(NULL), _nextExpiry(quint64(-1)) { }
virtual ~EntitySimulation() { setEntityTree(NULL); }
void lock() { _mutex.lock(); }

View file

@ -19,6 +19,7 @@
#include "ModelEntityItem.h"
const QString ModelEntityItem::DEFAULT_MODEL_URL = QString("");
const QString ModelEntityItem::DEFAULT_COLLISION_MODEL_URL = QString("");
const QString ModelEntityItem::DEFAULT_ANIMATION_URL = QString("");
const float ModelEntityItem::DEFAULT_ANIMATION_FRAME_INDEX = 0.0f;
const bool ModelEntityItem::DEFAULT_ANIMATION_IS_PLAYING = false;
@ -44,6 +45,7 @@ EntityItemProperties ModelEntityItem::getProperties() const {
COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getXColor);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(modelURL, getModelURL);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionModelURL, getCollisionModelURL);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationURL, getAnimationURL);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationIsPlaying, getAnimationIsPlaying);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationFrameIndex, getAnimationFrameIndex);
@ -61,6 +63,7 @@ bool ModelEntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(modelURL, setModelURL);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionModelURL, setCollisionModelURL);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationURL, setAnimationURL);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationIsPlaying, setAnimationIsPlaying);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationFrameIndex, setAnimationFrameIndex);
@ -92,6 +95,11 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
READ_ENTITY_PROPERTY_COLOR(PROP_COLOR, _color);
READ_ENTITY_PROPERTY_STRING(PROP_MODEL_URL, setModelURL);
if (args.bitstreamVersion < VERSION_ENTITIES_HAS_COLLISION_MODEL) {
setCollisionModelURL("");
} else {
READ_ENTITY_PROPERTY_STRING(PROP_COLLISION_MODEL_URL, setCollisionModelURL);
}
READ_ENTITY_PROPERTY_STRING(PROP_ANIMATION_URL, setAnimationURL);
// Because we're using AnimationLoop which will reset the frame index if you change it's running state
@ -128,6 +136,7 @@ EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams&
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
requestedProperties += PROP_MODEL_URL;
requestedProperties += PROP_COLLISION_MODEL_URL;
requestedProperties += PROP_ANIMATION_URL;
requestedProperties += PROP_ANIMATION_FPS;
requestedProperties += PROP_ANIMATION_FRAME_INDEX;
@ -151,6 +160,7 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
APPEND_ENTITY_PROPERTY(PROP_COLOR, appendColor, getColor());
APPEND_ENTITY_PROPERTY(PROP_MODEL_URL, appendValue, getModelURL());
APPEND_ENTITY_PROPERTY(PROP_COLLISION_MODEL_URL, appendValue, getCollisionModelURL());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, appendValue, getAnimationURL());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, appendValue, getAnimationFPS());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, appendValue, getAnimationFrameIndex());
@ -258,6 +268,7 @@ void ModelEntityItem::debugDump() const {
qDebug() << " position:" << getPosition();
qDebug() << " dimensions:" << getDimensions();
qDebug() << " model URL:" << getModelURL();
qDebug() << " collision model URL:" << getCollisionModelURL();
}
void ModelEntityItem::updateShapeType(ShapeType type) {

View file

@ -57,10 +57,14 @@ public:
const rgbColor& getColor() const { return _color; }
xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; }
bool hasModel() const { return !_modelURL.isEmpty(); }
bool hasCollisionModel() const { return !_collisionModelURL.isEmpty(); }
static const QString DEFAULT_MODEL_URL;
const QString& getModelURL() const { return _modelURL; }
static const QString DEFAULT_COLLISION_MODEL_URL;
const QString& getCollisionModelURL() const { return _collisionModelURL; }
bool hasAnimation() const { return !_animationURL.isEmpty(); }
static const QString DEFAULT_ANIMATION_URL;
const QString& getAnimationURL() const { return _animationURL; }
@ -74,6 +78,7 @@ public:
// model related properties
void setModelURL(const QString& url) { _modelURL = url; }
void setCollisionModelURL(const QString& url) { _collisionModelURL = url; }
void setAnimationURL(const QString& url);
static const float DEFAULT_ANIMATION_FRAME_INDEX;
void setAnimationFrameIndex(float value);
@ -121,6 +126,7 @@ protected:
rgbColor _color;
QString _modelURL;
QString _collisionModelURL;
quint64 _lastAnimated;
QString _animationURL;

View file

@ -295,7 +295,11 @@ public:
Tokenizer(QIODevice* device) : _device(device), _pushedBackToken(-1) { }
enum SpecialToken { DATUM_TOKEN = 0x100 };
enum SpecialToken {
NO_TOKEN = -1,
NO_PUSHBACKED_TOKEN = -1,
DATUM_TOKEN = 0x100
};
int nextToken();
const QByteArray& getDatum() const { return _datum; }
@ -311,9 +315,9 @@ private:
};
int Tokenizer::nextToken() {
if (_pushedBackToken != -1) {
if (_pushedBackToken != NO_PUSHBACKED_TOKEN) {
int token = _pushedBackToken;
_pushedBackToken = -1;
_pushedBackToken = NO_PUSHBACKED_TOKEN;
return token;
}
@ -361,7 +365,7 @@ int Tokenizer::nextToken() {
return DATUM_TOKEN;
}
}
return -1;
return NO_TOKEN;
}
FBXNode parseTextFBXNode(Tokenizer& tokenizer) {
@ -378,7 +382,7 @@ FBXNode parseTextFBXNode(Tokenizer& tokenizer) {
int token;
bool expectingDatum = true;
while ((token = tokenizer.nextToken()) != -1) {
while ((token = tokenizer.nextToken()) != Tokenizer::NO_TOKEN) {
if (token == '{') {
for (FBXNode child = parseTextFBXNode(tokenizer); !child.name.isNull(); child = parseTextFBXNode(tokenizer)) {
node.children.append(child);

View file

@ -152,8 +152,6 @@ public:
bool hasSpecularTexture() const;
bool hasEmissiveTexture() const;
model::Mesh _mesh;
};
/// A single animation frame extracted from an FBX document.

View file

@ -0,0 +1,287 @@
//
// OBJReader.cpp
// libraries/fbx/src/
//
// Created by Seth Alves on 3/7/15.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// http://en.wikipedia.org/wiki/Wavefront_.obj_file
// http://www.scratchapixel.com/old/lessons/3d-advanced-lessons/obj-file-format/obj-file-format/
// http://paulbourke.net/dataformats/obj/
#include <QBuffer>
#include <QIODevice>
#include "FBXReader.h"
#include "OBJReader.h"
#include "Shape.h"
class OBJTokenizer {
public:
OBJTokenizer(QIODevice* device) : _device(device), _pushedBackToken(-1) { }
enum SpecialToken {
NO_TOKEN = -1,
NO_PUSHBACKED_TOKEN = -1,
DATUM_TOKEN = 0x100
};
int nextToken();
const QByteArray& getDatum() const { return _datum; }
void skipLine() { _device->readLine(); }
void pushBackToken(int token) { _pushedBackToken = token; }
void ungetChar(char ch) { _device->ungetChar(ch); }
private:
QIODevice* _device;
QByteArray _datum;
int _pushedBackToken;
};
int OBJTokenizer::nextToken() {
if (_pushedBackToken != NO_PUSHBACKED_TOKEN) {
int token = _pushedBackToken;
_pushedBackToken = NO_PUSHBACKED_TOKEN;
return token;
}
char ch;
while (_device->getChar(&ch)) {
if (QChar(ch).isSpace()) {
continue; // skip whitespace
}
switch (ch) {
case '#':
_device->readLine(); // skip the comment
break;
case '\"':
_datum = "";
while (_device->getChar(&ch)) {
if (ch == '\"') { // end on closing quote
break;
}
if (ch == '\\') { // handle escaped quotes
if (_device->getChar(&ch) && ch != '\"') {
_datum.append('\\');
}
}
_datum.append(ch);
}
return DATUM_TOKEN;
default:
_datum = "";
_datum.append(ch);
while (_device->getChar(&ch)) {
if (QChar(ch).isSpace() || ch == '\"') {
ungetChar(ch); // read until we encounter a special character, then replace it
break;
}
_datum.append(ch);
}
return DATUM_TOKEN;
}
}
return NO_TOKEN;
}
bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping, FBXGeometry &geometry) {
FBXMesh &mesh = geometry.meshes[0];
mesh.parts.append(FBXMeshPart());
FBXMeshPart &meshPart = mesh.parts.last();
bool sawG = false;
bool result = true;
meshPart.materialID = QString("dontknow") + QString::number(mesh.parts.count());
meshPart.opacity = 1.0;
meshPart._material = model::MaterialPointer(new model::Material());
meshPart._material->setDiffuse(glm::vec3(1.0, 1.0, 1.0));
meshPart._material->setOpacity(1.0);
meshPart._material->setSpecular(glm::vec3(1.0, 1.0, 1.0));
meshPart._material->setShininess(96.0);
meshPart._material->setEmissive(glm::vec3(0.0, 0.0, 0.0));
while (true) {
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
result = false;
break;
}
QByteArray token = tokenizer.getDatum();
if (token == "g") {
if (sawG) {
// we've encountered the beginning of the next group.
tokenizer.pushBackToken(OBJTokenizer::DATUM_TOKEN);
break;
}
sawG = true;
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
break;
}
QByteArray groupName = tokenizer.getDatum();
meshPart.materialID = groupName;
} else if (token == "v") {
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
break;
}
float x = std::stof(tokenizer.getDatum().data());
// notice the order of z and y -- in OBJ files, up is the 3rd value
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
break;
}
float z = std::stof(tokenizer.getDatum().data());
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
break;
}
float y = std::stof(tokenizer.getDatum().data());
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
break;
}
// the spec gets vague here. might be w, might be a color... chop it off.
tokenizer.skipLine();
mesh.vertices.append(glm::vec3(x, y, z));
mesh.colors.append(glm::vec3(1, 1, 1));
} else if (token == "f") {
// a face can have 3 or more vertices
QVector<int> indices;
while (true) {
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { goto done; }
try {
int vertexIndex = std::stoi(tokenizer.getDatum().data());
// negative indexes count backward from the current end of the vertex list
vertexIndex = (vertexIndex >= 0 ? vertexIndex : mesh.vertices.count() + vertexIndex + 1);
// obj index is 1 based
assert(vertexIndex >= 1);
indices.append(vertexIndex - 1);
}
catch(const std::exception& e) {
// wasn't a number, but it back.
tokenizer.pushBackToken(OBJTokenizer::DATUM_TOKEN);
break;
}
}
if (indices.count() == 3) {
// flip these around (because of the y/z swap above) so our triangles face outward
meshPart.triangleIndices.append(indices[0]);
meshPart.triangleIndices.append(indices[2]);
meshPart.triangleIndices.append(indices[1]);
} else if (indices.count() == 4) {
meshPart.quadIndices << indices;
} else {
qDebug() << "no support for more than 4 vertices on a face in OBJ files";
}
} else {
// something we don't (yet) care about
qDebug() << "OBJ parser is skipping a line with" << token;
tokenizer.skipLine();
}
}
done:
return result;
}
FBXGeometry extractOBJGeometry(const FBXNode& node, const QVariantHash& mapping) {
FBXGeometry geometry;
return geometry;
}
FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping) {
QBuffer buffer(const_cast<QByteArray*>(&model));
buffer.open(QIODevice::ReadOnly);
return readOBJ(&buffer, mapping);
}
FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping) {
FBXGeometry geometry;
OBJTokenizer tokenizer(device);
geometry.meshExtents.reset();
geometry.meshes.append(FBXMesh());
try {
// call parseOBJGroup as long as it's returning true. Each successful call will
// add a new meshPart to the geometry's single mesh.
bool success = true;
while (success) {
success = parseOBJGroup(tokenizer, mapping, geometry);
}
FBXMesh &mesh = geometry.meshes[0];
mesh.meshExtents.reset();
foreach (const glm::vec3& vertex, mesh.vertices) {
mesh.meshExtents.addPoint(vertex);
geometry.meshExtents.addPoint(vertex);
}
geometry.joints.resize(1);
geometry.joints[0].isFree = false;
// geometry.joints[0].freeLineage;
geometry.joints[0].parentIndex = -1;
geometry.joints[0].distanceToParent = 0;
geometry.joints[0].boneRadius = 0;
geometry.joints[0].translation = glm::vec3(0, 0, 0);
// geometry.joints[0].preTransform = ;
geometry.joints[0].preRotation = glm::quat(0, 0, 0, 1);
geometry.joints[0].rotation = glm::quat(0, 0, 0, 1);
geometry.joints[0].postRotation = glm::quat(0, 0, 0, 1);
// geometry.joints[0].postTransform = ;
// geometry.joints[0].transform = ;
geometry.joints[0].rotationMin = glm::vec3(0, 0, 0);
geometry.joints[0].rotationMax = glm::vec3(0, 0, 0);
geometry.joints[0].inverseDefaultRotation = glm::quat(0, 0, 0, 1);
geometry.joints[0].inverseBindRotation = glm::quat(0, 0, 0, 1);
// geometry.joints[0].bindTransform = ;
geometry.joints[0].name = "OBJ";
geometry.joints[0].shapePosition = glm::vec3(0, 0, 0);
geometry.joints[0].shapeRotation = glm::quat(0, 0, 0, 1);
geometry.joints[0].shapeType = SPHERE_SHAPE;
geometry.joints[0].isSkeletonJoint = false;
// add bogus normal data for this mesh
mesh.normals.fill(glm::vec3(0,0,0), mesh.vertices.count());
mesh.tangents.fill(glm::vec3(0,0,0), mesh.vertices.count());
foreach (FBXMeshPart meshPart, mesh.parts) {
int triCount = meshPart.triangleIndices.count() / 3;
for (int i = 0; i < triCount; i++) {
int p0Index = meshPart.triangleIndices[i*3];
int p1Index = meshPart.triangleIndices[i*3+1];
int p2Index = meshPart.triangleIndices[i*3+2];
glm::vec3 p0 = mesh.vertices[p0Index];
glm::vec3 p1 = mesh.vertices[p1Index];
glm::vec3 p2 = mesh.vertices[p2Index];
glm::vec3 n = glm::cross(p1 - p0, p2 - p0);
glm::vec3 t = glm::cross(p2 - p0, n);
mesh.normals[p0Index] = n;
mesh.normals[p1Index] = n;
mesh.normals[p2Index] = n;
mesh.tangents[p0Index] = t;
mesh.tangents[p1Index] = t;
mesh.tangents[p2Index] = t;
}
}
}
catch(const std::exception& e) {
qDebug() << "something went wrong in OBJ reader";
}
return geometry;
}

View file

@ -0,0 +1,6 @@
#include "FBXReader.h"
FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping);
FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping);

View file

@ -25,7 +25,8 @@ Batch::Batch() :
_buffers(),
_textures(),
_streamFormats(),
_transforms()
_transforms(),
_pipelines()
{
}
@ -42,6 +43,7 @@ void Batch::clear() {
_textures.clear();
_streamFormats.clear();
_transforms.clear();
_pipelines.clear();
}
uint32 Batch::cacheResource(Resource* res) {
@ -159,6 +161,12 @@ void Batch::setProjectionTransform(const Mat4& proj) {
_params.push_back(cacheData(sizeof(Mat4), &proj));
}
void Batch::setPipeline(const PipelinePointer& pipeline) {
ADD_COMMAND(setPipeline);
_params.push_back(_pipelines.cache(pipeline));
}
void Batch::setUniformBuffer(uint32 slot, const BufferPointer& buffer, Offset offset, Offset size) {
ADD_COMMAND(setUniformBuffer);

View file

@ -21,6 +21,8 @@
#include "Stream.h"
#include "Texture.h"
#include "Pipeline.h"
#if defined(NSIGHT_FOUND)
#include "nvToolsExt.h"
class ProfileRange {
@ -96,7 +98,9 @@ public:
void setViewTransform(const Transform& view);
void setProjectionTransform(const Mat4& proj);
// Shader Stage
// Pipeline Stage
void setPipeline(const PipelinePointer& pipeline);
void setUniformBuffer(uint32 slot, const BufferPointer& buffer, Offset offset, Offset size);
void setUniformBuffer(uint32 slot, const BufferView& view); // not a command, just a shortcut from a BufferView
@ -164,6 +168,7 @@ public:
COMMAND_setViewTransform,
COMMAND_setProjectionTransform,
COMMAND_setPipeline,
COMMAND_setUniformBuffer,
COMMAND_setUniformTexture,
@ -281,6 +286,7 @@ public:
typedef Cache<TexturePointer>::Vector TextureCaches;
typedef Cache<Stream::FormatPointer>::Vector StreamFormatCaches;
typedef Cache<Transform>::Vector TransformCaches;
typedef Cache<PipelinePointer>::Vector PipelineCaches;
typedef unsigned char Byte;
typedef std::vector<Byte> Bytes;
@ -320,6 +326,7 @@ public:
TextureCaches _textures;
StreamFormatCaches _streamFormats;
TransformCaches _transforms;
PipelineCaches _pipelines;
protected:
};

View file

@ -10,3 +10,14 @@
//
#include "Context.h"
// this include should disappear! as soon as the gpu::Context is in place
#include "GLBackend.h"
using namespace gpu;
bool Context::makeProgram(Shader& shader, const Shader::BindingSet& bindings) {
if (shader.isProgram()) {
return GLBackend::makeProgram(shader, bindings);
}
return false;
}

View file

@ -15,6 +15,8 @@
#include "Resource.h"
#include "Texture.h"
#include "Shader.h"
#include "Pipeline.h"
namespace gpu {
@ -46,25 +48,39 @@ public:
template< typename T >
static void setGPUObject(const Buffer& buffer, T* bo) {
buffer.setGPUObject(reinterpret_cast<GPUObject*>(bo));
buffer.setGPUObject(bo);
}
template< typename T >
static T* getGPUObject(const Buffer& buffer) {
return reinterpret_cast<T*>(buffer.getGPUObject());
}
void syncGPUObject(const Buffer& buffer);
template< typename T >
static void setGPUObject(const Texture& texture, T* to) {
texture.setGPUObject(reinterpret_cast<GPUObject*>(to));
texture.setGPUObject(to);
}
template< typename T >
static T* getGPUObject(const Texture& texture) {
return reinterpret_cast<T*>(texture.getGPUObject());
}
template< typename T >
static void setGPUObject(const Shader& shader, T* so) {
shader.setGPUObject(so);
}
template< typename T >
static T* getGPUObject(const Shader& shader) {
return reinterpret_cast<T*>(shader.getGPUObject());
}
void syncGPUObject(const Texture& texture);
template< typename T >
static void setGPUObject(const Pipeline& pipeline, T* po) {
pipeline.setGPUObject(po);
}
template< typename T >
static T* getGPUObject(const Pipeline& pipeline) {
return reinterpret_cast<T*>(pipeline.getGPUObject());
}
protected:
@ -78,8 +94,17 @@ public:
void enqueueBatch(Batch& batch);
protected:
// This function can only be called by "static Shader::makeProgram()"
// makeProgramShader(...) make a program shader ready to be used in a Batch.
// It compiles the sub shaders, link them and defines the Slots and their bindings.
// If the shader passed is not a program, nothing happens.
static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings = Shader::BindingSet());
friend class Shader;
};

View file

@ -53,6 +53,8 @@ enum Type {
NUINT8,
NUM_TYPES,
BOOL = UINT8,
};
// Array providing the size in bytes for a given scalar type
static const int TYPE_SIZE[NUM_TYPES] = {
@ -81,9 +83,9 @@ enum Dimension {
VEC2,
VEC3,
VEC4,
MAT2,
MAT3,
MAT4,
NUM_DIMENSIONS,
};
// Count (of scalars) in an Element for a given Dimension
@ -92,8 +94,9 @@ static const int DIMENSION_COUNT[NUM_DIMENSIONS] = {
2,
3,
4,
4,
9,
16
16,
};
// Semantic of an Element
@ -118,6 +121,13 @@ enum Semantic {
SRGBA,
SBGRA,
UNIFORM,
UNIFORM_BUFFER,
SAMPLER,
SAMPLER_MULTISAMPLE,
SAMPLER_SHADOW,
NUM_SEMANTICS,
};

View file

@ -25,6 +25,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
(&::gpu::GLBackend::do_setViewTransform),
(&::gpu::GLBackend::do_setProjectionTransform),
(&::gpu::GLBackend::do_setPipeline),
(&::gpu::GLBackend::do_setUniformBuffer),
(&::gpu::GLBackend::do_setUniformTexture),
@ -71,7 +72,8 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
GLBackend::GLBackend() :
_input(),
_transform()
_transform(),
_pipeline()
{
initTransform();
}
@ -134,6 +136,7 @@ void GLBackend::checkGLError() {
void GLBackend::do_draw(Batch& batch, uint32 paramOffset) {
updateInput();
updateTransform();
updatePipeline();
Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint;
GLenum mode = _primitiveToGLmode[primitiveType];
@ -147,6 +150,7 @@ void GLBackend::do_draw(Batch& batch, uint32 paramOffset) {
void GLBackend::do_drawIndexed(Batch& batch, uint32 paramOffset) {
updateInput();
updateTransform();
updatePipeline();
Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint;
GLenum mode = _primitiveToGLmode[primitiveType];
@ -167,389 +171,6 @@ void GLBackend::do_drawIndexedInstanced(Batch& batch, uint32 paramOffset) {
CHECK_GL_ERROR();
}
void GLBackend::do_setInputFormat(Batch& batch, uint32 paramOffset) {
Stream::FormatPointer format = batch._streamFormats.get(batch._params[paramOffset]._uint);
if (format != _input._format) {
_input._format = format;
_input._invalidFormat = true;
}
}
void GLBackend::do_setInputBuffer(Batch& batch, uint32 paramOffset) {
Offset stride = batch._params[paramOffset + 0]._uint;
Offset offset = batch._params[paramOffset + 1]._uint;
BufferPointer buffer = batch._buffers.get(batch._params[paramOffset + 2]._uint);
uint32 channel = batch._params[paramOffset + 3]._uint;
if (channel < getNumInputBuffers()) {
_input._buffers[channel] = buffer;
_input._bufferOffsets[channel] = offset;
_input._bufferStrides[channel] = stride;
_input._buffersState.set(channel);
}
}
#define SUPPORT_LEGACY_OPENGL
#if defined(SUPPORT_LEGACY_OPENGL)
static const int NUM_CLASSIC_ATTRIBS = Stream::TANGENT;
static const GLenum attributeSlotToClassicAttribName[NUM_CLASSIC_ATTRIBS] = {
GL_VERTEX_ARRAY,
GL_NORMAL_ARRAY,
GL_COLOR_ARRAY,
GL_TEXTURE_COORD_ARRAY
};
#endif
void GLBackend::updateInput() {
if (_input._invalidFormat || _input._buffersState.any()) {
if (_input._invalidFormat) {
InputStageState::ActivationCache newActivation;
// Check expected activation
if (_input._format) {
const Stream::Format::AttributeMap& attributes = _input._format->getAttributes();
for (Stream::Format::AttributeMap::const_iterator it = attributes.begin(); it != attributes.end(); it++) {
const Stream::Attribute& attrib = (*it).second;
newActivation.set(attrib._slot);
}
}
// Manage Activation what was and what is expected now
for (unsigned int i = 0; i < newActivation.size(); i++) {
bool newState = newActivation[i];
if (newState != _input._attributeActivation[i]) {
#if defined(SUPPORT_LEGACY_OPENGL)
if (i < NUM_CLASSIC_ATTRIBS) {
if (newState) {
glEnableClientState(attributeSlotToClassicAttribName[i]);
}
else {
glDisableClientState(attributeSlotToClassicAttribName[i]);
}
} else {
#else
{
#endif
if (newState) {
glEnableVertexAttribArray(i);
} else {
glDisableVertexAttribArray(i);
}
}
CHECK_GL_ERROR();
_input._attributeActivation.flip(i);
}
}
}
// now we need to bind the buffers and assign the attrib pointers
if (_input._format) {
const Buffers& buffers = _input._buffers;
const Offsets& offsets = _input._bufferOffsets;
const Offsets& strides = _input._bufferStrides;
const Stream::Format::AttributeMap& attributes = _input._format->getAttributes();
for (Stream::Format::ChannelMap::const_iterator channelIt = _input._format->getChannels().begin();
channelIt != _input._format->getChannels().end();
channelIt++) {
const Stream::Format::ChannelMap::value_type::second_type& channel = (*channelIt).second;
if ((*channelIt).first < buffers.size()) {
int bufferNum = (*channelIt).first;
if (_input._buffersState.test(bufferNum) || _input._invalidFormat) {
GLuint vbo = gpu::GLBackend::getBufferID((*buffers[bufferNum]));
glBindBuffer(GL_ARRAY_BUFFER, vbo);
CHECK_GL_ERROR();
_input._buffersState[bufferNum] = false;
for (unsigned int i = 0; i < channel._slots.size(); i++) {
const Stream::Attribute& attrib = attributes.at(channel._slots[i]);
GLuint slot = attrib._slot;
GLuint count = attrib._element.getDimensionCount();
GLenum type = _elementTypeToGLType[attrib._element.getType()];
GLuint stride = strides[bufferNum];
GLuint pointer = attrib._offset + offsets[bufferNum];
#if defined(SUPPORT_LEGACY_OPENGL)
if (slot < NUM_CLASSIC_ATTRIBS) {
switch (slot) {
case Stream::POSITION:
glVertexPointer(count, type, stride, reinterpret_cast<GLvoid*>(pointer));
break;
case Stream::NORMAL:
glNormalPointer(type, stride, reinterpret_cast<GLvoid*>(pointer));
break;
case Stream::COLOR:
glColorPointer(count, type, stride, reinterpret_cast<GLvoid*>(pointer));
break;
case Stream::TEXCOORD:
glTexCoordPointer(count, type, stride, reinterpret_cast<GLvoid*>(pointer));
break;
};
} else {
#else
{
#endif
GLboolean isNormalized = attrib._element.isNormalized();
glVertexAttribPointer(slot, count, type, isNormalized, stride,
reinterpret_cast<GLvoid*>(pointer));
}
CHECK_GL_ERROR();
}
}
}
}
}
// everything format related should be in sync now
_input._invalidFormat = false;
}
/* TODO: Fancy version GL4.4
if (_needInputFormatUpdate) {
InputActivationCache newActivation;
// Assign the vertex format required
if (_inputFormat) {
const StreamFormat::AttributeMap& attributes = _inputFormat->getAttributes();
for (StreamFormat::AttributeMap::const_iterator it = attributes.begin(); it != attributes.end(); it++) {
const StreamFormat::Attribute& attrib = (*it).second;
newActivation.set(attrib._slot);
glVertexAttribFormat(
attrib._slot,
attrib._element.getDimensionCount(),
_elementTypeToGLType[attrib._element.getType()],
attrib._element.isNormalized(),
attrib._stride);
}
CHECK_GL_ERROR();
}
// Manage Activation what was and what is expected now
for (int i = 0; i < newActivation.size(); i++) {
bool newState = newActivation[i];
if (newState != _inputAttributeActivation[i]) {
if (newState) {
glEnableVertexAttribArray(i);
} else {
glDisableVertexAttribArray(i);
}
_inputAttributeActivation.flip(i);
}
}
CHECK_GL_ERROR();
_needInputFormatUpdate = false;
}
if (_needInputStreamUpdate) {
if (_inputStream) {
const Stream::Buffers& buffers = _inputStream->getBuffers();
const Stream::Offsets& offsets = _inputStream->getOffsets();
const Stream::Strides& strides = _inputStream->getStrides();
for (int i = 0; i < buffers.size(); i++) {
GLuint vbo = gpu::GLBackend::getBufferID((*buffers[i]));
glBindVertexBuffer(i, vbo, offsets[i], strides[i]);
}
CHECK_GL_ERROR();
}
_needInputStreamUpdate = false;
}
*/
}
void GLBackend::do_setIndexBuffer(Batch& batch, uint32 paramOffset) {
_input._indexBufferType = (Type) batch._params[paramOffset + 2]._uint;
BufferPointer indexBuffer = batch._buffers.get(batch._params[paramOffset + 1]._uint);
_input._indexBufferOffset = batch._params[paramOffset + 0]._uint;
_input._indexBuffer = indexBuffer;
if (indexBuffer) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, getBufferID(*indexBuffer));
} else {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
CHECK_GL_ERROR();
}
// Transform Stage
void GLBackend::do_setModelTransform(Batch& batch, uint32 paramOffset) {
_transform._model = batch._transforms.get(batch._params[paramOffset]._uint);
_transform._invalidModel = true;
}
void GLBackend::do_setViewTransform(Batch& batch, uint32 paramOffset) {
_transform._view = batch._transforms.get(batch._params[paramOffset]._uint);
_transform._invalidView = true;
}
void GLBackend::do_setProjectionTransform(Batch& batch, uint32 paramOffset) {
memcpy(&_transform._projection, batch.editData(batch._params[paramOffset]._uint), sizeof(Mat4));
_transform._invalidProj = true;
}
void GLBackend::initTransform() {
#if defined(Q_OS_WIN)
glGenBuffers(1, &_transform._transformObjectBuffer);
glGenBuffers(1, &_transform._transformCameraBuffer);
glBindBuffer(GL_UNIFORM_BUFFER, _transform._transformObjectBuffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(_transform._transformObject), (const void*) &_transform._transformObject, GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, _transform._transformCameraBuffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(_transform._transformCamera), (const void*) &_transform._transformCamera, GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
#else
#endif
}
void GLBackend::killTransform() {
#if defined(Q_OS_WIN)
glDeleteBuffers(1, &_transform._transformObjectBuffer);
glDeleteBuffers(1, &_transform._transformCameraBuffer);
#else
#endif
}
void GLBackend::updateTransform() {
// Check all the dirty flags and update the state accordingly
if (_transform._invalidProj) {
_transform._transformCamera._projection = _transform._projection;
}
if (_transform._invalidView) {
_transform._view.getInverseMatrix(_transform._transformCamera._view);
_transform._view.getMatrix(_transform._transformCamera._viewInverse);
}
if (_transform._invalidModel) {
_transform._model.getMatrix(_transform._transformObject._model);
_transform._model.getInverseMatrix(_transform._transformObject._modelInverse);
}
if (_transform._invalidView || _transform._invalidProj) {
Mat4 viewUntranslated = _transform._transformCamera._view;
viewUntranslated[3] = Vec4(0.0f, 0.0f, 0.0f, 1.0f);
_transform._transformCamera._projectionViewUntranslated = _transform._transformCamera._projection * viewUntranslated;
}
if (_transform._invalidView || _transform._invalidProj) {
#if defined(Q_OS_WIN)
glBindBufferBase(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, 0);
glBindBuffer(GL_ARRAY_BUFFER, _transform._transformCameraBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(_transform._transformCamera), (const void*) &_transform._transformCamera, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
CHECK_GL_ERROR();
#endif
}
if (_transform._invalidModel) {
#if defined(Q_OS_WIN)
glBindBufferBase(GL_UNIFORM_BUFFER, TRANSFORM_OBJECT_SLOT, 0);
glBindBuffer(GL_ARRAY_BUFFER, _transform._transformObjectBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(_transform._transformObject), (const void*) &_transform._transformObject, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
CHECK_GL_ERROR();
#endif
}
#if defined(Q_OS_WIN)
glBindBufferBase(GL_UNIFORM_BUFFER, TRANSFORM_OBJECT_SLOT, _transform._transformObjectBuffer);
glBindBufferBase(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, _transform._transformCameraBuffer);
CHECK_GL_ERROR();
#endif
#if defined(Q_OS_MAC) || defined(Q_OS_LINUX)
// Do it again for fixed pipeline until we can get rid of it
if (_transform._invalidProj) {
if (_transform._lastMode != GL_PROJECTION) {
glMatrixMode(GL_PROJECTION);
_transform._lastMode = GL_PROJECTION;
}
glLoadMatrixf(reinterpret_cast< const GLfloat* >(&_transform._projection));
CHECK_GL_ERROR();
}
if (_transform._invalidModel || _transform._invalidView) {
if (!_transform._model.isIdentity()) {
if (_transform._lastMode != GL_MODELVIEW) {
glMatrixMode(GL_MODELVIEW);
_transform._lastMode = GL_MODELVIEW;
}
Transform::Mat4 modelView;
if (!_transform._view.isIdentity()) {
Transform mvx;
Transform::inverseMult(mvx, _transform._view, _transform._model);
mvx.getMatrix(modelView);
} else {
_transform._model.getMatrix(modelView);
}
glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView));
} else {
if (!_transform._view.isIdentity()) {
if (_transform._lastMode != GL_MODELVIEW) {
glMatrixMode(GL_MODELVIEW);
_transform._lastMode = GL_MODELVIEW;
}
Transform::Mat4 modelView;
_transform._view.getInverseMatrix(modelView);
glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView));
} else {
// TODO: eventually do something about the matrix when neither view nor model is specified?
// glLoadIdentity();
}
}
CHECK_GL_ERROR();
}
#endif
// Flags are clean
_transform._invalidView = _transform._invalidProj = _transform._invalidModel = false;
}
void GLBackend::do_setUniformBuffer(Batch& batch, uint32 paramOffset) {
GLuint slot = batch._params[paramOffset + 3]._uint;
BufferPointer uniformBuffer = batch._buffers.get(batch._params[paramOffset + 2]._uint);
GLintptr rangeStart = batch._params[paramOffset + 1]._uint;
GLsizeiptr rangeSize = batch._params[paramOffset + 0]._uint;
#if defined(Q_OS_MAC)
GLfloat* data = (GLfloat*) (uniformBuffer->getData() + rangeStart);
glUniform4fv(slot, rangeSize / sizeof(GLfloat[4]), data);
// NOT working so we ll stick to the uniform float array until we move to core profile
// GLuint bo = getBufferID(*uniformBuffer);
//glUniformBufferEXT(_shader._program, slot, bo);
#elif defined(Q_OS_WIN)
GLuint bo = getBufferID(*uniformBuffer);
glBindBufferRange(GL_UNIFORM_BUFFER, slot, bo, rangeStart, rangeSize);
#else
GLfloat* data = (GLfloat*) (uniformBuffer->getData() + rangeStart);
glUniform4fv(slot, rangeSize / sizeof(GLfloat[4]), data);
#endif
CHECK_GL_ERROR();
}
void GLBackend::do_setUniformTexture(Batch& batch, uint32 paramOffset) {
GLuint slot = batch._params[paramOffset + 1]._uint;
TexturePointer uniformTexture = batch._textures.get(batch._params[paramOffset + 0]._uint);
GLuint to = getTextureID(uniformTexture);
glActiveTexture(GL_TEXTURE0 + slot);
glBindTexture(GL_TEXTURE_2D, to);
CHECK_GL_ERROR();
}
// TODO: As long as we have gl calls explicitely issued from interface
// code, we need to be able to record and batch these calls. THe long
// term strategy is to get rid of any GL calls in favor of the HIFI GPU API
@ -740,8 +361,10 @@ void Batch::_glUseProgram(GLuint program) {
}
void GLBackend::do_glUseProgram(Batch& batch, uint32 paramOffset) {
_shader._program = batch._params[paramOffset]._uint;
glUseProgram(_shader._program);
_pipeline._program = batch._params[paramOffset]._uint;
// for this call we still want to execute the glUseProgram in the order of the glCOmmand to avoid any issue
_pipeline._invalidProgram = false;
glUseProgram(_pipeline._program);
CHECK_GL_ERROR();
}
@ -998,49 +621,3 @@ void GLBackend::do_glColor4f(Batch& batch, uint32 paramOffset) {
CHECK_GL_ERROR();
}
GLBackend::GLBuffer::GLBuffer() :
_stamp(0),
_buffer(0),
_size(0)
{}
GLBackend::GLBuffer::~GLBuffer() {
if (_buffer != 0) {
glDeleteBuffers(1, &_buffer);
}
}
void GLBackend::syncGPUObject(const Buffer& buffer) {
GLBuffer* object = Backend::getGPUObject<GLBackend::GLBuffer>(buffer);
if (object && (object->_stamp == buffer.getSysmem().getStamp())) {
return;
}
// need to have a gpu object?
if (!object) {
object = new GLBuffer();
glGenBuffers(1, &object->_buffer);
CHECK_GL_ERROR();
Backend::setGPUObject(buffer, object);
}
// Now let's update the content of the bo with the sysmem version
// TODO: in the future, be smarter about when to actually upload the glBO version based on the data that did change
//if () {
glBindBuffer(GL_ARRAY_BUFFER, object->_buffer);
glBufferData(GL_ARRAY_BUFFER, buffer.getSysmem().getSize(), buffer.getSysmem().readData(), GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
object->_stamp = buffer.getSysmem().getStamp();
object->_size = buffer.getSysmem().getSize();
//}
CHECK_GL_ERROR();
}
GLuint GLBackend::getBufferID(const Buffer& buffer) {
GLBackend::syncGPUObject(buffer);
return Backend::getGPUObject<GLBackend::GLBuffer>(buffer)->_buffer;
}

View file

@ -33,6 +33,7 @@ public:
static void checkGLError();
static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings = Shader::BindingSet());
class GLBuffer : public GPUObject {
@ -44,7 +45,7 @@ public:
GLBuffer();
~GLBuffer();
};
static void syncGPUObject(const Buffer& buffer);
static GLBuffer* syncGPUObject(const Buffer& buffer);
static GLuint getBufferID(const Buffer& buffer);
class GLTexture : public GPUObject {
@ -57,9 +58,33 @@ public:
GLTexture();
~GLTexture();
};
static void syncGPUObject(const Texture& texture);
static GLTexture* syncGPUObject(const Texture& texture);
static GLuint getTextureID(const TexturePointer& texture);
class GLShader : public GPUObject {
public:
GLuint _shader;
GLuint _program;
GLuint _transformCameraSlot = -1;
GLuint _transformObjectSlot = -1;
GLShader();
~GLShader();
};
static GLShader* syncGPUObject(const Shader& shader);
static GLuint getShaderID(const ShaderPointer& shader);
class GLPipeline : public GPUObject {
public:
GLShader* _program;
GLPipeline();
~GLPipeline();
};
static GLPipeline* syncGPUObject(const Pipeline& pipeline);
static const int MAX_NUM_ATTRIBUTES = Stream::NUM_INPUT_SLOTS;
static const int MAX_NUM_INPUT_BUFFERS = 16;
@ -145,18 +170,24 @@ protected:
_lastMode(GL_TEXTURE) {}
} _transform;
// Shader Stage
// Pipeline Stage
void do_setPipeline(Batch& batch, uint32 paramOffset);
void do_setUniformBuffer(Batch& batch, uint32 paramOffset);
void do_setUniformTexture(Batch& batch, uint32 paramOffset);
void updateShader();
struct ShaderStageState {
void updatePipeline();
struct PipelineStageState {
PipelinePointer _pipeline;
GLuint _program;
bool _invalidProgram;
ShaderStageState() :
_program(0) {}
} _shader;
PipelineStageState() :
_pipeline(),
_program(0),
_invalidProgram(false)
{}
} _pipeline;
// TODO: As long as we have gl calls explicitely issued from interface

View file

@ -0,0 +1,66 @@
//
// GLBackendBuffer.cpp
// libraries/gpu/src/gpu
//
// Created by Sam Gateau on 3/8/2015.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "GLBackendShared.h"
using namespace gpu;
GLBackend::GLBuffer::GLBuffer() :
_stamp(0),
_buffer(0),
_size(0)
{}
GLBackend::GLBuffer::~GLBuffer() {
if (_buffer != 0) {
glDeleteBuffers(1, &_buffer);
}
}
GLBackend::GLBuffer* GLBackend::syncGPUObject(const Buffer& buffer) {
GLBuffer* object = Backend::getGPUObject<GLBackend::GLBuffer>(buffer);
if (object && (object->_stamp == buffer.getSysmem().getStamp())) {
return object;
}
// need to have a gpu object?
if (!object) {
object = new GLBuffer();
glGenBuffers(1, &object->_buffer);
CHECK_GL_ERROR();
Backend::setGPUObject(buffer, object);
}
// Now let's update the content of the bo with the sysmem version
// TODO: in the future, be smarter about when to actually upload the glBO version based on the data that did change
//if () {
glBindBuffer(GL_ARRAY_BUFFER, object->_buffer);
glBufferData(GL_ARRAY_BUFFER, buffer.getSysmem().getSize(), buffer.getSysmem().readData(), GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
object->_stamp = buffer.getSysmem().getStamp();
object->_size = buffer.getSysmem().getSize();
//}
CHECK_GL_ERROR();
return object;
}
GLuint GLBackend::getBufferID(const Buffer& buffer) {
GLBuffer* bo = GLBackend::syncGPUObject(buffer);
if (bo) {
return bo->_buffer;
} else {
return 0;
}
}

View file

@ -0,0 +1,223 @@
//
// GLBackendInput.cpp
// libraries/gpu/src/gpu
//
// Created by Sam Gateau on 3/8/2015.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "GLBackendShared.h"
using namespace gpu;
void GLBackend::do_setInputFormat(Batch& batch, uint32 paramOffset) {
Stream::FormatPointer format = batch._streamFormats.get(batch._params[paramOffset]._uint);
if (format != _input._format) {
_input._format = format;
_input._invalidFormat = true;
}
}
void GLBackend::do_setInputBuffer(Batch& batch, uint32 paramOffset) {
Offset stride = batch._params[paramOffset + 0]._uint;
Offset offset = batch._params[paramOffset + 1]._uint;
BufferPointer buffer = batch._buffers.get(batch._params[paramOffset + 2]._uint);
uint32 channel = batch._params[paramOffset + 3]._uint;
if (channel < getNumInputBuffers()) {
_input._buffers[channel] = buffer;
_input._bufferOffsets[channel] = offset;
_input._bufferStrides[channel] = stride;
_input._buffersState.set(channel);
}
}
#define SUPPORT_LEGACY_OPENGL
#if defined(SUPPORT_LEGACY_OPENGL)
static const int NUM_CLASSIC_ATTRIBS = Stream::TANGENT;
static const GLenum attributeSlotToClassicAttribName[NUM_CLASSIC_ATTRIBS] = {
GL_VERTEX_ARRAY,
GL_NORMAL_ARRAY,
GL_COLOR_ARRAY,
GL_TEXTURE_COORD_ARRAY
};
#endif
void GLBackend::updateInput() {
if (_input._invalidFormat || _input._buffersState.any()) {
if (_input._invalidFormat) {
InputStageState::ActivationCache newActivation;
// Check expected activation
if (_input._format) {
const Stream::Format::AttributeMap& attributes = _input._format->getAttributes();
for (Stream::Format::AttributeMap::const_iterator it = attributes.begin(); it != attributes.end(); it++) {
const Stream::Attribute& attrib = (*it).second;
newActivation.set(attrib._slot);
}
}
// Manage Activation what was and what is expected now
for (unsigned int i = 0; i < newActivation.size(); i++) {
bool newState = newActivation[i];
if (newState != _input._attributeActivation[i]) {
#if defined(SUPPORT_LEGACY_OPENGL)
if (i < NUM_CLASSIC_ATTRIBS) {
if (newState) {
glEnableClientState(attributeSlotToClassicAttribName[i]);
}
else {
glDisableClientState(attributeSlotToClassicAttribName[i]);
}
} else {
#else
{
#endif
if (newState) {
glEnableVertexAttribArray(i);
} else {
glDisableVertexAttribArray(i);
}
}
CHECK_GL_ERROR();
_input._attributeActivation.flip(i);
}
}
}
// now we need to bind the buffers and assign the attrib pointers
if (_input._format) {
const Buffers& buffers = _input._buffers;
const Offsets& offsets = _input._bufferOffsets;
const Offsets& strides = _input._bufferStrides;
const Stream::Format::AttributeMap& attributes = _input._format->getAttributes();
for (Stream::Format::ChannelMap::const_iterator channelIt = _input._format->getChannels().begin();
channelIt != _input._format->getChannels().end();
channelIt++) {
const Stream::Format::ChannelMap::value_type::second_type& channel = (*channelIt).second;
if ((*channelIt).first < buffers.size()) {
int bufferNum = (*channelIt).first;
if (_input._buffersState.test(bufferNum) || _input._invalidFormat) {
GLuint vbo = gpu::GLBackend::getBufferID((*buffers[bufferNum]));
glBindBuffer(GL_ARRAY_BUFFER, vbo);
CHECK_GL_ERROR();
_input._buffersState[bufferNum] = false;
for (unsigned int i = 0; i < channel._slots.size(); i++) {
const Stream::Attribute& attrib = attributes.at(channel._slots[i]);
GLuint slot = attrib._slot;
GLuint count = attrib._element.getDimensionCount();
GLenum type = _elementTypeToGLType[attrib._element.getType()];
GLuint stride = strides[bufferNum];
GLuint pointer = attrib._offset + offsets[bufferNum];
#if defined(SUPPORT_LEGACY_OPENGL)
if (slot < NUM_CLASSIC_ATTRIBS) {
switch (slot) {
case Stream::POSITION:
glVertexPointer(count, type, stride, reinterpret_cast<GLvoid*>(pointer));
break;
case Stream::NORMAL:
glNormalPointer(type, stride, reinterpret_cast<GLvoid*>(pointer));
break;
case Stream::COLOR:
glColorPointer(count, type, stride, reinterpret_cast<GLvoid*>(pointer));
break;
case Stream::TEXCOORD:
glTexCoordPointer(count, type, stride, reinterpret_cast<GLvoid*>(pointer));
break;
};
} else {
#else
{
#endif
GLboolean isNormalized = attrib._element.isNormalized();
glVertexAttribPointer(slot, count, type, isNormalized, stride,
reinterpret_cast<GLvoid*>(pointer));
}
CHECK_GL_ERROR();
}
}
}
}
}
// everything format related should be in sync now
_input._invalidFormat = false;
}
/* TODO: Fancy version GL4.4
if (_needInputFormatUpdate) {
InputActivationCache newActivation;
// Assign the vertex format required
if (_inputFormat) {
const StreamFormat::AttributeMap& attributes = _inputFormat->getAttributes();
for (StreamFormat::AttributeMap::const_iterator it = attributes.begin(); it != attributes.end(); it++) {
const StreamFormat::Attribute& attrib = (*it).second;
newActivation.set(attrib._slot);
glVertexAttribFormat(
attrib._slot,
attrib._element.getDimensionCount(),
_elementTypeToGLType[attrib._element.getType()],
attrib._element.isNormalized(),
attrib._stride);
}
CHECK_GL_ERROR();
}
// Manage Activation what was and what is expected now
for (int i = 0; i < newActivation.size(); i++) {
bool newState = newActivation[i];
if (newState != _inputAttributeActivation[i]) {
if (newState) {
glEnableVertexAttribArray(i);
} else {
glDisableVertexAttribArray(i);
}
_inputAttributeActivation.flip(i);
}
}
CHECK_GL_ERROR();
_needInputFormatUpdate = false;
}
if (_needInputStreamUpdate) {
if (_inputStream) {
const Stream::Buffers& buffers = _inputStream->getBuffers();
const Stream::Offsets& offsets = _inputStream->getOffsets();
const Stream::Strides& strides = _inputStream->getStrides();
for (int i = 0; i < buffers.size(); i++) {
GLuint vbo = gpu::GLBackend::getBufferID((*buffers[i]));
glBindVertexBuffer(i, vbo, offsets[i], strides[i]);
}
CHECK_GL_ERROR();
}
_needInputStreamUpdate = false;
}
*/
}
void GLBackend::do_setIndexBuffer(Batch& batch, uint32 paramOffset) {
_input._indexBufferType = (Type) batch._params[paramOffset + 2]._uint;
BufferPointer indexBuffer = batch._buffers.get(batch._params[paramOffset + 1]._uint);
_input._indexBufferOffset = batch._params[paramOffset + 0]._uint;
_input._indexBuffer = indexBuffer;
if (indexBuffer) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, getBufferID(*indexBuffer));
} else {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
CHECK_GL_ERROR();
}

View file

@ -0,0 +1,95 @@
//
// GLBackendPipeline.cpp
// libraries/gpu/src/gpu
//
// Created by Sam Gateau on 3/8/2015.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "GLBackendShared.h"
#include "Format.h"
using namespace gpu;
GLBackend::GLPipeline::GLPipeline() :
_program(nullptr)
{}
GLBackend::GLPipeline::~GLPipeline() {
_program = nullptr;
}
GLBackend::GLPipeline* GLBackend::syncGPUObject(const Pipeline& pipeline) {
GLPipeline* object = Backend::getGPUObject<GLBackend::GLPipeline>(pipeline);
// If GPU object already created then good
if (object) {
return object;
}
return nullptr;
}
void GLBackend::do_setPipeline(Batch& batch, uint32 paramOffset) {
PipelinePointer pipeline = batch._pipelines.get(batch._params[paramOffset + 0]._uint);
if (pipeline == _pipeline._pipeline) {
return;
}
auto pipelineObject = syncGPUObject((*pipeline));
if (!pipelineObject) {
return;
}
_pipeline._pipeline = pipeline;
_pipeline._program = pipelineObject->_program->_program;
_pipeline._invalidProgram = true;
}
void GLBackend::do_setUniformBuffer(Batch& batch, uint32 paramOffset) {
GLuint slot = batch._params[paramOffset + 3]._uint;
BufferPointer uniformBuffer = batch._buffers.get(batch._params[paramOffset + 2]._uint);
GLintptr rangeStart = batch._params[paramOffset + 1]._uint;
GLsizeiptr rangeSize = batch._params[paramOffset + 0]._uint;
#if defined(Q_OS_MAC)
GLfloat* data = (GLfloat*) (uniformBuffer->getData() + rangeStart);
glUniform4fv(slot, rangeSize / sizeof(GLfloat[4]), data);
// NOT working so we ll stick to the uniform float array until we move to core profile
// GLuint bo = getBufferID(*uniformBuffer);
//glUniformBufferEXT(_shader._program, slot, bo);
#elif defined(Q_OS_WIN)
GLuint bo = getBufferID(*uniformBuffer);
glBindBufferRange(GL_UNIFORM_BUFFER, slot, bo, rangeStart, rangeSize);
#else
GLfloat* data = (GLfloat*) (uniformBuffer->getData() + rangeStart);
glUniform4fv(slot, rangeSize / sizeof(GLfloat[4]), data);
#endif
CHECK_GL_ERROR();
}
void GLBackend::do_setUniformTexture(Batch& batch, uint32 paramOffset) {
GLuint slot = batch._params[paramOffset + 1]._uint;
TexturePointer uniformTexture = batch._textures.get(batch._params[paramOffset + 0]._uint);
GLuint to = getTextureID(uniformTexture);
glActiveTexture(GL_TEXTURE0 + slot);
glBindTexture(GL_TEXTURE_2D, to);
CHECK_GL_ERROR();
}
void GLBackend::updatePipeline() {
if (_pipeline._invalidProgram) {
glUseProgram(_pipeline._program);
CHECK_GL_ERROR();
_pipeline._invalidProgram = true;
}
}

View file

@ -0,0 +1,685 @@
//
// GLBackendShader.cpp
// libraries/gpu/src/gpu
//
// Created by Sam Gateau on 2/28/2015.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "GLBackendShared.h"
#include "Format.h"
using namespace gpu;
GLBackend::GLShader::GLShader() :
_shader(0),
_program(0)
{}
GLBackend::GLShader::~GLShader() {
if (_shader != 0) {
glDeleteShader(_shader);
}
if (_program != 0) {
glDeleteProgram(_program);
}
}
void makeBindings(GLBackend::GLShader* shader) {
if(!shader || !shader->_program) {
return;
}
GLuint glprogram = shader->_program;
GLint loc = -1;
//Check for gpu specific attribute slotBindings
loc = glGetAttribLocation(glprogram, "position");
if (loc >= 0) {
glBindAttribLocation(glprogram, gpu::Stream::POSITION, "position");
}
loc = glGetAttribLocation(glprogram, "normal");
if (loc >= 0) {
glBindAttribLocation(glprogram, gpu::Stream::NORMAL, "normal");
}
loc = glGetAttribLocation(glprogram, "color");
if (loc >= 0) {
glBindAttribLocation(glprogram, gpu::Stream::COLOR, "color");
}
loc = glGetAttribLocation(glprogram, "texcoord");
if (loc >= 0) {
glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD, "texcoord");
}
loc = glGetAttribLocation(glprogram, "tangent");
if (loc >= 0) {
glBindAttribLocation(glprogram, gpu::Stream::TANGENT, "tangent");
}
loc = glGetAttribLocation(glprogram, "texcoord1");
if (loc >= 0) {
glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD1, "texcoord1");
}
loc = glGetAttribLocation(glprogram, "clusterIndices");
if (loc >= 0) {
glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_INDEX, "clusterIndices");
}
loc = glGetAttribLocation(glprogram, "clusterWeights");
if (loc >= 0) {
glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_WEIGHT, "clusterWeights");
}
// Link again to take into account the assigned attrib location
glLinkProgram(glprogram);
GLint linked = 0;
glGetProgramiv(glprogram, GL_LINK_STATUS, &linked);
if (!linked) {
qDebug() << "GLShader::makeBindings - failed to link after assigning slotBindings?";
}
// now assign the ubo binding, then DON't relink!
//Check for gpu specific uniform slotBindings
#if defined(Q_OS_WIN)
loc = glGetUniformBlockIndex(glprogram, "transformObjectBuffer");
if (loc >= 0) {
glUniformBlockBinding(glprogram, loc, gpu::TRANSFORM_OBJECT_SLOT);
shader->_transformObjectSlot = gpu::TRANSFORM_OBJECT_SLOT;
}
loc = glGetUniformBlockIndex(glprogram, "transformCameraBuffer");
if (loc >= 0) {
glUniformBlockBinding(glprogram, loc, gpu::TRANSFORM_CAMERA_SLOT);
shader->_transformCameraSlot = gpu::TRANSFORM_OBJECT_SLOT;
}
#endif
}
GLBackend::GLShader* compileShader(const Shader& shader) {
// Any GLSLprogram ? normally yes...
const std::string& shaderSource = shader.getSource().getCode();
if (shaderSource.empty()) {
qDebug() << "GLShader::compileShader - no GLSL shader source code ? so failed to create";
return nullptr;
}
// Shader domain
const GLenum SHADER_DOMAINS[2] = { GL_VERTEX_SHADER, GL_FRAGMENT_SHADER };
GLenum shaderDomain = SHADER_DOMAINS[shader.getType()];
// Create the shader object
GLuint glshader = glCreateShader(shaderDomain);
if (!glshader) {
qDebug() << "GLShader::compileShader - failed to create the gl shader object";
return nullptr;
}
// Assign the source
const GLchar* srcstr = shaderSource.c_str();
glShaderSource(glshader, 1, &srcstr, NULL);
// Compile !
glCompileShader(glshader);
// check if shader compiled
GLint compiled = 0;
glGetShaderiv(glshader, GL_COMPILE_STATUS, &compiled);
// if compilation fails
if (!compiled) {
// save the source code to a temp file so we can debug easily
/* std::ofstream filestream;
filestream.open("debugshader.glsl");
if (filestream.is_open()) {
filestream << shaderSource->source;
filestream.close();
}
*/
GLint infoLength = 0;
glGetShaderiv(glshader, GL_INFO_LOG_LENGTH, &infoLength);
char* temp = new char[infoLength] ;
glGetShaderInfoLog(glshader, infoLength, NULL, temp);
qDebug() << "GLShader::compileShader - failed to compile the gl shader object:";
qDebug() << temp;
/*
filestream.open("debugshader.glsl.info.txt");
if (filestream.is_open()) {
filestream << std::string(temp);
filestream.close();
}
*/
delete[] temp;
glDeleteShader(glshader);
return nullptr;
}
GLuint glprogram = 0;
#ifdef SEPARATE_PROGRAM
// so far so good, program is almost done, need to link:
GLuint glprogram = glCreateProgram();
if (!glprogram) {
qDebug() << "GLShader::compileShader - failed to create the gl shader & gl program object";
return nullptr;
}
glProgramParameteri(glprogram, GL_PROGRAM_SEPARABLE, GL_TRUE);
glAttachShader(glprogram, glshader);
glLinkProgram(glprogram);
GLint linked = 0;
glGetProgramiv(glprogram, GL_LINK_STATUS, &linked);
if (!linked) {
/*
// save the source code to a temp file so we can debug easily
std::ofstream filestream;
filestream.open("debugshader.glsl");
if (filestream.is_open()) {
filestream << shaderSource->source;
filestream.close();
}
*/
GLint infoLength = 0;
glGetProgramiv(glprogram, GL_INFO_LOG_LENGTH, &infoLength);
char* temp = new char[infoLength] ;
glGetProgramInfoLog(glprogram, infoLength, NULL, temp);
qDebug() << "GLShader::compileShader - failed to LINK the gl program object :";
qDebug() << temp;
/*
filestream.open("debugshader.glsl.info.txt");
if (filestream.is_open()) {
filestream << String(temp);
filestream.close();
}
*/
delete[] temp;
glDeleteShader(glshader);
glDeleteProgram(glprogram);
return nullptr;
}
#endif
// So far so good, the shader is created successfully
GLBackend::GLShader* object = new GLBackend::GLShader();
object->_shader = glshader;
object->_program = glprogram;
makeBindings(object);
return object;
}
GLBackend::GLShader* compileProgram(const Shader& program) {
if(!program.isProgram()) {
return nullptr;
}
// Let's go through every shaders and make sure they are ready to go
std::vector< GLuint > shaderObjects;
for (auto subShader : program.getShaders()) {
GLuint so = GLBackend::getShaderID(subShader);
if (!so) {
qDebug() << "GLShader::compileProgram - One of the shaders of the program is not compiled?";
return nullptr;
}
shaderObjects.push_back(so);
}
// so far so good, program is almost done, need to link:
GLuint glprogram = glCreateProgram();
if (!glprogram) {
qDebug() << "GLShader::compileProgram - failed to create the gl program object";
return nullptr;
}
// glProgramParameteri(glprogram, GL_PROGRAM_, GL_TRUE);
// Create the program from the sub shaders
for (auto so : shaderObjects) {
glAttachShader(glprogram, so);
}
// Link!
glLinkProgram(glprogram);
GLint linked = 0;
glGetProgramiv(glprogram, GL_LINK_STATUS, &linked);
if (!linked) {
/*
// save the source code to a temp file so we can debug easily
std::ofstream filestream;
filestream.open("debugshader.glsl");
if (filestream.is_open()) {
filestream << shaderSource->source;
filestream.close();
}
*/
GLint infoLength = 0;
glGetProgramiv(glprogram, GL_INFO_LOG_LENGTH, &infoLength);
char* temp = new char[infoLength] ;
glGetProgramInfoLog(glprogram, infoLength, NULL, temp);
qDebug() << "GLShader::compileProgram - failed to LINK the gl program object :";
qDebug() << temp;
/*
filestream.open("debugshader.glsl.info.txt");
if (filestream.is_open()) {
filestream << std::string(temp);
filestream.close();
}
*/
delete[] temp;
glDeleteProgram(glprogram);
return nullptr;
}
// So far so good, the program is created successfully
GLBackend::GLShader* object = new GLBackend::GLShader();
object->_shader = 0;
object->_program = glprogram;
makeBindings(object);
return object;
}
GLBackend::GLShader* GLBackend::syncGPUObject(const Shader& shader) {
GLShader* object = Backend::getGPUObject<GLBackend::GLShader>(shader);
// If GPU object already created then good
if (object) {
return object;
}
// need to have a gpu object?
if (shader.isProgram()) {
GLShader* tempObject = compileProgram(shader);
if (tempObject) {
object = tempObject;
Backend::setGPUObject(shader, object);
}
} else if (shader.isDomain()) {
GLShader* tempObject = compileShader(shader);
if (tempObject) {
object = tempObject;
Backend::setGPUObject(shader, object);
}
}
return object;
}
GLuint GLBackend::getShaderID(const ShaderPointer& shader) {
if (!shader) {
return 0;
}
GLShader* object = GLBackend::syncGPUObject(*shader);
if (object) {
if (shader->isProgram()) {
return object->_program;
} else {
return object->_shader;
}
} else {
return 0;
}
}
class ElementResource {
public:
gpu::Element _element;
uint16 _resource;
ElementResource(Element&& elem, uint16 resource) : _element(elem), _resource(resource) {}
};
ElementResource getFormatFromGLUniform(GLenum gltype) {
switch (gltype) {
case GL_FLOAT: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_FLOAT_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_FLOAT_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_FLOAT_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER);
/*
case GL_DOUBLE: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_DOUBLE_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_DOUBLE_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_DOUBLE_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER);
*/
case GL_INT: return ElementResource(Element(SCALAR, gpu::INT32, UNIFORM), Resource::BUFFER);
case GL_INT_VEC2: return ElementResource(Element(VEC2, gpu::INT32, UNIFORM), Resource::BUFFER);
case GL_INT_VEC3: return ElementResource(Element(VEC3, gpu::INT32, UNIFORM), Resource::BUFFER);
case GL_INT_VEC4: return ElementResource(Element(VEC4, gpu::INT32, UNIFORM), Resource::BUFFER);
case GL_UNSIGNED_INT: return ElementResource(Element(SCALAR, gpu::UINT32, UNIFORM), Resource::BUFFER);
#if defined(Q_OS_WIN)
case GL_UNSIGNED_INT_VEC2: return ElementResource(Element(VEC2, gpu::UINT32, UNIFORM), Resource::BUFFER);
case GL_UNSIGNED_INT_VEC3: return ElementResource(Element(VEC3, gpu::UINT32, UNIFORM), Resource::BUFFER);
case GL_UNSIGNED_INT_VEC4: return ElementResource(Element(VEC4, gpu::UINT32, UNIFORM), Resource::BUFFER);
#endif
case GL_BOOL: return ElementResource(Element(SCALAR, gpu::BOOL, UNIFORM), Resource::BUFFER);
case GL_BOOL_VEC2: return ElementResource(Element(VEC2, gpu::BOOL, UNIFORM), Resource::BUFFER);
case GL_BOOL_VEC3: return ElementResource(Element(VEC3, gpu::BOOL, UNIFORM), Resource::BUFFER);
case GL_BOOL_VEC4: return ElementResource(Element(VEC4, gpu::BOOL, UNIFORM), Resource::BUFFER);
case GL_FLOAT_MAT2: return ElementResource(Element(gpu::MAT2, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_FLOAT_MAT3: return ElementResource(Element(MAT3, gpu::FLOAT, UNIFORM), Resource::BUFFER);
case GL_FLOAT_MAT4: return ElementResource(Element(MAT4, gpu::FLOAT, UNIFORM), Resource::BUFFER);
/* {GL_FLOAT_MAT2x3 mat2x3},
{GL_FLOAT_MAT2x4 mat2x4},
{GL_FLOAT_MAT3x2 mat3x2},
{GL_FLOAT_MAT3x4 mat3x4},
{GL_FLOAT_MAT4x2 mat4x2},
{GL_FLOAT_MAT4x3 mat4x3},
{GL_DOUBLE_MAT2 dmat2},
{GL_DOUBLE_MAT3 dmat3},
{GL_DOUBLE_MAT4 dmat4},
{GL_DOUBLE_MAT2x3 dmat2x3},
{GL_DOUBLE_MAT2x4 dmat2x4},
{GL_DOUBLE_MAT3x2 dmat3x2},
{GL_DOUBLE_MAT3x4 dmat3x4},
{GL_DOUBLE_MAT4x2 dmat4x2},
{GL_DOUBLE_MAT4x3 dmat4x3},
*/
case GL_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D);
case GL_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D);
case GL_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_3D);
case GL_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_CUBE);
#if defined(Q_OS_WIN)
case GL_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D);
case GL_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D_ARRAY);
case GL_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D_ARRAY);
case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY);
#endif
case GL_SAMPLER_2D_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D);
#if defined(Q_OS_WIN)
case GL_SAMPLER_CUBE_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_CUBE);
case GL_SAMPLER_2D_ARRAY_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D_ARRAY);
#endif
// {GL_SAMPLER_1D_SHADOW sampler1DShadow},
// {GL_SAMPLER_1D_ARRAY_SHADOW sampler1DArrayShadow},
// {GL_SAMPLER_BUFFER samplerBuffer},
// {GL_SAMPLER_2D_RECT sampler2DRect},
// {GL_SAMPLER_2D_RECT_SHADOW sampler2DRectShadow},
#if defined(Q_OS_WIN)
case GL_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D);
case GL_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D);
case GL_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D);
case GL_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_3D);
case GL_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_CUBE);
case GL_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D_ARRAY);
case GL_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D_ARRAY);
case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY);
// {GL_INT_SAMPLER_BUFFER isamplerBuffer},
// {GL_INT_SAMPLER_2D_RECT isampler2DRect},
case GL_UNSIGNED_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D);
case GL_UNSIGNED_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D);
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D);
case GL_UNSIGNED_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_3D);
case GL_UNSIGNED_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_CUBE);
case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D_ARRAY);
case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D_ARRAY);
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY);
#endif
// {GL_UNSIGNED_INT_SAMPLER_BUFFER usamplerBuffer},
// {GL_UNSIGNED_INT_SAMPLER_2D_RECT usampler2DRect},
/*
{GL_IMAGE_1D image1D},
{GL_IMAGE_2D image2D},
{GL_IMAGE_3D image3D},
{GL_IMAGE_2D_RECT image2DRect},
{GL_IMAGE_CUBE imageCube},
{GL_IMAGE_BUFFER imageBuffer},
{GL_IMAGE_1D_ARRAY image1DArray},
{GL_IMAGE_2D_ARRAY image2DArray},
{GL_IMAGE_2D_MULTISAMPLE image2DMS},
{GL_IMAGE_2D_MULTISAMPLE_ARRAY image2DMSArray},
{GL_INT_IMAGE_1D iimage1D},
{GL_INT_IMAGE_2D iimage2D},
{GL_INT_IMAGE_3D iimage3D},
{GL_INT_IMAGE_2D_RECT iimage2DRect},
{GL_INT_IMAGE_CUBE iimageCube},
{GL_INT_IMAGE_BUFFER iimageBuffer},
{GL_INT_IMAGE_1D_ARRAY iimage1DArray},
{GL_INT_IMAGE_2D_ARRAY iimage2DArray},
{GL_INT_IMAGE_2D_MULTISAMPLE iimage2DMS},
{GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY iimage2DMSArray},
{GL_UNSIGNED_INT_IMAGE_1D uimage1D},
{GL_UNSIGNED_INT_IMAGE_2D uimage2D},
{GL_UNSIGNED_INT_IMAGE_3D uimage3D},
{GL_UNSIGNED_INT_IMAGE_2D_RECT uimage2DRect},
{GL_UNSIGNED_INT_IMAGE_CUBE uimageCube},+ [0] {_name="fInnerRadius" _location=0 _element={_semantic=15 '\xf' _dimension=0 '\0' _type=0 '\0' } } gpu::Shader::Slot
{GL_UNSIGNED_INT_IMAGE_BUFFER uimageBuffer},
{GL_UNSIGNED_INT_IMAGE_1D_ARRAY uimage1DArray},
{GL_UNSIGNED_INT_IMAGE_2D_ARRAY uimage2DArray},
{GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE uimage2DMS},
{GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY uimage2DMSArray},
{GL_UNSIGNED_INT_ATOMIC_COUNTER atomic_uint}
*/
default:
return ElementResource(Element(), Resource::BUFFER);
}
};
int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers) {
GLint uniformsCount = 0;
glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount);
for (int i = 0; i < uniformsCount; i++) {
const GLint NAME_LENGTH = 256;
GLchar name[NAME_LENGTH];
GLint length = 0;
GLint size = 0;
GLenum type = 0;
glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name);
GLint location = glGetUniformLocation(glprogram, name);
const GLint INVALID_UNIFORM_LOCATION = -1;
// Try to make sense of the gltype
auto elementResource = getFormatFromGLUniform(type);
// The uniform as a standard var type
if (location != INVALID_UNIFORM_LOCATION) {
if (elementResource._resource == Resource::BUFFER) {
uniforms.insert(Shader::Slot(name, location, elementResource._element, elementResource._resource));
} else {
// For texture/Sampler, the location is the actual binding value
GLint binding = -1;
glGetUniformiv(glprogram, location, &binding);
auto requestedBinding = slotBindings.find(std::string(name));
if (requestedBinding != slotBindings.end()) {
if (binding != (*requestedBinding)._location) {
binding = (*requestedBinding)._location;
glUniform1i(location, binding);
}
}
textures.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource));
samplers.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource));
}
}
}
return uniformsCount;
}
const GLint UNUSED_SLOT = -1;
bool isUnusedSlot(GLint binding) {
return (binding == UNUSED_SLOT);
}
int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers) {
GLint buffersCount = 0;
#if defined(Q_OS_WIN)
glGetProgramiv(glprogram, GL_ACTIVE_UNIFORM_BLOCKS, &buffersCount);
// fast exit
if (buffersCount == 0) {
return 0;
}
GLint maxNumUniformBufferSlots = 0;
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxNumUniformBufferSlots);
std::vector<GLint> uniformBufferSlotMap(maxNumUniformBufferSlots, -1);
for (int i = 0; i < buffersCount; i++) {
const GLint NAME_LENGTH = 256;
GLchar name[NAME_LENGTH];
GLint length = 0;
GLint size = 0;
GLenum type = 0;
GLint binding = -1;
glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_NAME_LENGTH, &length);
glGetActiveUniformBlockName(glprogram, i, NAME_LENGTH, &length, name);
glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_BINDING, &binding);
glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_DATA_SIZE, &size);
GLuint blockIndex = glGetUniformBlockIndex(glprogram, name);
// CHeck if there is a requested binding for this block
auto requestedBinding = slotBindings.find(std::string(name));
if (requestedBinding != slotBindings.end()) {
// If yes force it
if (binding != (*requestedBinding)._location) {
binding = (*requestedBinding)._location;
glUniformBlockBinding(glprogram, blockIndex, binding);
}
} else if (binding == 0) {
// If no binding was assigned then just do it finding a free slot
auto slotIt = std::find_if(uniformBufferSlotMap.begin(), uniformBufferSlotMap.end(), isUnusedSlot);
if (slotIt != uniformBufferSlotMap.end()) {
binding = slotIt - uniformBufferSlotMap.begin();
glUniformBlockBinding(glprogram, blockIndex, binding);
} else {
// This should neve happen, an active ubo cannot find an available slot among the max available?!
binding = -1;
}
}
// If binding is valid record it
if (binding >= 0) {
uniformBufferSlotMap[binding] = blockIndex;
}
Element element(SCALAR, gpu::UINT32, gpu::UNIFORM_BUFFER);
buffers.insert(Shader::Slot(name, binding, element, Resource::BUFFER));
}
#endif
return buffersCount;
}
int makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs) {
GLint inputsCount = 0;
glGetProgramiv(glprogram, GL_ACTIVE_ATTRIBUTES, &inputsCount);
for (int i = 0; i < inputsCount; i++) {
const GLint NAME_LENGTH = 256;
GLchar name[NAME_LENGTH];
GLint length = 0;
GLint size = 0;
GLenum type = 0;
glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name);
GLint binding = glGetAttribLocation(glprogram, name);
auto elementResource = getFormatFromGLUniform(type);
inputs.insert(Shader::Slot(name, binding, elementResource._element, -1));
}
return inputsCount;
}
int makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs) {
/* GLint outputsCount = 0;
glGetProgramiv(glprogram, GL_ACTIVE_, &outputsCount);
for (int i = 0; i < inputsCount; i++) {
const GLint NAME_LENGTH = 256;
GLchar name[NAME_LENGTH];
GLint length = 0;
GLint size = 0;
GLenum type = 0;
glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name);
auto element = getFormatFromGLUniform(type);
outputs.insert(Shader::Slot(name, i, element));
}
*/
return 0; //inputsCount;
}
bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindings) {
// First make sure the Shader has been compiled
GLShader* object = GLBackend::syncGPUObject(shader);
if (!object) {
return false;
}
if (object->_program) {
Shader::SlotSet buffers;
makeUniformBlockSlots(object->_program, slotBindings, buffers);
Shader::SlotSet uniforms;
Shader::SlotSet textures;
Shader::SlotSet samplers;
makeUniformSlots(object->_program, slotBindings, uniforms, textures, samplers);
Shader::SlotSet inputs;
makeInputSlots(object->_program, slotBindings, inputs);
Shader::SlotSet outputs;
makeOutputSlots(object->_program, slotBindings, outputs);
shader.defineSlots(uniforms, buffers, textures, samplers, inputs, outputs);
} else if (object->_shader) {
}
return true;
}

View file

@ -223,7 +223,7 @@ public:
};
void GLBackend::syncGPUObject(const Texture& texture) {
GLBackend::GLTexture* GLBackend::syncGPUObject(const Texture& texture) {
GLTexture* object = Backend::getGPUObject<GLBackend::GLTexture>(texture);
// If GPU object already created and in sync
@ -232,14 +232,14 @@ void GLBackend::syncGPUObject(const Texture& texture) {
// If gpu object info is in sync with sysmem version
if (object->_contentStamp >= texture.getDataStamp()) {
// Then all good, GPU object is ready to be used
return;
return object;
} else {
// Need to update the content of the GPU object from the source sysmem of the texture
needUpdate = true;
}
} else if (!texture.isDefined()) {
// NO texture definition yet so let's avoid thinking
return;
return nullptr;
}
// need to have a gpu object?
@ -320,6 +320,8 @@ void GLBackend::syncGPUObject(const Texture& texture) {
qDebug() << "GLBackend::syncGPUObject(const Texture&) case for Texture Type " << texture.getType() << " not supported";
}
CHECK_GL_ERROR();
return object;
}
@ -328,8 +330,7 @@ GLuint GLBackend::getTextureID(const TexturePointer& texture) {
if (!texture) {
return 0;
}
GLBackend::syncGPUObject(*texture);
GLTexture* object = Backend::getGPUObject<GLBackend::GLTexture>(*texture);
GLTexture* object = GLBackend::syncGPUObject(*texture);
if (object) {
return object->_texture;
} else {

View file

@ -0,0 +1,156 @@
//
// GLBackendTransform.cpp
// libraries/gpu/src/gpu
//
// Created by Sam Gateau on 3/8/2015.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "GLBackendShared.h"
#include "Format.h"
using namespace gpu;
// Transform Stage
void GLBackend::do_setModelTransform(Batch& batch, uint32 paramOffset) {
_transform._model = batch._transforms.get(batch._params[paramOffset]._uint);
_transform._invalidModel = true;
}
void GLBackend::do_setViewTransform(Batch& batch, uint32 paramOffset) {
_transform._view = batch._transforms.get(batch._params[paramOffset]._uint);
_transform._invalidView = true;
}
void GLBackend::do_setProjectionTransform(Batch& batch, uint32 paramOffset) {
memcpy(&_transform._projection, batch.editData(batch._params[paramOffset]._uint), sizeof(Mat4));
_transform._invalidProj = true;
}
void GLBackend::initTransform() {
#if defined(Q_OS_WIN)
glGenBuffers(1, &_transform._transformObjectBuffer);
glGenBuffers(1, &_transform._transformCameraBuffer);
glBindBuffer(GL_UNIFORM_BUFFER, _transform._transformObjectBuffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(_transform._transformObject), (const void*) &_transform._transformObject, GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, _transform._transformCameraBuffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(_transform._transformCamera), (const void*) &_transform._transformCamera, GL_DYNAMIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
#else
#endif
}
void GLBackend::killTransform() {
#if defined(Q_OS_WIN)
glDeleteBuffers(1, &_transform._transformObjectBuffer);
glDeleteBuffers(1, &_transform._transformCameraBuffer);
#else
#endif
}
void GLBackend::updateTransform() {
// Check all the dirty flags and update the state accordingly
if (_transform._invalidProj) {
_transform._transformCamera._projection = _transform._projection;
}
if (_transform._invalidView) {
_transform._view.getInverseMatrix(_transform._transformCamera._view);
_transform._view.getMatrix(_transform._transformCamera._viewInverse);
}
if (_transform._invalidModel) {
_transform._model.getMatrix(_transform._transformObject._model);
_transform._model.getInverseMatrix(_transform._transformObject._modelInverse);
}
if (_transform._invalidView || _transform._invalidProj) {
Mat4 viewUntranslated = _transform._transformCamera._view;
viewUntranslated[3] = Vec4(0.0f, 0.0f, 0.0f, 1.0f);
_transform._transformCamera._projectionViewUntranslated = _transform._transformCamera._projection * viewUntranslated;
}
if (_transform._invalidView || _transform._invalidProj) {
#if defined(Q_OS_WIN)
glBindBufferBase(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, 0);
glBindBuffer(GL_ARRAY_BUFFER, _transform._transformCameraBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(_transform._transformCamera), (const void*) &_transform._transformCamera, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
CHECK_GL_ERROR();
#endif
}
if (_transform._invalidModel) {
#if defined(Q_OS_WIN)
glBindBufferBase(GL_UNIFORM_BUFFER, TRANSFORM_OBJECT_SLOT, 0);
glBindBuffer(GL_ARRAY_BUFFER, _transform._transformObjectBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(_transform._transformObject), (const void*) &_transform._transformObject, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
CHECK_GL_ERROR();
#endif
}
#if defined(Q_OS_WIN)
glBindBufferBase(GL_UNIFORM_BUFFER, TRANSFORM_OBJECT_SLOT, _transform._transformObjectBuffer);
glBindBufferBase(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, _transform._transformCameraBuffer);
CHECK_GL_ERROR();
#endif
#if defined(Q_OS_MAC) || defined(Q_OS_LINUX)
// Do it again for fixed pipeline until we can get rid of it
if (_transform._invalidProj) {
if (_transform._lastMode != GL_PROJECTION) {
glMatrixMode(GL_PROJECTION);
_transform._lastMode = GL_PROJECTION;
}
glLoadMatrixf(reinterpret_cast< const GLfloat* >(&_transform._projection));
CHECK_GL_ERROR();
}
if (_transform._invalidModel || _transform._invalidView) {
if (!_transform._model.isIdentity()) {
if (_transform._lastMode != GL_MODELVIEW) {
glMatrixMode(GL_MODELVIEW);
_transform._lastMode = GL_MODELVIEW;
}
Transform::Mat4 modelView;
if (!_transform._view.isIdentity()) {
Transform mvx;
Transform::inverseMult(mvx, _transform._view, _transform._model);
mvx.getMatrix(modelView);
} else {
_transform._model.getMatrix(modelView);
}
glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView));
} else {
if (!_transform._view.isIdentity()) {
if (_transform._lastMode != GL_MODELVIEW) {
glMatrixMode(GL_MODELVIEW);
_transform._lastMode = GL_MODELVIEW;
}
Transform::Mat4 modelView;
_transform._view.getInverseMatrix(modelView);
glLoadMatrixf(reinterpret_cast< const GLfloat* >(&modelView));
} else {
// TODO: eventually do something about the matrix when neither view nor model is specified?
// glLoadIdentity();
}
}
CHECK_GL_ERROR();
}
#endif
// Flags are clean
_transform._invalidView = _transform._invalidProj = _transform._invalidModel = false;
}

View file

@ -0,0 +1,34 @@
//
// Pipeline.cpp
// libraries/gpu/src/gpu
//
// Created by Sam Gateau on 3/8/2015.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Pipeline.h"
#include <math.h>
#include <QDebug>
using namespace gpu;
Pipeline::Pipeline():
_program(),
_states()
{
}
Pipeline::~Pipeline()
{
}
Pipeline* Pipeline::create(const ShaderPointer& program, const States& states) {
Pipeline* pipeline = new Pipeline();
pipeline->_program = program;
pipeline->_states = states;
return pipeline;
}

View file

@ -0,0 +1,53 @@
//
// Pipeline.h
// libraries/gpu/src/gpu
//
// Created by Sam Gateau on 3/8/2015.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_gpu_Pipeline_h
#define hifi_gpu_Pipeline_h
#include "Resource.h"
#include <memory>
#include <set>
#include "Shader.h"
#include "State.h"
namespace gpu {
class Pipeline {
public:
static Pipeline* create(const ShaderPointer& program, const States& states);
~Pipeline();
const ShaderPointer& getProgram() const { return _program; }
const States& getStates() const { return _states; }
protected:
ShaderPointer _program;
States _states;
Pipeline();
Pipeline(const Pipeline& pipeline); // deep copy of the sysmem shader
Pipeline& operator=(const Pipeline& pipeline); // deep copy of the sysmem texture
// This shouldn't be used by anything else than the Backend class with the proper casting.
mutable GPUObject* _gpuObject = NULL;
void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; }
GPUObject* getGPUObject() const { return _gpuObject; }
friend class Backend;
};
typedef std::shared_ptr< Pipeline > PipelinePointer;
typedef std::vector< PipelinePointer > Pipelines;
};
#endif

View file

@ -38,6 +38,18 @@ public:
// The size in bytes of data stored in the resource
virtual Size getSize() const = 0;
enum Type {
BUFFER = 0,
TEXTURE_1D,
TEXTURE_2D,
TEXTURE_3D,
TEXTURE_CUBE,
TEXTURE_1D_ARRAY,
TEXTURE_2D_ARRAY,
TEXTURE_3D_ARRAY,
TEXTURE_CUBE_ARRAY,
};
protected:
Resource() {}
@ -140,12 +152,11 @@ protected:
Sysmem* _sysmem = NULL;
mutable GPUObject* _gpuObject = NULL;
// This shouldn't be used by anything else than the Backend class with the proper casting.
mutable GPUObject* _gpuObject = NULL;
void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; }
GPUObject* getGPUObject() const { return _gpuObject; }
friend class Backend;
};

View file

@ -0,0 +1,73 @@
//
// Shader.cpp
// libraries/gpu/src/gpu
//
// Created by Sam Gateau on 2/27/2015.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Shader.h"
#include <math.h>
#include <QDebug>
#include "Context.h"
using namespace gpu;
Shader::Shader(Type type, const Source& source):
_source(source),
_type(type)
{
}
Shader::Shader(Type type, Pointer& vertex, Pointer& pixel):
_type(type)
{
_shaders.resize(2);
_shaders[VERTEX] = vertex;
_shaders[PIXEL] = pixel;
}
Shader::~Shader()
{
}
Shader* Shader::createVertex(const Source& source) {
Shader* shader = new Shader(VERTEX, source);
return shader;
}
Shader* Shader::createPixel(const Source& source) {
Shader* shader = new Shader(PIXEL, source);
return shader;
}
Shader* Shader::createProgram(Pointer& vertexShader, Pointer& pixelShader) {
if (vertexShader && vertexShader->getType() == VERTEX) {
if (pixelShader && pixelShader->getType() == PIXEL) {
Shader* shader = new Shader(PROGRAM, vertexShader, pixelShader);
return shader;
}
}
return nullptr;
}
void Shader::defineSlots(const SlotSet& uniforms, const SlotSet& buffers, const SlotSet& textures, const SlotSet& samplers, const SlotSet& inputs, const SlotSet& outputs) {
_uniforms = uniforms;
_buffers = buffers;
_textures = textures;
_samplers = samplers;
_inputs = inputs;
_outputs = outputs;
}
bool Shader::makeProgram(Shader& shader, const Shader::BindingSet& bindings) {
if (shader.isProgram()) {
return Context::makeProgram(shader, bindings);
}
return false;
}

165
libraries/gpu/src/gpu/Shader.h Executable file
View file

@ -0,0 +1,165 @@
//
// Shader.h
// libraries/gpu/src/gpu
//
// Created by Sam Gateau on 2/27/2015.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_gpu_Shader_h
#define hifi_gpu_Shader_h
#include "Resource.h"
#include <memory>
#include <set>
namespace gpu {
class Shader {
public:
typedef QSharedPointer< Shader > Pointer;
typedef std::vector< Pointer > Shaders;
class Source {
public:
enum Language {
GLSL = 0,
};
Source() {}
Source(const std::string& code, Language lang = GLSL) : _code(code), _lang(lang) {}
Source(const Source& source) : _code(source._code), _lang(source._lang) {}
virtual ~Source() {}
virtual const std::string& getCode() const { return _code; }
protected:
std::string _code;
Language _lang = GLSL;
};
class Slot {
public:
std::string _name;
uint32 _location;
Element _element;
uint16 _resourceType;
Slot(const std::string& name, uint16 location, const Element& element, uint16 resourceType = Resource::BUFFER) :
_name(name), _location(location), _element(element), _resourceType(resourceType) {}
};
class Binding {
public:
std::string _name;
uint32 _location;
Binding(const std::string&& name, uint32 loc = 0) : _name(name), _location(loc) {}
};
template <typename T> class Less {
public:
bool operator() (const T& x, const T& y) const { return x._name < y._name; }
};
typedef std::set<Slot, Less<Slot>> SlotSet;
typedef std::set<Binding, Less<Binding>> BindingSet;
enum Type {
VERTEX = 0,
PIXEL,
GEOMETRY,
NUM_DOMAINS,
PROGRAM,
};
static Shader* createVertex(const Source& source);
static Shader* createPixel(const Source& source);
static Shader* createProgram(Pointer& vertexShader, Pointer& pixelShader);
~Shader();
Type getType() const { return _type; }
bool isProgram() const { return getType() > NUM_DOMAINS; }
bool isDomain() const { return getType() < NUM_DOMAINS; }
const Source& getSource() const { return _source; }
const Shaders& getShaders() const { return _shaders; }
// Access the exposed uniform, input and output slot
const SlotSet& getUniforms() const { return _uniforms; }
const SlotSet& getBuffers() const { return _buffers; }
const SlotSet& getTextures() const { return _textures; }
const SlotSet& getSamplers() const { return _samplers; }
const SlotSet& getInputs() const { return _inputs; }
const SlotSet& getOutputs() const { return _outputs; }
// Define the list of uniforms, inputs and outputs for the shader
// This call is intendend to build the list of exposed slots in order
// to correctly bind resource to the shader.
// These can be build "manually" from knowledge of the atual shader code
// or automatically by calling "makeShader()", this is the preferred way
void defineSlots(const SlotSet& uniforms, const SlotSet& buffers, const SlotSet& textures, const SlotSet& samplers, const SlotSet& inputs, const SlotSet& outputs);
// makeProgram(...) make a program shader ready to be used in a Batch.
// It compiles the sub shaders, link them and defines the Slots and their bindings.
// If the shader passed is not a program, nothing happens.
//
// It is possible to provide a set of slot bindings (from the name of the slot to a unit number) allowing
// to make sure slots with the same semantics can be always bound on the same location from shader to shader.
// For example, the "diffuseMap" can always be bound to texture unit #1 for different shaders by specifying a Binding("diffuseMap", 1)
//
// As of now (03/2015), the call to makeProgram is in fact calling gpu::Context::makeProgram and does rely
// on the underneath gpu::Context::Backend available. Since we only support glsl, this means that it relies
// on a glContext and the driver to compile the glsl shader.
// Hoppefully in a few years the shader compilation will be completely abstracted in a separate shader compiler library
// independant of the graphics api in use underneath (looking at you opengl & vulkan).
static bool makeProgram(Shader& shader, const Shader::BindingSet& bindings = Shader::BindingSet());
protected:
Shader(Type type, const Source& source);
Shader(Type type, Pointer& vertex, Pointer& pixel);
Shader(const Shader& shader); // deep copy of the sysmem shader
Shader& operator=(const Shader& shader); // deep copy of the sysmem texture
// Source contains the actual source code or nothing if the shader is a program
Source _source;
// if shader is composed of sub shaders, here they are
Shaders _shaders;
// List of exposed uniform, input and output slots
SlotSet _uniforms;
SlotSet _buffers;
SlotSet _textures;
SlotSet _samplers;
SlotSet _inputs;
SlotSet _outputs;
// The type of the shader, the master key
Type _type;
// This shouldn't be used by anything else than the Backend class with the proper casting.
mutable GPUObject* _gpuObject = NULL;
void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; }
GPUObject* getGPUObject() const { return _gpuObject; }
friend class Backend;
};
typedef Shader::Pointer ShaderPointer;
typedef std::vector< ShaderPointer > Shaders;
};
#endif

20
libraries/gpu/src/gpu/State.cpp Executable file
View file

@ -0,0 +1,20 @@
//
// State.cpp
// libraries/gpu/src/gpu
//
// Created by Sam Gateau on 3/8/2015.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "State.h"
#include <QDebug>
using namespace gpu;
State::~State()
{
}

88
libraries/gpu/src/gpu/State.h Executable file
View file

@ -0,0 +1,88 @@
//
// Pipeline.h
// libraries/gpu/src/gpu
//
// Created by Sam Gateau on 3/8/2015.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_gpu_State_h
#define hifi_gpu_State_h
#include "Format.h"
#include <vector>
#include <QSharedPointer>
namespace gpu {
class GPUObject;
class State {
public:
State() {}
virtual ~State();
// Work in progress, not used
/*
enum Field {
FILL_MODE,
CULL_MODE,
DEPTH_BIAS,
DEPTH_BIAS_CLAMP,
DEPTH_BIASSLOPE_SCALE,
FRONT_CLOCKWISE,
DEPTH_CLIP_ENABLE,
SCISSR_ENABLE,
MULTISAMPLE_ENABLE,
ANTIALISED_LINE_ENABLE,
DEPTH_ENABLE,
DEPTH_WRITE_MASK,
DEPTH_FUNCTION,
STENCIL_ENABLE,
STENCIL_READ_MASK,
STENCIL_WRITE_MASK,
STENCIL_FUNCTION_FRONT,
STENCIL_FUNCTION_BACK,
STENCIL_REFERENCE,
BLEND_INDEPENDANT_ENABLE,
BLEND_ENABLE,
BLEND_SOURCE,
BLEND_DESTINATION,
BLEND_OPERATION,
BLEND_SOURCE_ALPHA,
BLEND_DESTINATION_ALPHA,
BLEND_OPERATION_ALPHA,
BLEND_WRITE_MASK,
BLEND_FACTOR,
SAMPLE_MASK,
ALPHA_TO_COVERAGE_ENABLE,
};
*/
protected:
State(const State& state);
State& operator=(const State& state);
// This shouldn't be used by anything else than the Backend class with the proper casting.
mutable GPUObject* _gpuObject = NULL;
void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; }
GPUObject* getGPUObject() const { return _gpuObject; }
friend class Backend;
};
typedef QSharedPointer< State > StatePointer;
typedef std::vector< StatePointer > States;
};
#endif

View file

@ -207,12 +207,10 @@ protected:
Size resize(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices);
mutable GPUObject* _gpuObject = NULL;
// This shouldn't be used by anything else than the Backend class with the proper casting.
mutable GPUObject* _gpuObject = NULL;
void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; }
GPUObject* getGPUObject() const { return _gpuObject; }
friend class Backend;
};

View file

@ -0,0 +1,245 @@
<!
// Atmospheric.slh
//
// Created by Sam Gateau on 3/9/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
!>
<@if not MODEL_ATMOSPHERE_SLH@>
<@def MODEL_ATMOSPHERE_SLH@>
<!
// Code is a modified version of:
// http://http.developer.nvidia.com/GPUGems/gpugems_app01.html
// Atmospheric scattering fragment shader
//
// Author: Sean O'Neil
//
// Copyright (c) 2004 Sean O'Neil
//
// For licensing information, see http://http.developer.nvidia.com/GPUGems/gpugems_app01.html:
//
// NVIDIA Statement on the Software
//
// The source code provided is freely distributable, so long as the NVIDIA header remains unaltered and user modifications are
// detailed.
//
// No Warranty
//
// THE SOFTWARE AND ANY OTHER MATERIALS PROVIDED BY NVIDIA ON THE ENCLOSED CD-ROM ARE PROVIDED "AS IS." NVIDIA DISCLAIMS ALL
// WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
// Limitation of Liability
//
// NVIDIA SHALL NOT BE LIABLE TO ANY USER, DEVELOPER, DEVELOPER'S CUSTOMERS, OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH OR
// UNDER DEVELOPER FOR ANY LOSS OF PROFITS, INCOME, SAVINGS, OR ANY OTHER CONSEQUENTIAL, INCIDENTAL, SPECIAL, PUNITIVE, DIRECT
// OR INDIRECT DAMAGES (WHETHER IN AN ACTION IN CONTRACT, TORT OR BASED ON A WARRANTY), EVEN IF NVIDIA HAS BEEN ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGES. THESE LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF THE ESSENTIAL PURPOSE OF ANY
// LIMITED REMEDY. IN NO EVENT SHALL NVIDIA'S AGGREGATE LIABILITY TO DEVELOPER OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH
// OR UNDER DEVELOPER EXCEED THE AMOUNT OF MONEY ACTUALLY PAID BY DEVELOPER TO NVIDIA FOR THE SOFTWARE OR ANY OTHER MATERIALS.
//
!>
struct Atmosphere {
vec4 _invWaveLength;
vec4 _radiuses;
vec4 _scales;
vec4 _scatterings;
vec4 _control;
};
const int numSamples = 2;
vec3 getAtmosphereInvWaveLength(Atmosphere a) { return a._invWaveLength.xyz; } // 1 / pow(wavelength, 4) for the red, green, and blue channels
float getAtmosphereInnerRadius(Atmosphere a) { return a._radiuses.x; } // The inner (planetary) radius
float getAtmosphereOuterRadius(Atmosphere a) { return a._radiuses.y; } // The outer (atmosphere) radius
float getAtmosphereScale(Atmosphere a) { return a._scales.x; } // 1 / (outerRadius - innerRadius)
float getAtmosphereScaleDepth(Atmosphere a) { return a._scales.y; } // The scale depth (i.e. the altitude at which the atmosphere's average density is found)
float getAtmosphereScaleOverScaleDepth(Atmosphere a) { return a._scales.z; } // scale / scaleDepth
vec4 getAtmosphereScattering(Atmosphere a) { return a._scatterings; } // The full Mie and Rayleigh scattering coefficients
float getAtmosphereKrESun(Atmosphere a) { return a._scatterings.x; } // Kr * ESun
float getAtmosphereKmESun(Atmosphere a) { return a._scatterings.y; } // Km * ESun
float getAtmosphereKr4PI(Atmosphere a) { return a._scatterings.z; } // Kr * 4 * PI
float getAtmosphereKm4PI(Atmosphere a) { return a._scatterings.w; } // Km * 4 * PI
float getAtmosphereNumSamples(Atmosphere a) { return a._control.x; } // numSamples
vec2 getAtmosphereGAndG2(Atmosphere a) { return a._control.yz; } // g and g2
float atmosphereScale(float scaleDepth, float fCos)
{
float x = 1.0 - fCos;
return scaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));
}
vec4 evalAtmosphereContribution(Atmosphere atmospheric, vec3 position, vec3 cameraPos, vec3 lightPos) {
float fInnerRadius = getAtmosphereInnerRadius(atmospheric);
float fSamples = getAtmosphereNumSamples(atmospheric);
vec3 v3InvWavelength = getAtmosphereInvWaveLength(atmospheric);
vec4 scatteringCoefs = getAtmosphereScattering(atmospheric);
float fKrESun = scatteringCoefs.x;
float fKmESun = scatteringCoefs.y;
float fKr4PI = scatteringCoefs.z;
float fKm4PI = scatteringCoefs.w;
vec2 gAndg2 = getAtmosphereGAndG2(atmospheric);
float g = gAndg2.x;
float g2 = gAndg2.y;
float fScale = getAtmosphereScale(atmospheric);
float fScaleDepth = getAtmosphereScaleDepth(atmospheric);
float fScaleOverScaleDepth = getAtmosphereScaleOverScaleDepth(atmospheric);
// Get the ray from the camera to the vertex, and its length (which is the far point of the ray passing through the atmosphere)
vec3 v3Pos = position;
vec3 v3Ray = v3Pos - cameraPos;
float fFar = length(v3Ray);
v3Ray /= fFar;
// Calculate the ray's starting position, then calculate its scattering offset
vec3 v3Start = cameraPos;
float fHeight = length(v3Start);
float fDepthStart = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight));
float fStartAngle = dot(v3Ray, v3Start) / fHeight;
float fStartOffset = fDepthStart * atmosphereScale(fScaleDepth, fStartAngle);
// Initialize the scattering loop variables
//gl_FrontColor = vec4(0.0, 0.0, 0.0, 0.0);
float fSampleLength = fFar / fSamples;
float fScaledLength = fSampleLength * fScale;
vec3 v3SampleRay = v3Ray * fSampleLength;
vec3 v3SamplePoint = v3Start + v3SampleRay * 0.5;
// Now loop through the sample rays
vec3 v3FrontColor = vec3(0.0, 0.0, 0.0);
// int nSamples = numSamples;
int nSamples = int(fSamples);
for(int i=0; i<nSamples; i++)
{
float fHeight = length(v3SamplePoint);
float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight));
float fLightAngle = dot(lightPos, v3SamplePoint) / fHeight;
float fCameraAngle = dot((v3Ray), v3SamplePoint) / fHeight * 0.99;
float fScatter = (fStartOffset + fDepth * (atmosphereScale(fScaleDepth, fLightAngle) - atmosphereScale(fScaleDepth, fCameraAngle)));
vec3 v3Attenuate = exp(-fScatter * (v3InvWavelength * fKr4PI + fKm4PI));
v3FrontColor += v3Attenuate * (fDepth * fScaledLength);
v3SamplePoint += v3SampleRay;
}
// Finally, scale the Mie and Rayleigh colors and set up the varying variables for the pixel shader
vec3 secondaryFrontColor = v3FrontColor * fKmESun;
vec3 frontColor = v3FrontColor * (v3InvWavelength * fKrESun);
vec3 v3Direction = cameraPos - v3Pos;
float fCos = dot(lightPos, v3Direction) / length(v3Direction);
float fMiePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos*fCos) / pow(1.0 + g2 - 2.0*g*fCos, 1.5);
vec4 finalColor;
finalColor.rgb = frontColor.rgb + fMiePhase * secondaryFrontColor.rgb;
finalColor.a = finalColor.b;
finalColor.rgb = pow(finalColor.rgb, vec3(1.0/2.2));
return finalColor;
}
<@if GLPROFILE == PC_GL@>
uniform atmosphereBuffer {
Atmosphere _atmosphere;
};
Atmosphere getAtmosphere() {
return _atmosphere;
}
<@else@>
uniform vec4 atmosphereBuffer[9];
Atmosphere getAtmosphere() {
Atmosphere atmosphere;
atmosphere._invWaveLength = atmosphereBuffer[0];
atmosphere._radiuses = atmosphereBuffer[1];
atmosphere._scales = atmosphereBuffer[2];
atmosphere._scatterings = atmosphereBuffer[3];
atmosphere._control = atmosphereBuffer[4];
return atmosphere;
}
<@endif@>
<!
/*
// uniform vec3 v3CameraPos; // The camera's current position
const int nSamples = 2;
const float fSamples = 2.0;
uniform vec3 v3LightPos;
uniform float g;
uniform float g2;
varying vec3 position;
float scale(float fCos)
{
float x = 1.0 - fCos;
return fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));
}
void main (void)
{
// Get the ray from the camera to the vertex, and its length (which is the far point of the ray passing through the atmosphere)
vec3 v3Pos = position;
vec3 v3Ray = v3Pos - v3CameraPos;
float fFar = length(v3Ray);
v3Ray /= fFar;
// Calculate the ray's starting position, then calculate its scattering offset
vec3 v3Start = v3CameraPos;
float fHeight = length(v3Start);
float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight));
float fStartAngle = dot(v3Ray, v3Start) / fHeight;
float fStartOffset = fDepth * scale(fStartAngle);
// Initialize the scattering loop variables
//gl_FrontColor = vec4(0.0, 0.0, 0.0, 0.0);
float fSampleLength = fFar / fSamples;
float fScaledLength = fSampleLength * fScale;
vec3 v3SampleRay = v3Ray * fSampleLength;
vec3 v3SamplePoint = v3Start + v3SampleRay * 0.5;
// Now loop through the sample rays
vec3 v3FrontColor = vec3(0.0, 0.0, 0.0);
for(int i=0; i<nSamples; i++)
{
float fHeight = length(v3SamplePoint);
float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight));
float fLightAngle = dot(v3LightPos, v3SamplePoint) / fHeight;
float fCameraAngle = dot((v3Ray), v3SamplePoint) / fHeight * 0.99;
float fScatter = (fStartOffset + fDepth * (scale(fLightAngle) - scale(fCameraAngle)));
vec3 v3Attenuate = exp(-fScatter * (v3InvWavelength * fKr4PI + fKm4PI));
v3FrontColor += v3Attenuate * (fDepth * fScaledLength);
v3SamplePoint += v3SampleRay;
}
// Finally, scale the Mie and Rayleigh colors and set up the varying variables for the pixel shader
vec3 secondaryFrontColor = v3FrontColor * fKmESun;
vec3 frontColor = v3FrontColor * (v3InvWavelength * fKrESun);
vec3 v3Direction = v3CameraPos - v3Pos;
float fCos = dot(v3LightPos, v3Direction) / length(v3Direction);
float fMiePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos*fCos) / pow(1.0 + g2 - 2.0*g*fCos, 1.5);
gl_FragColor.rgb = frontColor.rgb + fMiePhase * secondaryFrontColor.rgb;
gl_FragColor.a = gl_FragColor.b;
gl_FragColor.rgb = pow(gl_FragColor.rgb, vec3(1.0/2.2));
}
*/
!>
<@endif@>

View file

@ -76,26 +76,26 @@ public:
void setOpacity(float opacity);
// Schema to access the attribute values of the material
class Schema {
public:
Color _diffuse;
float _opacity;
Color _specular;
float _shininess;
Color _emissive;
float _spare0;
Schema() :
_diffuse(0.5f),
_opacity(1.0f),
_specular(0.03f),
_shininess(0.1f),
_emissive(0.0f)
{}
class Schema {
public:
Color _diffuse;
float _opacity;
Color _specular;
float _shininess;
Color _emissive;
float _spare0;
Schema() :
_diffuse(0.5f),
_opacity(1.0f),
_specular(0.03f),
_shininess(0.1f),
_emissive(0.0f)
{}
};
const UniformBufferView& getSchemaBuffer() const { return _schemaBuffer; }
const UniformBufferView& getSchemaBuffer() const { return _schemaBuffer; }
void setTextureView(MapChannel channel, const TextureView& texture);
const TextureMap& getTextureMap() const { return _textureMap; }

View file

@ -0,0 +1,108 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
//
// For licensing information, see http://http.developer.nvidia.com/GPUGems/gpugems_app01.html:
//
// NVIDIA Statement on the Software
//
// The source code provided is freely distributable, so long as the NVIDIA header remains unaltered and user modifications are
// detailed.
//
// No Warranty
//
// THE SOFTWARE AND ANY OTHER MATERIALS PROVIDED BY NVIDIA ON THE ENCLOSED CD-ROM ARE PROVIDED "AS IS." NVIDIA DISCLAIMS ALL
// WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
// Limitation of Liability
//
// NVIDIA SHALL NOT BE LIABLE TO ANY USER, DEVELOPER, DEVELOPER'S CUSTOMERS, OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH OR
// UNDER DEVELOPER FOR ANY LOSS OF PROFITS, INCOME, SAVINGS, OR ANY OTHER CONSEQUENTIAL, INCIDENTAL, SPECIAL, PUNITIVE, DIRECT
// OR INDIRECT DAMAGES (WHETHER IN AN ACTION IN CONTRACT, TORT OR BASED ON A WARRANTY), EVEN IF NVIDIA HAS BEEN ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGES. THESE LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF THE ESSENTIAL PURPOSE OF ANY
// LIMITED REMEDY. IN NO EVENT SHALL NVIDIA'S AGGREGATE LIABILITY TO DEVELOPER OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH
// OR UNDER DEVELOPER EXCEED THE AMOUNT OF MONEY ACTUALLY PAID BY DEVELOPER TO NVIDIA FOR THE SOFTWARE OR ANY OTHER MATERIALS.
//
//
// Atmospheric scattering fragment shader
//
// Author: Sean O'Neil
//
// Copyright (c) 2004 Sean O'Neil
//
uniform vec3 v3CameraPos; // The camera's current position
uniform vec3 v3InvWavelength; // 1 / pow(wavelength, 4) for the red, green, and blue channels
uniform float fInnerRadius; // The inner (planetary) radius
uniform float fKrESun; // Kr * ESun
uniform float fKmESun; // Km * ESun
uniform float fKr4PI; // Kr * 4 * PI
uniform float fKm4PI; // Km * 4 * PI
uniform float fScale; // 1 / (fOuterRadius - fInnerRadius)
uniform float fScaleDepth; // The scale depth (i.e. the altitude at which the atmosphere's average density is found)
uniform float fScaleOverScaleDepth; // fScale / fScaleDepth
const int nSamples = 2;
const float fSamples = 2.0;
uniform vec3 v3LightPos;
uniform float g;
uniform float g2;
varying vec3 position;
float scale(float fCos)
{
float x = 1.0 - fCos;
return fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));
}
void main (void)
{
// Get the ray from the camera to the vertex, and its length (which is the far point of the ray passing through the atmosphere)
vec3 v3Pos = position;
vec3 v3Ray = v3Pos - v3CameraPos;
float fFar = length(v3Ray);
v3Ray /= fFar;
// Calculate the ray's starting position, then calculate its scattering offset
vec3 v3Start = v3CameraPos;
float fHeight = length(v3Start);
float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight));
float fStartAngle = dot(v3Ray, v3Start) / fHeight;
float fStartOffset = fDepth * scale(fStartAngle);
// Initialize the scattering loop variables
//gl_FrontColor = vec4(0.0, 0.0, 0.0, 0.0);
float fSampleLength = fFar / fSamples;
float fScaledLength = fSampleLength * fScale;
vec3 v3SampleRay = v3Ray * fSampleLength;
vec3 v3SamplePoint = v3Start + v3SampleRay * 0.5;
// Now loop through the sample rays
vec3 v3FrontColor = vec3(0.0, 0.0, 0.0);
for(int i=0; i<nSamples; i++)
{
float fHeight = length(v3SamplePoint);
float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight));
float fLightAngle = dot(v3LightPos, v3SamplePoint) / fHeight;
float fCameraAngle = dot((v3Ray), v3SamplePoint) / fHeight * 0.99;
float fScatter = (fStartOffset + fDepth * (scale(fLightAngle) - scale(fCameraAngle)));
vec3 v3Attenuate = exp(-fScatter * (v3InvWavelength * fKr4PI + fKm4PI));
v3FrontColor += v3Attenuate * (fDepth * fScaledLength);
v3SamplePoint += v3SampleRay;
}
// Finally, scale the Mie and Rayleigh colors and set up the varying variables for the pixel shader
vec3 secondaryFrontColor = v3FrontColor * fKmESun;
vec3 frontColor = v3FrontColor * (v3InvWavelength * fKrESun);
vec3 v3Direction = v3CameraPos - v3Pos;
float fCos = dot(v3LightPos, v3Direction) / length(v3Direction);
float fMiePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos*fCos) / pow(1.0 + g2 - 2.0*g*fCos, 1.5);
gl_FragColor.rgb = frontColor.rgb + fMiePhase * secondaryFrontColor.rgb;
gl_FragColor.a = gl_FragColor.b;
gl_FragColor.rgb = pow(gl_FragColor.rgb, vec3(1.0/2.2));
}

View file

@ -0,0 +1,68 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
//
// For licensing information, see http://http.developer.nvidia.com/GPUGems/gpugems_app01.html:
//
// NVIDIA Statement on the Software
//
// The source code provided is freely distributable, so long as the NVIDIA header remains unaltered and user modifications are
// detailed.
//
// No Warranty
//
// THE SOFTWARE AND ANY OTHER MATERIALS PROVIDED BY NVIDIA ON THE ENCLOSED CD-ROM ARE PROVIDED "AS IS." NVIDIA DISCLAIMS ALL
// WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
// Limitation of Liability
//
// NVIDIA SHALL NOT BE LIABLE TO ANY USER, DEVELOPER, DEVELOPER'S CUSTOMERS, OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH OR
// UNDER DEVELOPER FOR ANY LOSS OF PROFITS, INCOME, SAVINGS, OR ANY OTHER CONSEQUENTIAL, INCIDENTAL, SPECIAL, PUNITIVE, DIRECT
// OR INDIRECT DAMAGES (WHETHER IN AN ACTION IN CONTRACT, TORT OR BASED ON A WARRANTY), EVEN IF NVIDIA HAS BEEN ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGES. THESE LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF THE ESSENTIAL PURPOSE OF ANY
// LIMITED REMEDY. IN NO EVENT SHALL NVIDIA'S AGGREGATE LIABILITY TO DEVELOPER OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH
// OR UNDER DEVELOPER EXCEED THE AMOUNT OF MONEY ACTUALLY PAID BY DEVELOPER TO NVIDIA FOR THE SOFTWARE OR ANY OTHER MATERIALS.
//
//
// Atmospheric scattering vertex shader
//
// Author: Sean O'Neil
//
// Copyright (c) 2004 Sean O'Neil
//
uniform vec3 v3CameraPos; // The camera's current position
uniform vec3 v3LightPos; // The direction vector to the light source
uniform vec3 v3InvWavelength; // 1 / pow(wavelength, 4) for the red, green, and blue channels
uniform float fOuterRadius; // The outer (atmosphere) radius
uniform float fInnerRadius; // The inner (planetary) radius
uniform float fKrESun; // Kr * ESun
uniform float fKmESun; // Km * ESun
uniform float fKr4PI; // Kr * 4 * PI
uniform float fKm4PI; // Km * 4 * PI
uniform float fScale; // 1 / (fOuterRadius - fInnerRadius)
uniform float fScaleDepth; // The scale depth (i.e. the altitude at which the atmosphere's average density is found)
uniform float fScaleOverScaleDepth; // fScale / fScaleDepth
const int nSamples = 2;
const float fSamples = 2.0;
varying vec3 position;
float scale(float fCos)
{
float x = 1.0 - fCos;
return fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));
}
void main(void)
{
// Get the ray from the camera to the vertex, and its length (which is the far point of the ray passing through the atmosphere)
position = gl_Vertex.xyz * fOuterRadius;
gl_Position = gl_ModelViewProjectionMatrix * vec4(position, 1.0);
}

View file

@ -0,0 +1,114 @@
#version 120
//
// For licensing information, see http://http.developer.nvidia.com/GPUGems/gpugems_app01.html:
//
// NVIDIA Statement on the Software
//
// The source code provided is freely distributable, so long as the NVIDIA header remains unaltered and user modifications are
// detailed.
//
// No Warranty
//
// THE SOFTWARE AND ANY OTHER MATERIALS PROVIDED BY NVIDIA ON THE ENCLOSED CD-ROM ARE PROVIDED "AS IS." NVIDIA DISCLAIMS ALL
// WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
// Limitation of Liability
//
// NVIDIA SHALL NOT BE LIABLE TO ANY USER, DEVELOPER, DEVELOPER'S CUSTOMERS, OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH OR
// UNDER DEVELOPER FOR ANY LOSS OF PROFITS, INCOME, SAVINGS, OR ANY OTHER CONSEQUENTIAL, INCIDENTAL, SPECIAL, PUNITIVE, DIRECT
// OR INDIRECT DAMAGES (WHETHER IN AN ACTION IN CONTRACT, TORT OR BASED ON A WARRANTY), EVEN IF NVIDIA HAS BEEN ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGES. THESE LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF THE ESSENTIAL PURPOSE OF ANY
// LIMITED REMEDY. IN NO EVENT SHALL NVIDIA'S AGGREGATE LIABILITY TO DEVELOPER OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH
// OR UNDER DEVELOPER EXCEED THE AMOUNT OF MONEY ACTUALLY PAID BY DEVELOPER TO NVIDIA FOR THE SOFTWARE OR ANY OTHER MATERIALS.
//
//
// Atmospheric scattering fragment shader
//
// Author: Sean O'Neil
//
// Copyright (c) 2004 Sean O'Neil
//
uniform vec3 v3CameraPos; // The camera's current position
uniform vec3 v3LightPos; // The direction vector to the light source
uniform vec3 v3InvWavelength; // 1 / pow(wavelength, 4) for the red, green, and blue channels
uniform float fCameraHeight2; // fCameraHeight^2
uniform float fOuterRadius; // The outer (atmosphere) radius
uniform float fOuterRadius2; // fOuterRadius^2
uniform float fInnerRadius; // The inner (planetary) radius
uniform float fKrESun; // Kr * ESun
uniform float fKmESun; // Km * ESun
uniform float fKr4PI; // Kr * 4 * PI
uniform float fKm4PI; // Km * 4 * PI
uniform float fScale; // 1 / (fOuterRadius - fInnerRadius)
uniform float fScaleDepth; // The scale depth (i.e. the altitude at which the atmosphere's average density is found)
uniform float fScaleOverScaleDepth; // fScale / fScaleDepth
uniform float g;
uniform float g2;
const int nSamples = 2;
const float fSamples = 2.0;
varying vec3 position;
float scale(float fCos)
{
float x = 1.0 - fCos;
return fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));
}
void main (void)
{
// Get the ray from the camera to the vertex and its length (which is the far point of the ray passing through the atmosphere)
vec3 v3Pos = position;
vec3 v3Ray = v3Pos - v3CameraPos;
float fFar = length(v3Ray);
v3Ray /= fFar;
// Calculate the closest intersection of the ray with the outer atmosphere (which is the near point of the ray passing through the atmosphere)
float B = 2.0 * dot(v3CameraPos, v3Ray);
float C = fCameraHeight2 - fOuterRadius2;
float fDet = max(0.0, B*B - 4.0 * C);
float fNear = 0.5 * (-B - sqrt(fDet));
// Calculate the ray's starting position, then calculate its scattering offset
vec3 v3Start = v3CameraPos + v3Ray * fNear;
fFar -= fNear;
float fStartAngle = dot(v3Ray, v3Start) / fOuterRadius;
float fStartDepth = exp(-1.0 / fScaleDepth);
float fStartOffset = fStartDepth * scale(fStartAngle);
// Initialize the scattering loop variables
//gl_FrontColor = vec4(0.0, 0.0, 0.0, 0.0);
float fSampleLength = fFar / fSamples;
float fScaledLength = fSampleLength * fScale;
vec3 v3SampleRay = v3Ray * fSampleLength;
vec3 v3SamplePoint = v3Start + v3SampleRay * 0.5;
// Now loop through the sample rays
vec3 v3FrontColor = vec3(0.0, 0.0, 0.0);
for(int i=0; i<nSamples; i++)
{
float fHeight = length(v3SamplePoint);
float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight));
float fLightAngle = dot(v3LightPos, v3SamplePoint) / fHeight;
float fCameraAngle = dot((v3Ray), v3SamplePoint) / fHeight * 0.99;
float fScatter = (fStartOffset + fDepth * (scale(fLightAngle) - scale(fCameraAngle)));
vec3 v3Attenuate = exp(-fScatter * (v3InvWavelength * fKr4PI + fKm4PI));
v3FrontColor += v3Attenuate * (fDepth * fScaledLength);
v3SamplePoint += v3SampleRay;
}
vec3 v3Direction = v3CameraPos - v3Pos;
float fCos = dot(v3LightPos, v3Direction) / length(v3Direction);
float fMiePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos*fCos) / pow(1.0 + g2 - 2.0*g*fCos, 1.5);
vec3 color = v3FrontColor * (v3InvWavelength * fKrESun);
vec3 secondaryColor = v3FrontColor * fKmESun;
gl_FragColor.rgb = color + fMiePhase * secondaryColor;
gl_FragColor.a = gl_FragColor.b;
gl_FragColor.rgb = pow(gl_FragColor.rgb, vec3(1.0/2.2));
}

View file

@ -0,0 +1,43 @@
#version 120
//
// For licensing information, see http://http.developer.nvidia.com/GPUGems/gpugems_app01.html:
//
// NVIDIA Statement on the Software
//
// The source code provided is freely distributable, so long as the NVIDIA header remains unaltered and user modifications are
// detailed.
//
// No Warranty
//
// THE SOFTWARE AND ANY OTHER MATERIALS PROVIDED BY NVIDIA ON THE ENCLOSED CD-ROM ARE PROVIDED "AS IS." NVIDIA DISCLAIMS ALL
// WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
// Limitation of Liability
//
// NVIDIA SHALL NOT BE LIABLE TO ANY USER, DEVELOPER, DEVELOPER'S CUSTOMERS, OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH OR
// UNDER DEVELOPER FOR ANY LOSS OF PROFITS, INCOME, SAVINGS, OR ANY OTHER CONSEQUENTIAL, INCIDENTAL, SPECIAL, PUNITIVE, DIRECT
// OR INDIRECT DAMAGES (WHETHER IN AN ACTION IN CONTRACT, TORT OR BASED ON A WARRANTY), EVEN IF NVIDIA HAS BEEN ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGES. THESE LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF THE ESSENTIAL PURPOSE OF ANY
// LIMITED REMEDY. IN NO EVENT SHALL NVIDIA'S AGGREGATE LIABILITY TO DEVELOPER OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH
// OR UNDER DEVELOPER EXCEED THE AMOUNT OF MONEY ACTUALLY PAID BY DEVELOPER TO NVIDIA FOR THE SOFTWARE OR ANY OTHER MATERIALS.
//
//
// Atmospheric scattering vertex shader
//
// Author: Sean O'Neil
//
// Copyright (c) 2004 Sean O'Neil
//
uniform float fOuterRadius; // The outer (atmosphere) radius
varying vec3 position;
void main(void)
{
position = gl_Vertex.xyz * fOuterRadius;
gl_Position = gl_ModelViewProjectionMatrix * vec4(position, 1.0);
}

View file

@ -13,6 +13,9 @@
#include <glm/gtx/transform.hpp>
#include <math.h>
#include "SkyFromAtmosphere_vert.h"
#include "SkyFromAtmosphere_frag.h"
using namespace model;
@ -131,6 +134,56 @@ void EarthSunModel::setSunLongitude(float lon) {
_sunLongitude = validateLongitude(lon);
invalidate();
}
Atmosphere::Atmosphere() {
// only if created from nothing shall we create the Buffer to store the properties
Data data;
_dataBuffer = gpu::BufferView(new gpu::Buffer(sizeof(Data), (const gpu::Buffer::Byte*) &data));
setScatteringWavelength(_scatteringWavelength);
setRayleighScattering(_rayleighScattering);
setInnerOuterRadiuses(getInnerRadius(), getOuterRadius());
}
void Atmosphere::setScatteringWavelength(Vec3 wavelength) {
_scatteringWavelength = wavelength;
Data& data = editData();
data._invWaveLength = Vec4(1.0f / powf(wavelength.x, 4.0f), 1.0f / powf(wavelength.y, 4.0f), 1.0f / powf(wavelength.z, 4.0f), 0.0f);
}
void Atmosphere::setRayleighScattering(float scattering) {
_rayleighScattering = scattering;
updateScattering();
}
void Atmosphere::setMieScattering(float scattering) {
_mieScattering = scattering;
updateScattering();
}
void Atmosphere::setSunBrightness(float brightness) {
_sunBrightness = brightness;
updateScattering();
}
void Atmosphere::updateScattering() {
Data& data = editData();
data._scatterings.x = getRayleighScattering() * getSunBrightness();
data._scatterings.y = getMieScattering() * getSunBrightness();
data._scatterings.z = getRayleighScattering() * 4.0f * glm::pi<float>();
data._scatterings.w = getMieScattering() * 4.0f * glm::pi<float>();
}
void Atmosphere::setInnerOuterRadiuses(float inner, float outer) {
Data& data = editData();
data._radiuses.x = inner;
data._radiuses.y = outer;
data._scales.x = 1.0f / (outer - inner);
data._scales.z = data._scales.x / data._scales.y;
}
const int NUM_DAYS_PER_YEAR = 365;
const float NUM_HOURS_PER_DAY = 24.0f;
@ -150,6 +203,12 @@ SunSkyStage::SunSkyStage() :
setDayTime(12.0f);
// Begining of march
setYearTime(60.0f);
auto skyFromAtmosphereVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(SkyFromAtmosphere_vert)));
auto skyFromAtmosphereFragment = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(SkyFromAtmosphere_frag)));
auto skyShader = gpu::ShaderPointer(gpu::Shader::createProgram(skyFromAtmosphereVertex, skyFromAtmosphereFragment));
_skyPipeline = gpu::PipelinePointer(gpu::Pipeline::create(skyShader, gpu::States()));
}
SunSkyStage::~SunSkyStage() {
@ -205,5 +264,12 @@ void SunSkyStage::updateGraphicsObject() const {
double originAlt = _earthSunModel.getAltitude();
_sunLight->setPosition(Vec3(0.0f, originAlt, 0.0f));
static int firstTime = 0;
if (firstTime == 0) {
firstTime++;
bool result = gpu::Shader::makeProgram(*(_skyPipeline->getProgram()));
}
}

View file

@ -11,6 +11,8 @@
#ifndef hifi_model_Stage_h
#define hifi_model_Stage_h
#include "gpu/Pipeline.h"
#include "Light.h"
namespace model {
@ -104,6 +106,60 @@ protected:
static Mat4d evalWorldToGeoLocationMat(double longitude, double latitude, double altitude, double scale);
};
class Atmosphere {
public:
Atmosphere();
Atmosphere(const Atmosphere& atmosphere);
Atmosphere& operator= (const Atmosphere& atmosphere);
virtual ~Atmosphere() {};
void setScatteringWavelength(Vec3 wavelength);
const Vec3& getScatteringWavelength() const { return _scatteringWavelength; }
void setRayleighScattering(float scattering);
float getRayleighScattering() const { return _rayleighScattering; }
void setMieScattering(float scattering);
float getMieScattering() const { return _mieScattering; }
void setSunBrightness(float brightness);
float getSunBrightness() const { return _sunBrightness; }
void setInnerOuterRadiuses(float inner, float outer);
float getInnerRadius() const { return getData()._radiuses.x; }
float getOuterRadius() const { return getData()._radiuses.y; }
// Data to access the attribute values of the atmosphere
class Data {
public:
Vec4 _invWaveLength = Vec4(0.0f);
Vec4 _radiuses = Vec4(6000.0f, 6025.0f, 0.0f, 0.0f);
Vec4 _scales = Vec4(0.0f, 0.25f, 0.0f, 0.0f);
Vec4 _scatterings = Vec4(0.0f);
Vec4 _control = Vec4(2.0f, -0.990f, -0.990f*-0.990f, 0.f);
Data() {}
};
const UniformBufferView& getDataBuffer() const { return _dataBuffer; }
protected:
UniformBufferView _dataBuffer;
Vec3 _scatteringWavelength = Vec3(0.650f, 0.570f, 0.475f);
float _rayleighScattering = 0.0025f;
float _mieScattering = 0.0010f;
float _sunBrightness = 20.0f;
const Data& getData() const { return _dataBuffer.get<Data>(); }
Data& editData() { return _dataBuffer.edit<Data>(); }
void updateScattering();
};
typedef QSharedPointer< Atmosphere > AtmospherePointer;
// Sun sky stage generates the rendering primitives to display a scene realistically
// at the specified location and time around earth
class SunSkyStage {
@ -139,9 +195,13 @@ public:
float getSunIntensity() const { return getSunLight()->getIntensity(); }
LightPointer getSunLight() const { valid(); return _sunLight; }
AtmospherePointer getAtmosphere() const { valid(); return _atmosphere; }
protected:
LightPointer _sunLight;
AtmospherePointer _atmosphere;
gpu::PipelinePointer _skyPipeline;
float _dayTime;
int _yearTime;

View file

@ -19,6 +19,7 @@
#include <QtCore/QUrlQuery>
#include <QtNetwork/QHttpMultiPart>
#include <QtNetwork/QNetworkRequest>
#include <QEventLoop>
#include <qthread.h>
#include <SettingHandle.h>
@ -299,6 +300,8 @@ void AccountManager::processReply() {
passErrorToCallback(requestReply);
}
delete requestReply;
emit replyFinished();
}
void AccountManager::passSuccessToCallback(QNetworkReply* requestReply) {
@ -339,6 +342,15 @@ void AccountManager::passErrorToCallback(QNetworkReply* requestReply) {
}
}
void AccountManager::waitForAllPendingReplies() {
while (_pendingCallbackMap.size() > 0) {
QEventLoop loop;
QObject::connect(this, &AccountManager::replyFinished, &loop, &QEventLoop::quit);
loop.exec();
}
}
void AccountManager::persistAccountToSettings() {
if (_shouldPersistToSettingsFile) {
// store this access token into the local settings

View file

@ -72,6 +72,8 @@ public:
void requestProfile();
DataServerAccountInfo& getAccountInfo() { return _accountInfo; }
void waitForAllPendingReplies();
public slots:
void requestAccessToken(const QString& login, const QString& password);
@ -93,6 +95,8 @@ signals:
void loginFailed();
void logoutComplete();
void balanceChanged(qint64 newBalance);
void replyFinished();
private slots:
void processReply();
void handleKeypairGenerationError();

View file

@ -26,14 +26,14 @@ class Assignment : public NodeData {
public:
enum Type {
AudioMixerType,
AvatarMixerType,
AgentType,
UNUSED_0,
UNUSED_1,
UNUSED_2,
EntityServerType,
AllTypes
AudioMixerType = 0,
AvatarMixerType = 1,
AgentType = 2,
UNUSED_0 = 3,
UNUSED_1 = 4,
UNUSED_2 = 5,
EntityServerType = 6,
AllTypes = 7
};
enum Command {

View file

@ -48,7 +48,8 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short
_localSockAddr(),
_publicSockAddr(),
_stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT),
_packetStatTimer()
_packetStatTimer(),
_thisNodeCanAdjustLocks(false)
{
static bool firstCall = true;
if (firstCall) {

View file

@ -216,7 +216,6 @@ protected:
void handleNodeKill(const SharedNodePointer& node);
QUuid _sessionUUID;
bool _thisNodeCanAdjustLocks;
NodeHash _nodeHash;
QReadWriteLock _nodeMutex;
QUdpSocket _nodeSocket;
@ -230,6 +229,7 @@ protected:
int _numCollectedBytes;
QElapsedTimer _packetStatTimer;
bool _thisNodeCanAdjustLocks;
template<typename IteratorLambda>
void eachNodeHashIterator(IteratorLambda functor) {

View file

@ -74,7 +74,7 @@ PacketVersion versionForPacketType(PacketType type) {
return 1;
case PacketTypeEntityAddOrEdit:
case PacketTypeEntityData:
return VERSION_ENTITIES_USE_METERS_AND_RADIANS;
return VERSION_ENTITIES_HAS_COLLISION_MODEL;
case PacketTypeEntityErase:
return 2;
case PacketTypeAudioStreamStats:

View file

@ -131,6 +131,7 @@ const PacketVersion VERSION_MODEL_ENTITIES_SUPPORT_SHAPE_TYPE = 8;
const PacketVersion VERSION_ENTITIES_LIGHT_HAS_INTENSITY_AND_COLOR_PROPERTIES = 9;
const PacketVersion VERSION_ENTITIES_HAS_PARTICLES = 10;
const PacketVersion VERSION_ENTITIES_USE_METERS_AND_RADIANS = 11;
const PacketVersion VERSION_ENTITIES_HAS_COLLISION_MODEL = 12;
const PacketVersion VERSION_OCTREE_HAS_FILE_BREAKS = 1;
#endif // hifi_PacketHeaders_h

View file

@ -61,7 +61,7 @@ void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCall
params.errorCallbackReceiver = this;
params.errorCallbackMethod = "requestError";
}
accountManager.authenticatedRequest(USER_ACTIVITY_URL,
QNetworkAccessManager::PostOperation,
params,
@ -89,6 +89,8 @@ void UserActivityLogger::launch(QString applicationVersion) {
void UserActivityLogger::close() {
const QString ACTION_NAME = "close";
logAction(ACTION_NAME, QJsonObject());
AccountManager::getInstance().waitForAllPendingReplies();
}
void UserActivityLogger::changedDisplayName(QString displayName) {

View file

@ -18,12 +18,20 @@
#include <cmath>
#include <fstream> // to load voxels from file
#include <QDataStream>
#include <QDebug>
#include <QEventLoop>
#include <QFile>
#include <QFileInfo>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QVector>
#include <GeometryUtil.h>
#include <OctalCode.h>
#include <LogHandler.h>
#include <NetworkAccessManager.h>
#include <OctalCode.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include <Shape.h>
@ -1836,141 +1844,184 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element,
bool Octree::readFromSVOFile(const char* fileName) {
bool fileOk = false;
PacketVersion gotVersion = 0;
std::ifstream file(fileName, std::ios::in|std::ios::binary|std::ios::ate);
QFile file(fileName);
fileOk = file.open(QIODevice::ReadOnly);
if(fileOk) {
QDataStream fileInputStream(&file);
QFileInfo fileInfo(fileName);
unsigned long fileLength = fileInfo.size();
if(file.is_open()) {
emit importSize(1.0f, 1.0f, 1.0f);
emit importProgress(0);
qDebug("Loading file %s...", fileName);
fileOk = readFromStream(fileLength, fileInputStream);
// get file length....
unsigned long fileLength = file.tellg();
file.seekg( 0, std::ios::beg );
emit importProgress(100);
file.close();
}
return fileOk;
}
bool Octree::readFromSVOURL(const QString& urlString) {
bool readOk = false;
// determine if this is a local file or a network resource
QUrl url(urlString);
if (url.isLocalFile()) {
readOk = readFromSVOFile(qPrintable(url.toLocalFile()));
} else {
QNetworkRequest request;
request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
request.setUrl(url);
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkReply* reply = networkAccessManager.get(request);
qDebug() << "Downloading svo at" << qPrintable(urlString);
QEventLoop loop;
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
if (reply->error() == QNetworkReply::NoError) {
int resourceSize = reply->bytesAvailable();
QDataStream inputStream(reply);
readOk = readFromStream(resourceSize, inputStream);
}
}
return readOk;
}
bool Octree::readFromStream(unsigned long streamLength, QDataStream& inputStream) {
bool fileOk = false;
PacketVersion gotVersion = 0;
unsigned long headerLength = 0; // bytes in the header
bool wantImportProgress = true;
PacketType expectedType = expectedDataPacketType();
PacketVersion expectedVersion = versionForPacketType(expectedType);
bool hasBufferBreaks = versionHasSVOfileBreaks(expectedVersion);
// before reading the file, check to see if this version of the Octree supports file versions
if (getWantSVOfileVersions()) {
// read just enough of the file to parse the header...
const unsigned long HEADER_LENGTH = sizeof(PacketType) + sizeof(PacketVersion);
unsigned char fileHeader[HEADER_LENGTH];
inputStream.readRawData((char*)&fileHeader, HEADER_LENGTH);
unsigned long headerLength = 0; // bytes in the header
headerLength = HEADER_LENGTH; // we need this later to skip to the data
unsigned char* dataAt = (unsigned char*)&fileHeader;
unsigned long dataLength = HEADER_LENGTH;
// if so, read the first byte of the file and see if it matches the expected version code
PacketType gotType;
memcpy(&gotType, dataAt, sizeof(gotType));
dataAt += sizeof(expectedType);
dataLength -= sizeof(expectedType);
gotVersion = *dataAt;
bool wantImportProgress = true;
if (gotType == expectedType) {
if (canProcessVersion(gotVersion)) {
dataAt += sizeof(gotVersion);
dataLength -= sizeof(gotVersion);
fileOk = true;
qDebug("SVO file version match. Expected: %d Got: %d",
versionForPacketType(expectedDataPacketType()), gotVersion);
PacketType expectedType = expectedDataPacketType();
PacketVersion expectedVersion = versionForPacketType(expectedType);
bool hasBufferBreaks = versionHasSVOfileBreaks(expectedVersion);
// before reading the file, check to see if this version of the Octree supports file versions
if (getWantSVOfileVersions()) {
// read just enough of the file to parse the header...
const unsigned long HEADER_LENGTH = sizeof(PacketType) + sizeof(PacketVersion);
unsigned char fileHeader[HEADER_LENGTH];
file.read((char*)&fileHeader, HEADER_LENGTH);
headerLength = HEADER_LENGTH; // we need this later to skip to the data
unsigned char* dataAt = (unsigned char*)&fileHeader;
unsigned long dataLength = HEADER_LENGTH;
// if so, read the first byte of the file and see if it matches the expected version code
PacketType gotType;
memcpy(&gotType, dataAt, sizeof(gotType));
dataAt += sizeof(expectedType);
dataLength -= sizeof(expectedType);
gotVersion = *dataAt;
if (gotType == expectedType) {
if (canProcessVersion(gotVersion)) {
dataAt += sizeof(gotVersion);
dataLength -= sizeof(gotVersion);
fileOk = true;
qDebug("SVO file version match. Expected: %d Got: %d",
versionForPacketType(expectedDataPacketType()), gotVersion);
hasBufferBreaks = versionHasSVOfileBreaks(gotVersion);
} else {
qDebug("SVO file version mismatch. Expected: %d Got: %d",
versionForPacketType(expectedDataPacketType()), gotVersion);
}
hasBufferBreaks = versionHasSVOfileBreaks(gotVersion);
} else {
qDebug() << "SVO file type mismatch. Expected: " << nameForPacketType(expectedType)
<< " Got: " << nameForPacketType(gotType);
qDebug("SVO file version mismatch. Expected: %d Got: %d",
versionForPacketType(expectedDataPacketType()), gotVersion);
}
} else {
qDebug() << " NOTE: this file type does not include type and version information.";
fileOk = true; // assume the file is ok
}
if (hasBufferBreaks) {
qDebug() << " this version includes buffer breaks";
} else {
qDebug() << " this version does not include buffer breaks";
qDebug() << "SVO file type mismatch. Expected: " << nameForPacketType(expectedType)
<< " Got: " << nameForPacketType(gotType);
}
if (fileOk) {
} else {
qDebug() << " NOTE: this file type does not include type and version information.";
fileOk = true; // assume the file is ok
}
if (hasBufferBreaks) {
qDebug() << " this version includes buffer breaks";
} else {
qDebug() << " this version does not include buffer breaks";
}
if (fileOk) {
// if this version of the file does not include buffer breaks, then we need to load the entire file at once
if (!hasBufferBreaks) {
// if this version of the file does not include buffer breaks, then we need to load the entire file at once
if (!hasBufferBreaks) {
// read the entire file into a buffer, WHAT!? Why not.
unsigned long dataLength = streamLength - headerLength;
unsigned char* entireFileDataSection = new unsigned char[dataLength];
inputStream.readRawData((char*)entireFileDataSection, dataLength);
unsigned char* dataAt = entireFileDataSection;
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, 0,
SharedNodePointer(), wantImportProgress, gotVersion);
readBitstreamToTree(dataAt, dataLength, args);
delete[] entireFileDataSection;
} else {
// read the entire file into a buffer, WHAT!? Why not.
unsigned long dataLength = fileLength - headerLength;
unsigned char* entireFileDataSection = new unsigned char[dataLength];
file.read((char*)entireFileDataSection, dataLength);
unsigned char* dataAt = entireFileDataSection;
unsigned long dataLength = streamLength - headerLength;
unsigned long remainingLength = dataLength;
const unsigned long MAX_CHUNK_LENGTH = MAX_OCTREE_PACKET_SIZE * 2;
unsigned char* fileChunk = new unsigned char[MAX_CHUNK_LENGTH];
while (remainingLength > 0) {
quint16 chunkLength = 0;
inputStream.readRawData((char*)&chunkLength, sizeof(chunkLength));
remainingLength -= sizeof(chunkLength);
if (chunkLength > remainingLength) {
qDebug() << "UNEXPECTED chunk size of:" << chunkLength
<< "greater than remaining length:" << remainingLength;
break;
}
if (chunkLength > MAX_CHUNK_LENGTH) {
qDebug() << "UNEXPECTED chunk size of:" << chunkLength
<< "greater than MAX_CHUNK_LENGTH:" << MAX_CHUNK_LENGTH;
break;
}
inputStream.readRawData((char*)fileChunk, chunkLength);
remainingLength -= chunkLength;
unsigned char* dataAt = fileChunk;
unsigned long dataLength = chunkLength;
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, 0,
SharedNodePointer(), wantImportProgress, gotVersion);
readBitstreamToTree(dataAt, dataLength, args);
delete[] entireFileDataSection;
} else {
unsigned long dataLength = fileLength - headerLength;
unsigned long remainingLength = dataLength;
const unsigned long MAX_CHUNK_LENGTH = MAX_OCTREE_PACKET_SIZE * 2;
unsigned char* fileChunk = new unsigned char[MAX_CHUNK_LENGTH];
while (remainingLength > 0) {
quint16 chunkLength = 0;
file.read((char*)&chunkLength, sizeof(chunkLength)); // read the chunk size from the file
remainingLength -= sizeof(chunkLength);
if (chunkLength > remainingLength) {
qDebug() << "UNEXPECTED chunk size of:" << chunkLength
<< "greater than remaining length:" << remainingLength;
break;
}
if (chunkLength > MAX_CHUNK_LENGTH) {
qDebug() << "UNEXPECTED chunk size of:" << chunkLength
<< "greater than MAX_CHUNK_LENGTH:" << MAX_CHUNK_LENGTH;
break;
}
file.read((char*)fileChunk, chunkLength); // read in a section of the file larger than the chunk;
remainingLength -= chunkLength;
unsigned char* dataAt = fileChunk;
unsigned long dataLength = chunkLength;
ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, 0,
SharedNodePointer(), wantImportProgress, gotVersion);
readBitstreamToTree(dataAt, dataLength, args);
}
delete[] fileChunk;
}
delete[] fileChunk;
}
emit importProgress(100);
file.close();
}
return fileOk;
}

View file

@ -326,7 +326,10 @@ public:
// these will read/write files that match the wireformat, excluding the 'V' leading
void writeToSVOFile(const char* filename, OctreeElement* element = NULL);
bool readFromSVOFile(const char* filename);
bool readFromSVOURL(const QString& url); // will support file urls as well...
bool readFromStream(unsigned long streamLength, QDataStream& inputStream);
unsigned long getOctreeElementsCount();

View file

@ -0,0 +1,148 @@
//
// MeshInfo.cpp
// libraries/physics/src
//
// Created by Virendra Singh 2015.02.28
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "MeshInfo.h"
#include <iostream>
using namespace meshinfo;
//class to compute volume, mass, center of mass, and inertia tensor of a mesh.
//origin is the default reference point for generating the tetrahedron from each triangle of the mesh.
MeshInfo::MeshInfo(vector<Vertex> *vertices, vector<int> *triangles) :\
_vertices(vertices),
_triangles(triangles),
_centerOfMass(Vertex(0.0, 0.0, 0.0)){
}
MeshInfo::~MeshInfo(){
_vertices = NULL;
_triangles = NULL;
}
inline float MeshInfo::getVolume(const Vertex p1, const Vertex p2, const Vertex p3, const Vertex p4) const{
glm::mat4 tet = { glm::vec4(p1.x, p2.x, p3.x, p4.x), glm::vec4(p1.y, p2.y, p3.y, p4.y), glm::vec4(p1.z, p2.z, p3.z, p4.z),
glm::vec4(1.0f, 1.0f, 1.0f, 1.0f) };
return glm::determinant(tet) / 6.0f;
}
Vertex MeshInfo::getMeshCentroid() const{
return _centerOfMass;
}
vector<float> MeshInfo::computeMassProperties(){
vector<float> volumeAndInertia = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
Vertex origin(0.0, 0.0, 0.0);
glm::mat3 identity;
float meshVolume = 0.0f;
glm::mat3 globalInertiaTensors(0.0);
for (unsigned int i = 0; i < _triangles->size(); i += 3){
Vertex p1 = _vertices->at(_triangles->at(i));
Vertex p2 = _vertices->at(_triangles->at(i + 1));
Vertex p3 = _vertices->at(_triangles->at(i + 2));
float volume = getVolume(p1, p2, p3, origin);
Vertex com = 0.25f * (p1 + p2 + p3);
meshVolume += volume;
_centerOfMass += com * volume;
//centroid is used for calculating inertia tensor relative to center of mass.
// translate the tetrahedron to its center of mass using P = P - centroid
Vertex p0 = origin - com;
p1 = p1 - com;
p2 = p2 - com;
p3 = p3 - com;
//Calculate inertia tensor based on Tonon's Formulae given in the paper mentioned below.
//http://docsdrive.com/pdfs/sciencepublications/jmssp/2005/8-11.pdf
//Explicit exact formulas for the 3-D tetrahedron inertia tensor in terms of its vertex coordinates - F.Tonon
float i11 = (volume * 0.1f) * (
p0.y*p0.y + p0.y*p1.y + p0.y*p2.y + p0.y*p3.y +
p1.y*p1.y + p1.y*p2.y + p1.y*p3.y +
p2.y*p2.y + p2.y*p3.y +
p3.y*p3.y +
p0.z*p0.z + p0.z*p1.z + p0.z*p2.z + p0.z*p3.z +
p1.z*p1.z + p1.z*p2.z + p1.z*p3.z +
p2.z*p2.z + p2.z*p3.z +
p3.z*p3.z);
float i22 = (volume * 0.1f) * (
p0.x*p0.x + p0.x*p1.x + p0.x*p2.x + p0.x*p3.x +
p1.x*p1.x + p1.x*p2.x + p1.x*p3.x +
p2.x*p2.x + p2.x*p3.x +
p3.x*p3.x +
p0.z*p0.z + p0.z*p1.z + p0.z*p2.z + p0.z*p3.z +
p1.z*p1.z + p1.z*p2.z + p1.z*p3.z +
p2.z*p2.z + p2.z*p3.z +
p3.z*p3.z);
float i33 = (volume * 0.1f) * (
p0.x*p0.x + p0.x*p1.x + p0.x*p2.x + p0.x*p3.x +
p1.x*p1.x + p1.x*p2.x + p1.x*p3.x +
p2.x*p2.x + p2.x*p3.x +
p3.x*p3.x +
p0.y*p0.y + p0.y*p1.y + p0.y*p2.y + p0.y*p3.y +
p1.y*p1.y + p1.y*p2.y + p1.y*p3.y +
p2.y*p2.y + p2.y*p3.y +
p3.y*p3.y);
float i23 = -(volume * 0.05f) * (2.0f * (p0.y*p0.z + p1.y*p1.z + p2.y*p2.z + p3.y*p3.z) +
p0.y*p1.z + p0.y*p2.z + p0.y*p3.z +
p1.y*p0.z + p1.y*p2.z + p1.y*p3.z +
p2.y*p0.z + p2.y*p1.z + p2.y*p3.z +
p3.y*p0.z + p3.y*p1.z + p3.y*p2.z);
float i21 = -(volume * 0.05f) * (2.0f * (p0.x*p0.z + p1.x*p1.z + p2.x*p2.z + p3.x*p3.z) +
p0.x*p1.z + p0.x*p2.z + p0.x*p3.z +
p1.x*p0.z + p1.x*p2.z + p1.x*p3.z +
p2.x*p0.z + p2.x*p1.z + p2.x*p3.z +
p3.x*p0.z + p3.x*p1.z + p3.x*p2.z);
float i31 = -(volume * 0.05f) * (2.0f * (p0.x*p0.y + p1.x*p1.y + p2.x*p2.y + p3.x*p3.y) +
p0.x*p1.y + p0.x*p2.y + p0.x*p3.y +
p1.x*p0.y + p1.x*p2.y + p1.x*p3.y +
p2.x*p0.y + p2.x*p1.y + p2.x*p3.y +
p3.x*p0.y + p3.x*p1.y + p3.x*p2.y);
//3x3 of local inertia tensors of each tetrahedron. Inertia tensors are the diagonal elements
// | I11 -I12 -I13 |
// I = | -I21 I22 -I23 |
// | -I31 -I32 I33 |
glm::mat3 localInertiaTensors = { Vertex(i11, i21, i31), Vertex(i21, i22, i23),
Vertex(i31, i23, i33) };
//Translate the inertia tensors from center of mass to origin
//Parallel axis theorem J = I * m[(R.R)*Identity - RxR] where x is outer cross product
globalInertiaTensors += localInertiaTensors + volume * ((glm::dot(com, com) * identity) -
glm::outerProduct(com, com));
}
//Translate accumulated center of mass from each tetrahedron to mesh's center of mass using parallel axis theorem
if (meshVolume == 0){
return volumeAndInertia;
}
_centerOfMass = (_centerOfMass / meshVolume);
//Translate the inertia tensors from origin to mesh's center of mass.
globalInertiaTensors = globalInertiaTensors - meshVolume * ((glm::dot(_centerOfMass, _centerOfMass) *
identity) - glm::outerProduct(_centerOfMass, _centerOfMass));
volumeAndInertia[0] = meshVolume;
volumeAndInertia[1] = globalInertiaTensors[0][0]; //i11
volumeAndInertia[2] = globalInertiaTensors[1][1]; //i22
volumeAndInertia[3] = globalInertiaTensors[2][2]; //i33
volumeAndInertia[4] = -globalInertiaTensors[2][1]; //i23 or i32
volumeAndInertia[5] = -globalInertiaTensors[1][0]; //i21 or i12
volumeAndInertia[6] = -globalInertiaTensors[2][0]; //i13 or i31
return volumeAndInertia;
}

View file

@ -0,0 +1,33 @@
//
// MeshInfo.h
// libraries/physics/src
//
// Created by Virendra Singh 2015.02.28
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_MeshInfo_h
#define hifi_MeshInfo_h
#include <vector>
#include <glm/glm.hpp>
#include <glm/gtx/norm.hpp>
using namespace std;
namespace meshinfo{
typedef glm::vec3 Vertex;
class MeshInfo{
private:
inline float getVolume(const Vertex p1, const Vertex p2, const Vertex p3, const Vertex p4) const;
vector<float> computeVolumeAndInertia(const Vertex p1, const Vertex p2, const Vertex p3, const Vertex p4) const;
public:
vector<Vertex> *_vertices;
Vertex _centerOfMass;
vector<int> *_triangles;
MeshInfo(vector<Vertex> *vertices, vector<int> *triangles);
~MeshInfo();
Vertex getMeshCentroid() const;
vector<float> computeMassProperties();
};
}
#endif // hifi_MeshInfo_h

View file

@ -13,6 +13,7 @@
#include "ShapeInfoUtil.h"
#include "PhysicsHelpers.h"
#include "ThreadSafeDynamicsWorld.h"
#include "AvatarData.h"
static uint32_t _numSubsteps;
@ -286,6 +287,20 @@ void PhysicsEngine::stepSimulation() {
_clock.reset();
float timeStep = btMin(dt, MAX_TIMESTEP);
if (_avatarData->isPhysicsEnabled()) {
_avatarGhostObject->setWorldTransform(btTransform(glmToBullet(_avatarData->getOrientation()),
glmToBullet(_avatarData->getPosition())));
// WORKAROUND: there is a bug in the debug Bullet-2.82 libs where a zero length walk velocity will trigger
// an assert when the getNormalizedVector() helper function in btKinematicCharacterController.cpp tries to
// first normalize a vector before checking its length. Here we workaround the problem by checking the
// length first. NOTE: the character's velocity is reset to zero after each step, so when we DON'T set
// the velocity for this time interval it is the same thing as setting its velocity to zero.
btVector3 walkVelocity = glmToBullet(_avatarData->getVelocity());
if (walkVelocity.length2() > FLT_EPSILON * FLT_EPSILON) {
_characterController->setVelocityForTimeInterval(walkVelocity, timeStep);
}
}
// This is step (2).
int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP);
_numSubsteps += (uint32_t)numSubsteps;
@ -302,9 +317,18 @@ void PhysicsEngine::stepSimulation() {
//
// TODO: untangle these lock sequences.
_entityTree->lockForWrite();
_avatarData->lockForWrite();
lock();
_dynamicsWorld->synchronizeMotionStates();
if (_avatarData->isPhysicsEnabled()) {
const btTransform& avatarTransform = _avatarGhostObject->getWorldTransform();
_avatarData->setOrientation(bulletToGLM(avatarTransform.getRotation()));
_avatarData->setPosition(bulletToGLM(avatarTransform.getOrigin()));
}
unlock();
_avatarData->unlock();
_entityTree->unlock();
computeCollisionEvents();
@ -582,3 +606,32 @@ bool PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
body->activate();
return true;
}
void PhysicsEngine::setAvatarData(AvatarData *avatarData) {
_avatarData = avatarData;
_avatarGhostObject = new btPairCachingGhostObject();
_avatarGhostObject->setWorldTransform(btTransform(glmToBullet(_avatarData->getOrientation()),
glmToBullet(_avatarData->getPosition())));
// XXX these values should be computed from the character model.
btScalar characterRadius = 0.3f;
btScalar characterHeight = 1.75 - 2.0f * characterRadius;
btScalar stepHeight = btScalar(0.35);
btConvexShape* capsule = new btCapsuleShape(characterRadius, characterHeight);
_avatarGhostObject->setCollisionShape(capsule);
_avatarGhostObject->setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT);
_characterController = new btKinematicCharacterController(_avatarGhostObject, capsule, stepHeight);
_dynamicsWorld->addCollisionObject(_avatarGhostObject, btBroadphaseProxy::CharacterFilter,
btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter);
_dynamicsWorld->addAction(_characterController);
_characterController->reset (_dynamicsWorld);
// _characterController->warp (btVector3(10.210001,-2.0306311,16.576973));
btGhostPairCallback* ghostPairCallback = new btGhostPairCallback();
_dynamicsWorld->getPairCache()->setInternalGhostPairCallback(ghostPairCallback);
}

View file

@ -16,6 +16,11 @@
#include <QSet>
#include <btBulletDynamicsCommon.h>
#include <BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h>
#include <BulletCollision/CollisionDispatch/btGhostObject.h>
#include <BulletDynamics/Character/btCharacterControllerInterface.h>
#include <BulletCollision/CollisionShapes/btCapsuleShape.h>
#include <BulletDynamics/Character/btKinematicCharacterController.h>
#include <EntityItem.h>
#include <EntitySimulation.h>
@ -25,6 +30,7 @@
#include "EntityMotionState.h"
#include "ShapeManager.h"
#include "ThreadSafeDynamicsWorld.h"
#include "AvatarData.h"
const float HALF_SIMULATION_EXTENT = 512.0f; // meters
@ -82,6 +88,8 @@ public:
/// process queue of changed from external sources
void relayIncomingChangesToSimulation();
void setAvatarData(AvatarData *avatarData);
private:
/// \param motionState pointer to Object's MotionState
void removeObjectFromBullet(ObjectMotionState* motionState);
@ -113,6 +121,11 @@ private:
ContactMap _contactMap;
uint32_t _numContactFrames = 0;
uint32_t _lastNumSubstepsAtUpdateInternal = 0;
/// character collisions
btCharacterControllerInterface* _characterController = 0;
class btPairCachingGhostObject* _avatarGhostObject = 0;
AvatarData *_avatarData = 0;
};
#endif // hifi_PhysicsEngine_h

View file

@ -61,6 +61,8 @@ public:
bool getShapeCollisions(const Shape* shape, CollisionList& collisions) const;
void setupAvatarCollision();
protected:
void integrate(float deltaTime);

View file

@ -13,6 +13,7 @@
<@include DeferredLighting.slh@>
struct SphericalHarmonics {
vec4 L00;
vec4 L1m1;

View file

@ -270,6 +270,12 @@ void DeferredLightingEffect::render() {
batch.setUniformBuffer(locations->lightBufferUnit, globalLight->getSchemaBuffer());
gpu::GLBackend::renderBatch(batch);
}
if (_atmosphere && (locations->atmosphereBufferUnit >= 0)) {
gpu::Batch batch;
batch.setUniformBuffer(locations->atmosphereBufferUnit, _atmosphere->getDataBuffer());
gpu::GLBackend::renderBatch(batch);
}
glUniformMatrix4fv(locations->invViewMat, 1, false, reinterpret_cast< const GLfloat* >(&invViewMat));
}
@ -511,6 +517,31 @@ void DeferredLightingEffect::loadLightProgram(const char* fragSource, bool limit
locations.lightBufferUnit = -1;
}
#endif
#if defined(Q_OS_MAC)
loc = program.uniformLocation("atmosphereBufferUnit");
if (loc >= 0) {
locations.atmosphereBufferUnit = loc;
} else {
locations.atmosphereBufferUnit = -1;
}
#elif defined(Q_OS_WIN)
loc = glGetUniformBlockIndex(program.programId(), "atmosphereBufferUnit");
if (loc >= 0) {
glUniformBlockBinding(program.programId(), loc, 1);
locations.atmosphereBufferUnit = 1;
} else {
locations.atmosphereBufferUnit = -1;
}
#else
loc = program.uniformLocation("atmosphereBufferUnit");
if (loc >= 0) {
locations.atmosphereBufferUnit = loc;
} else {
locations.atmosphereBufferUnit = -1;
}
#endif
program.release();
}

View file

@ -20,6 +20,7 @@
#include "ProgramObject.h"
#include "model/Light.h"
#include "model/Stage.h"
class AbstractViewStateInterface;
class PostLightingRenderable;
@ -73,6 +74,7 @@ public:
// update global lighting
void setAmbientLightMode(int preset);
void setGlobalLight(const glm::vec3& direction, const glm::vec3& diffuse, float intensity);
void setGlobalAtmosphere(const model::AtmospherePointer& atmosphere) { _atmosphere = atmosphere; }
private:
DeferredLightingEffect() {}
@ -89,6 +91,7 @@ private:
int radius;
int ambientSphere;
int lightBufferUnit;
int atmosphereBufferUnit;
int invViewMat;
};
@ -146,6 +149,7 @@ private:
AbstractViewStateInterface* _viewState;
int _ambientLightMode = 0;
model::AtmospherePointer _atmosphere;
};
/// Simple interface for objects that require something to be rendered after deferred lighting.

View file

@ -2084,6 +2084,7 @@ void GeometryReader::run() {
urlValid &= !urlname.isEmpty();
urlValid &= !_url.path().isEmpty();
urlValid &= _url.path().toLower().endsWith(".fbx")
|| _url.path().toLower().endsWith(".obj")
|| _url.path().toLower().endsWith(".svo");
if (urlValid) {
@ -2101,6 +2102,8 @@ void GeometryReader::run() {
lightmapLevel = 3.5f;
}
fbxgeo = readFBX(_reply, _mapping, grabLightmaps, lightmapLevel);
} else if (_url.path().toLower().endsWith(".obj")) {
fbxgeo = readOBJ(_reply, _mapping);
}
QMetaObject::invokeMethod(geometry.data(), "setGeometry", Q_ARG(const FBXGeometry&, fbxgeo));
} else {

View file

@ -22,6 +22,7 @@
#include <ResourceCache.h>
#include <FBXReader.h>
#include <OBJReader.h>
#include <AnimationCache.h>

View file

@ -198,51 +198,51 @@ void Model::initProgram(ProgramObject& program, Model::Locations& locations, boo
locations.emissiveTextureUnit = -1;
}
// bindable uniform version
#if defined(Q_OS_MAC)
loc = program.uniformLocation("materialBuffer");
if (loc >= 0) {
locations.materialBufferUnit = loc;
} else {
locations.materialBufferUnit = -1;
}
#elif defined(Q_OS_WIN)
loc = glGetUniformBlockIndex(program.programId(), "materialBuffer");
if (loc >= 0) {
glUniformBlockBinding(program.programId(), loc, 1);
locations.materialBufferUnit = 1;
} else {
locations.materialBufferUnit = -1;
}
#else
loc = program.uniformLocation("materialBuffer");
if (loc >= 0) {
locations.materialBufferUnit = loc;
} else {
locations.materialBufferUnit = -1;
}
#endif
#if defined(Q_OS_WIN)
loc = glGetUniformBlockIndex(program.programId(), "transformObjectBuffer");
if (loc >= 0) {
glUniformBlockBinding(program.programId(), loc, gpu::TRANSFORM_OBJECT_SLOT);
// locations.materialBufferUnit = 1;
}
#endif
#if defined(Q_OS_WIN)
loc = glGetUniformBlockIndex(program.programId(), "transformCameraBuffer");
if (loc >= 0) {
glUniformBlockBinding(program.programId(), loc, gpu::TRANSFORM_CAMERA_SLOT);
// locations.materialBufferUnit = 1;
}
#endif
// bindable uniform version
#if defined(Q_OS_MAC)
loc = program.uniformLocation("materialBuffer");
if (loc >= 0) {
locations.materialBufferUnit = loc;
} else {
locations.materialBufferUnit = -1;
}
#elif defined(Q_OS_WIN)
loc = glGetUniformBlockIndex(program.programId(), "materialBuffer");
if (loc >= 0) {
glUniformBlockBinding(program.programId(), loc, 1);
locations.materialBufferUnit = 1;
} else {
locations.materialBufferUnit = -1;
}
#else
loc = program.uniformLocation("materialBuffer");
if (loc >= 0) {
locations.materialBufferUnit = loc;
} else {
locations.materialBufferUnit = -1;
}
#endif
#if defined(Q_OS_WIN)
loc = glGetUniformBlockIndex(program.programId(), "transformObjectBuffer");
if (loc >= 0) {
glUniformBlockBinding(program.programId(), loc, gpu::TRANSFORM_OBJECT_SLOT);
// locations.materialBufferUnit = 1;
}
#endif
#if defined(Q_OS_WIN)
loc = glGetUniformBlockIndex(program.programId(), "transformCameraBuffer");
if (loc >= 0) {
glUniformBlockBinding(program.programId(), loc, gpu::TRANSFORM_CAMERA_SLOT);
// locations.materialBufferUnit = 1;
}
#endif
//program.link();
if (!program.isLinked()) {
program.release();
}
if (!program.isLinked()) {
program.release();
}
program.release();
}
@ -299,10 +299,93 @@ void Model::initJointTransforms() {
void Model::init() {
if (!_program.isLinked()) {
/* //Work in progress not used yet
gpu::Shader::BindingSet slotBindings;
slotBindings.insert(gpu::Shader::Binding(std::string("materialBuffer"), 1));
slotBindings.insert(gpu::Shader::Binding(std::string("diffuseMap"), 0));
slotBindings.insert(gpu::Shader::Binding(std::string("normalMap"), 1));
slotBindings.insert(gpu::Shader::Binding(std::string("specularMap"), 2));
slotBindings.insert(gpu::Shader::Binding(std::string("emissiveMap"), 3));
// Vertex shaders
auto modelVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(model_vert)));
auto modelNormalMapVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(model_normal_map_vert)));
auto modelLightmapVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(model_lightmap_vert)));
auto modelLightmapNormalMapVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(model_lightmap_normal_map_vert)));
auto modelShadowVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(model_shadow_vert)));
auto skinModelVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(skin_model_vert)));
auto skinModelNormalMapVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(skin_model_normal_map_vert)));
auto skinModelShadowVertex = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(skin_model_shadow_vert)));
// Pixel shaders
auto modelPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_frag)));
auto modelNormalMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_normal_map_frag)));
auto modelSpecularMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_specular_map_frag)));
auto modelNormalSpecularMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_normal_specular_map_frag)));
auto modelTranslucentPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_translucent_frag)));
auto modelShadowPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_shadow_frag)));
auto modelLightmapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_lightmap_frag)));
auto modelLightmapNormalMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_lightmap_normal_map_frag)));
auto modelLightmapSpecularMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_lightmap_specular_map_frag)));
auto modelLightmapNormalSpecularMapPixel = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(model_lightmap_normal_specular_map_frag)));
bool makeResult = false;
// Programs
auto program = gpu::ShaderPointer(gpu::Shader::createProgram(modelVertex, modelPixel));
makeResult = gpu::Shader::makeProgram(*program, slotBindings);
auto normalMapProgram = gpu::ShaderPointer(gpu::Shader::createProgram(modelNormalMapVertex, modelNormalMapPixel));
makeResult = gpu::Shader::makeProgram(*normalMapProgram, slotBindings);
auto specularMapProgram = gpu::ShaderPointer(gpu::Shader::createProgram(modelVertex, modelSpecularMapPixel));
makeResult = gpu::Shader::makeProgram(*specularMapProgram, slotBindings);
auto normalSpecularMapProgram = gpu::ShaderPointer(gpu::Shader::createProgram(modelNormalMapVertex, modelNormalSpecularMapPixel));
makeResult = gpu::Shader::makeProgram(*normalSpecularMapProgram, slotBindings);
auto translucentProgram = gpu::ShaderPointer(gpu::Shader::createProgram(modelVertex, modelTranslucentPixel));
makeResult = gpu::Shader::makeProgram(*translucentProgram, slotBindings);
auto shadowProgram = gpu::ShaderPointer(gpu::Shader::createProgram(modelShadowVertex, modelShadowPixel));
makeResult = gpu::Shader::makeProgram(*shadowProgram, slotBindings);
auto lightmapProgram = gpu::ShaderPointer(gpu::Shader::createProgram(modelLightmapVertex, modelLightmapPixel));
makeResult = gpu::Shader::makeProgram(*lightmapProgram, slotBindings);
auto lightmapNormalMapProgram = gpu::ShaderPointer(gpu::Shader::createProgram(modelLightmapNormalMapVertex, modelLightmapNormalMapPixel));
makeResult = gpu::Shader::makeProgram(*lightmapNormalMapProgram, slotBindings);
auto lightmapSpecularMapProgram = gpu::ShaderPointer(gpu::Shader::createProgram(modelLightmapVertex, modelLightmapSpecularMapPixel));
makeResult = gpu::Shader::makeProgram(*lightmapSpecularMapProgram, slotBindings);
auto lightmapNormalSpecularMapProgram = gpu::ShaderPointer(gpu::Shader::createProgram(modelLightmapNormalMapVertex, modelLightmapNormalSpecularMapPixel));
makeResult = gpu::Shader::makeProgram(*lightmapNormalSpecularMapProgram, slotBindings);
auto skinProgram = gpu::ShaderPointer(gpu::Shader::createProgram(skinModelVertex, modelPixel));
makeResult = gpu::Shader::makeProgram(*skinProgram, slotBindings);
auto skinNormalMapProgram = gpu::ShaderPointer(gpu::Shader::createProgram(skinModelNormalMapVertex, modelNormalMapPixel));
makeResult = gpu::Shader::makeProgram(*skinNormalMapProgram, slotBindings);
auto skinSpecularMapProgram = gpu::ShaderPointer(gpu::Shader::createProgram(skinModelVertex, modelSpecularMapPixel));
makeResult = gpu::Shader::makeProgram(*skinSpecularMapProgram, slotBindings);
auto skinNormalSpecularMapProgram = gpu::ShaderPointer(gpu::Shader::createProgram(skinModelNormalMapVertex, modelNormalSpecularMapPixel));
makeResult = gpu::Shader::makeProgram(*skinNormalSpecularMapProgram, slotBindings);
auto skinShadowProgram = gpu::ShaderPointer(gpu::Shader::createProgram(skinModelShadowVertex, modelShadowPixel));
makeResult = gpu::Shader::makeProgram(*skinShadowProgram, slotBindings);
auto skinTranslucentProgram = gpu::ShaderPointer(gpu::Shader::createProgram(skinModelVertex, modelTranslucentPixel));
makeResult = gpu::Shader::makeProgram(*skinTranslucentProgram, slotBindings);
*/
_program.addShaderFromSourceCode(QGLShader::Vertex, model_vert);
_program.addShaderFromSourceCode(QGLShader::Fragment, model_frag);
initProgram(_program, _locations);
_normalMapProgram.addShaderFromSourceCode(QGLShader::Vertex, model_normal_map_vert);
_normalMapProgram.addShaderFromSourceCode(QGLShader::Fragment, model_normal_map_frag);
initProgram(_normalMapProgram, _normalMapLocations);
@ -366,7 +449,7 @@ void Model::init() {
_skinTranslucentProgram.addShaderFromSourceCode(QGLShader::Vertex, skin_model_vert);
_skinTranslucentProgram.addShaderFromSourceCode(QGLShader::Fragment, model_translucent_frag);
_skinTranslucentProgram.addShaderFromSourceCode(QGLShader::Fragment, model_translucent_frag);
initSkinProgram(_skinTranslucentProgram, _skinTranslucentLocations);
}
}
@ -2470,16 +2553,16 @@ int Model::renderMeshesFromList(QVector<int>& list, gpu::Batch& batch, RenderMod
qDebug() << "part INDEX:" << j;
qDebug() << "NEW part.materialID:" << part.materialID;
}
if (locations->glowIntensity >= 0) {
GLBATCH(glUniform1f)(locations->glowIntensity, glowEffect->getIntensity());
if (locations->glowIntensity >= 0) {
GLBATCH(glUniform1f)(locations->glowIntensity, glowEffect->getIntensity());
}
if (!(translucent && alphaThreshold == 0.0f)) {
GLBATCH(glAlphaFunc)(GL_EQUAL, glowEffect->getIntensity());
}
if (locations->materialBufferUnit >= 0) {
batch.setUniformBuffer(locations->materialBufferUnit, material->getSchemaBuffer());
if (locations->materialBufferUnit >= 0) {
batch.setUniformBuffer(locations->materialBufferUnit, material->getSchemaBuffer());
}
Texture* diffuseMap = networkPart.diffuseTexture.data();

View file

@ -309,6 +309,7 @@ private:
int _blendNumber;
int _appliedBlendNumber;
static ProgramObject _program;
static ProgramObject _normalMapProgram;
static ProgramObject _specularMapProgram;

View file

@ -32,6 +32,7 @@ enum ExamplePropertyList {
EXAMPLE_PROP_POSITION,
EXAMPLE_PROP_RADIUS,
EXAMPLE_PROP_MODEL_URL,
EXAMPLE_PROP_COLLISION_MODEL_URL,
EXAMPLE_PROP_ROTATION,
EXAMPLE_PROP_COLOR,
EXAMPLE_PROP_SCRIPT,
@ -73,6 +74,7 @@ void OctreeTests::propertyFlagsTests(bool verbose) {
props.setHasProperty(PROP_POSITION);
props.setHasProperty(PROP_RADIUS);
props.setHasProperty(PROP_MODEL_URL);
props.setHasProperty(PROP_COLLISION_MODEL_URL);
props.setHasProperty(PROP_ROTATION);
QByteArray encoded = props.encode();

View file

@ -0,0 +1,235 @@
//
// MeshInfoTests.cpp
// tests/physics/src
//
// Created by Virendra Singh on 2015.03.02
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <iostream>
#include <iomanip>
#include <MeshInfo.h>
#include "MeshInfoTests.h"
const float epsilon = 0.015f;
void MeshInfoTests::testWithTetrahedron(){
glm::vec3 p0(8.33220, -11.86875, 0.93355);
glm::vec3 p1(0.75523, 5.00000, 16.37072);
glm::vec3 p2(52.61236, 5.00000, -5.38580);
glm::vec3 p3(2.00000, 5.00000, 3.00000);
glm::vec3 centroid(15.92492, 0.782813, 3.72962);
//translate the tetrahedron so that its apex is on origin
glm::vec3 p11 = p1 - p0;
glm::vec3 p22 = p2 - p0;
glm::vec3 p33 = p3 - p0;
vector<glm::vec3> vertices = { p11, p22, p33 };
vector<int> triangles = { 0, 1, 2 };
float volume = 1873.233236f;
float inertia_a = 43520.33257f;
//actual should be 194711.28938f. But for some reason it becomes 194711.296875 during
//runtime due to how floating points are stored.
float inertia_b = 194711.289f;
float inertia_c = 191168.76173f;
float inertia_aa = 4417.66150f;
float inertia_bb = -46343.16662f;
float inertia_cc = 11996.20119f;
meshinfo::MeshInfo meshinfo(&vertices,&triangles);
vector<float> voumeAndInertia = meshinfo.computeMassProperties();
glm::vec3 tetCenterOfMass = meshinfo.getMeshCentroid();
//get original center of mass
tetCenterOfMass = tetCenterOfMass + p0;
glm::vec3 diff = centroid - tetCenterOfMass;
std::cout << std::setprecision(12);
//test if centroid is correct
if (diff.x > epsilon || diff.y > epsilon || diff.z > epsilon){
std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Centroid is incorrect : Expected = " << centroid.x << " " <<
centroid.y << " " << centroid.z << ", actual = " << tetCenterOfMass.x << " " << tetCenterOfMass.y <<
" " << tetCenterOfMass.z << std::endl;
}
//test if volume is correct
if (abs(volume - voumeAndInertia.at(0)) > epsilon){
std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Volume is incorrect : Expected = " << volume << " " <<
", actual = " << voumeAndInertia.at(0) << std::endl;
}
//test if moment of inertia with respect to x axis is correct
if (abs(inertia_a - (voumeAndInertia.at(1))) > epsilon){
std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment of inertia with respect to x axis is incorrect : Expected = " <<
inertia_a << " " << ", actual = " << voumeAndInertia.at(1) << std::endl;
}
//test if moment of inertia with respect to y axis is correct
if (abs(inertia_b - voumeAndInertia.at(2)) > epsilon){
std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment of inertia with respect to y axis is incorrect : Expected = " <<
inertia_b << " " << ", actual = " << (voumeAndInertia.at(2)) << std::endl;
}
//test if moment of inertia with respect to z axis is correct
if (abs(inertia_c - (voumeAndInertia.at(3))) > epsilon){
std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment of inertia with respect to z axis is incorrect : Expected = " <<
inertia_c << " " << ", actual = " << (voumeAndInertia.at(3)) << std::endl;
}
//test if product of inertia with respect to x axis is correct
if (abs(inertia_aa - (voumeAndInertia.at(4))) > epsilon){
std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Product of inertia with respect to x axis is incorrect : Expected = " <<
inertia_aa << " " << ", actual = " << (voumeAndInertia.at(4)) << std::endl;
}
//test if product of inertia with respect to y axis is correct
if (abs(inertia_bb - (voumeAndInertia.at(5))) > epsilon){
std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Product of inertia with respect to y axis is incorrect : Expected = " <<
inertia_bb << " " << ", actual = " << (voumeAndInertia.at(5)) << std::endl;
}
//test if product of inertia with respect to z axis is correct
if (abs(inertia_cc - (voumeAndInertia.at(6))) > epsilon){
std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Product of inertia with respect to z axis is incorrect : Expected = " <<
inertia_cc << " " << ", actual = " << (voumeAndInertia.at(6)) << std::endl;
}
}
void MeshInfoTests::testWithTetrahedronAsMesh(){
glm::vec3 p0(8.33220, -11.86875, 0.93355);
glm::vec3 p1(0.75523, 5.00000, 16.37072);
glm::vec3 p2(52.61236, 5.00000, -5.38580);
glm::vec3 p3(2.00000, 5.00000, 3.00000);
glm::vec3 centroid(15.92492, 0.782813, 3.72962);
float volume = 1873.233236f;
float inertia_a = 43520.33257f;
//actual should be 194711.28938f. But for some reason it becomes 194711.296875 during
//runtime due to how floating points are stored.
float inertia_b = 194711.289f;
float inertia_c = 191168.76173f;
float inertia_aa = 4417.66150f;
float inertia_bb = -46343.16662f;
float inertia_cc = 11996.20119f;
std::cout << std::setprecision(12);
vector<glm::vec3> vertices = { p0, p1, p2, p3 };
vector<int> triangles = { 0, 2, 1, 0, 3, 2, 0, 1, 3, 1, 2, 3 };
meshinfo::MeshInfo massProp(&vertices, &triangles);
vector<float> volumeAndInertia = massProp.computeMassProperties();
std::cout << volumeAndInertia[0] << " " << volumeAndInertia[1] << " " << volumeAndInertia[2]
<< " " << volumeAndInertia[3]
<< " " << volumeAndInertia[4]
<< " " << volumeAndInertia[5] << " " << volumeAndInertia[6] << std::endl;
//translate the tetrahedron so that the model is placed at origin i.e. com is at origin
p0 -= centroid;
p1 -= centroid;
p2 -= centroid;
p3 -= centroid;
}
void MeshInfoTests::testWithCube(){
glm::vec3 p0(1.0, -1.0, -1.0);
glm::vec3 p1(1.0, -1.0, 1.0);
glm::vec3 p2(-1.0, -1.0, 1.0);
glm::vec3 p3(-1.0, -1.0, -1.0);
glm::vec3 p4(1.0, 1.0, -1.0);
glm::vec3 p5(1.0, 1.0, 1.0);
glm::vec3 p6(-1.0, 1.0, 1.0);
glm::vec3 p7(-1.0, 1.0, -1.0);
vector<glm::vec3> vertices;
vertices.push_back(p0);
vertices.push_back(p1);
vertices.push_back(p2);
vertices.push_back(p3);
vertices.push_back(p4);
vertices.push_back(p5);
vertices.push_back(p6);
vertices.push_back(p7);
std::cout << std::setprecision(10);
vector<int> triangles = { 0, 1, 2, 0, 2, 3, 4, 7, 6, 4, 6, 5, 0, 4, 5, 0, 5, 1, 1, 5, 6, 1, 6, 2, 2, 6,
7, 2, 7, 3, 4, 0, 3, 4, 3, 7 };
glm::vec3 centerOfMass(0.0, 0.0, 0.0);
double volume = 8.0;
double side = 2.0;
double inertia = (volume * side * side) / 6.0; //inertia of a unit cube is (mass * side * side) /6
//test with origin as reference point
meshinfo::MeshInfo massProp(&vertices, &triangles);
vector<float> volumeAndInertia = massProp.computeMassProperties();
if (abs(centerOfMass.x - massProp.getMeshCentroid().x) > epsilon || abs(centerOfMass.y - massProp.getMeshCentroid().y) > epsilon ||
abs(centerOfMass.z - massProp.getMeshCentroid().z) > epsilon){
std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Center of mass is incorrect : Expected = " << centerOfMass.x << " " <<
centerOfMass.y << " " << centerOfMass.z << ", actual = " << massProp.getMeshCentroid().x << " " <<
massProp.getMeshCentroid().y << " " << massProp.getMeshCentroid().z << std::endl;
}
if (abs(volume - volumeAndInertia.at(0)) > epsilon){
std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Volume is incorrect : Expected = " << volume <<
", actual = " << volumeAndInertia.at(0) << std::endl;
}
if (abs(inertia - (volumeAndInertia.at(1))) > epsilon || abs(inertia - (volumeAndInertia.at(2))) > epsilon ||
abs(inertia - (volumeAndInertia.at(3))) > epsilon){
std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment of inertia is incorrect : Expected = " << inertia << " " <<
inertia << " " << inertia << ", actual = " << (volumeAndInertia.at(1)) << " " << (volumeAndInertia.at(2)) <<
" " << (volumeAndInertia.at(3)) << std::endl;
}
}
void MeshInfoTests::testWithUnitCube()
{
glm::vec3 p0(0, 0, 1);
glm::vec3 p1(1, 0, 1);
glm::vec3 p2(0, 1, 1);
glm::vec3 p3(1, 1, 1);
glm::vec3 p4(0, 0, 0);
glm::vec3 p5(1, 0, 0);
glm::vec3 p6(0, 1, 0);
glm::vec3 p7(1, 1, 0);
vector<glm::vec3> vertices;
vertices.push_back(p0);
vertices.push_back(p1);
vertices.push_back(p2);
vertices.push_back(p3);
vertices.push_back(p4);
vertices.push_back(p5);
vertices.push_back(p6);
vertices.push_back(p7);
vector<int> triangles = { 0, 1, 2, 1, 3, 2, 2, 3, 7, 2, 7, 6, 1, 7, 3, 1, 5, 7, 6, 7, 4, 7, 5, 4, 0, 4, 1,
1, 4, 5, 2, 6, 4, 0, 2, 4 };
glm::vec3 centerOfMass(0.5, 0.5, 0.5);
double volume = 1.0;
double side = 1.0;
double inertia = (volume * side * side) / 6.0; //inertia of a unit cube is (mass * side * side) /6
std::cout << std::setprecision(10);
//test with origin as reference point
meshinfo::MeshInfo massProp(&vertices, &triangles);
vector<float> volumeAndInertia = massProp.computeMassProperties();
if (abs(centerOfMass.x - massProp.getMeshCentroid().x) > epsilon || abs(centerOfMass.y - massProp.getMeshCentroid().y) >
epsilon || abs(centerOfMass.z - massProp.getMeshCentroid().z) > epsilon){
std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Center of mass is incorrect : Expected = " << centerOfMass.x <<
" " << centerOfMass.y << " " << centerOfMass.z << ", actual = " << massProp.getMeshCentroid().x << " " <<
massProp.getMeshCentroid().y << " " << massProp.getMeshCentroid().z << std::endl;
}
if (abs(volume - volumeAndInertia.at(0)) > epsilon){
std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Volume is incorrect : Expected = " << volume <<
", actual = " << volumeAndInertia.at(0) << std::endl;
}
if (abs(inertia - (volumeAndInertia.at(1))) > epsilon || abs(inertia - (volumeAndInertia.at(2))) > epsilon ||
abs(inertia - (volumeAndInertia.at(3))) > epsilon){
std::cout << __FILE__ << ":" << __LINE__ << " ERROR : Moment of inertia is incorrect : Expected = " << inertia << " " <<
inertia << " " << inertia << ", actual = " << (volumeAndInertia.at(1)) << " " << (volumeAndInertia.at(2)) <<
" " << (volumeAndInertia.at(3)) << std::endl;
}
}
void MeshInfoTests::runAllTests(){
testWithTetrahedron();
testWithTetrahedronAsMesh();
testWithUnitCube();
testWithCube();
}

View file

@ -0,0 +1,21 @@
//
// MeshInfoTests.h
// tests/physics/src
//
// Created by Virendra Singh on 2015.03.02
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_MeshInfoTests_h
#define hifi_MeshInfoTests_h
namespace MeshInfoTests{
void testWithTetrahedron();
void testWithUnitCube();
void testWithCube();
void runAllTests();
void testWithTetrahedronAsMesh();
}
#endif // hifi_MeshInfoTests_h

View file

@ -13,6 +13,7 @@
#include "ShapeInfoTests.h"
#include "ShapeManagerTests.h"
#include "BulletUtilTests.h"
#include "MeshInfoTests.h"
int main(int argc, char** argv) {
ShapeColliderTests::runAllTests();
@ -20,5 +21,6 @@ int main(int argc, char** argv) {
ShapeInfoTests::runAllTests();
ShapeManagerTests::runAllTests();
BulletUtilTests::runAllTests();
MeshInfoTests::runAllTests();
return 0;
}

View file

@ -192,9 +192,17 @@ int main (int argc, char** argv) {
targetStringStream << "#ifndef scribe_" << targetName << "_h" << std::endl;
targetStringStream << "#define scribe_" << targetName << "_h" << std::endl << std::endl;
targetStringStream << "const char " << targetName << "[] = R\"XXXX(" << destStringStream.str() << ")XXXX\";";
// targetStringStream << "const char " << targetName << "[] = R\"XXXX(" << destStringStream.str() << ")XXXX\";";
std::istringstream destStringStreamAgain(destStringStream.str());
targetStringStream << "const char " << targetName << "[] = \n";
while (!destStringStreamAgain.eof()) {
std::string lineToken;
std::getline(destStringStreamAgain, lineToken);
// targetStringStream << "\"" << lineToken << "\"\n";
targetStringStream << "R\"X(" << lineToken << ")X\"\"\\n\"\n";
}
targetStringStream << std::endl << std::endl;
targetStringStream << ";\n" << std::endl << std::endl;
targetStringStream << "#endif" << std::endl;
} else {
targetStringStream << destStringStream.str();

View file

@ -1,5 +1,5 @@
set(TARGET_NAME vhacd)
setup_hifi_project()
set(TARGET_NAME vhacd-util)
setup_hifi_project(Core Widgets)
link_hifi_libraries(shared model fbx gpu networking octree)
#find_package(VHACD REQUIRED) done in CMakeList.txt in parent directory

View file

@ -14,7 +14,7 @@
//Read all the meshes from provided FBX file
bool vhacd::VHACDUtil::loadFBX(const QString filename, vhacd::LoadFBXResults *results){
bool vhacd::VHACDUtil::loadFBX(const QString filename, vhacd::LoadFBXResults *results) {
// open the fbx file
QFile fbx(filename);
@ -24,11 +24,25 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, vhacd::LoadFBXResults *re
std::cout << "Reading FBX.....\n";
QByteArray fbxContents = fbx.readAll();
FBXGeometry geometry = readFBX(fbxContents, QVariantHash());
FBXGeometry geometry;
if (filename.toLower().endsWith(".obj")) {
geometry = readOBJ(fbxContents, QVariantHash());
} else if (filename.toLower().endsWith(".fbx")) {
geometry = readFBX(fbxContents, QVariantHash());
} else {
qDebug() << "unknown file extension";
return false;
}
//results->meshCount = geometry.meshes.count();
// qDebug() << "read in" << geometry.meshes.count() << "meshes";
int count = 0;
foreach(FBXMesh mesh, geometry.meshes){
foreach(FBXMesh mesh, geometry.meshes) {
//get vertices for each mesh
QVector<glm::vec3> vertices = mesh.vertices;
@ -40,9 +54,9 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, vhacd::LoadFBXResults *re
}
//only read meshes with triangles
if (triangles.count() <= 0){
continue;
}
if (triangles.count() <= 0){
continue;
}
results->perMeshVertices.append(vertices);
results->perMeshTriangleIndices.append(triangles);
count++;
@ -82,6 +96,23 @@ bool vhacd::VHACDUtil::computeVHACD(vhacd::LoadFBXResults *meshes, VHACD::IVHACD
for (unsigned int j = 0; j < nConvexHulls; j++){
VHACD::IVHACD::ConvexHull hull;
interfaceVHACD->GetConvexHull(j, hull);
double *m_points_copy = new double[hull.m_nPoints * 3];
// std::copy(std::begin(hull.m_points), std::end(hull.m_points), std::begin(m_points_copy));
for (unsigned int i=0; i<hull.m_nPoints * 3; i++) {
m_points_copy[ i ] = hull.m_points[ i ];
}
hull.m_points = m_points_copy;
int *m_triangles_copy = new int[hull.m_nTriangles * 3];
// std::copy(std::begin(hull.m_triangles), std::end(hull.m_triangles), std::begin(m_triangles_copy));
for (unsigned int i=0; i<hull.m_nTriangles * 3; i++) {
m_triangles_copy[ i ] = hull.m_triangles[ i ];
}
hull.m_triangles = m_triangles_copy;
convexHulls.append(hull);
}
results->convexHullList.append(convexHulls);

View file

@ -19,30 +19,31 @@
#include <chrono> //c++11 feature
#include <QFile>
#include <FBXReader.h>
#include <OBJReader.h>
#include <VHACD.h>
namespace vhacd{
namespace vhacd {
typedef struct{
typedef struct {
int meshCount;
QVector<int> convexHullsCountList;
QVector<QVector<VHACD::IVHACD::ConvexHull>> convexHullList;
}ComputeResults;
} ComputeResults;
typedef struct{
typedef struct {
int meshCount;
QVector<QVector<glm::vec3>> perMeshVertices;
QVector<QVector<int>> perMeshTriangleIndices;
}LoadFBXResults;
} LoadFBXResults;
class VHACDUtil{
class VHACDUtil {
public:
bool loadFBX(const QString filename, vhacd::LoadFBXResults *results);
bool computeVHACD(vhacd::LoadFBXResults *meshes, VHACD::IVHACD::Parameters params, vhacd::ComputeResults *results)const;
~VHACDUtil();
};
class ProgressCallback : public VHACD::IVHACD::IUserCallback{
class ProgressCallback : public VHACD::IVHACD::IUserCallback {
public:
ProgressCallback(void);
~ProgressCallback();
@ -52,4 +53,4 @@ namespace vhacd{
const char * const stage, const char * const operation);
};
}
#endif //hifi_VHACDUtil_h
#endif //hifi_VHACDUtil_h

View file

@ -0,0 +1,224 @@
//
// VHACDUtil.h
// tools/vhacd/src
//
// Created by Seth Alves on 3/5/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QCommandLineParser>
#include <VHACD.h>
#include "VHACDUtilApp.h"
#include "VHACDUtil.h"
using namespace std;
using namespace VHACD;
QString formatFloat(double n) {
// limit precision to 6, but don't output trailing zeros.
QString s = QString::number(n, 'f', 6);
while (s.endsWith("0")) {
s.remove(s.size() - 1, 1);
}
if (s.endsWith(".")) {
s.remove(s.size() - 1, 1);
}
return s;
}
bool writeOBJ(QString outFileName, QVector<QVector<VHACD::IVHACD::ConvexHull>>& meshList, bool outputOneMesh) {
QFile file(outFileName);
if (!file.open(QIODevice::WriteOnly)) {
qDebug() << "Unable to write to " << outFileName;
return false;
}
QTextStream out(&file);
unsigned int pointStartOffset = 0;
foreach (QVector<VHACD::IVHACD::ConvexHull> hulls, meshList) {
unsigned int nth = 0;
foreach (VHACD::IVHACD::ConvexHull hull, hulls) {
out << "g hull-" << nth++ << "\n";
for (unsigned int i = 0; i < hull.m_nPoints; i++) {
out << "v ";
out << formatFloat(hull.m_points[i*3]) << " ";
// swap y and z because up is 3rd value in OBJ
out << formatFloat(hull.m_points[i*3+2]) << " ";
out << formatFloat(hull.m_points[i*3+1]) << "\n";
}
for (unsigned int i = 0; i < hull.m_nTriangles; i++) {
out << "f ";
// change order to flip normal (due to swapping y and z, above)
out << hull.m_triangles[i*3+1] + 1 + pointStartOffset << " ";
out << hull.m_triangles[i*3] + 1 + pointStartOffset << " ";
out << hull.m_triangles[i*3+2] + 1 + pointStartOffset << "\n";
}
out << "\n";
pointStartOffset += hull.m_nPoints;
}
}
return true;
}
VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
QCoreApplication(argc, argv)
{
vector<int> triangles; // array of indexes
vector<float> points; // array of coordinates
vhacd::VHACDUtil vUtil;
vhacd::LoadFBXResults fbx; //mesh data from loaded fbx file
vhacd::ComputeResults results; // results after computing vhacd
VHACD::IVHACD::Parameters params;
vhacd::ProgressCallback pCallBack;
// parse command-line
QCommandLineParser parser;
parser.setApplicationDescription("High Fidelity Object Decomposer");
parser.addHelpOption();
const QCommandLineOption helpOption = parser.addHelpOption();
const QCommandLineOption outputOneMeshOption("1", "output hulls as single mesh");
parser.addOption(outputOneMeshOption);
const QCommandLineOption inputFilenameOption("i", "input file", "filename.fbx");
parser.addOption(inputFilenameOption);
const QCommandLineOption outputFilenameOption("o", "output file", "filename.obj");
parser.addOption(outputFilenameOption);
if (!parser.parse(QCoreApplication::arguments())) {
qCritical() << parser.errorText() << endl;
parser.showHelp();
Q_UNREACHABLE();
}
if (parser.isSet(helpOption)) {
parser.showHelp();
Q_UNREACHABLE();
}
bool outputOneMesh = parser.isSet(outputOneMeshOption);
QString inputFilename;
// check for an assignment pool passed on the command line or in the config
if (parser.isSet(inputFilenameOption)) {
inputFilename = parser.value(inputFilenameOption);
}
QString outputFilename;
// check for an assignment pool passed on the command line or in the config
if (parser.isSet(outputFilenameOption)) {
outputFilename = parser.value(outputFilenameOption);
}
if (inputFilename == "") {
cerr << "input filename is required.";
parser.showHelp();
Q_UNREACHABLE();
}
if (outputFilename == "") {
cerr << "output filename is required.";
parser.showHelp();
Q_UNREACHABLE();
}
//set parameters for V-HACD
params.m_callback = &pCallBack; //progress callback
params.m_resolution = 100000; // 100000
params.m_depth = 20; // 20
params.m_concavity = 0.001; // 0.001
params.m_delta = 0.01; // 0.05
params.m_planeDownsampling = 4; // 4
params.m_convexhullDownsampling = 4; // 4
params.m_alpha = 0.05; // 0.05 // controls the bias toward clipping along symmetry planes
params.m_beta = 0.05; // 0.05
params.m_gamma = 0.0005; // 0.0005
params.m_pca = 0; // 0 enable/disable normalizing the mesh before applying the convex decomposition
params.m_mode = 0; // 0: voxel-based (recommended), 1: tetrahedron-based
params.m_maxNumVerticesPerCH = 64; // 64
params.m_minVolumePerCH = 0.00001; // 0.0001
params.m_callback = 0; // 0
params.m_logger = 0; // 0
params.m_convexhullApproximation = true; // true
params.m_oclAcceleration = true; // true
// load the mesh
auto begin = std::chrono::high_resolution_clock::now();
if (!vUtil.loadFBX(inputFilename, &fbx)){
cout << "Error in opening FBX file....";
}
auto end = std::chrono::high_resolution_clock::now();
auto loadDuration = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count();
//perform vhacd computation
begin = std::chrono::high_resolution_clock::now();
if (!vUtil.computeVHACD(&fbx, params, &results)){
cout << "Compute Failed...";
}
end = std::chrono::high_resolution_clock::now();
auto computeDuration = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count();
int totalVertices = 0;
for (int i = 0; i < fbx.meshCount; i++){
totalVertices += fbx.perMeshVertices.at(i).count();
}
int totalTriangles = 0;
for (int i = 0; i < fbx.meshCount; i++){
totalTriangles += fbx.perMeshTriangleIndices.at(i).count();
}
int totalHulls = 0;
QVector<int> hullCounts = results.convexHullsCountList;
for (int i = 0; i < results.meshCount; i++){
totalHulls += hullCounts.at(i);
}
cout << endl << "Summary of V-HACD Computation..................." << endl;
cout << "File Path : " << inputFilename.toStdString() << endl;
cout << "Number Of Meshes : " << fbx.meshCount << endl;
cout << "Processed Meshes : " << results.meshCount << endl;
cout << "Total vertices : " << totalVertices << endl;
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;
cout << endl << "Summary per convex hull ........................" << endl <<endl;
for (int i = 0; i < results.meshCount; i++) {
cout << "Mesh : " << i + 1 << endl;
QVector<VHACD::IVHACD::ConvexHull> chList = results.convexHullList.at(i);
cout << "\t" << "Number Of Hulls : " << chList.count() << endl;
for (int j = 0; j < results.convexHullList.at(i).count(); j++){
cout << "\tHUll : " << j + 1 << endl;
cout << "\t\tNumber Of Points : " << chList.at(j).m_nPoints << endl;
cout << "\t\tNumber Of Triangles : " << chList.at(j).m_nTriangles << endl;
}
}
writeOBJ(outputFilename, results.convexHullList, outputOneMesh);
}
VHACDUtilApp::~VHACDUtilApp() {
}

View file

@ -0,0 +1,28 @@
//
// VHACDUtil.h
// tools/vhacd/src
//
// Created by Seth Alves on 3/5/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_VHACDUtilApp_h
#define hifi_VHACDUtilApp_h
#include <QApplication>
class VHACDUtilApp : public QCoreApplication {
Q_OBJECT
public:
VHACDUtilApp(int argc, char* argv[]);
~VHACDUtilApp();
};
#endif //hifi_VHACDUtilApp_h

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