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

This commit is contained in:
Nissim Hadar 2018-01-10 09:30:05 -08:00
commit 9f25b01b10
44 changed files with 776 additions and 279 deletions

View file

@ -36,6 +36,7 @@ Rectangle {
property bool pendingInventoryReply: true; property bool pendingInventoryReply: true;
property bool isShowingMyItems: false; property bool isShowingMyItems: false;
property bool isDebuggingFirstUseTutorial: false; property bool isDebuggingFirstUseTutorial: false;
property int pendingItemCount: 0;
// Style // Style
color: hifi.colors.white; color: hifi.colors.white;
Connections { Connections {
@ -79,18 +80,22 @@ Rectangle {
onInventoryResult: { onInventoryResult: {
purchasesReceived = true; purchasesReceived = true;
if (root.pendingInventoryReply) {
inventoryTimer.start();
}
if (result.status !== 'success') { if (result.status !== 'success') {
console.log("Failed to get purchases", result.message); console.log("Failed to get purchases", result.message);
} else { } else if (!purchasesContentsList.dragging) { // Don't modify the view if the user's scrolling
var inventoryResult = processInventoryResult(result.data.assets); var inventoryResult = processInventoryResult(result.data.assets);
var currentIndex = purchasesContentsList.currentIndex === -1 ? 0 : purchasesContentsList.currentIndex;
purchasesModel.clear(); purchasesModel.clear();
purchasesModel.append(inventoryResult); purchasesModel.append(inventoryResult);
root.pendingItemCount = 0;
for (var i = 0; i < purchasesModel.count; i++) {
if (purchasesModel.get(i).status === "pending") {
root.pendingItemCount++;
}
}
if (previousPurchasesModel.count !== 0) { if (previousPurchasesModel.count !== 0) {
checkIfAnyItemStatusChanged(); checkIfAnyItemStatusChanged();
} else { } else {
@ -103,6 +108,12 @@ Rectangle {
previousPurchasesModel.append(inventoryResult); previousPurchasesModel.append(inventoryResult);
buildFilteredPurchasesModel(); buildFilteredPurchasesModel();
purchasesContentsList.positionViewAtIndex(currentIndex, ListView.Beginning);
}
if (root.pendingInventoryReply && root.pendingItemCount > 0) {
inventoryTimer.start();
} }
root.pendingInventoryReply = false; root.pendingInventoryReply = false;
@ -419,6 +430,8 @@ Rectangle {
visible: (root.isShowingMyItems && filteredPurchasesModel.count !== 0) || (!root.isShowingMyItems && filteredPurchasesModel.count !== 0); visible: (root.isShowingMyItems && filteredPurchasesModel.count !== 0) || (!root.isShowingMyItems && filteredPurchasesModel.count !== 0);
clip: true; clip: true;
model: filteredPurchasesModel; model: filteredPurchasesModel;
snapMode: ListView.SnapToItem;
highlightRangeMode: ListView.StrictlyEnforceRange;
// Anchors // Anchors
anchors.top: root.canRezCertifiedItems ? separator.bottom : cantRezCertified.bottom; anchors.top: root.canRezCertifiedItems ? separator.bottom : cantRezCertified.bottom;
anchors.topMargin: 12; anchors.topMargin: 12;

View file

@ -25,8 +25,12 @@ Item {
HifiConstants { id: hifi; } HifiConstants { id: hifi; }
id: root; id: root;
property bool historyReceived: false; property bool initialHistoryReceived: false;
property bool historyRequestPending: true;
property bool noMoreHistoryData: false;
property int pendingCount: 0; property int pendingCount: 0;
property int currentHistoryPage: 1;
property var pagesAlreadyAdded: new Array();
Connections { Connections {
target: Commerce; target: Commerce;
@ -36,32 +40,86 @@ Item {
} }
onHistoryResult : { onHistoryResult : {
historyReceived = true; root.initialHistoryReceived = true;
if (result.status === 'success') { root.historyRequestPending = false;
var sameItemCount = 0;
tempTransactionHistoryModel.clear();
tempTransactionHistoryModel.append(result.data.history);
for (var i = 0; i < tempTransactionHistoryModel.count; i++) {
if (!transactionHistoryModel.get(i)) {
sameItemCount = -1;
break;
} else if (tempTransactionHistoryModel.get(i).transaction_type === transactionHistoryModel.get(i).transaction_type &&
tempTransactionHistoryModel.get(i).text === transactionHistoryModel.get(i).text) {
sameItemCount++;
}
}
if (sameItemCount !== tempTransactionHistoryModel.count) { if (result.status === 'success') {
transactionHistoryModel.clear(); var currentPage = parseInt(result.current_page);
if (result.data.history.length === 0) {
root.noMoreHistoryData = true;
console.log("No more data to retrieve from Commerce.history() endpoint.")
} else if (root.currentHistoryPage === 1) {
var sameItemCount = 0;
tempTransactionHistoryModel.clear();
tempTransactionHistoryModel.append(result.data.history);
for (var i = 0; i < tempTransactionHistoryModel.count; i++) { for (var i = 0; i < tempTransactionHistoryModel.count; i++) {
transactionHistoryModel.append(tempTransactionHistoryModel.get(i)); if (!transactionHistoryModel.get(i)) {
sameItemCount = -1;
break;
} else if (tempTransactionHistoryModel.get(i).transaction_type === transactionHistoryModel.get(i).transaction_type &&
tempTransactionHistoryModel.get(i).text === transactionHistoryModel.get(i).text) {
sameItemCount++;
}
}
if (sameItemCount !== tempTransactionHistoryModel.count) {
transactionHistoryModel.clear();
for (var i = 0; i < tempTransactionHistoryModel.count; i++) {
transactionHistoryModel.append(tempTransactionHistoryModel.get(i));
}
calculatePendingAndInvalidated();
}
} else {
if (root.pagesAlreadyAdded.indexOf(currentPage) !== -1) {
console.log("Page " + currentPage + " of history has already been added to the list.");
} else {
// First, add the history result to a temporary model
tempTransactionHistoryModel.clear();
tempTransactionHistoryModel.append(result.data.history);
// Make a note that we've already added this page to the model...
root.pagesAlreadyAdded.push(currentPage);
var insertionIndex = 0;
// If there's nothing in the model right now, we don't need to modify insertionIndex.
if (transactionHistoryModel.count !== 0) {
var currentIteratorPage;
// Search through the whole transactionHistoryModel and look for the insertion point.
// The insertion point is found when the result page from the server is less than
// the page that the current item came from, OR when we've reached the end of the whole model.
for (var i = 0; i < transactionHistoryModel.count; i++) {
currentIteratorPage = transactionHistoryModel.get(i).resultIsFromPage;
if (currentPage < currentIteratorPage) {
insertionIndex = i;
break;
} else if (i === transactionHistoryModel.count - 1) {
insertionIndex = i + 1;
break;
}
}
}
// Go through the results we just got back from the server, setting the "resultIsFromPage"
// property of those results and adding them to the main model.
for (var i = 0; i < tempTransactionHistoryModel.count; i++) {
tempTransactionHistoryModel.setProperty(i, "resultIsFromPage", currentPage);
transactionHistoryModel.insert(i + insertionIndex, tempTransactionHistoryModel.get(i))
}
calculatePendingAndInvalidated();
} }
calculatePendingAndInvalidated();
} }
} }
refreshTimer.start();
// Only auto-refresh if the user hasn't scrolled
// and there is more data to grab
if (transactionHistory.atYBeginning && !root.noMoreHistoryData) {
refreshTimer.start();
}
} }
} }
@ -134,9 +192,13 @@ Item {
onVisibleChanged: { onVisibleChanged: {
if (visible) { if (visible) {
historyReceived = false; transactionHistoryModel.clear();
Commerce.balance(); Commerce.balance();
Commerce.history(); initialHistoryReceived = false;
root.currentHistoryPage = 1;
root.noMoreHistoryData = false;
root.historyRequestPending = true;
Commerce.history(root.currentHistoryPage);
} else { } else {
refreshTimer.stop(); refreshTimer.stop();
} }
@ -164,9 +226,12 @@ Item {
id: refreshTimer; id: refreshTimer;
interval: 4000; interval: 4000;
onTriggered: { onTriggered: {
console.log("Refreshing Wallet Home..."); if (transactionHistory.atYBeginning) {
Commerce.balance(); console.log("Refreshing 1st Page of Recent Activity...");
Commerce.history(); root.historyRequestPending = true;
Commerce.balance();
Commerce.history(1);
}
} }
} }
@ -241,7 +306,7 @@ Item {
anchors.right: parent.right; anchors.right: parent.right;
Item { Item {
visible: transactionHistoryModel.count === 0 && root.historyReceived; visible: transactionHistoryModel.count === 0 && root.initialHistoryReceived;
anchors.centerIn: parent; anchors.centerIn: parent;
width: parent.width - 12; width: parent.width - 12;
height: parent.height; height: parent.height;
@ -364,7 +429,12 @@ Item {
onAtYEndChanged: { onAtYEndChanged: {
if (transactionHistory.atYEnd) { if (transactionHistory.atYEnd) {
console.log("User scrolled to the bottom of 'Recent Activity'."); console.log("User scrolled to the bottom of 'Recent Activity'.");
// Grab next page of results and append to model if (!root.historyRequestPending && !root.noMoreHistoryData) {
// Grab next page of results and append to model
root.historyRequestPending = true;
Commerce.history(++root.currentHistoryPage);
console.log("Fetching Page " + root.currentHistoryPage + " of Recent Activity...");
}
} }
} }
} }

View file

@ -1851,6 +1851,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
DependencyManager::get<EntityTreeRenderer>()->setSetPrecisionPickingOperator([&](unsigned int rayPickID, bool value) { DependencyManager::get<EntityTreeRenderer>()->setSetPrecisionPickingOperator([&](unsigned int rayPickID, bool value) {
DependencyManager::get<PickManager>()->setPrecisionPicking(rayPickID, value); DependencyManager::get<PickManager>()->setPrecisionPicking(rayPickID, value);
}); });
EntityTreeRenderer::setRenderDebugHullsOperator([] {
return Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls);
});
// Preload Tablet sounds // Preload Tablet sounds
DependencyManager::get<TabletScriptingInterface>()->preloadSounds(); DependencyManager::get<TabletScriptingInterface>()->preloadSounds();
@ -5004,8 +5007,6 @@ void Application::update(float deltaTime) {
} }
} }
PerformanceTimer perfTimer("misc");
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "Application::update()"); PerformanceWarning warn(showWarnings, "Application::update()");
@ -5014,11 +5015,13 @@ void Application::update(float deltaTime) {
// TODO: break these out into distinct perfTimers when they prove interesting // TODO: break these out into distinct perfTimers when they prove interesting
{ {
PROFILE_RANGE(app, "PickManager"); PROFILE_RANGE(app, "PickManager");
PerformanceTimer perfTimer("pickManager");
DependencyManager::get<PickManager>()->update(); DependencyManager::get<PickManager>()->update();
} }
{ {
PROFILE_RANGE(app, "PointerManager"); PROFILE_RANGE(app, "PointerManager");
PerformanceTimer perfTimer("pointerManager");
DependencyManager::get<PointerManager>()->update(); DependencyManager::get<PointerManager>()->update();
} }
@ -5044,8 +5047,8 @@ void Application::update(float deltaTime) {
// Update my voxel servers with my current voxel query... // Update my voxel servers with my current voxel query...
{ {
PROFILE_RANGE_EX(app, "QueryOctree", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount()); PROFILE_RANGE_EX(app, "QueryOctree", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
QMutexLocker viewLocker(&_viewMutex);
PerformanceTimer perfTimer("queryOctree"); PerformanceTimer perfTimer("queryOctree");
QMutexLocker viewLocker(&_viewMutex);
quint64 sinceLastQuery = now - _lastQueriedTime; quint64 sinceLastQuery = now - _lastQueriedTime;
const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND; const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND;
bool queryIsDue = sinceLastQuery > TOO_LONG_SINCE_LAST_QUERY; bool queryIsDue = sinceLastQuery > TOO_LONG_SINCE_LAST_QUERY;
@ -5081,12 +5084,14 @@ void Application::update(float deltaTime) {
} }
} }
avatarManager->postUpdate(deltaTime, getMain3DScene()); {
PerformanceTimer perfTimer("avatarManager/postUpdate");
avatarManager->postUpdate(deltaTime, getMain3DScene());
}
{ {
PROFILE_RANGE_EX(app, "PreRenderLambdas", 0xffff0000, (uint64_t)0); PROFILE_RANGE_EX(app, "PostUpdateLambdas", 0xffff0000, (uint64_t)0);
PerformanceTimer perfTimer("postUpdateLambdas");
std::unique_lock<std::mutex> guard(_postUpdateLambdasLock); std::unique_lock<std::mutex> guard(_postUpdateLambdasLock);
for (auto& iter : _postUpdateLambdas) { for (auto& iter : _postUpdateLambdas) {
iter.second(); iter.second();
@ -5095,6 +5100,7 @@ void Application::update(float deltaTime) {
} }
editRenderArgs([this](AppRenderArgs& appRenderArgs) { editRenderArgs([this](AppRenderArgs& appRenderArgs) {
PerformanceTimer perfTimer("editRenderArgs");
appRenderArgs._headPose= getHMDSensorPose(); appRenderArgs._headPose= getHMDSensorPose();
auto myAvatar = getMyAvatar(); auto myAvatar = getMyAvatar();
@ -5208,12 +5214,20 @@ void Application::update(float deltaTime) {
} }
}); });
AnimDebugDraw::getInstance().update(); {
PerformanceTimer perfTimer("limitless");
AnimDebugDraw::getInstance().update();
}
DependencyManager::get<LimitlessVoiceRecognitionScriptingInterface>()->update(); {
PerformanceTimer perfTimer("limitless");
DependencyManager::get<LimitlessVoiceRecognitionScriptingInterface>()->update();
}
// Game loop is done, mark the end of the frame for the scene transactions and the render loop to take over { // Game loop is done, mark the end of the frame for the scene transactions and the render loop to take over
getMain3DScene()->enqueueFrame(); PerformanceTimer perfTimer("enqueueFrame");
getMain3DScene()->enqueueFrame();
}
} }
void Application::sendAvatarViewFrustum() { void Application::sendAvatarViewFrustum() {

View file

@ -50,7 +50,7 @@ float LODManager::getLODIncreaseFPS() {
const float LOD_ADJUST_RUNNING_AVG_TIMESCALE = 0.1f; // sec const float LOD_ADJUST_RUNNING_AVG_TIMESCALE = 0.1f; // sec
// //
// Assuming the measured value is affected by logic invoked by the runningAverage bumping up against its // Assuming the measured value is affected by logic invoked by the runningAverage bumping up against its
// thresholds, we expect the adjustment to introduce a step-function. We want the runningAverage settle // thresholds, we expect the adjustment to introduce a step-function. We want the runningAverage to settle
// to the new value BEFORE we test it aginst its thresholds again. Hence we test on a period that is a few // to the new value BEFORE we test it aginst its thresholds again. Hence we test on a period that is a few
// multiples of the running average timescale: // multiples of the running average timescale:
const uint64_t LOD_AUTO_ADJUST_PERIOD = 5 * (uint64_t)(LOD_ADJUST_RUNNING_AVG_TIMESCALE * (float)USECS_PER_MSEC); // usec const uint64_t LOD_AUTO_ADJUST_PERIOD = 5 * (uint64_t)(LOD_ADJUST_RUNNING_AVG_TIMESCALE * (float)USECS_PER_MSEC); // usec

View file

@ -698,7 +698,7 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowOwned, addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowOwned,
0, false, drawStatusConfig, SLOT(setShowNetwork(bool))); 0, false, drawStatusConfig, SLOT(setShowNetwork(bool)));
} }
addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowHulls); addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowHulls, 0, false, qApp->getEntities().data(), SIGNAL(setRenderDebugHulls()));
// Developer > Ask to Reset Settings // Developer > Ask to Reset Settings
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::AskToResetSettings, 0, false); addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::AskToResetSettings, 0, false);

View file

@ -72,11 +72,16 @@ void Ledger::signedSend(const QString& propertyName, const QByteArray& text, con
send(endpoint, success, fail, QNetworkAccessManager::PutOperation, AccountManagerAuth::Required, request); send(endpoint, success, fail, QNetworkAccessManager::PutOperation, AccountManagerAuth::Required, request);
} }
void Ledger::keysQuery(const QString& endpoint, const QString& success, const QString& fail) { void Ledger::keysQuery(const QString& endpoint, const QString& success, const QString& fail, QJsonObject& requestParams) {
auto wallet = DependencyManager::get<Wallet>(); auto wallet = DependencyManager::get<Wallet>();
QJsonObject request; requestParams["public_keys"] = QJsonArray::fromStringList(wallet->listPublicKeys());
request["public_keys"] = QJsonArray::fromStringList(wallet->listPublicKeys());
send(endpoint, success, fail, QNetworkAccessManager::PostOperation, AccountManagerAuth::Required, request); send(endpoint, success, fail, QNetworkAccessManager::PostOperation, AccountManagerAuth::Required, requestParams);
}
void Ledger::keysQuery(const QString& endpoint, const QString& success, const QString& fail) {
QJsonObject requestParams;
keysQuery(endpoint, success, fail, requestParams);
} }
void Ledger::buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const bool controlled_failure) { void Ledger::buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const bool controlled_failure) {
@ -169,6 +174,7 @@ void Ledger::historySuccess(QNetworkReply& reply) {
QJsonObject newDataData; QJsonObject newDataData;
newDataData["history"] = newHistoryArray; newDataData["history"] = newHistoryArray;
newData["data"] = newDataData; newData["data"] = newDataData;
newData["current_page"] = data["current_page"].toInt();
emit historyResult(newData); emit historyResult(newData);
} }
@ -176,8 +182,11 @@ void Ledger::historyFailure(QNetworkReply& reply) {
failResponse("history", reply); failResponse("history", reply);
} }
void Ledger::history(const QStringList& keys) { void Ledger::history(const QStringList& keys, const int& pageNumber) {
keysQuery("history", "historySuccess", "historyFailure"); QJsonObject params;
params["per_page"] = 100;
params["page"] = pageNumber;
keysQuery("history", "historySuccess", "historyFailure", params);
} }
// The api/failResponse is called just for the side effect of logging. // The api/failResponse is called just for the side effect of logging.

View file

@ -29,7 +29,7 @@ public:
bool receiveAt(const QString& hfc_key, const QString& old_key); bool receiveAt(const QString& hfc_key, const QString& old_key);
void balance(const QStringList& keys); void balance(const QStringList& keys);
void inventory(const QStringList& keys); void inventory(const QStringList& keys);
void history(const QStringList& keys); void history(const QStringList& keys, const int& pageNumber);
void account(); void account();
void reset(); void reset();
void updateLocation(const QString& asset_id, const QString location, const bool controlledFailure = false); void updateLocation(const QString& asset_id, const QString location, const bool controlledFailure = false);
@ -79,6 +79,7 @@ private:
QJsonObject apiResponse(const QString& label, QNetworkReply& reply); QJsonObject apiResponse(const QString& label, QNetworkReply& reply);
QJsonObject failResponse(const QString& label, QNetworkReply& reply); QJsonObject failResponse(const QString& label, QNetworkReply& reply);
void send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, QJsonObject request); void send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, QJsonObject request);
void keysQuery(const QString& endpoint, const QString& success, const QString& fail, QJsonObject& extraRequestParams);
void keysQuery(const QString& endpoint, const QString& success, const QString& fail); void keysQuery(const QString& endpoint, const QString& success, const QString& fail);
void signedSend(const QString& propertyName, const QByteArray& text, const QString& key, const QString& endpoint, const QString& success, const QString& fail, const bool controlled_failure = false); void signedSend(const QString& propertyName, const QByteArray& text, const QString& key, const QString& endpoint, const QString& success, const QString& fail, const bool controlled_failure = false);
}; };

View file

@ -96,12 +96,12 @@ void QmlCommerce::inventory() {
} }
} }
void QmlCommerce::history() { void QmlCommerce::history(const int& pageNumber) {
auto ledger = DependencyManager::get<Ledger>(); auto ledger = DependencyManager::get<Ledger>();
auto wallet = DependencyManager::get<Wallet>(); auto wallet = DependencyManager::get<Wallet>();
QStringList cachedPublicKeys = wallet->listPublicKeys(); QStringList cachedPublicKeys = wallet->listPublicKeys();
if (!cachedPublicKeys.isEmpty()) { if (!cachedPublicKeys.isEmpty()) {
ledger->history(cachedPublicKeys); ledger->history(cachedPublicKeys, pageNumber);
} }
} }

View file

@ -60,7 +60,7 @@ protected:
Q_INVOKABLE void buy(const QString& assetId, int cost, const bool controlledFailure = false); Q_INVOKABLE void buy(const QString& assetId, int cost, const bool controlledFailure = false);
Q_INVOKABLE void balance(); Q_INVOKABLE void balance();
Q_INVOKABLE void inventory(); Q_INVOKABLE void inventory();
Q_INVOKABLE void history(); Q_INVOKABLE void history(const int& pageNumber);
Q_INVOKABLE void generateKeyPair(); Q_INVOKABLE void generateKeyPair();
Q_INVOKABLE void reset(); Q_INVOKABLE void reset();
Q_INVOKABLE void resetLocalWalletOnly(); Q_INVOKABLE void resetLocalWalletOnly();

View file

@ -42,6 +42,7 @@
size_t std::hash<EntityItemID>::operator()(const EntityItemID& id) const { return qHash(id); } size_t std::hash<EntityItemID>::operator()(const EntityItemID& id) const { return qHash(id); }
std::function<bool()> EntityTreeRenderer::_entitiesShouldFadeFunction; std::function<bool()> EntityTreeRenderer::_entitiesShouldFadeFunction;
std::function<bool()> EntityTreeRenderer::_renderDebugHullsOperator = [] { return false; };
EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState, EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
AbstractScriptingServicesInterface* scriptingServices) : AbstractScriptingServicesInterface* scriptingServices) :

View file

@ -116,10 +116,14 @@ public:
EntityItemPointer getEntity(const EntityItemID& id); EntityItemPointer getEntity(const EntityItemID& id);
void onEntityChanged(const EntityItemID& id); void onEntityChanged(const EntityItemID& id);
static void setRenderDebugHullsOperator(std::function<bool()> renderDebugHullsOperator) { _renderDebugHullsOperator = renderDebugHullsOperator; }
static bool shouldRenderDebugHulls() { return _renderDebugHullsOperator(); }
signals: signals:
void enterEntity(const EntityItemID& entityItemID); void enterEntity(const EntityItemID& entityItemID);
void leaveEntity(const EntityItemID& entityItemID); void leaveEntity(const EntityItemID& entityItemID);
void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
void setRenderDebugHulls();
public slots: public slots:
void addingEntity(const EntityItemID& entityID); void addingEntity(const EntityItemID& entityID);
@ -255,6 +259,8 @@ private:
static int _entitiesScriptEngineCount; static int _entitiesScriptEngineCount;
static CalculateEntityLoadingPriority _calculateEntityLoadingPriorityFunc; static CalculateEntityLoadingPriority _calculateEntityLoadingPriorityFunc;
static std::function<bool()> _entitiesShouldFadeFunction; static std::function<bool()> _entitiesShouldFadeFunction;
static std::function<bool()> _renderDebugHullsOperator;
}; };

View file

@ -141,6 +141,10 @@ std::shared_ptr<T> make_renderer(const EntityItemPointer& entity) {
} }
EntityRenderer::EntityRenderer(const EntityItemPointer& entity) : _entity(entity) { EntityRenderer::EntityRenderer(const EntityItemPointer& entity) : _entity(entity) {
connect(entity.get(), &EntityItem::requestRenderUpdate, this, [&] {
_needsRenderUpdate = true;
emit requestRenderUpdate();
});
} }
EntityRenderer::~EntityRenderer() { } EntityRenderer::~EntityRenderer() { }
@ -311,6 +315,9 @@ void EntityRenderer::setSubRenderItemIDs(const render::ItemIDs& ids) {
// Returns true if the item needs to have updateInscene called because of internal rendering // Returns true if the item needs to have updateInscene called because of internal rendering
// changes (animation, fading, etc) // changes (animation, fading, etc)
bool EntityRenderer::needsRenderUpdate() const { bool EntityRenderer::needsRenderUpdate() const {
if (_needsRenderUpdate) {
return true;
}
if (_prevIsTransparent != isTransparent()) { if (_prevIsTransparent != isTransparent()) {
return true; return true;
} }
@ -331,10 +338,6 @@ bool EntityRenderer::needsRenderUpdateFromEntity(const EntityItemPointer& entity
return true; return true;
} }
if (_visible != entity->getVisible()) {
return true;
}
if (_moving != entity->isMovingRelativeToParent()) { if (_moving != entity->isMovingRelativeToParent()) {
return true; return true;
} }
@ -363,6 +366,7 @@ void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transa
_moving = entity->isMovingRelativeToParent(); _moving = entity->isMovingRelativeToParent();
_visible = entity->getVisible(); _visible = entity->getVisible();
_needsRenderUpdate = false;
}); });
} }

View file

@ -125,6 +125,7 @@ protected:
bool _prevIsTransparent { false }; bool _prevIsTransparent { false };
bool _visible { false }; bool _visible { false };
bool _moving { false }; bool _moving { false };
bool _needsRenderUpdate { false };
// Only touched on the rendering thread // Only touched on the rendering thread
bool _renderUpdateQueued{ false }; bool _renderUpdateQueued{ false };

View file

@ -954,8 +954,23 @@ void RenderableModelEntityItem::copyAnimationJointDataToModel() {
using namespace render; using namespace render;
using namespace render::entities; using namespace render::entities;
ModelEntityRenderer::ModelEntityRenderer(const EntityItemPointer& entity) : Parent(entity) {
connect(DependencyManager::get<EntityTreeRenderer>().data(), &EntityTreeRenderer::setRenderDebugHulls, this, [&] {
_needsCollisionGeometryUpdate = true;
emit requestRenderUpdate();
});
}
void ModelEntityRenderer::setKey(bool didVisualGeometryRequestSucceed) {
if (didVisualGeometryRequestSucceed) {
_itemKey = ItemKey::Builder().withTypeMeta();
} else {
_itemKey = ItemKey::Builder().withTypeMeta().withTypeShape();
}
}
ItemKey ModelEntityRenderer::getKey() { ItemKey ModelEntityRenderer::getKey() {
return ItemKey::Builder().withTypeMeta().withTypeShape(); return _itemKey;
} }
uint32_t ModelEntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) { uint32_t ModelEntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) {
@ -1209,7 +1224,10 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
// Check for addition // Check for addition
if (_hasModel && !(bool)_model) { if (_hasModel && !(bool)_model) {
model = std::make_shared<Model>(nullptr, entity.get()); model = std::make_shared<Model>(nullptr, entity.get());
connect(model.get(), &Model::setURLFinished, this, &ModelEntityRenderer::requestRenderUpdate); connect(model.get(), &Model::setURLFinished, this, [&](bool didVisualGeometryRequestSucceed) {
setKey(didVisualGeometryRequestSucceed);
emit requestRenderUpdate();
});
connect(model.get(), &Model::requestRenderUpdate, this, &ModelEntityRenderer::requestRenderUpdate); connect(model.get(), &Model::requestRenderUpdate, this, &ModelEntityRenderer::requestRenderUpdate);
connect(entity.get(), &RenderableModelEntityItem::requestCollisionGeometryUpdate, this, &ModelEntityRenderer::flagForCollisionGeometryUpdate); connect(entity.get(), &RenderableModelEntityItem::requestCollisionGeometryUpdate, this, &ModelEntityRenderer::flagForCollisionGeometryUpdate);
model->setLoadingPriority(EntityTreeRenderer::getEntityLoadingPriority(*entity)); model->setLoadingPriority(EntityTreeRenderer::getEntityLoadingPriority(*entity));
@ -1275,7 +1293,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
setCollisionMeshKey(entity->getCollisionMeshKey()); setCollisionMeshKey(entity->getCollisionMeshKey());
_needsCollisionGeometryUpdate = false; _needsCollisionGeometryUpdate = false;
ShapeType type = entity->getShapeType(); ShapeType type = entity->getShapeType();
if (_showCollisionGeometry && type != SHAPE_TYPE_STATIC_MESH && type != SHAPE_TYPE_NONE) { if (DependencyManager::get<EntityTreeRenderer>()->shouldRenderDebugHulls() && type != SHAPE_TYPE_STATIC_MESH && type != SHAPE_TYPE_NONE) {
// NOTE: it is OK if _collisionMeshKey is nullptr // NOTE: it is OK if _collisionMeshKey is nullptr
model::MeshPointer mesh = collisionMeshCache.getMesh(_collisionMeshKey); model::MeshPointer mesh = collisionMeshCache.getMesh(_collisionMeshKey);
// NOTE: the model will render the collisionGeometry if it has one // NOTE: the model will render the collisionGeometry if it has one
@ -1342,20 +1360,11 @@ void ModelEntityRenderer::doRender(RenderArgs* args) {
DETAILED_PROFILE_RANGE(render_detail, "MetaModelRender"); DETAILED_PROFILE_RANGE(render_detail, "MetaModelRender");
DETAILED_PERFORMANCE_TIMER("RMEIrender"); DETAILED_PERFORMANCE_TIMER("RMEIrender");
ModelPointer model; // If the model doesn't have visual geometry, render our bounding box as green wireframe
withReadLock([&]{ static glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f);
model = _model; gpu::Batch& batch = *args->_batch;
}); batch.setModelTransform(getModelTransform()); // we want to include the scale as well
DependencyManager::get<GeometryCache>()->renderWireCubeInstance(args, batch, greenColor);
// If we don't have a model, or the model doesn't have visual geometry, render our bounding box as green wireframe
if (!model || (model && model->didVisualGeometryRequestFail())) {
static glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f);
gpu::Batch& batch = *args->_batch;
batch.setModelTransform(getModelTransform()); // we want to include the scale as well
DependencyManager::get<GeometryCache>()->renderWireCubeInstance(args, batch, greenColor);
return;
}
// Enqueue updates for the next frame // Enqueue updates for the next frame
#if WANT_EXTRA_DEBUGGING #if WANT_EXTRA_DEBUGGING
@ -1366,12 +1375,6 @@ void ModelEntityRenderer::doRender(RenderArgs* args) {
// Remap textures for the next frame to avoid flicker // Remap textures for the next frame to avoid flicker
// remapTextures(); // remapTextures();
bool showCollisionGeometry = (bool)(args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS);
if (showCollisionGeometry != _showCollisionGeometry) {
_showCollisionGeometry = showCollisionGeometry;
flagForCollisionGeometryUpdate();
}
} }
void ModelEntityRenderer::mapJoints(const TypedEntityPointer& entity, const QStringList& modelJointNames) { void ModelEntityRenderer::mapJoints(const TypedEntityPointer& entity, const QStringList& modelJointNames) {

View file

@ -133,12 +133,13 @@ class ModelEntityRenderer : public TypedEntityRenderer<RenderableModelEntityItem
friend class EntityRenderer; friend class EntityRenderer;
public: public:
ModelEntityRenderer(const EntityItemPointer& entity) : Parent(entity) {} ModelEntityRenderer(const EntityItemPointer& entity);
protected: protected:
virtual void removeFromScene(const ScenePointer& scene, Transaction& transaction) override; virtual void removeFromScene(const ScenePointer& scene, Transaction& transaction) override;
virtual void onRemoveFromSceneTyped(const TypedEntityPointer& entity) override; virtual void onRemoveFromSceneTyped(const TypedEntityPointer& entity) override;
void setKey(bool didVisualGeometryRequestSucceed);
virtual ItemKey getKey() override; virtual ItemKey getKey() override;
virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) override; virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) override;
@ -169,10 +170,9 @@ private:
bool _hasTransitioned{ false }; bool _hasTransitioned{ false };
#endif #endif
bool _needsJointSimulation{ false }; bool _needsJointSimulation { false };
bool _showCollisionGeometry{ false }; bool _needsCollisionGeometryUpdate { false };
bool _needsCollisionGeometryUpdate{ false }; const void* _collisionMeshKey { nullptr };
const void* _collisionMeshKey{ nullptr };
// used on client side // used on client side
bool _jointMappingCompleted{ false }; bool _jointMappingCompleted{ false };
@ -185,6 +185,8 @@ private:
bool _shouldHighlight { false }; bool _shouldHighlight { false };
bool _animating { false }; bool _animating { false };
uint64_t _lastAnimated { 0 }; uint64_t _lastAnimated { 0 };
render::ItemKey _itemKey { render::ItemKey::Builder().withTypeMeta() };
}; };
} } // namespace } } // namespace

View file

@ -1422,27 +1422,29 @@ bool RenderablePolyVoxEntityItem::getMeshes(MeshProxyList& result) {
} }
bool success = false; bool success = false;
MeshProxy* meshProxy = nullptr; if (_mesh) {
glm::mat4 transform = voxelToLocalMatrix(); MeshProxy* meshProxy = nullptr;
withReadLock([&] { glm::mat4 transform = voxelToLocalMatrix();
gpu::BufferView::Index numVertices = (gpu::BufferView::Index)_mesh->getNumVertices(); withReadLock([&] {
if (!_meshReady) { gpu::BufferView::Index numVertices = (gpu::BufferView::Index)_mesh->getNumVertices();
// we aren't ready to return a mesh. the caller will have to try again later. if (!_meshReady) {
success = false; // we aren't ready to return a mesh. the caller will have to try again later.
} else if (numVertices == 0) { success = false;
// we are ready, but there are no triangles in the mesh. } else if (numVertices == 0) {
success = true; // we are ready, but there are no triangles in the mesh.
} else { success = true;
success = true; } else {
// the mesh will be in voxel-space. transform it into object-space success = true;
meshProxy = new SimpleMeshProxy( // the mesh will be in voxel-space. transform it into object-space
_mesh->map([=](glm::vec3 position){ return glm::vec3(transform * glm::vec4(position, 1.0f)); }, meshProxy = new SimpleMeshProxy(
[=](glm::vec3 color){ return color; }, _mesh->map([=](glm::vec3 position) { return glm::vec3(transform * glm::vec4(position, 1.0f)); },
[=](glm::vec3 normal){ return glm::normalize(glm::vec3(transform * glm::vec4(normal, 0.0f))); }, [=](glm::vec3 color) { return color; },
[&](uint32_t index){ return index; })); [=](glm::vec3 normal) { return glm::normalize(glm::vec3(transform * glm::vec4(normal, 0.0f))); },
result << meshProxy; [&](uint32_t index) { return index; }));
} result << meshProxy;
}); }
});
}
return success; return success;
} }

View file

@ -413,7 +413,6 @@ void ZoneEntityRenderer::setAmbientURL(const QString& ambientUrl) {
_ambientTextureURL = ambientUrl; _ambientTextureURL = ambientUrl;
if (_ambientTextureURL.isEmpty()) { if (_ambientTextureURL.isEmpty()) {
_validAmbientTexture = false;
_pendingAmbientTexture = false; _pendingAmbientTexture = false;
_ambientTexture.clear(); _ambientTexture.clear();
@ -441,7 +440,6 @@ void ZoneEntityRenderer::updateAmbientMap() {
_ambientLight->setAmbientSpherePreset(gpu::SphericalHarmonics::BREEZEWAY); _ambientLight->setAmbientSpherePreset(gpu::SphericalHarmonics::BREEZEWAY);
} }
editAmbientLight()->setAmbientMap(texture); editAmbientLight()->setAmbientMap(texture);
_validAmbientTexture = true;
} else { } else {
qCDebug(entitiesrenderer) << "Failed to load ambient texture:" << _ambientTexture->getURL(); qCDebug(entitiesrenderer) << "Failed to load ambient texture:" << _ambientTexture->getURL();
} }
@ -457,7 +455,6 @@ void ZoneEntityRenderer::setSkyboxURL(const QString& skyboxUrl) {
_skyboxTextureURL = skyboxUrl; _skyboxTextureURL = skyboxUrl;
if (_skyboxTextureURL.isEmpty()) { if (_skyboxTextureURL.isEmpty()) {
_validSkyboxTexture = false;
_pendingSkyboxTexture = false; _pendingSkyboxTexture = false;
_skyboxTexture.clear(); _skyboxTexture.clear();
@ -477,7 +474,6 @@ void ZoneEntityRenderer::updateSkyboxMap() {
auto texture = _skyboxTexture->getGPUTexture(); auto texture = _skyboxTexture->getGPUTexture();
if (texture) { if (texture) {
editSkybox()->setCubemap(texture); editSkybox()->setCubemap(texture);
_validSkyboxTexture = true;
} else { } else {
qCDebug(entitiesrenderer) << "Failed to load Skybox texture:" << _skyboxTexture->getURL(); qCDebug(entitiesrenderer) << "Failed to load Skybox texture:" << _skyboxTexture->getURL();
} }

View file

@ -118,12 +118,10 @@ private:
QString _ambientTextureURL; QString _ambientTextureURL;
NetworkTexturePointer _ambientTexture; NetworkTexturePointer _ambientTexture;
bool _pendingAmbientTexture{ false }; bool _pendingAmbientTexture{ false };
bool _validAmbientTexture{ false };
QString _skyboxTextureURL; QString _skyboxTextureURL;
NetworkTexturePointer _skyboxTexture; NetworkTexturePointer _skyboxTexture;
bool _pendingSkyboxTexture{ false }; bool _pendingSkyboxTexture{ false };
bool _validSkyboxTexture{ false };
QString _proceduralUserData; QString _proceduralUserData;
Transform _renderTransform; Transform _renderTransform;

View file

@ -2715,9 +2715,17 @@ bool EntityItem::getVisible() const {
} }
void EntityItem::setVisible(bool value) { void EntityItem::setVisible(bool value) {
bool changed = false;
withWriteLock([&] { withWriteLock([&] {
_visible = value; if (_visible != value) {
changed = true;
_visible = value;
}
}); });
if (changed) {
emit requestRenderUpdate();
}
} }
bool EntityItem::isChildOfMyAvatar() const { bool EntityItem::isChildOfMyAvatar() const {

View file

@ -469,6 +469,9 @@ public:
static QString _marketplacePublicKey; static QString _marketplacePublicKey;
static void retrieveMarketplacePublicKey(); static void retrieveMarketplacePublicKey();
signals:
void requestRenderUpdate();
protected: protected:
QHash<ChangeHandlerId, ChangeHandlerCallback> _changeHandlers; QHash<ChangeHandlerId, ChangeHandlerCallback> _changeHandlers;

View file

@ -2291,8 +2291,13 @@ bool EntityTree::readFromMap(QVariantMap& map) {
if (entityMap.contains("backgroundMode") && (entityMap["backgroundMode"].toString() == "nothing")) { if (entityMap.contains("backgroundMode") && (entityMap["backgroundMode"].toString() == "nothing")) {
properties.setSkyboxMode(COMPONENT_MODE_INHERIT); properties.setSkyboxMode(COMPONENT_MODE_INHERIT);
} else { } else {
// either the background mode field is missing (shouldn't happen) or the background mode is "skybox" // Either the background mode field is missing (shouldn't happen) or the background mode is "skybox"
properties.setSkyboxMode(COMPONENT_MODE_ENABLED); properties.setSkyboxMode(COMPONENT_MODE_ENABLED);
// Copy the skybox URL if the ambient URL is empty, as this is the legacy behaviour
if (properties.getAmbientLight().getAmbientURL() == "") {
properties.getAmbientLight().setAmbientURL(properties.getSkybox().getURL());
}
} }
} }

View file

@ -82,7 +82,7 @@ bool KeyLightPropertyGroup::appendToEditPacket(OctreePacketData* packetData,
return true; return true;
} }
bool KeyLightPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt, bool KeyLightPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt,
int& processedBytes) { int& processedBytes) {
int bytesRead = 0; int bytesRead = 0;
@ -92,7 +92,6 @@ bool KeyLightPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFl
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, xColor, setColor); READ_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, xColor, setColor);
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, float, setIntensity); READ_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, float, setIntensity);
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, glm::vec3, setDirection); READ_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, glm::vec3, setDirection);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_COLOR, Color); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_COLOR, Color);
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_INTENSITY, Intensity); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_INTENSITY, Intensity);

View file

@ -345,7 +345,7 @@ void ZoneEntityItem::resetRenderingPropertiesChanged() {
} }
void ZoneEntityItem::setHazeMode(const uint32_t value) { void ZoneEntityItem::setHazeMode(const uint32_t value) {
if (value < COMPONENT_MODE_ITEM_COUNT) { if (value < COMPONENT_MODE_ITEM_COUNT && value != _hazeMode) {
_hazeMode = value; _hazeMode = value;
_hazePropertiesChanged = true; _hazePropertiesChanged = true;
} }
@ -356,7 +356,7 @@ uint32_t ZoneEntityItem::getHazeMode() const {
} }
void ZoneEntityItem::setKeyLightMode(const uint32_t value) { void ZoneEntityItem::setKeyLightMode(const uint32_t value) {
if (value < COMPONENT_MODE_ITEM_COUNT) { if (value < COMPONENT_MODE_ITEM_COUNT && value != _keyLightMode) {
_keyLightMode = value; _keyLightMode = value;
_keyLightPropertiesChanged = true; _keyLightPropertiesChanged = true;
} }
@ -367,7 +367,7 @@ uint32_t ZoneEntityItem::getKeyLightMode() const {
} }
void ZoneEntityItem::setAmbientLightMode(const uint32_t value) { void ZoneEntityItem::setAmbientLightMode(const uint32_t value) {
if (value < COMPONENT_MODE_ITEM_COUNT) { if (value < COMPONENT_MODE_ITEM_COUNT && value != _ambientLightMode) {
_ambientLightMode = value; _ambientLightMode = value;
_ambientLightPropertiesChanged = true; _ambientLightPropertiesChanged = true;
} }
@ -378,7 +378,7 @@ uint32_t ZoneEntityItem::getAmbientLightMode() const {
} }
void ZoneEntityItem::setSkyboxMode(const uint32_t value) { void ZoneEntityItem::setSkyboxMode(const uint32_t value) {
if (value < COMPONENT_MODE_ITEM_COUNT) { if (value < COMPONENT_MODE_ITEM_COUNT && value != _skyboxMode) {
_skyboxMode = value; _skyboxMode = value;
_skyboxPropertiesChanged = true; _skyboxPropertiesChanged = true;
} }

View file

@ -60,6 +60,7 @@ public:
QVector<int> indices; QVector<int> indices;
QVector<glm::vec3> vertices; QVector<glm::vec3> vertices;
QVector<glm::vec3> normals; QVector<glm::vec3> normals;
QVector<glm::vec3> tangents;
}; };
struct FBXJointShapeInfo { struct FBXJointShapeInfo {

View file

@ -303,17 +303,97 @@ FBXBlendshape extractBlendshape(const FBXNode& object) {
return blendshape; return blendshape;
} }
using IndexAccessor = std::function<glm::vec3*(const FBXMesh&, int, int, glm::vec3*, glm::vec3&)>;
void setTangents(FBXMesh& mesh, int firstIndex, int secondIndex) { static void setTangents(const FBXMesh& mesh, const IndexAccessor& vertexAccessor, int firstIndex, int secondIndex,
const glm::vec3& normal = mesh.normals.at(firstIndex); const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals, QVector<glm::vec3>& tangents) {
glm::vec3 bitangent = glm::cross(normal, mesh.vertices.at(secondIndex) - mesh.vertices.at(firstIndex)); glm::vec3 vertex[2];
if (glm::length(bitangent) < EPSILON) { glm::vec3 normal;
return; glm::vec3* tangent = vertexAccessor(mesh, firstIndex, secondIndex, vertex, normal);
if (tangent) {
glm::vec3 bitangent = glm::cross(normal, vertex[1] - vertex[0]);
if (glm::length(bitangent) < EPSILON) {
return;
}
glm::vec2 texCoordDelta = mesh.texCoords.at(secondIndex) - mesh.texCoords.at(firstIndex);
glm::vec3 normalizedNormal = glm::normalize(normal);
*tangent += glm::cross(glm::angleAxis(-atan2f(-texCoordDelta.t, texCoordDelta.s), normalizedNormal) *
glm::normalize(bitangent), normalizedNormal);
} }
glm::vec2 texCoordDelta = mesh.texCoords.at(secondIndex) - mesh.texCoords.at(firstIndex); }
glm::vec3 normalizedNormal = glm::normalize(normal);
mesh.tangents[firstIndex] += glm::cross(glm::angleAxis(-atan2f(-texCoordDelta.t, texCoordDelta.s), normalizedNormal) * static void createTangents(const FBXMesh& mesh, bool generateFromTexCoords,
glm::normalize(bitangent), normalizedNormal); const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals, QVector<glm::vec3>& tangents,
IndexAccessor accessor) {
// if we have a normal map (and texture coordinates), we must compute tangents
if (generateFromTexCoords && !mesh.texCoords.isEmpty()) {
tangents.resize(vertices.size());
foreach(const FBXMeshPart& part, mesh.parts) {
for (int i = 0; i < part.quadIndices.size(); i += 4) {
setTangents(mesh, accessor, part.quadIndices.at(i), part.quadIndices.at(i + 1), vertices, normals, tangents);
setTangents(mesh, accessor, part.quadIndices.at(i + 1), part.quadIndices.at(i + 2), vertices, normals, tangents);
setTangents(mesh, accessor, part.quadIndices.at(i + 2), part.quadIndices.at(i + 3), vertices, normals, tangents);
setTangents(mesh, accessor, part.quadIndices.at(i + 3), part.quadIndices.at(i), vertices, normals, tangents);
}
// <= size - 3 in order to prevent overflowing triangleIndices when (i % 3) != 0
// This is most likely evidence of a further problem in extractMesh()
for (int i = 0; i <= part.triangleIndices.size() - 3; i += 3) {
setTangents(mesh, accessor, part.triangleIndices.at(i), part.triangleIndices.at(i + 1), vertices, normals, tangents);
setTangents(mesh, accessor, part.triangleIndices.at(i + 1), part.triangleIndices.at(i + 2), vertices, normals, tangents);
setTangents(mesh, accessor, part.triangleIndices.at(i + 2), part.triangleIndices.at(i), vertices, normals, tangents);
}
if ((part.triangleIndices.size() % 3) != 0) {
qCDebug(modelformat) << "Error in extractFBXGeometry part.triangleIndices.size() is not divisible by three ";
}
}
}
}
static void createMeshTangents(FBXMesh& mesh, bool generateFromTexCoords) {
// This is the only workaround I've found to trick the compiler into understanding that mesh.tangents isn't
// const in the lambda function.
auto& tangents = mesh.tangents;
createTangents(mesh, generateFromTexCoords, mesh.vertices, mesh.normals, mesh.tangents,
[&](const FBXMesh& mesh, int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec3& outNormal) {
outVertices[0] = mesh.vertices[firstIndex];
outVertices[1] = mesh.vertices[secondIndex];
outNormal = mesh.normals[firstIndex];
return &(tangents[firstIndex]);
});
}
static void createBlendShapeTangents(FBXMesh& mesh, bool generateFromTexCoords, FBXBlendshape& blendShape) {
// Create lookup to get index in blend shape from vertex index in mesh
std::vector<int> reverseIndices;
reverseIndices.resize(mesh.vertices.size());
std::iota(reverseIndices.begin(), reverseIndices.end(), 0);
for (int indexInBlendShape = 0; indexInBlendShape < blendShape.indices.size(); ++indexInBlendShape) {
auto indexInMesh = blendShape.indices[indexInBlendShape];
reverseIndices[indexInMesh] = indexInBlendShape;
}
createTangents(mesh, generateFromTexCoords, blendShape.vertices, blendShape.normals, blendShape.tangents,
[&](const FBXMesh& mesh, int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec3& outNormal) {
const auto index1 = reverseIndices[firstIndex];
const auto index2 = reverseIndices[secondIndex];
if (index1 < blendShape.vertices.size()) {
outVertices[0] = blendShape.vertices[index1];
if (index2 < blendShape.vertices.size()) {
outVertices[1] = blendShape.vertices[index2];
} else {
// Index isn't in the blend shape so return vertex from mesh
outVertices[1] = mesh.vertices[secondIndex];
}
outNormal = blendShape.normals[index1];
return &blendShape.tangents[index1];
} else {
// Index isn't in blend shape so return nullptr
return (glm::vec3*)nullptr;
}
});
} }
QVector<int> getIndices(const QVector<QString> ids, QVector<QString> modelIDs) { QVector<int> getIndices(const QVector<QString> ids, QVector<QString> modelIDs) {
@ -1570,27 +1650,9 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
} }
} }
// if we have a normal map (and texture coordinates), we must compute tangents createMeshTangents(extracted.mesh, generateTangents);
if (generateTangents && !extracted.mesh.texCoords.isEmpty()) { for (auto& blendShape : extracted.mesh.blendshapes) {
extracted.mesh.tangents.resize(extracted.mesh.vertices.size()); createBlendShapeTangents(extracted.mesh, generateTangents, blendShape);
foreach (const FBXMeshPart& part, extracted.mesh.parts) {
for (int i = 0; i < part.quadIndices.size(); i += 4) {
setTangents(extracted.mesh, part.quadIndices.at(i), part.quadIndices.at(i + 1));
setTangents(extracted.mesh, part.quadIndices.at(i + 1), part.quadIndices.at(i + 2));
setTangents(extracted.mesh, part.quadIndices.at(i + 2), part.quadIndices.at(i + 3));
setTangents(extracted.mesh, part.quadIndices.at(i + 3), part.quadIndices.at(i));
}
// <= size - 3 in order to prevent overflowing triangleIndices when (i % 3) != 0
// This is most likely evidence of a further problem in extractMesh()
for (int i = 0; i <= part.triangleIndices.size() - 3; i += 3) {
setTangents(extracted.mesh, part.triangleIndices.at(i), part.triangleIndices.at(i + 1));
setTangents(extracted.mesh, part.triangleIndices.at(i + 1), part.triangleIndices.at(i + 2));
setTangents(extracted.mesh, part.triangleIndices.at(i + 2), part.triangleIndices.at(i));
}
if ((part.triangleIndices.size() % 3) != 0){
qCDebug(modelformat) << "Error in extractFBXGeometry part.triangleIndices.size() is not divisible by three ";
}
}
} }
// find the clusters with which the mesh is associated // find the clusters with which the mesh is associated

View file

@ -33,6 +33,15 @@
class QIODevice; class QIODevice;
class FBXNode; class FBXNode;
#define FBX_PACK_NORMALS 1
#if FBX_PACK_NORMALS
using NormalType = glm::uint32;
#define FBX_NORMAL_ELEMENT gpu::Element::VEC4F_NORMALIZED_XYZ10W2
#else
using NormalType = glm::vec3;
#define FBX_NORMAL_ELEMENT gpu::Element::VEC3F_XYZ
#endif
/// Reads FBX geometry from the supplied model and mapping data. /// Reads FBX geometry from the supplied model and mapping data.
/// \exception QString if an error occurs in parsing /// \exception QString if an error occurs in parsing
@ -114,6 +123,8 @@ public:
QHash<QString, ExtractedMesh> meshes; QHash<QString, ExtractedMesh> meshes;
static void buildModelMesh(FBXMesh& extractedMesh, const QString& url); static void buildModelMesh(FBXMesh& extractedMesh, const QString& url);
static glm::vec3 normalizeDirForPacking(const glm::vec3& dir);
FBXTexture getTexture(const QString& textureID); FBXTexture getTexture(const QString& textureID);
QHash<QString, QString> _textureNames; QHash<QString, QString> _textureNames;

View file

@ -37,6 +37,20 @@
#include <memory> #include <memory>
#include <glm/detail/type_half.hpp>
#include <glm/gtc/packing.hpp>
using vec2h = glm::tvec2<glm::detail::hdata>;
#define FBX_PACK_COLORS 1
#if FBX_PACK_COLORS
using ColorType = glm::uint32;
#define FBX_COLOR_ELEMENT gpu::Element::COLOR_RGBA_32
#else
using ColorType = glm::vec3;
#define FBX_COLOR_ELEMENT gpu::Element::VEC3F_XYZ
#endif
class Vertex { class Vertex {
public: public:
@ -225,7 +239,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn
foreach (const FBXNode& subdata, child.children) { foreach (const FBXNode& subdata, child.children) {
if (subdata.name == "Colors") { if (subdata.name == "Colors") {
data.colors = createVec4VectorRGBA(getDoubleVector(subdata), data.averageColor); data.colors = createVec4VectorRGBA(getDoubleVector(subdata), data.averageColor);
} else if (subdata.name == "ColorsIndex") { } else if (subdata.name == "ColorsIndex" || subdata.name == "ColorIndex") {
data.colorIndices = getIntVector(subdata); data.colorIndices = getIntVector(subdata);
} else if (subdata.name == "MappingInformationType" && subdata.properties.at(0) == BY_VERTICE) { } else if (subdata.name == "MappingInformationType" && subdata.properties.at(0) == BY_VERTICE) {
@ -543,6 +557,14 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn
return data.extracted; return data.extracted;
} }
glm::vec3 FBXReader::normalizeDirForPacking(const glm::vec3& dir) {
auto maxCoord = glm::max(fabsf(dir.x), glm::max(fabsf(dir.y), fabsf(dir.z)));
if (maxCoord > 1e-6f) {
return dir / maxCoord;
}
return dir;
}
void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) {
static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex("buildModelMesh failed -- .*"); static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex("buildModelMesh failed -- .*");
@ -571,37 +593,115 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) {
gpu::BufferView vbv(vb, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); gpu::BufferView vbv(vb, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ));
mesh->setVertexBuffer(vbv); mesh->setVertexBuffer(vbv);
if (!fbxMesh.normals.empty() && fbxMesh.tangents.empty()) {
// Fill with a dummy value to force tangents to be present if there are normals
fbxMesh.tangents.reserve(fbxMesh.normals.size());
std::fill_n(std::back_inserter(fbxMesh.tangents), fbxMesh.normals.size(), Vectors::UNIT_X);
}
// Same thing with blend shapes
for (auto& blendShape : fbxMesh.blendshapes) {
if (!blendShape.normals.empty() && blendShape.tangents.empty()) {
// Fill with a dummy value to force tangents to be present if there are normals
blendShape.tangents.reserve(blendShape.normals.size());
std::fill_n(std::back_inserter(fbxMesh.tangents), blendShape.normals.size(), Vectors::UNIT_X);
}
}
// evaluate all attribute channels sizes // evaluate all attribute channels sizes
int normalsSize = fbxMesh.normals.size() * sizeof(glm::vec3); const int normalsSize = fbxMesh.normals.size() * sizeof(NormalType);
int tangentsSize = fbxMesh.tangents.size() * sizeof(glm::vec3); const int tangentsSize = fbxMesh.tangents.size() * sizeof(NormalType);
int colorsSize = fbxMesh.colors.size() * sizeof(glm::vec3); // If there are normals then there should be tangents
int texCoordsSize = fbxMesh.texCoords.size() * sizeof(glm::vec2); assert(normalsSize == tangentsSize);
int texCoords1Size = fbxMesh.texCoords1.size() * sizeof(glm::vec2); const auto normalsAndTangentsSize = normalsSize + tangentsSize;
const int normalsAndTangentsStride = 2 * sizeof(NormalType);
const int colorsSize = fbxMesh.colors.size() * sizeof(ColorType);
// Texture coordinates are stored in 2 half floats
const int texCoordsSize = fbxMesh.texCoords.size() * sizeof(vec2h);
const int texCoords1Size = fbxMesh.texCoords1.size() * sizeof(vec2h);
int clusterIndicesSize = fbxMesh.clusterIndices.size() * sizeof(uint8_t); int clusterIndicesSize = fbxMesh.clusterIndices.size() * sizeof(uint8_t);
if (fbxMesh.clusters.size() > UINT8_MAX) { if (fbxMesh.clusters.size() > UINT8_MAX) {
// we need 16 bits instead of just 8 for clusterIndices // we need 16 bits instead of just 8 for clusterIndices
clusterIndicesSize *= 2; clusterIndicesSize *= 2;
} }
int clusterWeightsSize = fbxMesh.clusterWeights.size() * sizeof(uint8_t); const int clusterWeightsSize = fbxMesh.clusterWeights.size() * sizeof(uint8_t);
int normalsOffset = 0; // Normals and tangents are interleaved
int tangentsOffset = normalsOffset + normalsSize; const int normalsOffset = 0;
int colorsOffset = tangentsOffset + tangentsSize; const int tangentsOffset = normalsOffset + sizeof(NormalType);
int texCoordsOffset = colorsOffset + colorsSize; const int colorsOffset = normalsOffset + normalsSize + tangentsSize;
int texCoords1Offset = texCoordsOffset + texCoordsSize; const int texCoordsOffset = colorsOffset + colorsSize;
int clusterIndicesOffset = texCoords1Offset + texCoords1Size; const int texCoords1Offset = texCoordsOffset + texCoordsSize;
int clusterWeightsOffset = clusterIndicesOffset + clusterIndicesSize; const int clusterIndicesOffset = texCoords1Offset + texCoords1Size;
int totalAttributeSize = clusterWeightsOffset + clusterWeightsSize; const int clusterWeightsOffset = clusterIndicesOffset + clusterIndicesSize;
const int totalAttributeSize = clusterWeightsOffset + clusterWeightsSize;
// Copy all attribute data in a single attribute buffer // Copy all attribute data in a single attribute buffer
auto attribBuffer = std::make_shared<gpu::Buffer>(); auto attribBuffer = std::make_shared<gpu::Buffer>();
attribBuffer->resize(totalAttributeSize); attribBuffer->resize(totalAttributeSize);
attribBuffer->setSubData(normalsOffset, normalsSize, (gpu::Byte*) fbxMesh.normals.constData());
attribBuffer->setSubData(tangentsOffset, tangentsSize, (gpu::Byte*) fbxMesh.tangents.constData()); // Interleave normals and tangents
attribBuffer->setSubData(colorsOffset, colorsSize, (gpu::Byte*) fbxMesh.colors.constData()); if (normalsSize > 0) {
attribBuffer->setSubData(texCoordsOffset, texCoordsSize, (gpu::Byte*) fbxMesh.texCoords.constData()); std::vector<NormalType> normalsAndTangents;
attribBuffer->setSubData(texCoords1Offset, texCoords1Size, (gpu::Byte*) fbxMesh.texCoords1.constData());
normalsAndTangents.reserve(fbxMesh.normals.size() + fbxMesh.tangents.size());
for (auto normalIt = fbxMesh.normals.constBegin(), tangentIt = fbxMesh.tangents.constBegin();
normalIt != fbxMesh.normals.constEnd();
++normalIt, ++tangentIt) {
#if FBX_PACK_NORMALS
const auto normal = normalizeDirForPacking(*normalIt);
const auto tangent = normalizeDirForPacking(*tangentIt);
const auto packedNormal = glm::packSnorm3x10_1x2(glm::vec4(normal, 0.0f));
const auto packedTangent = glm::packSnorm3x10_1x2(glm::vec4(tangent, 0.0f));
#else
const auto packedNormal = *normalIt;
const auto packedTangent = *tangentIt;
#endif
normalsAndTangents.push_back(packedNormal);
normalsAndTangents.push_back(packedTangent);
}
attribBuffer->setSubData(normalsOffset, normalsAndTangentsSize, (const gpu::Byte*) normalsAndTangents.data());
}
if (colorsSize > 0) {
#if FBX_PACK_COLORS
std::vector<ColorType> colors;
colors.reserve(fbxMesh.colors.size());
for (const auto& color : fbxMesh.colors) {
colors.push_back(glm::packUnorm4x8(glm::vec4(color, 1.0f)));
}
attribBuffer->setSubData(colorsOffset, colorsSize, (const gpu::Byte*) colors.data());
#else
attribBuffer->setSubData(colorsOffset, colorsSize, (const gpu::Byte*) fbxMesh.colors.constData());
#endif
}
if (texCoordsSize > 0) {
QVector<vec2h> texCoordData;
texCoordData.reserve(fbxMesh.texCoords.size());
for (auto& texCoordVec2f : fbxMesh.texCoords) {
vec2h texCoordVec2h;
texCoordVec2h.x = glm::detail::toFloat16(texCoordVec2f.x);
texCoordVec2h.y = glm::detail::toFloat16(texCoordVec2f.y);
texCoordData.push_back(texCoordVec2h);
}
attribBuffer->setSubData(texCoordsOffset, texCoordsSize, (const gpu::Byte*) texCoordData.constData());
}
if (texCoords1Size > 0) {
QVector<vec2h> texCoordData;
texCoordData.reserve(fbxMesh.texCoords1.size());
for (auto& texCoordVec2f : fbxMesh.texCoords1) {
vec2h texCoordVec2h;
texCoordVec2h.x = glm::detail::toFloat16(texCoordVec2f.x);
texCoordVec2h.y = glm::detail::toFloat16(texCoordVec2f.y);
texCoordData.push_back(texCoordVec2h);
}
attribBuffer->setSubData(texCoords1Offset, texCoords1Size, (const gpu::Byte*) texCoordData.constData());
}
if (fbxMesh.clusters.size() < UINT8_MAX) { if (fbxMesh.clusters.size() < UINT8_MAX) {
// yay! we can fit the clusterIndices within 8-bits // yay! we can fit the clusterIndices within 8-bits
@ -612,40 +712,37 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) {
assert(fbxMesh.clusterIndices[i] <= UINT8_MAX); assert(fbxMesh.clusterIndices[i] <= UINT8_MAX);
clusterIndices[i] = (uint8_t)(fbxMesh.clusterIndices[i]); clusterIndices[i] = (uint8_t)(fbxMesh.clusterIndices[i]);
} }
attribBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (gpu::Byte*) clusterIndices.constData()); attribBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) clusterIndices.constData());
} else { } else {
attribBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (gpu::Byte*) fbxMesh.clusterIndices.constData()); attribBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) fbxMesh.clusterIndices.constData());
} }
attribBuffer->setSubData(clusterWeightsOffset, clusterWeightsSize, (gpu::Byte*) fbxMesh.clusterWeights.constData()); attribBuffer->setSubData(clusterWeightsOffset, clusterWeightsSize, (const gpu::Byte*) fbxMesh.clusterWeights.constData());
if (normalsSize) { if (normalsSize) {
mesh->addAttribute(gpu::Stream::NORMAL, mesh->addAttribute(gpu::Stream::NORMAL,
model::BufferView(attribBuffer, normalsOffset, normalsSize, model::BufferView(attribBuffer, normalsOffset, normalsAndTangentsSize,
gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ))); normalsAndTangentsStride, FBX_NORMAL_ELEMENT));
}
if (tangentsSize) {
mesh->addAttribute(gpu::Stream::TANGENT, mesh->addAttribute(gpu::Stream::TANGENT,
model::BufferView(attribBuffer, tangentsOffset, tangentsSize, model::BufferView(attribBuffer, tangentsOffset, normalsAndTangentsSize,
gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ))); normalsAndTangentsStride, FBX_NORMAL_ELEMENT));
} }
if (colorsSize) { if (colorsSize) {
mesh->addAttribute(gpu::Stream::COLOR, mesh->addAttribute(gpu::Stream::COLOR,
model::BufferView(attribBuffer, colorsOffset, colorsSize, model::BufferView(attribBuffer, colorsOffset, colorsSize, FBX_COLOR_ELEMENT));
gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB)));
} }
if (texCoordsSize) { if (texCoordsSize) {
mesh->addAttribute(gpu::Stream::TEXCOORD, mesh->addAttribute(gpu::Stream::TEXCOORD,
model::BufferView( attribBuffer, texCoordsOffset, texCoordsSize, model::BufferView( attribBuffer, texCoordsOffset, texCoordsSize,
gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV))); gpu::Element(gpu::VEC2, gpu::HALF, gpu::UV)));
} }
if (texCoords1Size) { if (texCoords1Size) {
mesh->addAttribute( gpu::Stream::TEXCOORD1, mesh->addAttribute( gpu::Stream::TEXCOORD1,
model::BufferView(attribBuffer, texCoords1Offset, texCoords1Size, model::BufferView(attribBuffer, texCoords1Offset, texCoords1Size,
gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV))); gpu::Element(gpu::VEC2, gpu::HALF, gpu::UV)));
} else if (texCoordsSize) { } else if (texCoordsSize) {
mesh->addAttribute(gpu::Stream::TEXCOORD1, mesh->addAttribute(gpu::Stream::TEXCOORD1,
model::BufferView(attribBuffer, texCoordsOffset, texCoordsSize, model::BufferView(attribBuffer, texCoordsOffset, texCoordsSize,
gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV))); gpu::Element(gpu::VEC2, gpu::HALF, gpu::UV)));
} }
if (clusterIndicesSize) { if (clusterIndicesSize) {

View file

@ -110,7 +110,8 @@ static const GLenum ELEMENT_TYPE_TO_GL[gpu::NUM_TYPES] = {
GL_UNSIGNED_SHORT, GL_UNSIGNED_SHORT,
GL_BYTE, GL_BYTE,
GL_UNSIGNED_BYTE, GL_UNSIGNED_BYTE,
GL_UNSIGNED_BYTE GL_UNSIGNED_BYTE,
GL_INT_2_10_10_10_REV,
}; };
bool checkGLError(const char* name = nullptr); bool checkGLError(const char* name = nullptr);

View file

@ -218,6 +218,7 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
case gpu::NINT8: case gpu::NINT8:
result = GL_RGBA8_SNORM; result = GL_RGBA8_SNORM;
break; break;
case gpu::NINT2_10_10_10:
case gpu::NUINT32: case gpu::NUINT32:
case gpu::NINT32: case gpu::NINT32:
case gpu::COMPRESSED: case gpu::COMPRESSED:
@ -502,6 +503,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
} }
case gpu::COMPRESSED: case gpu::COMPRESSED:
case gpu::NUINT2: case gpu::NUINT2:
case gpu::NINT2_10_10_10:
case gpu::NUM_TYPES: { // quiet compiler case gpu::NUM_TYPES: { // quiet compiler
Q_UNREACHABLE(); Q_UNREACHABLE();
} }
@ -553,6 +555,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
} }
case gpu::COMPRESSED: case gpu::COMPRESSED:
case gpu::NUINT2: case gpu::NUINT2:
case gpu::NINT2_10_10_10:
case gpu::NUM_TYPES: { // quiet compiler case gpu::NUM_TYPES: { // quiet compiler
Q_UNREACHABLE(); Q_UNREACHABLE();
} }
@ -671,6 +674,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
break; break;
case gpu::NUINT32: case gpu::NUINT32:
case gpu::NINT32: case gpu::NINT32:
case gpu::NINT2_10_10_10:
case gpu::COMPRESSED: case gpu::COMPRESSED:
case gpu::NUM_TYPES: // quiet compiler case gpu::NUM_TYPES: // quiet compiler
Q_UNREACHABLE(); Q_UNREACHABLE();

View file

@ -38,6 +38,7 @@ const Element Element::VEC2F_UV{ VEC2, FLOAT, UV };
const Element Element::VEC2F_XY{ VEC2, FLOAT, XY }; const Element Element::VEC2F_XY{ VEC2, FLOAT, XY };
const Element Element::VEC3F_XYZ{ VEC3, FLOAT, XYZ }; const Element Element::VEC3F_XYZ{ VEC3, FLOAT, XYZ };
const Element Element::VEC4F_XYZW{ VEC4, FLOAT, XYZW }; const Element Element::VEC4F_XYZW{ VEC4, FLOAT, XYZW };
const Element Element::VEC4F_NORMALIZED_XYZ10W2{ VEC4, NINT2_10_10_10, XYZW };
const Element Element::INDEX_UINT16 { SCALAR, UINT16, INDEX }; const Element Element::INDEX_UINT16 { SCALAR, UINT16, INDEX };
const Element Element::INDEX_INT32 { SCALAR, INT32, INDEX }; const Element Element::INDEX_INT32 { SCALAR, INT32, INDEX };
const Element Element::PART_DRAWCALL{ VEC4, UINT32, PART }; const Element Element::PART_DRAWCALL{ VEC4, UINT32, PART };

View file

@ -39,6 +39,7 @@ enum Type : uint8_t {
NINT8, NINT8,
NUINT8, NUINT8,
NUINT2, NUINT2,
NINT2_10_10_10,
COMPRESSED, COMPRESSED,
@ -65,6 +66,7 @@ static const int TYPE_SIZE[NUM_TYPES] = {
2, 2,
1, 1,
1, 1,
4,
1 1
}; };
@ -86,6 +88,7 @@ static const bool TYPE_IS_INTEGER[NUM_TYPES] = {
false, false,
false, false,
false, false,
false,
false, false,
}; };
@ -326,6 +329,7 @@ public:
static const Element VEC2F_XY; static const Element VEC2F_XY;
static const Element VEC3F_XYZ; static const Element VEC3F_XYZ;
static const Element VEC4F_XYZW; static const Element VEC4F_XYZW;
static const Element VEC4F_NORMALIZED_XYZ10W2;
static const Element INDEX_UINT16; static const Element INDEX_UINT16;
static const Element INDEX_INT32; static const Element INDEX_INT32;
static const Element PART_DRAWCALL; static const Element PART_DRAWCALL;

View file

@ -92,6 +92,15 @@ bool Stream::Format::setAttribute(Slot slot, Slot channel, Frequency frequency)
return true; return true;
} }
Stream::Attribute Stream::Format::getAttribute(Slot slot) const {
auto attribIt = _attributes.find(slot);
if (attribIt != _attributes.end()) {
return attribIt->second;
} else {
return Attribute();
}
}
void BufferStream::addBuffer(const BufferPointer& buffer, Offset offset, Offset stride) { void BufferStream::addBuffer(const BufferPointer& buffer, Offset offset, Offset stride) {
_buffers.push_back(buffer); _buffers.push_back(buffer);
_offsets.push_back(offset); _offsets.push_back(offset);

View file

@ -112,6 +112,7 @@ public:
bool setAttribute(Slot slot, Slot channel, Frequency frequency = PER_VERTEX); bool setAttribute(Slot slot, Slot channel, Frequency frequency = PER_VERTEX);
bool hasAttribute(Slot slot) const { return (_attributes.find(slot) != _attributes.end()); } bool hasAttribute(Slot slot) const { return (_attributes.find(slot) != _attributes.end()); }
Attribute getAttribute(Slot slot) const;
const std::string& getKey() const { return _key; } const std::string& getKey() const { return _key; }

View file

@ -11,6 +11,8 @@
#include "Geometry.h" #include "Geometry.h"
#include <glm/gtc/packing.hpp>
using namespace model; using namespace model;
Mesh::Mesh() : Mesh::Mesh() :
@ -72,12 +74,14 @@ void Mesh::evalVertexStream() {
int channelNum = 0; int channelNum = 0;
if (hasVertexData()) { if (hasVertexData()) {
_vertexStream.addBuffer(_vertexBuffer._buffer, _vertexBuffer._offset, _vertexFormat->getChannelStride(channelNum)); auto stride = glm::max<gpu::Offset>(_vertexFormat->getChannelStride(channelNum), _vertexBuffer._stride);
_vertexStream.addBuffer(_vertexBuffer._buffer, _vertexBuffer._offset, stride);
channelNum++; channelNum++;
} }
for (auto attrib : _attributeBuffers) { for (auto attrib : _attributeBuffers) {
BufferView& view = attrib.second; BufferView& view = attrib.second;
_vertexStream.addBuffer(view._buffer, view._offset, _vertexFormat->getChannelStride(channelNum)); auto stride = glm::max<gpu::Offset>(_vertexFormat->getChannelStride(channelNum), view._stride);
_vertexStream.addBuffer(view._buffer, view._offset, stride);
channelNum++; channelNum++;
} }
} }
@ -137,13 +141,15 @@ model::MeshPointer Mesh::map(std::function<glm::vec3(glm::vec3)> vertexFunc,
std::function<glm::vec3(glm::vec3)> colorFunc, std::function<glm::vec3(glm::vec3)> colorFunc,
std::function<glm::vec3(glm::vec3)> normalFunc, std::function<glm::vec3(glm::vec3)> normalFunc,
std::function<uint32_t(uint32_t)> indexFunc) const { std::function<uint32_t(uint32_t)> indexFunc) const {
const auto vertexFormat = getVertexFormat();
// vertex data // vertex data
const gpu::BufferView& vertexBufferView = getVertexBuffer(); const gpu::BufferView& vertexBufferView = getVertexBuffer();
gpu::BufferView::Index numVertices = (gpu::BufferView::Index)getNumVertices(); gpu::BufferView::Index numVertices = (gpu::BufferView::Index)getNumVertices();
gpu::Resource::Size vertexSize = numVertices * sizeof(glm::vec3); gpu::Resource::Size vertexSize = numVertices * sizeof(glm::vec3);
unsigned char* resultVertexData = new unsigned char[vertexSize]; std::unique_ptr<unsigned char> resultVertexData{ new unsigned char[vertexSize] };
unsigned char* vertexDataCursor = resultVertexData; unsigned char* vertexDataCursor = resultVertexData.get();
for (gpu::BufferView::Index i = 0; i < numVertices; i++) { for (gpu::BufferView::Index i = 0; i < numVertices; i++) {
glm::vec3 pos = vertexFunc(vertexBufferView.get<glm::vec3>(i)); glm::vec3 pos = vertexFunc(vertexBufferView.get<glm::vec3>(i));
@ -157,13 +163,24 @@ model::MeshPointer Mesh::map(std::function<glm::vec3(glm::vec3)> vertexFunc,
gpu::BufferView::Index numColors = (gpu::BufferView::Index)colorsBufferView.getNumElements(); gpu::BufferView::Index numColors = (gpu::BufferView::Index)colorsBufferView.getNumElements();
gpu::Resource::Size colorSize = numColors * sizeof(glm::vec3); gpu::Resource::Size colorSize = numColors * sizeof(glm::vec3);
unsigned char* resultColorData = new unsigned char[colorSize]; std::unique_ptr<unsigned char> resultColorData{ new unsigned char[colorSize] };
unsigned char* colorDataCursor = resultColorData; unsigned char* colorDataCursor = resultColorData.get();
auto colorAttribute = vertexFormat->getAttribute(attributeTypeColor);
for (gpu::BufferView::Index i = 0; i < numColors; i++) { if (colorAttribute._element == gpu::Element::VEC3F_XYZ) {
glm::vec3 color = colorFunc(colorsBufferView.get<glm::vec3>(i)); for (gpu::BufferView::Index i = 0; i < numColors; i++) {
memcpy(colorDataCursor, &color, sizeof(color)); glm::vec3 color = colorFunc(colorsBufferView.get<glm::vec3>(i));
colorDataCursor += sizeof(color); memcpy(colorDataCursor, &color, sizeof(color));
colorDataCursor += sizeof(color);
}
} else if (colorAttribute._element == gpu::Element::COLOR_RGBA_32) {
for (gpu::BufferView::Index i = 0; i < numColors; i++) {
auto rawColor = colorsBufferView.get<glm::uint32>(i);
auto color = glm::vec3(glm::unpackUnorm4x8(rawColor));
color = colorFunc(color);
memcpy(colorDataCursor, &color, sizeof(color));
colorDataCursor += sizeof(color);
}
} }
// normal data // normal data
@ -171,22 +188,34 @@ model::MeshPointer Mesh::map(std::function<glm::vec3(glm::vec3)> vertexFunc,
const gpu::BufferView& normalsBufferView = getAttributeBuffer(attributeTypeNormal); const gpu::BufferView& normalsBufferView = getAttributeBuffer(attributeTypeNormal);
gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements(); gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements();
gpu::Resource::Size normalSize = numNormals * sizeof(glm::vec3); gpu::Resource::Size normalSize = numNormals * sizeof(glm::vec3);
unsigned char* resultNormalData = new unsigned char[normalSize]; std::unique_ptr<unsigned char> resultNormalData{ new unsigned char[normalSize] };
unsigned char* normalDataCursor = resultNormalData; unsigned char* normalDataCursor = resultNormalData.get();
auto normalAttribute = vertexFormat->getAttribute(attributeTypeNormal);
for (gpu::BufferView::Index i = 0; i < numNormals; i++) { if (normalAttribute._element == gpu::Element::VEC3F_XYZ) {
glm::vec3 normal = normalFunc(normalsBufferView.get<glm::vec3>(i)); for (gpu::BufferView::Index i = 0; i < numNormals; i++) {
memcpy(normalDataCursor, &normal, sizeof(normal)); glm::vec3 normal = normalFunc(normalsBufferView.get<glm::vec3>(i));
normalDataCursor += sizeof(normal); memcpy(normalDataCursor, &normal, sizeof(normal));
normalDataCursor += sizeof(normal);
}
} else if (normalAttribute._element == gpu::Element::VEC4F_NORMALIZED_XYZ10W2) {
for (gpu::BufferView::Index i = 0; i < numColors; i++) {
auto packedNormal = normalsBufferView.get<glm::uint32>(i);
auto normal = glm::vec3(glm::unpackSnorm3x10_1x2(packedNormal));
normal = normalFunc(normal);
memcpy(normalDataCursor, &normal, sizeof(normal));
normalDataCursor += sizeof(normal);
}
} }
// TODO -- other attributes // TODO -- other attributes
// face data // face data
const gpu::BufferView& indexBufferView = getIndexBuffer(); const gpu::BufferView& indexBufferView = getIndexBuffer();
gpu::BufferView::Index numIndexes = (gpu::BufferView::Index)getNumIndices(); gpu::BufferView::Index numIndexes = (gpu::BufferView::Index)getNumIndices();
gpu::Resource::Size indexSize = numIndexes * sizeof(uint32_t); gpu::Resource::Size indexSize = numIndexes * sizeof(uint32_t);
unsigned char* resultIndexData = new unsigned char[indexSize]; std::unique_ptr<unsigned char> resultIndexData{ new unsigned char[indexSize] };
unsigned char* indexDataCursor = resultIndexData; unsigned char* indexDataCursor = resultIndexData.get();
for (gpu::BufferView::Index i = 0; i < numIndexes; i++) { for (gpu::BufferView::Index i = 0; i < numIndexes; i++) {
uint32_t index = indexFunc(indexBufferView.get<uint32_t>(i)); uint32_t index = indexFunc(indexBufferView.get<uint32_t>(i));
@ -197,25 +226,25 @@ model::MeshPointer Mesh::map(std::function<glm::vec3(glm::vec3)> vertexFunc,
model::MeshPointer result(new model::Mesh()); model::MeshPointer result(new model::Mesh());
gpu::Element vertexElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); gpu::Element vertexElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ);
gpu::Buffer* resultVertexBuffer = new gpu::Buffer(vertexSize, resultVertexData); gpu::Buffer* resultVertexBuffer = new gpu::Buffer(vertexSize, resultVertexData.get());
gpu::BufferPointer resultVertexBufferPointer(resultVertexBuffer); gpu::BufferPointer resultVertexBufferPointer(resultVertexBuffer);
gpu::BufferView resultVertexBufferView(resultVertexBufferPointer, vertexElement); gpu::BufferView resultVertexBufferView(resultVertexBufferPointer, vertexElement);
result->setVertexBuffer(resultVertexBufferView); result->setVertexBuffer(resultVertexBufferView);
gpu::Element colorElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); gpu::Element colorElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ);
gpu::Buffer* resultColorsBuffer = new gpu::Buffer(colorSize, resultColorData); gpu::Buffer* resultColorsBuffer = new gpu::Buffer(colorSize, resultColorData.get());
gpu::BufferPointer resultColorsBufferPointer(resultColorsBuffer); gpu::BufferPointer resultColorsBufferPointer(resultColorsBuffer);
gpu::BufferView resultColorsBufferView(resultColorsBufferPointer, colorElement); gpu::BufferView resultColorsBufferView(resultColorsBufferPointer, colorElement);
result->addAttribute(attributeTypeColor, resultColorsBufferView); result->addAttribute(attributeTypeColor, resultColorsBufferView);
gpu::Element normalElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); gpu::Element normalElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ);
gpu::Buffer* resultNormalsBuffer = new gpu::Buffer(normalSize, resultNormalData); gpu::Buffer* resultNormalsBuffer = new gpu::Buffer(normalSize, resultNormalData.get());
gpu::BufferPointer resultNormalsBufferPointer(resultNormalsBuffer); gpu::BufferPointer resultNormalsBufferPointer(resultNormalsBuffer);
gpu::BufferView resultNormalsBufferView(resultNormalsBufferPointer, normalElement); gpu::BufferView resultNormalsBufferView(resultNormalsBufferPointer, normalElement);
result->addAttribute(attributeTypeNormal, resultNormalsBufferView); result->addAttribute(attributeTypeNormal, resultNormalsBufferView);
gpu::Element indexElement = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW); gpu::Element indexElement = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW);
gpu::Buffer* resultIndexesBuffer = new gpu::Buffer(indexSize, resultIndexData); gpu::Buffer* resultIndexesBuffer = new gpu::Buffer(indexSize, resultIndexData.get());
gpu::BufferPointer resultIndexesBufferPointer(resultIndexesBuffer); gpu::BufferPointer resultIndexesBufferPointer(resultIndexesBuffer);
gpu::BufferView resultIndexesBufferView(resultIndexesBufferPointer, indexElement); gpu::BufferView resultIndexesBufferView(resultIndexesBufferPointer, indexElement);
result->setIndexBuffer(resultIndexesBufferView); result->setIndexBuffer(resultIndexesBufferView);
@ -239,6 +268,8 @@ void Mesh::forEach(std::function<void(glm::vec3)> vertexFunc,
std::function<void(glm::vec3)> colorFunc, std::function<void(glm::vec3)> colorFunc,
std::function<void(glm::vec3)> normalFunc, std::function<void(glm::vec3)> normalFunc,
std::function<void(uint32_t)> indexFunc) { std::function<void(uint32_t)> indexFunc) {
const auto vertexFormat = getVertexFormat();
// vertex data // vertex data
const gpu::BufferView& vertexBufferView = getVertexBuffer(); const gpu::BufferView& vertexBufferView = getVertexBuffer();
gpu::BufferView::Index numVertices = (gpu::BufferView::Index)getNumVertices(); gpu::BufferView::Index numVertices = (gpu::BufferView::Index)getNumVertices();
@ -250,17 +281,36 @@ void Mesh::forEach(std::function<void(glm::vec3)> vertexFunc,
int attributeTypeColor = gpu::Stream::InputSlot::COLOR; // libraries/gpu/src/gpu/Stream.h int attributeTypeColor = gpu::Stream::InputSlot::COLOR; // libraries/gpu/src/gpu/Stream.h
const gpu::BufferView& colorsBufferView = getAttributeBuffer(attributeTypeColor); const gpu::BufferView& colorsBufferView = getAttributeBuffer(attributeTypeColor);
gpu::BufferView::Index numColors = (gpu::BufferView::Index)colorsBufferView.getNumElements(); gpu::BufferView::Index numColors = (gpu::BufferView::Index)colorsBufferView.getNumElements();
for (gpu::BufferView::Index i = 0; i < numColors; i++) { auto colorAttribute = vertexFormat->getAttribute(attributeTypeColor);
colorFunc(colorsBufferView.get<glm::vec3>(i)); if (colorAttribute._element == gpu::Element::VEC3F_XYZ) {
for (gpu::BufferView::Index i = 0; i < numColors; i++) {
colorFunc(colorsBufferView.get<glm::vec3>(i));
}
} else if (colorAttribute._element == gpu::Element::COLOR_RGBA_32) {
for (gpu::BufferView::Index i = 0; i < numColors; i++) {
auto rawColor = colorsBufferView.get<glm::uint32>(i);
auto color = glm::unpackUnorm4x8(rawColor);
colorFunc(color);
}
} }
// normal data // normal data
int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h
const gpu::BufferView& normalsBufferView = getAttributeBuffer(attributeTypeNormal); const gpu::BufferView& normalsBufferView = getAttributeBuffer(attributeTypeNormal);
gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements(); gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements();
for (gpu::BufferView::Index i = 0; i < numNormals; i++) { auto normalAttribute = vertexFormat->getAttribute(attributeTypeNormal);
normalFunc(normalsBufferView.get<glm::vec3>(i)); if (normalAttribute._element == gpu::Element::VEC3F_XYZ) {
for (gpu::BufferView::Index i = 0; i < numNormals; i++) {
normalFunc(normalsBufferView.get<glm::vec3>(i));
}
} else if (normalAttribute._element == gpu::Element::VEC4F_NORMALIZED_XYZ10W2) {
for (gpu::BufferView::Index i = 0; i < numColors; i++) {
auto packedNormal = normalsBufferView.get<glm::uint32>(i);
auto normal = glm::unpackSnorm3x10_1x2(packedNormal);
normalFunc(normal);
}
} }
// TODO -- other attributes // TODO -- other attributes
// face data // face data

View file

@ -202,7 +202,7 @@ enum class EntityVersion : PacketVersion {
HazeEffect, HazeEffect,
StaticCertJsonVersionOne, StaticCertJsonVersionOne,
OwnershipChallengeFix, OwnershipChallengeFix,
ZoneLightInheritModes ZoneLightInheritModes
}; };
enum class EntityScriptCallMethodVersion : PacketVersion { enum class EntityScriptCallMethodVersion : PacketVersion {

View file

@ -763,7 +763,8 @@ void DefaultLightingSetup::run(const RenderContextPointer& renderContext) {
_defaultLight = lp; _defaultLight = lp;
// Add the global light to the light stage (for later shadow rendering) // Add the global light to the light stage (for later shadow rendering)
_defaultLightID = lightStage->addLight(lp); // Set this light to be the default
_defaultLightID = lightStage->addLight(lp, true);
lightStage->addShadow(_defaultLightID); lightStage->addShadow(_defaultLightID);
} }

View file

@ -67,7 +67,6 @@ public:
NetworkTexturePointer _ambientTexture; NetworkTexturePointer _ambientTexture;
QString _ambientTextureURL; QString _ambientTextureURL;
bool _pendingAmbientTexture { false }; bool _pendingAmbientTexture { false };
bool _validAmbientTextureURL { false };
protected: protected:
model::LightPointer _light; model::LightPointer _light;

View file

@ -34,28 +34,31 @@ LightStage::LightStage() {
ambientOffLight->setColor(model::Vec3(0.0)); ambientOffLight->setColor(model::Vec3(0.0));
ambientOffLight->setIntensity(0.0f); ambientOffLight->setIntensity(0.0f);
ambientOffLight->setType(model::Light::Type::AMBIENT); ambientOffLight->setType(model::Light::Type::AMBIENT);
_ambientOffLight = addLight(ambientOffLight); _ambientOffLightId = addLight(ambientOffLight);
const LightPointer pointOffLight { std::make_shared<model::Light>() }; const LightPointer pointOffLight { std::make_shared<model::Light>() };
pointOffLight->setAmbientIntensity(0.0f); pointOffLight->setAmbientIntensity(0.0f);
pointOffLight->setColor(model::Vec3(0.0)); pointOffLight->setColor(model::Vec3(0.0));
pointOffLight->setIntensity(0.0f); pointOffLight->setIntensity(0.0f);
pointOffLight->setType(model::Light::Type::POINT); pointOffLight->setType(model::Light::Type::POINT);
_pointOffLight = addLight(pointOffLight); _pointOffLightId = addLight(pointOffLight);
const LightPointer spotOffLight { std::make_shared<model::Light>() }; const LightPointer spotOffLight { std::make_shared<model::Light>() };
spotOffLight->setAmbientIntensity(0.0f); spotOffLight->setAmbientIntensity(0.0f);
spotOffLight->setColor(model::Vec3(0.0)); spotOffLight->setColor(model::Vec3(0.0));
spotOffLight->setIntensity(0.0f); spotOffLight->setIntensity(0.0f);
spotOffLight->setType(model::Light::Type::SPOT); spotOffLight->setType(model::Light::Type::SPOT);
_spotOffLight = addLight(spotOffLight); _spotOffLightId = addLight(spotOffLight);
const LightPointer sunOffLight { std::make_shared<model::Light>() }; const LightPointer sunOffLight { std::make_shared<model::Light>() };
sunOffLight->setAmbientIntensity(0.0f); sunOffLight->setAmbientIntensity(0.0f);
sunOffLight->setColor(model::Vec3(0.0)); sunOffLight->setColor(model::Vec3(0.0));
sunOffLight->setIntensity(0.0f); sunOffLight->setIntensity(0.0f);
sunOffLight->setType(model::Light::Type::SUN); sunOffLight->setType(model::Light::Type::SUN);
_sunOffLight = addLight(sunOffLight); _sunOffLightId = addLight(sunOffLight);
// Set default light to the off ambient light (until changed)
_defaultLightId = _ambientOffLightId;
} }
LightStage::Shadow::Schema::Schema() { LightStage::Shadow::Schema::Schema() {
@ -294,10 +297,12 @@ LightStage::Index LightStage::findLight(const LightPointer& light) const {
} }
} }
LightStage::Index LightStage::addLight(const LightPointer& light) { LightStage::Index LightStage::addLight(const LightPointer& light, const bool shouldSetAsDefault) {
Index lightId;
auto found = _lightMap.find(light); auto found = _lightMap.find(light);
if (found == _lightMap.end()) { if (found == _lightMap.end()) {
auto lightId = _lights.newElement(light); lightId = _lights.newElement(light);
// Avoid failing to allocate a light, just pass // Avoid failing to allocate a light, just pass
if (lightId != INVALID_INDEX) { if (lightId != INVALID_INDEX) {
@ -314,10 +319,15 @@ LightStage::Index LightStage::addLight(const LightPointer& light) {
updateLightArrayBuffer(lightId); updateLightArrayBuffer(lightId);
} }
return lightId;
} else { } else {
return (*found).second; lightId = (*found).second;
} }
if (shouldSetAsDefault) {
_defaultLightId = lightId;
}
return lightId;
} }
LightStage::Index LightStage::addShadow(Index lightIndex, float maxDistance, unsigned int cascadeCount) { LightStage::Index LightStage::addShadow(Index lightIndex, float maxDistance, unsigned int cascadeCount) {
@ -349,7 +359,7 @@ LightStage::LightPointer LightStage::removeLight(Index index) {
} }
LightStage::LightPointer LightStage::getCurrentKeyLight() const { LightStage::LightPointer LightStage::getCurrentKeyLight() const {
Index keyLightId{ 0 }; Index keyLightId{ _defaultLightId };
if (!_currentFrame._sunLights.empty()) { if (!_currentFrame._sunLights.empty()) {
keyLightId = _currentFrame._sunLights.front(); keyLightId = _currentFrame._sunLights.front();
} }
@ -357,7 +367,7 @@ LightStage::LightPointer LightStage::getCurrentKeyLight() const {
} }
LightStage::LightPointer LightStage::getCurrentAmbientLight() const { LightStage::LightPointer LightStage::getCurrentAmbientLight() const {
Index keyLightId{ 0 }; Index keyLightId { _defaultLightId };
if (!_currentFrame._ambientLights.empty()) { if (!_currentFrame._ambientLights.empty()) {
keyLightId = _currentFrame._ambientLights.front(); keyLightId = _currentFrame._ambientLights.front();
} }
@ -365,7 +375,7 @@ LightStage::LightPointer LightStage::getCurrentAmbientLight() const {
} }
LightStage::ShadowPointer LightStage::getCurrentKeyShadow() const { LightStage::ShadowPointer LightStage::getCurrentKeyShadow() const {
Index keyLightId{ 0 }; Index keyLightId { _defaultLightId };
if (!_currentFrame._sunLights.empty()) { if (!_currentFrame._sunLights.empty()) {
keyLightId = _currentFrame._sunLights.front(); keyLightId = _currentFrame._sunLights.front();
} }
@ -375,7 +385,7 @@ LightStage::ShadowPointer LightStage::getCurrentKeyShadow() const {
} }
LightStage::LightAndShadow LightStage::getCurrentKeyLightAndShadow() const { LightStage::LightAndShadow LightStage::getCurrentKeyLightAndShadow() const {
Index keyLightId{ 0 }; Index keyLightId { _defaultLightId };
if (!_currentFrame._sunLights.empty()) { if (!_currentFrame._sunLights.empty()) {
keyLightId = _currentFrame._sunLights.front(); keyLightId = _currentFrame._sunLights.front();
} }

View file

@ -118,7 +118,9 @@ public:
using Shadows = render::indexed_container::IndexedPointerVector<Shadow>; using Shadows = render::indexed_container::IndexedPointerVector<Shadow>;
Index findLight(const LightPointer& light) const; Index findLight(const LightPointer& light) const;
Index addLight(const LightPointer& light); Index addLight(const LightPointer& light, const bool shouldSetAsDefault = false);
Index getDefaultLight() { return _defaultLightId; }
Index addShadow(Index lightIndex, float maxDistance = 20.0f, unsigned int cascadeCount = 1U); Index addShadow(Index lightIndex, float maxDistance = 20.0f, unsigned int cascadeCount = 1U);
@ -185,10 +187,10 @@ public:
Frame _currentFrame; Frame _currentFrame;
Index getAmbientOffLight() { return _ambientOffLight; } Index getAmbientOffLight() { return _ambientOffLightId; }
Index getPointOffLight() { return _pointOffLight; } Index getPointOffLight() { return _pointOffLightId; }
Index getSpotOffLight() { return _spotOffLight; } Index getSpotOffLight() { return _spotOffLightId; }
Index getSunOffLight() { return _sunOffLight; } Index getSunOffLight() { return _sunOffLightId; }
protected: protected:
@ -205,17 +207,12 @@ protected:
LightMap _lightMap; LightMap _lightMap;
// define off lights // define off lights
Index _ambientOffLightId;
Index _pointOffLightId;
Index _spotOffLightId;
Index _sunOffLightId;
const LightPointer ambientOffLight { std::make_shared<model::Light>() }; Index _defaultLightId;
const LightPointer pointOffLight { std::make_shared<model::Light>() };
const LightPointer spotOffLight { std::make_shared<model::Light>() };
const LightPointer sunOffLight { std::make_shared<model::Light>() };
Index _ambientOffLight;
Index _pointOffLight;
Index _spotOffLight;
Index _sunOffLight;
}; };
using LightStagePointer = std::shared_ptr<LightStage>; using LightStagePointer = std::shared_ptr<LightStage>;

View file

@ -484,7 +484,8 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) {
batch.setInputFormat((_drawMesh->getVertexFormat())); batch.setInputFormat((_drawMesh->getVertexFormat()));
if (_isBlendShaped && _blendedVertexBuffer) { if (_isBlendShaped && _blendedVertexBuffer) {
batch.setInputBuffer(0, _blendedVertexBuffer, 0, sizeof(glm::vec3)); batch.setInputBuffer(0, _blendedVertexBuffer, 0, sizeof(glm::vec3));
batch.setInputBuffer(1, _blendedVertexBuffer, _drawMesh->getNumVertices() * sizeof(glm::vec3), sizeof(glm::vec3)); // Stride is 2*sizeof(glm::vec3) because normal and tangents are interleaved
batch.setInputBuffer(1, _blendedVertexBuffer, _drawMesh->getNumVertices() * sizeof(glm::vec3), 2 * sizeof(NormalType));
batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2)); batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2));
} else { } else {
batch.setInputStream(0, _drawMesh->getVertexStream()); batch.setInputStream(0, _drawMesh->getVertexStream());

View file

@ -24,8 +24,12 @@
#include <PerfStat.h> #include <PerfStat.h>
#include <ViewFrustum.h> #include <ViewFrustum.h>
#include <GLMHelpers.h> #include <GLMHelpers.h>
#include <TBBHelpers.h>
#include <model-networking/SimpleMeshProxy.h> #include <model-networking/SimpleMeshProxy.h>
#include <glm/gtc/packing.hpp>
#include "AbstractViewStateInterface.h" #include "AbstractViewStateInterface.h"
#include "MeshPartPayload.h" #include "MeshPartPayload.h"
@ -310,6 +314,34 @@ void Model::reset() {
} }
} }
#if FBX_PACK_NORMALS
static void packNormalAndTangent(glm::vec3 normal, glm::vec3 tangent, glm::uint32& packedNormal, glm::uint32& packedTangent) {
auto absNormal = glm::abs(normal);
auto absTangent = glm::abs(tangent);
normal /= glm::max(1e-6f, glm::max(glm::max(absNormal.x, absNormal.y), absNormal.z));
tangent /= glm::max(1e-6f, glm::max(glm::max(absTangent.x, absTangent.y), absTangent.z));
normal = glm::clamp(normal, -1.0f, 1.0f);
tangent = glm::clamp(tangent, -1.0f, 1.0f);
normal *= 511.0f;
tangent *= 511.0f;
normal = glm::round(normal);
tangent = glm::round(tangent);
glm::detail::i10i10i10i2 normalStruct;
glm::detail::i10i10i10i2 tangentStruct;
normalStruct.data.x = int(normal.x);
normalStruct.data.y = int(normal.y);
normalStruct.data.z = int(normal.z);
normalStruct.data.w = 0;
tangentStruct.data.x = int(tangent.x);
tangentStruct.data.y = int(tangent.y);
tangentStruct.data.z = int(tangent.z);
tangentStruct.data.w = 0;
packedNormal = normalStruct.pack;
packedTangent = tangentStruct.pack;
}
#endif
bool Model::updateGeometry() { bool Model::updateGeometry() {
bool needFullUpdate = false; bool needFullUpdate = false;
@ -335,10 +367,28 @@ bool Model::updateGeometry() {
// TODO? make _blendedVertexBuffers a map instead of vector and only add for meshes with blendshapes? // TODO? make _blendedVertexBuffers a map instead of vector and only add for meshes with blendshapes?
auto buffer = std::make_shared<gpu::Buffer>(); auto buffer = std::make_shared<gpu::Buffer>();
if (!mesh.blendshapes.isEmpty()) { if (!mesh.blendshapes.isEmpty()) {
buffer->resize((mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3)); std::vector<NormalType> normalsAndTangents;
buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.vertices.constData()); normalsAndTangents.reserve(mesh.normals.size() + mesh.tangents.size());
for (auto normalIt = mesh.normals.begin(), tangentIt = mesh.tangents.begin();
normalIt != mesh.normals.end();
++normalIt, ++tangentIt) {
#if FBX_PACK_NORMALS
glm::uint32 finalNormal;
glm::uint32 finalTangent;
packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent);
#else
const auto finalNormal = *normalIt;
const auto finalTangent = *tangentIt;
#endif
normalsAndTangents.push_back(finalNormal);
normalsAndTangents.push_back(finalTangent);
}
buffer->resize(mesh.vertices.size() * (sizeof(glm::vec3) + 2 * sizeof(NormalType)));
buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (const gpu::Byte*) mesh.vertices.constData());
buffer->setSubData(mesh.vertices.size() * sizeof(glm::vec3), buffer->setSubData(mesh.vertices.size() * sizeof(glm::vec3),
mesh.normals.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.normals.constData()); mesh.normals.size() * 2 * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data());
} }
_blendedVertexBuffers.push_back(buffer); _blendedVertexBuffers.push_back(buffer);
} }
@ -1006,7 +1056,7 @@ Blender::Blender(ModelPointer model, int blendNumber, const Geometry::WeakPointe
void Blender::run() { void Blender::run() {
DETAILED_PROFILE_RANGE_EX(simulation_animation, __FUNCTION__, 0xFFFF0000, 0, { { "url", _model->getURL().toString() } }); DETAILED_PROFILE_RANGE_EX(simulation_animation, __FUNCTION__, 0xFFFF0000, 0, { { "url", _model->getURL().toString() } });
QVector<glm::vec3> vertices, normals; QVector<glm::vec3> vertices, normals, tangents;
if (_model) { if (_model) {
int offset = 0; int offset = 0;
foreach (const FBXMesh& mesh, _meshes) { foreach (const FBXMesh& mesh, _meshes) {
@ -1015,8 +1065,10 @@ void Blender::run() {
} }
vertices += mesh.vertices; vertices += mesh.vertices;
normals += mesh.normals; normals += mesh.normals;
tangents += mesh.tangents;
glm::vec3* meshVertices = vertices.data() + offset; glm::vec3* meshVertices = vertices.data() + offset;
glm::vec3* meshNormals = normals.data() + offset; glm::vec3* meshNormals = normals.data() + offset;
glm::vec3* meshTangents = tangents.data() + offset;
offset += mesh.vertices.size(); offset += mesh.vertices.size();
const float NORMAL_COEFFICIENT_SCALE = 0.01f; const float NORMAL_COEFFICIENT_SCALE = 0.01f;
for (int i = 0, n = qMin(_blendshapeCoefficients.size(), mesh.blendshapes.size()); i < n; i++) { for (int i = 0, n = qMin(_blendshapeCoefficients.size(), mesh.blendshapes.size()); i < n; i++) {
@ -1031,6 +1083,7 @@ void Blender::run() {
int index = blendshape.indices.at(j); int index = blendshape.indices.at(j);
meshVertices[index] += blendshape.vertices.at(j) * vertexCoefficient; meshVertices[index] += blendshape.vertices.at(j) * vertexCoefficient;
meshNormals[index] += blendshape.normals.at(j) * normalCoefficient; meshNormals[index] += blendshape.normals.at(j) * normalCoefficient;
meshTangents[index] += blendshape.tangents.at(j) * normalCoefficient;
} }
} }
} }
@ -1039,7 +1092,7 @@ void Blender::run() {
QMetaObject::invokeMethod(DependencyManager::get<ModelBlender>().data(), "setBlendedVertices", QMetaObject::invokeMethod(DependencyManager::get<ModelBlender>().data(), "setBlendedVertices",
Q_ARG(ModelPointer, _model), Q_ARG(int, _blendNumber), Q_ARG(ModelPointer, _model), Q_ARG(int, _blendNumber),
Q_ARG(const Geometry::WeakPointer&, _geometry), Q_ARG(const QVector<glm::vec3>&, vertices), Q_ARG(const Geometry::WeakPointer&, _geometry), Q_ARG(const QVector<glm::vec3>&, vertices),
Q_ARG(const QVector<glm::vec3>&, normals)); Q_ARG(const QVector<glm::vec3>&, normals), Q_ARG(const QVector<glm::vec3>&, tangents));
} }
void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions, bool forceRescale) { void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions, bool forceRescale) {
@ -1220,7 +1273,7 @@ bool Model::maybeStartBlender() {
} }
void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geometry, void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geometry,
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals) { const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals, const QVector<glm::vec3>& tangents) {
auto geometryRef = geometry.lock(); auto geometryRef = geometry.lock();
if (!geometryRef || _renderGeometry != geometryRef || _blendedVertexBuffers.empty() || blendNumber < _appliedBlendNumber) { if (!geometryRef || _renderGeometry != geometryRef || _blendedVertexBuffers.empty() || blendNumber < _appliedBlendNumber) {
return; return;
@ -1228,6 +1281,7 @@ void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geo
_appliedBlendNumber = blendNumber; _appliedBlendNumber = blendNumber;
const FBXGeometry& fbxGeometry = getFBXGeometry(); const FBXGeometry& fbxGeometry = getFBXGeometry();
int index = 0; int index = 0;
std::vector<NormalType> normalsAndTangents;
for (int i = 0; i < fbxGeometry.meshes.size(); i++) { for (int i = 0; i < fbxGeometry.meshes.size(); i++) {
const FBXMesh& mesh = fbxGeometry.meshes.at(i); const FBXMesh& mesh = fbxGeometry.meshes.at(i);
if (mesh.blendshapes.isEmpty()) { if (mesh.blendshapes.isEmpty()) {
@ -1235,11 +1289,67 @@ void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geo
} }
gpu::BufferPointer& buffer = _blendedVertexBuffers[i]; gpu::BufferPointer& buffer = _blendedVertexBuffers[i];
buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Byte*) vertices.constData() + index*sizeof(glm::vec3)); const auto vertexCount = mesh.vertices.size();
buffer->setSubData(mesh.vertices.size() * sizeof(glm::vec3), const auto verticesSize = vertexCount * sizeof(glm::vec3);
mesh.normals.size() * sizeof(glm::vec3), (gpu::Byte*) normals.constData() + index*sizeof(glm::vec3)); const auto offset = index * sizeof(glm::vec3);
index += mesh.vertices.size(); normalsAndTangents.clear();
normalsAndTangents.resize(normals.size()+tangents.size());
assert(normalsAndTangents.size() == 2 * vertexCount);
// Interleave normals and tangents
#if 0
// Sequential version for debugging
auto normalsRange = std::make_pair(normals.begin() + index, normals.begin() + index + vertexCount);
auto tangentsRange = std::make_pair(tangents.begin() + index, tangents.begin() + index + vertexCount);
auto normalsAndTangentsIt = normalsAndTangents.begin();
for (auto normalIt = normalsRange.first, tangentIt = tangentsRange.first;
normalIt != normalsRange.second;
++normalIt, ++tangentIt) {
#if FBX_PACK_NORMALS
glm::uint32 finalNormal;
glm::uint32 finalTangent;
packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent);
#else
const auto finalNormal = *normalIt;
const auto finalTangent = *tangentIt;
#endif
*normalsAndTangentsIt = finalNormal;
++normalsAndTangentsIt;
*normalsAndTangentsIt = finalTangent;
++normalsAndTangentsIt;
}
#else
// Parallel version for performance
tbb::parallel_for(tbb::blocked_range<size_t>(index, index+vertexCount), [&](const tbb::blocked_range<size_t>& range) {
auto normalsRange = std::make_pair(normals.begin() + range.begin(), normals.begin() + range.end());
auto tangentsRange = std::make_pair(tangents.begin() + range.begin(), tangents.begin() + range.end());
auto normalsAndTangentsIt = normalsAndTangents.begin() + (range.begin()-index)*2;
for (auto normalIt = normalsRange.first, tangentIt = tangentsRange.first;
normalIt != normalsRange.second;
++normalIt, ++tangentIt) {
#if FBX_PACK_NORMALS
glm::uint32 finalNormal;
glm::uint32 finalTangent;
packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent);
#else
const auto finalNormal = *normalIt;
const auto finalTangent = *tangentIt;
#endif
*normalsAndTangentsIt = finalNormal;
++normalsAndTangentsIt;
*normalsAndTangentsIt = finalTangent;
++normalsAndTangentsIt;
}
});
#endif
buffer->setSubData(0, verticesSize, (gpu::Byte*) vertices.constData() + offset);
buffer->setSubData(verticesSize, 2 * vertexCount * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data());
index += vertexCount;
} }
} }
@ -1406,10 +1516,11 @@ void ModelBlender::noteRequiresBlend(ModelPointer model) {
} }
} }
void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry,
const Geometry::WeakPointer& geometry, const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals) { const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals,
const QVector<glm::vec3>& tangents) {
if (model) { if (model) {
model->setBlendedVertices(blendNumber, geometry, vertices, normals); model->setBlendedVertices(blendNumber, geometry, vertices, normals, tangents);
} }
_pendingBlenders--; _pendingBlenders--;
{ {

View file

@ -114,7 +114,7 @@ public:
/// Sets blended vertices computed in a separate thread. /// Sets blended vertices computed in a separate thread.
void setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geometry, void setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geometry,
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals); const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals, const QVector<glm::vec3>& tangents);
bool isLoaded() const { return (bool)_renderGeometry && _renderGeometry->isGeometryLoaded(); } bool isLoaded() const { return (bool)_renderGeometry && _renderGeometry->isGeometryLoaded(); }
bool isAddedToScene() const { return _addedToScene; } bool isAddedToScene() const { return _addedToScene; }
@ -437,7 +437,7 @@ public:
public slots: public slots:
void setBlendedVertices(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry, void setBlendedVertices(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry,
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals); const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals, const QVector<glm::vec3>& tangents);
private: private:
using Mutex = std::mutex; using Mutex = std::mutex;

View file

@ -68,10 +68,11 @@ void SetupZones::run(const RenderContextPointer& context, const Inputs& inputs)
auto lightStage = context->_scene->getStage<LightStage>(); auto lightStage = context->_scene->getStage<LightStage>();
assert(lightStage); assert(lightStage);
lightStage->_currentFrame.pushSunLight(0); lightStage->_currentFrame.pushSunLight(lightStage->getDefaultLight());
lightStage->_currentFrame.pushAmbientLight(0); lightStage->_currentFrame.pushAmbientLight(lightStage->getDefaultLight());
hazeStage->_currentFrame.pushHaze(0);
backgroundStage->_currentFrame.pushBackground(0); backgroundStage->_currentFrame.pushBackground(0);
hazeStage->_currentFrame.pushHaze(0);
} }
const gpu::PipelinePointer& DebugZoneLighting::getKeyLightPipeline() { const gpu::PipelinePointer& DebugZoneLighting::getKeyLightPipeline() {

View file

@ -60,20 +60,20 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) {
// alloc the resulting mesh // alloc the resulting mesh
gpu::Resource::Size combinedVertexSize = totalVertexCount * sizeof(glm::vec3); gpu::Resource::Size combinedVertexSize = totalVertexCount * sizeof(glm::vec3);
unsigned char* combinedVertexData = new unsigned char[combinedVertexSize]; std::unique_ptr<unsigned char> combinedVertexData{ new unsigned char[combinedVertexSize] };
unsigned char* combinedVertexDataCursor = combinedVertexData; unsigned char* combinedVertexDataCursor = combinedVertexData.get();
gpu::Resource::Size combinedColorSize = totalColorCount * sizeof(glm::vec3); gpu::Resource::Size combinedColorSize = totalColorCount * sizeof(glm::vec3);
unsigned char* combinedColorData = new unsigned char[combinedColorSize]; std::unique_ptr<unsigned char> combinedColorData{ new unsigned char[combinedColorSize] };
unsigned char* combinedColorDataCursor = combinedColorData; unsigned char* combinedColorDataCursor = combinedColorData.get();
gpu::Resource::Size combinedNormalSize = totalNormalCount * sizeof(glm::vec3); gpu::Resource::Size combinedNormalSize = totalNormalCount * sizeof(glm::vec3);
unsigned char* combinedNormalData = new unsigned char[combinedNormalSize]; std::unique_ptr<unsigned char> combinedNormalData{ new unsigned char[combinedNormalSize] };
unsigned char* combinedNormalDataCursor = combinedNormalData; unsigned char* combinedNormalDataCursor = combinedNormalData.get();
gpu::Resource::Size combinedIndexSize = totalIndexCount * sizeof(uint32_t); gpu::Resource::Size combinedIndexSize = totalIndexCount * sizeof(uint32_t);
unsigned char* combinedIndexData = new unsigned char[combinedIndexSize]; std::unique_ptr<unsigned char> combinedIndexData{ new unsigned char[combinedIndexSize] };
unsigned char* combinedIndexDataCursor = combinedIndexData; unsigned char* combinedIndexDataCursor = combinedIndexData.get();
uint32_t indexStartOffset { 0 }; uint32_t indexStartOffset { 0 };
@ -105,27 +105,27 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) {
model::MeshPointer result(new model::Mesh()); model::MeshPointer result(new model::Mesh());
gpu::Element vertexElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); gpu::Element vertexElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ);
gpu::Buffer* combinedVertexBuffer = new gpu::Buffer(combinedVertexSize, combinedVertexData); gpu::Buffer* combinedVertexBuffer = new gpu::Buffer(combinedVertexSize, combinedVertexData.get());
gpu::BufferPointer combinedVertexBufferPointer(combinedVertexBuffer); gpu::BufferPointer combinedVertexBufferPointer(combinedVertexBuffer);
gpu::BufferView combinedVertexBufferView(combinedVertexBufferPointer, vertexElement); gpu::BufferView combinedVertexBufferView(combinedVertexBufferPointer, vertexElement);
result->setVertexBuffer(combinedVertexBufferView); result->setVertexBuffer(combinedVertexBufferView);
int attributeTypeColor = gpu::Stream::InputSlot::COLOR; // libraries/gpu/src/gpu/Stream.h int attributeTypeColor = gpu::Stream::InputSlot::COLOR; // libraries/gpu/src/gpu/Stream.h
gpu::Element colorElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); gpu::Element colorElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ);
gpu::Buffer* combinedColorsBuffer = new gpu::Buffer(combinedColorSize, combinedColorData); gpu::Buffer* combinedColorsBuffer = new gpu::Buffer(combinedColorSize, combinedColorData.get());
gpu::BufferPointer combinedColorsBufferPointer(combinedColorsBuffer); gpu::BufferPointer combinedColorsBufferPointer(combinedColorsBuffer);
gpu::BufferView combinedColorsBufferView(combinedColorsBufferPointer, colorElement); gpu::BufferView combinedColorsBufferView(combinedColorsBufferPointer, colorElement);
result->addAttribute(attributeTypeColor, combinedColorsBufferView); result->addAttribute(attributeTypeColor, combinedColorsBufferView);
int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h
gpu::Element normalElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); gpu::Element normalElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ);
gpu::Buffer* combinedNormalsBuffer = new gpu::Buffer(combinedNormalSize, combinedNormalData); gpu::Buffer* combinedNormalsBuffer = new gpu::Buffer(combinedNormalSize, combinedNormalData.get());
gpu::BufferPointer combinedNormalsBufferPointer(combinedNormalsBuffer); gpu::BufferPointer combinedNormalsBufferPointer(combinedNormalsBuffer);
gpu::BufferView combinedNormalsBufferView(combinedNormalsBufferPointer, normalElement); gpu::BufferView combinedNormalsBufferView(combinedNormalsBufferPointer, normalElement);
result->addAttribute(attributeTypeNormal, combinedNormalsBufferView); result->addAttribute(attributeTypeNormal, combinedNormalsBufferView);
gpu::Element indexElement = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW); gpu::Element indexElement = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW);
gpu::Buffer* combinedIndexesBuffer = new gpu::Buffer(combinedIndexSize, combinedIndexData); gpu::Buffer* combinedIndexesBuffer = new gpu::Buffer(combinedIndexSize, combinedIndexData.get());
gpu::BufferPointer combinedIndexesBufferPointer(combinedIndexesBuffer); gpu::BufferPointer combinedIndexesBufferPointer(combinedIndexesBuffer);
gpu::BufferView combinedIndexesBufferView(combinedIndexesBufferPointer, indexElement); gpu::BufferView combinedIndexesBufferView(combinedIndexesBufferPointer, indexElement);
result->setIndexBuffer(combinedIndexesBufferView); result->setIndexBuffer(combinedIndexesBufferView);
@ -152,9 +152,10 @@ QScriptValue ModelScriptingInterface::transformMesh(glm::mat4 transform, MeshPro
return QScriptValue(false); return QScriptValue(false);
} }
const auto inverseTransposeTransform = glm::inverse(glm::transpose(transform));
model::MeshPointer result = mesh->map([&](glm::vec3 position){ return glm::vec3(transform * glm::vec4(position, 1.0f)); }, model::MeshPointer result = mesh->map([&](glm::vec3 position){ return glm::vec3(transform * glm::vec4(position, 1.0f)); },
[&](glm::vec3 color){ return color; }, [&](glm::vec3 color){ return color; },
[&](glm::vec3 normal){ return glm::vec3(transform * glm::vec4(normal, 0.0f)); }, [&](glm::vec3 normal){ return glm::vec3(inverseTransposeTransform * glm::vec4(normal, 0.0f)); },
[&](uint32_t index){ return index; }); [&](uint32_t index){ return index; });
MeshProxy* resultProxy = new SimpleMeshProxy(result); MeshProxy* resultProxy = new SimpleMeshProxy(result);
return meshToScriptValue(_modelScriptEngine, resultProxy); return meshToScriptValue(_modelScriptEngine, resultProxy);