mirror of
https://github.com/overte-org/overte.git
synced 2025-04-23 19:53:30 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into upload_thumbnail
This commit is contained in:
commit
a949af3296
101 changed files with 4476 additions and 900 deletions
|
@ -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()
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
88
interface/src/DiscoverabilityManager.cpp
Normal file
88
interface/src/DiscoverabilityManager.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
45
interface/src/DiscoverabilityManager.h
Normal file
45
interface/src/DiscoverabilityManager.h
Normal 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
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ class GLCanvas : public QGLWidget {
|
|||
|
||||
public:
|
||||
GLCanvas();
|
||||
|
||||
void stopFrameTimer();
|
||||
|
||||
bool isThrottleRendering() const;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,8 @@ AvatarData::AvatarData() :
|
|||
_billboard(),
|
||||
_errorLogExpiry(0),
|
||||
_owningAvatarMixer(),
|
||||
_lastUpdateTimer()
|
||||
_lastUpdateTimer(),
|
||||
_velocity(0.0f)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -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*)
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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, "");
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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(); }
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -152,8 +152,6 @@ public:
|
|||
|
||||
bool hasSpecularTexture() const;
|
||||
bool hasEmissiveTexture() const;
|
||||
|
||||
model::Mesh _mesh;
|
||||
};
|
||||
|
||||
/// A single animation frame extracted from an FBX document.
|
||||
|
|
287
libraries/fbx/src/OBJReader.cpp
Normal file
287
libraries/fbx/src/OBJReader.cpp
Normal 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;
|
||||
}
|
6
libraries/fbx/src/OBJReader.h
Normal file
6
libraries/fbx/src/OBJReader.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
|
||||
#include "FBXReader.h"
|
||||
|
||||
FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping);
|
||||
FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping);
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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:
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
66
libraries/gpu/src/gpu/GLBackendBuffer.cpp
Executable file
66
libraries/gpu/src/gpu/GLBackendBuffer.cpp
Executable 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;
|
||||
}
|
||||
}
|
||||
|
223
libraries/gpu/src/gpu/GLBackendInput.cpp
Executable file
223
libraries/gpu/src/gpu/GLBackendInput.cpp
Executable 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();
|
||||
}
|
95
libraries/gpu/src/gpu/GLBackendPipeline.cpp
Executable file
95
libraries/gpu/src/gpu/GLBackendPipeline.cpp
Executable 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;
|
||||
}
|
||||
}
|
||||
|
685
libraries/gpu/src/gpu/GLBackendShader.cpp
Executable file
685
libraries/gpu/src/gpu/GLBackendShader.cpp
Executable 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;
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
156
libraries/gpu/src/gpu/GLBackendTransform.cpp
Executable file
156
libraries/gpu/src/gpu/GLBackendTransform.cpp
Executable 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;
|
||||
}
|
||||
|
||||
|
34
libraries/gpu/src/gpu/Pipeline.cpp
Executable file
34
libraries/gpu/src/gpu/Pipeline.cpp
Executable 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;
|
||||
}
|
53
libraries/gpu/src/gpu/Pipeline.h
Executable file
53
libraries/gpu/src/gpu/Pipeline.h
Executable 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
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
73
libraries/gpu/src/gpu/Shader.cpp
Executable file
73
libraries/gpu/src/gpu/Shader.cpp
Executable 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
165
libraries/gpu/src/gpu/Shader.h
Executable 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
20
libraries/gpu/src/gpu/State.cpp
Executable 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
88
libraries/gpu/src/gpu/State.h
Executable 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
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
245
libraries/model/src/model/Atmosphere.slh
Executable file
245
libraries/model/src/model/Atmosphere.slh
Executable 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@>
|
|
@ -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; }
|
||||
|
|
108
libraries/model/src/model/SkyFromAtmosphere.slf
Executable file
108
libraries/model/src/model/SkyFromAtmosphere.slf
Executable 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));
|
||||
}
|
68
libraries/model/src/model/SkyFromAtmosphere.slv
Executable file
68
libraries/model/src/model/SkyFromAtmosphere.slv
Executable 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);
|
||||
}
|
114
libraries/model/src/model/SkyFromSpace.slf
Executable file
114
libraries/model/src/model/SkyFromSpace.slf
Executable 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));
|
||||
}
|
43
libraries/model/src/model/SkyFromSpace.slv
Executable file
43
libraries/model/src/model/SkyFromSpace.slv
Executable 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);
|
||||
}
|
|
@ -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()));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
148
libraries/physics/src/MeshInfo.cpp
Normal file
148
libraries/physics/src/MeshInfo.cpp
Normal 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;
|
||||
}
|
33
libraries/physics/src/MeshInfo.h
Normal file
33
libraries/physics/src/MeshInfo.h
Normal 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
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -61,6 +61,8 @@ public:
|
|||
|
||||
bool getShapeCollisions(const Shape* shape, CollisionList& collisions) const;
|
||||
|
||||
void setupAvatarCollision();
|
||||
|
||||
protected:
|
||||
void integrate(float deltaTime);
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
<@include DeferredLighting.slh@>
|
||||
|
||||
|
||||
struct SphericalHarmonics {
|
||||
vec4 L00;
|
||||
vec4 L1m1;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <ResourceCache.h>
|
||||
|
||||
#include <FBXReader.h>
|
||||
#include <OBJReader.h>
|
||||
|
||||
#include <AnimationCache.h>
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -309,6 +309,7 @@ private:
|
|||
int _blendNumber;
|
||||
int _appliedBlendNumber;
|
||||
|
||||
|
||||
static ProgramObject _program;
|
||||
static ProgramObject _normalMapProgram;
|
||||
static ProgramObject _specularMapProgram;
|
||||
|
|
|
@ -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();
|
||||
|
|
235
tests/physics/src/MeshInfoTests.cpp
Normal file
235
tests/physics/src/MeshInfoTests.cpp
Normal 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();
|
||||
}
|
21
tests/physics/src/MeshInfoTests.h
Normal file
21
tests/physics/src/MeshInfoTests.h
Normal 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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
224
tools/vhacd/src/VHACDUtilApp.cpp
Normal file
224
tools/vhacd/src/VHACDUtilApp.cpp
Normal 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() {
|
||||
}
|
28
tools/vhacd/src/VHACDUtilApp.h
Normal file
28
tools/vhacd/src/VHACDUtilApp.h
Normal 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
Loading…
Reference in a new issue