mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-16 22:30:42 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into stageRemoval
This commit is contained in:
commit
9f25b01b10
44 changed files with 776 additions and 279 deletions
|
@ -36,6 +36,7 @@ Rectangle {
|
|||
property bool pendingInventoryReply: true;
|
||||
property bool isShowingMyItems: false;
|
||||
property bool isDebuggingFirstUseTutorial: false;
|
||||
property int pendingItemCount: 0;
|
||||
// Style
|
||||
color: hifi.colors.white;
|
||||
Connections {
|
||||
|
@ -79,18 +80,22 @@ Rectangle {
|
|||
onInventoryResult: {
|
||||
purchasesReceived = true;
|
||||
|
||||
if (root.pendingInventoryReply) {
|
||||
inventoryTimer.start();
|
||||
}
|
||||
|
||||
if (result.status !== 'success') {
|
||||
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 currentIndex = purchasesContentsList.currentIndex === -1 ? 0 : purchasesContentsList.currentIndex;
|
||||
purchasesModel.clear();
|
||||
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) {
|
||||
checkIfAnyItemStatusChanged();
|
||||
} else {
|
||||
|
@ -103,6 +108,12 @@ Rectangle {
|
|||
previousPurchasesModel.append(inventoryResult);
|
||||
|
||||
buildFilteredPurchasesModel();
|
||||
|
||||
purchasesContentsList.positionViewAtIndex(currentIndex, ListView.Beginning);
|
||||
}
|
||||
|
||||
if (root.pendingInventoryReply && root.pendingItemCount > 0) {
|
||||
inventoryTimer.start();
|
||||
}
|
||||
|
||||
root.pendingInventoryReply = false;
|
||||
|
@ -419,6 +430,8 @@ Rectangle {
|
|||
visible: (root.isShowingMyItems && filteredPurchasesModel.count !== 0) || (!root.isShowingMyItems && filteredPurchasesModel.count !== 0);
|
||||
clip: true;
|
||||
model: filteredPurchasesModel;
|
||||
snapMode: ListView.SnapToItem;
|
||||
highlightRangeMode: ListView.StrictlyEnforceRange;
|
||||
// Anchors
|
||||
anchors.top: root.canRezCertifiedItems ? separator.bottom : cantRezCertified.bottom;
|
||||
anchors.topMargin: 12;
|
||||
|
|
|
@ -25,8 +25,12 @@ Item {
|
|||
HifiConstants { id: hifi; }
|
||||
|
||||
id: root;
|
||||
property bool historyReceived: false;
|
||||
property bool initialHistoryReceived: false;
|
||||
property bool historyRequestPending: true;
|
||||
property bool noMoreHistoryData: false;
|
||||
property int pendingCount: 0;
|
||||
property int currentHistoryPage: 1;
|
||||
property var pagesAlreadyAdded: new Array();
|
||||
|
||||
Connections {
|
||||
target: Commerce;
|
||||
|
@ -36,32 +40,86 @@ Item {
|
|||
}
|
||||
|
||||
onHistoryResult : {
|
||||
historyReceived = true;
|
||||
if (result.status === 'success') {
|
||||
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++;
|
||||
}
|
||||
}
|
||||
root.initialHistoryReceived = true;
|
||||
root.historyRequestPending = false;
|
||||
|
||||
if (sameItemCount !== tempTransactionHistoryModel.count) {
|
||||
transactionHistoryModel.clear();
|
||||
if (result.status === 'success') {
|
||||
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++) {
|
||||
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: {
|
||||
if (visible) {
|
||||
historyReceived = false;
|
||||
transactionHistoryModel.clear();
|
||||
Commerce.balance();
|
||||
Commerce.history();
|
||||
initialHistoryReceived = false;
|
||||
root.currentHistoryPage = 1;
|
||||
root.noMoreHistoryData = false;
|
||||
root.historyRequestPending = true;
|
||||
Commerce.history(root.currentHistoryPage);
|
||||
} else {
|
||||
refreshTimer.stop();
|
||||
}
|
||||
|
@ -164,9 +226,12 @@ Item {
|
|||
id: refreshTimer;
|
||||
interval: 4000;
|
||||
onTriggered: {
|
||||
console.log("Refreshing Wallet Home...");
|
||||
Commerce.balance();
|
||||
Commerce.history();
|
||||
if (transactionHistory.atYBeginning) {
|
||||
console.log("Refreshing 1st Page of Recent Activity...");
|
||||
root.historyRequestPending = true;
|
||||
Commerce.balance();
|
||||
Commerce.history(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,7 +306,7 @@ Item {
|
|||
anchors.right: parent.right;
|
||||
|
||||
Item {
|
||||
visible: transactionHistoryModel.count === 0 && root.historyReceived;
|
||||
visible: transactionHistoryModel.count === 0 && root.initialHistoryReceived;
|
||||
anchors.centerIn: parent;
|
||||
width: parent.width - 12;
|
||||
height: parent.height;
|
||||
|
@ -364,7 +429,12 @@ Item {
|
|||
onAtYEndChanged: {
|
||||
if (transactionHistory.atYEnd) {
|
||||
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...");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1851,6 +1851,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
DependencyManager::get<EntityTreeRenderer>()->setSetPrecisionPickingOperator([&](unsigned int rayPickID, bool value) {
|
||||
DependencyManager::get<PickManager>()->setPrecisionPicking(rayPickID, value);
|
||||
});
|
||||
EntityTreeRenderer::setRenderDebugHullsOperator([] {
|
||||
return Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls);
|
||||
});
|
||||
|
||||
// Preload Tablet sounds
|
||||
DependencyManager::get<TabletScriptingInterface>()->preloadSounds();
|
||||
|
@ -5004,8 +5007,6 @@ void Application::update(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
PerformanceTimer perfTimer("misc");
|
||||
|
||||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
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
|
||||
{
|
||||
PROFILE_RANGE(app, "PickManager");
|
||||
PerformanceTimer perfTimer("pickManager");
|
||||
DependencyManager::get<PickManager>()->update();
|
||||
}
|
||||
|
||||
{
|
||||
PROFILE_RANGE(app, "PointerManager");
|
||||
PerformanceTimer perfTimer("pointerManager");
|
||||
DependencyManager::get<PointerManager>()->update();
|
||||
}
|
||||
|
||||
|
@ -5044,8 +5047,8 @@ void Application::update(float deltaTime) {
|
|||
// Update my voxel servers with my current voxel query...
|
||||
{
|
||||
PROFILE_RANGE_EX(app, "QueryOctree", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||
QMutexLocker viewLocker(&_viewMutex);
|
||||
PerformanceTimer perfTimer("queryOctree");
|
||||
QMutexLocker viewLocker(&_viewMutex);
|
||||
quint64 sinceLastQuery = now - _lastQueriedTime;
|
||||
const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND;
|
||||
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);
|
||||
for (auto& iter : _postUpdateLambdas) {
|
||||
iter.second();
|
||||
|
@ -5095,6 +5100,7 @@ void Application::update(float deltaTime) {
|
|||
}
|
||||
|
||||
editRenderArgs([this](AppRenderArgs& appRenderArgs) {
|
||||
PerformanceTimer perfTimer("editRenderArgs");
|
||||
appRenderArgs._headPose= getHMDSensorPose();
|
||||
|
||||
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
|
||||
getMain3DScene()->enqueueFrame();
|
||||
{ // Game loop is done, mark the end of the frame for the scene transactions and the render loop to take over
|
||||
PerformanceTimer perfTimer("enqueueFrame");
|
||||
getMain3DScene()->enqueueFrame();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::sendAvatarViewFrustum() {
|
||||
|
|
|
@ -50,7 +50,7 @@ float LODManager::getLODIncreaseFPS() {
|
|||
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
|
||||
// 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
|
||||
// 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
|
||||
|
|
|
@ -698,7 +698,7 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowOwned,
|
||||
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
|
||||
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::AskToResetSettings, 0, false);
|
||||
|
|
|
@ -72,11 +72,16 @@ void Ledger::signedSend(const QString& propertyName, const QByteArray& text, con
|
|||
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>();
|
||||
QJsonObject request;
|
||||
request["public_keys"] = QJsonArray::fromStringList(wallet->listPublicKeys());
|
||||
send(endpoint, success, fail, QNetworkAccessManager::PostOperation, AccountManagerAuth::Required, request);
|
||||
requestParams["public_keys"] = QJsonArray::fromStringList(wallet->listPublicKeys());
|
||||
|
||||
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) {
|
||||
|
@ -169,6 +174,7 @@ void Ledger::historySuccess(QNetworkReply& reply) {
|
|||
QJsonObject newDataData;
|
||||
newDataData["history"] = newHistoryArray;
|
||||
newData["data"] = newDataData;
|
||||
newData["current_page"] = data["current_page"].toInt();
|
||||
emit historyResult(newData);
|
||||
}
|
||||
|
||||
|
@ -176,8 +182,11 @@ void Ledger::historyFailure(QNetworkReply& reply) {
|
|||
failResponse("history", reply);
|
||||
}
|
||||
|
||||
void Ledger::history(const QStringList& keys) {
|
||||
keysQuery("history", "historySuccess", "historyFailure");
|
||||
void Ledger::history(const QStringList& keys, const int& pageNumber) {
|
||||
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.
|
||||
|
|
|
@ -29,7 +29,7 @@ public:
|
|||
bool receiveAt(const QString& hfc_key, const QString& old_key);
|
||||
void balance(const QStringList& keys);
|
||||
void inventory(const QStringList& keys);
|
||||
void history(const QStringList& keys);
|
||||
void history(const QStringList& keys, const int& pageNumber);
|
||||
void account();
|
||||
void reset();
|
||||
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 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 keysQuery(const QString& endpoint, const QString& success, const QString& fail, QJsonObject& extraRequestParams);
|
||||
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);
|
||||
};
|
||||
|
|
|
@ -96,12 +96,12 @@ void QmlCommerce::inventory() {
|
|||
}
|
||||
}
|
||||
|
||||
void QmlCommerce::history() {
|
||||
void QmlCommerce::history(const int& pageNumber) {
|
||||
auto ledger = DependencyManager::get<Ledger>();
|
||||
auto wallet = DependencyManager::get<Wallet>();
|
||||
QStringList cachedPublicKeys = wallet->listPublicKeys();
|
||||
if (!cachedPublicKeys.isEmpty()) {
|
||||
ledger->history(cachedPublicKeys);
|
||||
ledger->history(cachedPublicKeys, pageNumber);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ protected:
|
|||
Q_INVOKABLE void buy(const QString& assetId, int cost, const bool controlledFailure = false);
|
||||
Q_INVOKABLE void balance();
|
||||
Q_INVOKABLE void inventory();
|
||||
Q_INVOKABLE void history();
|
||||
Q_INVOKABLE void history(const int& pageNumber);
|
||||
Q_INVOKABLE void generateKeyPair();
|
||||
Q_INVOKABLE void reset();
|
||||
Q_INVOKABLE void resetLocalWalletOnly();
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
|
||||
size_t std::hash<EntityItemID>::operator()(const EntityItemID& id) const { return qHash(id); }
|
||||
std::function<bool()> EntityTreeRenderer::_entitiesShouldFadeFunction;
|
||||
std::function<bool()> EntityTreeRenderer::_renderDebugHullsOperator = [] { return false; };
|
||||
|
||||
EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
|
||||
AbstractScriptingServicesInterface* scriptingServices) :
|
||||
|
|
|
@ -116,10 +116,14 @@ public:
|
|||
EntityItemPointer getEntity(const EntityItemID& id);
|
||||
void onEntityChanged(const EntityItemID& id);
|
||||
|
||||
static void setRenderDebugHullsOperator(std::function<bool()> renderDebugHullsOperator) { _renderDebugHullsOperator = renderDebugHullsOperator; }
|
||||
static bool shouldRenderDebugHulls() { return _renderDebugHullsOperator(); }
|
||||
|
||||
signals:
|
||||
void enterEntity(const EntityItemID& entityItemID);
|
||||
void leaveEntity(const EntityItemID& entityItemID);
|
||||
void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
|
||||
void setRenderDebugHulls();
|
||||
|
||||
public slots:
|
||||
void addingEntity(const EntityItemID& entityID);
|
||||
|
@ -255,6 +259,8 @@ private:
|
|||
static int _entitiesScriptEngineCount;
|
||||
static CalculateEntityLoadingPriority _calculateEntityLoadingPriorityFunc;
|
||||
static std::function<bool()> _entitiesShouldFadeFunction;
|
||||
|
||||
static std::function<bool()> _renderDebugHullsOperator;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -141,6 +141,10 @@ std::shared_ptr<T> make_renderer(const EntityItemPointer& entity) {
|
|||
}
|
||||
|
||||
EntityRenderer::EntityRenderer(const EntityItemPointer& entity) : _entity(entity) {
|
||||
connect(entity.get(), &EntityItem::requestRenderUpdate, this, [&] {
|
||||
_needsRenderUpdate = true;
|
||||
emit requestRenderUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
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
|
||||
// changes (animation, fading, etc)
|
||||
bool EntityRenderer::needsRenderUpdate() const {
|
||||
if (_needsRenderUpdate) {
|
||||
return true;
|
||||
}
|
||||
if (_prevIsTransparent != isTransparent()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -331,10 +338,6 @@ bool EntityRenderer::needsRenderUpdateFromEntity(const EntityItemPointer& entity
|
|||
return true;
|
||||
}
|
||||
|
||||
if (_visible != entity->getVisible()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_moving != entity->isMovingRelativeToParent()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -363,6 +366,7 @@ void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transa
|
|||
|
||||
_moving = entity->isMovingRelativeToParent();
|
||||
_visible = entity->getVisible();
|
||||
_needsRenderUpdate = false;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -125,6 +125,7 @@ protected:
|
|||
bool _prevIsTransparent { false };
|
||||
bool _visible { false };
|
||||
bool _moving { false };
|
||||
bool _needsRenderUpdate { false };
|
||||
// Only touched on the rendering thread
|
||||
bool _renderUpdateQueued{ false };
|
||||
|
||||
|
|
|
@ -954,8 +954,23 @@ void RenderableModelEntityItem::copyAnimationJointDataToModel() {
|
|||
using namespace render;
|
||||
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() {
|
||||
return ItemKey::Builder().withTypeMeta().withTypeShape();
|
||||
return _itemKey;
|
||||
}
|
||||
|
||||
uint32_t ModelEntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) {
|
||||
|
@ -1209,7 +1224,10 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
// Check for addition
|
||||
if (_hasModel && !(bool)_model) {
|
||||
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(entity.get(), &RenderableModelEntityItem::requestCollisionGeometryUpdate, this, &ModelEntityRenderer::flagForCollisionGeometryUpdate);
|
||||
model->setLoadingPriority(EntityTreeRenderer::getEntityLoadingPriority(*entity));
|
||||
|
@ -1275,7 +1293,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
setCollisionMeshKey(entity->getCollisionMeshKey());
|
||||
_needsCollisionGeometryUpdate = false;
|
||||
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
|
||||
model::MeshPointer mesh = collisionMeshCache.getMesh(_collisionMeshKey);
|
||||
// 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_PERFORMANCE_TIMER("RMEIrender");
|
||||
|
||||
ModelPointer model;
|
||||
withReadLock([&]{
|
||||
model = _model;
|
||||
});
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// If the model doesn't have visual geometry, render our bounding box as green wireframe
|
||||
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);
|
||||
|
||||
// Enqueue updates for the next frame
|
||||
#if WANT_EXTRA_DEBUGGING
|
||||
|
@ -1366,12 +1375,6 @@ void ModelEntityRenderer::doRender(RenderArgs* args) {
|
|||
|
||||
// Remap textures for the next frame to avoid flicker
|
||||
// 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) {
|
||||
|
|
|
@ -133,12 +133,13 @@ class ModelEntityRenderer : public TypedEntityRenderer<RenderableModelEntityItem
|
|||
friend class EntityRenderer;
|
||||
|
||||
public:
|
||||
ModelEntityRenderer(const EntityItemPointer& entity) : Parent(entity) {}
|
||||
ModelEntityRenderer(const EntityItemPointer& entity);
|
||||
|
||||
protected:
|
||||
virtual void removeFromScene(const ScenePointer& scene, Transaction& transaction) override;
|
||||
virtual void onRemoveFromSceneTyped(const TypedEntityPointer& entity) override;
|
||||
|
||||
void setKey(bool didVisualGeometryRequestSucceed);
|
||||
virtual ItemKey getKey() override;
|
||||
virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) override;
|
||||
|
||||
|
@ -169,10 +170,9 @@ private:
|
|||
bool _hasTransitioned{ false };
|
||||
#endif
|
||||
|
||||
bool _needsJointSimulation{ false };
|
||||
bool _showCollisionGeometry{ false };
|
||||
bool _needsCollisionGeometryUpdate{ false };
|
||||
const void* _collisionMeshKey{ nullptr };
|
||||
bool _needsJointSimulation { false };
|
||||
bool _needsCollisionGeometryUpdate { false };
|
||||
const void* _collisionMeshKey { nullptr };
|
||||
|
||||
// used on client side
|
||||
bool _jointMappingCompleted{ false };
|
||||
|
@ -185,6 +185,8 @@ private:
|
|||
bool _shouldHighlight { false };
|
||||
bool _animating { false };
|
||||
uint64_t _lastAnimated { 0 };
|
||||
|
||||
render::ItemKey _itemKey { render::ItemKey::Builder().withTypeMeta() };
|
||||
};
|
||||
|
||||
} } // namespace
|
||||
|
|
|
@ -1422,27 +1422,29 @@ bool RenderablePolyVoxEntityItem::getMeshes(MeshProxyList& result) {
|
|||
}
|
||||
|
||||
bool success = false;
|
||||
MeshProxy* meshProxy = nullptr;
|
||||
glm::mat4 transform = voxelToLocalMatrix();
|
||||
withReadLock([&] {
|
||||
gpu::BufferView::Index numVertices = (gpu::BufferView::Index)_mesh->getNumVertices();
|
||||
if (!_meshReady) {
|
||||
// we aren't ready to return a mesh. the caller will have to try again later.
|
||||
success = false;
|
||||
} else if (numVertices == 0) {
|
||||
// we are ready, but there are no triangles in the mesh.
|
||||
success = true;
|
||||
} else {
|
||||
success = true;
|
||||
// the mesh will be in voxel-space. transform it into object-space
|
||||
meshProxy = new SimpleMeshProxy(
|
||||
_mesh->map([=](glm::vec3 position){ return glm::vec3(transform * glm::vec4(position, 1.0f)); },
|
||||
[=](glm::vec3 color){ return color; },
|
||||
[=](glm::vec3 normal){ return glm::normalize(glm::vec3(transform * glm::vec4(normal, 0.0f))); },
|
||||
[&](uint32_t index){ return index; }));
|
||||
result << meshProxy;
|
||||
}
|
||||
});
|
||||
if (_mesh) {
|
||||
MeshProxy* meshProxy = nullptr;
|
||||
glm::mat4 transform = voxelToLocalMatrix();
|
||||
withReadLock([&] {
|
||||
gpu::BufferView::Index numVertices = (gpu::BufferView::Index)_mesh->getNumVertices();
|
||||
if (!_meshReady) {
|
||||
// we aren't ready to return a mesh. the caller will have to try again later.
|
||||
success = false;
|
||||
} else if (numVertices == 0) {
|
||||
// we are ready, but there are no triangles in the mesh.
|
||||
success = true;
|
||||
} else {
|
||||
success = true;
|
||||
// the mesh will be in voxel-space. transform it into object-space
|
||||
meshProxy = new SimpleMeshProxy(
|
||||
_mesh->map([=](glm::vec3 position) { return glm::vec3(transform * glm::vec4(position, 1.0f)); },
|
||||
[=](glm::vec3 color) { return color; },
|
||||
[=](glm::vec3 normal) { return glm::normalize(glm::vec3(transform * glm::vec4(normal, 0.0f))); },
|
||||
[&](uint32_t index) { return index; }));
|
||||
result << meshProxy;
|
||||
}
|
||||
});
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
|
|
@ -413,7 +413,6 @@ void ZoneEntityRenderer::setAmbientURL(const QString& ambientUrl) {
|
|||
_ambientTextureURL = ambientUrl;
|
||||
|
||||
if (_ambientTextureURL.isEmpty()) {
|
||||
_validAmbientTexture = false;
|
||||
_pendingAmbientTexture = false;
|
||||
_ambientTexture.clear();
|
||||
|
||||
|
@ -441,7 +440,6 @@ void ZoneEntityRenderer::updateAmbientMap() {
|
|||
_ambientLight->setAmbientSpherePreset(gpu::SphericalHarmonics::BREEZEWAY);
|
||||
}
|
||||
editAmbientLight()->setAmbientMap(texture);
|
||||
_validAmbientTexture = true;
|
||||
} else {
|
||||
qCDebug(entitiesrenderer) << "Failed to load ambient texture:" << _ambientTexture->getURL();
|
||||
}
|
||||
|
@ -457,7 +455,6 @@ void ZoneEntityRenderer::setSkyboxURL(const QString& skyboxUrl) {
|
|||
_skyboxTextureURL = skyboxUrl;
|
||||
|
||||
if (_skyboxTextureURL.isEmpty()) {
|
||||
_validSkyboxTexture = false;
|
||||
_pendingSkyboxTexture = false;
|
||||
_skyboxTexture.clear();
|
||||
|
||||
|
@ -477,7 +474,6 @@ void ZoneEntityRenderer::updateSkyboxMap() {
|
|||
auto texture = _skyboxTexture->getGPUTexture();
|
||||
if (texture) {
|
||||
editSkybox()->setCubemap(texture);
|
||||
_validSkyboxTexture = true;
|
||||
} else {
|
||||
qCDebug(entitiesrenderer) << "Failed to load Skybox texture:" << _skyboxTexture->getURL();
|
||||
}
|
||||
|
|
|
@ -118,12 +118,10 @@ private:
|
|||
QString _ambientTextureURL;
|
||||
NetworkTexturePointer _ambientTexture;
|
||||
bool _pendingAmbientTexture{ false };
|
||||
bool _validAmbientTexture{ false };
|
||||
|
||||
QString _skyboxTextureURL;
|
||||
NetworkTexturePointer _skyboxTexture;
|
||||
bool _pendingSkyboxTexture{ false };
|
||||
bool _validSkyboxTexture{ false };
|
||||
|
||||
QString _proceduralUserData;
|
||||
Transform _renderTransform;
|
||||
|
|
|
@ -2715,9 +2715,17 @@ bool EntityItem::getVisible() const {
|
|||
}
|
||||
|
||||
void EntityItem::setVisible(bool value) {
|
||||
bool changed = false;
|
||||
withWriteLock([&] {
|
||||
_visible = value;
|
||||
if (_visible != value) {
|
||||
changed = true;
|
||||
_visible = value;
|
||||
}
|
||||
});
|
||||
|
||||
if (changed) {
|
||||
emit requestRenderUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
bool EntityItem::isChildOfMyAvatar() const {
|
||||
|
|
|
@ -469,6 +469,9 @@ public:
|
|||
static QString _marketplacePublicKey;
|
||||
static void retrieveMarketplacePublicKey();
|
||||
|
||||
signals:
|
||||
void requestRenderUpdate();
|
||||
|
||||
protected:
|
||||
QHash<ChangeHandlerId, ChangeHandlerCallback> _changeHandlers;
|
||||
|
||||
|
|
|
@ -2291,8 +2291,13 @@ bool EntityTree::readFromMap(QVariantMap& map) {
|
|||
if (entityMap.contains("backgroundMode") && (entityMap["backgroundMode"].toString() == "nothing")) {
|
||||
properties.setSkyboxMode(COMPONENT_MODE_INHERIT);
|
||||
} 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);
|
||||
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ bool KeyLightPropertyGroup::appendToEditPacket(OctreePacketData* packetData,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool KeyLightPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt,
|
||||
bool KeyLightPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt,
|
||||
int& processedBytes) {
|
||||
|
||||
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_INTENSITY, float, setIntensity);
|
||||
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_INTENSITY, Intensity);
|
||||
|
|
|
@ -345,7 +345,7 @@ void ZoneEntityItem::resetRenderingPropertiesChanged() {
|
|||
}
|
||||
|
||||
void ZoneEntityItem::setHazeMode(const uint32_t value) {
|
||||
if (value < COMPONENT_MODE_ITEM_COUNT) {
|
||||
if (value < COMPONENT_MODE_ITEM_COUNT && value != _hazeMode) {
|
||||
_hazeMode = value;
|
||||
_hazePropertiesChanged = true;
|
||||
}
|
||||
|
@ -356,7 +356,7 @@ uint32_t ZoneEntityItem::getHazeMode() const {
|
|||
}
|
||||
|
||||
void ZoneEntityItem::setKeyLightMode(const uint32_t value) {
|
||||
if (value < COMPONENT_MODE_ITEM_COUNT) {
|
||||
if (value < COMPONENT_MODE_ITEM_COUNT && value != _keyLightMode) {
|
||||
_keyLightMode = value;
|
||||
_keyLightPropertiesChanged = true;
|
||||
}
|
||||
|
@ -367,7 +367,7 @@ uint32_t ZoneEntityItem::getKeyLightMode() const {
|
|||
}
|
||||
|
||||
void ZoneEntityItem::setAmbientLightMode(const uint32_t value) {
|
||||
if (value < COMPONENT_MODE_ITEM_COUNT) {
|
||||
if (value < COMPONENT_MODE_ITEM_COUNT && value != _ambientLightMode) {
|
||||
_ambientLightMode = value;
|
||||
_ambientLightPropertiesChanged = true;
|
||||
}
|
||||
|
@ -378,7 +378,7 @@ uint32_t ZoneEntityItem::getAmbientLightMode() const {
|
|||
}
|
||||
|
||||
void ZoneEntityItem::setSkyboxMode(const uint32_t value) {
|
||||
if (value < COMPONENT_MODE_ITEM_COUNT) {
|
||||
if (value < COMPONENT_MODE_ITEM_COUNT && value != _skyboxMode) {
|
||||
_skyboxMode = value;
|
||||
_skyboxPropertiesChanged = true;
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@ public:
|
|||
QVector<int> indices;
|
||||
QVector<glm::vec3> vertices;
|
||||
QVector<glm::vec3> normals;
|
||||
QVector<glm::vec3> tangents;
|
||||
};
|
||||
|
||||
struct FBXJointShapeInfo {
|
||||
|
|
|
@ -303,17 +303,97 @@ FBXBlendshape extractBlendshape(const FBXNode& object) {
|
|||
return blendshape;
|
||||
}
|
||||
|
||||
using IndexAccessor = std::function<glm::vec3*(const FBXMesh&, int, int, glm::vec3*, glm::vec3&)>;
|
||||
|
||||
void setTangents(FBXMesh& mesh, int firstIndex, int secondIndex) {
|
||||
const glm::vec3& normal = mesh.normals.at(firstIndex);
|
||||
glm::vec3 bitangent = glm::cross(normal, mesh.vertices.at(secondIndex) - mesh.vertices.at(firstIndex));
|
||||
if (glm::length(bitangent) < EPSILON) {
|
||||
return;
|
||||
static void setTangents(const FBXMesh& mesh, const IndexAccessor& vertexAccessor, int firstIndex, int secondIndex,
|
||||
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals, QVector<glm::vec3>& tangents) {
|
||||
glm::vec3 vertex[2];
|
||||
glm::vec3 normal;
|
||||
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) *
|
||||
glm::normalize(bitangent), normalizedNormal);
|
||||
}
|
||||
|
||||
static void createTangents(const FBXMesh& mesh, bool generateFromTexCoords,
|
||||
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) {
|
||||
|
@ -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
|
||||
if (generateTangents && !extracted.mesh.texCoords.isEmpty()) {
|
||||
extracted.mesh.tangents.resize(extracted.mesh.vertices.size());
|
||||
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 ";
|
||||
}
|
||||
}
|
||||
createMeshTangents(extracted.mesh, generateTangents);
|
||||
for (auto& blendShape : extracted.mesh.blendshapes) {
|
||||
createBlendShapeTangents(extracted.mesh, generateTangents, blendShape);
|
||||
}
|
||||
|
||||
// find the clusters with which the mesh is associated
|
||||
|
|
|
@ -33,6 +33,15 @@
|
|||
class QIODevice;
|
||||
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.
|
||||
/// \exception QString if an error occurs in parsing
|
||||
|
@ -114,6 +123,8 @@ public:
|
|||
QHash<QString, ExtractedMesh> meshes;
|
||||
static void buildModelMesh(FBXMesh& extractedMesh, const QString& url);
|
||||
|
||||
static glm::vec3 normalizeDirForPacking(const glm::vec3& dir);
|
||||
|
||||
FBXTexture getTexture(const QString& textureID);
|
||||
|
||||
QHash<QString, QString> _textureNames;
|
||||
|
|
|
@ -37,6 +37,20 @@
|
|||
|
||||
#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 {
|
||||
public:
|
||||
|
@ -225,7 +239,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn
|
|||
foreach (const FBXNode& subdata, child.children) {
|
||||
if (subdata.name == "Colors") {
|
||||
data.colors = createVec4VectorRGBA(getDoubleVector(subdata), data.averageColor);
|
||||
} else if (subdata.name == "ColorsIndex") {
|
||||
} else if (subdata.name == "ColorsIndex" || subdata.name == "ColorIndex") {
|
||||
data.colorIndices = getIntVector(subdata);
|
||||
|
||||
} 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;
|
||||
}
|
||||
|
||||
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) {
|
||||
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));
|
||||
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
|
||||
int normalsSize = fbxMesh.normals.size() * sizeof(glm::vec3);
|
||||
int tangentsSize = fbxMesh.tangents.size() * sizeof(glm::vec3);
|
||||
int colorsSize = fbxMesh.colors.size() * sizeof(glm::vec3);
|
||||
int texCoordsSize = fbxMesh.texCoords.size() * sizeof(glm::vec2);
|
||||
int texCoords1Size = fbxMesh.texCoords1.size() * sizeof(glm::vec2);
|
||||
const int normalsSize = fbxMesh.normals.size() * sizeof(NormalType);
|
||||
const int tangentsSize = fbxMesh.tangents.size() * sizeof(NormalType);
|
||||
// If there are normals then there should be tangents
|
||||
assert(normalsSize == tangentsSize);
|
||||
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);
|
||||
if (fbxMesh.clusters.size() > UINT8_MAX) {
|
||||
// we need 16 bits instead of just 8 for clusterIndices
|
||||
clusterIndicesSize *= 2;
|
||||
}
|
||||
int clusterWeightsSize = fbxMesh.clusterWeights.size() * sizeof(uint8_t);
|
||||
const int clusterWeightsSize = fbxMesh.clusterWeights.size() * sizeof(uint8_t);
|
||||
|
||||
int normalsOffset = 0;
|
||||
int tangentsOffset = normalsOffset + normalsSize;
|
||||
int colorsOffset = tangentsOffset + tangentsSize;
|
||||
int texCoordsOffset = colorsOffset + colorsSize;
|
||||
int texCoords1Offset = texCoordsOffset + texCoordsSize;
|
||||
int clusterIndicesOffset = texCoords1Offset + texCoords1Size;
|
||||
int clusterWeightsOffset = clusterIndicesOffset + clusterIndicesSize;
|
||||
int totalAttributeSize = clusterWeightsOffset + clusterWeightsSize;
|
||||
// Normals and tangents are interleaved
|
||||
const int normalsOffset = 0;
|
||||
const int tangentsOffset = normalsOffset + sizeof(NormalType);
|
||||
const int colorsOffset = normalsOffset + normalsSize + tangentsSize;
|
||||
const int texCoordsOffset = colorsOffset + colorsSize;
|
||||
const int texCoords1Offset = texCoordsOffset + texCoordsSize;
|
||||
const int clusterIndicesOffset = texCoords1Offset + texCoords1Size;
|
||||
const int clusterWeightsOffset = clusterIndicesOffset + clusterIndicesSize;
|
||||
const int totalAttributeSize = clusterWeightsOffset + clusterWeightsSize;
|
||||
|
||||
// Copy all attribute data in a single attribute buffer
|
||||
auto attribBuffer = std::make_shared<gpu::Buffer>();
|
||||
attribBuffer->resize(totalAttributeSize);
|
||||
attribBuffer->setSubData(normalsOffset, normalsSize, (gpu::Byte*) fbxMesh.normals.constData());
|
||||
attribBuffer->setSubData(tangentsOffset, tangentsSize, (gpu::Byte*) fbxMesh.tangents.constData());
|
||||
attribBuffer->setSubData(colorsOffset, colorsSize, (gpu::Byte*) fbxMesh.colors.constData());
|
||||
attribBuffer->setSubData(texCoordsOffset, texCoordsSize, (gpu::Byte*) fbxMesh.texCoords.constData());
|
||||
attribBuffer->setSubData(texCoords1Offset, texCoords1Size, (gpu::Byte*) fbxMesh.texCoords1.constData());
|
||||
|
||||
// Interleave normals and tangents
|
||||
if (normalsSize > 0) {
|
||||
std::vector<NormalType> normalsAndTangents;
|
||||
|
||||
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) {
|
||||
// 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);
|
||||
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 {
|
||||
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) {
|
||||
mesh->addAttribute(gpu::Stream::NORMAL,
|
||||
model::BufferView(attribBuffer, normalsOffset, normalsSize,
|
||||
gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)));
|
||||
}
|
||||
if (tangentsSize) {
|
||||
model::BufferView(attribBuffer, normalsOffset, normalsAndTangentsSize,
|
||||
normalsAndTangentsStride, FBX_NORMAL_ELEMENT));
|
||||
mesh->addAttribute(gpu::Stream::TANGENT,
|
||||
model::BufferView(attribBuffer, tangentsOffset, tangentsSize,
|
||||
gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)));
|
||||
model::BufferView(attribBuffer, tangentsOffset, normalsAndTangentsSize,
|
||||
normalsAndTangentsStride, FBX_NORMAL_ELEMENT));
|
||||
}
|
||||
if (colorsSize) {
|
||||
mesh->addAttribute(gpu::Stream::COLOR,
|
||||
model::BufferView(attribBuffer, colorsOffset, colorsSize,
|
||||
gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB)));
|
||||
model::BufferView(attribBuffer, colorsOffset, colorsSize, FBX_COLOR_ELEMENT));
|
||||
}
|
||||
if (texCoordsSize) {
|
||||
mesh->addAttribute(gpu::Stream::TEXCOORD,
|
||||
model::BufferView( attribBuffer, texCoordsOffset, texCoordsSize,
|
||||
gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)));
|
||||
model::BufferView( attribBuffer, texCoordsOffset, texCoordsSize,
|
||||
gpu::Element(gpu::VEC2, gpu::HALF, gpu::UV)));
|
||||
}
|
||||
if (texCoords1Size) {
|
||||
mesh->addAttribute( gpu::Stream::TEXCOORD1,
|
||||
model::BufferView(attribBuffer, texCoords1Offset, texCoords1Size,
|
||||
gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)));
|
||||
gpu::Element(gpu::VEC2, gpu::HALF, gpu::UV)));
|
||||
} else if (texCoordsSize) {
|
||||
mesh->addAttribute(gpu::Stream::TEXCOORD1,
|
||||
model::BufferView(attribBuffer, texCoordsOffset, texCoordsSize,
|
||||
gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV)));
|
||||
model::BufferView(attribBuffer, texCoordsOffset, texCoordsSize,
|
||||
gpu::Element(gpu::VEC2, gpu::HALF, gpu::UV)));
|
||||
}
|
||||
|
||||
if (clusterIndicesSize) {
|
||||
|
|
|
@ -110,7 +110,8 @@ static const GLenum ELEMENT_TYPE_TO_GL[gpu::NUM_TYPES] = {
|
|||
GL_UNSIGNED_SHORT,
|
||||
GL_BYTE,
|
||||
GL_UNSIGNED_BYTE,
|
||||
GL_UNSIGNED_BYTE
|
||||
GL_UNSIGNED_BYTE,
|
||||
GL_INT_2_10_10_10_REV,
|
||||
};
|
||||
|
||||
bool checkGLError(const char* name = nullptr);
|
||||
|
|
|
@ -218,6 +218,7 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) {
|
|||
case gpu::NINT8:
|
||||
result = GL_RGBA8_SNORM;
|
||||
break;
|
||||
case gpu::NINT2_10_10_10:
|
||||
case gpu::NUINT32:
|
||||
case gpu::NINT32:
|
||||
case gpu::COMPRESSED:
|
||||
|
@ -502,6 +503,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
|
|||
}
|
||||
case gpu::COMPRESSED:
|
||||
case gpu::NUINT2:
|
||||
case gpu::NINT2_10_10_10:
|
||||
case gpu::NUM_TYPES: { // quiet compiler
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
@ -553,6 +555,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
|
|||
}
|
||||
case gpu::COMPRESSED:
|
||||
case gpu::NUINT2:
|
||||
case gpu::NINT2_10_10_10:
|
||||
case gpu::NUM_TYPES: { // quiet compiler
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
@ -671,6 +674,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E
|
|||
break;
|
||||
case gpu::NUINT32:
|
||||
case gpu::NINT32:
|
||||
case gpu::NINT2_10_10_10:
|
||||
case gpu::COMPRESSED:
|
||||
case gpu::NUM_TYPES: // quiet compiler
|
||||
Q_UNREACHABLE();
|
||||
|
|
|
@ -38,6 +38,7 @@ const Element Element::VEC2F_UV{ VEC2, FLOAT, UV };
|
|||
const Element Element::VEC2F_XY{ VEC2, FLOAT, XY };
|
||||
const Element Element::VEC3F_XYZ{ VEC3, FLOAT, XYZ };
|
||||
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_INT32 { SCALAR, INT32, INDEX };
|
||||
const Element Element::PART_DRAWCALL{ VEC4, UINT32, PART };
|
||||
|
|
|
@ -39,6 +39,7 @@ enum Type : uint8_t {
|
|||
NINT8,
|
||||
NUINT8,
|
||||
NUINT2,
|
||||
NINT2_10_10_10,
|
||||
|
||||
COMPRESSED,
|
||||
|
||||
|
@ -65,6 +66,7 @@ static const int TYPE_SIZE[NUM_TYPES] = {
|
|||
2,
|
||||
1,
|
||||
1,
|
||||
4,
|
||||
|
||||
1
|
||||
};
|
||||
|
@ -86,6 +88,7 @@ static const bool TYPE_IS_INTEGER[NUM_TYPES] = {
|
|||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
|
||||
false,
|
||||
};
|
||||
|
@ -326,6 +329,7 @@ public:
|
|||
static const Element VEC2F_XY;
|
||||
static const Element VEC3F_XYZ;
|
||||
static const Element VEC4F_XYZW;
|
||||
static const Element VEC4F_NORMALIZED_XYZ10W2;
|
||||
static const Element INDEX_UINT16;
|
||||
static const Element INDEX_INT32;
|
||||
static const Element PART_DRAWCALL;
|
||||
|
|
|
@ -92,6 +92,15 @@ bool Stream::Format::setAttribute(Slot slot, Slot channel, Frequency frequency)
|
|||
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) {
|
||||
_buffers.push_back(buffer);
|
||||
_offsets.push_back(offset);
|
||||
|
|
|
@ -112,6 +112,7 @@ public:
|
|||
bool setAttribute(Slot slot, Slot channel, Frequency frequency = PER_VERTEX);
|
||||
|
||||
bool hasAttribute(Slot slot) const { return (_attributes.find(slot) != _attributes.end()); }
|
||||
Attribute getAttribute(Slot slot) const;
|
||||
|
||||
const std::string& getKey() const { return _key; }
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include "Geometry.h"
|
||||
|
||||
#include <glm/gtc/packing.hpp>
|
||||
|
||||
using namespace model;
|
||||
|
||||
Mesh::Mesh() :
|
||||
|
@ -72,12 +74,14 @@ void Mesh::evalVertexStream() {
|
|||
|
||||
int channelNum = 0;
|
||||
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++;
|
||||
}
|
||||
for (auto attrib : _attributeBuffers) {
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
@ -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)> normalFunc,
|
||||
std::function<uint32_t(uint32_t)> indexFunc) const {
|
||||
const auto vertexFormat = getVertexFormat();
|
||||
|
||||
// vertex data
|
||||
const gpu::BufferView& vertexBufferView = getVertexBuffer();
|
||||
gpu::BufferView::Index numVertices = (gpu::BufferView::Index)getNumVertices();
|
||||
|
||||
gpu::Resource::Size vertexSize = numVertices * sizeof(glm::vec3);
|
||||
unsigned char* resultVertexData = new unsigned char[vertexSize];
|
||||
unsigned char* vertexDataCursor = resultVertexData;
|
||||
std::unique_ptr<unsigned char> resultVertexData{ new unsigned char[vertexSize] };
|
||||
unsigned char* vertexDataCursor = resultVertexData.get();
|
||||
|
||||
for (gpu::BufferView::Index i = 0; i < numVertices; 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::Resource::Size colorSize = numColors * sizeof(glm::vec3);
|
||||
unsigned char* resultColorData = new unsigned char[colorSize];
|
||||
unsigned char* colorDataCursor = resultColorData;
|
||||
std::unique_ptr<unsigned char> resultColorData{ new unsigned char[colorSize] };
|
||||
unsigned char* colorDataCursor = resultColorData.get();
|
||||
auto colorAttribute = vertexFormat->getAttribute(attributeTypeColor);
|
||||
|
||||
for (gpu::BufferView::Index i = 0; i < numColors; i++) {
|
||||
glm::vec3 color = colorFunc(colorsBufferView.get<glm::vec3>(i));
|
||||
memcpy(colorDataCursor, &color, sizeof(color));
|
||||
colorDataCursor += sizeof(color);
|
||||
if (colorAttribute._element == gpu::Element::VEC3F_XYZ) {
|
||||
for (gpu::BufferView::Index i = 0; i < numColors; i++) {
|
||||
glm::vec3 color = colorFunc(colorsBufferView.get<glm::vec3>(i));
|
||||
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
|
||||
|
@ -171,22 +188,34 @@ model::MeshPointer Mesh::map(std::function<glm::vec3(glm::vec3)> vertexFunc,
|
|||
const gpu::BufferView& normalsBufferView = getAttributeBuffer(attributeTypeNormal);
|
||||
gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements();
|
||||
gpu::Resource::Size normalSize = numNormals * sizeof(glm::vec3);
|
||||
unsigned char* resultNormalData = new unsigned char[normalSize];
|
||||
unsigned char* normalDataCursor = resultNormalData;
|
||||
std::unique_ptr<unsigned char> resultNormalData{ new unsigned char[normalSize] };
|
||||
unsigned char* normalDataCursor = resultNormalData.get();
|
||||
auto normalAttribute = vertexFormat->getAttribute(attributeTypeNormal);
|
||||
|
||||
for (gpu::BufferView::Index i = 0; i < numNormals; i++) {
|
||||
glm::vec3 normal = normalFunc(normalsBufferView.get<glm::vec3>(i));
|
||||
memcpy(normalDataCursor, &normal, sizeof(normal));
|
||||
normalDataCursor += sizeof(normal);
|
||||
if (normalAttribute._element == gpu::Element::VEC3F_XYZ) {
|
||||
for (gpu::BufferView::Index i = 0; i < numNormals; i++) {
|
||||
glm::vec3 normal = normalFunc(normalsBufferView.get<glm::vec3>(i));
|
||||
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
|
||||
|
||||
// face data
|
||||
const gpu::BufferView& indexBufferView = getIndexBuffer();
|
||||
gpu::BufferView::Index numIndexes = (gpu::BufferView::Index)getNumIndices();
|
||||
gpu::Resource::Size indexSize = numIndexes * sizeof(uint32_t);
|
||||
unsigned char* resultIndexData = new unsigned char[indexSize];
|
||||
unsigned char* indexDataCursor = resultIndexData;
|
||||
std::unique_ptr<unsigned char> resultIndexData{ new unsigned char[indexSize] };
|
||||
unsigned char* indexDataCursor = resultIndexData.get();
|
||||
|
||||
for (gpu::BufferView::Index i = 0; i < numIndexes; 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());
|
||||
|
||||
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::BufferView resultVertexBufferView(resultVertexBufferPointer, vertexElement);
|
||||
result->setVertexBuffer(resultVertexBufferView);
|
||||
|
||||
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::BufferView resultColorsBufferView(resultColorsBufferPointer, colorElement);
|
||||
result->addAttribute(attributeTypeColor, resultColorsBufferView);
|
||||
|
||||
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::BufferView resultNormalsBufferView(resultNormalsBufferPointer, normalElement);
|
||||
result->addAttribute(attributeTypeNormal, resultNormalsBufferView);
|
||||
|
||||
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::BufferView resultIndexesBufferView(resultIndexesBufferPointer, indexElement);
|
||||
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)> normalFunc,
|
||||
std::function<void(uint32_t)> indexFunc) {
|
||||
const auto vertexFormat = getVertexFormat();
|
||||
|
||||
// vertex data
|
||||
const gpu::BufferView& vertexBufferView = getVertexBuffer();
|
||||
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
|
||||
const gpu::BufferView& colorsBufferView = getAttributeBuffer(attributeTypeColor);
|
||||
gpu::BufferView::Index numColors = (gpu::BufferView::Index)colorsBufferView.getNumElements();
|
||||
for (gpu::BufferView::Index i = 0; i < numColors; i++) {
|
||||
colorFunc(colorsBufferView.get<glm::vec3>(i));
|
||||
auto colorAttribute = vertexFormat->getAttribute(attributeTypeColor);
|
||||
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
|
||||
int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h
|
||||
const gpu::BufferView& normalsBufferView = getAttributeBuffer(attributeTypeNormal);
|
||||
gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements();
|
||||
for (gpu::BufferView::Index i = 0; i < numNormals; i++) {
|
||||
normalFunc(normalsBufferView.get<glm::vec3>(i));
|
||||
auto normalAttribute = vertexFormat->getAttribute(attributeTypeNormal);
|
||||
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
|
||||
|
||||
// face data
|
||||
|
|
|
@ -202,7 +202,7 @@ enum class EntityVersion : PacketVersion {
|
|||
HazeEffect,
|
||||
StaticCertJsonVersionOne,
|
||||
OwnershipChallengeFix,
|
||||
ZoneLightInheritModes
|
||||
ZoneLightInheritModes
|
||||
};
|
||||
|
||||
enum class EntityScriptCallMethodVersion : PacketVersion {
|
||||
|
|
|
@ -763,7 +763,8 @@ void DefaultLightingSetup::run(const RenderContextPointer& renderContext) {
|
|||
_defaultLight = lp;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -67,7 +67,6 @@ public:
|
|||
NetworkTexturePointer _ambientTexture;
|
||||
QString _ambientTextureURL;
|
||||
bool _pendingAmbientTexture { false };
|
||||
bool _validAmbientTextureURL { false };
|
||||
|
||||
protected:
|
||||
model::LightPointer _light;
|
||||
|
|
|
@ -34,28 +34,31 @@ LightStage::LightStage() {
|
|||
ambientOffLight->setColor(model::Vec3(0.0));
|
||||
ambientOffLight->setIntensity(0.0f);
|
||||
ambientOffLight->setType(model::Light::Type::AMBIENT);
|
||||
_ambientOffLight = addLight(ambientOffLight);
|
||||
_ambientOffLightId = addLight(ambientOffLight);
|
||||
|
||||
const LightPointer pointOffLight { std::make_shared<model::Light>() };
|
||||
pointOffLight->setAmbientIntensity(0.0f);
|
||||
pointOffLight->setColor(model::Vec3(0.0));
|
||||
pointOffLight->setIntensity(0.0f);
|
||||
pointOffLight->setType(model::Light::Type::POINT);
|
||||
_pointOffLight = addLight(pointOffLight);
|
||||
_pointOffLightId = addLight(pointOffLight);
|
||||
|
||||
const LightPointer spotOffLight { std::make_shared<model::Light>() };
|
||||
spotOffLight->setAmbientIntensity(0.0f);
|
||||
spotOffLight->setColor(model::Vec3(0.0));
|
||||
spotOffLight->setIntensity(0.0f);
|
||||
spotOffLight->setType(model::Light::Type::SPOT);
|
||||
_spotOffLight = addLight(spotOffLight);
|
||||
_spotOffLightId = addLight(spotOffLight);
|
||||
|
||||
const LightPointer sunOffLight { std::make_shared<model::Light>() };
|
||||
sunOffLight->setAmbientIntensity(0.0f);
|
||||
sunOffLight->setColor(model::Vec3(0.0));
|
||||
sunOffLight->setIntensity(0.0f);
|
||||
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() {
|
||||
|
@ -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);
|
||||
if (found == _lightMap.end()) {
|
||||
auto lightId = _lights.newElement(light);
|
||||
lightId = _lights.newElement(light);
|
||||
// Avoid failing to allocate a light, just pass
|
||||
if (lightId != INVALID_INDEX) {
|
||||
|
||||
|
@ -314,10 +319,15 @@ LightStage::Index LightStage::addLight(const LightPointer& light) {
|
|||
|
||||
updateLightArrayBuffer(lightId);
|
||||
}
|
||||
return lightId;
|
||||
} else {
|
||||
return (*found).second;
|
||||
lightId = (*found).second;
|
||||
}
|
||||
|
||||
if (shouldSetAsDefault) {
|
||||
_defaultLightId = lightId;
|
||||
}
|
||||
|
||||
return lightId;
|
||||
}
|
||||
|
||||
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 {
|
||||
Index keyLightId{ 0 };
|
||||
Index keyLightId{ _defaultLightId };
|
||||
if (!_currentFrame._sunLights.empty()) {
|
||||
keyLightId = _currentFrame._sunLights.front();
|
||||
}
|
||||
|
@ -357,7 +367,7 @@ LightStage::LightPointer LightStage::getCurrentKeyLight() const {
|
|||
}
|
||||
|
||||
LightStage::LightPointer LightStage::getCurrentAmbientLight() const {
|
||||
Index keyLightId{ 0 };
|
||||
Index keyLightId { _defaultLightId };
|
||||
if (!_currentFrame._ambientLights.empty()) {
|
||||
keyLightId = _currentFrame._ambientLights.front();
|
||||
}
|
||||
|
@ -365,7 +375,7 @@ LightStage::LightPointer LightStage::getCurrentAmbientLight() const {
|
|||
}
|
||||
|
||||
LightStage::ShadowPointer LightStage::getCurrentKeyShadow() const {
|
||||
Index keyLightId{ 0 };
|
||||
Index keyLightId { _defaultLightId };
|
||||
if (!_currentFrame._sunLights.empty()) {
|
||||
keyLightId = _currentFrame._sunLights.front();
|
||||
}
|
||||
|
@ -375,7 +385,7 @@ LightStage::ShadowPointer LightStage::getCurrentKeyShadow() const {
|
|||
}
|
||||
|
||||
LightStage::LightAndShadow LightStage::getCurrentKeyLightAndShadow() const {
|
||||
Index keyLightId{ 0 };
|
||||
Index keyLightId { _defaultLightId };
|
||||
if (!_currentFrame._sunLights.empty()) {
|
||||
keyLightId = _currentFrame._sunLights.front();
|
||||
}
|
||||
|
|
|
@ -118,7 +118,9 @@ public:
|
|||
using Shadows = render::indexed_container::IndexedPointerVector<Shadow>;
|
||||
|
||||
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);
|
||||
|
||||
|
@ -185,10 +187,10 @@ public:
|
|||
|
||||
Frame _currentFrame;
|
||||
|
||||
Index getAmbientOffLight() { return _ambientOffLight; }
|
||||
Index getPointOffLight() { return _pointOffLight; }
|
||||
Index getSpotOffLight() { return _spotOffLight; }
|
||||
Index getSunOffLight() { return _sunOffLight; }
|
||||
Index getAmbientOffLight() { return _ambientOffLightId; }
|
||||
Index getPointOffLight() { return _pointOffLightId; }
|
||||
Index getSpotOffLight() { return _spotOffLightId; }
|
||||
Index getSunOffLight() { return _sunOffLightId; }
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -205,17 +207,12 @@ protected:
|
|||
LightMap _lightMap;
|
||||
|
||||
// define off lights
|
||||
Index _ambientOffLightId;
|
||||
Index _pointOffLightId;
|
||||
Index _spotOffLightId;
|
||||
Index _sunOffLightId;
|
||||
|
||||
const LightPointer ambientOffLight { std::make_shared<model::Light>() };
|
||||
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;
|
||||
|
||||
Index _defaultLightId;
|
||||
};
|
||||
using LightStagePointer = std::shared_ptr<LightStage>;
|
||||
|
||||
|
|
|
@ -484,7 +484,8 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) {
|
|||
batch.setInputFormat((_drawMesh->getVertexFormat()));
|
||||
if (_isBlendShaped && _blendedVertexBuffer) {
|
||||
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));
|
||||
} else {
|
||||
batch.setInputStream(0, _drawMesh->getVertexStream());
|
||||
|
|
|
@ -24,8 +24,12 @@
|
|||
#include <PerfStat.h>
|
||||
#include <ViewFrustum.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <TBBHelpers.h>
|
||||
|
||||
#include <model-networking/SimpleMeshProxy.h>
|
||||
|
||||
#include <glm/gtc/packing.hpp>
|
||||
|
||||
#include "AbstractViewStateInterface.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 needFullUpdate = false;
|
||||
|
||||
|
@ -335,10 +367,28 @@ bool Model::updateGeometry() {
|
|||
// TODO? make _blendedVertexBuffers a map instead of vector and only add for meshes with blendshapes?
|
||||
auto buffer = std::make_shared<gpu::Buffer>();
|
||||
if (!mesh.blendshapes.isEmpty()) {
|
||||
buffer->resize((mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3));
|
||||
buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.vertices.constData());
|
||||
std::vector<NormalType> normalsAndTangents;
|
||||
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),
|
||||
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);
|
||||
}
|
||||
|
@ -1006,7 +1056,7 @@ Blender::Blender(ModelPointer model, int blendNumber, const Geometry::WeakPointe
|
|||
|
||||
void Blender::run() {
|
||||
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) {
|
||||
int offset = 0;
|
||||
foreach (const FBXMesh& mesh, _meshes) {
|
||||
|
@ -1015,8 +1065,10 @@ void Blender::run() {
|
|||
}
|
||||
vertices += mesh.vertices;
|
||||
normals += mesh.normals;
|
||||
tangents += mesh.tangents;
|
||||
glm::vec3* meshVertices = vertices.data() + offset;
|
||||
glm::vec3* meshNormals = normals.data() + offset;
|
||||
glm::vec3* meshTangents = tangents.data() + offset;
|
||||
offset += mesh.vertices.size();
|
||||
const float NORMAL_COEFFICIENT_SCALE = 0.01f;
|
||||
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);
|
||||
meshVertices[index] += blendshape.vertices.at(j) * vertexCoefficient;
|
||||
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",
|
||||
Q_ARG(ModelPointer, _model), Q_ARG(int, _blendNumber),
|
||||
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) {
|
||||
|
@ -1220,7 +1273,7 @@ bool Model::maybeStartBlender() {
|
|||
}
|
||||
|
||||
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();
|
||||
if (!geometryRef || _renderGeometry != geometryRef || _blendedVertexBuffers.empty() || blendNumber < _appliedBlendNumber) {
|
||||
return;
|
||||
|
@ -1228,6 +1281,7 @@ void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geo
|
|||
_appliedBlendNumber = blendNumber;
|
||||
const FBXGeometry& fbxGeometry = getFBXGeometry();
|
||||
int index = 0;
|
||||
std::vector<NormalType> normalsAndTangents;
|
||||
for (int i = 0; i < fbxGeometry.meshes.size(); i++) {
|
||||
const FBXMesh& mesh = fbxGeometry.meshes.at(i);
|
||||
if (mesh.blendshapes.isEmpty()) {
|
||||
|
@ -1235,11 +1289,67 @@ void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geo
|
|||
}
|
||||
|
||||
gpu::BufferPointer& buffer = _blendedVertexBuffers[i];
|
||||
buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Byte*) vertices.constData() + index*sizeof(glm::vec3));
|
||||
buffer->setSubData(mesh.vertices.size() * sizeof(glm::vec3),
|
||||
mesh.normals.size() * sizeof(glm::vec3), (gpu::Byte*) normals.constData() + index*sizeof(glm::vec3));
|
||||
const auto vertexCount = mesh.vertices.size();
|
||||
const auto verticesSize = vertexCount * 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,
|
||||
const Geometry::WeakPointer& geometry, const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals) {
|
||||
void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry,
|
||||
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals,
|
||||
const QVector<glm::vec3>& tangents) {
|
||||
if (model) {
|
||||
model->setBlendedVertices(blendNumber, geometry, vertices, normals);
|
||||
model->setBlendedVertices(blendNumber, geometry, vertices, normals, tangents);
|
||||
}
|
||||
_pendingBlenders--;
|
||||
{
|
||||
|
|
|
@ -114,7 +114,7 @@ public:
|
|||
|
||||
/// Sets blended vertices computed in a separate thread.
|
||||
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 isAddedToScene() const { return _addedToScene; }
|
||||
|
@ -437,7 +437,7 @@ public:
|
|||
|
||||
public slots:
|
||||
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:
|
||||
using Mutex = std::mutex;
|
||||
|
|
|
@ -68,10 +68,11 @@ void SetupZones::run(const RenderContextPointer& context, const Inputs& inputs)
|
|||
auto lightStage = context->_scene->getStage<LightStage>();
|
||||
assert(lightStage);
|
||||
|
||||
lightStage->_currentFrame.pushSunLight(0);
|
||||
lightStage->_currentFrame.pushAmbientLight(0);
|
||||
hazeStage->_currentFrame.pushHaze(0);
|
||||
lightStage->_currentFrame.pushSunLight(lightStage->getDefaultLight());
|
||||
lightStage->_currentFrame.pushAmbientLight(lightStage->getDefaultLight());
|
||||
|
||||
backgroundStage->_currentFrame.pushBackground(0);
|
||||
hazeStage->_currentFrame.pushHaze(0);
|
||||
}
|
||||
|
||||
const gpu::PipelinePointer& DebugZoneLighting::getKeyLightPipeline() {
|
||||
|
|
|
@ -60,20 +60,20 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) {
|
|||
|
||||
// alloc the resulting mesh
|
||||
gpu::Resource::Size combinedVertexSize = totalVertexCount * sizeof(glm::vec3);
|
||||
unsigned char* combinedVertexData = new unsigned char[combinedVertexSize];
|
||||
unsigned char* combinedVertexDataCursor = combinedVertexData;
|
||||
std::unique_ptr<unsigned char> combinedVertexData{ new unsigned char[combinedVertexSize] };
|
||||
unsigned char* combinedVertexDataCursor = combinedVertexData.get();
|
||||
|
||||
gpu::Resource::Size combinedColorSize = totalColorCount * sizeof(glm::vec3);
|
||||
unsigned char* combinedColorData = new unsigned char[combinedColorSize];
|
||||
unsigned char* combinedColorDataCursor = combinedColorData;
|
||||
std::unique_ptr<unsigned char> combinedColorData{ new unsigned char[combinedColorSize] };
|
||||
unsigned char* combinedColorDataCursor = combinedColorData.get();
|
||||
|
||||
gpu::Resource::Size combinedNormalSize = totalNormalCount * sizeof(glm::vec3);
|
||||
unsigned char* combinedNormalData = new unsigned char[combinedNormalSize];
|
||||
unsigned char* combinedNormalDataCursor = combinedNormalData;
|
||||
std::unique_ptr<unsigned char> combinedNormalData{ new unsigned char[combinedNormalSize] };
|
||||
unsigned char* combinedNormalDataCursor = combinedNormalData.get();
|
||||
|
||||
gpu::Resource::Size combinedIndexSize = totalIndexCount * sizeof(uint32_t);
|
||||
unsigned char* combinedIndexData = new unsigned char[combinedIndexSize];
|
||||
unsigned char* combinedIndexDataCursor = combinedIndexData;
|
||||
std::unique_ptr<unsigned char> combinedIndexData{ new unsigned char[combinedIndexSize] };
|
||||
unsigned char* combinedIndexDataCursor = combinedIndexData.get();
|
||||
|
||||
uint32_t indexStartOffset { 0 };
|
||||
|
||||
|
@ -105,27 +105,27 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) {
|
|||
model::MeshPointer result(new model::Mesh());
|
||||
|
||||
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::BufferView combinedVertexBufferView(combinedVertexBufferPointer, vertexElement);
|
||||
result->setVertexBuffer(combinedVertexBufferView);
|
||||
|
||||
int attributeTypeColor = gpu::Stream::InputSlot::COLOR; // libraries/gpu/src/gpu/Stream.h
|
||||
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::BufferView combinedColorsBufferView(combinedColorsBufferPointer, colorElement);
|
||||
result->addAttribute(attributeTypeColor, combinedColorsBufferView);
|
||||
|
||||
int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h
|
||||
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::BufferView combinedNormalsBufferView(combinedNormalsBufferPointer, normalElement);
|
||||
result->addAttribute(attributeTypeNormal, combinedNormalsBufferView);
|
||||
|
||||
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::BufferView combinedIndexesBufferView(combinedIndexesBufferPointer, indexElement);
|
||||
result->setIndexBuffer(combinedIndexesBufferView);
|
||||
|
@ -152,9 +152,10 @@ QScriptValue ModelScriptingInterface::transformMesh(glm::mat4 transform, MeshPro
|
|||
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)); },
|
||||
[&](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; });
|
||||
MeshProxy* resultProxy = new SimpleMeshProxy(result);
|
||||
return meshToScriptValue(_modelScriptEngine, resultProxy);
|
||||
|
|
Loading…
Reference in a new issue